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
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"
54 #include "../siginfo/siginfo.h"
56 #include "gitversion.h"
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";
79 g_keep_walking
= false;
88 struct a2j_stream
*str
= &self
->stream
[dir
];
90 str
->new_ports
= jack_ringbuffer_create(MAX_PORTS
* sizeof(struct a2j_port
*));
91 if (str
->new_ports
== NULL
)
96 snd_midi_event_new(MAX_EVENT_SIZE
, &str
->codec
);
97 INIT_LIST_HEAD(&str
->list
);
105 struct a2j_stream
* stream_ptr
)
112 struct a2j_stream
* stream_ptr
)
114 struct a2j_port
* port_ptr
;
115 struct list_head
* node_ptr
;
117 while (!list_empty(&stream_ptr
->list
))
119 node_ptr
= stream_ptr
->list
.next
;
121 port_ptr
= list_entry(node_ptr
, struct a2j_port
, siblings
);
122 a2j_info("port deleted: %s", port_ptr
->name
);
123 a2j_port_free(port_ptr
);
133 struct a2j_stream
*str
= &self
->stream
[dir
];
136 snd_midi_event_free(str
->codec
);
138 jack_ringbuffer_free(str
->new_ports
);
141 struct a2j
* a2j_new(void)
144 void * thread_status
;
146 struct a2j
*self
= calloc(1, sizeof(struct a2j
));
147 a2j_debug("midi: new");
150 a2j_error("calloc() failed to allocate a2j struct");
154 self
->port_add
= jack_ringbuffer_create(2 * MAX_PORTS
* sizeof(snd_seq_addr_t
));
155 if (self
->port_add
== NULL
)
160 self
->port_del
= jack_ringbuffer_create(2 * MAX_PORTS
* sizeof(struct a2j_port
*));
161 if (self
->port_del
== NULL
)
163 goto free_ringbuffer_add
;
166 self
->outbound_events
= jack_ringbuffer_create(MAX_EVENT_SIZE
* 16 * sizeof(struct a2j_delivery_event
));
167 if (self
->outbound_events
== NULL
)
169 goto free_ringbuffer_del
;
172 if (!a2j_stream_init(self
, A2J_PORT_CAPTURE
))
174 goto free_ringbuffer_outbound
;
177 if (!a2j_stream_init(self
, A2J_PORT_PLAYBACK
))
179 goto close_capture_stream
;
182 error
= snd_seq_open(&self
->seq
, "hw", SND_SEQ_OPEN_DUPLEX
, 0);
185 a2j_error("failed to open alsa seq");
186 goto close_playback_stream
;
189 error
= snd_seq_set_client_name(self
->seq
, "a2jmidid");
192 a2j_error("snd_seq_set_client_name() failed");
193 goto close_seq_client
;
196 self
->port_id
= snd_seq_create_simple_port(
199 SND_SEQ_PORT_CAP_READ
|SND_SEQ_PORT_CAP_WRITE
201 |SND_SEQ_PORT_CAP_NO_EXPORT
203 ,SND_SEQ_PORT_TYPE_APPLICATION
);
204 if (self
->port_id
< 0)
206 a2j_error("snd_seq_create_simple_port() failed");
207 goto close_seq_client
;
210 self
->client_id
= snd_seq_client_id(self
->seq
);
211 if (self
->client_id
< 0)
213 a2j_error("snd_seq_client_id() failed");
214 goto close_seq_client
;
217 self
->queue
= snd_seq_alloc_queue(self
->seq
);
220 a2j_error("snd_seq_alloc_queue() failed");
221 goto close_seq_client
;
224 snd_seq_start_queue(self
->seq
, self
->queue
, 0);
226 a2j_stream_attach(self
->stream
+ A2J_PORT_CAPTURE
);
227 a2j_stream_attach(self
->stream
+ A2J_PORT_PLAYBACK
);
229 error
= snd_seq_nonblock(self
->seq
, 1);
232 a2j_error("snd_seq_nonblock() failed");
233 goto close_seq_client
;
236 snd_seq_drop_input(self
->seq
);
238 a2j_add_ports(&self
->stream
[A2J_PORT_CAPTURE
]);
239 a2j_add_ports(&self
->stream
[A2J_PORT_PLAYBACK
]);
241 self
->jack_client
= a2j_jack_client_create(self
, A2J_JACK_CLIENT_NAME
, g_a2j_jack_server_name
);
242 if (self
->jack_client
== NULL
)
247 if (sem_init(&self
->io_semaphore
, 0, 0) < 0)
249 a2j_error("can't create IO semaphore");
250 goto close_jack_client
;
253 if (jack_activate(self
->jack_client
))
255 a2j_error("can't activate jack client");
259 g_keep_alsa_walking
= true;
261 if (pthread_create(&self
->alsa_input_thread
, NULL
, a2j_alsa_input_thread
, self
) < 0)
263 a2j_error("cannot start ALSA input thread");
267 /* wake the poll loop in the alsa input thread so initial ports are fetched */
268 error
= snd_seq_connect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
271 a2j_error("snd_seq_connect_from() failed");
272 goto join_input_thread
;
275 if (pthread_create(&self
->alsa_output_thread
, NULL
, a2j_alsa_output_thread
, self
) < 0)
277 a2j_error("cannot start ALSA output thread");
284 g_keep_alsa_walking
= false; /* tell alsa threads to stop */
285 snd_seq_disconnect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
287 pthread_join(self
->alsa_input_thread
, &thread_status
);
289 sem_destroy(&self
->io_semaphore
);
291 error
= jack_client_close(self
->jack_client
);
294 a2j_error("Cannot close jack client");
297 snd_seq_close(self
->seq
);
298 close_playback_stream
:
299 a2j_stream_close(self
, A2J_PORT_PLAYBACK
);
300 close_capture_stream
:
301 a2j_stream_close(self
, A2J_PORT_CAPTURE
);
302 free_ringbuffer_outbound
:
303 jack_ringbuffer_free(self
->outbound_events
);
305 jack_ringbuffer_free(self
->port_del
);
307 jack_ringbuffer_free(self
->port_add
);
314 static void a2j_destroy(struct a2j
* self
)
317 void * thread_status
;
319 a2j_debug("midi: delete");
321 g_keep_alsa_walking
= false; /* tell alsa threads to stop */
323 /* do something that we need to do anyway and will wake the input thread, then join */
324 snd_seq_disconnect_from(self
->seq
, self
->port_id
, SND_SEQ_CLIENT_SYSTEM
, SND_SEQ_PORT_SYSTEM_ANNOUNCE
);
325 pthread_join(self
->alsa_input_thread
, &thread_status
);
327 /* wake output thread and join, then destroy the semaphore */
328 sem_post(&self
->io_semaphore
);
329 pthread_join(self
->alsa_output_thread
, &thread_status
);
330 sem_destroy(&self
->io_semaphore
);
332 jack_ringbuffer_reset(self
->port_add
);
334 jack_deactivate(self
->jack_client
);
336 a2j_stream_detach(self
->stream
+ A2J_PORT_CAPTURE
);
337 a2j_stream_detach(self
->stream
+ A2J_PORT_PLAYBACK
);
339 error
= jack_client_close(self
->jack_client
);
342 a2j_error("Cannot close jack client (%d)", error
);
345 snd_seq_close(self
->seq
);
348 a2j_stream_close(self
, A2J_PORT_PLAYBACK
);
349 a2j_stream_close(self
, A2J_PORT_CAPTURE
);
351 jack_ringbuffer_free(self
->outbound_events
);
352 jack_ringbuffer_free(self
->port_add
);
353 jack_ringbuffer_free(self
->port_del
);
362 a2j_error("Bridge already started");
366 a2j_info("Bridge starting...");
368 a2j_info("Using JACK server '%s'", g_a2j_jack_server_name
);
370 a2j_info("Hardware ports %s be exported.", g_a2j_export_hw_ports
? "will": "will not");
375 a2j_error("a2j_new() failed.");
379 a2j_info("Bridge started");
382 if (a2j_dbus_is_available())
384 a2j_dbus_signal_emit_bridge_started();
397 a2j_error("Bridge already stopped");
401 a2j_info("Bridge stopping...");
406 a2j_info("Bridge stopped");
411 if (a2j_dbus_is_available())
413 a2j_dbus_signal_emit_bridge_stopped();
431 a2j_info("Usage: %s [-j jack-server] [-e | --export-hw] [-u] [-n]", self
);
432 a2j_info("Defaults:");
433 a2j_info("-j default");
443 char timestamp_str
[26];
449 ctime_r(&st
.st_mtime
, timestamp_str
);
450 timestamp_str
[24] = 0;
452 g_max_jack_port_name_size
= jack_port_name_size();
454 if (!a2j_paths_init())
460 dbus
= argc
== 2 && strcmp(argv
[1], "dbus") == 0;
465 if (!a2j_log_init(dbus
))
467 goto fail_paths_uninit
;
472 struct option long_opts
[] = { { "export-hw", 0, 0, 'e' }, { 0, 0, 0, 0 } };
474 int option_index
= 0;
476 while ((c
= getopt_long(argc
, argv
, "j:eu", long_opts
, &option_index
)) != -1)
481 g_a2j_jack_server_name
= strdup(optarg
);
484 g_a2j_export_hw_ports
= true;
487 g_disable_port_uniqueness
= true;
490 g_filter_note_on
= false;
505 a2j_info("----------------------------");
508 a2j_info("JACK MIDI <-> ALSA sequencer MIDI bridge, version " A2J_VERSION
509 #if (HAVE_GITVERSION_H)
512 " built on %s", timestamp_str
);
513 a2j_info("Copyright 2006,2007 Dmitry S. Baikov");
514 a2j_info("Copyright 2007,2008,2009,2011,2012 Nedko Arnaudov");
518 a2j_info("----------------------------");
519 a2j_info("Activated.");
526 /* setup our SIGSEGV magic that prints nice stack in our logfile */
532 signal(SIGINT
, &a2j_sigint_handler
);
533 signal(SIGTERM
, &a2j_sigint_handler
);
538 if (!a2j_dbus_init())
540 a2j_error("a2j_dbus_init() failed.");
541 goto fail_uninit_log
;
549 goto fail_uninit_log
;
552 a2j_info("Press ctrl-c to stop the bridge");
555 while (g_keep_walking
)
560 if (!a2j_dbus_run(MAIN_LOOP_SLEEP_INTERVAL
))
562 a2j_warning("Disconnect message was received from D-Bus.");
569 usleep(MAIN_LOOP_SLEEP_INTERVAL
* 1000);
574 g_stop_request
= false;
586 a2j_free_ports(g_a2j
->port_del
);
587 a2j_update_ports(g_a2j
);
601 a2j_info("Deactivated.");
602 a2j_info("----------------------------");