add dbus command to allow checking of wether hardware is currently exported
[a2jmidid.git] / a2jmidid.c
blobf47edded59464aec83fa42982f12c167a9999bfd
1 /*
2 * ALSA SEQ < - > JACK MIDI bridge
4 * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
5 * Copyright (c) 2007,2008,2009,2011 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
21 #include "config.h"
23 #include <signal.h>
24 #include <semaphore.h>
25 #include <stdbool.h>
26 #include <sys/stat.h>
27 #include <pthread.h>
29 #include <alsa/asoundlib.h>
31 #include <jack/jack.h>
32 #include <jack/midiport.h>
33 #include <jack/ringbuffer.h>
35 #if HAVE_DBUS_1
36 # include <dbus/dbus.h>
37 #endif
39 #include <getopt.h>
41 #include "list.h"
42 #include "structs.h"
43 #include "port.h"
44 #include "port_thread.h"
45 #include "port_hash.h"
46 #include "log.h"
47 #if HAVE_DBUS_1
48 # include "dbus.h"
49 #endif
50 #include "a2jmidid.h"
51 #include "paths.h"
52 #include "conf.h"
53 #include "jack.h"
54 #include "sigsegv.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;
66 bool g_disable_port_uniqueness = false;
68 bool g_a2j_export_hw_ports = false;
69 char * g_a2j_jack_server_name = "default";
71 static
72 void
73 a2j_sigint_handler(
74 int i)
76 g_keep_walking = false;
79 static
80 bool
81 a2j_stream_init(
82 struct a2j * self,
83 int dir)
85 struct a2j_stream *str = &self->stream[dir];
87 str->new_ports = jack_ringbuffer_create(MAX_PORTS * sizeof(struct a2j_port *));
88 if (str->new_ports == NULL)
90 return false;
93 snd_midi_event_new(MAX_EVENT_SIZE, &str->codec);
94 INIT_LIST_HEAD(&str->list);
96 return true;
99 static
100 void
101 a2j_stream_attach(
102 struct a2j_stream * stream_ptr)
106 static
107 void
108 a2j_stream_detach(
109 struct a2j_stream * stream_ptr)
111 struct a2j_port * port_ptr;
112 struct list_head * node_ptr;
114 while (!list_empty(&stream_ptr->list))
116 node_ptr = stream_ptr->list.next;
117 list_del(node_ptr);
118 port_ptr = list_entry(node_ptr, struct a2j_port, siblings);
119 a2j_info("port deleted: %s", port_ptr->name);
120 a2j_port_free(port_ptr);
124 static
125 void
126 a2j_stream_close(
127 struct a2j * self,
128 int dir)
130 struct a2j_stream *str = &self->stream[dir];
132 if (str->codec)
133 snd_midi_event_free(str->codec);
134 if (str->new_ports)
135 jack_ringbuffer_free(str->new_ports);
138 struct a2j * a2j_new(void)
140 int error;
141 void * thread_status;
143 struct a2j *self = calloc(1, sizeof(struct a2j));
144 a2j_debug("midi: new");
145 if (!self)
147 a2j_error("calloc() failed to allocate a2j struct");
148 goto fail;
151 self->port_add = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(snd_seq_addr_t));
152 if (self->port_add == NULL)
154 goto free_self;
157 self->port_del = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(struct a2j_port *));
158 if (self->port_del == NULL)
160 goto free_ringbuffer_add;
163 self->outbound_events = jack_ringbuffer_create(MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event));
164 if (self->outbound_events == NULL)
166 goto free_ringbuffer_del;
169 if (!a2j_stream_init(self, A2J_PORT_CAPTURE))
171 goto free_ringbuffer_outbound;
174 if (!a2j_stream_init(self, A2J_PORT_PLAYBACK))
176 goto close_capture_stream;
179 error = snd_seq_open(&self->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0);
180 if (error < 0)
182 a2j_error("failed to open alsa seq");
183 goto close_playback_stream;
186 error = snd_seq_set_client_name(self->seq, "a2jmidid");
187 if (error < 0)
189 a2j_error("snd_seq_set_client_name() failed");
190 goto close_seq_client;
193 self->port_id = snd_seq_create_simple_port(
194 self->seq,
195 "port",
196 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE
197 #ifndef DEBUG
198 |SND_SEQ_PORT_CAP_NO_EXPORT
199 #endif
200 ,SND_SEQ_PORT_TYPE_APPLICATION);
201 if (self->port_id < 0)
203 a2j_error("snd_seq_create_simple_port() failed");
204 goto close_seq_client;
207 self->client_id = snd_seq_client_id(self->seq);
208 if (self->client_id < 0)
210 a2j_error("snd_seq_client_id() failed");
211 goto close_seq_client;
214 self->queue = snd_seq_alloc_queue(self->seq);
215 if (self->queue < 0)
217 a2j_error("snd_seq_alloc_queue() failed");
218 goto close_seq_client;
221 snd_seq_start_queue(self->seq, self->queue, 0);
223 a2j_stream_attach(self->stream + A2J_PORT_CAPTURE);
224 a2j_stream_attach(self->stream + A2J_PORT_PLAYBACK);
226 error = snd_seq_nonblock(self->seq, 1);
227 if (error < 0)
229 a2j_error("snd_seq_nonblock() failed");
230 goto close_seq_client;
233 snd_seq_drop_input(self->seq);
235 a2j_add_ports(&self->stream[A2J_PORT_CAPTURE]);
236 a2j_add_ports(&self->stream[A2J_PORT_PLAYBACK]);
238 self->jack_client = a2j_jack_client_create(self, A2J_JACK_CLIENT_NAME, g_a2j_jack_server_name);
239 if (self->jack_client == NULL)
241 goto free_self;
244 if (sem_init(&self->io_semaphore, 0, 0) < 0)
246 a2j_error("can't create IO semaphore");
247 goto close_jack_client;
250 if (jack_activate(self->jack_client))
252 a2j_error("can't activate jack client");
253 goto sem_destroy;
256 g_keep_alsa_walking = true;
258 if (pthread_create(&self->alsa_input_thread, NULL, a2j_alsa_input_thread, self) < 0)
260 a2j_error("cannot start ALSA input thread");
261 goto sem_destroy;
264 /* wake the poll loop in the alsa input thread so initial ports are fetched */
265 error = snd_seq_connect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
266 if (error < 0)
268 a2j_error("snd_seq_connect_from() failed");
269 goto join_input_thread;
272 if (pthread_create(&self->alsa_output_thread, NULL, a2j_alsa_output_thread, self) < 0)
274 a2j_error("cannot start ALSA output thread");
275 goto disconnect;
278 return self;
280 disconnect:
281 g_keep_alsa_walking = false; /* tell alsa threads to stop */
282 snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
283 join_input_thread:
284 pthread_join(self->alsa_input_thread, &thread_status);
285 sem_destroy:
286 sem_destroy(&self->io_semaphore);
287 close_jack_client:
288 error = jack_client_close(self->jack_client);
289 if (error != 0)
291 a2j_error("Cannot close jack client");
293 close_seq_client:
294 snd_seq_close(self->seq);
295 close_playback_stream:
296 a2j_stream_close(self, A2J_PORT_PLAYBACK);
297 close_capture_stream:
298 a2j_stream_close(self, A2J_PORT_CAPTURE);
299 free_ringbuffer_outbound:
300 jack_ringbuffer_free(self->outbound_events);
301 free_ringbuffer_del:
302 jack_ringbuffer_free(self->port_del);
303 free_ringbuffer_add:
304 jack_ringbuffer_free(self->port_add);
305 free_self:
306 free(self);
307 fail:
308 return NULL;
311 static void a2j_destroy(struct a2j * self)
313 int error;
314 void * thread_status;
316 a2j_debug("midi: delete");
318 g_keep_alsa_walking = false; /* tell alsa threads to stop */
320 /* do something that we need to do anyway and will wake the input thread, then join */
321 snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
322 pthread_join(self->alsa_input_thread, &thread_status);
324 /* wake output thread and join, then destroy the semaphore */
325 sem_post(&self->io_semaphore);
326 pthread_join(self->alsa_output_thread, &thread_status);
327 sem_destroy(&self->io_semaphore);
329 jack_ringbuffer_reset(self->port_add);
331 jack_deactivate(self->jack_client);
333 a2j_stream_detach(self->stream + A2J_PORT_CAPTURE);
334 a2j_stream_detach(self->stream + A2J_PORT_PLAYBACK);
336 error = jack_client_close(self->jack_client);
337 if (error != 0)
339 a2j_error("Cannot close jack client (%d)", error);
342 snd_seq_close(self->seq);
343 self->seq = NULL;
345 a2j_stream_close(self, A2J_PORT_PLAYBACK);
346 a2j_stream_close(self, A2J_PORT_CAPTURE);
348 jack_ringbuffer_free(self->outbound_events);
349 jack_ringbuffer_free(self->port_add);
350 jack_ringbuffer_free(self->port_del);
352 free(self);
355 bool a2j_start(void)
357 if (g_started)
359 a2j_error("Bridge already started");
360 return false;
363 a2j_info("Bridge starting...");
365 a2j_info("Using JACK server '%s'", g_a2j_jack_server_name);
367 a2j_info("Hardware ports %s be exported.", g_a2j_export_hw_ports ? "will": "will not");
369 g_a2j = a2j_new();
370 if (g_a2j == NULL)
372 a2j_error("a2j_new() failed.");
373 return false;
376 a2j_info("Bridge started");
378 #if HAVE_DBUS_1
379 if (a2j_dbus_is_available())
381 a2j_dbus_signal_emit_bridge_started();
383 #endif
385 g_started = true;
387 return true;
390 bool a2j_stop(void)
392 if (!g_started)
394 a2j_error("Bridge already stopped");
395 return false;
398 a2j_info("Bridge stopping...");
400 a2j_destroy(g_a2j);
401 g_a2j = NULL;
403 a2j_info("Bridge stopped");
405 g_started = false;
407 #if HAVE_DBUS_1
408 if (a2j_dbus_is_available())
410 a2j_dbus_signal_emit_bridge_stopped();
412 #endif
414 return true;
417 bool
418 a2j_is_started()
420 return g_started;
423 static
424 void
425 a2j_help(
426 const char * self)
428 a2j_info("Usage: %s [-j jack-server] [-e | --export-hw] [-u]", self);
429 a2j_info("Defaults:");
430 a2j_info("-j default");
434 main(
435 int argc,
436 char *argv[])
438 bool dbus;
439 struct stat st;
440 char timestamp_str[26];
442 //test_list_sort();
444 st.st_mtime = 0;
445 stat(argv[0], &st);
446 ctime_r(&st.st_mtime, timestamp_str);
447 timestamp_str[24] = 0;
449 g_max_jack_port_name_size = jack_port_name_size();
451 if (!a2j_paths_init())
453 goto fail;
456 #if HAVE_DBUS_1
457 dbus = argc == 2 && strcmp(argv[1], "dbus") == 0;
458 #else
459 dbus = false;
460 #endif
462 if (!a2j_log_init(dbus))
464 goto fail_paths_uninit;
467 if (!dbus)
469 struct option long_opts[] = { { "export-hw", 0, 0, 'e' }, { 0, 0, 0, 0 } };
471 int option_index = 0;
472 int c;
473 while ((c = getopt_long(argc, argv, "j:eu", long_opts, &option_index)) != -1)
475 switch (c)
477 case 'j':
478 g_a2j_jack_server_name = strdup(optarg);
479 break;
480 case 'e':
481 g_a2j_export_hw_ports = true;
482 break;
483 case 'u':
484 g_disable_port_uniqueness = true;
485 break;
486 default:
487 a2j_help(argv[0]);
488 return 1;
492 else
494 //a2j_conf_load();
497 if (dbus)
499 a2j_info("----------------------------");
502 a2j_info("JACK MIDI <-> ALSA sequencer MIDI bridge, version " A2J_VERSION " (" GIT_VERSION ") built on %s", timestamp_str);
503 a2j_info("Copyright 2006,2007 Dmitry S. Baikov");
504 a2j_info("Copyright 2007,2008,2009,2011 Nedko Arnaudov");
506 if (dbus)
508 a2j_info("----------------------------");
509 a2j_info("Activated.");
511 else
513 a2j_info("");
516 /* setup our SIGSEGV magic that prints nice stack in our logfile */
517 if (dbus)
519 setup_sigsegv();
522 signal(SIGINT, &a2j_sigint_handler);
523 signal(SIGTERM, &a2j_sigint_handler);
525 if (dbus)
527 #if HAVE_DBUS_1
528 if (!a2j_dbus_init())
530 a2j_error("a2j_dbus_init() failed.");
531 goto fail_uninit_log;
533 #endif
535 else
537 if (!a2j_start())
539 goto fail_uninit_log;
542 a2j_info("Press ctrl-c to stop the bridge");
545 while (g_keep_walking)
547 if (dbus)
549 #if HAVE_DBUS_1
550 if (!a2j_dbus_run(MAIN_LOOP_SLEEP_INTERVAL))
552 a2j_warning("Disconnect message was received from D-Bus.");
553 break;
555 #endif
557 else
559 usleep(MAIN_LOOP_SLEEP_INTERVAL * 1000);
562 if (g_stop_request)
564 g_stop_request = false;
566 a2j_stop();
568 if (!dbus)
570 break;
574 if (g_started)
576 a2j_free_ports(g_a2j->port_del);
577 a2j_update_ports(g_a2j);
581 if (g_started)
583 a2j_stop();
586 #if HAVE_DBUS_1
587 if (dbus)
589 a2j_dbus_uninit();
591 a2j_info("Deactivated.");
592 a2j_info("----------------------------");
594 #endif
596 fail_uninit_log:
597 a2j_log_uninit();
599 fail_paths_uninit:
600 a2j_paths_uninit();
602 fail:
603 return 0;