website/index.html: Extend Feedback section (codeberg.org)
[a2jmidid.git] / a2jmidid.c
blobaa864644e69dc658bc0aac94bc1baf6236d382ce
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 "siginfo/siginfo.h"
55 #if HAVE_GITVERSION_H
56 #include "gitversion.h"
57 #endif
58 #include "dbus_iface_control.h"
60 #define MAIN_LOOP_SLEEP_INTERVAL 50 // in milliseconds
62 bool g_keep_walking = true;
63 bool g_keep_alsa_walking = false;
64 bool g_stop_request = false;
65 static bool g_started = false;
66 struct a2j * g_a2j = NULL;
67 size_t g_max_jack_port_name_size;
68 bool g_disable_port_uniqueness = false;
69 bool g_filter_note_on = true;
71 bool g_a2j_export_hw_ports = false;
72 char * g_a2j_jack_server_name = "default";
74 static
75 void
76 a2j_sigint_handler(
77 int i)
79 ((void)(i)); /* unreferenced parameter */
80 g_keep_walking = false;
83 static
84 bool
85 a2j_stream_init(
86 struct a2j * self,
87 int dir)
89 struct a2j_stream *str = &self->stream[dir];
91 str->new_ports = jack_ringbuffer_create(MAX_PORTS * sizeof(struct a2j_port *));
92 if (str->new_ports == NULL)
94 return false;
97 snd_midi_event_new(MAX_EVENT_SIZE, &str->codec);
98 INIT_LIST_HEAD(&str->list);
100 return true;
103 static
104 void
105 a2j_stream_attach(
106 struct a2j_stream * stream_ptr)
108 ((void)(stream_ptr)); /* unreferenced parameter */
111 static
112 void
113 a2j_stream_detach(
114 struct a2j_stream * stream_ptr)
116 struct a2j_port * port_ptr;
117 struct list_head * node_ptr;
119 while (!list_empty(&stream_ptr->list))
121 node_ptr = stream_ptr->list.next;
122 list_del(node_ptr);
123 port_ptr = list_entry(node_ptr, struct a2j_port, siblings);
124 a2j_info("port deleted: %s", port_ptr->name);
125 a2j_port_free(port_ptr);
129 static
130 void
131 a2j_stream_close(
132 struct a2j * self,
133 int dir)
135 struct a2j_stream *str = &self->stream[dir];
137 if (str->codec)
138 snd_midi_event_free(str->codec);
139 if (str->new_ports)
140 jack_ringbuffer_free(str->new_ports);
143 struct a2j * a2j_new(void)
145 int error;
146 void * thread_status;
148 struct a2j *self = calloc(1, sizeof(struct a2j));
149 a2j_debug("midi: new");
150 if (!self)
152 a2j_error("calloc() failed to allocate a2j struct");
153 goto fail;
156 self->port_add = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(snd_seq_addr_t));
157 if (self->port_add == NULL)
159 goto free_self;
162 self->port_del = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(struct a2j_port *));
163 if (self->port_del == NULL)
165 goto free_ringbuffer_add;
168 self->outbound_events = jack_ringbuffer_create(MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event));
169 if (self->outbound_events == NULL)
171 goto free_ringbuffer_del;
174 if (!a2j_stream_init(self, A2J_PORT_CAPTURE))
176 goto free_ringbuffer_outbound;
179 if (!a2j_stream_init(self, A2J_PORT_PLAYBACK))
181 goto close_capture_stream;
184 error = snd_seq_open(&self->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0);
185 if (error < 0)
187 a2j_error("failed to open alsa seq");
188 goto close_playback_stream;
191 error = snd_seq_set_client_name(self->seq, "a2jmidid");
192 if (error < 0)
194 a2j_error("snd_seq_set_client_name() failed");
195 goto close_seq_client;
198 self->port_id = snd_seq_create_simple_port(
199 self->seq,
200 "port",
201 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE
202 #ifndef DEBUG
203 |SND_SEQ_PORT_CAP_NO_EXPORT
204 #endif
205 ,SND_SEQ_PORT_TYPE_APPLICATION);
206 if (self->port_id < 0)
208 a2j_error("snd_seq_create_simple_port() failed");
209 goto close_seq_client;
212 self->client_id = snd_seq_client_id(self->seq);
213 if (self->client_id < 0)
215 a2j_error("snd_seq_client_id() failed");
216 goto close_seq_client;
219 self->queue = snd_seq_alloc_queue(self->seq);
220 if (self->queue < 0)
222 a2j_error("snd_seq_alloc_queue() failed");
223 goto close_seq_client;
226 snd_seq_start_queue(self->seq, self->queue, 0);
228 a2j_stream_attach(self->stream + A2J_PORT_CAPTURE);
229 a2j_stream_attach(self->stream + A2J_PORT_PLAYBACK);
231 error = snd_seq_nonblock(self->seq, 1);
232 if (error < 0)
234 a2j_error("snd_seq_nonblock() failed");
235 goto close_seq_client;
238 snd_seq_drop_input(self->seq);
240 a2j_add_ports(&self->stream[A2J_PORT_CAPTURE]);
241 a2j_add_ports(&self->stream[A2J_PORT_PLAYBACK]);
243 self->jack_client = a2j_jack_client_create(self, A2J_JACK_CLIENT_NAME, g_a2j_jack_server_name);
244 if (self->jack_client == NULL)
246 goto free_self;
249 if (sem_init(&self->io_semaphore, 0, 0) < 0)
251 a2j_error("can't create IO semaphore");
252 goto close_jack_client;
255 if (jack_activate(self->jack_client))
257 a2j_error("can't activate jack client");
258 goto sem_destroy;
261 g_keep_alsa_walking = true;
263 if (pthread_create(&self->alsa_input_thread, NULL, a2j_alsa_input_thread, self) < 0)
265 a2j_error("cannot start ALSA input thread");
266 goto sem_destroy;
269 /* wake the poll loop in the alsa input thread so initial ports are fetched */
270 error = snd_seq_connect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
271 if (error < 0)
273 a2j_error("snd_seq_connect_from() failed");
274 goto join_input_thread;
277 if (pthread_create(&self->alsa_output_thread, NULL, a2j_alsa_output_thread, self) < 0)
279 a2j_error("cannot start ALSA output thread");
280 goto disconnect;
283 return self;
285 disconnect:
286 g_keep_alsa_walking = false; /* tell alsa threads to stop */
287 snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
288 join_input_thread:
289 pthread_join(self->alsa_input_thread, &thread_status);
290 sem_destroy:
291 sem_destroy(&self->io_semaphore);
292 close_jack_client:
293 error = jack_client_close(self->jack_client);
294 if (error != 0)
296 a2j_error("Cannot close jack client");
298 close_seq_client:
299 snd_seq_close(self->seq);
300 close_playback_stream:
301 a2j_stream_close(self, A2J_PORT_PLAYBACK);
302 close_capture_stream:
303 a2j_stream_close(self, A2J_PORT_CAPTURE);
304 free_ringbuffer_outbound:
305 jack_ringbuffer_free(self->outbound_events);
306 free_ringbuffer_del:
307 jack_ringbuffer_free(self->port_del);
308 free_ringbuffer_add:
309 jack_ringbuffer_free(self->port_add);
310 free_self:
311 free(self);
312 fail:
313 return NULL;
316 static void a2j_destroy(struct a2j * self)
318 int error;
319 void * thread_status;
321 a2j_debug("midi: delete");
323 g_keep_alsa_walking = false; /* tell alsa threads to stop */
325 /* do something that we need to do anyway and will wake the input thread, then join */
326 snd_seq_disconnect_from(self->seq, self->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
327 pthread_join(self->alsa_input_thread, &thread_status);
329 /* wake output thread and join, then destroy the semaphore */
330 sem_post(&self->io_semaphore);
331 pthread_join(self->alsa_output_thread, &thread_status);
332 sem_destroy(&self->io_semaphore);
334 jack_ringbuffer_reset(self->port_add);
336 jack_deactivate(self->jack_client);
338 a2j_stream_detach(self->stream + A2J_PORT_CAPTURE);
339 a2j_stream_detach(self->stream + A2J_PORT_PLAYBACK);
341 error = jack_client_close(self->jack_client);
342 if (error != 0)
344 a2j_error("Cannot close jack client (%d)", error);
347 snd_seq_close(self->seq);
348 self->seq = NULL;
350 a2j_stream_close(self, A2J_PORT_PLAYBACK);
351 a2j_stream_close(self, A2J_PORT_CAPTURE);
353 jack_ringbuffer_free(self->outbound_events);
354 jack_ringbuffer_free(self->port_add);
355 jack_ringbuffer_free(self->port_del);
357 free(self);
360 bool a2j_start(void)
362 if (g_started)
364 a2j_error("Bridge already started");
365 return false;
368 a2j_info("Bridge starting...");
370 a2j_info("Using JACK server '%s'", g_a2j_jack_server_name);
372 a2j_info("Hardware ports %s be exported.", g_a2j_export_hw_ports ? "will": "will not");
374 g_a2j = a2j_new();
375 if (g_a2j == NULL)
377 a2j_error("a2j_new() failed.");
378 return false;
381 a2j_info("Bridge started");
383 #if HAVE_DBUS_1
384 if (a2j_dbus_is_available())
386 a2j_dbus_signal_emit_bridge_started();
388 #endif
390 g_started = true;
392 return true;
395 bool a2j_stop(void)
397 if (!g_started)
399 a2j_error("Bridge already stopped");
400 return false;
403 a2j_info("Bridge stopping...");
405 a2j_destroy(g_a2j);
406 g_a2j = NULL;
408 a2j_info("Bridge stopped");
410 g_started = false;
412 #if HAVE_DBUS_1
413 if (a2j_dbus_is_available())
415 a2j_dbus_signal_emit_bridge_stopped();
417 #endif
419 return true;
422 bool
423 a2j_is_started(void)
425 return g_started;
428 static
429 void
430 a2j_help(
431 const char * self)
433 a2j_info("Usage: %s [-j jack-server] [-e | --export-hw] [-u] [-n]", self);
434 a2j_info("Defaults:");
435 a2j_info("-j default");
439 main(
440 int argc,
441 char *argv[])
443 bool dbus;
444 struct stat st;
445 char timestamp_str[26];
447 //test_list_sort();
449 st.st_mtime = 0;
450 stat(argv[0], &st);
451 ctime_r(&st.st_mtime, timestamp_str);
452 timestamp_str[24] = 0;
454 g_max_jack_port_name_size = jack_port_name_size();
456 if (!a2j_paths_init())
458 goto fail;
461 #if HAVE_DBUS_1
462 dbus = argc == 2 && strcmp(argv[1], "dbus") == 0;
463 #else
464 dbus = false;
465 #endif
467 if (!a2j_log_init(dbus))
469 goto fail_paths_uninit;
472 if (!dbus)
474 struct option long_opts[] = { { "export-hw", 0, 0, 'e' }, { 0, 0, 0, 0 } };
476 int option_index = 0;
477 int c;
478 while ((c = getopt_long(argc, argv, "j:eu", long_opts, &option_index)) != -1)
480 switch (c)
482 case 'j':
483 g_a2j_jack_server_name = strdup(optarg);
484 break;
485 case 'e':
486 g_a2j_export_hw_ports = true;
487 break;
488 case 'u':
489 g_disable_port_uniqueness = true;
490 break;
491 case 'n':
492 g_filter_note_on = false;
493 break;
494 default:
495 a2j_help(argv[0]);
496 return 1;
500 else
502 //a2j_conf_load();
505 if (dbus)
507 a2j_info("----------------------------");
510 a2j_info("JACK MIDI <-> ALSA sequencer MIDI bridge, version " A2J_VERSION
511 #if (HAVE_GITVERSION_H)
512 " (" GIT_VERSION ")"
513 #endif
514 " built on %s", timestamp_str);
515 a2j_info("Copyright 2006,2007 Dmitry S. Baikov");
516 a2j_info("Copyright 2007,2008,2009,2011,2012 Nedko Arnaudov");
518 if (dbus)
520 a2j_info("----------------------------");
521 a2j_info("Activated.");
523 else
525 a2j_info("");
528 /* setup our SIGSEGV magic that prints nice stack in our logfile */
529 if (dbus)
531 setup_siginfo();
534 signal(SIGINT, &a2j_sigint_handler);
535 signal(SIGTERM, &a2j_sigint_handler);
537 if (dbus)
539 #if HAVE_DBUS_1
540 if (!a2j_dbus_init())
542 a2j_error("a2j_dbus_init() failed.");
543 goto fail_uninit_log;
545 #endif
547 else
549 if (!a2j_start())
551 goto fail_uninit_log;
554 a2j_info("Press ctrl-c to stop the bridge");
557 while (g_keep_walking)
559 if (dbus)
561 #if HAVE_DBUS_1
562 if (!a2j_dbus_run(MAIN_LOOP_SLEEP_INTERVAL))
564 a2j_warning("Disconnect message was received from D-Bus.");
565 break;
567 #endif
569 else
571 usleep(MAIN_LOOP_SLEEP_INTERVAL * 1000);
574 if (g_stop_request)
576 g_stop_request = false;
578 a2j_stop();
580 if (!dbus)
582 break;
586 if (g_started)
588 a2j_free_ports(g_a2j->port_del);
589 a2j_update_ports(g_a2j);
593 if (g_started)
595 a2j_stop();
598 #if HAVE_DBUS_1
599 if (dbus)
601 a2j_dbus_uninit();
603 a2j_info("Deactivated.");
604 a2j_info("----------------------------");
606 #endif
608 fail_uninit_log:
609 a2j_log_uninit();
611 fail_paths_uninit:
612 a2j_paths_uninit();
614 fail:
615 return 0;