2 * ALSA SEQ < - > JACK MIDI bridge
4 * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
5 * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <semaphore.h>
29 #include <alsa/asoundlib.h>
31 #include <jack/jack.h>
32 #include <jack/midiport.h>
33 #include <jack/ringbuffer.h>
36 # include <dbus/dbus.h>
44 #include "port_thread.h"
45 #include "port_hash.h"
55 #include "gitversion.h"
56 #include "dbus_iface_control.h"
58 #define MAIN_LOOP_SLEEP_INTERVAL 50 // in milliseconds
60 bool g_keep_walking
= true;
61 bool g_keep_alsa_walking
= false;
62 bool g_stop_request
= false;
63 static bool g_started
= false;
64 struct a2j
* g_a2j
= NULL
;
65 size_t g_max_jack_port_name_size
;
67 bool g_a2j_export_hw_ports
= false;
68 char * g_a2j_jack_server_name
= "default";
75 g_keep_walking
= false;
84 struct a2j_stream
*str
= &self
->stream
[dir
];
86 str
->new_ports
= jack_ringbuffer_create(MAX_PORTS
* sizeof(struct a2j_port
*));
87 if (str
->new_ports
== NULL
)
92 snd_midi_event_new(MAX_EVENT_SIZE
, &str
->codec
);
93 INIT_LIST_HEAD(&str
->list
);
101 struct a2j_stream
* stream_ptr
)
108 struct a2j_stream
* stream_ptr
)
110 struct a2j_port
* port_ptr
;
111 struct list_head
* node_ptr
;
113 while (!list_empty(&stream_ptr
->list
))
115 node_ptr
= stream_ptr
->list
.next
;
117 port_ptr
= list_entry(node_ptr
, struct a2j_port
, siblings
);
118 a2j_info("port deleted: %s", port_ptr
->name
);
119 a2j_port_free(port_ptr
);
129 struct a2j_stream
*str
= &self
->stream
[dir
];
132 snd_midi_event_free(str
->codec
);
134 jack_ringbuffer_free(str
->new_ports
);
137 struct a2j
* a2j_new(void)
140 void * thread_status
;
142 struct a2j
*self
= calloc(1, sizeof(struct a2j
));
143 a2j_debug("midi: new");
146 a2j_error("calloc() failed to allocate a2j struct");
150 self
->port_add
= jack_ringbuffer_create(2 * MAX_PORTS
* sizeof(snd_seq_addr_t
));
151 if (self
->port_add
== NULL
)
156 self
->port_del
= jack_ringbuffer_create(2 * MAX_PORTS
* sizeof(struct a2j_port
*));
157 if (self
->port_del
== NULL
)
159 goto free_ringbuffer_add
;
162 self
->outbound_events
= jack_ringbuffer_create(MAX_EVENT_SIZE
* 16 * sizeof(struct a2j_delivery_event
));
163 if (self
->outbound_events
== NULL
)
165 goto free_ringbuffer_del
;
168 if (!a2j_stream_init(self
, A2J_PORT_CAPTURE
))
170 goto free_ringbuffer_outbound
;
173 if (!a2j_stream_init(self
, A2J_PORT_PLAYBACK
))
175 goto close_capture_stream
;
178 error
= snd_seq_open(&self
->seq
, "hw", SND_SEQ_OPEN_DUPLEX
, 0);
181 a2j_error("failed to open alsa seq");
182 goto close_playback_stream
;
185 error
= snd_seq_set_client_name(self
->seq
, "a2jmidid");
188 a2j_error("snd_seq_set_client_name() failed");
189 goto close_seq_client
;
192 self
->port_id
= snd_seq_create_simple_port(
195 SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_WRITE
197 |SND_SEQ_PORT_CAP_NO_EXPORT
199 ,SND_SEQ_PORT_TYPE_APPLICATION
);
200 if (self
->port_id
< 0)
202 a2j_error("snd_seq_create_simple_port() failed");
203 goto close_seq_client
;
206 self
->client_id
= snd_seq_client_id(self
->seq
);
207 if (self
->client_id
< 0)
209 a2j_error("snd_seq_client_id() failed");
210 goto close_seq_client
;
213 self
->queue
= snd_seq_alloc_queue(self
->seq
);
216 a2j_error("snd_seq_alloc_queue() failed");
217 goto close_seq_client
;
220 snd_seq_start_queue(self
->seq
, self
->queue
, 0);
222 a2j_stream_attach(self
->stream
+ A2J_PORT_CAPTURE
);
223 a2j_stream_attach(self
->stream
+ A2J_PORT_PLAYBACK
);
225 error
= snd_seq_nonblock(self
->seq
, 1);
228 a2j_error("snd_seq_nonblock() failed");
229 goto close_seq_client
;
232 snd_seq_drop_input(self
->seq
);
234 a2j_add_ports(&self
->stream
[A2J_PORT_CAPTURE
]);
235 a2j_add_ports(&self
->stream
[A2J_PORT_PLAYBACK
]);
237 self
->jack_client
= a2j_jack_client_create(self
, A2J_JACK_CLIENT_NAME
, g_a2j_jack_server_name
);
238 if (self
->jack_client
== NULL
)
243 if (sem_init(&self
->io_semaphore
, 0, 0) < 0)
245 a2j_error("can't create IO semaphore");
246 goto close_jack_client
;
249 if (jack_activate(self
->jack_client
))
251 a2j_error("can't activate jack client");
255 g_keep_alsa_walking
= true;
257 if (pthread_create(&self
->alsa_input_thread
, NULL
, a2j_alsa_input_thread
, self
) < 0)
259 a2j_error("cannot start ALSA input thread");
263 /* wake the poll loop in the alsa input thread so initial ports are fetched */
264 error
= snd_seq_connect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
267 a2j_error("snd_seq_connect_from() failed");
268 goto join_input_thread
;
271 if (pthread_create(&self
->alsa_output_thread
, NULL
, a2j_alsa_output_thread
, self
) < 0)
273 a2j_error("cannot start ALSA output thread");
280 g_keep_alsa_walking
= false; /* tell alsa threads to stop */
281 snd_seq_disconnect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
283 pthread_join(self
->alsa_input_thread
, &thread_status
);
285 sem_destroy(&self
->io_semaphore
);
287 error
= jack_client_close(self
->jack_client
);
290 a2j_error("Cannot close jack client");
293 snd_seq_close(self
->seq
);
294 close_playback_stream
:
295 a2j_stream_close(self
, A2J_PORT_PLAYBACK
);
296 close_capture_stream
:
297 a2j_stream_close(self
, A2J_PORT_CAPTURE
);
298 free_ringbuffer_outbound
:
299 jack_ringbuffer_free(self
->outbound_events
);
301 jack_ringbuffer_free(self
->port_del
);
303 jack_ringbuffer_free(self
->port_add
);
310 static void a2j_destroy(struct a2j
* self
)
313 void * thread_status
;
315 a2j_debug("midi: delete");
317 g_keep_alsa_walking
= false; /* tell alsa threads to stop */
319 /* do something that we need to do anyway and will wake the input thread, then join */
320 snd_seq_disconnect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
321 pthread_join(self
->alsa_input_thread
, &thread_status
);
323 /* wake output thread and join, then destroy the semaphore */
324 sem_post(&self
->io_semaphore
);
325 pthread_join(self
->alsa_output_thread
, &thread_status
);
326 sem_destroy(&self
->io_semaphore
);
328 jack_ringbuffer_reset(self
->port_add
);
330 jack_deactivate(self
->jack_client
);
332 a2j_stream_detach(self
->stream
+ A2J_PORT_CAPTURE
);
333 a2j_stream_detach(self
->stream
+ A2J_PORT_PLAYBACK
);
335 error
= jack_client_close(self
->jack_client
);
338 a2j_error("Cannot close jack client (%d)", error
);
341 snd_seq_close(self
->seq
);
344 a2j_stream_close(self
, A2J_PORT_PLAYBACK
);
345 a2j_stream_close(self
, A2J_PORT_CAPTURE
);
347 jack_ringbuffer_free(self
->outbound_events
);
348 jack_ringbuffer_free(self
->port_add
);
349 jack_ringbuffer_free(self
->port_del
);
358 a2j_error("Bridge already started");
362 a2j_info("Bridge starting...");
364 a2j_info("Using JACK server '%s'", g_a2j_jack_server_name
);
366 a2j_info("Hardware ports %s be exported.", g_a2j_export_hw_ports
? "will": "will not");
371 a2j_error("a2j_new() failed.");
375 a2j_info("Bridge started");
378 if (a2j_dbus_is_available())
380 a2j_dbus_signal_emit_bridge_started();
393 a2j_error("Bridge already stopped");
397 a2j_info("Bridge stopping...");
402 a2j_info("Bridge stopped");
407 if (a2j_dbus_is_available())
409 a2j_dbus_signal_emit_bridge_stopped();
427 a2j_info("Usage: %s [-j jack-server] [-e | --export-hw]", self
);
428 a2j_info("Defaults:");
429 a2j_info("-j default");
439 char timestamp_str
[26];
445 ctime_r(&st
.st_mtime
, timestamp_str
);
446 timestamp_str
[24] = 0;
448 g_max_jack_port_name_size
= jack_port_name_size();
450 if (!a2j_paths_init())
456 dbus
= argc
== 2 && strcmp(argv
[1], "dbus") == 0;
461 if (!a2j_log_init(dbus
))
463 goto fail_paths_uninit
;
468 struct option long_opts
[] = { { "export-hw", 0, 0, 'e' }, { 0, 0, 0, 0 } };
470 int option_index
= 0;
472 while ((c
= getopt_long(argc
, argv
, "j:eq", long_opts
, &option_index
)) != -1)
477 g_a2j_jack_server_name
= strdup(optarg
);
480 g_a2j_export_hw_ports
= true;
495 a2j_info("----------------------------");
498 a2j_info("JACK MIDI <-> ALSA sequencer MIDI bridge, version " A2J_VERSION
" (" GIT_VERSION
") built on %s", timestamp_str
);
499 a2j_info("Copyright 2006,2007 Dmitry S. Baikov");
500 a2j_info("Copyright 2007,2008,2009 Nedko Arnaudov");
504 a2j_info("----------------------------");
505 a2j_info("Activated.");
512 /* setup our SIGSEGV magic that prints nice stack in our logfile */
518 signal(SIGINT
, &a2j_sigint_handler
);
519 signal(SIGTERM
, &a2j_sigint_handler
);
524 if (!a2j_dbus_init())
526 a2j_error("a2j_dbus_init() failed.");
527 goto fail_uninit_log
;
535 goto fail_uninit_log
;
538 a2j_info("Press ctrl-c to stop the bridge");
541 while (g_keep_walking
)
546 if (!a2j_dbus_run(MAIN_LOOP_SLEEP_INTERVAL
))
548 a2j_warning("Disconnect message was received from D-Bus.");
555 usleep(MAIN_LOOP_SLEEP_INTERVAL
* 1000);
560 g_stop_request
= false;
572 a2j_free_ports(g_a2j
->port_del
);
573 a2j_update_ports(g_a2j
);
587 a2j_info("Deactivated.");
588 a2j_info("----------------------------");