diff options
Diffstat (limited to 'libs/ardour')
56 files changed, 5594 insertions, 1666 deletions
diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h new file mode 100644 index 0000000000..77b7eadb48 --- /dev/null +++ b/libs/ardour/ardour/audio_backend.h @@ -0,0 +1,410 @@ +/* + 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_audiobackend_h__ +#define __libardour_audiobackend_h__ + +#include <string> +#include <vector> + +#include <stdint.h> +#include <stdlib.h> + +#include <boost/function.hpp> + +#include "ardour/types.h" + +namespace ARDOUR { + +class AudioEngine; +class PortEngine; +class PortManager; + +class AudioBackend { + public: + + AudioBackend (AudioEngine& e) : engine (e){} + virtual ~AudioBackend () {} + + /** Return the name of this backend. + * + * Should use a well-known, unique term. Expected examples + * might include "JACK", "CoreAudio", "ASIO" etc. + */ + virtual std::string name() const = 0; + + /** Return a private, type-free pointer to any data + * that might be useful to a concrete implementation + */ + virtual void* private_handle() const = 0; + + /** Return true if the underlying mechanism/API is still available + * for us to utilize. return false if some or all of the AudioBackend + * API can no longer be effectively used. + */ + virtual bool connected() const = 0; + + /** Return true if the callback from the underlying mechanism/API + * (CoreAudio, JACK, ASIO etc.) occurs in a thread subject to realtime + * constraints. Return false otherwise. + */ + virtual bool is_realtime () const = 0; + + /* Discovering devices and parameters */ + + /** Return true if this backend requires the selection of a "driver" + * before any device can be selected. Return false otherwise. + * + * Intended mainly to differentiate between meta-APIs like JACK + * which can still expose different backends (such as ALSA or CoreAudio + * or FFADO or netjack) and those like ASIO or CoreAudio which + * do not. + */ + virtual bool requires_driver_selection() const { return false; } + + /** If the return value of requires_driver_selection() is true, + * then this function can return the list of known driver names. + * + * If the return value of requires_driver_selection() is false, + * then this function should not be called. If it is called + * its return value is an empty vector of strings. + */ + virtual std::vector<std::string> enumerate_drivers() const { return std::vector<std::string>(); } + + /** Returns zero if the backend can successfully use @param name as the + * driver, non-zero otherwise. + * + * Should not be used unless the backend returns true from + * requires_driver_selection() + */ + virtual int set_driver (const std::string& /*drivername*/) { return 0; } + + /** used to list device names along with whether or not they are currently + * available. + */ + struct DeviceStatus { + std::string name; + bool available; + + DeviceStatus (const std::string& s, bool avail) : name (s), available (avail) {} + }; + + /** Returns a collection of DeviceStatuses identifying devices discovered + * by this backend since the start of the process. + * + * Any of the names in each DeviceStatus may be used to identify a + * device in other calls to the backend, though any of them may become + * invalid at any time. + */ + virtual std::vector<DeviceStatus> enumerate_devices () const = 0; + + /** Returns a collection of float identifying sample rates that are + * potentially usable with the hardware identified by @param device. + * Any of these values may be supplied in other calls to this backend + * as the desired sample rate to use with the name device, but the + * requested sample rate may turn out to be unavailable, or become invalid + * at any time. + */ + virtual std::vector<float> available_sample_rates (const std::string& device) const = 0; + /** Returns a collection of uint32 identifying buffer sizes that are + * potentially usable with the hardware identified by @param device. + * Any of these values may be supplied in other calls to this backend + * as the desired buffer size to use with the name device, but the + * requested buffer size may turn out to be unavailable, or become invalid + * at any time. + */ + virtual std::vector<uint32_t> available_buffer_sizes (const std::string& device) const = 0; + + /** 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; + + /* 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; + + /* Retrieving parameters */ + + virtual std::string device_name () const = 0; + virtual float sample_rate () const = 0; + virtual uint32_t buffer_size () const = 0; + virtual SampleFormat sample_format () const = 0; + virtual bool interleaved () const = 0; + virtual uint32_t input_channels () const = 0; + virtual uint32_t output_channels () const = 0; + virtual uint32_t systemic_input_latency () const = 0; + virtual uint32_t systemic_output_latency () const = 0; + + /* Basic state control */ + + /** Start using the device named in the most recent 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 + * the AudioEngine referenced by @param engine. These calls will + * occur in a thread created by and/or under the control of the backend. + * + * Return zero if successful, negative values otherwise. + */ + virtual int start () = 0; + + /** 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 parameters are reset and start() are called again. + * + * The backend is considered to be un-configured after a successful + * 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. + * + * Return zero if successful, 1 if the device is not in use, negative values on error + */ + virtual int stop () = 0; + + /** Temporarily cease using the device named in the most recent call to set_parameters(). + * + * If the function is successfully called, no subsequent calls to the + * process_callback() of @param engine will be made after the function + * returns, until start() is called again. + * + * The backend will retain its existing parameter configuration after a successful + * 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 + */ + virtual int pause () = 0; + + /** 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. Once process_callback() has returned, it + * will be called again immediately, thus allowing for faster-than-realtime + * processing. + * + * 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. + * + * Return zero on success, non-zero otherwise. + */ + virtual int freewheel (bool start_stop) = 0; + + /** 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 cpu_load() const = 0; + + /* Transport Control (JACK is the only audio API that currently offers + the concept of shared transport control) + */ + + /** Attempt to change the transport state to TransportRolling. + */ + virtual void transport_start () {} + /** Attempt to change the transport state to TransportStopped. + */ + virtual void transport_stop () {} + /** return the current transport state + */ + virtual TransportState transport_state () const { return TransportStopped; } + /** Attempt to locate the transport to @param pos + */ + virtual void transport_locate (framepos_t /*pos*/) {} + /** Return the current transport location, in samples measured + * from the origin (defined by the transport time master) + */ + virtual framepos_t transport_frame() const { 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 int usecs_per_cycle () const { return 1000000 * (buffer_size() / sample_rate()); } + virtual size_t raw_buffer_size (DataType t) = 0; + + /* Process time */ + + /** return the time according to the sample clock in use, measured in + * samples since an arbitrary zero time in the past. The value should + * increase monotonically and linearly, without interruption from any + * source (including CPU frequency scaling). + * + * It is extremely likely that any implementation will use a DLL, since + * this function can be called from any thread, at any time, and must be + * able to accurately determine the correct sample time. + * + * Can be called from any thread. + */ + virtual pframes_t sample_time () = 0; + + /** Return the time according to the sample clock in use when the most + * recent buffer process cycle began. Can be called from any thread. + */ + virtual pframes_t sample_time_at_cycle_start () = 0; + + /** Return the time since the current buffer process cycle started, + * in samples, according to the sample clock in use. + * + * Can ONLY be called from within a process() callback tree (which + * implies that it can only be called by a process thread) + */ + virtual pframes_t samples_since_cycle_start () = 0; + + /** Return true if it possible to determine the offset in samples of the + * first video frame that starts within the current buffer process cycle, + * measured from the first sample of the cycle. If returning true, + * set @param offset to that offset. + * + * Eg. if it can be determined that the first video frame within the cycle + * starts 28 samples after the first sample of the cycle, then this method + * should return true and set @param offset to 28. + * + * May be impossible to support outside of JACK, which has specific support + * (in some cases, hardware support) for this feature. + * + * Can ONLY be called from within a process() callback tree (which implies + * that it can only be called by a process thread) + */ + virtual bool get_sync_offset (pframes_t& /*offset*/) const { return false; } + + /** 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<void()> func, pthread_t*, size_t stacksize) = 0; + + virtual void update_latencies () = 0; + + protected: + AudioEngine& engine; +}; + +struct AudioBackendInfo { + const char* name; + + int (*instantiate) (const std::string& arg1, const std::string& arg2); + int (*deinstantiate) (void); + + boost::shared_ptr<AudioBackend> (*backend_factory) (AudioEngine&); + boost::shared_ptr<PortEngine> (*portengine_factory) (PortManager&); + + /** Return true if the underlying mechanism/API has been + * configured and does not need (re)configuration in order + * to be usable. Return false otherwise. + * + * Note that this may return true if (re)configuration, even though + * not currently required, is still possible. + */ + bool (*already_configured)(); +}; + +} // namespace + +#endif /* __libardour_audiobackend_h__ */ + diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index b636cb4734..5a856e9b36 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -114,7 +114,7 @@ class AudioDiskstream : public Diskstream XMLNode& get_state(void); int set_state(const XMLNode& node, int version); - void request_jack_monitors_input (bool); + void request_input_monitoring (bool); static void swap_by_ptr (Sample *first, Sample *last) { while (first < last) { @@ -160,7 +160,7 @@ class AudioDiskstream : public Diskstream std::string name; bool is_physical () const; - void request_jack_monitors_input (bool) const; + void request_input_monitoring (bool) const; }; /** Information about one of our channels */ diff --git a/libs/ardour/ardour/audio_port.h b/libs/ardour/ardour/audio_port.h index 7f084a5c85..f5affb0580 100644 --- a/libs/ardour/ardour/audio_port.h +++ b/libs/ardour/ardour/audio_port.h @@ -46,10 +46,12 @@ class AudioPort : public Port AudioBuffer& get_audio_buffer (pframes_t nframes); protected: - friend class AudioEngine; + friend class PortManager; + AudioPort (std::string const &, PortFlags); - AudioPort (std::string const &, Flags); - /* special access for engine only */ + protected: + friend class AudioEngine; + /* special access for engine only (hah, C++) */ Sample* engine_get_whole_audio_buffer (); private: diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 165ad6744f..6fb13b7ae0 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -33,7 +33,6 @@ #include <glibmm/threads.h> -#include "pbd/rcu.h" #include "pbd/signals.h" #include "pbd/stacktrace.h" @@ -48,6 +47,7 @@ #include "ardour/session_handle.h" #include "ardour/types.h" #include "ardour/chan_count.h" +#include "ardour/port_manager.h" #ifdef HAVE_JACK_SESSION #include <jack/session.h> @@ -60,299 +60,178 @@ class MidiPort; class Port; class Session; class ProcessThread; +class AudioBackend; +class AudioBackendInfo; -class AudioEngine : public SessionHandlePtr +class AudioEngine : public SessionHandlePtr, public PortManager { public: - typedef std::map<std::string,boost::shared_ptr<Port> > Ports; - AudioEngine (std::string client_name, std::string session_uuid); - virtual ~AudioEngine (); - - jack_client_t* jack() const; - bool connected() const { return _jack != 0; } - - bool is_realtime () const; - - ProcessThread* main_thread() const { return _main_thread; } - - std::string client_name() const { return jack_client_name; } - - int reconnect_to_jack (); - int disconnect_from_jack(); - - int stop (bool forever = false); - int start (); - bool running() const { return _running; } - - Glib::Threads::Mutex& process_lock() { return _process_lock; } - - 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 get_sync_offset (pframes_t & offset) const; - - pframes_t 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 frame_time () { - jack_client_t* _priv_jack = _jack; - if (!_running || !_priv_jack) { - return 0; - } - return jack_frame_time (_priv_jack); - } - - pframes_t frame_time_at_cycle_start () { - jack_client_t* _priv_jack = _jack; - if (!_running || !_priv_jack) { - return 0; - } - return jack_last_frame_time (_priv_jack); - } - - pframes_t transport_frame () const { - const jack_client_t* _priv_jack = _jack; - if (!_running || !_priv_jack) { - return 0; - } - return jack_get_current_transport_frame (_priv_jack); - } - - 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 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; - }; - - class NoBackendAvailable : public std::exception { - public: - virtual const char *what() const throw() { return "could not connect to engine backend"; } - }; - - boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname); - boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname); - int unregister_port (boost::shared_ptr<Port>); - - bool port_is_physical (const std::string&) const; - void request_jack_monitors_input (const std::string&, bool) const; - - void split_cycle (pframes_t offset); - - int connect (const std::string& source, const std::string& destination); - int disconnect (const std::string& source, const std::string& destination); - int disconnect (boost::shared_ptr<Port>); - - const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags); - - bool can_request_hardware_monitoring (); - - ChanCount n_physical_outputs () const; - ChanCount n_physical_inputs () const; - - void get_physical_outputs (DataType type, std::vector<std::string>&); - void get_physical_inputs (DataType type, std::vector<std::string>&); - - boost::shared_ptr<Port> get_port_by_name (const std::string &); - void port_renamed (const std::string&, const std::string&); - - enum TransportState { - TransportStopped = JackTransportStopped, - TransportRolling = JackTransportRolling, - TransportLooping = JackTransportLooping, - TransportStarting = JackTransportStarting - }; - - void transport_start (); - void transport_stop (); - void transport_locate (framepos_t); - TransportState transport_state (); - - 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<int, pframes_t> Freewheel; - - PBD::Signal0<void> Xrun; - - /* this signal is if JACK notifies us of a graph order event */ - - PBD::Signal0<void> GraphReordered; + static AudioEngine* create (); + + virtual ~AudioEngine (); + + int discover_backends(); + std::vector<const AudioBackendInfo*> available_backends() const; + std::string current_backend_name () const; + boost::shared_ptr<AudioBackend> set_backend (const std::string&, const std::string& arg1, const std::string& arg2); + boost::shared_ptr<AudioBackend> current_backend() const { return _backend; } + bool setup_required () const; + + ProcessThread* main_thread() const { return _main_thread; } + + /* START BACKEND PROXY API + * + * See audio_backend.h for full documentation and semantics. These wrappers + * just forward to a backend implementation. + */ + + int start (); + int stop (); + int pause (); + int freewheel (bool start_stop); + float get_cpu_load() const ; + void transport_start (); + void transport_stop (); + TransportState transport_state (); + void transport_locate (framepos_t pos); + framepos_t transport_frame(); + framecnt_t sample_rate () const; + pframes_t samples_per_cycle () const; + int usecs_per_cycle () const; + size_t raw_buffer_size (DataType t); + pframes_t sample_time (); + pframes_t sample_time_at_cycle_start (); + pframes_t samples_since_cycle_start (); + bool get_sync_offset (pframes_t& offset) const; + int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize); + bool is_realtime() const; + bool connected() const; + + int set_device_name (const std::string&); + int set_sample_rate (float); + int set_buffer_size (uint32_t); + int set_sample_format (SampleFormat); + int set_interleaved (bool yn); + int set_input_channels (uint32_t); + int set_output_channels (uint32_t); + int set_systemic_input_latency (uint32_t); + int set_systemic_output_latency (uint32_t); + + /* END BACKEND PROXY API */ + + bool freewheeling() const { return _freewheeling; } + bool running() const { return _running; } + + Glib::Threads::Mutex& process_lock() { return _process_lock; } + + int request_buffer_size (pframes_t samples) { + return set_buffer_size (samples); + } + + framecnt_t processed_frames() const { return _processed_frames; } + + 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<int, pframes_t> Freewheel; + + PBD::Signal0<void> Xrun; + #ifdef HAVE_JACK_SESSION - PBD::Signal1<void,jack_session_event_t *> JackSessionEvent; + PBD::Signal1<void,jack_session_event_t *> JackSessionEvent; #endif - - - /* this signal is emitted if the sample rate changes */ - - PBD::Signal1<void, framecnt_t> SampleRateChanged; - - /* this signal is sent if JACK ever disconnects us */ - - PBD::Signal1<void,const char*> Halted; - - /* these two are emitted when the engine itself is - started and stopped - */ - - PBD::Signal0<void> Running; - PBD::Signal0<void> Stopped; - - /** Emitted if a JACK port is registered or unregistered */ - PBD::Signal0<void> 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<void, boost::weak_ptr<Port>, std::string, boost::weak_ptr<Port>, 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<void()>, pthread_t*, size_t stacksize); - -private: - static AudioEngine* _instance; - - jack_client_t* volatile _jack; /* could be reset to null by SIGPIPE or another thread */ - std::string jack_client_name; - 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<DataType,size_t> _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> ports; - - boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input); - - int process_callback (pframes_t nframes); - void* process_thread (); - void remove_all_ports (); - - ChanCount n_physical (unsigned long) const; - void get_physical (DataType, unsigned long, std::vector<std::string> &); - - void port_registration_failure (const std::string& portname); - - static int _xrun_callback (void *arg); -#ifdef HAVE_JACK_SESSION - static void _session_callback (jack_session_event_t *event, void *arg); -#endif - static int _graph_order_callback (void *arg); - static void* _process_thread (void *arg); - static int _sample_rate_callback (pframes_t nframes, void *arg); - static int _bufsize_callback (pframes_t nframes, void *arg); - static void _jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int, void*); - static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg); - static void _freewheel_callback (int , void *arg); - static void _registration_callback (jack_port_id_t, int, void *); - static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *); - - void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int); - int jack_sync_callback (jack_transport_state_t, jack_position_t*); - int jack_bufsize_callback (pframes_t); - int jack_sample_rate_callback (pframes_t); - void freewheel_callback (int); - void connect_callback (jack_port_id_t, jack_port_id_t, int); - - void set_jack_callbacks (); - - static void _latency_callback (jack_latency_callback_mode_t, void*); - void jack_latency_callback (jack_latency_callback_mode_t); - - int connect_to_jack (std::string client_name, std::string session_uuid); - - static void halted (void *); - static void halted_info (jack_status_t,const char*,void *); - - void meter_thread (); - void start_metering_thread (); - void stop_metering_thread (); - - static gint m_meter_exit; - - struct ThreadData { - AudioEngine* engine; - boost::function<void()> f; - size_t stacksize; - - ThreadData (AudioEngine* ae, boost::function<void()> fp, size_t stacksz) - : engine (ae) , f (fp) , stacksize (stacksz) {} - }; - - static void* _start_process_thread (void*); - void parameter_changed (const std::string&); - PBD::ScopedConnection config_connection; + + /* this signal is emitted if the sample rate changes */ + + PBD::Signal1<void, framecnt_t> SampleRateChanged; + + /* this signal is sent if the backend ever disconnects us */ + + PBD::Signal1<void,const char*> Halted; + + /* these two are emitted when the engine itself is + started and stopped + */ + + PBD::Signal0<void> Running; + PBD::Signal0<void> Stopped; + + static AudioEngine* instance() { return _instance; } + static void destroy(); + void died (); + + /* The backend will cause these at the appropriate time(s) + */ + int process_callback (pframes_t nframes); + int buffer_size_change (pframes_t nframes); + int sample_rate_change (pframes_t nframes); + void freewheel_callback (bool); + void timebase_callback (TransportState state, pframes_t nframes, framepos_t pos, int new_position); + int sync_callback (TransportState state, framepos_t position); + int port_registration_callback (); + void latency_callback (bool for_playback); + void halted_callback (const char* reason); + + /* sets up the process callback thread */ + static void thread_init_callback (void *); + + private: + AudioEngine (); + + static AudioEngine* _instance; + + boost::shared_ptr<AudioBackend> _backend; + + 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; + /// 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; + 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; + + typedef std::map<std::string,AudioBackendInfo*> BackendMap; + BackendMap _backends; + AudioBackendInfo* backend_discover (const std::string&); + void drop_backend (); }; - + } // namespace ARDOUR #endif /* __ardour_audioengine_h__ */ diff --git a/libs/ardour/ardour/backend_search_path.h b/libs/ardour/ardour/backend_search_path.h new file mode 100644 index 0000000000..2adc22bd6f --- /dev/null +++ b/libs/ardour/ardour/backend_search_path.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2011 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 __ardour_backend_search_path_h__ +#define __ardour_backend_search_path_h__ + +#include "pbd/search_path.h" + +namespace ARDOUR { + + /** + * return a SearchPath containing directories in which to look for + * backend plugins. + * + * If ARDOUR_BACKEND_PATH is defined then the SearchPath returned + * will contain only those directories specified in it, otherwise it will + * contain the user and system directories which may contain audio/MIDI + * backends. + */ + PBD::SearchPath backend_search_path (); + +} // namespace ARDOUR + +#endif /* __ardour_backend_search_path_h__ */ diff --git a/libs/ardour/ardour/data_type.h b/libs/ardour/ardour/data_type.h index 6a016ae75a..b4129e2629 100644 --- a/libs/ardour/ardour/data_type.h +++ b/libs/ardour/ardour/data_type.h @@ -21,11 +21,11 @@ #define __ardour_data_type_h__ #include <string> -#include <jack/jack.h> +#include <stdint.h> +#include <glib.h> namespace ARDOUR { - /** A type of Data Ardour is capable of processing. * * The majority of this class is dedicated to conversion to and from various @@ -61,33 +61,25 @@ public: /** Construct from a string (Used for loading from XML and Ports) * The string can be as in an XML file (eg "audio" or "midi"), or a - * Jack type string (from jack_port_type) */ + */ DataType(const std::string& str) : _symbol(NIL) { - if (str == "audio" || str == JACK_DEFAULT_AUDIO_TYPE) + if (!g_ascii_strncasecmp(str.c_str(), "audio", str.length())) { _symbol = AUDIO; - else if (str == "midi" || str == JACK_DEFAULT_MIDI_TYPE) + } else if (!g_ascii_strncasecmp(str.c_str(), "midi", str.length())) { _symbol = MIDI; - } - - /** Get the Jack type this DataType corresponds to */ - const char* to_jack_type() const { - switch (_symbol) { - case AUDIO: return JACK_DEFAULT_AUDIO_TYPE; - case MIDI: return JACK_DEFAULT_MIDI_TYPE; - default: return ""; } } /** Inverse of the from-string constructor */ const char* to_string() const { switch (_symbol) { - case AUDIO: return "audio"; - case MIDI: return "midi"; - default: return "unknown"; // reeeally shouldn't ever happen + case AUDIO: return "audio"; + case MIDI: return "midi"; + default: return "unknown"; // reeeally shouldn't ever happen } } - + const char* to_i18n_string () const; inline operator uint32_t() const { return (uint32_t)_symbol; } @@ -125,7 +117,6 @@ private: }; - } // namespace ARDOUR #endif // __ardour_data_type_h__ diff --git a/libs/ardour/ardour/directory_names.h b/libs/ardour/ardour/directory_names.h index 9f7c778d33..935cdd977b 100644 --- a/libs/ardour/ardour/directory_names.h +++ b/libs/ardour/ardour/directory_names.h @@ -38,6 +38,7 @@ extern const char* const route_templates_dir_name; extern const char* const surfaces_dir_name; extern const char* const user_config_dir_name; extern const char* const panner_dir_name; +extern const char* const backend_dir_name; }; diff --git a/libs/ardour/ardour/jack_audiobackend.h b/libs/ardour/ardour/jack_audiobackend.h new file mode 100644 index 0000000000..9fa3d0c1cc --- /dev/null +++ b/libs/ardour/ardour/jack_audiobackend.h @@ -0,0 +1,186 @@ +/* + 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_jack_audiobackend_h__ +#define __libardour_jack_audiobackend_h__ + +#include <string> +#include <vector> +#include <map> +#include <set> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include <jack/jack.h> +#ifdef HAVE_JACK_SESSION +#include <jack/session.h> +#endif + +#include "ardour/audio_backend.h" + +namespace ARDOUR { + +class JackConnection; + +class JACKAudioBackend : public AudioBackend { + public: + JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection>); + ~JACKAudioBackend (); + + std::string name() const; + void* private_handle() const; + bool connected() const; + bool is_realtime () const; + + bool requires_driver_selection() const; + std::vector<std::string> enumerate_drivers () const; + int set_driver (const std::string&); + + std::vector<DeviceStatus> enumerate_devices () const; + + std::vector<float> available_sample_rates (const std::string& device) const; + std::vector<uint32_t> available_buffer_sizes (const std::string& device) const; + uint32_t available_input_channel_count (const std::string& device) const; + uint32_t available_output_channel_count (const std::string& device) const; + + int set_device_name (const std::string&); + int set_sample_rate (float); + int set_buffer_size (uint32_t); + int set_sample_format (SampleFormat); + int set_interleaved (bool yn); + int set_input_channels (uint32_t); + int set_output_channels (uint32_t); + int set_systemic_input_latency (uint32_t); + int set_systemic_output_latency (uint32_t); + + std::string device_name () const; + float sample_rate () const; + uint32_t buffer_size () const; + SampleFormat sample_format () const; + bool interleaved () const; + uint32_t input_channels () const; + uint32_t output_channels () const; + uint32_t systemic_input_latency () const; + uint32_t systemic_output_latency () const; + + int start (); + int stop (); + int pause (); + int freewheel (bool); + + float cpu_load() const; + + pframes_t sample_time (); + pframes_t sample_time_at_cycle_start (); + pframes_t samples_since_cycle_start (); + + size_t raw_buffer_size (DataType t); + + int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize); + + void transport_start (); + void transport_stop (); + void transport_locate (framepos_t /*pos*/); + TransportState transport_state () const; + framepos_t transport_frame() const; + + int set_time_master (bool /*yn*/); + bool get_sync_offset (pframes_t& /*offset*/) const; + + void update_latencies (); + + static bool already_configured(); + + private: + boost::shared_ptr<JackConnection> _jack_connection; //< shared with JACKPortEngine + bool _running; + bool _freewheeling; + std::map<DataType,size_t> _raw_buffer_sizes; + + static int _xrun_callback (void *arg); + static void* _process_thread (void *arg); + static int _sample_rate_callback (pframes_t nframes, void *arg); + static int _bufsize_callback (pframes_t nframes, void *arg); + static void _jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int, void*); + static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg); + static void _freewheel_callback (int , void *arg); + static void _latency_callback (jack_latency_callback_mode_t, void*); +#ifdef HAVE_JACK_SESSION + static void _session_callback (jack_session_event_t *event, void *arg); +#endif + + void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int); + int jack_sync_callback (jack_transport_state_t, jack_position_t*); + int jack_bufsize_callback (pframes_t); + int jack_sample_rate_callback (pframes_t); + void freewheel_callback (int); + int process_callback (pframes_t nframes); + void jack_latency_callback (jack_latency_callback_mode_t); + void disconnected (const char*); + + void set_jack_callbacks (); + int reconnect_to_jack (); + + struct ThreadData { + JACKAudioBackend* engine; + boost::function<void()> f; + size_t stacksize; + + ThreadData (JACKAudioBackend* e, boost::function<void()> fp, size_t stacksz) + : engine (e) , f (fp) , stacksize (stacksz) {} + }; + + void* process_thread (); + static void* _start_process_thread (void*); + + ChanCount n_physical (unsigned long) const; + + void setup_jack_startup_command (); + + /* pffooo */ + + std::string _target_driver; + 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; + uint32_t _target_systemic_input_latency; + uint32_t _target_systemic_output_latency; + + uint32_t _current_sample_rate; + uint32_t _current_buffer_size; + uint32_t _current_usecs_per_cycle; + uint32_t _current_systemic_input_latency; + uint32_t _current_systemic_output_latency; + + typedef std::set<std::string> DeviceList; + typedef std::map<std::string,DeviceList> DriverDeviceMap; + + mutable DriverDeviceMap all_devices; +}; + +} // namespace + +#endif /* __ardour_audiobackend_h__ */ + diff --git a/libs/ardour/ardour/jack_connection.h b/libs/ardour/ardour/jack_connection.h new file mode 100644 index 0000000000..cd45f3b9ba --- /dev/null +++ b/libs/ardour/ardour/jack_connection.h @@ -0,0 +1,40 @@ +#ifndef __libardour_jack_connection_h__ +#define __libardour_jack_connection_h__ + +#include <string> +#include <jack/jack.h> + +#include "pbd/signals.h" + +namespace ARDOUR { + +class JackConnection { + public: + JackConnection (const std::string& client_name, const std::string& session_uuid); + ~JackConnection (); + + const std::string& client_name() const { return _client_name; } + + int open (); + int close (); + bool connected () const { return _jack != 0; } + + jack_client_t* jack() const { return _jack; } + + PBD::Signal0<void> Connected; + PBD::Signal1<void,const char*> Disconnected; + + void halted_callback (); + void halted_info_callback (jack_status_t, const char*); + + static bool server_running(); + + private: + jack_client_t* volatile _jack; + std::string _client_name; + std::string session_uuid; +}; + +} // namespace + +#endif /* __libardour_jack_connection_h__ */ diff --git a/libs/ardour/ardour/jack_portengine.h b/libs/ardour/ardour/jack_portengine.h new file mode 100644 index 0000000000..d595b638fa --- /dev/null +++ b/libs/ardour/ardour/jack_portengine.h @@ -0,0 +1,127 @@ +/* + 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_jack_portengine_h__ +#define __libardour_jack_portengine_h__ + +#include <string> +#include <vector> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include "pbd/signals.h" + +#include "ardour/port_engine.h" +#include "ardour/types.h" + +namespace ARDOUR { + +class JackConnection; + +class JACKPortEngine : public PortEngine +{ + public: + JACKPortEngine (PortManager&, boost::shared_ptr<JackConnection>); + ~JACKPortEngine(); + + void* private_handle() const; + + const std::string& my_name() const; + + uint32_t port_name_size() const; + + int set_port_name (PortHandle, const std::string&); + std::string get_port_name (PortHandle) const; + PortHandle* get_port_by_name (const std::string&) const; + + int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&) const; + + DataType port_data_type (PortHandle) const; + + PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags); + void unregister_port (PortHandle); + + bool connected (PortHandle); + bool connected_to (PortHandle, const std::string&); + bool physically_connected (PortHandle); + int get_connections (PortHandle, std::vector<std::string>&); + int connect (PortHandle, const std::string&); + int disconnect (PortHandle, const std::string&); + int disconnect_all (PortHandle); + int connect (const std::string& src, const std::string& dst); + int disconnect (const std::string& src, const std::string& dst); + + /* MIDI */ + + int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index); + int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size); + uint32_t get_midi_event_count (void* port_buffer); + void midi_clear (void* port_buffer); + + /* Monitoring */ + + bool can_monitor_input() const; + int request_input_monitoring (PortHandle, bool); + int ensure_input_monitoring (PortHandle, bool); + bool monitoring_input (PortHandle); + + /* Latency management + */ + + void set_latency_range (PortHandle, bool for_playback, LatencyRange); + LatencyRange get_latency_range (PortHandle, bool for_playback); + + /* Physical ports */ + + bool port_is_physical (PortHandle) const; + void get_physical_outputs (DataType type, std::vector<std::string>&); + void get_physical_inputs (DataType type, std::vector<std::string>&); + ChanCount n_physical_outputs () const; + ChanCount n_physical_inputs () const; + + /* Getting access to the data buffer for a port */ + + void* get_buffer (PortHandle, pframes_t); + + /* Miscellany */ + + pframes_t sample_time_at_cycle_start (); + + private: + boost::shared_ptr<JackConnection> _jack_connection; + + static int _graph_order_callback (void *arg); + static void _registration_callback (jack_port_id_t, int, void *); + static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *); + + void connect_callback (jack_port_id_t, jack_port_id_t, int); + + ChanCount n_physical (unsigned long flags) const; + void get_physical (DataType type, unsigned long flags, std::vector<std::string>& phy) const; + + PBD::ScopedConnection jack_connection_connection; + void connected_to_jack (); + +}; + +} // namespace + +#endif /* __libardour_jack_portengine_h__ */ diff --git a/libs/ardour/ardour/jack_utils.h b/libs/ardour/ardour/jack_utils.h new file mode 100644 index 0000000000..169dc12580 --- /dev/null +++ b/libs/ardour/ardour/jack_utils.h @@ -0,0 +1,243 @@ +/* + Copyright (C) 2011 Tim Mayberry + + 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. + +*/ + +#include <stdint.h> + +#include <vector> +#include <map> +#include <string> + +namespace ARDOUR { + + // Names for the drivers on all possible systems + extern const char * const portaudio_driver_name; + extern const char * const coreaudio_driver_name; + extern const char * const alsa_driver_name; + extern const char * const oss_driver_name; + extern const char * const freebob_driver_name; + extern const char * const ffado_driver_name; + extern const char * const netjack_driver_name; + extern const char * const dummy_driver_name; + + /** + * Get a list of possible JACK audio driver names based on platform + */ + void get_jack_audio_driver_names (std::vector<std::string>& driver_names); + + /** + * Get the default JACK audio driver based on platform + */ + void get_jack_default_audio_driver_name (std::string& driver_name); + + /** + * Get a list of possible JACK midi driver names based on platform + */ + void get_jack_midi_system_names (const std::string& driver, std::vector<std::string>& driver_names); + + /** + * Get the default JACK midi driver based on platform + */ + void get_jack_default_midi_system_name (const std::string& driver_name, std::string& midi_system); + + /** + * Get a list of possible samplerates supported be JACK + */ + void get_jack_sample_rate_strings (std::vector<std::string>& sample_rates); + + /** + * @return The default samplerate + */ + std::string get_jack_default_sample_rate (); + + /** + * @return true if sample rate string was able to be converted + */ + bool get_jack_sample_rate_value_from_string (const std::string& srs, uint32_t& srv); + + /** + * Get a list of possible period sizes supported be JACK + */ + void get_jack_period_size_strings (std::vector<std::string>& samplerates); + + /** + * @return The default period size + */ + std::string get_jack_default_period_size (); + + /** + * @return true if period size string was able to be converted + */ + bool get_jack_period_size_value_from_string (const std::string& pss, uint32_t& psv); + + /** + * These are driver specific I think, so it may require a driver arg + * in future + */ + void get_jack_dither_mode_strings (const std::string& driver, std::vector<std::string>& dither_modes); + + /** + * @return The default dither mode + */ + std::string get_jack_default_dither_mode (const std::string& driver); + + /** + * @return Estimate of latency + * + * API matches current use in GUI + */ + std::string get_jack_latency_string (std::string samplerate, float periods, std::string period_size); + + /** + * Key being a readable name to display in a GUI + * Value being name used in a jack commandline + */ + typedef std::map<std::string, std::string> device_map_t; + + /** + * Use library specific code to find out what what devices exist for a given + * driver that might work in JACK. There is no easy way to find out what + * modules the JACK server supports so guess based on platform. For instance + * portaudio is cross-platform but we only return devices if built for + * windows etc + */ + void get_jack_alsa_device_names (device_map_t& devices); + void get_jack_portaudio_device_names (device_map_t& devices); + void get_jack_coreaudio_device_names (device_map_t& devices); + void get_jack_oss_device_names (device_map_t& devices); + void get_jack_freebob_device_names (device_map_t& devices); + void get_jack_ffado_device_names (device_map_t& devices); + void get_jack_netjack_device_names (device_map_t& devices); + void get_jack_dummy_device_names (device_map_t& devices); + + /* + * @return true if there were devices found for the driver + * + * @param driver The driver name returned by get_jack_audio_driver_names + * @param devices The map used to insert the drivers into, devices will be cleared before + * adding the available drivers + */ + bool get_jack_device_names_for_audio_driver (const std::string& driver, device_map_t& devices); + + /* + * @return a list of readable device names for a specific driver. + */ + std::vector<std::string> get_jack_device_names_for_audio_driver (const std::string& driver); + + /** + * @return true if the driver supports playback and recording + * on separate devices + */ + bool get_jack_audio_driver_supports_two_devices (const std::string& driver); + + bool get_jack_audio_driver_supports_latency_adjustment (const std::string& driver); + + bool get_jack_audio_driver_supports_setting_period_count (const std::string& driver); + + /** + * The possible names to use to try and find servers, this includes + * any file extensions like .exe on Windows + * + * @return true if the JACK application names for this platform could be guessed + */ + bool get_jack_server_application_names (std::vector<std::string>& server_names); + + /** + * Sets the PATH environment variable to contain directories likely to contain + * JACK servers so that if the JACK server is auto-started it can find the server + * executable. + * + * This is only modifies PATH on the mac at the moment. + */ + void set_path_env_for_jack_autostart (const std::vector<std::string>&); + + /** + * Get absolute paths to directories that might contain JACK servers on the system + * + * @return true if !server_paths.empty() + */ + bool get_jack_server_dir_paths (std::vector<std::string>& server_dir_paths); + + /** + * Get absolute paths to JACK servers on the system + * + * @return true if a server was found + */ + bool get_jack_server_paths (const std::vector<std::string>& server_dir_paths, + const std::vector<std::string>& server_names, + std::vector<std::string>& server_paths); + + + bool get_jack_server_paths (std::vector<std::string>& server_paths); + + /** + * Get absolute path to default JACK server + */ + bool get_jack_default_server_path (std::string& server_path); + + /** + * @return The name of the jack server config file + */ + std::string get_jack_server_config_file_name (); + + std::string get_jack_server_user_config_dir_path (); + + std::string get_jack_server_user_config_file_path (); + + bool write_jack_config_file (const std::string& config_file_path, const std::string& command_line); + + struct JackCommandLineOptions { + + // see implementation for defaults + JackCommandLineOptions (); + + //operator bool + //operator ostream + + std::string server_path; + uint32_t timeout; + bool no_mlock; + uint32_t ports_max; + bool realtime; + uint32_t priority; + bool unlock_gui_libs; + bool verbose; + bool temporary; + bool playback_only; + bool capture_only; + std::string driver; + std::string input_device; + std::string output_device; + uint32_t num_periods; + uint32_t period_size; + uint32_t samplerate; + uint32_t input_latency; + uint32_t output_latency; + bool hardware_metering; + bool hardware_monitoring; + std::string dither_mode; + bool force16_bit; + bool soft_mode; + std::string midi_driver; + }; + + /** + * @return true if able to build a valid command line based on options + */ + bool get_jack_command_line_string (const JackCommandLineOptions& options, std::string& command_line); +} diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 5ef5e4c845..781396a598 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -44,7 +44,6 @@ public: void copy(const MidiBuffer& copy); bool push_back(const Evoral::MIDIEvent<TimeType>& event); - bool push_back(const jack_midi_event_t& event); bool push_back(TimeType time, size_t size, const uint8_t* data); uint8_t* reserve(TimeType time, size_t size); diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h index 5dc55398cb..4a9a2ededc 100644 --- a/libs/ardour/ardour/midi_port.h +++ b/libs/ardour/ardour/midi_port.h @@ -57,9 +57,9 @@ class MidiPort : public Port { MidiBuffer& get_midi_buffer (pframes_t nframes); protected: - friend class AudioEngine; + friend class PortManager; - MidiPort (const std::string& name, Flags); + MidiPort (const std::string& name, PortFlags); private: MidiBuffer* _buffer; diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index e225117d94..06175de673 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -30,6 +30,7 @@ #include "pbd/signals.h" #include "ardour/data_type.h" +#include "ardour/port_engine.h" #include "ardour/types.h" namespace ARDOUR { @@ -40,11 +41,6 @@ class Buffer; class Port : public boost::noncopyable { public: - enum Flags { - IsInput = JackPortIsInput, - IsOutput = JackPortIsOutput, - }; - virtual ~Port (); static void set_connecting_blocked( bool yn ) { @@ -62,7 +58,7 @@ public: int set_name (std::string const &); /** @return flags */ - Flags flags () const { + PortFlags flags () const { return _flags; } @@ -90,24 +86,24 @@ public: virtual int connect (Port *); int disconnect (Port *); - void ensure_jack_monitors_input (bool); - bool jack_monitoring_input () const; + void request_input_monitoring (bool); + void ensure_input_monitoring (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; } - jack_port_t* jack_port() const { return _jack_port; } + PortEngine::PortHandle port_handle() { return _port_handle; } - void get_connected_latency_range (jack_latency_range_t& range, bool playback) const; + void get_connected_latency_range (LatencyRange& range, bool playback) const; - void set_private_latency_range (jack_latency_range_t& range, bool playback); - const jack_latency_range_t& private_latency_range (bool playback) const; + void set_private_latency_range (LatencyRange& range, bool playback); + const LatencyRange& private_latency_range (bool playback) const; - void set_public_latency_range (jack_latency_range_t& range, bool playback) const; - jack_latency_range_t public_latency_range (bool playback) const; + void set_public_latency_range (LatencyRange& range, bool playback) const; + LatencyRange public_latency_range (bool playback) const; virtual void reset (); @@ -122,8 +118,6 @@ public: bool physically_connected () const; - static void set_engine (AudioEngine *); - PBD::Signal1<void,bool> MonitorInputChanged; static PBD::Signal2<void,boost::shared_ptr<Port>,boost::shared_ptr<Port> > PostDisconnect; static PBD::Signal0<void> PortDrop; @@ -143,9 +137,9 @@ public: protected: - Port (std::string const &, DataType, Flags); + Port (std::string const &, DataType, PortFlags); - jack_port_t* _jack_port; ///< JACK port + PortEngine::PortHandle _port_handle; static bool _connecting_blocked; static pframes_t _global_port_buffer_offset; /* access only from process() tree */ @@ -153,18 +147,16 @@ protected: framecnt_t _port_buffer_offset; /* access only from process() tree */ - jack_latency_range_t _private_playback_latency; - jack_latency_range_t _private_capture_latency; - - static AudioEngine* _engine; ///< the AudioEngine + LatencyRange _private_playback_latency; + LatencyRange _private_capture_latency; private: std::string _name; ///< port short name - Flags _flags; ///< flags + PortFlags _flags; ///< flags bool _last_monitor; /** ports that we are connected to, kept so that we can - reconnect to JACK when required + reconnect to the backend when required */ std::set<std::string> _connections; diff --git a/libs/ardour/ardour/port_engine.h b/libs/ardour/ardour/port_engine.h new file mode 100644 index 0000000000..8e36ad8b09 --- /dev/null +++ b/libs/ardour/ardour/port_engine.h @@ -0,0 +1,343 @@ +/* + 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_engine_h__ +#define __libardour_port_engine_h__ + +#include <vector> +#include <string> + +#include <stdint.h> + +#include "ardour/data_type.h" +#include "ardour/types.h" + +namespace ARDOUR { + +class PortManager; + +/** PortEngine is an abstract base class that defines the functionality + * required by Ardour. + * + * A Port is basically an endpoint for a datastream (which can either be + * continuous, like audio, or event-based, like MIDI). Ports have buffers + * associated with them into which data can be written (if they are output + * ports) and from which data can be read (if they input ports). Ports can be + * connected together so that data written to an output port can be read from + * an input port. These connections can be 1:1, 1:N OR N:1. + * + * Ports may be associated with software only, or with hardware. Hardware + * related ports are often referred to as physical, and correspond to some + * relevant physical entity on a hardware device, such as an audio jack or a + * MIDI connector. Physical ports may be potentially asked to monitor their + * inputs, though some implementations may not support this. + * + * Most physical ports will also be considered "terminal", which means that + * data delivered there or read from there will go to or comes from a system + * outside of the PortEngine implementation's control (e.g. the analog domain + * for audio, or external MIDI devices for MIDI). Non-physical ports can also + * be considered "terminal". For example, the output port of a software + * synthesizer is a terminal port, because the data contained in its buffer + * does not and cannot be considered to come from any other port - it is + * synthesized by its owner. + * + * Ports also have latency associated with them. Each port has a playback + * latency and a capture latency: + * + * <b>capture latency</b>: how long since the data read from the buffer of a + * port arrived at at a terminal port. The data will have + * come from the "outside world" if the terminal port is also + * physical, or will have been synthesized by the entity that + * owns the terminal port. + * + * <b>playback latency</b>: how long until the data written to the buffer of + * port will reach a terminal port. + * + * + * For more detailed questions about the PortEngine API, consult the JACK API + * documentation, on which this entire object is based. + */ + +class PortEngine { + public: + PortEngine (PortManager& pm) : manager (pm) {} + virtual ~PortEngine() {} + + /* We use void* here so that the API can be defined for any implementation. + * + * We could theoretically use a template (PortEngine<T>) and define + * PortHandle as T, but this complicates the desired inheritance + * pattern in which FooPortEngine handles things for the Foo API, + * rather than being a derivative of PortEngine<Foo>. + */ + + typedef void* PortHandle; + + /** Return a typeless pointer to an object that may be of interest + * that understands the internals of a particular PortEngine + * implementation. + * + * XXX the existence of this method is a band-aid over some design + * issues and will it will be removed in the future + */ + virtual void* private_handle() const = 0; + + /** Return the name of this process as used by the port manager + * when naming ports. + */ + virtual const std::string& my_name() const = 0; + + /** Return the maximum size of a port name + */ + virtual uint32_t port_name_size() const = 0; + + /** Returns zero if the port referred to by @param port was set to @param + * name. Return non-zero otherwise. + */ + virtual int set_port_name (PortHandle port, const std::string& name) = 0; + /** Return the name of the port referred to by @param port. If the port + * does not exist, return an empty string. + */ + virtual std::string get_port_name (PortHandle) const = 0; + /** Return a reference to a port with the fullname @param name. Return + * a null pointer if no such port exists. + */ + virtual PortHandle* get_port_by_name (const std::string&) const = 0; + + /** Find the set of ports whose names, types and flags match + * specified values, place the names of each port into @param ports, + * and return the count of the number found. + * + * To avoid selecting by name, pass an empty string for @param + * port_name_pattern. + * + * To avoid selecting by type, pass DataType::NIL as @param type. + * + * To avoid selecting by flags, pass PortFlags (0) as @param flags. + */ + virtual int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>& ports) const = 0; + + /** Return the Ardour data type handled by the port referred to by @param + * port. Returns DataType::NIL if the port does not exist. + */ + virtual DataType port_data_type (PortHandle port) const = 0; + + /** Create a new port whose fullname will be the conjuction of my_name(), + * ":" and @param shortname. The port will handle data specified by @param + * type and will have the flags given by @param flags. If successfull, + * return a reference to the port, otherwise return a null pointer. + */ + virtual PortHandle register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags) = 0; + + /* Destroy the port referred to by @param port, including all resources + * associated with it. This will also disconnect @param port from any ports it + * is connected to. + */ + virtual void unregister_port (PortHandle) = 0; + + /* Connection management */ + + /** Ensure that data written to the port named by @param src will be + * readable from the port named by @param dst. Return zero on success, + * non-zero otherwise. + */ + virtual int connect (const std::string& src, const std::string& dst) = 0; + + /** Remove any existing connection between the ports named by @param src and + * @param dst. Return zero on success, non-zero otherwise. + */ + virtual int disconnect (const std::string& src, const std::string& dst) = 0; + + + /** Ensure that data written to the port referenced by @param portwill be + * readable from the port named by @param dst. Return zero on success, + * non-zero otherwise. + */ + virtual int connect (PortHandle src, const std::string& dst) = 0; + /** Remove any existing connection between the port referenced by @param src and + * the port named @param dst. Return zero on success, non-zero otherwise. + */ + virtual int disconnect (PortHandle src, const std::string& dst) = 0; + + /** Remove all connections between the port referred to by @param port and + * any other ports. Return zero on success, non-zero otherwise. + */ + virtual int disconnect_all (PortHandle port) = 0; + + /** Return true if the port referred to by @param port has any connections + * to other ports. Return false otherwise. + */ + virtual bool connected (PortHandle port) = 0; + /** Return true if the port referred to by @param port is connected to + * the port named by @param name. Return false otherwise. + */ + virtual bool connected_to (PortHandle, const std::string& name) = 0; + + /** Return true if the port referred to by @param port has any connections + * to ports marked with the PortFlag IsPhysical. Return false otherwise. + */ + virtual bool physically_connected (PortHandle port) = 0; + + /** Place the names of all ports connected to the port named by @param + * ports into @param names, and return the number of connections. + */ + virtual int get_connections (PortHandle port, std::vector<std::string>& names) = 0; + + /* MIDI */ + + /** Retrieve a MIDI event from the data at @param port_buffer. The event + number to be retrieved is given by @param event_index (a value of zero + indicates that the first event in the port_buffer should be retrieved). + * + * The data associated with the event will be copied into the buffer at + * @param buf and the number of bytes written will be stored in @param + * size. The timestamp of the event (which is always relative to the start + * of the current process cycle, in samples) will be stored in @param + * timestamp + */ + virtual int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index) = 0; + + /** Place a MIDI event consisting of @param size bytes copied from the data + * at @param buf into the port buffer referred to by @param + * port_buffer. The MIDI event will be marked with a time given by @param + * timestamp. Return zero on success, non-zero otherwise. + * + * Events must be added monotonically to a port buffer. An attempt to + * add a non-monotonic event (e.g. out-of-order) will cause this method + * to return a failure status. + */ + virtual int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size) = 0; + + /** Return the number of MIDI events in the data at @param port_buffer + */ + virtual uint32_t get_midi_event_count (void* port_buffer) = 0; + + /** Clear the buffer at @param port_buffer of all MIDI events. + * + * After a call to this method, an immediate, subsequent call to + * get_midi_event_count() with the same @param port_buffer argument must + * return zero. + */ + virtual void midi_clear (void* port_buffer) = 0; + + /* Monitoring */ + + /** Return true if the implementation can offer input monitoring. + * + * Input monitoring involves the (selective) routing of incoming data + * to an outgoing data stream, without the data being passed to the CPU. + * + * Only certain audio hardware can provide this, and only certain audio + * APIs can offer it. + */ + virtual bool can_monitor_input() const = 0; + /** Increment or decrement the number of requests to monitor the input + * of the hardware channel represented by the port referred to by @param + * port. + * + * If the number of requests rises above zero, input monitoring will + * be enabled (if can_monitor_input() returns true for the implementation). + * + * If the number of requests falls to zero, input monitoring will be + * disabled (if can_monitor_input() returns true for the implementation) + */ + virtual int request_input_monitoring (PortHandle port, bool yn) = 0; + /* Force input monitoring of the hardware channel represented by the port + * referred to by @param port to be on or off, depending on the true/false + * status of @param yn. The request count is ignored when using this + * method, so if this is called with yn set to false, input monitoring will + * be disabled regardless of the number of requests to enable it. + */ + virtual int ensure_input_monitoring (PortHandle port, bool yn) = 0; + /** Return true if input monitoring is enabled for the hardware channel + * represented by the port referred to by @param port. Return false + * otherwise. + */ + virtual bool monitoring_input (PortHandle port) = 0; + + /* Latency management + */ + + /** Set the latency range for the port referred to by @param port to @param + * r. The playback range will be set if @param for_playback is true, + * otherwise the capture range will be set. + */ + virtual void set_latency_range (PortHandle port, bool for_playback, LatencyRange r) = 0; + /** Return the latency range for the port referred to by @param port. + * The playback range will be returned if @param for_playback is true, + * otherwise the capture range will be returned. + */ + virtual LatencyRange get_latency_range (PortHandle port, bool for_playback) = 0; + + /* Discovering physical ports */ + + /** Return true if the port referred to by @param port has the IsPhysical + * flag set. Return false otherwise. + */ + virtual bool port_is_physical (PortHandle port) const = 0; + + /** Store into @param names the names of all ports with the IsOutput and + * IsPhysical flag set, that handle data of type @param type. + * + * This can be used to discover outputs associated with hardware devices. + */ + virtual void get_physical_outputs (DataType type, std::vector<std::string>& names) = 0; + /** Store into @param names the names of all ports with the IsInput and + * IsPhysical flags set, that handle data of type @param type. + * + * This can be used to discover inputs associated with hardware devices. + */ + virtual void get_physical_inputs (DataType type, std::vector<std::string>& names) = 0; + /** Return the total count (possibly mixed between different data types) + of the number of ports with the IsPhysical and IsOutput flags set. + */ + virtual ChanCount n_physical_outputs () const = 0; + /** Return the total count (possibly mixed between different data types) + of the number of ports with the IsPhysical and IsInput flags set. + */ + virtual ChanCount n_physical_inputs () const = 0; + + /** Return the address of the memory area where data for the port can be + * written (if the port has the PortFlag IsOutput set) or read (if the port + * has the PortFlag IsInput set). + * + * The return value is untyped because buffers containing different data + * depending on the port type. + */ + virtual void* get_buffer (PortHandle, pframes_t) = 0; + + /* MIDI ports (the ones in libmidi++) need this to be able to correctly + * schedule MIDI events within their buffers. It is a bit odd that we + * expose this here, because it is also exposed by AudioBackend, but they + * only have access to a PortEngine object, not an AudioBackend. + * + * Return the time according to the sample clock in use when the current + * buffer process cycle began. + * + * XXX to be removed after some more design cleanup. + */ + virtual pframes_t sample_time_at_cycle_start () = 0; + + protected: + PortManager& manager; +}; + +} + +#endif /* __libardour_port_engine_h__ */ diff --git a/libs/ardour/ardour/port_manager.h b/libs/ardour/ardour/port_manager.h new file mode 100644 index 0000000000..06e4939101 --- /dev/null +++ b/libs/ardour/ardour/port_manager.h @@ -0,0 +1,144 @@ +/* + 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 <vector> +#include <string> +#include <exception> +#include <map> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include "pbd/rcu.h" + +#include "ardour/chan_count.h" +#include "ardour/port.h" +#include "ardour/port_engine.h" + +namespace ARDOUR { + +class PortManager +{ + public: + typedef std::map<std::string,boost::shared_ptr<Port> > Ports; + + PortManager (); + virtual ~PortManager() {} + + void set_port_engine (PortEngine& pe); + PortEngine& port_engine() { return *_impl; } + + uint32_t port_name_size() const; + std::string my_name() const; + + /* Port registration */ + + boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname); + boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname); + int unregister_port (boost::shared_ptr<Port>); + + /* Port connectivity */ + + int connect (const std::string& source, const std::string& destination); + int disconnect (const std::string& source, const std::string& destination); + int disconnect (boost::shared_ptr<Port>); + int reestablish_ports (); + int reconnect_ports (); + + bool connected (const std::string&); + bool connected_to (const std::string&, const std::string&); + bool physically_connected (const std::string&); + int get_connections (const std::string&, std::vector<std::string>&); + + /* Naming */ + + boost::shared_ptr<Port> get_port_by_name (const std::string &); + void port_renamed (const std::string&, const std::string&); + std::string make_port_name_relative (const std::string& name) const; + std::string make_port_name_non_relative (const std::string& name) const; + bool port_is_mine (const std::string& fullname) const; + + /* other Port management */ + + bool port_is_physical (const std::string&) const; + void get_physical_outputs (DataType type, std::vector<std::string>&); + void get_physical_inputs (DataType type, std::vector<std::string>&); + ChanCount n_physical_outputs () const; + ChanCount n_physical_inputs () const; + + int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&); + + void remove_all_ports (); + + /* per-Port monitoring */ + + bool can_request_input_monitoring () const; + void request_input_monitoring (const std::string&, bool) const; + void ensure_input_monitoring (const std::string&, bool) const; + + class PortRegistrationFailure : public std::exception { + public: + PortRegistrationFailure (std::string const & why = "") + : reason (why) {} + + ~PortRegistrationFailure () throw () {} + + const char *what() const throw () { return reason.c_str(); } + + private: + std::string reason; + }; + + /* the port engine will invoke these callbacks when the time is right */ + + void registration_callback (); + int graph_order_callback (); + void connect_callback (const std::string&, const std::string&, bool connection); + + bool port_remove_in_progress() const { return _port_remove_in_progress; } + + /** Emitted if the backend notifies us of a graph order event */ + PBD::Signal0<void> GraphReordered; + + /** Emitted if a Port is registered or unregistered */ + PBD::Signal0<void> 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<void, boost::weak_ptr<Port>, std::string, boost::weak_ptr<Port>, std::string, bool> PortConnectedOrDisconnected; + + protected: + boost::shared_ptr<PortEngine> _impl; + SerializedRCUManager<Ports> ports; + bool _port_remove_in_progress; + + boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input); + void port_registration_failure (const std::string& portname); +}; + +} // namespace + +#endif /* __libardour_port_manager_h__ */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index efe51e7b49..a12f816c1e 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1216,7 +1216,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi framepos_t ltc_timecode_offset; bool ltc_timecode_negative_offset; - jack_latency_range_t ltc_out_latency; + LatencyRange ltc_out_latency; void ltc_tx_initialize(); void ltc_tx_cleanup(); @@ -1429,8 +1429,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi */ std::list<GQuark> _current_trans_quarks; - void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int); - int jack_sync_callback (jack_transport_state_t, jack_position_t*); + // void timebase_callback (TransportState, pframes_t, jack_position_t*, int); + int jack_sync_callback (TransportState, framepos_t); void reset_jack_connection (jack_client_t* jack); void process_rtop (SessionEvent*); diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index 9862824da9..4408da2d25 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -48,6 +48,7 @@ namespace ARDOUR { class TempoMap; class Session; +class AudioEngine; /** * @class Slave @@ -391,7 +392,7 @@ public: PBD::ScopedConnectionList port_connections; PBD::ScopedConnection config_connection; - jack_latency_range_t ltc_slave_latency; + LatencyRange ltc_slave_latency; /* DLL - chase LTC */ int transport_direction; @@ -492,7 +493,7 @@ class MIDIClock_Slave : public Slave { class JACK_Slave : public Slave { public: - JACK_Slave (jack_client_t*); + JACK_Slave (AudioEngine&); ~JACK_Slave (); bool speed_and_position (double& speed, framepos_t& pos); @@ -502,11 +503,10 @@ class JACK_Slave : public Slave bool ok() const; framecnt_t resolution () const { return 1; } bool requires_seekahead () const { return false; } - void reset_client (jack_client_t* jack); bool is_always_synced() const { return true; } private: - jack_client_t* jack; + AudioEngine& engine; double speed; bool _starting; }; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 2115149872..2fb4ec9691 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -585,6 +585,32 @@ namespace ARDOUR { FadeSymmetric, }; + enum TransportState { + /* these values happen to match the constants used by JACK but + this equality cannot be assumed. + */ + TransportStopped = 0, + TransportRolling = 1, + TransportLooping = 2, + TransportStarting = 3, + }; + + enum PortFlags { + /* these values happen to match the constants used by JACK but + this equality cannot be assumed. + */ + IsInput = 0x1, + IsOutput = 0x2, + IsPhysical = 0x4, + CanMonitor = 0x8, + IsTerminal = 0x10 + }; + + struct LatencyRange { + uint32_t min; //< samples + uint32_t max; //< samples + }; + } // namespace ARDOUR diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index b9ce987c87..77c14de103 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -1754,7 +1754,7 @@ AudioDiskstream::prep_record_enable () if (Config->get_monitoring_model() == HardwareMonitoring) { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_jack_monitors_input (!(_session.config.get_auto_input() && rolling)); + (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling)); capturing_sources.push_back ((*chan)->write_source); (*chan)->write_source->mark_streaming_write_started (); } @@ -1775,7 +1775,7 @@ AudioDiskstream::prep_record_disable () boost::shared_ptr<ChannelList> c = channels.reader(); if (Config->get_monitoring_model() == HardwareMonitoring) { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_jack_monitors_input (false); + (*chan)->source.request_input_monitoring (false); } } capturing_sources.clear (); @@ -2041,12 +2041,12 @@ AudioDiskstream::allocate_temporary_buffers () } void -AudioDiskstream::request_jack_monitors_input (bool yn) +AudioDiskstream::request_input_monitoring (bool yn) { boost::shared_ptr<ChannelList> c = channels.reader(); for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->source.request_jack_monitors_input (yn); + (*chan)->source.request_input_monitoring (yn); } } @@ -2369,13 +2369,13 @@ AudioDiskstream::ChannelSource::is_physical () const } void -AudioDiskstream::ChannelSource::request_jack_monitors_input (bool yn) const +AudioDiskstream::ChannelSource::request_input_monitoring (bool yn) const { if (name.empty()) { return; } - return AudioEngine::instance()->request_jack_monitors_input (name, yn); + return AudioEngine::instance()->request_input_monitoring (name, yn); } AudioDiskstream::ChannelInfo::ChannelInfo (framecnt_t playback_bufsize, framecnt_t capture_bufsize, framecnt_t speed_size, framecnt_t wrap_size) diff --git a/libs/ardour/audio_port.cc b/libs/ardour/audio_port.cc index 240224ea5e..6a86360b69 100644 --- a/libs/ardour/audio_port.cc +++ b/libs/ardour/audio_port.cc @@ -21,13 +21,17 @@ #include "pbd/stacktrace.h" #include "ardour/audio_buffer.h" +#include "ardour/audioengine.h" #include "ardour/audio_port.h" #include "ardour/data_type.h" +#include "ardour/port_engine.h" using namespace ARDOUR; using namespace std; -AudioPort::AudioPort (const std::string& name, Flags flags) +#define port_engine AudioEngine::instance()->port_engine() + +AudioPort::AudioPort (const std::string& name, PortFlags flags) : Port (name, DataType::AUDIO, flags) , _buffer (new AudioBuffer (0)) { @@ -73,7 +77,7 @@ AudioBuffer& AudioPort::get_audio_buffer (pframes_t nframes) { /* caller must hold process lock */ - _buffer->set_data ((Sample *) jack_port_get_buffer (_jack_port, _cycle_nframes) + + _buffer->set_data ((Sample *) port_engine.get_buffer (_port_handle, _cycle_nframes) + _global_port_buffer_offset + _port_buffer_offset, nframes); return *_buffer; } @@ -82,7 +86,7 @@ Sample* AudioPort::engine_get_whole_audio_buffer () { /* caller must hold process lock */ - return (Sample *) jack_port_get_buffer (_jack_port, _cycle_nframes); + return (Sample *) port_engine.get_buffer (_port_handle, _cycle_nframes); } diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 08de54960c..0c88e7c0fd 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -25,11 +25,14 @@ #include <sstream> #include <glibmm/timer.h> +#include <glibmm/pattern.h> +#include <glibmm/module.h> +#include "pbd/epa.h" +#include "pbd/file_utils.h" #include "pbd/pthread_utils.h" #include "pbd/stacktrace.h" #include "pbd/unknown_type.h" -#include "pbd/epa.h" #include <jack/weakjack.h> @@ -39,7 +42,9 @@ #include "midi++/manager.h" #include "ardour/audio_port.h" +#include "ardour/audio_backend.h" #include "ardour/audioengine.h" +#include "ardour/backend_search_path.h" #include "ardour/buffer.h" #include "ardour/cycle_timer.h" #include "ardour/internal_send.h" @@ -58,60 +63,44 @@ using namespace PBD; gint AudioEngine::m_meter_exit; AudioEngine* AudioEngine::_instance = 0; -#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; } -#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; } - -AudioEngine::AudioEngine (string client_name, string session_uuid) - : _jack (0) - , session_remove_pending (false) +AudioEngine::AudioEngine () + : session_remove_pending (false) , session_removal_countdown (-1) - , _running (false) - , _has_run (false) - , _buffer_size (0) - , _frame_rate (0) , monitor_check_interval (INT32_MAX) , last_monitor_check (0) , _processed_frames (0) , _freewheeling (false) , _pre_freewheel_mmc_enabled (false) - , _usecs_per_cycle (0) - , port_remove_in_progress (false) , m_meter_thread (0) , _main_thread (0) - , ports (new Ports) { - _instance = this; /* singleton */ - g_atomic_int_set (&m_meter_exit, 0); - - if (connect_to_jack (client_name, session_uuid)) { - throw NoBackendAvailable (); - } - - Port::set_engine (this); + discover_backends (); } AudioEngine::~AudioEngine () { + drop_backend (); + config_connection.disconnect (); { Glib::Threads::Mutex::Lock tm (_process_lock); session_removed.signal (); - - if (_running) { - jack_client_close (_jack); - _jack = 0; - } - stop_metering_thread (); } } -jack_client_t* -AudioEngine::jack() const +AudioEngine* +AudioEngine::create () { - return _jack; + if (_instance) { + return _instance; + } + + _instance = new AudioEngine (); + + return _instance; } void @@ -131,289 +120,6 @@ _thread_init_callback (void * /*arg*/) MIDI::JackMIDIPort::set_process_thread (pthread_self()); } -static void -ardour_jack_error (const char* msg) -{ - error << "JACK: " << msg << endmsg; -} - -void -AudioEngine::set_jack_callbacks () -{ - GET_PRIVATE_JACK_POINTER (_jack); - - if (jack_on_info_shutdown) { - jack_on_info_shutdown (_priv_jack, halted_info, this); - } else { - jack_on_shutdown (_priv_jack, halted, this); - } - - jack_set_thread_init_callback (_priv_jack, _thread_init_callback, this); - jack_set_process_thread (_priv_jack, _process_thread, this); - jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this); - jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this); - jack_set_graph_order_callback (_priv_jack, _graph_order_callback, this); - jack_set_port_registration_callback (_priv_jack, _registration_callback, this); - jack_set_port_connect_callback (_priv_jack, _connect_callback, this); - jack_set_xrun_callback (_priv_jack, _xrun_callback, this); - jack_set_sync_callback (_priv_jack, _jack_sync_callback, this); - jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this); - - if (_session && _session->config.get_jack_time_master()) { - jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); - } - -#ifdef HAVE_JACK_SESSION - if( jack_set_session_callback) - jack_set_session_callback (_priv_jack, _session_callback, this); -#endif - - if (jack_set_latency_callback) { - jack_set_latency_callback (_priv_jack, _latency_callback, this); - } - - jack_set_error_function (ardour_jack_error); -} - -int -AudioEngine::start () -{ - GET_PRIVATE_JACK_POINTER_RET (_jack, -1); - - if (!_running) { - - if (!jack_port_type_get_buffer_size) { - warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg; - } - - if (_session) { - BootMessage (_("Connect session to engine")); - _session->set_frame_rate (jack_get_sample_rate (_priv_jack)); - } - - /* a proxy for whether jack_activate() will definitely call the buffer size - * callback. with older versions of JACK, this function symbol will be null. - * this is reliable, but not clean. - */ - - if (!jack_port_type_get_buffer_size) { - jack_bufsize_callback (jack_get_buffer_size (_priv_jack)); - } - - _processed_frames = 0; - last_monitor_check = 0; - - set_jack_callbacks (); - - if (jack_activate (_priv_jack) == 0) { - _running = true; - _has_run = true; - Running(); /* EMIT SIGNAL */ - } else { - // error << _("cannot activate JACK client") << endmsg; - } - } - - return _running ? 0 : -1; -} - -int -AudioEngine::stop (bool forever) -{ - GET_PRIVATE_JACK_POINTER_RET (_jack, -1); - - if (_priv_jack) { - if (forever) { - disconnect_from_jack (); - } else { - jack_deactivate (_priv_jack); - MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ - Stopped(); /* EMIT SIGNAL */ - } - } - - if (forever) { - stop_metering_thread (); - } - - return _running ? -1 : 0; -} - - -bool -AudioEngine::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; -} - -void -AudioEngine::_jack_timebase_callback (jack_transport_state_t state, pframes_t nframes, - jack_position_t* pos, int new_position, void *arg) -{ - static_cast<AudioEngine*> (arg)->jack_timebase_callback (state, nframes, pos, new_position); -} - -void -AudioEngine::jack_timebase_callback (jack_transport_state_t state, pframes_t nframes, - jack_position_t* pos, int new_position) -{ - if (_jack && _session && _session->synced_to_jack()) { - _session->jack_timebase_callback (state, nframes, pos, new_position); - } -} - -int -AudioEngine::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg) -{ - return static_cast<AudioEngine*> (arg)->jack_sync_callback (state, pos); -} - -int -AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos) -{ - if (_jack && _session) { - return _session->jack_sync_callback (state, pos); - } - - return true; -} - -int -AudioEngine::_xrun_callback (void *arg) -{ - AudioEngine* ae = static_cast<AudioEngine*> (arg); - if (ae->connected()) { - ae->Xrun (); /* EMIT SIGNAL */ - } - return 0; -} - -#ifdef HAVE_JACK_SESSION -void -AudioEngine::_session_callback (jack_session_event_t *event, void *arg) -{ - AudioEngine* ae = static_cast<AudioEngine*> (arg); - if (ae->connected()) { - ae->JackSessionEvent ( event ); /* EMIT SIGNAL */ - } -} -#endif - -int -AudioEngine::_graph_order_callback (void *arg) -{ - AudioEngine* ae = static_cast<AudioEngine*> (arg); - - if (ae->connected() && !ae->port_remove_in_progress) { - ae->GraphReordered (); /* EMIT SIGNAL */ - } - - return 0; -} - -void* -AudioEngine::_process_thread (void *arg) -{ - return static_cast<AudioEngine *> (arg)->process_thread (); -} - -void -AudioEngine::_freewheel_callback (int onoff, void *arg) -{ - static_cast<AudioEngine*>(arg)->freewheel_callback (onoff); -} - -void -AudioEngine::freewheel_callback (int onoff) -{ - _freewheeling = onoff; - - if (onoff) { - _pre_freewheel_mmc_enabled = MIDI::Manager::instance()->mmc()->send_enabled (); - MIDI::Manager::instance()->mmc()->enable_send (false); - } else { - MIDI::Manager::instance()->mmc()->enable_send (_pre_freewheel_mmc_enabled); - } -} - -void -AudioEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg) -{ - AudioEngine* ae = static_cast<AudioEngine*> (arg); - - if (!ae->port_remove_in_progress) { - ae->PortRegisteredOrUnregistered (); /* EMIT SIGNAL */ - } -} - -void -AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg) -{ - return static_cast<AudioEngine *> (arg)->jack_latency_callback (mode); -} - -void -AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg) -{ - AudioEngine* ae = static_cast<AudioEngine*> (arg); - ae->connect_callback (id_a, id_b, conn); -} - -void -AudioEngine::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn) -{ - if (port_remove_in_progress) { - return; - } - - GET_PRIVATE_JACK_POINTER (_jack); - - jack_port_t* jack_port_a = jack_port_by_id (_priv_jack, id_a); - jack_port_t* jack_port_b = jack_port_by_id (_priv_jack, id_b); - - boost::shared_ptr<Port> port_a; - boost::shared_ptr<Port> port_b; - Ports::iterator x; - boost::shared_ptr<Ports> pr = ports.reader (); - - - x = pr->find (make_port_name_relative (jack_port_name (jack_port_a))); - if (x != pr->end()) { - port_a = x->second; - } - - x = pr->find (make_port_name_relative (jack_port_name (jack_port_b))); - if (x != pr->end()) { - port_b = x->second; - } - - PortConnectedOrDisconnected ( - port_a, jack_port_name (jack_port_a), - port_b, jack_port_name (jack_port_b), - conn == 0 ? false : true - ); /* EMIT SIGNAL */ -} - void AudioEngine::split_cycle (pframes_t offset) { @@ -430,29 +136,32 @@ AudioEngine::split_cycle (pframes_t offset) } } -void* -AudioEngine::process_thread () +int +AudioEngine::sample_rate_change (pframes_t nframes) { - /* JACK doesn't do this for us when we use the wait API - */ - - _thread_init_callback (0); + /* check for monitor input change every 1/10th of second */ - _main_thread = new ProcessThread; + monitor_check_interval = nframes / 10; + last_monitor_check = 0; - while (1) { - GET_PRIVATE_JACK_POINTER_RET(_jack,0); + if (_session) { + _session->set_frame_rate (nframes); + } - pframes_t nframes = jack_cycle_wait (_priv_jack); + SampleRateChanged (nframes); /* EMIT SIGNAL */ - if (process_callback (nframes)) { - return 0; - } + return 0; +} - jack_cycle_signal (_priv_jack, 0); - } +int +AudioEngine::buffer_size_change (pframes_t bufsiz) +{ + if (_session) { + _session->set_block_size (bufsiz); + last_monitor_check = 0; + } - return 0; + return 0; } /** Method called by our ::process_thread when there is work to be done. @@ -461,7 +170,6 @@ AudioEngine::process_thread () int AudioEngine::process_callback (pframes_t nframes) { - GET_PRIVATE_JACK_POINTER_RET(_jack,0); Glib::Threads::Mutex::Lock tm (_process_lock, Glib::Threads::TRY_LOCK); PT_TIMING_REF; @@ -491,7 +199,7 @@ AudioEngine::process_callback (pframes_t nframes) if (session_removal_countdown < 0) { /* fade out over 1 second */ - session_removal_countdown = _frame_rate/2; + session_removal_countdown = sample_rate()/2; session_removal_gain = 1.0; session_removal_gain_step = 1.0/session_removal_countdown; @@ -582,7 +290,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 ... @@ -644,97 +352,6 @@ AudioEngine::process_callback (pframes_t nframes) return 0; } -int -AudioEngine::_sample_rate_callback (pframes_t nframes, void *arg) -{ - return static_cast<AudioEngine *> (arg)->jack_sample_rate_callback (nframes); -} - -int -AudioEngine::jack_sample_rate_callback (pframes_t nframes) -{ - _frame_rate = nframes; - _usecs_per_cycle = (int) floor ((((double) frames_per_cycle() / nframes)) * 1000000.0); - - /* check for monitor input change every 1/10th of second */ - - monitor_check_interval = nframes / 10; - last_monitor_check = 0; - - if (_session) { - _session->set_frame_rate (nframes); - } - - SampleRateChanged (nframes); /* EMIT SIGNAL */ - - return 0; -} - -void -AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode) -{ - if (_session) { - _session->update_latency (mode == JackPlaybackLatency); - } -} - -int -AudioEngine::_bufsize_callback (pframes_t nframes, void *arg) -{ - return static_cast<AudioEngine *> (arg)->jack_bufsize_callback (nframes); -} - -int -AudioEngine::jack_bufsize_callback (pframes_t nframes) -{ - /* if the size has not changed, this should be a no-op */ - - if (nframes == _buffer_size) { - return 0; - } - - GET_PRIVATE_JACK_POINTER_RET (_jack, 1); - - _buffer_size = nframes; - _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0); - last_monitor_check = 0; - - if (jack_port_type_get_buffer_size) { - _raw_buffer_sizes[DataType::AUDIO] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_AUDIO_TYPE); - _raw_buffer_sizes[DataType::MIDI] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_MIDI_TYPE); - } else { - - /* Old version of JACK. - - These crude guesses, see below where we try to get the right answers. - - Note that our guess for MIDI deliberatey tries to overestimate - by a little. It would be nicer if we could get the actual - size from a port, but we have to use this estimate in the - event that there are no MIDI ports currently. If there are - the value will be adjusted below. - */ - - _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample); - _raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2); - } - - { - Glib::Threads::Mutex::Lock lm (_process_lock); - - boost::shared_ptr<Ports> p = ports.reader(); - - for (Ports::iterator i = p->begin(); i != p->end(); ++i) { - i->second->reset(); - } - } - - if (_session) { - _session->set_block_size (_buffer_size); - } - - return 0; -} void AudioEngine::stop_metering_thread () @@ -759,8 +376,9 @@ void AudioEngine::meter_thread () { pthread_set_name (X_("meter")); + while (true) { - Glib::usleep (10000); + Glib::usleep (10000); /* 1/100th sec interval */ if (g_atomic_int_get(&m_meter_exit)) { break; } @@ -779,7 +397,7 @@ AudioEngine::set_session (Session *s) start_metering_thread (); - pframes_t blocksize = jack_get_buffer_size (_jack); + pframes_t blocksize = _backend->buffer_size (); /* page in as much of the session process code as we can before we really start running. @@ -817,6 +435,7 @@ AudioEngine::remove_session () if (_session) { session_remove_pending = true; + session_removal_countdown = 0; session_removed.wait(_process_lock); } @@ -827,793 +446,562 @@ AudioEngine::remove_session () remove_all_ports (); } + void -AudioEngine::port_registration_failure (const std::string& portname) +AudioEngine::died () { - GET_PRIVATE_JACK_POINTER (_jack); - string full_portname = jack_client_name; - full_portname += ':'; - full_portname += portname; - - - jack_port_t* p = jack_port_by_name (_priv_jack, full_portname.c_str()); - string reason; + /* called from a signal handler for SIGPIPE */ - if (p) { - reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname); - } else { - reason = string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks."), PROGRAM_NAME); - } + stop_metering_thread (); - throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); + _running = false; } -boost::shared_ptr<Port> -AudioEngine::register_port (DataType dtype, const string& portname, bool input) +int +AudioEngine::reset_timebase () { - boost::shared_ptr<Port> newport; - - try { - if (dtype == DataType::AUDIO) { - newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput))); - } else if (dtype == DataType::MIDI) { - newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput))); + if (_session) { + if (_session->config.get_jack_time_master()) { + _backend->set_time_master (true); } else { - throw PortRegistrationFailure("unable to create port (unknown type)"); + _backend->set_time_master (false); } - - RCUWriter<Ports> writer (ports); - boost::shared_ptr<Ports> ps = writer.get_copy (); - ps->insert (make_pair (make_port_name_relative (portname), newport)); - - /* writer goes out of scope, forces update */ - - return newport; - } - - catch (PortRegistrationFailure& err) { - throw err; - } catch (std::exception& e) { - throw PortRegistrationFailure(string_compose( - _("unable to create port: %1"), e.what()).c_str()); - } catch (...) { - throw PortRegistrationFailure("unable to create port (unknown error)"); } + return 0; } -boost::shared_ptr<Port> -AudioEngine::register_input_port (DataType type, const string& portname) -{ - return register_port (type, portname, true); -} -boost::shared_ptr<Port> -AudioEngine::register_output_port (DataType type, const string& portname) +void +AudioEngine::destroy () { - return register_port (type, portname, false); + delete _instance; + _instance = 0; } int -AudioEngine::unregister_port (boost::shared_ptr<Port> port) +AudioEngine::discover_backends () { - /* caller must hold process lock */ + vector<std::string> backend_modules; - if (!_running) { - /* probably happening when the engine has been halted by JACK, - in which case, there is nothing we can do here. - */ - return 0; - } + _backends.clear (); - { - RCUWriter<Ports> writer (ports); - boost::shared_ptr<Ports> ps = writer.get_copy (); - Ports::iterator x = ps->find (make_port_name_relative (port->name())); + Glib::PatternSpec so_extension_pattern("*backend.so"); + Glib::PatternSpec dylib_extension_pattern("*backend.dylib"); - if (x != ps->end()) { - ps->erase (x); - } + find_matching_files_in_search_path (backend_search_path (), + so_extension_pattern, backend_modules); - /* writer goes out of scope, forces update */ - } + find_matching_files_in_search_path (backend_search_path (), + dylib_extension_pattern, backend_modules); - ports.flush (); + DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for backends in %1\n"), backend_search_path().to_string())); - return 0; -} + for (vector<std::string>::iterator i = backend_modules.begin(); i != backend_modules.end(); ++i) { -int -AudioEngine::connect (const string& source, const string& destination) -{ - int ret; + AudioBackendInfo* info; - if (!_running) { - if (!_has_run) { - fatal << _("connect called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - return -1; + if ((info = backend_discover (*i)) != 0) { + _backends.insert (make_pair (info->name, info)); } } - string s = make_port_name_non_relative (source); - string d = make_port_name_non_relative (destination); - + return _backends.size(); +} - boost::shared_ptr<Port> src = get_port_by_name (s); - boost::shared_ptr<Port> dst = get_port_by_name (d); +AudioBackendInfo* +AudioEngine::backend_discover (const string& path) +{ + Glib::Module module (path); + AudioBackendInfo* info; + void* sym = 0; - if (src) { - ret = src->connect (d); - } else if (dst) { - ret = dst->connect (s); - } else { - /* neither port is known to us, and this API isn't intended for use as a general patch bay */ - ret = -1; + if (!module) { + error << string_compose(_("AudioEngine: cannot load module \"%1\" (%2)"), path, + Glib::Module::get_last_error()) << endmsg; + return 0; } - - if (ret > 0) { - /* already exists - no error, no warning */ - } else if (ret < 0) { - error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"), - source, s, destination, d) - << endmsg; + + if (!module.get_symbol ("descriptor", sym)) { + error << string_compose(_("AudioEngine: backend at \"%1\" has no descriptor."), path) << endmsg; + error << Glib::Module::get_last_error() << endmsg; + return 0; } - return ret; + module.make_resident (); + + info = (AudioBackendInfo*) sym; + + return info; } -int -AudioEngine::disconnect (const string& source, const string& destination) +vector<const AudioBackendInfo*> +AudioEngine::available_backends() const { - int ret; - - if (!_running) { - if (!_has_run) { - fatal << _("disconnect called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - return -1; - } + vector<const AudioBackendInfo*> r; + + for (BackendMap::const_iterator i = _backends.begin(); i != _backends.end(); ++i) { + r.push_back (i->second); } - string s = make_port_name_non_relative (source); - string d = make_port_name_non_relative (destination); - - boost::shared_ptr<Port> src = get_port_by_name (s); - boost::shared_ptr<Port> dst = get_port_by_name (d); - - if (src) { - ret = src->disconnect (d); - } else if (dst) { - ret = dst->disconnect (s); - } else { - /* neither port is known to us, and this API isn't intended for use as a general patch bay */ - ret = -1; - } - return ret; + return r; } -int -AudioEngine::disconnect (boost::shared_ptr<Port> port) +string +AudioEngine::current_backend_name() const { - GET_PRIVATE_JACK_POINTER_RET (_jack,-1); - - if (!_running) { - if (!_has_run) { - fatal << _("disconnect called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - return -1; - } - } - - return port->disconnect_all (); + if (_backend) { + return _backend->name(); + } + return string(); } -ARDOUR::framecnt_t -AudioEngine::frame_rate () const +void +AudioEngine::drop_backend () { - GET_PRIVATE_JACK_POINTER_RET (_jack, 0); - if (_frame_rate == 0) { - return (_frame_rate = jack_get_sample_rate (_priv_jack)); - } else { - return _frame_rate; + if (_backend) { + _backend->stop (); + _backend.reset (); } } -size_t -AudioEngine::raw_buffer_size (DataType t) +boost::shared_ptr<AudioBackend> +AudioEngine::set_backend (const std::string& name, const std::string& arg1, const std::string& arg2) { - std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t); - return (s != _raw_buffer_sizes.end()) ? s->second : 0; -} + BackendMap::iterator b = _backends.find (name); -ARDOUR::pframes_t -AudioEngine::frames_per_cycle () const -{ - GET_PRIVATE_JACK_POINTER_RET (_jack,0); - if (_buffer_size == 0) { - return jack_get_buffer_size (_jack); - } else { - return _buffer_size; + if (b == _backends.end()) { + return boost::shared_ptr<AudioBackend>(); } -} -/** @param name Full or short name of port - * @return Corresponding Port or 0. - */ + drop_backend (); -boost::shared_ptr<Port> -AudioEngine::get_port_by_name (const string& portname) -{ - if (!_running) { - if (!_has_run) { - fatal << _("get_port_by_name() called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - boost::shared_ptr<Port> (); - } - } + try { + cerr << "Instantiate " << b->second->name << " with " << arg1 << " + " << arg2 << endl; - if (!port_is_mine (portname)) { - /* not an ardour port */ - return boost::shared_ptr<Port> (); - } + if (b->second->instantiate (arg1, arg2)) { + cerr << "i failed\n"; + throw failed_constructor (); + } - boost::shared_ptr<Ports> pr = ports.reader(); - std::string rel = make_port_name_relative (portname); - Ports::iterator x = pr->find (rel); + _backend = b->second->backend_factory (*this); + _impl = b->second->portengine_factory (*this); - if (x != pr->end()) { - /* its possible that the port was renamed by some 3rd party and - we don't know about it. check for this (the check is quick - and cheap), and if so, rename the port (which will alter - the port map as a side effect). - */ - const std::string check = make_port_name_relative (jack_port_name (x->second->jack_port())); - if (check != rel) { - x->second->set_name (check); - } - return x->second; + } catch (exception& e) { + error << string_compose (_("Could not create backend for %1: %2"), name, e.what()) << endmsg; + return boost::shared_ptr<AudioBackend>(); } - return boost::shared_ptr<Port> (); + return _backend; } -void -AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name) -{ - RCUWriter<Ports> writer (ports); - boost::shared_ptr<Ports> p = writer.get_copy(); - Ports::iterator x = p->find (old_relative_name); - - if (x != p->end()) { - boost::shared_ptr<Port> port = x->second; - p->erase (x); - p->insert (make_pair (new_relative_name, port)); - } -} +/* BACKEND PROXY WRAPPERS */ -const char ** -AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags) +int +AudioEngine::start () { - GET_PRIVATE_JACK_POINTER_RET (_jack,0); - if (!_running) { - if (!_has_run) { - fatal << _("get_ports called before engine was started") << endmsg; - /*NOTREACHED*/ - } else { - return 0; - } + if (!_backend) { + return -1; } - return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags); -} -void -AudioEngine::halted_info (jack_status_t code, const char* reason, void *arg) -{ - /* called from jack shutdown handler */ - - AudioEngine* ae = static_cast<AudioEngine *> (arg); - bool was_running = ae->_running; - - ae->stop_metering_thread (); - - ae->_running = false; - ae->_buffer_size = 0; - ae->_frame_rate = 0; - ae->_jack = 0; - - if (was_running) { - MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ -#ifdef HAVE_JACK_ON_INFO_SHUTDOWN - switch (code) { - case JackBackendError: - ae->Halted(reason); /* EMIT SIGNAL */ - break; - default: - ae->Halted(""); /* EMIT SIGNAL */ - } -#else - ae->Halted(""); /* EMIT SIGNAL */ -#endif - } -} - -void -AudioEngine::halted (void *arg) -{ - cerr << "HALTED by JACK\n"; + if (!_running) { - /* called from jack shutdown handler */ + if (_session) { + BootMessage (_("Connect session to engine")); + _session->set_frame_rate (_backend->sample_rate()); + } - AudioEngine* ae = static_cast<AudioEngine *> (arg); - bool was_running = ae->_running; + _processed_frames = 0; + last_monitor_check = 0; - ae->stop_metering_thread (); + if (_backend->start() == 0) { + _running = true; + last_monitor_check = 0; - ae->_running = false; - ae->_buffer_size = 0; - ae->_frame_rate = 0; - ae->_jack = 0; + if (_session && _session->config.get_jack_time_master()) { + _backend->set_time_master (true); + } - if (was_running) { - MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ - ae->Halted(""); /* EMIT SIGNAL */ + Running(); /* EMIT SIGNAL */ + } else { + /* should report error? */ + } } + + return _running ? 0 : -1; } -void -AudioEngine::died () +int +AudioEngine::stop () { - /* called from a signal handler for SIGPIPE */ + if (!_backend) { + return 0; + } - stop_metering_thread (); + Glib::Threads::Mutex::Lock lm (_process_lock); - _running = false; - _buffer_size = 0; - _frame_rate = 0; - _jack = 0; -} + if (_backend->stop () == 0) { + _running = false; + stop_metering_thread (); -bool -AudioEngine::can_request_hardware_monitoring () -{ - GET_PRIVATE_JACK_POINTER_RET (_jack,false); - const char ** ports; + Stopped (); /* EMIT SIGNAL */ - if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) { - return false; + return 0; } - free (ports); - - return true; + return -1; } -ChanCount -AudioEngine::n_physical (unsigned long flags) const +int +AudioEngine::pause () { - ChanCount c; - - GET_PRIVATE_JACK_POINTER_RET (_jack, c); - - const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags); - if (ports == 0) { - return c; + if (!_backend) { + return 0; } - - for (uint32_t i = 0; ports[i]; ++i) { - if (!strstr (ports[i], "Midi-Through")) { - DataType t (jack_port_type (jack_port_by_name (_jack, ports[i]))); - c.set (t, c.get (t) + 1); - } + + if (_backend->pause () == 0) { + _running = false; + Stopped(); /* EMIT SIGNAL */ + return 0; } - free (ports); - - return c; + return -1; } -ChanCount -AudioEngine::n_physical_inputs () const +int +AudioEngine::freewheel (bool start_stop) { - return n_physical (JackPortIsInput); + if (!_backend) { + return -1; + } + + /* _freewheeling will be set when first Freewheel signal occurs */ + + return _backend->freewheel (start_stop); } -ChanCount -AudioEngine::n_physical_outputs () const +float +AudioEngine::get_cpu_load() const { - return n_physical (JackPortIsOutput); + if (!_backend) { + return 0.0; + } + return _backend->cpu_load (); } -void -AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy) +bool +AudioEngine::is_realtime() const { - GET_PRIVATE_JACK_POINTER (_jack); - const char ** ports; - - if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) { - return; + if (!_backend) { + return false; } - if (ports) { - for (uint32_t i = 0; ports[i]; ++i) { - if (strstr (ports[i], "Midi-Through")) { - continue; - } - phy.push_back (ports[i]); - } - free (ports); - } + return _backend->is_realtime(); } -/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to - * a physical input connector. - */ -void -AudioEngine::get_physical_inputs (DataType type, vector<string>& ins) +bool +AudioEngine::connected() const { - get_physical (type, JackPortIsOutput, ins); + if (!_backend) { + return false; + } + + return _backend->connected(); } -/** Get physical ports for which JackPortIsInput is set; ie those that correspond to - * a physical output connector. - */ void -AudioEngine::get_physical_outputs (DataType type, vector<string>& outs) +AudioEngine::transport_start () { - get_physical (type, JackPortIsInput, outs); + if (!_backend) { + return; + } + return _backend->transport_start (); } void AudioEngine::transport_stop () { - GET_PRIVATE_JACK_POINTER (_jack); - jack_transport_stop (_priv_jack); + if (!_backend) { + return; + } + return _backend->transport_stop (); } -void -AudioEngine::transport_start () +TransportState +AudioEngine::transport_state () { - GET_PRIVATE_JACK_POINTER (_jack); - jack_transport_start (_priv_jack); + if (!_backend) { + return TransportStopped; + } + return _backend->transport_state (); } void -AudioEngine::transport_locate (framepos_t where) +AudioEngine::transport_locate (framepos_t pos) { - GET_PRIVATE_JACK_POINTER (_jack); - jack_transport_locate (_priv_jack, where); + if (!_backend) { + return; + } + return _backend->transport_locate (pos); } -AudioEngine::TransportState -AudioEngine::transport_state () +framepos_t +AudioEngine::transport_frame() { - GET_PRIVATE_JACK_POINTER_RET (_jack, ((TransportState) JackTransportStopped)); - jack_position_t pos; - return (TransportState) jack_transport_query (_priv_jack, &pos); + if (!_backend) { + return 0; + } + return _backend->transport_frame (); } -int -AudioEngine::reset_timebase () +framecnt_t +AudioEngine::sample_rate () const { - 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); - } else { - return jack_release_timebase (_jack); - } + if (!_backend) { + return 0; } - return 0; + return _backend->sample_rate (); } -int -AudioEngine::freewheel (bool onoff) +pframes_t +AudioEngine::samples_per_cycle () const { - GET_PRIVATE_JACK_POINTER_RET (_jack, -1); - - if (onoff != _freewheeling) { - return jack_set_freewheel (_priv_jack, onoff); - - } else { - /* already doing what has been asked for */ - return 0; + if (!_backend) { + return 0; } + return _backend->buffer_size (); } -void -AudioEngine::remove_all_ports () +int +AudioEngine::usecs_per_cycle () const { - /* make sure that JACK callbacks that will be invoked as we cleanup - * ports know that they have nothing to do. - */ - - port_remove_in_progress = true; - - /* process lock MUST be held by caller - */ - - { - RCUWriter<Ports> writer (ports); - boost::shared_ptr<Ports> ps = writer.get_copy (); - ps->clear (); + if (!_backend) { + return -1; } - - /* clear dead wood list in RCU */ - - ports.flush (); - - port_remove_in_progress = false; + return _backend->usecs_per_cycle (); } -int -AudioEngine::connect_to_jack (string client_name, string session_uuid) +size_t +AudioEngine::raw_buffer_size (DataType t) { - EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); - boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa; - jack_status_t status; - - /* revert all environment settings back to whatever they were when ardour started - */ - - if (global_epa) { - current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */ - global_epa->restore (); - } - - jack_client_name = client_name; /* might be reset below */ -#ifdef HAVE_JACK_SESSION - if (! session_uuid.empty()) - _jack = jack_client_open (jack_client_name.c_str(), JackSessionID, &status, session_uuid.c_str()); - else -#endif - _jack = jack_client_open (jack_client_name.c_str(), JackNullOption, &status, 0); - - if (_jack == NULL) { - // error message is not useful here + if (!_backend) { return -1; } + return _backend->raw_buffer_size (t); +} - GET_PRIVATE_JACK_POINTER_RET (_jack, -1); - - if (status & JackNameNotUnique) { - jack_client_name = jack_get_client_name (_priv_jack); +pframes_t +AudioEngine::sample_time () +{ + if (!_backend) { + return 0; } - - return 0; + return _backend->sample_time (); } -int -AudioEngine::disconnect_from_jack () +pframes_t +AudioEngine::sample_time_at_cycle_start () { - GET_PRIVATE_JACK_POINTER_RET (_jack, 0); - - if (_running) { - stop_metering_thread (); + if (!_backend) { + return 0; } + return _backend->sample_time_at_cycle_start (); +} - { - Glib::Threads::Mutex::Lock lm (_process_lock); - jack_client_close (_priv_jack); - _jack = 0; +pframes_t +AudioEngine::samples_since_cycle_start () +{ + if (!_backend) { + return 0; } + return _backend->samples_since_cycle_start (); +} - _buffer_size = 0; - _frame_rate = 0; - _raw_buffer_sizes.clear(); - - if (_running) { - _running = false; - MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */ - Stopped(); /* EMIT SIGNAL */ +bool +AudioEngine::get_sync_offset (pframes_t& offset) const +{ + if (!_backend) { + return false; } - - return 0; + return _backend->get_sync_offset (offset); } int -AudioEngine::reconnect_to_jack () +AudioEngine::create_process_thread (boost::function<void()> func, pthread_t* thr, size_t stacksize) { - if (_running) { - disconnect_from_jack (); - /* XXX give jackd a chance */ - Glib::usleep (250000); - } - - if (connect_to_jack (jack_client_name, "")) { - error << _("failed to connect to JACK") << endmsg; + if (!_backend) { return -1; } + return _backend->create_process_thread (func, thr, stacksize); +} - Ports::iterator i; - - boost::shared_ptr<Ports> p = ports.reader (); - - for (i = p->begin(); i != p->end(); ++i) { - if (i->second->reestablish ()) { - break; - } - } - if (i != p->end()) { - /* failed */ - remove_all_ports (); +int +AudioEngine::set_device_name (const std::string& name) +{ + if (!_backend) { return -1; } + return _backend->set_device_name (name); +} - GET_PRIVATE_JACK_POINTER_RET (_jack,-1); - - MIDI::Manager::instance()->reestablish (_priv_jack); - - if (_session) { - _session->reset_jack_connection (_priv_jack); - jack_bufsize_callback (jack_get_buffer_size (_priv_jack)); - _session->set_frame_rate (jack_get_sample_rate (_priv_jack)); - } - - last_monitor_check = 0; - - set_jack_callbacks (); - - if (jack_activate (_priv_jack) == 0) { - _running = true; - _has_run = true; - } else { +int +AudioEngine::set_sample_rate (float sr) +{ + if (!_backend) { return -1; } + return _backend->set_sample_rate (sr); +} - /* re-establish connections */ - - for (i = p->begin(); i != p->end(); ++i) { - i->second->reconnect (); +int +AudioEngine::set_buffer_size (uint32_t bufsiz) +{ + if (!_backend) { + return -1; } - - MIDI::Manager::instance()->reconnect (); - - Running (); /* EMIT SIGNAL*/ - - start_metering_thread (); - - return 0; + return _backend->set_buffer_size (bufsiz); } int -AudioEngine::request_buffer_size (pframes_t nframes) +AudioEngine::set_sample_format (SampleFormat sf) { - GET_PRIVATE_JACK_POINTER_RET (_jack, -1); - - if (nframes == jack_get_buffer_size (_priv_jack)) { - return 0; + if (!_backend) { + return -1; } - - return jack_set_buffer_size (_priv_jack, nframes); + return _backend->set_sample_format (sf); } -string -AudioEngine::make_port_name_relative (string portname) const +int +AudioEngine::set_interleaved (bool yn) { - string::size_type len; - string::size_type n; - - len = portname.length(); - - for (n = 0; n < len; ++n) { - if (portname[n] == ':') { - break; - } + if (!_backend) { + return -1; } + return _backend->set_interleaved (yn); +} - if ((n != len) && (portname.substr (0, n) == jack_client_name)) { - return portname.substr (n+1); +int +AudioEngine::set_input_channels (uint32_t ic) +{ + if (!_backend) { + return -1; } - - return portname; + return _backend->set_input_channels (ic); } -string -AudioEngine::make_port_name_non_relative (string portname) const +int +AudioEngine::set_output_channels (uint32_t oc) { - string str; - - if (portname.find_first_of (':') != string::npos) { - return portname; + if (!_backend) { + return -1; } - - str = jack_client_name; - str += ':'; - str += portname; - - return str; + return _backend->set_output_channels (oc); } -bool -AudioEngine::port_is_mine (const string& portname) const +int +AudioEngine::set_systemic_input_latency (uint32_t il) { - if (portname.find_first_of (':') != string::npos) { - if (portname.substr (0, jack_client_name.length ()) != jack_client_name) { - return false; - } - } - return true; + if (!_backend) { + return -1; + } + return _backend->set_systemic_input_latency (il); } -bool -AudioEngine::is_realtime () const +int +AudioEngine::set_systemic_output_latency (uint32_t ol) { - GET_PRIVATE_JACK_POINTER_RET (_jack,false); - return jack_is_realtime (_priv_jack); + if (!_backend) { + return -1; + } + return _backend->set_systemic_output_latency (ol); } -int -AudioEngine::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize) +/* END OF BACKEND PROXY API */ + +void +AudioEngine::thread_init_callback (void* arg) { - GET_PRIVATE_JACK_POINTER_RET (_jack, 0); - ThreadData* td = new ThreadData (this, f, stacksize); + /* make sure that anybody who needs to know about this thread + knows about it. + */ - 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; - } + pthread_set_name (X_("audioengine")); - return 0; -} + PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("AudioEngine"), 4096); + PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("AudioEngine"), 128); -void* -AudioEngine::_start_process_thread (void* arg) -{ - ThreadData* td = reinterpret_cast<ThreadData*> (arg); - boost::function<void()> f = td->f; - delete td; + SessionEvent::create_per_thread_pool (X_("AudioEngine"), 512); - f (); + MIDI::JackMIDIPort::set_process_thread (pthread_self()); - return 0; + if (arg) { + AudioEngine* ae = static_cast<AudioEngine*> (arg); + /* the special thread created/managed by the backend */ + ae->_main_thread = new ProcessThread; + } } -bool -AudioEngine::port_is_physical (const std::string& portname) const +/* XXXX +void +AudioEngine::timebase_callback (TransportState state, pframes_t nframes, jack_position_t pos, int new_position) { - GET_PRIVATE_JACK_POINTER_RET(_jack, false); - - jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str()); - - if (!port) { - return false; - } + if (_session && _session->synced_to_jack()) { + // _session->timebase_callback (state, nframes, pos, new_position); + } +} +*/ - return jack_port_flags (port) & JackPortIsPhysical; +int +AudioEngine::sync_callback (TransportState state, framepos_t position) +{ + if (_session) { + return _session->jack_sync_callback (state, position); + } + return 0; } void -AudioEngine::request_jack_monitors_input (const std::string& portname, bool yn) const +AudioEngine::freewheel_callback (bool onoff) { - GET_PRIVATE_JACK_POINTER(_jack); - - jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str()); + if (onoff) { + _pre_freewheel_mmc_enabled = MIDI::Manager::instance()->mmc()->send_enabled (); + MIDI::Manager::instance()->mmc()->enable_send (false); + } else { + MIDI::Manager::instance()->mmc()->enable_send (_pre_freewheel_mmc_enabled); + } +} - if (!port) { - return; +void +AudioEngine::latency_callback (bool for_playback) +{ + if (_session) { + _session->update_latency (for_playback); } - - jack_port_request_monitor (port, yn); } void AudioEngine::update_latencies () { - if (jack_recompute_total_latencies) { - GET_PRIVATE_JACK_POINTER (_jack); - jack_recompute_total_latencies (_priv_jack); - } + if (_backend) { + _backend->update_latencies (); + } } void -AudioEngine::destroy () +AudioEngine::halted_callback (const char* why) { - delete _instance; - _instance = 0; + stop_metering_thread (); + + MIDI::JackMIDIPort::EngineHalted (); /* EMIT SIGNAL */ + Halted (why); /* EMIT SIGNAL */ } +bool +AudioEngine::setup_required () const +{ + if (_backends.size() == 1 && _backends.begin()->second->already_configured()) { + return false; + } + + return true; +} diff --git a/libs/ardour/backend_search_path.cc b/libs/ardour/backend_search_path.cc new file mode 100644 index 0000000000..9a0425094b --- /dev/null +++ b/libs/ardour/backend_search_path.cc @@ -0,0 +1,45 @@ +/* + 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. + +*/ + +#include <glibmm/miscutils.h> + +#include "ardour/backend_search_path.h" +#include "ardour/directory_names.h" +#include "ardour/filesystem_paths.h" + +namespace { + const char * const backend_env_variable_name = "ARDOUR_BACKEND_PATH"; +} // anonymous + +using namespace PBD; + +namespace ARDOUR { + +SearchPath +backend_search_path () +{ + SearchPath spath(user_config_directory ()); + spath += ardour_dll_directory (); + spath.add_subdirectory_to_paths(backend_dir_name); + + spath += SearchPath(Glib::getenv(backend_env_variable_name)); + return spath; +} + +} // namespace ARDOUR diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc index 0ac62d7076..be4b04e36a 100644 --- a/libs/ardour/bundle.cc +++ b/libs/ardour/bundle.cc @@ -450,20 +450,19 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine) bool Bundle::connected_to_anything (AudioEngine& engine) { + PortManager& pm (engine); + for (uint32_t i = 0; i < nchannels().n_total(); ++i) { Bundle::PortList const & ports = channel_ports (i); for (uint32_t j = 0; j < ports.size(); ++j) { - /* ports[j] may not be an Ardour port, so use JACK directly + + /* ports[j] may not be an Ardour port, so use the port manager directly rather than doing it with Port. */ - jack_port_t* jp = jack_port_by_name (engine.jack(), ports[j].c_str()); - if (jp) { - const char ** c = jack_port_get_all_connections (engine.jack(), jp); - if (c) { - jack_free (c); - return true; - } + + if (pm.connected (ports[j])) { + return true; } } } diff --git a/libs/ardour/capturing_processor.cc b/libs/ardour/capturing_processor.cc index 4a31d92cc8..ce4a546fb4 100644 --- a/libs/ardour/capturing_processor.cc +++ b/libs/ardour/capturing_processor.cc @@ -28,7 +28,7 @@ namespace ARDOUR { CapturingProcessor::CapturingProcessor (Session & session) : Processor (session, X_("capture point")) - , block_size (session.engine().frames_per_cycle()) + , block_size (AudioEngine::instance()->samples_per_cycle()) { realloc_buffers (); } diff --git a/libs/ardour/directory_names.cc b/libs/ardour/directory_names.cc index 0632c6f8f2..af7f7f550c 100644 --- a/libs/ardour/directory_names.cc +++ b/libs/ardour/directory_names.cc @@ -37,6 +37,7 @@ const char* const templates_dir_name = X_("templates"); const char* const route_templates_dir_name = X_("route_templates"); const char* const surfaces_dir_name = X_("surfaces"); const char* const panner_dir_name = X_("panners"); +const char* const backend_dir_name = X_("backends"); /* these should end up using variants of PROGRAM_NAME */ #ifdef __APPLE__ diff --git a/libs/ardour/export_channel.cc b/libs/ardour/export_channel.cc index 9b3f50e85d..82e5d80244 100644 --- a/libs/ardour/export_channel.cc +++ b/libs/ardour/export_channel.cc @@ -117,7 +117,7 @@ RegionExportChannelFactory::RegionExportChannelFactory (Session * session, Audio : region (region) , track (track) , type (type) - , frames_per_cycle (session->engine().frames_per_cycle ()) + , frames_per_cycle (session->engine().samples_per_cycle ()) , buffers_up_to_date (false) , region_start (region.position()) , position (region_start) diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc index c7875c0b9b..301914b0ae 100644 --- a/libs/ardour/export_graph_builder.cc +++ b/libs/ardour/export_graph_builder.cc @@ -54,7 +54,7 @@ ExportGraphBuilder::ExportGraphBuilder (Session const & session) : session (session) , thread_pool (hardware_concurrency()) { - process_buffer_frames = session.engine().frames_per_cycle(); + process_buffer_frames = session.engine().samples_per_cycle(); } ExportGraphBuilder::~ExportGraphBuilder () @@ -505,7 +505,7 @@ ExportGraphBuilder::ChannelConfig::ChannelConfig (ExportGraphBuilder & parent, F config = new_config; - framecnt_t max_frames = parent.session.engine().frames_per_cycle(); + framecnt_t max_frames = parent.session.engine().samples_per_cycle(); interleaver.reset (new Interleaver<Sample> ()); interleaver->init (new_config.channel_config->get_n_chans(), max_frames); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 4c91956ffd..d744368fe7 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -72,6 +72,7 @@ #include "ardour/analyser.h" #include "ardour/audio_library.h" +#include "ardour/audio_backend.h" #include "ardour/audioengine.h" #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" @@ -331,6 +332,8 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir EventTypeMap::instance().new_parameter(EnvelopeAutomation); EventTypeMap::instance().new_parameter(MidiCCAutomation); + ARDOUR::AudioEngine::create (); + return 0; } @@ -338,7 +341,7 @@ void ARDOUR::init_post_engine () { /* the MIDI Manager is needed by the ControlProtocolManager */ - MIDI::Manager::create (AudioEngine::instance()->jack()); + MIDI::Manager::create (AudioEngine::instance()->port_engine()); ControlProtocolManager::instance().discover_control_protocols (); diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index 029a46ce53..8136985e1f 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -310,7 +310,7 @@ bool InternalSend::configure_io (ChanCount in, ChanCount out) { bool ret = Send::configure_io (in, out); - set_block_size (_session.engine().frames_per_cycle()); + set_block_size (_session.engine().samples_per_cycle()); return ret; } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 21fdca6e96..c517defe1a 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -1337,7 +1337,7 @@ IO::bundle_changed (Bundle::Change /*c*/) string IO::build_legal_port_name (DataType type) { - const int name_size = jack_port_name_size(); + const int name_size = AudioEngine::instance()->port_name_size(); int limit; string suffix; @@ -1371,7 +1371,7 @@ IO::build_legal_port_name (DataType type) // allow up to 4 digits for the output port number, plus the slash, suffix and extra space - limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5); + limit = name_size - AudioEngine::instance()->my_name().length() - (suffix.length() + 5); char buf1[name_size+1]; char buf2[name_size+1]; diff --git a/libs/ardour/jack_api.cc b/libs/ardour/jack_api.cc new file mode 100644 index 0000000000..fcfc499107 --- /dev/null +++ b/libs/ardour/jack_api.cc @@ -0,0 +1,102 @@ +/* + 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. + +*/ + +#include "ardour/jack_connection.h" +#include "ardour/jack_audiobackend.h" +#include "ardour/jack_portengine.h" + +using namespace ARDOUR; + +static boost::shared_ptr<JACKAudioBackend> backend; +static boost::shared_ptr<JACKPortEngine> port_engine; +static boost::shared_ptr<JackConnection> jack_connection; + +static boost::shared_ptr<AudioBackend> +backend_factory (AudioEngine& ae) +{ + if (!jack_connection) { + return boost::shared_ptr<AudioBackend>(); + } + + if (!backend) { + backend.reset (new JACKAudioBackend (ae, jack_connection)); + } + + return backend; +} + +static boost::shared_ptr<PortEngine> +portengine_factory (PortManager& pm) +{ + if (!jack_connection) { + return boost::shared_ptr<PortEngine>(); + } + + if (!port_engine) { + port_engine.reset (new JACKPortEngine (pm, jack_connection)); + } + + return port_engine; +} + +static int +instantiate (const std::string& arg1, const std::string& arg2) +{ + try { + jack_connection.reset (new JackConnection (arg1, arg2)); + } catch (...) { + return -1; + } + + return 0; +} + +static int +deinstantiate () +{ + port_engine.reset (); + backend.reset (); + jack_connection.reset (); + + return 0; +} + +static bool +already_configured () +{ + return JackConnection::server_running (); +} + +extern "C" { + + + /* functions looked up using dlopen-and-cousins, and so naming scope + * must be non-mangled. + */ + + ARDOUR::AudioBackendInfo descriptor = { + "JACK", + instantiate, + deinstantiate, + backend_factory, + portengine_factory, + already_configured, + }; +} + diff --git a/libs/ardour/jack_audiobackend.cc b/libs/ardour/jack_audiobackend.cc new file mode 100644 index 0000000000..19158aacc9 --- /dev/null +++ b/libs/ardour/jack_audiobackend.cc @@ -0,0 +1,947 @@ +/* + 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. + +*/ + +#include <math.h> + +#include <boost/scoped_ptr.hpp> +#include <glibmm/timer.h> + +#include "pbd/error.h" + +#include "midi++/manager.h" + +#include "ardour/audioengine.h" +#include "ardour/types.h" +#include "ardour/jack_audiobackend.h" +#include "ardour/jack_connection.h" +#include "ardour/jack_portengine.h" +#include "ardour/jack_utils.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using std::string; +using std::vector; + +#define GET_PRIVATE_JACK_POINTER(localvar) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; } +#define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; } + +JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection> jc) + : AudioBackend (e) + , _jack_connection (jc) + , _running (false) + , _freewheeling (false) + , _target_sample_rate (48000) + , _target_buffer_size (1024) + , _target_sample_format (FormatFloat) + , _target_interleaved (false) + , _target_input_channels (-1) + , _target_output_channels (-1) + , _target_systemic_input_latency (0) + , _target_systemic_output_latency (0) +{ +} + +JACKAudioBackend::~JACKAudioBackend() +{ +} + +string +JACKAudioBackend::name() const +{ + return X_("JACK"); +} + +void* +JACKAudioBackend::private_handle() const +{ + return _jack_connection->jack(); +} + +bool +JACKAudioBackend::connected() const +{ + return (private_handle() != 0); +} + +bool +JACKAudioBackend::is_realtime () const +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false); + return jack_is_realtime (_priv_jack); +} + +bool +JACKAudioBackend::requires_driver_selection() const +{ + return true; +} + +vector<string> +JACKAudioBackend::enumerate_drivers () const +{ + vector<string> currently_available; + get_jack_audio_driver_names (currently_available); + return currently_available; +} + +int +JACKAudioBackend::set_driver (const std::string& name) +{ + _target_driver = name; + return 0; +} + +vector<AudioBackend::DeviceStatus> +JACKAudioBackend::enumerate_devices () const +{ + vector<string> currently_available = get_jack_device_names_for_audio_driver (_target_driver); + vector<DeviceStatus> statuses; + + if (all_devices.find (_target_driver) == all_devices.end()) { + all_devices.insert (make_pair (_target_driver, std::set<string>())); + } + + /* store every device we've found, by driver name. + * + * This is so we do not confuse ALSA, FFADO, netjack etc. devices + * with each other. + */ + + DeviceList& all (all_devices[_target_driver]); + + for (vector<string>::const_iterator d = currently_available.begin(); d != currently_available.end(); ++d) { + all.insert (*d); + } + + for (DeviceList::const_iterator d = all.begin(); d != all.end(); ++d) { + if (find (currently_available.begin(), currently_available.end(), *d) == currently_available.end()) { + statuses.push_back (DeviceStatus (*d, false)); + } else { + statuses.push_back (DeviceStatus (*d, false)); + } + } + + return statuses; +} + +vector<float> +JACKAudioBackend::available_sample_rates (const string& /*device*/) const +{ + vector<float> f; + + if (connected()) { + f.push_back (sample_rate()); + return f; + } + + /* if JACK is not already running, just list a bunch of reasonable + values and let the future sort it all out. + */ + + f.push_back (8000.0); + f.push_back (16000.0); + f.push_back (24000.0); + f.push_back (32000.0); + f.push_back (44100.0); + f.push_back (48000.0); + f.push_back (88200.0); + f.push_back (96000.0); + f.push_back (192000.0); + f.push_back (384000.0); + + return f; +} + +vector<uint32_t> +JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const +{ + vector<uint32_t> s; + + if (connected()) { + s.push_back (buffer_size()); + return s; + } + + s.push_back (8); + s.push_back (16); + s.push_back (32); + s.push_back (64); + s.push_back (128); + s.push_back (256); + s.push_back (512); + s.push_back (1024); + s.push_back (2048); + s.push_back (4096); + s.push_back (8192); + + return s; +} + +uint32_t +JACKAudioBackend::available_input_channel_count (const string& /*device*/) const +{ + return 128; +} + +uint32_t +JACKAudioBackend::available_output_channel_count (const string& /*device*/) const +{ + return 128; +} + +/* -- parameter setting -- */ + +int +JACKAudioBackend::set_device_name (const string& dev) +{ + if (connected()) { + /* need to stop and restart JACK for this to work, at present */ + return -1; + } + + _target_device = dev; + return 0; +} + +int +JACKAudioBackend::set_sample_rate (float sr) +{ + if (!connected()) { + _target_sample_rate = sr; + return 0; + } + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + if (sr == jack_get_sample_rate (_priv_jack)) { + return 0; + } + + return -1; +} + +int +JACKAudioBackend::set_buffer_size (uint32_t nframes) +{ + if (!connected()) { + _target_buffer_size = nframes; + return 0; + } + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + if (nframes == jack_get_buffer_size (_priv_jack)) { + return 0; + } + + return jack_set_buffer_size (_priv_jack, nframes); +} + +int +JACKAudioBackend::set_sample_format (SampleFormat sf) +{ + /* as far as JACK clients are concerned, the hardware is always + * floating point format. + */ + if (sf == FormatFloat) { + return 0; + } + return -1; +} + +int +JACKAudioBackend::set_interleaved (bool yn) +{ + /* as far as JACK clients are concerned, the hardware is always + * non-interleaved + */ + if (!yn) { + return 0; + } + return -1; +} + +int +JACKAudioBackend::set_input_channels (uint32_t cnt) +{ + if (connected()) { + return -1; + } + + _target_input_channels = cnt; + + return 0; +} + +int +JACKAudioBackend::set_output_channels (uint32_t cnt) +{ + if (connected()) { + return -1; + } + + _target_output_channels = cnt; + + return 0; +} + +int +JACKAudioBackend::set_systemic_input_latency (uint32_t l) +{ + if (connected()) { + return -1; + } + + _target_systemic_input_latency = l; + + return 0; +} + +int +JACKAudioBackend::set_systemic_output_latency (uint32_t l) +{ + if (connected()) { + return -1; + } + + _target_systemic_output_latency = l; + + return 0; +} + +/* --- Parameter retrieval --- */ + +std::string +JACKAudioBackend::device_name () const +{ + return string(); +} + +float +JACKAudioBackend::sample_rate () const +{ + if (connected()) { + return _current_sample_rate; + } + return _target_sample_rate; +} + +uint32_t +JACKAudioBackend::buffer_size () const +{ + if (connected()) { + return _current_buffer_size; + } + return _target_buffer_size; +} + +SampleFormat +JACKAudioBackend::sample_format () const +{ + return FormatFloat; +} + +bool +JACKAudioBackend::interleaved () const +{ + return false; +} + +uint32_t +JACKAudioBackend::input_channels () const +{ + if (connected()) { + return n_physical (JackPortIsInput).n_audio(); + } + return _target_input_channels; +} + +uint32_t +JACKAudioBackend::output_channels () const +{ + if (connected()) { + return n_physical (JackPortIsOutput).n_audio(); + } + return _target_output_channels; +} + +uint32_t +JACKAudioBackend::systemic_input_latency () const +{ + return _current_systemic_output_latency; +} + +uint32_t +JACKAudioBackend::systemic_output_latency () const +{ + return _current_systemic_output_latency; +} + +size_t +JACKAudioBackend::raw_buffer_size(DataType t) +{ + std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t); + return (s != _raw_buffer_sizes.end()) ? s->second : 0; +} + +void +JACKAudioBackend::setup_jack_startup_command () +{ + /* first we map the parameters that have been set onto a + * JackCommandLineOptions object. + */ + + JackCommandLineOptions options; + + get_jack_default_server_path (options.server_path); + options.driver = _target_driver; + options.samplerate = _target_sample_rate; + options.period_size = _target_buffer_size; + options.num_periods = 2; + options.input_device = _target_device; + options.output_device = _target_device; + options.input_latency = _target_systemic_input_latency; + options.output_latency = _target_systemic_output_latency; + if (_target_sample_format == FormatInt16) { + options.force16_bit = _target_sample_format; + } + options.realtime = true; + options.ports_max = 2048; + + /* this must always be true for any server instance we start ourselves + */ + + options.temporary = true; + + string cmdline; + + if (!get_jack_command_line_string (options, cmdline)) { + /* error, somehow */ + } + + std::cerr << "JACK command line will be: " << cmdline << std::endl; + + // write_jack_config_file (get_jack_server_user_config_file_path(), cmdline); +} + +/* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */ + +int +JACKAudioBackend::start () +{ + if (!connected()) { + + if (!_jack_connection->server_running()) { + setup_jack_startup_command (); + } + + _jack_connection->open (); + } + + engine.reestablish_ports (); + + if (!jack_port_type_get_buffer_size) { + warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg; + } + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + engine.sample_rate_change (jack_get_sample_rate (_priv_jack)); + + /* testing the nullity of this function name is a proxy for + * whether jack_activate() will definitely call the buffer size + * callback. with older versions of JACK, this function symbol + * will be null. this is sort of reliable, but not clean since + * weak symbol support is highly platform and compiler + * specific. + */ + if (!jack_port_type_get_buffer_size) { + jack_bufsize_callback (jack_get_buffer_size (_priv_jack)); + } + + set_jack_callbacks (); + + if (jack_activate (_priv_jack) == 0) { + _running = true; + } else { + // error << _("cannot activate JACK client") << endmsg; + } + + engine.reconnect_ports (); + + return 0; +} + +int +JACKAudioBackend::stop () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + _jack_connection->close (); + + _current_buffer_size = 0; + _current_sample_rate = 0; + + _raw_buffer_sizes.clear(); + + return 0; +} + +int +JACKAudioBackend::pause () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + if (_priv_jack) { + jack_deactivate (_priv_jack); + } + + return 0; +} + +int +JACKAudioBackend::freewheel (bool onoff) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + + if (onoff == _freewheeling) { + /* already doing what has been asked for */ + + return 0; + } + + if (jack_set_freewheel (_priv_jack, onoff) == 0) { + _freewheeling = true; + return 0; + } + + return -1; +} + +/* --- TRANSPORT STATE MANAGEMENT --- */ + +void +JACKAudioBackend::transport_stop () +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_transport_stop (_priv_jack); +} + +void +JACKAudioBackend::transport_start () +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_transport_start (_priv_jack); +} + +void +JACKAudioBackend::transport_locate (framepos_t where) +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_transport_locate (_priv_jack, where); +} + +framepos_t +JACKAudioBackend::transport_frame () const +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_get_current_transport_frame (_priv_jack); +} + +TransportState +JACKAudioBackend::transport_state () const +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_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 (_priv_jack, -1); + if (yn) { + return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); + } else { + return jack_release_timebase (_priv_jack); + } +} + +/* process-time */ + +bool +JACKAudioBackend::get_sync_offset (pframes_t& offset) const +{ + +#ifdef HAVE_JACK_VIDEO_SUPPORT + + GET_PRIVATE_JACK_POINTER_RET (_priv_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::sample_time () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_frame_time (_priv_jack); +} + +pframes_t +JACKAudioBackend::sample_time_at_cycle_start () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_last_frame_time (_priv_jack); +} + +pframes_t +JACKAudioBackend::samples_since_cycle_start () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_frames_since_cycle_start (_priv_jack); +} + +/* JACK Callbacks */ + +static void +ardour_jack_error (const char* msg) +{ + error << "JACK: " << msg << endmsg; +} + +void +JACKAudioBackend::set_jack_callbacks () +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + + jack_set_thread_init_callback (_priv_jack, AudioEngine::thread_init_callback, 0); + + jack_set_process_thread (_priv_jack, _process_thread, this); + jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this); + jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this); + jack_set_xrun_callback (_priv_jack, _xrun_callback, this); + jack_set_sync_callback (_priv_jack, _jack_sync_callback, this); + jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this); + +#ifdef HAVE_JACK_SESSION + if( jack_set_session_callback) + jack_set_session_callback (_priv_jack, _session_callback, this); +#endif + + if (jack_set_latency_callback) { + jack_set_latency_callback (_priv_jack, _latency_callback, this); + } + + jack_set_error_function (ardour_jack_error); +} + +void +JACKAudioBackend::_jack_timebase_callback (jack_transport_state_t state, pframes_t nframes, + jack_position_t* pos, int new_position, void *arg) +{ + static_cast<JACKAudioBackend*> (arg)->jack_timebase_callback (state, nframes, pos, new_position); +} + +void +JACKAudioBackend::jack_timebase_callback (jack_transport_state_t state, pframes_t /*nframes*/, + jack_position_t* pos, int /*new_position*/) +{ + TransportState tstate; + framepos_t position; + + switch (state) { + case JackTransportStopped: + tstate = TransportStopped; + break; + case JackTransportRolling: + tstate = TransportRolling; + break; + case JackTransportLooping: + tstate = TransportLooping; + break; + case JackTransportStarting: + tstate = TransportStarting; + break; + } + + if (pos) { + position = pos->frame; + } + + // engine.timebase_callback (tstate, nframes, position, new_position); +} + +int +JACKAudioBackend::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg) +{ + return static_cast<JACKAudioBackend*> (arg)->jack_sync_callback (state, pos); +} + +int +JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos) +{ + TransportState tstate; + + switch (state) { + case JackTransportStopped: + tstate = TransportStopped; + break; + case JackTransportRolling: + tstate = TransportRolling; + break; + case JackTransportLooping: + tstate = TransportLooping; + break; + case JackTransportStarting: + tstate = TransportStarting; + break; + } + + return engine.sync_callback (tstate, pos->frame); + + return true; +} + +int +JACKAudioBackend::_xrun_callback (void *arg) +{ + JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg); + if (ae->connected()) { + ae->engine.Xrun (); /* EMIT SIGNAL */ + } + return 0; +} + +#ifdef HAVE_JACK_SESSION +void +JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg) +{ + JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg); + if (ae->connected()) { + ae->engine.JackSessionEvent (event); /* EMIT SIGNAL */ + } +} +#endif + +void +JACKAudioBackend::_freewheel_callback (int onoff, void *arg) +{ + static_cast<JACKAudioBackend*>(arg)->freewheel_callback (onoff); +} + +void +JACKAudioBackend::freewheel_callback (int onoff) +{ + _freewheeling = onoff; + engine.freewheel_callback (onoff); +} + +void +JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* arg) +{ + return static_cast<JACKAudioBackend*> (arg)->jack_latency_callback (mode); +} + +int +JACKAudioBackend::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_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<ThreadData*> (arg); + boost::function<void()> f = td->f; + delete td; + + f (); + + return 0; +} + +void* +JACKAudioBackend::_process_thread (void *arg) +{ + return static_cast<JACKAudioBackend*> (arg)->process_thread (); +} + +void* +JACKAudioBackend::process_thread () +{ + /* JACK doesn't do this for us when we use the wait API + */ + + AudioEngine::thread_init_callback (this); + + while (1) { + GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0); + + pframes_t nframes = jack_cycle_wait (_priv_jack); + + if (engine.process_callback (nframes)) { + return 0; + } + + jack_cycle_signal (_priv_jack, 0); + } + + return 0; +} + +int +JACKAudioBackend::_sample_rate_callback (pframes_t nframes, void *arg) +{ + return static_cast<JACKAudioBackend*> (arg)->jack_sample_rate_callback (nframes); +} + +int +JACKAudioBackend::jack_sample_rate_callback (pframes_t nframes) +{ + _current_sample_rate = nframes; + return engine.sample_rate_change (nframes); +} + +void +JACKAudioBackend::jack_latency_callback (jack_latency_callback_mode_t mode) +{ + engine.latency_callback (mode == JackPlaybackLatency); +} + +int +JACKAudioBackend::_bufsize_callback (pframes_t nframes, void *arg) +{ + return static_cast<JACKAudioBackend*> (arg)->jack_bufsize_callback (nframes); +} + +int +JACKAudioBackend::jack_bufsize_callback (pframes_t nframes) +{ + /* if the size has not changed, this should be a no-op */ + + if (nframes == _current_buffer_size) { + return 0; + } + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 1); + + _current_buffer_size = nframes; + _current_usecs_per_cycle = (int) floor ((((double) nframes / sample_rate())) * 1000000.0); + + if (jack_port_type_get_buffer_size) { + _raw_buffer_sizes[DataType::AUDIO] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_AUDIO_TYPE); + _raw_buffer_sizes[DataType::MIDI] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_MIDI_TYPE); + } else { + + /* Old version of JACK. + + These crude guesses, see below where we try to get the right answers. + + Note that our guess for MIDI deliberatey tries to overestimate + by a little. It would be nicer if we could get the actual + size from a port, but we have to use this estimate in the + event that there are no MIDI ports currently. If there are + the value will be adjusted below. + */ + + _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample); + _raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2); + } + + engine.buffer_size_change (nframes); + + return 0; +} + +void +JACKAudioBackend::disconnected (const char* why) +{ + /* called from jack shutdown handler */ + + bool was_running = _running; + + _running = false; + _current_buffer_size = 0; + _current_sample_rate = 0; + + if (was_running) { + engine.halted_callback (why); /* EMIT SIGNAL */ + } +} +float +JACKAudioBackend::cpu_load() const +{ + GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0); + return jack_cpu_load (_priv_jack); +} + +void +JACKAudioBackend::update_latencies () +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_recompute_total_latencies (_priv_jack); +} + +ChanCount +JACKAudioBackend::n_physical (unsigned long flags) const +{ + ChanCount c; + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, c); + + const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags); + + if (ports) { + for (uint32_t i = 0; ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + DataType t (jack_port_type (jack_port_by_name (_priv_jack, ports[i]))); + c.set (t, c.get (t) + 1); + } + } + + jack_free (ports); + } + + return c; +} diff --git a/libs/ardour/jack_connection.cc b/libs/ardour/jack_connection.cc new file mode 100644 index 0000000000..57950b0e17 --- /dev/null +++ b/libs/ardour/jack_connection.cc @@ -0,0 +1,158 @@ +/* + 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. + +*/ + +#include <boost/scoped_ptr.hpp> +#include <jack/session.h> + +#include "pbd/epa.h" + +#include "ardour/jack_connection.h" +#include "ardour/jack_utils.h" + +#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; } +#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; } + +using namespace ARDOUR; +using namespace PBD; +using std::string; +using std::vector; + +static void jack_halted_callback (void* arg) +{ + JackConnection* jc = static_cast<JackConnection*> (arg); + jc->halted_callback (); +} + +static void jack_halted_info_callback (jack_status_t code, const char* reason, void* arg) +{ + JackConnection* jc = static_cast<JackConnection*> (arg); + jc->halted_info_callback (code, reason); +} + + +JackConnection::JackConnection (const std::string& arg1, const std::string& arg2) + : _jack (0) + , _client_name (arg1) + , session_uuid (arg2) +{ +} + +JackConnection::~JackConnection () +{ + close (); +} + +bool +JackConnection::server_running () +{ + EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); + boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa; + + /* revert all environment settings back to whatever they were when + * ardour started, because ardour's startup script may have reset + * something in ways that interfere with finding/starting JACK. + */ + + if (global_epa) { + current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */ + global_epa->restore (); + } + + jack_status_t status; + jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status); + + if (status == 0) { + jack_client_close (c); + return true; + } + + return false; +} + +int +JackConnection::open () +{ + EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); + boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa; + jack_status_t status; + + close (); + + /* revert all environment settings back to whatever they were when ardour started + */ + + if (global_epa) { + current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */ + global_epa->restore (); + } + + /* ensure that PATH or equivalent includes likely locations of the JACK + * server, in case the user's default does not. + */ + + vector<string> dirs; + get_jack_server_dir_paths (dirs); + set_path_env_for_jack_autostart (dirs); + + if ((_jack = jack_client_open (_client_name.c_str(), JackSessionID, &status, session_uuid.c_str())) == 0) { + return -1; + } + + if (status & JackNameNotUnique) { + _client_name = jack_get_client_name (_jack); + } + + /* attach halted handler */ + + if (jack_on_info_shutdown) { + jack_on_info_shutdown (_jack, jack_halted_info_callback, this); + } else { + jack_on_shutdown (_jack, jack_halted_callback, this); + } + + return 0; +} + +int +JackConnection::close () +{ + GET_PRIVATE_JACK_POINTER_RET (_jack, -1); + + if (_priv_jack) { + return jack_client_close (_priv_jack); + } + + return 0; +} + +void +JackConnection::halted_callback () +{ + _jack = 0; + Disconnected (""); +} + +void +JackConnection::halted_info_callback (jack_status_t /*status*/, const char* reason) +{ + _jack = 0; + Disconnected (reason); +} + + diff --git a/libs/ardour/jack_portengine.cc b/libs/ardour/jack_portengine.cc new file mode 100644 index 0000000000..82b4502cb6 --- /dev/null +++ b/libs/ardour/jack_portengine.cc @@ -0,0 +1,532 @@ +/* + 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. + +*/ + +#include <string.h> +#include <stdint.h> + +#include "pbd/error.h" + +#include "ardour/jack_portengine.h" +#include "ardour/jack_connection.h" +#include "ardour/port_manager.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using std::string; +using std::vector; + +#define GET_PRIVATE_JACK_POINTER(localvar) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; } +#define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; } + +static uint32_t +ardour_port_flags_to_jack_flags (PortFlags flags) +{ + uint32_t jack_flags = 0; + + if (flags & IsInput) { + jack_flags |= JackPortIsInput; + } + if (flags & IsOutput) { + jack_flags |= JackPortIsOutput; + } + if (flags & IsTerminal) { + jack_flags |= JackPortIsTerminal; + } + if (flags & IsPhysical) { + jack_flags |= JackPortIsPhysical; + } + if (flags & CanMonitor) { + jack_flags |= JackPortCanMonitor; + } + + return jack_flags; +} + +static DataType +jack_port_type_to_ardour_data_type (const char* jack_type) +{ + if (strcmp (jack_type, JACK_DEFAULT_AUDIO_TYPE) == 0) { + return DataType::AUDIO; + } else if (strcmp (jack_type, JACK_DEFAULT_MIDI_TYPE) == 0) { + return DataType::MIDI; + } + return DataType::NIL; +} + +static const char* +ardour_data_type_to_jack_port_type (DataType d) +{ + switch (d) { + case DataType::AUDIO: + return JACK_DEFAULT_AUDIO_TYPE; + case DataType::MIDI: + return JACK_DEFAULT_MIDI_TYPE; + } + + return ""; +} + +JACKPortEngine::JACKPortEngine (PortManager& pm, boost::shared_ptr<JackConnection> jc) + : PortEngine (pm) + , _jack_connection (jc) +{ + _jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKPortEngine::connected_to_jack, this)); +} + +JACKPortEngine::~JACKPortEngine () +{ + /* a default destructor would do this, and so would this one, + but we'll make it explicit in case we ever need to debug + the lifetime of the JACKConnection + */ + _jack_connection.reset (); +} + +void +JACKPortEngine::connected_to_jack () +{ + /* register callbacks for stuff that is our responsibility */ + + jack_client_t* client = _jack_connection->jack(); + + if (!client) { + /* how could this happen? it could ... */ + error << _("Already disconnected from JACK before PortEngine could register callbacks") << endmsg; + return; + } + + jack_set_port_registration_callback (client, _registration_callback, this); + jack_set_port_connect_callback (client, _connect_callback, this); + jack_set_graph_order_callback (client, _graph_order_callback, this); +} + +void* +JACKPortEngine::private_handle() const +{ + return _jack_connection->jack(); +} + +int +JACKPortEngine::set_port_name (PortHandle port, const std::string& name) +{ + return jack_port_set_name ((jack_port_t*) port, name.c_str()); +} + +string +JACKPortEngine::get_port_name (PortHandle port) const +{ + return jack_port_name ((jack_port_t*) port); +} + +PortEngine::PortHandle* +JACKPortEngine:: get_port_by_name (const std::string& name) const +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return (PortHandle*) jack_port_by_name (_priv_jack, name.c_str()); +} + +void +JACKPortEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg) +{ + static_cast<JACKPortEngine*> (arg)->manager.registration_callback (); +} + +int +JACKPortEngine::_graph_order_callback (void *arg) +{ + return static_cast<JACKPortEngine*> (arg)->manager.graph_order_callback (); +} + +void +JACKPortEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg) +{ + static_cast<JACKPortEngine*> (arg)->connect_callback (id_a, id_b, conn); +} + +void +JACKPortEngine::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn) +{ + if (manager.port_remove_in_progress()) { + return; + } + + GET_PRIVATE_JACK_POINTER (_priv_jack); + + jack_port_t* a = jack_port_by_id (_priv_jack, id_a); + jack_port_t* b = jack_port_by_id (_priv_jack, id_b); + + manager.connect_callback (jack_port_name (a), jack_port_name (b), conn == 0 ? false : true); +} + +bool +JACKPortEngine::connected (PortHandle port) +{ + bool ret = false; + + const char** ports = jack_port_get_connections ((jack_port_t*) port); + + if (ports) { + ret = true; + } + + jack_free (ports); + + return ret; +} + +bool +JACKPortEngine::connected_to (PortHandle port, const std::string& other) +{ + bool ret = false; + const char** ports = jack_port_get_connections ((jack_port_t*) port); + + if (ports) { + for (int i = 0; ports[i]; ++i) { + if (other == ports[i]) { + ret = true; + } + } + jack_free (ports); + } + + return ret; +} + +bool +JACKPortEngine::physically_connected (PortHandle p) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false); + jack_port_t* port = (jack_port_t*) p; + + const char** ports = jack_port_get_connections (port); + + if (ports) { + for (int i = 0; ports[i]; ++i) { + + jack_port_t* other = jack_port_by_name (_priv_jack, ports[i]); + + if (other && (jack_port_flags (other) & JackPortIsPhysical)) { + return true; + } + } + jack_free (ports); + } + + return false; +} + +int +JACKPortEngine::get_connections (PortHandle port, vector<string>& s) +{ + const char** ports = jack_port_get_connections ((jack_port_t*) port); + + if (ports) { + for (int i = 0; ports[i]; ++i) { + s.push_back (ports[i]); + } + jack_free (ports); + } + + return s.size(); +} + +DataType +JACKPortEngine::port_data_type (PortHandle p) const +{ + return jack_port_type_to_ardour_data_type (jack_port_type ((jack_port_t*) p)); +} + +const string& +JACKPortEngine::my_name() const +{ + return _jack_connection->client_name(); +} + +bool +JACKPortEngine::port_is_physical (PortHandle ph) const +{ + if (!ph) { + return false; + } + + return jack_port_flags ((jack_port_t*) ph) & JackPortIsPhysical; +} + +int +JACKPortEngine::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s) const +{ + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack,0); + + const char** ports = jack_get_ports (_priv_jack, port_name_pattern.c_str(), + ardour_data_type_to_jack_port_type (type), + ardour_port_flags_to_jack_flags (flags)); + + if (ports == 0) { + return 0; + } + + for (uint32_t i = 0; ports[i]; ++i) { + s.push_back (ports[i]); + } + + jack_free (ports); + + return s.size(); +} + +ChanCount +JACKPortEngine::n_physical (unsigned long flags) const +{ + ChanCount c; + + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, c); + + const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags); + + if (ports) { + for (uint32_t i = 0; ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + DataType t = port_data_type (jack_port_by_name (_priv_jack, ports[i])); + if (t != DataType::NIL) { + c.set (t, c.get (t) + 1); + } + } + } + + jack_free (ports); + } + + return c; +} + +ChanCount +JACKPortEngine::n_physical_inputs () const +{ + return n_physical (JackPortIsInput); +} + +ChanCount +JACKPortEngine::n_physical_outputs () const +{ + return n_physical (JackPortIsOutput); +} + +void +JACKPortEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy) const +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + const char ** ports; + + if ((ports = jack_get_ports (_priv_jack, NULL, ardour_data_type_to_jack_port_type (type), JackPortIsPhysical | flags)) == 0) { + return; + } + + if (ports) { + for (uint32_t i = 0; ports[i]; ++i) { + if (strstr (ports[i], "Midi-Through")) { + continue; + } + phy.push_back (ports[i]); + } + jack_free (ports); + } +} + +/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to + * a physical input connector. + */ +void +JACKPortEngine::get_physical_inputs (DataType type, vector<string>& ins) +{ + get_physical (type, JackPortIsOutput, ins); +} + +/** Get physical ports for which JackPortIsInput is set; ie those that correspond to + * a physical output connector. + */ +void +JACKPortEngine::get_physical_outputs (DataType type, vector<string>& outs) +{ + get_physical (type, JackPortIsInput, outs); +} + + +bool +JACKPortEngine::can_monitor_input () const +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false); + const char ** ports; + + if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) { + return false; + } + + jack_free (ports); + + return true; +} + +int +JACKPortEngine::request_input_monitoring (PortHandle port, bool yn) +{ + return jack_port_request_monitor ((jack_port_t*) port, yn); +} +int +JACKPortEngine::ensure_input_monitoring (PortHandle port, bool yn) +{ + return jack_port_ensure_monitor ((jack_port_t*) port, yn); +} +bool +JACKPortEngine::monitoring_input (PortHandle port) +{ + return jack_port_monitoring_input ((jack_port_t*) port); +} + + +pframes_t +JACKPortEngine::sample_time_at_cycle_start () +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_last_frame_time (_priv_jack); +} + + +PortEngine::PortHandle +JACKPortEngine::register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0); + return jack_port_register (_priv_jack, shortname.c_str(), + ardour_data_type_to_jack_port_type (type), + ardour_port_flags_to_jack_flags (flags), + 0); +} + +void +JACKPortEngine::unregister_port (PortHandle port) +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + (void) jack_port_unregister (_priv_jack, (jack_port_t*) port); +} + +int +JACKPortEngine::connect (PortHandle port, const std::string& other) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_connect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str()); +} +int +JACKPortEngine::connect (const std::string& src, const std::string& dst) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_connect (_priv_jack, src.c_str(), dst.c_str()); +} + +int +JACKPortEngine::disconnect (PortHandle port, const std::string& other) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_disconnect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str()); +} + +int +JACKPortEngine::disconnect (const std::string& src, const std::string& dst) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_disconnect (_priv_jack, src.c_str(), dst.c_str()); +} + +int +JACKPortEngine::disconnect_all (PortHandle port) +{ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1); + return jack_port_disconnect (_priv_jack, (jack_port_t*) port); +} + +int +JACKPortEngine::midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index) +{ + jack_midi_event_t ev; + int ret; + + if ((ret = jack_midi_event_get (&ev, port_buffer, event_index)) == 0) { + timestamp = ev.time; + size = ev.size; + *buf = ev.buffer; + } + + return ret; +} + +int +JACKPortEngine::midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size) +{ + return jack_midi_event_write (port_buffer, timestamp, buffer, size); +} + +uint32_t +JACKPortEngine::get_midi_event_count (void* port_buffer) +{ + return jack_midi_get_event_count (port_buffer); +} + +void +JACKPortEngine::midi_clear (void* port_buffer) +{ + jack_midi_clear_buffer (port_buffer); +} + +void +JACKPortEngine::set_latency_range (PortHandle port, bool for_playback, LatencyRange r) +{ + jack_latency_range_t range; + + range.min = r.min; + range.max = r.max; + + jack_port_set_latency_range ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range); +} + +LatencyRange +JACKPortEngine::get_latency_range (PortHandle port, bool for_playback) +{ + jack_latency_range_t range; + LatencyRange ret; + + jack_port_get_latency_range ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range); + + ret.min = range.min; + ret.max = range.max; + + return ret; +} + +void* +JACKPortEngine::get_buffer (PortHandle port, pframes_t nframes) +{ + return jack_port_get_buffer ((jack_port_t*) port, nframes); +} + +uint32_t +JACKPortEngine::port_name_size() const +{ + return jack_port_name_size (); +} diff --git a/libs/ardour/jack_slave.cc b/libs/ardour/jack_slave.cc index 4c2da4c6c4..4b2f3b1860 100644 --- a/libs/ardour/jack_slave.cc +++ b/libs/ardour/jack_slave.cc @@ -20,16 +20,14 @@ #include <iostream> #include <cerrno> -#include <jack/jack.h> -#include <jack/transport.h> - +#include "ardour/audioengine.h" #include "ardour/slave.h" using namespace std; using namespace ARDOUR; -JACK_Slave::JACK_Slave (jack_client_t* j) - : jack (j) +JACK_Slave::JACK_Slave (AudioEngine& e) + : engine (e) { double x; framepos_t p; @@ -41,12 +39,6 @@ JACK_Slave::~JACK_Slave () { } -void -JACK_Slave::reset_client (jack_client_t* j) -{ - jack = j; -} - bool JACK_Slave::locked() const { @@ -62,33 +54,26 @@ JACK_Slave::ok() const bool JACK_Slave::speed_and_position (double& sp, framepos_t& position) { - jack_position_t pos; - jack_transport_state_t state; - - state = jack_transport_query (jack, &pos); - - switch (state) { - case JackTransportStopped: + switch (engine.transport_state()) { + case TransportStopped: speed = 0; _starting = false; break; - case JackTransportRolling: + case TransportRolling: speed = 1.0; _starting = false; break; - case JackTransportLooping: + case TransportLooping: speed = 1.0; _starting = false; break; - case JackTransportStarting: + case TransportStarting: _starting = true; // don't adjust speed here, just leave it as it was break; - default: - cerr << "WARNING: Unknown JACK transport state: " << state << endl; } sp = speed; - position = pos.frame; + position = engine.transport_frame(); return true; } diff --git a/libs/ardour/jack_utils.cc b/libs/ardour/jack_utils.cc new file mode 100644 index 0000000000..ea1aea2623 --- /dev/null +++ b/libs/ardour/jack_utils.cc @@ -0,0 +1,894 @@ +/* + Copyright (C) 2010 Paul Davis + Copyright (C) 2011 Tim Mayberry + + 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. + +*/ + +#ifdef WAF_BUILD +#include "libardour-config.h" +#endif + +#ifdef HAVE_ALSA +#include <alsa/asoundlib.h> +#endif + +#ifdef __APPLE__ +#include <CoreAudio/CoreAudio.h> +#include <CoreFoundation/CFString.h> +#include <sys/param.h> +#include <mach-o/dyld.h> +#endif + +#ifdef HAVE_PORTAUDIO +#include <portaudio.h> +#endif + +#include <jack/jack.h> + +#include <fstream> + +#include <boost/scoped_ptr.hpp> + +#include <glibmm/miscutils.h> + +#include "pbd/epa.h" +#include "pbd/error.h" +#include "pbd/convert.h" +#include "pbd/file_utils.h" +#include "pbd/search_path.h" + +#include "ardour/jack_utils.h" + +#ifdef __APPLE +#include <CFBundle.h> +#endif + +#include "i18n.h" + +using namespace std; +using namespace PBD; + +namespace ARDOUR { + // The pretty driver names + const char * const portaudio_driver_name = X_("Portaudio"); + const char * const coreaudio_driver_name = X_("CoreAudio"); + const char * const alsa_driver_name = X_("ALSA"); + const char * const oss_driver_name = X_("OSS"); + const char * const freebob_driver_name = X_("FreeBoB"); + const char * const ffado_driver_name = X_("FFADO"); + const char * const netjack_driver_name = X_("NetJACK"); + const char * const dummy_driver_name = X_("Dummy"); +} + +namespace { + + // The real driver names + const char * const portaudio_driver_command_line_name = X_("portaudio"); + const char * const coreaudio_driver_command_line_name = X_("coreaudio"); + const char * const alsa_driver_command_line_name = X_("alsa"); + const char * const oss_driver_command_line_name = X_("oss"); + const char * const freebob_driver_command_line_name = X_("freebob"); + const char * const ffado_driver_command_line_name = X_("firewire"); + const char * const netjack_driver_command_line_name = X_("netjack"); + const char * const dummy_driver_command_line_name = X_("dummy"); + + // should we provide more "pretty" names like above? + const char * const alsaseq_midi_driver_name = X_("seq"); + const char * const alsaraw_midi_driver_name = X_("raw"); + const char * const winmme_midi_driver_name = X_("winmme"); + const char * const coremidi_midi_driver_name = X_("coremidi"); + + // this should probably be translated + const char * const default_device_name = X_("Default"); +} + +std::string +get_none_string () +{ + return _("None"); +} + +void +ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names) +{ +#ifdef WIN32 + audio_driver_names.push_back (portaudio_driver_name); +#elif __APPLE__ + audio_driver_names.push_back (coreaudio_driver_name); +#else +#ifdef HAVE_ALSA + audio_driver_names.push_back (alsa_driver_name); +#endif + audio_driver_names.push_back (oss_driver_name); + audio_driver_names.push_back (freebob_driver_name); + audio_driver_names.push_back (ffado_driver_name); +#endif + audio_driver_names.push_back (netjack_driver_name); + audio_driver_names.push_back (dummy_driver_name); +} + +void +ARDOUR::get_jack_default_audio_driver_name (string& audio_driver_name) +{ + vector<string> drivers; + get_jack_audio_driver_names (drivers); + audio_driver_name = drivers.front (); +} + +void +ARDOUR::get_jack_midi_system_names (const string& driver, vector<string>& midi_system_names) +{ + midi_system_names.push_back (get_none_string ()); +#ifdef WIN32 + midi_system_names.push_back (winmme_midi_driver_name); +#elif __APPLE__ + midi_system_names.push_back (coreaudio_midi_driver_name); +#else +#ifdef HAVE_ALSA + if (driver == alsa_driver_name) { + midi_system_names.push_back (alsaseq_midi_driver_name); + midi_system_names.push_back (alsaraw_midi_driver_name); + } +#endif +#endif +} + +void +ARDOUR::get_jack_default_midi_system_name (const string& driver, string& midi_system_name) +{ + vector<string> drivers; + get_jack_midi_system_names (driver, drivers); + midi_system_name = drivers.front (); +} + +void +ARDOUR::get_jack_sample_rate_strings (vector<string>& samplerates) +{ + // do these really need to be translated? + samplerates.push_back (_("8000Hz")); + samplerates.push_back (_("22050Hz")); + samplerates.push_back (_("44100Hz")); + samplerates.push_back (_("48000Hz")); + samplerates.push_back (_("88200Hz")); + samplerates.push_back (_("96000Hz")); + samplerates.push_back (_("192000Hz")); +} + +string +ARDOUR::get_jack_default_sample_rate () +{ + return _("48000Hz"); +} + +void +ARDOUR::get_jack_period_size_strings (std::vector<std::string>& period_sizes) +{ + period_sizes.push_back ("32"); + period_sizes.push_back ("64"); + period_sizes.push_back ("128"); + period_sizes.push_back ("256"); + period_sizes.push_back ("512"); + period_sizes.push_back ("1024"); + period_sizes.push_back ("2048"); + period_sizes.push_back ("4096"); + period_sizes.push_back ("8192"); +} + +string +ARDOUR::get_jack_default_period_size () +{ + return "1024"; +} + +void +ARDOUR::get_jack_dither_mode_strings (const string& driver, vector<string>& dither_modes) +{ + dither_modes.push_back (get_none_string ()); + + if (driver == alsa_driver_name ) { + dither_modes.push_back (_("Triangular")); + dither_modes.push_back (_("Rectangular")); + dither_modes.push_back (_("Shaped")); + } +} + +string +ARDOUR::get_jack_default_dither_mode (const string& /*driver*/) +{ + return get_none_string (); +} + +string +ARDOUR::get_jack_latency_string (string samplerate, float periods, string period_size) +{ + uint32_t rate = atoi (samplerate); + float psize = atof (period_size); + + char buf[32]; + snprintf (buf, sizeof(buf), "%.1fmsec", (periods * psize) / (rate/1000.0)); + + return buf; +} + +bool +get_jack_command_line_audio_driver_name (const string& driver_name, string& command_line_name) +{ + using namespace ARDOUR; + if (driver_name == portaudio_driver_name) { + command_line_name = portaudio_driver_command_line_name; + return true; + } else if (driver_name == coreaudio_driver_name) { + command_line_name = coreaudio_driver_command_line_name; + return true; + } else if (driver_name == alsa_driver_name) { + command_line_name = alsa_driver_command_line_name; + return true; + } else if (driver_name == oss_driver_name) { + command_line_name = oss_driver_command_line_name; + return true; + } else if (driver_name == freebob_driver_name) { + command_line_name = freebob_driver_command_line_name; + return true; + } else if (driver_name == ffado_driver_name) { + command_line_name = ffado_driver_command_line_name; + return true; + } else if (driver_name == netjack_driver_name) { + command_line_name = netjack_driver_command_line_name; + return true; + } else if (driver_name == dummy_driver_name) { + command_line_name = dummy_driver_command_line_name; + return true; + } + return false; +} + +bool +get_jack_command_line_audio_device_name (const string& driver_name, + const string& device_name, string& command_line_device_name) +{ + using namespace ARDOUR; + device_map_t devices; + + get_jack_device_names_for_audio_driver (driver_name, devices); + + for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) { + if (i->first == device_name) { + command_line_device_name = i->second; + return true; + } + } + return false; +} + +bool +get_jack_command_line_dither_mode (const string& dither_mode, string& command_line_dither_mode) +{ + using namespace ARDOUR; + + if (dither_mode == _("Triangular")) { + command_line_dither_mode = "triangular"; + return true; + } else if (dither_mode == _("Rectangular")) { + command_line_dither_mode = "rectangular"; + return true; + } else if (dither_mode == _("Shaped")) { + command_line_dither_mode = "shaped"; + return true; + } + + return false; +} + +void +ARDOUR::get_jack_alsa_device_names (device_map_t& devices) +{ +#ifdef HAVE_ALSA + snd_ctl_t *handle; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + string devname; + int cardnum = -1; + int device = -1; + + while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) { + + devname = "hw:"; + devname += PBD::to_string (cardnum, std::dec); + + if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) { + + while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) { + + snd_pcm_info_set_device (pcminfo, device); + snd_pcm_info_set_subdevice (pcminfo, 0); + snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK); + + if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { + devname += ','; + devname += PBD::to_string (device, std::dec); + devices.insert (std::make_pair (snd_pcm_info_get_name (pcminfo), devname)); + } + } + + snd_ctl_close(handle); + } + } +#else + /* silence a compiler unused variable warning */ + (void) devices; +#endif +} + +#ifdef __APPLE__ +static OSStatus +getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UI; + OSStatus res = AudioDeviceGetProperty(id, 0, false, + kAudioDevicePropertyDeviceUID, &size, &UI); + if (res == noErr) + CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding()); + CFRelease(UI); + return res; +} +#endif + +void +ARDOUR::get_jack_coreaudio_device_names (device_map_t& devices) +{ +#ifdef __APPLE__ + // Find out how many Core Audio devices are there, if any... + // (code snippet gently "borrowed" from St?hane Letz jackdmp;) + OSStatus err; + Boolean isWritable; + UInt32 outSize = sizeof(isWritable); + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, + &outSize, &isWritable); + if (err == noErr) { + // Calculate the number of device available... + int numCoreDevices = outSize / sizeof(AudioDeviceID); + // Make space for the devices we are about to get... + AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices]; + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &outSize, (void *) coreDeviceIDs); + if (err == noErr) { + // Look for the CoreAudio device name... + char coreDeviceName[256]; + UInt32 nameSize; + + for (int i = 0; i < numCoreDevices; i++) { + + nameSize = sizeof (coreDeviceName); + + /* enforce duplex devices only */ + + err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyStreams, + &outSize, &isWritable); + + if (err != noErr || outSize == 0) { + continue; + } + + err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i], + 0, false, kAudioDevicePropertyStreams, + &outSize, &isWritable); + + if (err != noErr || outSize == 0) { + continue; + } + + err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &outSize, &isWritable); + if (err == noErr) { + err = AudioDeviceGetProperty(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &nameSize, (void *) coreDeviceName); + if (err == noErr) { + char drivername[128]; + + // this returns the unique id for the device + // that must be used on the commandline for jack + + if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) { + devices.insert (make_pair (coreDeviceName, drivername)); + } + } + } + } + } + delete [] coreDeviceIDs; + } +#else + /* silence a compiler unused variable warning */ + (void) devices; +#endif +} + +void +ARDOUR::get_jack_portaudio_device_names (device_map_t& devices) +{ +#ifdef HAVE_PORTAUDIO + if (Pa_Initialize() != paNoError) { + return; + } + + for (PaDeviceIndex i = 0; i < Pa_GetDeviceCount (); ++i) { + string api_name; + string readable_name; + string jack_device_name; + const PaDeviceInfo* device_info = Pa_GetDeviceInfo(i); + + if (device_info != NULL) { // it should never be ? + api_name = Pa_GetHostApiInfo (device_info->hostApi)->name; + readable_name = api_name + " " + device_info->name; + jack_device_name = api_name + "::" + device_info->name; + devices.insert (make_pair (readable_name, jack_device_name)); + } + } + Pa_Terminate(); +#else + /* silence a compiler unused variable warning */ + (void) devices; +#endif +} + +void +ARDOUR::get_jack_oss_device_names (device_map_t& devices) +{ + devices.insert (make_pair (default_device_name, default_device_name)); +} + +void +ARDOUR::get_jack_freebob_device_names (device_map_t& devices) +{ + devices.insert (make_pair (default_device_name, default_device_name)); +} + +void +ARDOUR::get_jack_ffado_device_names (device_map_t& devices) +{ + devices.insert (make_pair (default_device_name, default_device_name)); +} + +void +ARDOUR::get_jack_netjack_device_names (device_map_t& devices) +{ + devices.insert (make_pair (default_device_name, default_device_name)); +} + +void +ARDOUR::get_jack_dummy_device_names (device_map_t& devices) +{ + devices.insert (make_pair (default_device_name, default_device_name)); +} + +bool +ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name, device_map_t& devices) +{ + devices.clear(); + + if (driver_name == portaudio_driver_name) { + get_jack_portaudio_device_names (devices); + } else if (driver_name == coreaudio_driver_name) { + get_jack_coreaudio_device_names (devices); + } else if (driver_name == alsa_driver_name) { + get_jack_alsa_device_names (devices); + } else if (driver_name == oss_driver_name) { + get_jack_oss_device_names (devices); + } else if (driver_name == freebob_driver_name) { + get_jack_freebob_device_names (devices); + } else if (driver_name == ffado_driver_name) { + get_jack_ffado_device_names (devices); + } else if (driver_name == netjack_driver_name) { + get_jack_netjack_device_names (devices); + } else if (driver_name == dummy_driver_name) { + get_jack_dummy_device_names (devices); + } + + return !devices.empty(); +} + + +std::vector<std::string> +ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name) +{ + std::vector<std::string> readable_names; + device_map_t devices; + + get_jack_device_names_for_audio_driver (driver_name, devices); + + for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) { + readable_names.push_back (i->first); + } + + return readable_names; +} + +bool +ARDOUR::get_jack_audio_driver_supports_two_devices (const string& driver) +{ + return (driver == alsa_driver_name || driver == oss_driver_name); +} + +bool +ARDOUR::get_jack_audio_driver_supports_latency_adjustment (const string& driver) +{ + return (driver == alsa_driver_name || driver == coreaudio_driver_name || + driver == ffado_driver_name || driver == portaudio_driver_name); +} + +bool +ARDOUR::get_jack_audio_driver_supports_setting_period_count (const string& driver) +{ + return !(driver == dummy_driver_name || driver == coreaudio_driver_name || + driver == portaudio_driver_name); +} + +bool +ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names) +{ +#ifdef WIN32 + server_names.push_back ("jackd.exe"); +#else + server_names.push_back ("jackd"); + server_names.push_back ("jackdmp"); +#endif + return !server_names.empty(); +} + +void +ARDOUR::set_path_env_for_jack_autostart (const vector<std::string>& dirs) +{ +#ifdef __APPLE__ + // push it back into the environment so that auto-started JACK can find it. + // XXX why can't we just expect OS X users to have PATH set correctly? we can't ... + setenv ("PATH", SearchPath(dirs).to_string(), 1); +#else + /* silence a compiler unused variable warning */ + (void) dirs; +#endif +} + +bool +ARDOUR::get_jack_server_dir_paths (vector<std::string>& server_dir_paths) +{ +#ifdef __APPLE__ + /* this magic lets us finds the path to the OSX bundle, and then + we infer JACK's location from there + */ + + char execpath[MAXPATHLEN+1]; + uint32_t pathsz = sizeof (execpath); + + _NSGetExecutablePath (execpath, &pathsz); + + server_dir_paths.push_back (Glib::path_get_dirname (execpath)); +#endif + + SearchPath sp(string(g_getenv("PATH"))); + +#ifdef WIN32 + gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL); + if (install_dir) { + sp.push_back (install_dir); + g_free (install_dir); + } + // don't try and use a system wide JACK install yet. +#else + if (sp.empty()) { + sp.push_back ("/usr/bin"); + sp.push_back ("/bin"); + sp.push_back ("/usr/local/bin"); + sp.push_back ("/opt/local/bin"); + } +#endif + + std::copy (sp.begin(), sp.end(), std::back_inserter(server_dir_paths)); + + return !server_dir_paths.empty(); +} + +bool +ARDOUR::get_jack_server_paths (const vector<std::string>& server_dir_paths, + const vector<string>& server_names, + vector<std::string>& server_paths) +{ + for (vector<string>::const_iterator i = server_names.begin(); i != server_names.end(); ++i) { + find_matching_files_in_directories (server_dir_paths, Glib::PatternSpec(*i), server_paths); + } + return !server_paths.empty(); +} + +bool +ARDOUR::get_jack_server_paths (vector<std::string>& server_paths) +{ + vector<std::string> server_dirs; + + if (!get_jack_server_dir_paths (server_dirs)) { + return false; + } + + vector<string> server_names; + + if (!get_jack_server_application_names (server_names)) { + return false; + } + + if (!get_jack_server_paths (server_dirs, server_names, server_paths)) { + return false; + } + + return !server_paths.empty(); +} + +bool +ARDOUR::get_jack_default_server_path (std::string& server_path) +{ + vector<std::string> server_paths; + + if (!get_jack_server_paths (server_paths)) { + return false; + } + + server_path = server_paths.front (); + return true; +} + +string +quote_string (const string& str) +{ + return "\"" + str + "\""; +} + +ARDOUR::JackCommandLineOptions::JackCommandLineOptions () + : server_path () + , timeout(0) + , no_mlock(false) + , ports_max(128) + , realtime(true) + , priority(0) + , unlock_gui_libs(false) + , verbose(false) + , temporary(true) + , driver() + , input_device() + , output_device() + , num_periods(2) + , period_size(1024) + , samplerate(48000) + , input_latency(0) + , output_latency(0) + , hardware_metering(false) + , hardware_monitoring(false) + , dither_mode() + , force16_bit(false) + , soft_mode(false) + , midi_driver() +{ + +} + +bool +ARDOUR::get_jack_command_line_string (const JackCommandLineOptions& options, string& command_line) +{ + vector<string> args; + + args.push_back (options.server_path); + +#ifdef WIN32 + // must use sync mode on windows + args.push_back ("-S"); + + // this needs to be added now on windows + if (!options.midi_driver.empty () && options.midi_driver != get_none_string ()) { + args.push_back ("-X"); + args.push_back (options.midi_driver); + } +#endif + + if (options.timeout) { + args.push_back ("-t"); + args.push_back (to_string (options.timeout, std::dec)); + } + + if (options.no_mlock) { + args.push_back ("-m"); + } + + args.push_back ("-p"); + args.push_back (to_string(options.ports_max, std::dec)); + + if (options.realtime) { + args.push_back ("-R"); + if (options.priority != 0) { + args.push_back ("-P"); + args.push_back (to_string(options.priority, std::dec)); + } + } else { + args.push_back ("-r"); + } + + if (options.unlock_gui_libs) { + args.push_back ("-u"); + } + + if (options.verbose) { + args.push_back ("-v"); + } + +#ifndef WIN32 + if (options.temporary) { + args.push_back ("-T"); + } +#endif + + string command_line_driver_name; + + if (!get_jack_command_line_audio_driver_name (options.driver, command_line_driver_name)) { + return false; + } + + args.push_back ("-d"); + args.push_back (command_line_driver_name); + + if (options.output_device.empty() && options.input_device.empty()) { + return false; + } + + string command_line_input_device_name; + string command_line_output_device_name; + + if (!get_jack_command_line_audio_device_name (options.driver, + options.input_device, command_line_input_device_name)) { + return false; + } + + if (!get_jack_command_line_audio_device_name (options.driver, + options.output_device, command_line_output_device_name)) { + return false; + } + + if (options.input_device.empty()) { + // playback only + if (options.output_device.empty()) { + return false; + } + args.push_back ("-P"); + } else if (options.output_device.empty()) { + // capture only + if (options.input_device.empty()) { + return false; + } + args.push_back ("-C"); + } else if (options.input_device != options.output_device) { + // capture and playback on two devices if supported + if (get_jack_audio_driver_supports_two_devices (options.driver)) { + args.push_back ("-C"); + args.push_back (command_line_input_device_name); + args.push_back ("-P"); + args.push_back (command_line_output_device_name); + } else { + return false; + } + } + + if (get_jack_audio_driver_supports_setting_period_count (options.driver)) { + args.push_back ("-n"); + args.push_back (to_string (options.num_periods, std::dec)); + } + + args.push_back ("-r"); + args.push_back (to_string (options.samplerate, std::dec)); + + args.push_back ("-p"); + args.push_back (to_string (options.period_size, std::dec)); + + if (get_jack_audio_driver_supports_latency_adjustment (options.driver)) { + if (options.input_latency) { + args.push_back ("-I"); + args.push_back (to_string (options.input_latency, std::dec)); + } + if (options.output_latency) { + args.push_back ("-0"); + args.push_back (to_string (options.output_latency, std::dec)); + } + } + + if (options.input_device == options.output_device && options.input_device != default_device_name) { + args.push_back ("-d"); + args.push_back (command_line_input_device_name); + } + + if (options.driver == alsa_driver_name) { + if (options.hardware_metering) { + args.push_back ("-M"); + } + if (options.hardware_monitoring) { + args.push_back ("-H"); + } + + string command_line_dither_mode; + if (get_jack_command_line_dither_mode (options.dither_mode, command_line_dither_mode)) { + args.push_back ("-z"); + args.push_back (command_line_dither_mode); + } + if (options.force16_bit) { + args.push_back ("-S"); + } + if (options.soft_mode) { + args.push_back ("-s"); + } + + if (!options.midi_driver.empty() && options.midi_driver != get_none_string ()) { + args.push_back ("-X"); + args.push_back (options.midi_driver); + } + } + + ostringstream oss; + + for (vector<string>::const_iterator i = args.begin(); i != args.end();) { +#ifdef WIN32 + oss << quote_string (*i); +#else + oss << *i; +#endif + if (++i != args.end()) oss << ' '; + } + + command_line = oss.str(); + return true; +} + +string +ARDOUR::get_jack_server_config_file_name () +{ + return ".jackdrc"; +} + +std::string +ARDOUR::get_jack_server_user_config_dir_path () +{ + return Glib::get_home_dir (); +} + +std::string +ARDOUR::get_jack_server_user_config_file_path () +{ + return Glib::build_filename (get_jack_server_user_config_dir_path (), get_jack_server_config_file_name ()); +} + +bool +ARDOUR::write_jack_config_file (const std::string& config_file_path, const string& command_line) +{ + ofstream jackdrc (config_file_path.c_str()); + + if (!jackdrc) { + error << string_compose (_("cannot open JACK rc file %1 to store parameters"), config_file_path) << endmsg; + return false; + } + + jackdrc << command_line << endl; + jackdrc.close (); + return true; +} diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc index 8d08fd6bb5..1085f44f1c 100644 --- a/libs/ardour/ltc_slave.cc +++ b/libs/ardour/ltc_slave.cc @@ -134,7 +134,7 @@ LTC_Slave::resync_latency() if (!session.deletion_in_progress() && session.ltc_output_io()) { /* check if Port exits */ boost::shared_ptr<Port> ltcport = session.ltc_input_port(); - ltcport->get_connected_latency_range(ltc_slave_latency, false); + ltcport->get_connected_latency_range (ltc_slave_latency, false); } } @@ -420,15 +420,15 @@ bool LTC_Slave::speed_and_position (double& speed, framepos_t& pos) { bool engine_init_called = false; - framepos_t now = session.engine().frame_time_at_cycle_start(); + framepos_t now = session.engine().sample_time_at_cycle_start(); framepos_t sess_pos = session.transport_frame(); // corresponds to now - framecnt_t nframes = session.engine().frames_per_cycle(); + framecnt_t nframes = session.engine().samples_per_cycle(); - jack_default_audio_sample_t *in; + Sample* in; boost::shared_ptr<Port> ltcport = session.ltc_input_port(); - in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltcport->jack_port(), nframes); + in = (Sample*) AudioEngine::instance()->port_engine().get_buffer (ltcport->port_handle(), nframes); frameoffset_t skip = now - (monotonic_cnt + nframes); monotonic_cnt = now; @@ -441,7 +441,7 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) else if (engine_dll_initstate != transport_direction && ltc_speed != 0) { engine_dll_initstate = transport_direction; init_engine_dll(last_ltc_frame + rint(ltc_speed * double(2 * nframes + now - last_timestamp)), - session.engine().frames_per_cycle()); + session.engine().samples_per_cycle()); engine_init_called = true; } @@ -521,8 +521,8 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) t0 = t1; t1 += b * e + e2; e2 += c * e; - speed_flt = (t1 - t0) / double(session.engine().frames_per_cycle()); - DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().frames_per_cycle() )); + speed_flt = (t1 - t0) / double(session.engine().samples_per_cycle()); + DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().samples_per_cycle() )); } else { DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC adjusting elapsed (no DLL) from %1 by %2\n", elapsed, (2 * nframes * ltc_speed))); speed_flt = 0; diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index bfa593aea3..95a29b7c8f 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -289,7 +289,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _latency_control_port = 0; _next_cycle_start = std::numeric_limits<framepos_t>::max(); _next_cycle_speed = 1.0; - _block_length = _engine.frames_per_cycle(); + _block_length = _engine.samples_per_cycle(); _seq_size = _engine.raw_buffer_size(DataType::MIDI); _state_version = 0; _was_activated = false; @@ -1901,7 +1901,7 @@ LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** buf return port; } -static bool lv2_filter (const string& str, void *arg) +static bool lv2_filter (const string& str, void* /*arg*/) { /* Not a dotfile, has a prefix before a period, suffix is "lv2" */ diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index b47c3ca152..0b0e61000d 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -190,55 +190,6 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data) return true; } - -/** Push an event into the buffer. - * - * Note that the raw MIDI pointed to by ev will be COPIED and unmodified. - * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM). - * Realtime safe. - * @return false if operation failed (not enough room) - */ -bool -MidiBuffer::push_back(const jack_midi_event_t& ev) -{ - const size_t stamp_size = sizeof(TimeType); - - if (_size + stamp_size + ev.size >= _capacity) { - cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; - return false; - } - - if (!Evoral::midi_event_is_valid(ev.buffer, ev.size)) { - cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl; - return false; - } - -#ifndef NDEBUG - if (DEBUG::MidiIO & PBD::debug_bits) { - DEBUG_STR_DECL(a); - DEBUG_STR_APPEND(a, string_compose ("midibuffer %1 push jack event @ %2 sz %3 ", this, ev.time, ev.size)); - for (size_t i=0; i < ev.size; ++i) { - DEBUG_STR_APPEND(a,hex); - DEBUG_STR_APPEND(a,"0x"); - DEBUG_STR_APPEND(a,(int)ev.buffer[i]); - DEBUG_STR_APPEND(a,' '); - } - DEBUG_STR_APPEND(a,'\n'); - DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str()); - } -#endif - - uint8_t* const write_loc = _data + _size; - *((TimeType*)write_loc) = ev.time; - memcpy(write_loc + stamp_size, ev.buffer, ev.size); - - _size += stamp_size + ev.size; - _silent = false; - - return true; -} - - /** Reserve space for a new event in the buffer. * * This call is for copying MIDI directly into the buffer, the data location diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 489a84e477..e67ce9b831 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -1111,7 +1111,7 @@ MidiDiskstream::prep_record_enable () boost::shared_ptr<MidiPort> sp = _source_port.lock (); if (sp && Config->get_monitoring_model() == HardwareMonitoring) { - sp->request_jack_monitors_input (!(_session.config.get_auto_input() && rolling)); + sp->request_input_monitoring (!(_session.config.get_auto_input() && rolling)); } return true; @@ -1264,7 +1264,7 @@ MidiDiskstream::ensure_jack_monitors_input (bool yn) boost::shared_ptr<MidiPort> sp = _source_port.lock (); if (sp) { - sp->ensure_jack_monitors_input (yn); + sp->ensure_input_monitoring (yn); } } diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 72150f8cfc..9191a57080 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -26,7 +26,9 @@ using namespace ARDOUR; using namespace std; -MidiPort::MidiPort (const std::string& name, Flags flags) +#define port_engine AudioEngine::instance()->port_engine() + +MidiPort::MidiPort (const std::string& name, PortFlags flags) : Port (name, DataType::MIDI, flags) , _has_been_mixed_down (false) , _resolve_required (false) @@ -48,7 +50,7 @@ MidiPort::cycle_start (pframes_t nframes) _buffer->clear (); if (sends_output ()) { - jack_midi_clear_buffer (jack_port_get_buffer (_jack_port, nframes)); + port_engine.midi_clear (port_engine.get_buffer (_port_handle, nframes)); } } @@ -63,31 +65,33 @@ MidiPort::get_midi_buffer (pframes_t nframes) if (_input_active) { - void* jack_buffer = jack_port_get_buffer (_jack_port, nframes); - const pframes_t event_count = jack_midi_get_event_count (jack_buffer); + void* buffer = port_engine.get_buffer (_port_handle, nframes); + const pframes_t event_count = port_engine.get_midi_event_count (buffer); - /* suck all relevant MIDI events from the JACK MIDI port buffer + /* suck all relevant MIDI events from the MIDI port buffer into our MidiBuffer */ for (pframes_t i = 0; i < event_count; ++i) { - jack_midi_event_t ev; + pframes_t timestamp; + size_t size; + uint8_t* buf; - jack_midi_event_get (&ev, jack_buffer, i); + port_engine.midi_event_get (timestamp, size, &buf, buffer, i); - if (ev.buffer[0] == 0xfe) { + if (buf[0] == 0xfe) { /* throw away active sensing */ continue; } /* check that the event is in the acceptable time range */ - if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) && - (ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) { - _buffer->push_back (ev); + if ((timestamp >= (_global_port_buffer_offset + _port_buffer_offset)) && + (timestamp < (_global_port_buffer_offset + _port_buffer_offset + nframes))) { + _buffer->push_back (timestamp, size, buf); } else { - cerr << "Dropping incoming MIDI at time " << ev.time << "; offset=" + cerr << "Dropping incoming MIDI at time " << timestamp << "; offset=" << _global_port_buffer_offset << " limit=" << (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n"; } @@ -121,7 +125,7 @@ MidiPort::cycle_split () } void -MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when) +MidiPort::resolve_notes (void* port_buffer, MidiBuffer::TimeType when) { for (uint8_t channel = 0; channel <= 0xF; channel++) { @@ -132,13 +136,13 @@ MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when) * that prioritize sustain over AllNotesOff */ - if (jack_midi_event_write (jack_buffer, when, ev, 3) != 0) { + if (port_engine.midi_event_put (port_buffer, when, ev, 3) != 0) { cerr << "failed to deliver sustain-zero on channel " << channel << " on port " << name() << endl; } ev[1] = MIDI_CTL_ALL_NOTES_OFF; - if (jack_midi_event_write (jack_buffer, 0, ev, 3) != 0) { + if (port_engine.midi_event_put (port_buffer, 0, ev, 3) != 0) { cerr << "failed to deliver ALL NOTES OFF on channel " << channel << " on port " << name() << endl; } } @@ -149,11 +153,11 @@ MidiPort::flush_buffers (pframes_t nframes) { if (sends_output ()) { - void* jack_buffer = jack_port_get_buffer (_jack_port, nframes); - + void* port_buffer = port_engine.get_buffer (_port_handle, nframes); + if (_resolve_required) { /* resolve all notes at the start of the buffer */ - resolve_notes (jack_buffer, 0); + resolve_notes (port_buffer, 0); _resolve_required = false; } @@ -166,7 +170,7 @@ MidiPort::flush_buffers (pframes_t nframes) assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset)); if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) { - if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) { + if (port_engine.midi_event_put (port_buffer, (pframes_t) ev.time(), ev.buffer(), ev.size()) != 0) { cerr << "write failed, drop flushed note off on the floor, time " << ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl; } diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc index 78da32e427..0d9ac17601 100644 --- a/libs/ardour/midi_ui.cc +++ b/libs/ardour/midi_ui.cc @@ -107,7 +107,7 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port) CrossThreadChannel::drain (port->selectable()); DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name())); - framepos_t now = _session.engine().frame_time(); + framepos_t now = _session.engine().sample_time(); port->parse (now); } diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index 8ce0722d8b..0f2761c350 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -579,7 +579,7 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc) bool MTC_Slave::speed_and_position (double& speed, framepos_t& pos) { - framepos_t now = session.engine().frame_time_at_cycle_start(); + framepos_t now = session.engine().sample_time_at_cycle_start(); framepos_t sess_pos = session.transport_frame(); // corresponds to now //sess_pos -= session.engine().frames_since_cycle_start(); @@ -593,7 +593,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) if (last.timestamp == 0) { engine_dll_initstate = 0; } else if (engine_dll_initstate != transport_direction && last.speed != 0) { engine_dll_initstate = transport_direction; - init_engine_dll(last.position, session.engine().frames_per_cycle()); + init_engine_dll(last.position, session.engine().samples_per_cycle()); engine_dll_reinitialized = true; } @@ -643,8 +643,8 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) te0 = te1; te1 += be * e + ee2; ee2 += ce * e; - speed_flt = (te1 - te0) / double(session.engine().frames_per_cycle()); - DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().frames_per_cycle() )); + speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle()); + DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().samples_per_cycle() )); } } diff --git a/libs/ardour/panner_manager.cc b/libs/ardour/panner_manager.cc index c3601d41de..94e8d4ff2f 100644 --- a/libs/ardour/panner_manager.cc +++ b/libs/ardour/panner_manager.cc @@ -79,6 +79,7 @@ PannerManager::discover_panners () panner_discover (*i); } } + int PannerManager::panner_discover (string path) { diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 3473b73617..571d227711 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -30,6 +30,7 @@ #include "ardour/audioengine.h" #include "ardour/debug.h" #include "ardour/port.h" +#include "ardour/port_engine.h" #include "i18n.h" @@ -40,13 +41,18 @@ using namespace PBD; PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect; PBD::Signal0<void> Port::PortDrop; -AudioEngine* Port::_engine = 0; bool Port::_connecting_blocked = false; pframes_t Port::_global_port_buffer_offset = 0; pframes_t Port::_cycle_nframes = 0; +/* a handy define to shorten what would otherwise be a needlessly verbose + * repeated phrase + */ +#define port_engine AudioEngine::instance()->port_engine() +#define port_manager AudioEngine::instance() + /** @param n Port short name */ -Port::Port (std::string const & n, DataType t, Flags f) +Port::Port (std::string const & n, DataType t, PortFlags f) : _port_buffer_offset (0) , _name (n) , _flags (f) @@ -58,21 +64,17 @@ Port::Port (std::string const & n, DataType t, Flags f) _private_capture_latency.max = 0; /* Unfortunately we have to pass the DataType into this constructor so that - we can create the right kind of JACK port; aside from this we'll use the + we can create the right kind of port; aside from this we'll use the virtual function type () to establish type. */ assert (_name.find_first_of (':') == std::string::npos); - if (!_engine->connected()) { - throw failed_constructor (); - } - - if ((_jack_port = jack_port_register (_engine->jack (), _name.c_str (), t.to_jack_type (), _flags, 0)) == 0) { - cerr << "Failed to register JACK port \"" << _name << "\", reason is unknown from here\n"; + if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) { + cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n"; throw failed_constructor (); } - + PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this)); } @@ -85,11 +87,9 @@ Port::~Port () void Port::drop () { - if (_jack_port) { - if (_engine->jack ()) { - jack_port_unregister (_engine->jack (), _jack_port); - } - _jack_port = 0; + if (_port_handle) { + port_engine.unregister_port (_port_handle); + _port_handle = 0; } } @@ -97,18 +97,18 @@ Port::drop () bool Port::connected () const { - return (jack_port_connected (_jack_port) != 0); + return (port_engine.connected (_port_handle) != 0); } int Port::disconnect_all () { - jack_port_disconnect (_engine->jack(), _jack_port); + port_engine.disconnect_all (_port_handle); _connections.clear (); /* a cheaper, less hacky way to do boost::shared_from_this() ... */ - boost::shared_ptr<Port> pself = _engine->get_port_by_name (name()); + boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name()); PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal return 0; @@ -120,48 +120,20 @@ Port::disconnect_all () bool Port::connected_to (std::string const & o) const { - if (!_engine->connected()) { - /* in some senses, this answer isn't the right one all the time, - because we know about our connections and will re-establish - them when we reconnect to JACK. - */ - return false; - } - - return jack_port_connected_to (_jack_port, - _engine->make_port_name_non_relative(o).c_str ()); + return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o)); } -/** @param o Filled in with port full names of ports that we are connected to */ int Port::get_connections (std::vector<std::string> & c) const { - int n = 0; - - if (_engine->connected()) { - const char** jc = jack_port_get_connections (_jack_port); - if (jc) { - for (int i = 0; jc[i]; ++i) { - c.push_back (jc[i]); - ++n; - } - - if (jack_free) { - jack_free (jc); - } else { - free (jc); - } - } - } - - return n; + return port_engine.get_connections (_port_handle, c); } int Port::connect (std::string const & other) { - std::string const other_shrt = _engine->make_port_name_non_relative (other); - std::string const this_shrt = _engine->make_port_name_non_relative (_name); + std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other); + std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name); int r = 0; @@ -170,9 +142,9 @@ Port::connect (std::string const & other) } if (sends_output ()) { - r = jack_connect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ()); + port_engine.connect (our_name, other_name); } else { - r = jack_connect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str()); + port_engine.connect (other_name, our_name); } if (r == 0) { @@ -185,15 +157,15 @@ Port::connect (std::string const & other) int Port::disconnect (std::string const & other) { - std::string const other_fullname = _engine->make_port_name_non_relative (other); - std::string const this_fullname = _engine->make_port_name_non_relative (_name); + std::string const other_fullname = port_manager->make_port_name_non_relative (other); + std::string const this_fullname = port_manager->make_port_name_non_relative (_name); int r = 0; if (sends_output ()) { - r = jack_disconnect (_engine->jack (), this_fullname.c_str (), other_fullname.c_str ()); + r = port_engine.disconnect (this_fullname, other_fullname); } else { - r = jack_disconnect (_engine->jack (), other_fullname.c_str (), this_fullname.c_str ()); + r = port_engine.disconnect (other_fullname, this_fullname); } if (r == 0) { @@ -202,8 +174,8 @@ Port::disconnect (std::string const & other) /* a cheaper, less hacky way to do boost::shared_from_this() ... */ - boost::shared_ptr<Port> pself = _engine->get_port_by_name (name()); - boost::shared_ptr<Port> pother = _engine->get_port_by_name (other); + boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name()); + boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other); if (pself && pother) { /* Disconnecting from another Ardour port: need to allow @@ -236,21 +208,22 @@ Port::disconnect (Port* o) } void -Port::set_engine (AudioEngine* e) +Port::request_input_monitoring (bool yn) { - _engine = e; + port_engine.request_input_monitoring (_port_handle, yn); } void -Port::ensure_jack_monitors_input (bool yn) +Port::ensure_input_monitoring (bool yn) { - jack_port_ensure_monitor (_jack_port, yn); + port_engine.ensure_input_monitoring (_port_handle, yn); } bool -Port::jack_monitoring_input () const +Port::monitoring_input () const { - return jack_port_monitoring_input (_jack_port); + + return port_engine.monitoring_input (_port_handle); } void @@ -272,28 +245,23 @@ Port::increment_port_buffer_offset (pframes_t nframes) } void -Port::set_public_latency_range (jack_latency_range_t& range, bool playback) const +Port::set_public_latency_range (LatencyRange& range, bool playback) const { - /* this sets the visible latency that the rest of JACK sees. because we do latency - compensation, all (most) of our visible port latency values are identical. + /* this sets the visible latency that the rest of the port system + sees. because we do latency compensation, all (most) of our visible + port latency values are identical. */ - if (!jack_port_set_latency_range) { - return; - } - DEBUG_TRACE (DEBUG::Latency, string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n", name(), range.min, range.max, (playback ? "PLAYBACK" : "CAPTURE")));; - jack_port_set_latency_range (_jack_port, - (playback ? JackPlaybackLatency : JackCaptureLatency), - &range); + port_engine.set_latency_range (_port_handle, playback, range); } void -Port::set_private_latency_range (jack_latency_range_t& range, bool playback) +Port::set_private_latency_range (LatencyRange& range, bool playback) { if (playback) { _private_playback_latency = range; @@ -311,12 +279,12 @@ Port::set_private_latency_range (jack_latency_range_t& range, bool playback) _private_capture_latency.max)); } - /* push to public (JACK) location so that everyone else can see it */ + /* push to public (port system) location so that everyone else can see it */ set_public_latency_range (range, playback); } -const jack_latency_range_t& +const LatencyRange& Port::private_latency_range (bool playback) const { if (playback) { @@ -336,14 +304,13 @@ Port::private_latency_range (bool playback) const } } -jack_latency_range_t +LatencyRange Port::public_latency_range (bool /*playback*/) const { - jack_latency_range_t r; + LatencyRange r; + + r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false); - jack_port_get_latency_range (_jack_port, - sends_output() ? JackPlaybackLatency : JackCaptureLatency, - &r); DEBUG_TRACE (DEBUG::Latency, string_compose ( "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n", name(), r.min, r.max, @@ -352,21 +319,9 @@ Port::public_latency_range (bool /*playback*/) const } void -Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const +Port::get_connected_latency_range (LatencyRange& range, bool playback) const { - if (!jack_port_get_latency_range) { - return; - } - vector<string> connections; - jack_client_t* jack = _engine->jack(); - - if (!jack) { - range.min = 0; - range.max = 0; - PBD::warning << _("get_connected_latency_range() called while disconnected from JACK") << endmsg; - return; - } get_connections (connections); @@ -380,21 +335,18 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c for (vector<string>::const_iterator c = connections.begin(); c != connections.end(); ++c) { - jack_latency_range_t lr; + LatencyRange lr; if (!AudioEngine::instance()->port_is_mine (*c)) { - /* port belongs to some other JACK client, use - * JACK to lookup its latency information. + /* port belongs to some other port-system client, use + * the port engine to lookup its latency information. */ - jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str()); + PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c); if (remote_port) { - jack_port_get_latency_range ( - remote_port, - (playback ? JackPlaybackLatency : JackCaptureLatency), - &lr); + lr = port_engine.get_latency_range (remote_port, playback); DEBUG_TRACE (DEBUG::Latency, string_compose ( "\t%1 <-> %2 : latter has latency range %3 .. %4\n", @@ -438,15 +390,9 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c int Port::reestablish () { - jack_client_t* jack = _engine->jack(); - - if (!jack) { - return -1; - } - - _jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0); + _port_handle = port_engine.register_port (_name, type(), _flags); - if (_jack_port == 0) { + if (_port_handle == 0) { PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg; return -1; } @@ -471,7 +417,7 @@ Port::reconnect () return 0; } -/** @param n Short port name (no JACK client name) */ +/** @param n Short port name (no port-system client name) */ int Port::set_name (std::string const & n) { @@ -479,10 +425,10 @@ Port::set_name (std::string const & n) return 0; } - int const r = jack_port_set_name (_jack_port, n.c_str()); + int const r = port_engine.set_port_name (_port_handle, n); if (r == 0) { - _engine->port_renamed (_name, n); + AudioEngine::instance()->port_renamed (_name, n); _name = n; } @@ -490,38 +436,9 @@ 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 { - const char** jc = jack_port_get_connections (_jack_port); - - if (jc) { - for (int i = 0; jc[i]; ++i) { - - jack_port_t* port = jack_port_by_name (_engine->jack(), jc[i]); - - if (port && (jack_port_flags (port) & JackPortIsPhysical)) { - if (jack_free) { - jack_free (jc); - } else { - free (jc); - } - return true; - } - } - if (jack_free) { - jack_free (jc); - } else { - free (jc); - } - } - - return false; + return port_engine.physically_connected (_port_handle); } diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc index 411d8d1e19..d64920b1e2 100644 --- a/libs/ardour/port_insert.cc +++ b/libs/ardour/port_insert.cc @@ -72,7 +72,7 @@ PortInsert::start_latency_detection () void PortInsert::stop_latency_detection () { - _latency_flush_frames = signal_latency() + _session.engine().frames_per_cycle(); + _latency_flush_frames = signal_latency() + _session.engine().samples_per_cycle(); _latency_detect = false; } @@ -93,7 +93,7 @@ PortInsert::latency() const */ if (_measured_latency == 0) { - return _session.engine().frames_per_cycle() + _input->latency(); + return _session.engine().samples_per_cycle() + _input->latency(); } else { return _measured_latency; } @@ -240,7 +240,7 @@ PortInsert::signal_latency() const */ if (_measured_latency == 0) { - return _session.engine().frames_per_cycle() + _input->signal_latency(); + return _session.engine().samples_per_cycle() + _input->signal_latency(); } else { return _measured_latency; } diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc new file mode 100644 index 0000000000..5c807a6979 --- /dev/null +++ b/libs/ardour/port_manager.cc @@ -0,0 +1,545 @@ +/* + 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. + +*/ + +#include "pbd/error.h" + +#include "midi++/manager.h" + +#include "ardour/port_manager.h" +#include "ardour/audio_port.h" +#include "ardour/midi_port.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using std::string; +using std::vector; + +PortManager::PortManager () + : ports (new Ports) + , _port_remove_in_progress (false) +{ +} + +void +PortManager::remove_all_ports () +{ + /* make sure that JACK callbacks that will be invoked as we cleanup + * ports know that they have nothing to do. + */ + + _port_remove_in_progress = true; + + /* process lock MUST be held by caller + */ + + { + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + ps->clear (); + } + + /* clear dead wood list in RCU */ + + ports.flush (); + + _port_remove_in_progress = false; +} + + +string +PortManager::make_port_name_relative (const string& portname) const +{ + if (!_impl) { + return portname; + } + + string::size_type len; + string::size_type n; + string self = _impl->my_name(); + + len = portname.length(); + + for (n = 0; n < len; ++n) { + if (portname[n] == ':') { + break; + } + } + + if ((n != len) && (portname.substr (0, n) == self)) { + return portname.substr (n+1); + } + + return portname; +} + +string +PortManager::make_port_name_non_relative (const string& portname) const +{ + string str; + + if (portname.find_first_of (':') != string::npos) { + return portname; + } + + str = _impl->my_name(); + str += ':'; + str += portname; + + return str; +} + +bool +PortManager::port_is_mine (const string& portname) const +{ + if (!_impl) { + return true; + } + + string self = _impl->my_name(); + + if (portname.find_first_of (':') != string::npos) { + if (portname.substr (0, self.length ()) != self) { + return false; + } + } + + return true; +} + +bool +PortManager::port_is_physical (const std::string& portname) const +{ + if (!_impl) { + return false; + } + + PortEngine::PortHandle ph = _impl->get_port_by_name (portname); + if (!ph) { + return false; + } + + return _impl->port_is_physical (ph); +} + +void +PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s) +{ + if (!_impl) { + return; + } + _impl->get_physical_outputs (type, s); +} + +void +PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s) +{ + if (!_impl) { + return; + } + + _impl->get_physical_inputs (type, s); +} + +ChanCount +PortManager::n_physical_outputs () const +{ + if (!_impl) { + return ChanCount::ZERO; + } + + return _impl->n_physical_outputs (); +} + +ChanCount +PortManager::n_physical_inputs () const +{ + if (!_impl) { + return ChanCount::ZERO; + } + return _impl->n_physical_inputs (); +} + +/** @param name Full or short name of port + * @return Corresponding Port or 0. + */ + +boost::shared_ptr<Port> +PortManager::get_port_by_name (const string& portname) +{ + if (!_impl) { + return boost::shared_ptr<Port>(); + } + + if (!port_is_mine (portname)) { + /* not an ardour port */ + return boost::shared_ptr<Port> (); + } + + boost::shared_ptr<Ports> pr = ports.reader(); + std::string rel = make_port_name_relative (portname); + Ports::iterator x = pr->find (rel); + + if (x != pr->end()) { + /* its possible that the port was renamed by some 3rd party and + we don't know about it. check for this (the check is quick + and cheap), and if so, rename the port (which will alter + the port map as a side effect). + */ + const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle())); + if (check != rel) { + x->second->set_name (check); + } + return x->second; + } + + return boost::shared_ptr<Port> (); +} + +void +PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name) +{ + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> p = writer.get_copy(); + Ports::iterator x = p->find (old_relative_name); + + if (x != p->end()) { + boost::shared_ptr<Port> port = x->second; + p->erase (x); + p->insert (make_pair (new_relative_name, port)); + } +} + +int +PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s) +{ + if (!_impl) { + return 0; + } + + return _impl->get_ports (port_name_pattern, type, flags, s); +} + +void +PortManager::port_registration_failure (const std::string& portname) +{ + if (!_impl) { + return; + } + + string full_portname = _impl->my_name(); + full_portname += ':'; + full_portname += portname; + + + PortEngine::PortHandle p = _impl->get_port_by_name (full_portname); + string reason; + + if (p) { + reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname); + } else { + reason = string_compose (_("No more ports are available. You will need to stop %1 and restart with more ports if you need this many tracks."), PROGRAM_NAME); + } + + throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); +} + +boost::shared_ptr<Port> +PortManager::register_port (DataType dtype, const string& portname, bool input) +{ + boost::shared_ptr<Port> newport; + + try { + if (dtype == DataType::AUDIO) { + newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput))); + } else if (dtype == DataType::MIDI) { + newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput))); + } else { + throw PortRegistrationFailure("unable to create port (unknown type)"); + } + + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + ps->insert (make_pair (make_port_name_relative (portname), newport)); + + /* writer goes out of scope, forces update */ + + return newport; + } + + catch (PortRegistrationFailure& err) { + throw err; + } catch (std::exception& e) { + throw PortRegistrationFailure(string_compose( + _("unable to create port: %1"), e.what()).c_str()); + } catch (...) { + throw PortRegistrationFailure("unable to create port (unknown error)"); + } +} + +boost::shared_ptr<Port> +PortManager::register_input_port (DataType type, const string& portname) +{ + return register_port (type, portname, true); +} + +boost::shared_ptr<Port> +PortManager::register_output_port (DataType type, const string& portname) +{ + return register_port (type, portname, false); +} + +int +PortManager::unregister_port (boost::shared_ptr<Port> port) +{ + /* caller must hold process lock */ + + { + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + Ports::iterator x = ps->find (make_port_name_relative (port->name())); + + if (x != ps->end()) { + ps->erase (x); + } + + /* writer goes out of scope, forces update */ + } + + ports.flush (); + + return 0; +} + +bool +PortManager::connected (const string& port_name) +{ + if (!_impl) { + return false; + } + + PortEngine::PortHandle handle = _impl->get_port_by_name (port_name); + + if (!handle) { + return false; + } + + return _impl->connected (handle); +} + +int +PortManager::connect (const string& source, const string& destination) +{ + int ret; + + string s = make_port_name_non_relative (source); + string d = make_port_name_non_relative (destination); + + boost::shared_ptr<Port> src = get_port_by_name (s); + boost::shared_ptr<Port> dst = get_port_by_name (d); + + if (src) { + ret = src->connect (d); + } else if (dst) { + ret = dst->connect (s); + } else { + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + ret = -1; + } + + if (ret > 0) { + /* already exists - no error, no warning */ + } else if (ret < 0) { + error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"), + source, s, destination, d) + << endmsg; + } + + return ret; +} + +int +PortManager::disconnect (const string& source, const string& destination) +{ + int ret; + + string s = make_port_name_non_relative (source); + string d = make_port_name_non_relative (destination); + + boost::shared_ptr<Port> src = get_port_by_name (s); + boost::shared_ptr<Port> dst = get_port_by_name (d); + + if (src) { + ret = src->disconnect (d); + } else if (dst) { + ret = dst->disconnect (s); + } else { + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + ret = -1; + } + return ret; +} + +int +PortManager::disconnect (boost::shared_ptr<Port> port) +{ + return port->disconnect_all (); +} + +int +PortManager::reestablish_ports () +{ + Ports::iterator i; + + boost::shared_ptr<Ports> p = ports.reader (); + + for (i = p->begin(); i != p->end(); ++i) { + if (i->second->reestablish ()) { + break; + } + } + + if (i != p->end()) { + /* failed */ + remove_all_ports (); + return -1; + } + + MIDI::Manager::instance()->reestablish (); + + return 0; +} + +int +PortManager::reconnect_ports () +{ + boost::shared_ptr<Ports> p = ports.reader (); + + /* re-establish connections */ + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + i->second->reconnect (); + } + + MIDI::Manager::instance()->reconnect (); + + return 0; +} + +void +PortManager::connect_callback (const string& a, const string& b, bool conn) +{ + boost::shared_ptr<Port> port_a; + boost::shared_ptr<Port> port_b; + Ports::iterator x; + boost::shared_ptr<Ports> pr = ports.reader (); + + x = pr->find (make_port_name_relative (a)); + if (x != pr->end()) { + port_a = x->second; + } + + x = pr->find (make_port_name_relative (b)); + if (x != pr->end()) { + port_b = x->second; + } + + PortConnectedOrDisconnected ( + port_a, a, + port_b, b, + conn + ); /* EMIT SIGNAL */ +} + +void +PortManager::registration_callback () +{ + if (!_port_remove_in_progress) { + PortRegisteredOrUnregistered (); /* EMIT SIGNAL */ + } +} + +bool +PortManager::can_request_input_monitoring () const +{ + if (!_impl) { + return false; + } + + return _impl->can_monitor_input (); +} + +void +PortManager::request_input_monitoring (const string& name, bool yn) const +{ + if (!_impl) { + return; + } + + PortEngine::PortHandle ph = _impl->get_port_by_name (name); + + if (ph) { + _impl->request_input_monitoring (ph, yn); + } +} + +void +PortManager::ensure_input_monitoring (const string& name, bool yn) const +{ + if (!_impl) { + return; + } + + PortEngine::PortHandle ph = _impl->get_port_by_name (name); + + if (ph) { + _impl->ensure_input_monitoring (ph, yn); + } +} + +uint32_t +PortManager::port_name_size() const +{ + if (!_impl) { + return 0; + } + + return _impl->port_name_size (); +} + +string +PortManager::my_name() const +{ + if (!_impl) { + return string(); + } + + return _impl->my_name(); +} + +int +PortManager::graph_order_callback () +{ + if (!_port_remove_in_progress) { + GraphReordered(); /* EMIT SIGNAL */ + } + + return 0; +} diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 2e1517fe21..8e0ac8604e 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -971,7 +971,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr< DEBUG_TRACE (DEBUG::Processors, string_compose ( "%1 adding processor %2\n", name(), processor->name())); - if (!_session.engine().connected() || !processor) { + if (!AudioEngine::instance()->connected() || !processor) { return 1; } @@ -3814,7 +3814,7 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn universally true, but the alternative is way too corner-case to worry about. */ - jack_latency_range_t all_connections; + LatencyRange all_connections; if (from.empty()) { all_connections.min = 0; @@ -3829,7 +3829,7 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { - jack_latency_range_t range; + LatencyRange range; p->get_connected_latency_range (range, playback); @@ -3894,7 +3894,7 @@ Route::set_public_port_latencies (framecnt_t value, bool playback) const latency compensation into account. */ - jack_latency_range_t range; + LatencyRange range; range.min = value; range.max = value; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ce82f79bb5..9667bbcd2c 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -346,8 +346,8 @@ Session::when_engine_running () BootMessage (_("Set block size and sample rate")); - set_block_size (_engine.frames_per_cycle()); - set_frame_rate (_engine.frame_rate()); + set_block_size (_engine.samples_per_cycle()); + set_frame_rate (_engine.sample_rate()); BootMessage (_("Using configuration")); diff --git a/libs/ardour/session_ltc.cc b/libs/ardour/session_ltc.cc index d873bc07f9..d52d9e919a 100644 --- a/libs/ardour/session_ltc.cc +++ b/libs/ardour/session_ltc.cc @@ -56,7 +56,7 @@ using namespace Timecode; * This filter is adaptive so that fast vari-speed signals * will not be affected by it. */ -#define LTC_RISE_TIME(speed) MIN (100, MAX(40, (4000000 / ((speed==0)?1:speed) / engine().frame_rate()))) +#define LTC_RISE_TIME(speed) MIN (100, MAX(40, (4000000 / ((speed==0)?1:speed) / engine().sample_rate()))) #define TV_STANDARD(tcf) \ (timecode_to_frames_per_second(tcf)==25.0 ? LTC_TV_625_50 : \ @@ -565,7 +565,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end * To do better than this, resampling (or a rewrite of the * encoder) is required. */ - ltc_speed -= ((ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) / engine().frame_rate(); + ltc_speed -= ((ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) / engine().sample_rate(); } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 49a0eed559..cae3b9720a 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -155,7 +155,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) set_history_depth (Config->get_history_depth()); - _current_frame_rate = _engine.frame_rate (); + _current_frame_rate = _engine.sample_rate (); _nominal_frame_rate = _current_frame_rate; _base_frame_rate = _current_frame_rate; @@ -736,7 +736,15 @@ Session::jack_session_event (jack_session_event_t * event) } } - jack_session_reply (_engine.jack(), event); + /* this won't be called if the port engine in use is not JACK, so we do + not have to worry about the type of PortEngine::private_handle() + */ + + jack_client_t* jack_client = (jack_client_t*) AudioEngine::instance()->port_engine().private_handle(); + + if (jack_client) { + jack_session_reply (jack_client, event); + } if (event->type == JackSessionSaveAndQuit) { Quit (); /* EMIT SIGNAL */ diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index 18805afa90..088712f625 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -180,31 +180,30 @@ Session::timecode_time (Timecode::Time &t) } int -Session::jack_sync_callback (jack_transport_state_t state, - jack_position_t* pos) +Session::jack_sync_callback (TransportState state, framepos_t pos) { bool slave = synced_to_jack(); switch (state) { - case JackTransportStopped: - if (slave && _transport_frame != pos->frame && post_transport_work() == 0) { - request_locate (pos->frame, false); + case TransportStopped: + if (slave && _transport_frame != pos && post_transport_work() == 0) { + request_locate (pos, false); // cerr << "SYNC: stopped, locate to " << pos->frame << " from " << _transport_frame << endl; return false; } else { return true; } - case JackTransportStarting: + case TransportStarting: // cerr << "SYNC: starting @ " << pos->frame << " a@ " << _transport_frame << " our work = " << post_transport_work() << " pos matches ? " << (_transport_frame == pos->frame) << endl; if (slave) { - return _transport_frame == pos->frame && post_transport_work() == 0; + return _transport_frame == pos && post_transport_work() == 0; } else { return true; } break; - case JackTransportRolling: + case TransportRolling: // cerr << "SYNC: rolling slave = " << slave << endl; if (slave) { start_transport (); @@ -212,17 +211,21 @@ Session::jack_sync_callback (jack_transport_state_t state, break; default: - error << string_compose (_("Unknown JACK transport state %1 in sync callback"), state) + error << string_compose (_("Unknown transport state %1 in sync callback"), state) << endmsg; } return true; } +/* XXX REQUIRES SOMEWAY TO EFFICIENTLY ACCESS jack_position_t WITHOUT BRIDGING + * THE ENTIRE DATA STRUCTURE + */ +#if 0 void -Session::jack_timebase_callback (jack_transport_state_t /*state*/, +Session::jack_timebase_callback (TransportState /*state*/, pframes_t /*nframes*/, - jack_position_t* pos, + framepos_t pos, int /*new_position*/) { Timecode::BBT_Time bbt; @@ -299,6 +302,7 @@ Session::jack_timebase_callback (jack_transport_state_t /*state*/, } #endif } +#endif /* jack data structure issues */ ARDOUR::framecnt_t Session::convert_to_frames (AnyTime const & position) diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 51d8d2c369..08e9a89481 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1426,7 +1426,7 @@ Session::switch_to_sync_source (SyncSource src) return; } - new_slave = new JACK_Slave (_engine.jack()); + new_slave = new JACK_Slave (*AudioEngine::instance()); break; default: @@ -1616,16 +1616,6 @@ Session::allow_auto_play (bool yn) auto_play_legal = yn; } -void -Session::reset_jack_connection (jack_client_t* jack) -{ - JACK_Slave* js; - - if (_slave && ((js = dynamic_cast<JACK_Slave*> (_slave)) != 0)) { - js->reset_client (jack); - } -} - bool Session::maybe_stop (framepos_t limit) { diff --git a/libs/ardour/slave.cc b/libs/ardour/slave.cc index 1cbcba8ee6..e8c63b43c1 100644 --- a/libs/ardour/slave.cc +++ b/libs/ardour/slave.cc @@ -50,13 +50,13 @@ SlaveSessionProxy::transport_frame() const pframes_t SlaveSessionProxy::frames_since_cycle_start() const { - return session.engine().frames_since_cycle_start(); + return session.engine().samples_since_cycle_start(); } framepos_t SlaveSessionProxy::frame_time() const { - return session.engine().frame_time(); + return session.engine().sample_time(); } void diff --git a/libs/ardour/wscript b/libs/ardour/wscript index ea08636ea8..d48a4d6d85 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -1,4 +1,4 @@ -#!/usr/bin/env python +1#!/usr/bin/env python from waflib.extras import autowaf as autowaf from waflib import Options import os @@ -43,6 +43,7 @@ libardour_sources = [ 'automation_control.cc', 'automation_list.cc', 'automation_watch.cc', + 'backend_search_path.cc', 'beats_frames_converter.cc', 'broadcast_info.cc', 'buffer.cc', @@ -148,6 +149,7 @@ libardour_sources = [ 'plugin_manager.cc', 'port.cc', 'port_insert.cc', + 'port_manager.cc', 'port_set.cc', 'process_thread.cc', 'processor.cc', @@ -432,6 +434,32 @@ def build(bld): elif bld.env['build_target'] == 'x86_64': obj.source += [ 'sse_functions_xmm.cc', 'sse_functions_64bit.s' ] + # the JACK audio backend + + obj = bld.shlib (features = 'c cxx cshlib cxxshlib', + source = [ + 'jack_api.cc', + 'jack_connection.cc', + 'jack_audiobackend.cc', + 'jack_portengine.cc', + 'jack_utils.cc' + ]) + obj.cxxflags = [ '-fPIC' ] + obj.name = 'jack_audiobackend' + obj.target = 'jack_audiobackend' + obj.uselib = [ 'JACK' ] + obj.use = [ 'ardour' ] + obj.vnum = '1.0.0' + obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + obj.includes = [ '.' ] + obj.defines = [ + 'PACKAGE="' + I18N_PACKAGE + '"', + 'DATA_DIR="' + os.path.normpath(bld.env['DATADIR']) + '"', + 'CONFIG_DIR="' + os.path.normpath(bld.env['SYSCONFDIR']) + '"', + 'PROGRAM_NAME="' + bld.env['PROGRAM_NAME'] + '"', + ] + + # i18n if bld.is_defined('ENABLE_NLS'): mo_files = bld.path.ant_glob('po/*.mo') |