Sync translations
[ladish.git] / jmcore.c
blob3839c2d7ec27303464b705895dacc991b23c9207
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010 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 "dbus/helpers.h"
35 #include "dbus/error.h"
36 #include "dbus_constants.h"
38 extern const struct dbus_interface_descriptor g_interface;
40 static const char * g_dbus_unique_name;
41 static dbus_object_path g_object;
42 static bool g_quit;
43 static unsigned int g_unique_index;
45 struct list_head g_pairs;
47 struct port_pair
49 struct list_head siblings;
50 jack_client_t * client;
51 bool dead;
52 bool midi;
53 jack_port_t * input_port;
54 jack_port_t * output_port;
55 char * input_port_name;
56 char * output_port_name;
59 #define pair_ptr ((struct port_pair *)arg)
61 void shutdown_callback(void * arg)
63 pair_ptr->dead = true;
66 int process_callback(jack_nframes_t nframes, void * arg)
68 void * input;
69 void * output;
70 jack_midi_event_t midi_event;
71 jack_nframes_t midi_event_index;
73 input = jack_port_get_buffer(pair_ptr->input_port, nframes);
74 output = jack_port_get_buffer(pair_ptr->output_port, nframes);
76 if (!pair_ptr->midi)
78 memcpy(output, input, nframes * sizeof(jack_default_audio_sample_t));
80 else
82 jack_midi_clear_buffer(output);
83 midi_event_index = 0;
84 while (jack_midi_event_get(&midi_event, input, midi_event_index) == 0)
86 jack_midi_event_write(output, midi_event.time, midi_event.buffer, midi_event.size);
87 midi_event_index++;
91 return 0;
94 #undef pair_ptr
96 static void destroy_pair(struct port_pair * pair_ptr)
98 list_del(&pair_ptr->siblings);
99 jack_client_close(pair_ptr->client);
100 free(pair_ptr->input_port_name);
101 free(pair_ptr->output_port_name);
102 free(pair_ptr);
105 static void bury_zombie_pairs(void)
107 struct list_head * node_ptr;
108 struct list_head * temp_node_ptr;
109 struct port_pair * pair_ptr;
111 list_for_each_safe(node_ptr, temp_node_ptr, &g_pairs)
113 pair_ptr = list_entry(node_ptr, struct port_pair, siblings);
114 if (pair_ptr->dead)
116 log_info("Bury zombie '%s':'%s'", pair_ptr->input_port_name, pair_ptr->output_port_name);
117 destroy_pair(pair_ptr);
122 static bool connect_dbus(void)
124 int ret;
126 dbus_error_init(&g_dbus_error);
128 g_dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &g_dbus_error);
129 if (dbus_error_is_set(&g_dbus_error))
131 log_error("Failed to get bus: %s", g_dbus_error.message);
132 dbus_error_free(&g_dbus_error);
133 goto fail;
136 g_dbus_unique_name = dbus_bus_get_unique_name(g_dbus_connection);
137 if (g_dbus_unique_name == NULL)
139 log_error("Failed to read unique bus name");
140 goto unref_connection;
143 log_info("Connected to local session bus, unique name is \"%s\"", g_dbus_unique_name);
145 ret = dbus_bus_request_name(g_dbus_connection, JMCORE_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &g_dbus_error);
146 if (ret == -1)
148 log_error("Failed to acquire bus name: %s", g_dbus_error.message);
149 dbus_error_free(&g_dbus_error);
150 goto unref_connection;
153 if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
155 log_error("Requested connection name already exists");
156 goto unref_connection;
159 g_object = dbus_object_path_new(JMCORE_OBJECT_PATH, &g_interface, NULL, NULL);
160 if (g_object == NULL)
162 goto unref_connection;
165 if (!dbus_object_path_register(g_dbus_connection, g_object))
167 goto destroy_control_object;
170 return true;
172 destroy_control_object:
173 dbus_object_path_destroy(g_dbus_connection, g_object);
174 unref_connection:
175 dbus_connection_unref(g_dbus_connection);
177 fail:
178 return false;
181 static void disconnect_dbus(void)
183 dbus_object_path_destroy(g_dbus_connection, g_object);
184 dbus_connection_unref(g_dbus_connection);
187 void term_signal_handler(int signum)
189 log_info("Caught signal %d (%s), terminating", signum, strsignal(signum));
190 g_quit = true;
193 bool install_term_signal_handler(int signum, bool ignore_if_already_ignored)
195 sig_t sigh;
197 sigh = signal(signum, term_signal_handler);
198 if (sigh == SIG_ERR)
200 log_error("signal() failed to install handler function for signal %d.", signum);
201 return false;
204 if (sigh == SIG_IGN && ignore_if_already_ignored)
206 signal(SIGTERM, SIG_IGN);
209 return true;
212 int main(int argc, char ** argv)
214 int ret;
216 INIT_LIST_HEAD(&g_pairs);
218 install_term_signal_handler(SIGTERM, false);
219 install_term_signal_handler(SIGINT, true);
221 if (!connect_dbus())
223 log_error("Failed to connect to D-Bus");
224 return 1;
227 while (!g_quit)
229 dbus_connection_read_write_dispatch(g_dbus_connection, 50);
230 bury_zombie_pairs();
233 ret = 0;
235 while (!list_empty(&g_pairs))
237 destroy_pair(list_entry(g_pairs.next, struct port_pair, siblings));
240 disconnect_dbus();
241 return 0;
244 /***************************************************************************/
245 /* D-Bus interface implementation */
247 static void jmcore_get_pid(struct dbus_method_call * call_ptr)
249 dbus_int64_t pid;
251 pid = getpid();
253 method_return_new_single(call_ptr, DBUS_TYPE_INT64, &pid);
256 static void jmcore_create(struct dbus_method_call * call_ptr)
258 dbus_bool_t midi;
259 const char * input;
260 const char * output;
261 struct port_pair * pair_ptr;
262 int ret;
263 char client_name[256];
265 g_unique_index++;
266 sprintf(client_name, "jmcore-%u", g_unique_index);
268 dbus_error_init(&g_dbus_error);
269 if (!dbus_message_get_args(
270 call_ptr->message,
271 &g_dbus_error,
272 DBUS_TYPE_BOOLEAN, &midi,
273 DBUS_TYPE_STRING, &input,
274 DBUS_TYPE_STRING, &output,
275 DBUS_TYPE_INVALID))
277 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
278 dbus_error_free(&g_dbus_error);
279 goto exit;
282 pair_ptr = malloc(sizeof(struct port_pair));
283 if (pair_ptr == NULL)
285 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Allocation of port pair structure failed");
286 goto exit;
289 pair_ptr->input_port_name = strdup(input);
290 if (pair_ptr->input_port_name == NULL)
292 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Allocation of port name buffer failed");
293 goto free_pair;
296 pair_ptr->output_port_name = strdup(output);
297 if (pair_ptr->input_port_name == NULL)
299 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Allocation of port name buffer failed");
300 goto free_input_name;
303 pair_ptr->client = jack_client_open(client_name, JackNoStartServer, NULL);
304 if (pair_ptr->client == NULL)
306 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot connect to JACK server");
307 goto free_output_name;
310 pair_ptr->midi = midi;
311 pair_ptr->dead = false;
313 ret = jack_set_process_callback(pair_ptr->client, process_callback, pair_ptr);
314 if (ret != 0)
316 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "JACK process callback setup failed");
317 goto close_client;
320 jack_on_shutdown(pair_ptr->client, shutdown_callback, pair_ptr);
322 pair_ptr->input_port = jack_port_register(pair_ptr->client, input, midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
323 if (pair_ptr->input_port == NULL)
325 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Port '%s' registration failed.", input);
326 goto free_output_name;
329 pair_ptr->output_port = jack_port_register(pair_ptr->client, output, midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
330 if (pair_ptr->input_port == NULL)
332 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Port '%s' registration failed.", output);
333 goto unregister_input_port;
336 list_add_tail(&pair_ptr->siblings, &g_pairs);
338 ret = jack_activate(pair_ptr->client);
339 if (ret != 0)
341 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "JACK client activation failed");
342 goto remove_from_list;
345 method_return_new_void(call_ptr);
346 goto exit;
348 remove_from_list:
349 list_del(&pair_ptr->siblings);
350 //unregister_output_port:
351 jack_port_unregister(pair_ptr->client, pair_ptr->output_port);
352 unregister_input_port:
353 jack_port_unregister(pair_ptr->client, pair_ptr->input_port);
354 close_client:
355 jack_client_close(pair_ptr->client);
356 free_output_name:
357 free(pair_ptr->output_port_name);
358 free_input_name:
359 free(pair_ptr->input_port_name);
360 free_pair:
361 free(pair_ptr);
362 exit:
363 return;
366 static void jmcore_destroy(struct dbus_method_call * call_ptr)
368 const char * port;
369 struct list_head * node_ptr;
370 struct port_pair * pair_ptr;
372 dbus_error_init(&g_dbus_error);
373 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &port, DBUS_TYPE_INVALID))
375 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
376 dbus_error_free(&g_dbus_error);
377 return;
380 list_for_each(node_ptr, &g_pairs)
382 pair_ptr = list_entry(node_ptr, struct port_pair, siblings);
383 if (strcmp(pair_ptr->input_port_name, port) == 0 ||
384 strcmp(pair_ptr->output_port_name, port) == 0)
386 destroy_pair(pair_ptr);
387 method_return_new_void(call_ptr);
388 return;
392 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "port '%s' not found.", port);
393 return;
396 static void jmcore_exit(struct dbus_method_call * call_ptr)
398 log_info("Exit command received through D-Bus");
399 g_quit = true;
400 method_return_new_void(call_ptr);
403 METHOD_ARGS_BEGIN(get_pid, "Get process ID")
404 METHOD_ARG_DESCRIBE_OUT("process_id", DBUS_TYPE_INT64_AS_STRING, "Process ID")
405 METHOD_ARGS_END
407 METHOD_ARGS_BEGIN(create, "Create port pair")
408 METHOD_ARG_DESCRIBE_IN("midi", "b", "Whether to create midi or audio ports")
409 METHOD_ARG_DESCRIBE_IN("input_port", "s", "Input port name")
410 METHOD_ARG_DESCRIBE_IN("output_port", "s", "Output port name")
411 METHOD_ARGS_END
413 METHOD_ARGS_BEGIN(destroy, "Destroy port pair")
414 METHOD_ARG_DESCRIBE_IN("port", "s", "Port name")
415 METHOD_ARGS_END
417 METHOD_ARGS_BEGIN(exit, "Tell jmcore D-Bus service to exit")
418 METHOD_ARGS_END
420 METHODS_BEGIN
421 METHOD_DESCRIBE(get_pid, jmcore_get_pid)
422 METHOD_DESCRIBE(create, jmcore_create)
423 METHOD_DESCRIBE(destroy, jmcore_destroy)
424 METHOD_DESCRIBE(exit, jmcore_exit)
425 METHODS_END
427 INTERFACE_BEGIN(g_interface, JMCORE_IFACE)
428 INTERFACE_DEFAULT_HANDLER
429 INTERFACE_EXPOSE_METHODS
430 INTERFACE_END