/* -*- Mode: C ; c-basic-offset: 2 -*- */ /* * LADI Session Handler (ladish) * * Copyright (C) 2008,2009,2010,2011,2012 Nedko Arnaudov * Copyright (C) 2007 Dave Robillard * ************************************************************************** * This file contains the JACK related functionality ************************************************************************** * * LADI Session Handler is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * LADI Session Handler is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LADI Session Handler. If not, see * or write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "graph_view.h" #include "studio.h" #include "menu.h" #include "statusbar.h" #include "action.h" #include "../proxies/jack_proxy.h" #include "../proxies/a2j_proxy.h" #include "../proxies/conf_proxy.h" #include "gtk_builder.h" #include "ask_dialog.h" /* JACK states */ #define JACK_STATE_NA 0 #define JACK_STATE_STOPPED 1 #define JACK_STATE_STARTED 2 unsigned int g_jack_state = JACK_STATE_NA; static uint32_t g_xruns; static double g_jack_max_dsp_load = 0.0; static GtkWidget * g_xrun_progress_bar; static uint32_t g_sample_rate; static bool g_jack_view_enabled = false; static graph_view_handle g_jack_view = NULL; static guint g_jack_poll_source_tag; static void update_raw_jack_visibility(void) { /* if there is no jack view and its display is enabled and jack is avaialable, create the raw jack view */ if (g_jack_view == NULL && g_jack_view_enabled && g_jack_state != JACK_STATE_NA) { if (!create_view(_("Raw JACK"), JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, false, false, true, &g_jack_view)) { log_error("create_view() failed for jack"); return; } } /* if there is jack view and its display is disabled or it is enabled byt jack is not avaialable, destroy the raw jack view */ if (g_jack_view != NULL && (!g_jack_view_enabled || g_jack_state == JACK_STATE_NA)) { destroy_view(g_jack_view); g_jack_view = NULL; } } void buffer_size_clear(void) { menu_set_jack_latency_items_sensivity(false); clear_latency_text(); } static void buffer_size_set(uint32_t size, bool force) { char buf[100]; static uint32_t last_buffer_size; if (!menu_set_jack_latency(size, force)) { buffer_size_clear(); return; } if (size != last_buffer_size) { log_info("JACK latency changed: %"PRIu32" samples", size); snprintf(buf, sizeof(buf), _("%4.1f ms (%"PRIu32")"), (float)size / (float)g_sample_rate * 1000.0f, size); set_latency_text(buf); } last_buffer_size = size; } static void update_buffer_size(bool force) { uint32_t size; if (jack_proxy_get_buffer_size(&size)) { buffer_size_set(size, force); } else { buffer_size_clear(); } } static void update_load(void) { double load; char tmp_buf[100]; uint32_t xruns; if (jack_proxy_get_xruns(&xruns)) { snprintf(tmp_buf, sizeof(tmp_buf), ngettext("%"PRIu32" dropout", "%"PRIu32" dropouts", xruns), xruns); set_xruns_text(tmp_buf); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(g_xrun_progress_bar), tmp_buf); } else { gtk_progress_bar_set_text(GTK_PROGRESS_BAR(g_xrun_progress_bar), _("error")); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(g_xrun_progress_bar), 0.0); set_xruns_text("?"); } if (jack_proxy_get_dsp_load(&load)) { if (load > g_jack_max_dsp_load) { g_jack_max_dsp_load = load; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(g_xrun_progress_bar), load / 100.0); } snprintf(tmp_buf, sizeof(tmp_buf), _("DSP: %5.1f%% (%5.1f%%)"), (float)load, (float)g_jack_max_dsp_load); set_dsp_load_text(tmp_buf); } else { set_xruns_text("?"); } if ((g_xruns == 0 && xruns != 0) || (g_xruns != 0 && xruns == 0)) { g_xruns = xruns; studio_state_changed(NULL); } else { g_xruns = xruns; } } static gboolean poll_jack(gpointer UNUSED(data)) { update_load(); update_buffer_size(false); return TRUE; } static void jack_appeared(void) { log_info("JACK appeared"); g_jack_state = JACK_STATE_STOPPED; studio_state_changed(NULL); update_raw_jack_visibility(); } static void jack_started(void) { log_info("JACK started"); g_jack_state = JACK_STATE_STARTED; studio_state_changed(NULL); menu_set_jack_latency_items_sensivity(true); update_buffer_size(true); enable_action(g_clear_xruns_and_max_dsp_action); g_jack_poll_source_tag = g_timeout_add(100, poll_jack, NULL); } static void jack_stopped(void) { if (g_jack_state == JACK_STATE_STARTED) { log_info("JACK stopped"); g_source_remove(g_jack_poll_source_tag); } g_jack_state = JACK_STATE_STOPPED; studio_state_changed(NULL); menu_set_jack_latency_items_sensivity(false); buffer_size_clear(); disable_action(g_clear_xruns_and_max_dsp_action); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(g_xrun_progress_bar), 0.0); } static void jack_disappeared(void) { log_info("JACK disappeared"); jack_stopped(); g_jack_state = JACK_STATE_NA; studio_state_changed(NULL); update_raw_jack_visibility(); } void clear_xruns_and_max_dsp(void) { log_info("clearing xruns and max dsp load"); jack_proxy_reset_xruns(); g_jack_max_dsp_load = 0.0; } void menu_request_jack_latency_change(uint32_t buffer_size) { log_info("JACK latency change request: %"PRIu32" samples", buffer_size); if (!jack_proxy_set_buffer_size(buffer_size)) { log_error("cannot set JACK buffer size"); } } void menu_request_jackdbus_exit(void) { bool result; log_info("jackdbus exit request"); if (g_jack_state == JACK_STATE_STARTED) { if (!ask_dialog(&result, _("Are you sure"), _("jackdbus will be reactivated. Are you sure?")) || !result) { return; } } if (!jack_proxy_exit()) { error_message_box(_("jackdbus exit command failed, please inspect logs.")); } } void menu_request_a2jmidid_exit(void) { bool result; log_info("a2jmidid exit request"); if (g_jack_state == JACK_STATE_STARTED) { if (!ask_dialog(&result, _("Are you sure"), _("a2jmidid will be reactivated. Are you sure?")) || !result) { return; } } if (!a2j_proxy_exit()) { error_message_box(_("a2jmidid exit command failed, please inspect logs.")); } } void menu_request_jack_configure(void) { GError * error_ptr; gchar * argv[] = {NULL, NULL}; GtkWidget * dialog; const char * jack_conf_tool; log_info("JACK configure request"); if (!conf_get(LADISH_CONF_KEY_JACK_CONF_TOOL, &jack_conf_tool)) { jack_conf_tool = LADISH_CONF_KEY_JACK_CONF_TOOL_DEFAULT; } argv[0] = (gchar *)jack_conf_tool; error_ptr = NULL; if (!g_spawn_async( NULL, /* working directory */ argv, NULL, /* envp */ G_SPAWN_SEARCH_PATH, /* flags */ NULL, /* child_setup callback */ NULL, /* user_data */ NULL, &error_ptr)) { dialog = get_gtk_builder_widget("error_dialog"); gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), _("Error executing JACK configuration tool.\n")); gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(dialog), "%s", error_ptr->message); gtk_widget_show(dialog); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); g_error_free(error_ptr); } } void menu_request_toggle_raw_jack(bool visible) { //log_info("toogle raw jack visibility -> %s", visible ? "visible" : "invisible"); g_jack_view_enabled = visible; update_raw_jack_visibility(); } void init_jack_widgets(void) { g_xrun_progress_bar = get_gtk_builder_widget("xrun_progress_bar"); } bool init_jack(void) { return jack_proxy_init(jack_started, jack_stopped, jack_appeared, jack_disappeared); } void uninit_jack(void) { jack_proxy_uninit(); } bool jack_xruns(void) { return g_xruns != 0; } void set_xrun_progress_bar_text(const char * text) { gtk_progress_bar_set_text(GTK_PROGRESS_BAR(g_xrun_progress_bar), text); /* workaround a bug in GtkProgressBar. it needs fraction change in order to redraw the changed text * GtkProgressBar tracks the dirty state and it is checked before painting, so sending redraw request * through gtk_widget_queue_draw() will not help because expose handler will ignore the redraw request */ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(g_xrun_progress_bar), 1.0); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(g_xrun_progress_bar), 0.0); } void update_jack_sample_rate(void) { if (jack_proxy_sample_rate(&g_sample_rate)) { char buf[100]; if (fmod(g_sample_rate, 1000.0) != 0.0) { snprintf(buf, sizeof(buf), _("%.1f kHz"), (float)g_sample_rate / 1000.0f); } else { snprintf(buf, sizeof(buf), _("%u kHz"), g_sample_rate / 1000); } set_sample_rate_text(buf); } else { clear_sample_rate_text(); } } unsigned int get_jack_state(void) { return g_jack_state; }