git aware version script
[a2jmidid.git] / jack.c
blobb5f15b8f20c183da195c228a33faf3d0cc06ca24
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
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
22 #include <stdbool.h>
23 #include <alsa/asoundlib.h>
24 #include <jack/jack.h>
25 #include <jack/midiport.h>
26 #include <jack/ringbuffer.h>
28 #include "list.h"
29 #include "structs.h"
30 #include "jack.h"
31 #include "log.h"
32 #include "port_hash.h"
33 #include "port.h"
34 #include "a2jmidid.h"
36 static bool g_freewheeling = false;
39 * =================== Input/output port handling =========================
41 static
42 void
43 a2j_set_process_info(
44 struct a2j_process_info * info,
45 struct a2j * self,
46 int dir,
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);
54 info->dir = dir;
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);
74 void
75 a2j_add_ports(
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);
85 static
86 void
87 a2j_jack_process_internal(
88 struct a2j * self,
89 struct a2j_process_info * info_ptr)
91 struct a2j_stream * stream_ptr;
92 int i;
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);
100 // process ports
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);
121 else
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));
131 continue;
134 port_ptr_ptr = &port_ptr->next;
140 * ============================ Input ==============================
142 void
143 a2j_do_jack_input(
144 struct a2j * self,
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;
153 if (time < 0)
154 time = 0;
155 else if (time >= info->nframes)
156 time = info->nframes - 1;
157 buf = jack_midi_event_reserve(port->jack_buf, time, ev.size);
158 if (buf)
159 jack_ringbuffer_read(port->early_events, (char*)buf, ev.size);
160 else
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);
166 static
167 void
168 a2j_port_event(
169 struct a2j * self,
170 snd_seq_event_t * ev)
172 const snd_seq_addr_t addr = ev->data.addr;
174 if (addr.client == self->client_id)
175 return;
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);
189 static
190 void
191 a2j_input_event(
192 struct a2j * self,
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];
198 long size;
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);
204 if (!port)
205 return;
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)
213 return;
215 // fixup NoteOn with vel 0
216 if ((data[0] & 0xF0) == 0x90 && data[2] == 0x00) {
217 data[0] = 0x80 + (data[0] & 0x0F);
218 data[2] = 0x40;
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;
232 ev.size = size;
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));
236 return;
239 if (event_frame < 0)
240 event_frame = 0;
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 ==============================
251 void
252 a2j_do_jack_output(
253 struct a2j * self,
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);
259 int i;
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;
264 int64_t out_time;
265 snd_seq_real_time_t out_rt;
266 int err;
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;
296 } else
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)
310 static
312 a2j_jack_process(
313 jack_nframes_t nframes,
314 void * arg)
316 int res;
317 snd_seq_event_t *event;
318 struct a2j_process_info info;
320 if (g_freewheeling)
321 return 0;
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);
329 else
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);
337 return 0;
340 static
341 void
342 a2j_jack_freewheel(
343 int starting,
344 void * arg)
346 g_freewheeling = starting;
349 static
350 void
351 a2j_jack_shutdown(
352 void * arg)
354 a2j_warning("JACK server shutdown notification received.");
355 g_stop_request = true;
358 #undef a2j_ptr
360 jack_client_t *
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);
373 else
375 jack_client = jack_client_open(client_name, JackNoStartServer|JackUseExactName, &status);
378 if (!jack_client)
380 a2j_error("Cannot create jack client");
381 return NULL;
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);
388 return jack_client;