Fix typo in a comment
[a2jmidid.git] / a2jmidid.c
blobe3790e91b3af3ad9461d795d3991a63256bafa33
1 /*
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
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;
67 bool g_a2j_export_hw_ports = false;
68 char * g_a2j_jack_server_name = "default";
70 static
71 void
72 a2j_sigint_handler(
73 int i)
75 g_keep_walking = false;
78 static
79 bool
80 a2j_stream_init(
81 struct a2j * self,
82 int dir)
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)
89 return false;
92 snd_midi_event_new(MAX_EVENT_SIZE, &str->codec);
93 INIT_LIST_HEAD(&str->list);
95 return true;
98 static
99 void
100 a2j_stream_attach(
101 struct a2j_stream * stream_ptr)
105 static
106 void
107 a2j_stream_detach(
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;
116 list_del(node_ptr);
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);
123 static
124 void
125 a2j_stream_close(
126 struct a2j * self,
127 int dir)
129 struct a2j_stream *str = &self->stream[dir];
131 if (str->codec)
132 snd_midi_event_free(str->codec);
133 if (str->new_ports)
134 jack_ringbuffer_free(str->new_ports);
137 struct a2j * a2j_new(void)
139 int error;
140 void * thread_status;
142 struct a2j *self = calloc(1, sizeof(struct a2j));
143 a2j_debug("midi: new");
144 if (!self)
146 a2j_error("calloc() failed to allocate a2j struct");
147 goto fail;
150 self->port_add = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(snd_seq_addr_t));
151 if (self->port_add == NULL)
153 goto free_self;
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);
179 if (error < 0)
181 a2j_error("failed to open alsa seq");
182 goto close_playback_stream;
185 error = snd_seq_set_client_name(self->seq, "a2jmidid");
186 if (error < 0)
188 a2j_error("snd_seq_set_client_name() failed");
189 goto close_seq_client;
192 self->port_id = snd_seq_create_simple_port(
193 self->seq,
194 "port",
195 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE
196 #ifndef DEBUG
197 |SND_SEQ_PORT_CAP_NO_EXPORT
198 #endif
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);
214 if (self->queue < 0)
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);
226 if (error < 0)
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)
240 goto free_self;
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");
252 goto sem_destroy;
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");
260 goto sem_destroy;
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);
265 if (error < 0)
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");
274 goto disconnect;
277 return self;
279 disconnect:
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);
282 join_input_thread:
283 pthread_join(self->alsa_input_thread, &thread_status);
284 sem_destroy:
285 sem_destroy(&self->io_semaphore);
286 close_jack_client:
287 error = jack_client_close(self->jack_client);
288 if (error != 0)
290 a2j_error("Cannot close jack client");
292 close_seq_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);
300 free_ringbuffer_del:
301 jack_ringbuffer_free(self->port_del);
302 free_ringbuffer_add:
303 jack_ringbuffer_free(self->port_add);
304 free_self:
305 free(self);
306 fail:
307 return NULL;
310 static void a2j_destroy(struct a2j * self)
312 int error;
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);
336 if (error != 0)
338 a2j_error("Cannot close jack client (%d)", error);
341 snd_seq_close(self->seq);
342 self->seq = NULL;
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);
351 free(self);
354 bool a2j_start(void)
356 if (g_started)
358 a2j_error("Bridge already started");
359 return false;
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");
368 g_a2j = a2j_new();
369 if (g_a2j == NULL)
371 a2j_error("a2j_new() failed.");
372 return false;
375 a2j_info("Bridge started");
377 #if HAVE_DBUS_1
378 if (a2j_dbus_is_available())
380 a2j_dbus_signal_emit_bridge_started();
382 #endif
384 g_started = true;
386 return true;
389 bool a2j_stop(void)
391 if (!g_started)
393 a2j_error("Bridge already stopped");
394 return false;
397 a2j_info("Bridge stopping...");
399 a2j_destroy(g_a2j);
400 g_a2j = NULL;
402 a2j_info("Bridge stopped");
404 g_started = false;
406 #if HAVE_DBUS_1
407 if (a2j_dbus_is_available())
409 a2j_dbus_signal_emit_bridge_stopped();
411 #endif
413 return true;
416 bool
417 a2j_is_started()
419 return g_started;
422 static
423 void
424 a2j_help(
425 const char * self)
427 a2j_info("Usage: %s [-j jack-server] [-e | --export-hw]", self);
428 a2j_info("Defaults:");
429 a2j_info("-j default");
433 main(
434 int argc,
435 char *argv[])
437 bool dbus;
438 struct stat st;
439 char timestamp_str[26];
441 //test_list_sort();
443 st.st_mtime = 0;
444 stat(argv[0], &st);
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())
452 goto fail;
455 #if HAVE_DBUS_1
456 dbus = argc == 2 && strcmp(argv[1], "dbus") == 0;
457 #else
458 dbus = false;
459 #endif
461 if (!a2j_log_init(dbus))
463 goto fail_paths_uninit;
466 if (!dbus)
468 struct option long_opts[] = { { "export-hw", 0, 0, 'e' }, { 0, 0, 0, 0 } };
470 int option_index = 0;
471 int c;
472 while ((c = getopt_long(argc, argv, "j:eq", long_opts, &option_index)) != -1)
474 switch (c)
476 case 'j':
477 g_a2j_jack_server_name = strdup(optarg);
478 break;
479 case 'e':
480 g_a2j_export_hw_ports = true;
481 break;
482 default:
483 a2j_help(argv[0]);
484 return 1;
488 else
490 //a2j_conf_load();
493 if (dbus)
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");
502 if (dbus)
504 a2j_info("----------------------------");
505 a2j_info("Activated.");
507 else
509 a2j_info("");
512 /* setup our SIGSEGV magic that prints nice stack in our logfile */
513 if (dbus)
515 setup_sigsegv();
518 signal(SIGINT, &a2j_sigint_handler);
519 signal(SIGTERM, &a2j_sigint_handler);
521 if (dbus)
523 #if HAVE_DBUS_1
524 if (!a2j_dbus_init())
526 a2j_error("a2j_dbus_init() failed.");
527 goto fail_uninit_log;
529 #endif
531 else
533 if (!a2j_start())
535 goto fail_uninit_log;
538 a2j_info("Press ctrl-c to stop the bridge");
541 while (g_keep_walking)
543 if (dbus)
545 #if HAVE_DBUS_1
546 if (!a2j_dbus_run(MAIN_LOOP_SLEEP_INTERVAL))
548 a2j_warning("Disconnect message was received from D-Bus.");
549 break;
551 #endif
553 else
555 usleep(MAIN_LOOP_SLEEP_INTERVAL * 1000);
558 if (g_stop_request)
560 g_stop_request = false;
562 a2j_stop();
564 if (!dbus)
566 break;
570 if (g_started)
572 a2j_free_ports(g_a2j->port_del);
573 a2j_update_ports(g_a2j);
577 if (g_started)
579 a2j_stop();
582 #if HAVE_DBUS_1
583 if (dbus)
585 a2j_dbus_uninit();
587 a2j_info("Deactivated.");
588 a2j_info("----------------------------");
590 #endif
592 fail_uninit_log:
593 a2j_log_uninit();
595 fail_paths_uninit:
596 a2j_paths_uninit();
598 fail:
599 return 0;