gladish: quit on ctrl-q
[ladish.git] / jmcore.c
blobc3fd55b214af805d8cf903c723cb16ee494c70fd
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010,2011,2012 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the JACK multicore (snake)
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "common.h"
29 #include <unistd.h>
30 #include <signal.h>
31 #include <jack/jack.h>
32 #include <jack/midiport.h>
34 #include "cdbus/helpers.h"
35 #include "dbus_constants.h"
37 extern const struct cdbus_interface_descriptor g_interface;
39 static const char * g_dbus_unique_name;
40 static cdbus_object_path g_object;
41 static bool g_quit;
42 static unsigned int g_unique_index;
44 struct list_head g_pairs;
46 struct port_pair
48 struct list_head siblings;
49 jack_client_t * client;
50 bool dead;
51 bool midi;
52 jack_port_t * input_port;
53 jack_port_t * output_port;
54 char * input_port_name;
55 char * output_port_name;
58 #define pair_ptr ((struct port_pair *)arg)
60 void shutdown_callback(void * arg)
62 pair_ptr->dead = true;
65 int process_callback(jack_nframes_t nframes, void * arg)
67 void * input;
68 void * output;
69 jack_midi_event_t midi_event;
70 jack_nframes_t midi_event_index;
72 input = jack_port_get_buffer(pair_ptr->input_port, nframes);
73 output = jack_port_get_buffer(pair_ptr->output_port, nframes);
75 if (!pair_ptr->midi)
77 memcpy(output, input, nframes * sizeof(jack_default_audio_sample_t));
79 else
81 jack_midi_clear_buffer(output);
82 midi_event_index = 0;
83 while (jack_midi_event_get(&midi_event, input, midi_event_index) == 0)
85 jack_midi_event_write(output, midi_event.time, midi_event.buffer, midi_event.size);
86 midi_event_index++;
90 return 0;
93 #undef pair_ptr
95 static void destroy_pair(struct port_pair * pair_ptr)
97 list_del(&pair_ptr->siblings);
98 jack_client_close(pair_ptr->client);
99 free(pair_ptr->input_port_name);
100 free(pair_ptr->output_port_name);
101 free(pair_ptr);
104 static void bury_zombie_pairs(void)
106 struct list_head * node_ptr;
107 struct list_head * temp_node_ptr;
108 struct port_pair * pair_ptr;
110 list_for_each_safe(node_ptr, temp_node_ptr, &g_pairs)
112 pair_ptr = list_entry(node_ptr, struct port_pair, siblings);
113 if (pair_ptr->dead)
115 log_info("Bury zombie '%s':'%s'", pair_ptr->input_port_name, pair_ptr->output_port_name);
116 destroy_pair(pair_ptr);
121 static bool connect_dbus(void)
123 int ret;
125 dbus_error_init(&cdbus_g_dbus_error);
127 cdbus_g_dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &cdbus_g_dbus_error);
128 if (dbus_error_is_set(&cdbus_g_dbus_error))
130 log_error("Failed to get bus: %s", cdbus_g_dbus_error.message);
131 dbus_error_free(&cdbus_g_dbus_error);
132 goto fail;
135 g_dbus_unique_name = dbus_bus_get_unique_name(cdbus_g_dbus_connection);
136 if (g_dbus_unique_name == NULL)
138 log_error("Failed to read unique bus name");
139 goto unref_connection;
142 log_info("Connected to local session bus, unique name is \"%s\"", g_dbus_unique_name);
144 ret = dbus_bus_request_name(cdbus_g_dbus_connection, JMCORE_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &cdbus_g_dbus_error);
145 if (ret == -1)
147 log_error("Failed to acquire bus name: %s", cdbus_g_dbus_error.message);
148 dbus_error_free(&cdbus_g_dbus_error);
149 goto unref_connection;
152 if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
154 log_error("Requested connection name already exists");
155 goto unref_connection;
158 g_object = cdbus_object_path_new(JMCORE_OBJECT_PATH, &g_interface, NULL, NULL);
159 if (g_object == NULL)
161 goto unref_connection;
164 if (!cdbus_object_path_register(cdbus_g_dbus_connection, g_object))
166 goto destroy_control_object;
169 return true;
171 destroy_control_object:
172 cdbus_object_path_destroy(cdbus_g_dbus_connection, g_object);
173 unref_connection:
174 dbus_connection_unref(cdbus_g_dbus_connection);
176 fail:
177 return false;
180 static void disconnect_dbus(void)
182 cdbus_object_path_destroy(cdbus_g_dbus_connection, g_object);
183 dbus_connection_unref(cdbus_g_dbus_connection);
186 void term_signal_handler(int signum)
188 log_info("Caught signal %d (%s), terminating", signum, strsignal(signum));
189 g_quit = true;
192 bool install_term_signal_handler(int signum, bool ignore_if_already_ignored)
194 sig_t sigh;
196 sigh = signal(signum, term_signal_handler);
197 if (sigh == SIG_ERR)
199 log_error("signal() failed to install handler function for signal %d.", signum);
200 return false;
203 if (sigh == SIG_IGN && ignore_if_already_ignored)
205 signal(SIGTERM, SIG_IGN);
208 return true;
211 int main(int UNUSED(argc), char ** UNUSED(argv))
213 INIT_LIST_HEAD(&g_pairs);
215 install_term_signal_handler(SIGTERM, false);
216 install_term_signal_handler(SIGINT, true);
218 if (!connect_dbus())
220 log_error("Failed to connect to D-Bus");
221 return 1;
224 while (!g_quit)
226 dbus_connection_read_write_dispatch(cdbus_g_dbus_connection, 50);
227 bury_zombie_pairs();
230 while (!list_empty(&g_pairs))
232 destroy_pair(list_entry(g_pairs.next, struct port_pair, siblings));
235 disconnect_dbus();
236 return 0;
239 /***************************************************************************/
240 /* D-Bus interface implementation */
242 static void jmcore_get_pid(struct cdbus_method_call * call_ptr)
244 dbus_int64_t pid;
246 pid = getpid();
248 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_INT64, &pid);
251 static void jmcore_create(struct cdbus_method_call * call_ptr)
253 dbus_bool_t midi;
254 const char * input;
255 const char * output;
256 struct port_pair * pair_ptr;
257 int ret;
258 char client_name[256];
260 g_unique_index++;
261 sprintf(client_name, "jmcore-%u", g_unique_index);
263 dbus_error_init(&cdbus_g_dbus_error);
264 if (!dbus_message_get_args(
265 call_ptr->message,
266 &cdbus_g_dbus_error,
267 DBUS_TYPE_BOOLEAN, &midi,
268 DBUS_TYPE_STRING, &input,
269 DBUS_TYPE_STRING, &output,
270 DBUS_TYPE_INVALID))
272 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
273 dbus_error_free(&cdbus_g_dbus_error);
274 goto exit;
277 pair_ptr = malloc(sizeof(struct port_pair));
278 if (pair_ptr == NULL)
280 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Allocation of port pair structure failed");
281 goto exit;
284 pair_ptr->input_port_name = strdup(input);
285 if (pair_ptr->input_port_name == NULL)
287 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Allocation of port name buffer failed");
288 goto free_pair;
291 pair_ptr->output_port_name = strdup(output);
292 if (pair_ptr->input_port_name == NULL)
294 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Allocation of port name buffer failed");
295 goto free_input_name;
298 pair_ptr->client = jack_client_open(client_name, JackNoStartServer, NULL);
299 if (pair_ptr->client == NULL)
301 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Cannot connect to JACK server");
302 goto free_output_name;
305 pair_ptr->midi = midi;
306 pair_ptr->dead = false;
308 ret = jack_set_process_callback(pair_ptr->client, process_callback, pair_ptr);
309 if (ret != 0)
311 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "JACK process callback setup failed");
312 goto close_client;
315 jack_on_shutdown(pair_ptr->client, shutdown_callback, pair_ptr);
317 pair_ptr->input_port = jack_port_register(pair_ptr->client, input, midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
318 if (pair_ptr->input_port == NULL)
320 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Port '%s' registration failed.", input);
321 goto free_output_name;
324 pair_ptr->output_port = jack_port_register(pair_ptr->client, output, midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
325 if (pair_ptr->input_port == NULL)
327 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Port '%s' registration failed.", output);
328 goto unregister_input_port;
331 list_add_tail(&pair_ptr->siblings, &g_pairs);
333 ret = jack_activate(pair_ptr->client);
334 if (ret != 0)
336 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "JACK client activation failed");
337 goto remove_from_list;
340 cdbus_method_return_new_void(call_ptr);
341 goto exit;
343 remove_from_list:
344 list_del(&pair_ptr->siblings);
345 //unregister_output_port:
346 jack_port_unregister(pair_ptr->client, pair_ptr->output_port);
347 unregister_input_port:
348 jack_port_unregister(pair_ptr->client, pair_ptr->input_port);
349 close_client:
350 jack_client_close(pair_ptr->client);
351 free_output_name:
352 free(pair_ptr->output_port_name);
353 free_input_name:
354 free(pair_ptr->input_port_name);
355 free_pair:
356 free(pair_ptr);
357 exit:
358 return;
361 static void jmcore_destroy(struct cdbus_method_call * call_ptr)
363 const char * port;
364 struct list_head * node_ptr;
365 struct port_pair * pair_ptr;
367 dbus_error_init(&cdbus_g_dbus_error);
368 if (!dbus_message_get_args(call_ptr->message, &cdbus_g_dbus_error, DBUS_TYPE_STRING, &port, DBUS_TYPE_INVALID))
370 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
371 dbus_error_free(&cdbus_g_dbus_error);
372 return;
375 list_for_each(node_ptr, &g_pairs)
377 pair_ptr = list_entry(node_ptr, struct port_pair, siblings);
378 if (strcmp(pair_ptr->input_port_name, port) == 0 ||
379 strcmp(pair_ptr->output_port_name, port) == 0)
381 destroy_pair(pair_ptr);
382 cdbus_method_return_new_void(call_ptr);
383 return;
387 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "port '%s' not found.", port);
388 return;
391 static void jmcore_exit(struct cdbus_method_call * call_ptr)
393 log_info("Exit command received through D-Bus");
394 g_quit = true;
395 cdbus_method_return_new_void(call_ptr);
398 CDBUS_METHOD_ARGS_BEGIN(get_pid, "Get process ID")
399 CDBUS_METHOD_ARG_DESCRIBE_OUT("process_id", DBUS_TYPE_INT64_AS_STRING, "Process ID")
400 CDBUS_METHOD_ARGS_END
402 CDBUS_METHOD_ARGS_BEGIN(create, "Create port pair")
403 CDBUS_METHOD_ARG_DESCRIBE_IN("midi", "b", "Whether to create midi or audio ports")
404 CDBUS_METHOD_ARG_DESCRIBE_IN("input_port", "s", "Input port name")
405 CDBUS_METHOD_ARG_DESCRIBE_IN("output_port", "s", "Output port name")
406 CDBUS_METHOD_ARGS_END
408 CDBUS_METHOD_ARGS_BEGIN(destroy, "Destroy port pair")
409 CDBUS_METHOD_ARG_DESCRIBE_IN("port", "s", "Port name")
410 CDBUS_METHOD_ARGS_END
412 CDBUS_METHOD_ARGS_BEGIN(exit, "Tell jmcore D-Bus service to exit")
413 CDBUS_METHOD_ARGS_END
415 CDBUS_METHODS_BEGIN
416 CDBUS_METHOD_DESCRIBE(get_pid, jmcore_get_pid)
417 CDBUS_METHOD_DESCRIBE(create, jmcore_create)
418 CDBUS_METHOD_DESCRIBE(destroy, jmcore_destroy)
419 CDBUS_METHOD_DESCRIBE(exit, jmcore_exit)
420 CDBUS_METHODS_END
422 CDBUS_INTERFACE_DEFAULT_HANDLER_METHODS_ONLY(g_interface, JMCORE_IFACE)