summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/audio_backend.h410
-rw-r--r--libs/ardour/ardour/audio_diskstream.h4
-rw-r--r--libs/ardour/ardour/audio_port.h8
-rw-r--r--libs/ardour/ardour/audioengine.h451
-rw-r--r--libs/ardour/ardour/backend_search_path.h39
-rw-r--r--libs/ardour/ardour/data_type.h27
-rw-r--r--libs/ardour/ardour/directory_names.h1
-rw-r--r--libs/ardour/ardour/jack_audiobackend.h186
-rw-r--r--libs/ardour/ardour/jack_connection.h40
-rw-r--r--libs/ardour/ardour/jack_portengine.h127
-rw-r--r--libs/ardour/ardour/jack_utils.h243
-rw-r--r--libs/ardour/ardour/midi_buffer.h1
-rw-r--r--libs/ardour/ardour/midi_port.h4
-rw-r--r--libs/ardour/ardour/port.h42
-rw-r--r--libs/ardour/ardour/port_engine.h343
-rw-r--r--libs/ardour/ardour/port_manager.h144
-rw-r--r--libs/ardour/ardour/session.h6
-rw-r--r--libs/ardour/ardour/slave.h8
-rw-r--r--libs/ardour/ardour/types.h26
-rw-r--r--libs/ardour/audio_diskstream.cc12
-rw-r--r--libs/ardour/audio_port.cc10
-rw-r--r--libs/ardour/audioengine.cc1400
-rw-r--r--libs/ardour/backend_search_path.cc45
-rw-r--r--libs/ardour/bundle.cc15
-rw-r--r--libs/ardour/capturing_processor.cc2
-rw-r--r--libs/ardour/directory_names.cc1
-rw-r--r--libs/ardour/export_channel.cc2
-rw-r--r--libs/ardour/export_graph_builder.cc4
-rw-r--r--libs/ardour/globals.cc5
-rw-r--r--libs/ardour/internal_send.cc2
-rw-r--r--libs/ardour/io.cc4
-rw-r--r--libs/ardour/jack_api.cc102
-rw-r--r--libs/ardour/jack_audiobackend.cc947
-rw-r--r--libs/ardour/jack_connection.cc158
-rw-r--r--libs/ardour/jack_portengine.cc532
-rw-r--r--libs/ardour/jack_slave.cc33
-rw-r--r--libs/ardour/jack_utils.cc894
-rw-r--r--libs/ardour/ltc_slave.cc16
-rw-r--r--libs/ardour/lv2_plugin.cc4
-rw-r--r--libs/ardour/midi_buffer.cc49
-rw-r--r--libs/ardour/midi_diskstream.cc4
-rw-r--r--libs/ardour/midi_port.cc42
-rw-r--r--libs/ardour/midi_ui.cc2
-rw-r--r--libs/ardour/mtc_slave.cc8
-rw-r--r--libs/ardour/panner_manager.cc1
-rw-r--r--libs/ardour/port.cc205
-rw-r--r--libs/ardour/port_insert.cc6
-rw-r--r--libs/ardour/port_manager.cc545
-rw-r--r--libs/ardour/route.cc8
-rw-r--r--libs/ardour/session.cc4
-rw-r--r--libs/ardour/session_ltc.cc4
-rw-r--r--libs/ardour/session_state.cc12
-rw-r--r--libs/ardour/session_time.cc26
-rw-r--r--libs/ardour/session_transport.cc12
-rw-r--r--libs/ardour/slave.cc4
-rw-r--r--libs/ardour/wscript30
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')