1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * ALSA SEQ < - > JACK MIDI bridge
5 * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
6 * Copyright (c) 2007,2008 Nedko Arnaudov <nedko@arnaudov.name>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <alsa/asoundlib.h>
24 #include <jack/jack.h>
25 #include <jack/midiport.h>
26 #include <jack/ringbuffer.h>
32 #include "port_hash.h"
36 static bool g_freewheeling
= false;
39 * =================== Input/output port handling =========================
44 struct a2j_process_info
* info
,
47 jack_nframes_t nframes
)
49 const snd_seq_real_time_t
* alsa_time
;
50 snd_seq_queue_status_t
*status
;
52 snd_seq_queue_status_alloca(&status
);
56 info
->period_start
= jack_last_frame_time(self
->jack_client
);
57 info
->nframes
= nframes
;
58 info
->sample_rate
= jack_get_sample_rate(self
->jack_client
);
60 info
->cur_frames
= jack_frame_time(self
->jack_client
);
62 // immediately get alsa'a real time (uhh, why everybody has their on 'real' time)
63 snd_seq_get_queue_status(self
->seq
, self
->queue
, status
);
64 alsa_time
= snd_seq_queue_status_get_real_time(status
);
65 info
->alsa_time
= alsa_time
->tv_sec
* NSEC_PER_SEC
+ alsa_time
->tv_nsec
;
67 if (info
->period_start
+ info
->nframes
< info
->cur_frames
) {
68 int periods_lost
= (info
->cur_frames
- info
->period_start
) / info
->nframes
;
69 info
->period_start
+= periods_lost
* info
->nframes
;
70 a2j_debug("xrun detected: %d periods lost", periods_lost
);
76 struct a2j_stream
* str
)
78 struct a2j_port
*port
;
79 while (jack_ringbuffer_read(str
->new_ports
, (char*)&port
, sizeof(port
))) {
80 a2j_debug("jack: inserted port %s", port
->name
);
81 a2j_port_insert(str
->port_hash
, port
);
87 a2j_jack_process_internal(
89 struct a2j_process_info
* info_ptr
)
91 struct a2j_stream
* stream_ptr
;
93 struct a2j_port
** port_ptr_ptr
;
94 struct a2j_port
* port_ptr
;
96 stream_ptr
= &self
->stream
[info_ptr
->dir
];
98 a2j_add_ports(stream_ptr
);
101 for (i
= 0 ; i
< PORT_HASH_SIZE
; i
++)
103 port_ptr_ptr
= &stream_ptr
->port_hash
[i
];
104 while (*port_ptr_ptr
!= NULL
)
106 port_ptr
= *port_ptr_ptr
;
108 port_ptr
->jack_buf
= jack_port_get_buffer(port_ptr
->jack_port
, info_ptr
->nframes
);
110 if (info_ptr
->dir
== A2J_PORT_CAPTURE
)
112 jack_midi_clear_buffer(port_ptr
->jack_buf
);
115 if (!port_ptr
->is_dead
)
117 if (info_ptr
->dir
== A2J_PORT_CAPTURE
)
119 a2j_do_jack_input(self
, port_ptr
, info_ptr
);
123 a2j_do_jack_output(self
, port_ptr
, info_ptr
);
126 else if (jack_ringbuffer_write_space(self
->port_del
) >= sizeof(port_ptr
))
128 a2j_debug("jack: removed port %s", port_ptr
->name
);
129 *port_ptr_ptr
= port_ptr
->next
;
130 jack_ringbuffer_write(self
->port_del
, (char*)&port_ptr
, sizeof(port_ptr
));
134 port_ptr_ptr
= &port_ptr
->next
;
140 * ============================ Input ==============================
145 struct a2j_port
* port
,
146 struct a2j_process_info
* info
)
148 // process port->early_events
149 struct a2j_alsa_midi_event ev
;
150 while (jack_ringbuffer_read(port
->early_events
, (char*)&ev
, sizeof(ev
))) {
151 jack_midi_data_t
* buf
;
152 jack_nframes_t time
= ev
.time
- info
->period_start
;
155 else if (time
>= info
->nframes
)
156 time
= info
->nframes
- 1;
157 buf
= jack_midi_event_reserve(port
->jack_buf
, time
, ev
.size
);
159 jack_ringbuffer_read(port
->early_events
, (char*)buf
, ev
.size
);
161 jack_ringbuffer_read_advance(port
->early_events
, ev
.size
);
162 a2j_debug("input: it's time for %d bytes at %d", ev
.size
, time
);
170 snd_seq_event_t
* ev
)
172 const snd_seq_addr_t addr
= ev
->data
.addr
;
174 if (addr
.client
== self
->client_id
)
177 if (ev
->type
== SND_SEQ_EVENT_PORT_START
|| ev
->type
== SND_SEQ_EVENT_PORT_CHANGE
) {
178 assert (jack_ringbuffer_write_space(self
->port_add
) >= sizeof(addr
));
180 a2j_debug("port_event: add/change %d:%d", addr
.client
, addr
.port
);
181 jack_ringbuffer_write(self
->port_add
, (char*)&addr
, sizeof(addr
));
182 } else if (ev
->type
== SND_SEQ_EVENT_PORT_EXIT
) {
183 a2j_debug("port_event: del %d:%d", addr
.client
, addr
.port
);
184 a2j_port_setdead(self
->stream
[A2J_PORT_CAPTURE
].port_hash
, addr
);
185 a2j_port_setdead(self
->stream
[A2J_PORT_PLAYBACK
].port_hash
, addr
);
193 snd_seq_event_t
* alsa_event
,
194 struct a2j_process_info
* info
)
196 jack_midi_data_t data
[MAX_EVENT_SIZE
];
197 struct a2j_stream
*str
= &self
->stream
[A2J_PORT_CAPTURE
];
199 int64_t alsa_time
, time_offset
;
200 int64_t frame_offset
, event_frame
;
201 struct a2j_port
*port
;
203 port
= a2j_port_get(str
->port_hash
, alsa_event
->source
);
208 * RPNs, NRPNs, Bank Change, etc. need special handling
209 * but seems, ALSA does it for us already.
211 snd_midi_event_reset_decode(str
->codec
);
212 if ((size
= snd_midi_event_decode(str
->codec
, data
, sizeof(data
), alsa_event
))<0)
215 // fixup NoteOn with vel 0
216 if ((data
[0] & 0xF0) == 0x90 && data
[2] == 0x00) {
217 data
[0] = 0x80 + (data
[0] & 0x0F);
221 alsa_time
= alsa_event
->time
.time
.tv_sec
* NSEC_PER_SEC
+ alsa_event
->time
.time
.tv_nsec
;
222 time_offset
= info
->alsa_time
- alsa_time
;
223 frame_offset
= (info
->sample_rate
* time_offset
) / NSEC_PER_SEC
;
224 event_frame
= (int64_t)info
->cur_frames
- info
->period_start
- frame_offset
+ info
->nframes
;
226 a2j_debug("input: %d bytes at event_frame=%d", (int)size
, (int)event_frame
);
228 if (event_frame
>= info
->nframes
&&
229 jack_ringbuffer_write_space(port
->early_events
) >= (sizeof(struct a2j_alsa_midi_event
) + size
)) {
230 struct a2j_alsa_midi_event ev
;
231 ev
.time
= event_frame
+ info
->period_start
;
233 jack_ringbuffer_write(port
->early_events
, (char*)&ev
, sizeof(ev
));
234 jack_ringbuffer_write(port
->early_events
, (char*)data
, size
);
235 a2j_debug("postponed to next frame +%d", (int) (event_frame
- info
->nframes
));
241 else if (event_frame
>= info
->nframes
)
242 event_frame
= info
->nframes
- 1;
244 jack_midi_event_write(port
->jack_buf
, event_frame
, data
, size
);
248 * ============================ Output ==============================
254 struct a2j_port
* port
,
255 struct a2j_process_info
* info
)
257 struct a2j_stream
*str
= &self
->stream
[info
->dir
];
258 int nevents
= jack_midi_get_event_count(port
->jack_buf
);
260 for (i
=0; i
<nevents
; ++i
) {
261 jack_midi_event_t jack_event
;
262 snd_seq_event_t alsa_event
;
263 int64_t frame_offset
;
265 snd_seq_real_time_t out_rt
;
268 jack_midi_event_get(&jack_event
, port
->jack_buf
, i
);
270 snd_seq_ev_clear(&alsa_event
);
271 snd_midi_event_reset_encode(str
->codec
);
272 if (!snd_midi_event_encode(str
->codec
, jack_event
.buffer
, jack_event
.size
, &alsa_event
))
273 continue; // invalid event
275 snd_seq_ev_set_source(&alsa_event
, self
->port_id
);
276 snd_seq_ev_set_dest(&alsa_event
, port
->remote
.client
, port
->remote
.port
);
278 /* NOTE: in case of xrun it could become negative, so it is essential to use signed type! */
279 frame_offset
= (int64_t)jack_event
.time
+ info
->period_start
+ info
->nframes
- info
->cur_frames
;
280 if (frame_offset
< 0) {
281 frame_offset
= info
->nframes
+ jack_event
.time
;
282 a2j_error("internal xrun detected: frame_offset = %"PRId64
, frame_offset
);
284 /* Ken Ellinwood reported problems with this assert.
285 * Seems, magic 2 should be replaced with nperiods. */
286 //FIXME: assert (frame_offset < info->nframes*2);
287 //if (frame_offset < info->nframes * info->nperiods)
288 // debug_log("alsa_out: BLAH-BLAH-BLAH");
290 out_time
= info
->alsa_time
+ (frame_offset
* NSEC_PER_SEC
) / info
->sample_rate
;
292 // we should use absolute time to prevent reordering caused by rounding errors
293 if (out_time
< port
->last_out_time
) {
294 a2j_debug("alsa_out: limiting out_time %lld at %lld", out_time
, port
->last_out_time
);
295 out_time
= port
->last_out_time
;
297 port
->last_out_time
= out_time
;
299 out_rt
.tv_nsec
= out_time
% NSEC_PER_SEC
;
300 out_rt
.tv_sec
= out_time
/ NSEC_PER_SEC
;
301 snd_seq_ev_schedule_real(&alsa_event
, self
->queue
, 0, &out_rt
);
303 err
= snd_seq_event_output(self
->seq
, &alsa_event
);
304 a2j_debug("alsa_out: written %d bytes to %s at %d (%lld): %d %s", jack_event
.size
, port
->name
, (int)frame_offset
, out_time
- info
->alsa_time
, err
, err
< 0 ? snd_strerror(err
) : "bytes queued");
308 #define a2j_ptr ((struct a2j *)arg)
313 jack_nframes_t nframes
,
317 snd_seq_event_t
*event
;
318 struct a2j_process_info info
;
323 a2j_set_process_info(&info
, a2j_ptr
, A2J_PORT_CAPTURE
, nframes
);
324 a2j_jack_process_internal(a2j_ptr
, &info
);
326 while ((res
= snd_seq_event_input(a2j_ptr
->seq
, &event
))>0) {
327 if (event
->source
.client
== SND_SEQ_CLIENT_SYSTEM
)
328 a2j_port_event(a2j_ptr
, event
);
330 a2j_input_event(a2j_ptr
, event
, &info
);
333 a2j_set_process_info(&info
, a2j_ptr
, A2J_PORT_PLAYBACK
, nframes
);
334 a2j_jack_process_internal(a2j_ptr
, &info
);
335 snd_seq_drain_output(a2j_ptr
->seq
);
346 g_freewheeling
= starting
;
354 a2j_warning("JACK server shutdown notification received.");
355 g_stop_request
= true;
361 a2j_jack_client_create(
362 struct a2j
* a2j_ptr
,
363 const char * client_name
,
364 const char * server_name
)
366 jack_status_t status
;
367 jack_client_t
* jack_client
;
369 if (server_name
!= NULL
)
371 jack_client
= jack_client_open(client_name
, JackServerName
|JackNoStartServer
|JackUseExactName
, &status
, server_name
);
375 jack_client
= jack_client_open(client_name
, JackNoStartServer
|JackUseExactName
, &status
);
380 a2j_error("Cannot create jack client");
384 jack_set_process_callback(jack_client
, a2j_jack_process
, a2j_ptr
);
385 jack_set_freewheel_callback(jack_client
, a2j_jack_freewheel
, NULL
);
386 jack_on_shutdown(jack_client
, a2j_jack_shutdown
, NULL
);