From 6972a4a3990a573937e420864268fcca39a7741a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 1 Jan 2007 06:30:55 +0000 Subject: Jack toolbar, modification of Jack buffer size on the fly. git-svn-id: http://svn.drobilla.net/lad/patchage@226 a436a847-0d15-0410-975c-d299462d15a1 --- src/JackDriver.cpp | 125 ++++++++++++++++-- src/JackDriver.h | 48 +++++-- src/Patchage.cpp | 214 +++++++++++++++++++++++++++++- src/Patchage.h | 20 ++- src/patchage.glade | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 764 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp index 9bc837d..875f8f1 100644 --- a/src/JackDriver.cpp +++ b/src/JackDriver.cpp @@ -20,6 +20,8 @@ #include #include "config.h" #include +#include +#include #include "PatchageFlowCanvas.h" #include "JackDriver.h" #include "Patchage.h" @@ -32,8 +34,11 @@ using namespace LibFlowCanvas; JackDriver::JackDriver(Patchage* app) -: m_app(app), - m_client(NULL) +: m_app(app) +, m_client(NULL) +, m_is_activated(false) +, m_xruns(0) +, m_xrun_delay(0) { m_last_pos.frame = 0; m_last_pos.valid = (jack_position_bits_t)0; @@ -60,19 +65,27 @@ JackDriver::attach(bool launch_daemon) jack_options_t options = (!launch_daemon) ? JackNoStartServer : JackNullOption; m_client = jack_client_open("Patchage", options, NULL); if (m_client == NULL) { - m_app->status_message("[JACK] Unable to attach"); + m_app->status_message("[JACK] Unable to create client"); + m_is_activated = false; } else { jack_set_error_function(error_cb); jack_on_shutdown(m_client, jack_shutdown_cb, this); jack_set_port_registration_callback(m_client, jack_port_registration_cb, this); jack_set_graph_order_callback(m_client, jack_graph_order_cb, this); + jack_set_buffer_size_callback(m_client, jack_buffer_size_cb, this); + jack_set_xrun_callback(m_client, jack_xrun_cb, this); m_is_dirty = true; - - jack_activate(m_client); - - signal_attached.emit(); - m_app->status_message("[JACK] Attached"); + m_buffer_size = jack_get_buffer_size(m_client); + + if (!jack_activate(m_client)) { + m_is_activated = true; + signal_attached.emit(); + m_app->status_message("[JACK] Attached"); + } else { + m_app->status_message("[JACK] ERROR: Failed to attach"); + m_is_activated = false; + } } } @@ -85,6 +98,7 @@ JackDriver::detach() jack_client_close(m_client); m_client = NULL; destroy_all_ports(); + m_is_activated = false; signal_detached.emit(); m_app->status_message("[JACK] Detached"); } @@ -144,7 +158,7 @@ JackDriver::create_port(boost::shared_ptr parent, jack_port_t* p void JackDriver::refresh() { - m_mutex.lock(); + //m_mutex.lock(); if (m_client == NULL) { // Shutdown @@ -153,7 +167,7 @@ JackDriver::refresh() signal_detached.emit(); } m_is_dirty = false; - m_mutex.unlock(); + //m_mutex.unlock(); return; } @@ -266,7 +280,7 @@ JackDriver::refresh() free(ports); undirty(); - m_mutex.unlock(); + //m_mutex.unlock(); } @@ -331,6 +345,8 @@ JackDriver::jack_port_registration_cb(jack_port_id_t port_id, int /*registered*/ jack_port_t* const port = jack_port_by_id(me->m_client, port_id); const string full_name = jack_port_name(port); + + jack_reset_max_delayed_usecs(me->m_client); //(me->m_mutex).lock(); @@ -353,6 +369,8 @@ JackDriver::jack_graph_order_cb(void* jack_driver) JackDriver* me = reinterpret_cast(jack_driver); assert(me->m_client); + jack_reset_max_delayed_usecs(me->m_client); + //(me->m_mutex).lock(); me->m_is_dirty = true; //(me->m_mutex).unlock(); @@ -361,6 +379,47 @@ JackDriver::jack_graph_order_cb(void* jack_driver) } +int +JackDriver::jack_buffer_size_cb(jack_nframes_t buffer_size, void* jack_driver) +{ + assert(jack_driver); + JackDriver* me = reinterpret_cast(jack_driver); + assert(me->m_client); + + jack_reset_max_delayed_usecs(me->m_client); + + //(me->m_mutex).lock(); + me->m_buffer_size = buffer_size; + me->reset_xruns(); + me->reset_delay(); + //(me->m_mutex).unlock(); + + return 0; +} + + +int +JackDriver::jack_xrun_cb(void* jack_driver) +{ + assert(jack_driver); + JackDriver* me = reinterpret_cast(jack_driver); + assert(me->m_client); + + //(me->m_mutex).lock(); + me->m_xruns++; + me->m_xrun_delay = jack_get_xrun_delayed_usecs(me->m_client); + jack_reset_max_delayed_usecs(me->m_client); + + //cerr << "** XRUN Delay = " << me->m_xrun_delay << endl; + + me->m_is_dirty = true; + + //(me->m_mutex).unlock(); + + return 0; +} + + void JackDriver::jack_shutdown_cb(void* jack_driver) { @@ -368,6 +427,8 @@ JackDriver::jack_shutdown_cb(void* jack_driver) JackDriver* me = reinterpret_cast(jack_driver); assert(me->m_client); + jack_reset_max_delayed_usecs(me->m_client); + //(me->m_mutex).lock(); me->m_client = NULL; me->m_is_dirty = true; @@ -382,3 +443,45 @@ JackDriver::error_cb(const char* msg) } +jack_nframes_t +JackDriver::buffer_size() +{ + if (m_is_activated) + return m_buffer_size; + else + return jack_get_buffer_size(m_client); +} + + +void +JackDriver::reset_xruns() +{ + m_xruns = 0; + m_xrun_delay = 0; +} + + +void +JackDriver::set_buffer_size(jack_nframes_t size) +{ + if (m_client && jack_set_buffer_size(m_client, size)) + m_app->status_message("[JACK] ERROR: Unable to set buffer size"); +} + +void +JackDriver::set_realtime(bool /*realtime*/, int /*priority*/) +{ + /* need a jack_set_realtime, this doesn't make sense + pthread_t jack_thread = jack_client_thread_id(m_client); + + if (realtime) + if (jack_acquire_real_time_scheduling(jack_thread, priority)) + m_app->status_message("[JACK] ERROR: Unable to set real-time priority"); + else + if (jack_drop_real_time_scheduling(jack_thread)) + m_app->status_message("[JACK] ERROR: Unable to drop real-time priority"); + + cerr << "Set Jack realtime: " << realtime << endl; + */ +} + diff --git a/src/JackDriver.h b/src/JackDriver.h index 01dc49d..c581df0 100644 --- a/src/JackDriver.h +++ b/src/JackDriver.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "Driver.h" class Patchage; @@ -46,6 +47,7 @@ public: void detach(); bool is_attached() const { return (m_client != NULL); } + bool is_realtime() const { return m_client && jack_is_realtime(m_client); } void refresh(); bool connect(boost::shared_ptr src, @@ -57,6 +59,9 @@ public: void start_transport() { jack_transport_start(m_client); } void stop_transport() { jack_transport_stop(m_client); } + void reset_xruns(); + void reset_delay() { jack_reset_max_delayed_usecs(m_client); } + void rewind_transport() { jack_position_t zero; zero.frame = 0; @@ -64,18 +69,20 @@ public: jack_transport_reposition(m_client, &zero); } + //jack_client_t* client() { return m_client; } + + jack_nframes_t buffer_size(); + void set_buffer_size(jack_nframes_t size); -private: - Patchage* m_app; + inline float sample_rate() { return jack_get_sample_rate(m_client); } - jack_client_t* m_client; + void set_realtime(bool realtime, int priority=80); - Mutex m_mutex; + inline size_t xruns() { return m_xruns; } - list m_added_ports; - list m_removed_ports; + inline float max_delay() { return jack_get_max_delayed_usecs(m_client); } - jack_position_t m_last_pos; +private: boost::shared_ptr create_port(boost::shared_ptr parent, jack_port_t* port); @@ -86,9 +93,30 @@ private: void update_time(); - static void jack_port_registration_cb(jack_port_id_t port_id, int registered, void* controller); - static int jack_graph_order_cb(void* controller); - static void jack_shutdown_cb(void* controller); + static void jack_port_registration_cb(jack_port_id_t port_id, int registered, void* me); + static int jack_graph_order_cb(void* me); + static int jack_buffer_size_cb(jack_nframes_t buffer_size, void* me); + static int jack_xrun_cb(void* me); + static void jack_shutdown_cb(void* me); + + Patchage* m_app; + + jack_client_t* m_client; + + bool m_is_activated; + + //Mutex m_mutex; + + list m_added_ports; + list m_removed_ports; + + jack_position_t m_last_pos; + + jack_nframes_t m_buffer_size; + size_t m_xruns; + float m_xrun_delay; + + bool m_settings_changed; }; diff --git a/src/Patchage.cpp b/src/Patchage.cpp index b7e00ef..357f334 100644 --- a/src/Patchage.cpp +++ b/src/Patchage.cpp @@ -14,6 +14,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "Patchage.h" #include "config.h" #include @@ -22,6 +23,7 @@ #include #include "StateManager.h" #include "PatchageFlowCanvas.h" +#include #include "JackDriver.h" #include "JackSettingsDialog.h" #ifdef HAVE_LASH @@ -34,6 +36,40 @@ // FIXME: include to avoid undefined reference to boost SP debug hooks stuff #include + + +/* Gtk helpers (resize combo boxes) */ + +static void +gtkmm_get_ink_pixel_size (Glib::RefPtr layout, + int& width, + int& height) +{ + Pango::Rectangle ink_rect = layout->get_ink_extents (); + + width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE; + height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE; +} + +static void +gtkmm_set_width_for_given_text (Gtk::Widget &w, const gchar *text, + gint hpadding/*, gint vpadding*/) + +{ + int old_width, old_height; + w.get_size_request(old_width, old_height); + + int width, height; + w.ensure_style (); + + gtkmm_get_ink_pixel_size (w.create_pango_layout (text), width, height); + w.set_size_request(width + hpadding, old_height);//height + vpadding); +} + +/* end Gtk helpers */ + + + Patchage::Patchage(int argc, char** argv) : m_pane_closed(false), m_update_pane_position(true), @@ -122,7 +158,19 @@ Patchage::Patchage(int argc, char** argv) xml->get_widget("stop_but", m_stop_button); xml->get_widget("zoom_full_but", m_zoom_full_button); xml->get_widget("zoom_normal_but", m_zoom_normal_button); + //xml->get_widget("main_statusbar", m_status_bar); + //xml->get_widget("main_load_progress", m_load_progress_bar); + xml->get_widget("main_jack_connect_toggle", m_jack_connect_toggle); + xml->get_widget("main_jack_realtime_check", m_jack_realtime_check); + xml->get_widget("main_buffer_size_combo", m_buffer_size_combo); + xml->get_widget("main_sample_rate_combo", m_sample_rate_combo); + xml->get_widget("main_xrun_progress", m_xrun_progress_bar); + xml->get_widget("main_xrun_counter", m_xrun_counter); + xml->get_widget("main_clear_load_button", m_clear_load_button); + gtkmm_set_width_for_given_text(*m_buffer_size_combo, "4096", 40); + gtkmm_set_width_for_given_text(*m_sample_rate_combo, "44.1", 40); + m_canvas_scrolledwindow->add(*m_canvas); //m_canvas_scrolledwindow->signal_event().connect(sigc::mem_fun(m_canvas, &FlowCanvas::scroll_event_handler)); m_canvas->scroll_to(static_cast(m_canvas->width()/2 - 320), @@ -130,10 +178,18 @@ Patchage::Patchage(int argc, char** argv) m_zoom_slider->signal_value_changed().connect(sigc::mem_fun(this, &Patchage::zoom_changed)); + m_jack_connect_toggle->signal_toggled().connect(sigc::mem_fun(this, &Patchage::jack_connect_changed)); + + m_buffer_size_combo->signal_changed().connect(sigc::mem_fun(this, &Patchage::buffer_size_changed)); + m_sample_rate_combo->signal_changed().connect(sigc::mem_fun(this, &Patchage::sample_rate_changed)); + m_jack_realtime_check->signal_toggled().connect(sigc::mem_fun(this, &Patchage::realtime_changed)); + m_rewind_button->signal_clicked().connect(sigc::mem_fun(m_jack_driver, &JackDriver::rewind_transport)); m_play_button->signal_clicked().connect(sigc::mem_fun(m_jack_driver, &JackDriver::start_transport)); m_stop_button->signal_clicked().connect(sigc::mem_fun(m_jack_driver, &JackDriver::stop_transport)); + m_clear_load_button->signal_clicked().connect(sigc::mem_fun(this, &Patchage::clear_load)); + m_zoom_normal_button->signal_clicked().connect(sigc::bind( sigc::mem_fun(this, &Patchage::zoom), 1.0)); @@ -165,7 +221,7 @@ Patchage::Patchage(int argc, char** argv) m_menu_view_messages->signal_toggled().connect( sigc::mem_fun(this, &Patchage::show_messages_toggled)); m_menu_help_about->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_help_about)); - attach_menu_items(); + connect_widgets(); update_state(); @@ -189,6 +245,9 @@ Patchage::Patchage(int argc, char** argv) // Idle callback, check if we need to refresh Glib::signal_timeout().connect(sigc::mem_fun(this, &Patchage::idle_callback), 100); + + // Faster idle callback to update DSP load progress bar + //Glib::signal_timeout().connect(sigc::mem_fun(this, &Patchage::update_load), 50); } @@ -218,6 +277,10 @@ Patchage::attach() #endif menu_view_refresh(); + + update_toolbar(); + + //m_status_bar->push("Connected to JACK server"); } @@ -250,6 +313,76 @@ Patchage::idle_callback() m_refresh = false; } + update_load(); + + return true; +} + + +void +Patchage::update_toolbar() +{ + m_jack_connect_toggle->set_active(m_jack_driver->is_attached()); + m_jack_realtime_check->set_active(m_jack_driver->is_realtime()); + + if (m_jack_driver->is_attached()) { + m_buffer_size_combo->set_active((int)log2f(m_jack_driver->buffer_size()) - 5); + + switch ((int)m_jack_driver->sample_rate()) { + case 44100: + m_sample_rate_combo->set_active(0); + break; + case 48000: + m_sample_rate_combo->set_active(1); + break; + case 96000: + m_sample_rate_combo->set_active(2); + break; + default: + m_sample_rate_combo->set_active(-1); + status_message("[JACK] ERROR: Unknown sample rate"); + break; + } + } +} + + +bool +Patchage::update_load() +{ + if (!m_jack_driver->is_attached()) + return true; + + static float last_delay = 0; + + const float max_delay = m_jack_driver->max_delay(); + + if (max_delay != last_delay) { + const float sample_rate = m_jack_driver->sample_rate(); + const float buffer_size = m_jack_driver->buffer_size(); + const float period = buffer_size / sample_rate * 1000000; // usecs + /* + if (max_delay > 0) { + cerr << "SR: " << sample_rate << ", BS: " << buffer_size << ", P = " << period + << ", MD: " << max_delay << endl; + }*/ + + m_xrun_progress_bar->set_fraction(max_delay / period); + + char tmp_buf[8]; + snprintf(tmp_buf, 8, "%zd", m_jack_driver->xruns()); + + //m_xrun_progress_bar->set_text(string(tmp_buf) + " XRuns"); + m_xrun_counter->set_text(tmp_buf); + + if (max_delay > period) { + m_xrun_progress_bar->set_fraction(1.0); + m_jack_driver->reset_delay(); + } + + last_delay = max_delay; + } + return true; } @@ -312,7 +445,7 @@ Patchage::status_message(const string& msg) * (eg. disable "Connect to Jack" when Patchage is already connected to Jack) */ void -Patchage::attach_menu_items() +Patchage::connect_widgets() { #ifdef HAVE_LASH m_lash_driver->signal_attached.connect(sigc::bind( @@ -330,6 +463,12 @@ Patchage::attach_menu_items() sigc::mem_fun(m_menu_lash_disconnect, &Gtk::MenuItem::set_sensitive), false)); #endif + m_jack_driver->signal_attached.connect( + sigc::mem_fun(this, &Patchage::update_toolbar)); + + m_jack_driver->signal_attached.connect(sigc::bind( + sigc::mem_fun(m_jack_connect_toggle, &Gtk::ToggleButton::set_active), true)); + m_jack_driver->signal_attached.connect(sigc::bind( sigc::mem_fun(m_menu_jack_launch, &Gtk::MenuItem::set_sensitive), false)); m_jack_driver->signal_attached.connect(sigc::bind( @@ -337,6 +476,8 @@ Patchage::attach_menu_items() m_jack_driver->signal_attached.connect(sigc::bind( sigc::mem_fun(m_menu_jack_disconnect, &Gtk::MenuItem::set_sensitive), true)); + m_jack_driver->signal_detached.connect(sigc::bind( + sigc::mem_fun(m_jack_connect_toggle, &Gtk::ToggleButton::set_active), false)); m_jack_driver->signal_detached.connect(sigc::bind( sigc::mem_fun(m_menu_jack_launch, &Gtk::MenuItem::set_sensitive), true)); m_jack_driver->signal_detached.connect(sigc::bind( @@ -518,3 +659,72 @@ Patchage::store_window_location() } +void +Patchage::clear_load() +{ + cerr << "CLEAR LOAD\n"; + m_xrun_progress_bar->set_fraction(0.0); + m_jack_driver->reset_xruns(); + m_jack_driver->reset_delay(); +} + + +void +Patchage::buffer_size_changed() +{ + const int selected = m_buffer_size_combo->get_active_row_number(); + + if (selected == -1) { + update_toolbar(); + } else { + jack_nframes_t buffer_size = 1 << (selected+5); + + //cerr << "BS Changed: " << selected << ": " << buffer_size << endl; + + m_jack_driver->set_buffer_size(buffer_size); + } +} + + +void +Patchage::sample_rate_changed() +{ + const int selected = m_sample_rate_combo->get_active_row_number(); + + if (selected == -1) { + update_toolbar(); + } else { + jack_nframes_t rate = 44100; // selected == 0 + if (selected == 1) + rate = 48000; + else if (selected == 2) + rate = 96000; + + //cerr << "SR Changed: " << selected << ": " << rate << endl; + + //m_jack_driver->set_sample_rate(rate); + } +} + + +void +Patchage::realtime_changed() +{ + m_jack_driver->set_realtime(m_jack_realtime_check->get_active()); +} + + +void +Patchage::jack_connect_changed() +{ + const bool selected = m_jack_connect_toggle->get_active(); + + if (selected != m_jack_driver->is_attached()) { + if (selected) { + m_jack_driver->attach(true); + } else { + m_jack_driver->detach(); + } + } +} + diff --git a/src/Patchage.h b/src/Patchage.h index 7de2ef0..46d134a 100644 --- a/src/Patchage.h +++ b/src/Patchage.h @@ -53,6 +53,8 @@ public: void attach(); void quit() { m_main_window->hide(); } + void clear_load(); + void update_state(); void store_window_location(); @@ -63,7 +65,7 @@ public: { return m_main_paned->property_max_position() - m_messages_expander->get_label_widget()->get_height() - 8; } protected: - void attach_menu_items(); + void connect_widgets(); void menu_store_positions(); void menu_file_quit(); @@ -73,7 +75,14 @@ protected: void zoom(double z); void zoom_changed(); bool idle_callback(); + bool update_load(); + void update_toolbar(); + void jack_connect_changed(); + void buffer_size_changed(); + void sample_rate_changed(); + void realtime_changed(); + void on_pane_position_changed(); void on_messages_expander_changed(); @@ -131,6 +140,15 @@ protected: Gtk::Button* m_stop_button; Gtk::Button* m_zoom_normal_button; Gtk::Button* m_zoom_full_button; + //Gtk::ProgressBar* m_load_progress_bar; + Gtk::ToggleButton* m_jack_connect_toggle; + Gtk::ToggleButton* m_jack_realtime_check; + Gtk::ComboBox* m_buffer_size_combo; + Gtk::ComboBox* m_sample_rate_combo; + Gtk::ProgressBar* m_xrun_progress_bar; + Gtk::Entry* m_xrun_counter; + Gtk::Button* m_clear_load_button; + //Gtk::Statusbar* m_status_bar; }; #endif // PATCHAGE_H diff --git a/src/patchage.glade b/src/patchage.glade index 85d8eba..86d02d5 100644 --- a/src/patchage.glade +++ b/src/patchage.glade @@ -465,6 +465,386 @@ + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + True + + + + True + True + True + False + + + + True + True + GTK_RELIEF_NORMAL + True + False + False + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-connect + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + JACK + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + False + False + + + + + + True + True + True + False + + + + 1 + True + False + True + Realtime + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + False + False + + + + + + True + True + True + False + + + + True + 0.5 + 0.5 + 1 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 0 + + + + True + 32 +64 +128 +256 +512 +1024 +2048 +4096 + False + True + True + + + 1 + True + True + + + + + + True + frames @ + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 2 + 2 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + False + False + + + + + + 2 + True + False + 44.1 +48 +96 + False + True + True + + + 1 + True + True + + + + + + True + kHz + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + False + False + + + + + + 4 + True + True + True + True + + + False + False + + + + + + True + True + True + False + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + gtk-clear + 4 + 0.5 + 0.5 + 0 + 0 + + + + + + + False + False + + + + + + True + True + True + False + + + + True + 0.5 + 0.5 + 1 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 0 + + + + True + Delay/XRun Indicator + +The bar represents the maximum processing delay as a fraction of the time available for a cycle. If the bar reaches 100%, an XRun will occur. + GTK_PROGRESS_LEFT_TO_RIGHT + 0 + 0.10000000149 + XRun % + PANGO_ELLIPSIZE_NONE + + + 1 + False + False + + + + + + True + True + True + True + 0 + 0 + True + * + False + 3 + + + 0 + True + True + + + + + + + + + False + False + + + + + 0 + False + False + + + True @@ -610,6 +990,7 @@ + 4 True True True -- cgit v1.2.1