1 /* -*- Mode: C ; c-basic-offset: 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.
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
;
44 struct list_head g_pairs
;
48 struct list_head siblings
;
49 jack_client_t
* client
;
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
)
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
);
77 memcpy(output
, input
, nframes
* sizeof(jack_default_audio_sample_t
));
81 jack_midi_clear_buffer(output
);
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
);
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
);
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
);
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)
125 dbus_error_init(&g_dbus_error
);
127 g_dbus_connection
= dbus_bus_get(DBUS_BUS_SESSION
, &g_dbus_error
);
128 if (dbus_error_is_set(&g_dbus_error
))
130 log_error("Failed to get bus: %s", g_dbus_error
.message
);
131 dbus_error_free(&g_dbus_error
);
135 g_dbus_unique_name
= dbus_bus_get_unique_name(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(g_dbus_connection
, JMCORE_SERVICE_NAME
, DBUS_NAME_FLAG_DO_NOT_QUEUE
, &g_dbus_error
);
147 log_error("Failed to acquire bus name: %s", g_dbus_error
.message
);
148 dbus_error_free(&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
= dbus_object_path_new(JMCORE_OBJECT_PATH
, &g_interface
, NULL
, NULL
);
159 if (g_object
== NULL
)
161 goto unref_connection
;
164 if (!dbus_object_path_register(g_dbus_connection
, g_object
))
166 goto destroy_control_object
;
171 destroy_control_object
:
172 dbus_object_path_destroy(g_dbus_connection
, g_object
);
174 dbus_connection_unref(g_dbus_connection
);
180 static void disconnect_dbus(void)
182 dbus_object_path_destroy(g_dbus_connection
, g_object
);
183 dbus_connection_unref(g_dbus_connection
);
186 void term_signal_handler(int signum
)
188 log_info("Caught signal %d (%s), terminating", signum
, strsignal(signum
));
192 bool install_term_signal_handler(int signum
, bool ignore_if_already_ignored
)
196 sigh
= signal(signum
, term_signal_handler
);
199 log_error("signal() failed to install handler function for signal %d.", signum
);
203 if (sigh
== SIG_IGN
&& ignore_if_already_ignored
)
205 signal(SIGTERM
, SIG_IGN
);
211 int main(int argc
, char ** argv
)
215 INIT_LIST_HEAD(&g_pairs
);
217 install_term_signal_handler(SIGTERM
, false);
218 install_term_signal_handler(SIGINT
, true);
222 log_error("Failed to connect to D-Bus");
228 dbus_connection_read_write_dispatch(g_dbus_connection
, 50);
234 while (!list_empty(&g_pairs
))
236 destroy_pair(list_entry(g_pairs
.next
, struct port_pair
, siblings
));
243 /***************************************************************************/
244 /* D-Bus interface implementation */
246 static void jmcore_get_pid(struct dbus_method_call
* call_ptr
)
252 method_return_new_single(call_ptr
, DBUS_TYPE_INT64
, &pid
);
255 static void jmcore_create(struct dbus_method_call
* call_ptr
)
260 struct port_pair
* pair_ptr
;
263 dbus_error_init(&g_dbus_error
);
264 if (!dbus_message_get_args(
267 DBUS_TYPE_BOOLEAN
, &midi
,
268 DBUS_TYPE_STRING
, &input
,
269 DBUS_TYPE_STRING
, &output
,
272 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
273 dbus_error_free(&g_dbus_error
);
277 pair_ptr
= malloc(sizeof(struct port_pair
));
278 if (pair_ptr
== NULL
)
280 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Allocation of port pair structure failed");
284 pair_ptr
->input_port_name
= strdup(input
);
285 if (pair_ptr
->input_port_name
== NULL
)
287 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Allocation of port name buffer failed");
291 pair_ptr
->output_port_name
= strdup(output
);
292 if (pair_ptr
->input_port_name
== NULL
)
294 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Allocation of port name buffer failed");
295 goto free_input_name
;
298 pair_ptr
->client
= jack_client_open("jmcore", JackNoStartServer
, NULL
);
299 if (pair_ptr
->client
== NULL
)
301 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "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
);
311 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "JACK process callback setup failed");
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 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "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 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "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
);
336 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "JACK client activation failed");
337 goto remove_from_list
;
340 method_return_new_void(call_ptr
);
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
);
350 jack_client_close(pair_ptr
->client
);
352 free(pair_ptr
->output_port_name
);
354 free(pair_ptr
->input_port_name
);
361 static void jmcore_destroy(struct dbus_method_call
* call_ptr
)
364 struct list_head
* node_ptr
;
365 struct port_pair
* pair_ptr
;
367 dbus_error_init(&g_dbus_error
);
368 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &port
, DBUS_TYPE_INVALID
))
370 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
371 dbus_error_free(&g_dbus_error
);
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 method_return_new_void(call_ptr
);
387 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "port '%s' not found.", port
);
391 static void jmcore_exit(struct dbus_method_call
* call_ptr
)
393 log_info("Exit command received through D-Bus");
395 method_return_new_void(call_ptr
);
398 METHOD_ARGS_BEGIN(get_pid
, "Get process ID")
399 METHOD_ARG_DESCRIBE_OUT("process_id", DBUS_TYPE_INT64_AS_STRING
, "Process ID")
402 METHOD_ARGS_BEGIN(create
, "Create port pair")
403 METHOD_ARG_DESCRIBE_IN("midi", "b", "Whether to create midi or audio ports")
404 METHOD_ARG_DESCRIBE_IN("input_port", "s", "Input port name")
405 METHOD_ARG_DESCRIBE_IN("output_port", "s", "Output port name")
408 METHOD_ARGS_BEGIN(destroy
, "Destroy port pair")
409 METHOD_ARG_DESCRIBE_IN("port", "s", "Port name")
412 METHOD_ARGS_BEGIN(exit
, "Tell jmcore D-Bus service to exit")
416 METHOD_DESCRIBE(get_pid
, jmcore_get_pid
)
417 METHOD_DESCRIBE(create
, jmcore_create
)
418 METHOD_DESCRIBE(destroy
, jmcore_destroy
)
419 METHOD_DESCRIBE(exit
, jmcore_exit
)
422 INTERFACE_BEGIN(g_interface
, JMCORE_IFACE
)
423 INTERFACE_DEFAULT_HANDLER
424 INTERFACE_EXPOSE_METHODS