diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 556dee99fe..aa71040f46 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -396,7 +396,7 @@ ARDOUR_UI::create_engine () loading_message (_("Starting audio engine")); try { - engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name, ARDOUR_COMMAND_LINE::jack_session_uuid); + engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::backend_client_name, ARDOUR_COMMAND_LINE::backend_session_uuid); } catch (...) { diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 21e5343979..e5a7e8d580 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -372,33 +372,29 @@ static void load_custom_fonts() { #endif static gboolean -tell_about_jack_death (void* /* ignored */) +tell_about_backend_death (void* /* ignored */) { if (AudioEngine::instance()->processed_frames() == 0) { /* died during startup */ - MessageDialog msg (_("JACK exited"), false); + MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false); msg.set_position (Gtk::WIN_POS_CENTER); msg.set_secondary_text (string_compose (_( -"JACK exited unexpectedly, and without notifying %1.\n\ +"%2 exited unexpectedly, and without notifying %1.\n\ \n\ -This could be due to misconfiguration or to an error inside JACK.\n\ +This could be due to misconfiguration or to an error inside %2.\n\ \n\ -Click OK to exit %1."), PROGRAM_NAME)); +Click OK to exit %1."), PROGRAM_NAME, AudioEngine::instance()->current_backend_name())); msg.run (); _exit (0); } else { - /* engine has already run, so this is a mid-session JACK death */ - - MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false)); - msg->set_secondary_text (string_compose (_( -"JACK exited unexpectedly, and without notifying %1.\n\ -\n\ -This is probably due to an error inside JACK. You should restart JACK\n\ -and reconnect %1 to it, or exit %1 now. You cannot save your\n\ -session at this time, because we would lose your connection information.\n"), PROGRAM_NAME)); + /* engine has already run, so this is a mid-session backend death */ + + MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false); + msg->set_secondary_text (string_compose (_("%2 exited unexpectedly, and without notifying %1."), + PROGRAM_NAME, AudioEngine::instance()->current_backend_name())); msg->present (); } return false; /* do not call again */ @@ -407,15 +403,15 @@ session at this time, because we would lose your connection information.\n"), PR static void sigpipe_handler (int /*signal*/) { - /* XXX fix this so that we do this again after a reconnect to JACK + /* XXX fix this so that we do this again after a reconnect to the backend */ - static bool done_the_jack_thing = false; + static bool done_the_backend_thing = false; - if (!done_the_jack_thing) { + if (!done_the_backend_thing) { AudioEngine::instance()->died (); - g_idle_add (tell_about_jack_death, 0); - done_the_jack_thing = true; + g_idle_add (tell_about_backend_death, 0); + done_the_backend_thing = true; } } diff --git a/gtk2_ardour/opts.cc b/gtk2_ardour/opts.cc index b29f106bfe..90d753af4c 100644 --- a/gtk2_ardour/opts.cc +++ b/gtk2_ardour/opts.cc @@ -32,7 +32,8 @@ using namespace std; string ARDOUR_COMMAND_LINE::session_name = ""; -string ARDOUR_COMMAND_LINE::jack_client_name = "ardour"; +string ARDOUR_COMMAND_LINE::backend_client_name = "ardour"; +string ARDOUR_COMMAND_LINE::backend_session_uuid; bool ARDOUR_COMMAND_LINE::show_key_actions = false; bool ARDOUR_COMMAND_LINE::no_splash = false; bool ARDOUR_COMMAND_LINE::just_version = false; @@ -45,7 +46,6 @@ string ARDOUR_COMMAND_LINE::keybindings_path = ""; /* empty means use builtin de std::string ARDOUR_COMMAND_LINE::menus_file = "ardour.menus"; bool ARDOUR_COMMAND_LINE::finder_invoked_ardour = false; string ARDOUR_COMMAND_LINE::immediate_save; -string ARDOUR_COMMAND_LINE::jack_session_uuid; string ARDOUR_COMMAND_LINE::load_template; bool ARDOUR_COMMAND_LINE::check_announcements = true; @@ -60,7 +60,7 @@ print_help (const char *execname) << _(" -h, --help Print this message\n") << _(" -a, --no-announcements Do not contact website for announcements\n") << _(" -b, --bindings Print all possible keyboard binding names\n") - << _(" -c, --name Use a specific jack client name, default is ardour\n") + << _(" -c, --name Use a specific backend client name, default is ardour\n") << _(" -d, --disable-plugins Disable all plugins in an existing session\n") << _(" -D, --debug Set debug flags. Use \"-D list\" to see available options\n") << _(" -n, --no-splash Do not show splash screen\n") @@ -199,7 +199,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[]) break; case 'c': - jack_client_name = optarg; + backend_client_name = optarg; break; case 'C': @@ -215,7 +215,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[]) break; case 'U': - jack_session_uuid = optarg; + backend_session_uuid = optarg; break; default: diff --git a/gtk2_ardour/opts.h b/gtk2_ardour/opts.h index b9faa36d72..fdf29157d2 100644 --- a/gtk2_ardour/opts.h +++ b/gtk2_ardour/opts.h @@ -28,7 +28,8 @@ extern std::string session_name; extern bool show_key_actions; extern bool no_splash; extern bool just_version; -extern std::string jack_client_name; +extern std::string backend_client_name; +extern std::string backend_session_uuid; extern bool use_vst; extern bool new_session; extern char* curvetest_file; @@ -39,7 +40,6 @@ extern std::string keybindings_path; extern std::string menus_file; extern bool finder_invoked_ardour; extern std::string immediate_save; -extern std::string jack_session_uuid; extern std::string load_template; extern bool check_announcements; diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h index d286fbd9c2..98f6c4d8a7 100644 --- a/libs/ardour/ardour/audio_backend.h +++ b/libs/ardour/ardour/audio_backend.h @@ -80,24 +80,102 @@ class AudioBackend { */ virtual std::vector available_buffer_sizes (const std::string& device) const = 0; - struct Parameters { - std::string device_name; - float sample_rate; - uint32_t buffer_size; - uint32_t systemic_input_latency; - uint32_t systemic_output_latency; - uint32_t input_channels; - uint32_t output_channels; + /** Returns the maximum number of input channels that are potentially + * usable with the hardware identified by @param device. Any number from 1 + * to the value returned may be supplied in other calls to this backend as + * the input channel count to use with the name device, but the requested + * count may turn out to be unavailable, or become invalid at any time. + */ + virtual uint32_t available_input_channel_count (const std::string& device) const = 0; + + /** Returns the maximum number of output channels that are potentially + * usable with the hardware identified by @param device. Any number from 1 + * to the value returned may be supplied in other calls to this backend as + * the output channel count to use with the name device, but the requested + * count may turn out to be unavailable, or become invalid at any time. + */ + virtual uint32_t available_output_channel_count (const std::string& device) const = 0; + + enum SampleFormat { + Signed16bitInteger, + Signed24bitInteger, + Signed32bitInteger, + FloatingPoint }; - virtual int set_parameters (const Parameters&) = 0; - virtual int get_parameters (Parameters&) const = 0; + /* Set the hardware parameters. + * + * If called when the current state is stopped or paused, + * the changes will not take effect until the state changes to running. + * + * If called while running, the state will change as fast as the + * implementation allows. + * + * All set_*() methods return zero on success, non-zero otherwise. + */ + + /** Set the name of the device to be used + */ + virtual int set_device_name (const std::string&) = 0; + /** Set the sample rate to be used + */ + virtual int set_sample_rate (float) = 0; + /** Set the buffer size to be used. + * + * The device is assumed to use a double buffering scheme, so that one + * buffer's worth of data can be processed by hardware while software works + * on the other buffer. All known suitable audio APIs support this model + * (though ALSA allows for alternate numbers of buffers, and CoreAudio + * doesn't directly expose the concept). + */ + virtual int set_buffer_size (uint32_t) = 0; + /** Set the preferred underlying hardware sample format + * + * This does not change the sample format (32 bit float) read and + * written to the device via the Port API. + */ + virtual int set_sample_format (SampleFormat) = 0; + /** Set the preferred underlying hardware data layout. + * If @param yn is true, then the hardware will interleave + * samples for successive channels; otherwise, the hardware will store + * samples for a single channel contiguously. + * + * Setting this does not change the fact that all data streams + * to and from Ports are mono (essentially, non-interleaved) + */ + virtual int set_interleaved (bool yn) = 0; + /** Set the number of input channels that should be used + */ + virtual int set_input_channels (uint32_t) = 0; + /** Set the number of output channels that should be used + */ + virtual int set_output_channels (uint32_t) = 0; + /** Set the (additional) input latency that cannot be determined via + * the implementation's underlying code (e.g. latency from + * external D-A/D-A converters. Units are samples. + */ + virtual int set_systemic_input_latency (uint32_t) = 0; + /** Set the (additional) output latency that cannot be determined via + * the implementation's underlying code (e.g. latency from + * external D-A/D-A converters. Units are samples. + */ + virtual int set_systemic_output_latency (uint32_t) = 0; + + virtual std::string get_device_name () const = 0; + virtual float get_sample_rate () const = 0; + virtual uint32_t get_buffer_size () const = 0; + virtual SampleFormat get_sample_format () const = 0; + virtual bool get_interleaved () const = 0; + virtual uint32_t get_input_channels () const = 0; + virtual uint32_t get_output_channels () const = 0; + virtual uint32_t get_systemic_input_latency () const = 0; + virtual uint32_t get_systemic_output_latency () const = 0; /* Basic state control */ /** Start using the device named in the most recent call - * to set_parameters(), with the parameters also provided - * to that call. + * to set_device(), with the parameters set by various + * the most recent calls to set_sample_rate() etc. etc. * * At some undetermined time after this function is successfully called, * the backend will start calling the ::process_callback() method of @@ -108,14 +186,14 @@ class AudioBackend { */ virtual int start () = 0; - /** Stop using the device named in the most recent call to set_parameters(). + /** Stop using the device currently in use. * * If the function is successfully called, no subsequent calls to the * process_callback() of @param engine will be made after the function - * returns, until set_parameters() and start() are called again. + * returns, until parameters are reset and start() are called again. * * The backend is considered to be un-configured after a successful - * return, and requires a call to set_parameters() before it can be + * return, and requires calls to set hardware parameters before it can be * start()-ed again. See pause() for a way to avoid this. stop() should * only be used when reconfiguration is required OR when there are no * plans to use the backend in the future with a reconfiguration. @@ -131,10 +209,8 @@ class AudioBackend { * returns, until start() is called again. * * The backend will retain its existing parameter configuration after a successful - * return, and requires a call to set_parameters() before it can be - * start()-ed again. See pause() for a way to avoid this. stop() should - * only be used when reconfiguration is required OR when there are no - * plans to use the backend in the future with a reconfiguration. + * return, and does NOT require any calls to set hardware parameters before it can be + * start()-ed again. * * Return zero if successful, 1 if the device is not in use, negative values on error */ @@ -142,9 +218,15 @@ class AudioBackend { /** While remaining connected to the device, and without changing its * configuration, start (or stop) calling the process_callback() of @param engine - * without waiting for the device. + * without waiting for the device. Once process_callback() has returned, it + * will be called again immediately, thus allowing for faster-than-realtime + * processing. * - * If @param start_stop is true, begin this behaviour, otherwise cease this + * All registered ports remain in existence and all connections remain + * unaltered. However, any physical ports should NOT be used by the + * process_callback() during freewheeling - the data behaviour is undefined. + * + * If @param start_stop is true, begin this behaviour; otherwise cease this * behaviour if it currently occuring, and return to calling * process_callback() of @param engine by waiting for the device. * @@ -155,7 +237,13 @@ class AudioBackend { /** return the fraction of the time represented by the current buffer * size that is being used for each buffer process cycle, as a value * from 0.0 to 1.0 - */ + * + * E.g. if the buffer size represents 5msec and current processing + * takes 1msec, the returned value should be 0.2. + * + * Implementations can feel free to smooth the values returned over + * time (e.g. high pass filtering, or its equivalent). + */ virtual float get_cpu_load() const = 0; /* Transport Control (JACK is the only audio API that currently offers @@ -179,6 +267,16 @@ class AudioBackend { */ virtual framepos_t transport_frame() { return 0; } + /** If @param yn is true, become the time master for any inter-application transport + * timebase, otherwise cease to be the time master for the same. + * + * Return zero on success, non-zero otherwise + * + * JACK is the only currently known audio API with the concept of a shared + * transport timebase. + */ + virtual int set_time_master (bool yn) { return 0; } + virtual framecnt_t sample_rate () const; virtual pframes_t samples_per_cycle () const; virtual int usecs_per_cycle () const { return _usecs_per_cycle; } @@ -229,11 +327,28 @@ class AudioBackend { * that it can only be called by a process thread) */ virtual bool get_sync_offset (pframes_t& offset) const { return 0; } + + /** Create a new thread suitable for running part of the buffer process + * cycle (i.e. Realtime scheduling, memory allocation, etc. etc are all + * correctly setup), with a stack size given in bytes by specified @param + * stacksize. The thread will begin executing @param func, and will exit + * when that function returns. + */ + virtual int create_process_thread (boost::function func, pthread_t*, size_t stacksize) = 0; private: AudioEngine& engine; - Parameters _last_requested_parameters; State _state; + + std::string _target_device; + float _target_sample_rate; + uint32_t _target_buffer_size; + SampleFormat _target_sample_format; + bool _target_interleaved; + uint32_t _target_input_channels; + uint32_t _target_output_channels; + uin32_t _target_systemic_input_latency; + uin32_t _target_systemic_input_latency; }; } diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 5a064f5183..a3f16ff376 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -33,7 +33,6 @@ #include -#include "pbd/rcu.h" #include "pbd/signals.h" #include "pbd/stacktrace.h" @@ -60,163 +59,145 @@ class MidiPort; class Port; class Session; class ProcessThread; +class AudioBackend; class AudioEngine : public SessionHandlePtr { public: - typedef std::map > Ports; - - AudioEngine (std::string client_name, std::string session_uuid); - virtual ~AudioEngine (); - - static int discover_backends(); - std::vector available_backends() const; - - ProcessThread* main_thread() const { return _main_thread; } - - std::string client_name() const { return jack_client_name; } - - int stop (bool forever = false); - int start (); - int pause (); - int freewheel (bool); + typedef std::map > Ports; - bool running() const { return _running; } + AudioEngine (std::string client_name, std::string session_uuid); + virtual ~AudioEngine (); + + static int discover_backends(); + std::vector available_backends() const; + std::string current_backend_name () const; - Glib::Threads::Mutex& process_lock() { return _process_lock; } + ProcessThread* main_thread() const { return _main_thread; } + + std::string client_name() const { return backend_client_name; } + + int stop (bool forever = false); + int start (); + int pause (); + int freewheel (bool onoff); + bool freewheeling() const { return _freewheeling; } + + bool running() const { return _running; } + Glib::Threads::Mutex& process_lock() { return _process_lock; } + int request_buffer_size (pframes_t); - int request_buffer_size (pframes_t); - - framecnt_t processed_frames() const { return _processed_frames; } - - float get_cpu_load() { - jack_client_t* _priv_jack = _jack; - if (!_running || !_priv_jack) { - return 0; - } - return jack_cpu_load (_priv_jack); - } - - void set_session (Session *); - void remove_session (); // not a replacement for SessionHandle::session_going_away() - - class NoBackendAvailable : public std::exception { - public: - virtual const char *what() const throw() { return "could not connect to engine backend"; } - }; - - void split_cycle (pframes_t offset); - - int reset_timebase (); - - void update_latencies (); - - /* start/stop freewheeling */ - - int freewheel (bool onoff); - bool freewheeling() const { return _freewheeling; } - - /* this signal is sent for every process() cycle while freewheeling. -_ the regular process() call to session->process() is not made. - */ - - PBD::Signal1 Freewheel; - - PBD::Signal0 Xrun; - - /* this signal is if JACK notifies us of a graph order event */ - - PBD::Signal0 GraphReordered; + framecnt_t processed_frames() const { return _processed_frames; } + + float get_cpu_load(); + + void set_session (Session *); + void remove_session (); // not a replacement for SessionHandle::session_going_away() + + class NoBackendAvailable : public std::exception { + public: + virtual const char *what() const throw() { return "could not connect to engine backend"; } + }; + + void split_cycle (pframes_t offset); + + int reset_timebase (); + + void update_latencies (); + + /* this signal is sent for every process() cycle while freewheeling. + (the regular process() call to session->process() is not made) + */ + + PBD::Signal1 Freewheel; + + PBD::Signal0 Xrun; + + /* this signal is if the backend notifies us of a graph order event */ + + PBD::Signal0 GraphReordered; + #ifdef HAVE_JACK_SESSION - PBD::Signal1 JackSessionEvent; + PBD::Signal1 JackSessionEvent; #endif - - - /* this signal is emitted if the sample rate changes */ - - PBD::Signal1 SampleRateChanged; - - /* this signal is sent if JACK ever disconnects us */ - - PBD::Signal1 Halted; - - /* these two are emitted when the engine itself is - started and stopped - */ - - PBD::Signal0 Running; - PBD::Signal0 Stopped; - - /** Emitted if a JACK port is registered or unregistered */ - PBD::Signal0 PortRegisteredOrUnregistered; - - /** Emitted if a JACK port is connected or disconnected. - * The Port parameters are the ports being connected / disconnected, or 0 if they are not known to Ardour. - * The std::string parameters are the (long) port names. - * The bool parameter is true if ports were connected, or false for disconnected. - */ - PBD::Signal5, std::string, boost::weak_ptr, std::string, bool> PortConnectedOrDisconnected; - - std::string make_port_name_relative (std::string) const; - std::string make_port_name_non_relative (std::string) const; - bool port_is_mine (const std::string&) const; - - static AudioEngine* instance() { return _instance; } - static void destroy(); - void died (); - - int create_process_thread (boost::function, pthread_t*, size_t stacksize); - -private: - static AudioEngine* _instance; - - Glib::Threads::Mutex _process_lock; - Glib::Threads::Cond session_removed; - bool session_remove_pending; - frameoffset_t session_removal_countdown; - gain_t session_removal_gain; - gain_t session_removal_gain_step; - bool _running; - bool _has_run; - mutable framecnt_t _buffer_size; - std::map _raw_buffer_sizes; - mutable framecnt_t _frame_rate; - /// number of frames between each check for changes in monitor input - framecnt_t monitor_check_interval; - /// time of the last monitor check in frames - framecnt_t last_monitor_check; - /// the number of frames processed since start() was called - framecnt_t _processed_frames; - bool _freewheeling; - bool _pre_freewheel_mmc_enabled; - int _usecs_per_cycle; - bool port_remove_in_progress; - Glib::Threads::Thread* m_meter_thread; - ProcessThread* _main_thread; - - - SerializedRCUManager ports; - - boost::shared_ptr register_port (DataType type, const std::string& portname, bool input); - - int process_callback (pframes_t nframes); - void* process_thread (); - void remove_all_ports (); - - void port_registration_failure (const std::string& portname); - - void meter_thread (); - void start_metering_thread (); - void stop_metering_thread (); - - static gint m_meter_exit; - - void parameter_changed (const std::string&); - PBD::ScopedConnection config_connection; + + /* this signal is emitted if the sample rate changes */ + + PBD::Signal1 SampleRateChanged; + + /* this signal is sent if the backend ever disconnects us */ + + PBD::Signal1 Halted; + + /* these two are emitted when the engine itself is + started and stopped + */ + + PBD::Signal0 Running; + PBD::Signal0 Stopped; + + /** Emitted if a Port is registered or unregistered */ + PBD::Signal0 PortRegisteredOrUnregistered; + + /** Emitted if a Port is connected or disconnected. + * The Port parameters are the ports being connected / disconnected, or 0 if they are not known to Ardour. + * The std::string parameters are the (long) port names. + * The bool parameter is true if ports were connected, or false for disconnected. + */ + PBD::Signal5, std::string, boost::weak_ptr, std::string, bool> PortConnectedOrDisconnected; + + std::string make_port_name_relative (std::string) const; + std::string make_port_name_non_relative (std::string) const; + bool port_is_mine (const std::string&) const; + + static AudioEngine* instance() { return _instance; } + static void destroy(); + void died (); + + /* The backend will cause this at the appropriate time(s) + */ + int process_callback (pframes_t nframes); + + private: + static AudioEngine* _instance; + + Glib::Threads::Mutex _process_lock; + Glib::Threads::Cond session_removed; + bool session_remove_pending; + frameoffset_t session_removal_countdown; + gain_t session_removal_gain; + gain_t session_removal_gain_step; + bool _running; + bool _has_run; + mutable framecnt_t _buffer_size; + std::map _raw_buffer_sizes; + mutable framecnt_t _frame_rate; + /// number of frames between each check for changes in monitor input + framecnt_t monitor_check_interval; + /// time of the last monitor check in frames + framecnt_t last_monitor_check; + /// the number of frames processed since start() was called + framecnt_t _processed_frames; + bool _freewheeling; + bool _pre_freewheel_mmc_enabled; + int _usecs_per_cycle; + bool port_remove_in_progress; + Glib::Threads::Thread* m_meter_thread; + ProcessThread* _main_thread; + + + void meter_thread (); + void start_metering_thread (); + void stop_metering_thread (); + + static gint m_meter_exit; + + void parameter_changed (const std::string&); + PBD::ScopedConnection config_connection; }; - + } // namespace ARDOUR #endif /* __ardour_audioengine_h__ */ diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index e225117d94..5afe8e2806 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -90,11 +90,11 @@ public: virtual int connect (Port *); int disconnect (Port *); - void ensure_jack_monitors_input (bool); - bool jack_monitoring_input () const; + void request_monitor_input (bool); + void ensure_monitor_input (bool); + bool monitoring_input () const; int reestablish (); int reconnect (); - void request_jack_monitors_input (bool); bool last_monitor() const { return _last_monitor; } void set_last_monitor (bool yn) { _last_monitor = yn; } diff --git a/libs/ardour/ardour/port_manager.h b/libs/ardour/ardour/port_manager.h index 8a363899ea..f6aedc02a1 100644 --- a/libs/ardour/ardour/port_manager.h +++ b/libs/ardour/ardour/port_manager.h @@ -1,50 +1,108 @@ -namespace ARDOUR +/* + Copyright (C) 2013 Paul Davis + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libardour_port_manager_h__ +#define __libardour_port_manager_h__ + +#include +#include +#include +#include + +#include + +#include + +#include "pbd/rcu.h" + +#include "ardour/chan_count.h" + +namespace ARDOUR { + +class Port; class PortManager { public: - PortManager() {} - virtual ~PortManager() {} - - /* Port registration */ + typedef std::map > Ports; + + PortManager(); + virtual ~PortManager() {} + + /* Port registration */ + + virtual boost::shared_ptr register_input_port (DataType, const std::string& portname) = 0; + virtual boost::shared_ptr register_output_port (DataType, const std::string& portname) = 0; + virtual int unregister_port (boost::shared_ptr) = 0; + + /* Port connectivity */ + + virtual int connect (const std::string& source, const std::string& destination) = 0; + virtual int disconnect (const std::string& source, const std::string& destination) = 0; + virtual int disconnect (boost::shared_ptr) = 0; + + /* other Port management */ + + virtual bool port_is_physical (const std::string&) const = 0; + virtual void get_physical_outputs (DataType type, std::vector&) = 0; + virtual void get_physical_inputs (DataType type, std::vector&) = 0; + virtual boost::shared_ptr get_port_by_name (const std::string &) = 0; + virtual void port_renamed (const std::string&, const std::string&) = 0; + virtual ChanCount n_physical_outputs () const = 0; + virtual ChanCount n_physical_inputs () const = 0; + virtual const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags) = 0; + + void remove_all_ports (); + + /* per-Port monitoring */ + + virtual bool can_request_input_monitoring () const = 0; + virtual void request_input_monitoring (const std::string&, bool) const = 0; + + class PortRegistrationFailure : public std::exception { + public: + PortRegistrationFailure (std::string const & why = "") + : reason (why) {} - virtual boost::shared_ptr register_input_port (DataType, const std::string& portname) = 0; - virtual boost::shared_ptr register_output_port (DataType, const std::string& portname) = 0; - virtual int unregister_port (boost::shared_ptr) = 0; + ~PortRegistrationFailure () throw () {} - /* Port connectivity */ + virtual const char *what() const throw () { return reason.c_str(); } - virtual int connect (const std::string& source, const std::string& destination) = 0; - virtual int disconnect (const std::string& source, const std::string& destination) = 0; - virtual int disconnect (boost::shared_ptr) = 0; - - /* other Port management */ - - virtual bool port_is_physical (const std::string&) const = 0; - virtual void get_physical_outputs (DataType type, std::vector&) = 0; - virtual void get_physical_inputs (DataType type, std::vector&) = 0; - virtual boost::shared_ptr get_port_by_name (const std::string &) = 0; - virtual void port_renamed (const std::string&, const std::string&) = 0; - virtual ChanCount n_physical_outputs () const = 0; - virtual ChanCount n_physical_inputs () const = 0; - virtual const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags) = 0; + private: + std::string reason; + }; - /* per-Port monitoring */ - - virtual bool can_request_input_monitoring () const = 0; - virtual void request_input_monitoring (const std::string&, bool) const = 0; - - class PortRegistrationFailure : public std::exception { - public: - PortRegistrationFailure (std::string const & why = "") - : reason (why) {} - - ~PortRegistrationFailure () throw () {} - - virtual const char *what() const throw () { return reason.c_str(); } - - private: - std::string reason; - }; + protected: + typedef void* PortHandle; + PortHandle register (const std::string&, DataType type, Port::Flags); + void unregister (PortHandle); + bool connected (PortHandle); + int disconnect_all (PortHandle); + bool connected_to (PortHandle, const std::string); + int get_connections (PortHandle, std::vector&); + private: + SerializedRCUManager ports; + boost::shared_ptr register_port (DataType type, const std::string& portname, bool input); + void port_registration_failure (const std::string& portname); }; + +} + +#endif /* __libardour_port_manager_h__ */ diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 1166b906f8..25a55b5aaf 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -70,7 +70,6 @@ AudioEngine::AudioEngine () , port_remove_in_progress (false) , m_meter_thread (0) , _main_thread (0) - , ports (new Ports) { g_atomic_int_set (&m_meter_exit, 0); @@ -330,7 +329,7 @@ AudioEngine::process_callback (pframes_t nframes) bool x; - if (i->second->last_monitor() != (x = i->second->jack_monitoring_input ())) { + if (i->second->last_monitor() != (x = i->second->monitoring_input ())) { i->second->set_last_monitor (x); /* XXX I think this is dangerous, due to a likely mutex in the signal handlers ... @@ -878,9 +877,9 @@ AudioEngine::reset_timebase () GET_PRIVATE_JACK_POINTER_RET (_jack, -1); if (_session) { if (_session->config.get_jack_time_master()) { - return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); + _backend->set_time_master (true); } else { - return jack_release_timebase (_jack); + _backend->set_time_master (false); } } return 0; diff --git a/libs/ardour/jack_audiobackend.cc b/libs/ardour/jack_audiobackend.cc index b5e21b8207..2d614a80a0 100644 --- a/libs/ardour/jack_audiobackend.cc +++ b/libs/ardour/jack_audiobackend.cc @@ -114,7 +114,7 @@ JACKAudioBackend::disconnect_from_jack () { int -AudioEngine::reconnect_to_jack () +JACKAudioBackend::reconnect_to_jack () { if (_running) { disconnect_from_jack (); @@ -194,41 +194,116 @@ JACKAudioBackend::request_buffer_size (pframes_t nframes) /* --- TRANSPORT STATE MANAGEMENT --- */ void -AudioEngine::transport_stop () +JACKAudioBackend::transport_stop () { GET_PRIVATE_JACK_POINTER (_jack); jack_transport_stop (_priv_jack); } void -AudioEngine::transport_start () +JACKAudioBackend::transport_start () { GET_PRIVATE_JACK_POINTER (_jack); jack_transport_start (_priv_jack); } void -AudioEngine::transport_locate (framepos_t where) +JACKAudioBackend::transport_locate (framepos_t where) { GET_PRIVATE_JACK_POINTER (_jack); jack_transport_locate (_priv_jack, where); } framepos_t -AudioEngine::transport_frame () const +JACKAudioBackend::transport_frame () const { GET_PRIVATE_JACK_POINTER_RET (_jack, 0); return jack_get_current_transport_frame (_priv_jack); } -AudioEngine::TransportState -AudioEngine::transport_state () +JACKAudioBackend::TransportState +JACKAudioBackend::transport_state () { GET_PRIVATE_JACK_POINTER_RET (_jack, ((TransportState) JackTransportStopped)); jack_position_t pos; return (TransportState) jack_transport_query (_priv_jack, &pos); } +int +JACKAudioBackend::set_time_master (bool yn) +{ + GET_PRIVATE_JACK_POINTER_RET (_jack, -1); + if (yn) { + return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); + } else { + return jack_release_timebase (_jack); + } +} + +/* process-time */ + +framecnt_t frame_rate () const; +pframes_t frames_per_cycle () const; + +size_t raw_buffer_size(DataType t); + +int usecs_per_cycle () const { return _usecs_per_cycle; } + +bool +JACKAudioBackend::get_sync_offset (pframes_t& offset) const +{ + +#ifdef HAVE_JACK_VIDEO_SUPPORT + + GET_PRIVATE_JACK_POINTER_RET (_jack, false); + + jack_position_t pos; + + if (_priv_jack) { + (void) jack_transport_query (_priv_jack, &pos); + + if (pos.valid & JackVideoFrameOffset) { + offset = pos.video_offset; + return true; + } + } +#else + /* keep gcc happy */ + offset = 0; +#endif + + return false; +} + +pframes_t +JACKAudioBackend::frames_since_cycle_start () +{ + jack_client_t* _priv_jack = _jack; + if (!_running || !_priv_jack) { + return 0; + } + return jack_frames_since_cycle_start (_priv_jack); +} + +pframes_t +JACKAudioBackend::frame_time () +{ + jack_client_t* _priv_jack = _jack; + if (!_running || !_priv_jack) { + return 0; + } + return jack_frame_time (_priv_jack); +} + +pframes_t +JACKAudioBackend::frame_time_at_cycle_start () +{ + jack_client_t* _priv_jack = _jack; + if (!_running || !_priv_jack) { + return 0; + } + return jack_last_frame_time (_priv_jack); +} /* JACK Callbacks */ @@ -418,6 +493,32 @@ JACKAudioBackend::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, in ); /* EMIT SIGNAL */ } +int +JACKAudioBackend::create_process_thread (boost::function f, pthread_t* thread, size_t stacksize) +{ + GET_PRIVATE_JACK_POINTER_RET (_jack, 0); + ThreadData* td = new ThreadData (this, f, stacksize); + + if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack), + jack_is_realtime (_priv_jack), _start_process_thread, td)) { + return -1; + } + + return 0; +} + +void* +JACKAudioBackend::_start_process_thread (void* arg) +{ + ThreadData* td = reinterpret_cast (arg); + boost::function f = td->f; + delete td; + + f (); + + return 0; +} + void* JACKAudioBackend::_process_thread (void *arg) { diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 3473b73617..859f1ff8ab 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -242,13 +242,19 @@ Port::set_engine (AudioEngine* e) } void -Port::ensure_jack_monitors_input (bool yn) +Port::request_monitor_input (bool yn) +{ + jack_port_request_monitor (_jack_port, yn); +} + +void +Port::ensure_monitor_input (bool yn) { jack_port_ensure_monitor (_jack_port, yn); } bool -Port::jack_monitoring_input () const +Port::monitoring_input () const { return jack_port_monitoring_input (_jack_port); } @@ -490,12 +496,6 @@ Port::set_name (std::string const & n) return r; } -void -Port::request_jack_monitors_input (bool yn) -{ - jack_port_request_monitor (_jack_port, yn); -} - bool Port::physically_connected () const {