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
;
43 static unsigned int g_unique_index
;
45 struct list_head g_pairs
;
49 struct list_head siblings
;
50 jack_client_t
* client
;
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
)
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
);
78 memcpy(output
, input
, nframes
* sizeof(jack_default_audio_sample_t
));
82 jack_midi_clear_buffer(output
);
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
);
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
);
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
);
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)
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
);
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
);
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
;
172 destroy_control_object
:
173 dbus_object_path_destroy(g_dbus_connection
, g_object
);
175 dbus_connection_unref(g_dbus_connection
);
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
));
193 bool install_term_signal_handler(int signum
, bool ignore_if_already_ignored
)
197 sigh
= signal(signum
, term_signal_handler
);
200 log_error("signal() failed to install handler function for signal %d.", signum
);
204 if (sigh
== SIG_IGN
&& ignore_if_already_ignored
)
206 signal(SIGTERM
, SIG_IGN
);
212 int main(int argc
, char ** argv
)
216 INIT_LIST_HEAD(&g_pairs
);
218 install_term_signal_handler(SIGTERM
, false);
219 install_term_signal_handler(SIGINT
, true);
223 log_error("Failed to connect to D-Bus");
229 dbus_connection_read_write_dispatch(g_dbus_connection
, 50);
235 while (!list_empty(&g_pairs
))
237 destroy_pair(list_entry(g_pairs
.next
, struct port_pair
, siblings
));
244 /***************************************************************************/
245 /* D-Bus interface implementation */
247 static void jmcore_get_pid(struct dbus_method_call
* call_ptr
)
253 method_return_new_single(call_ptr
, DBUS_TYPE_INT64
, &pid
);
256 static void jmcore_create(struct dbus_method_call
* call_ptr
)
261 struct port_pair
* pair_ptr
;
263 char client_name
[256];
266 sprintf(client_name
, "jmcore-%u", g_unique_index
);
268 dbus_error_init(&g_dbus_error
);
269 if (!dbus_message_get_args(
272 DBUS_TYPE_BOOLEAN
, &midi
,
273 DBUS_TYPE_STRING
, &input
,
274 DBUS_TYPE_STRING
, &output
,
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
);
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");
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");
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
);
316 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "JACK process callback setup failed");
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
);
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
);
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
);
355 jack_client_close(pair_ptr
->client
);
357 free(pair_ptr
->output_port_name
);
359 free(pair_ptr
->input_port_name
);
366 static void jmcore_destroy(struct dbus_method_call
* call_ptr
)
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
);
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
);
392 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "port '%s' not found.", port
);
396 static void jmcore_exit(struct dbus_method_call
* call_ptr
)
398 log_info("Exit command received through D-Bus");
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")
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")
413 METHOD_ARGS_BEGIN(destroy
, "Destroy port pair")
414 METHOD_ARG_DESCRIBE_IN("port", "s", "Port name")
417 METHOD_ARGS_BEGIN(exit
, "Tell jmcore D-Bus service to exit")
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
)
427 INTERFACE_BEGIN(g_interface
, JMCORE_IFACE
)
428 INTERFACE_DEFAULT_HANDLER
429 INTERFACE_EXPOSE_METHODS