summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2013-09-12 11:28:50 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2013-09-12 11:28:50 -0400
commitbb826f4beebfcedc50570b818c305560d2233e47 (patch)
tree1484460f914e64fdbdce58230ffe7934bc0761f4 /libs/ardour
parent8c5cff60912c7e0a7256f635641399500d8d00d9 (diff)
parentf85b362351a5f9167f93b6988f2c8a4c7e03a33c (diff)
incomplete merge of master into windows (requires upcoming changes to master to be complete)
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/ardour.h4
-rw-r--r--libs/ardour/ardour/async_midi_port.h99
-rw-r--r--libs/ardour/ardour/audio_backend.h432
-rw-r--r--libs/ardour/ardour/audio_diskstream.h4
-rw-r--r--libs/ardour/ardour/audio_port.h6
-rw-r--r--libs/ardour/ardour/audioengine.h481
-rw-r--r--libs/ardour/ardour/backend_search_path.h39
-rw-r--r--libs/ardour/ardour/buffer_set.h2
-rw-r--r--libs/ardour/ardour/data_type.h27
-rw-r--r--libs/ardour/ardour/debug.h1
-rw-r--r--libs/ardour/ardour/directory_names.h1
-rw-r--r--libs/ardour/ardour/diskstream.h4
-rw-r--r--libs/ardour/ardour/graph.h2
-rw-r--r--libs/ardour/ardour/midi_buffer.h1
-rw-r--r--libs/ardour/ardour/midi_diskstream.h2
-rw-r--r--libs/ardour/ardour/midi_port.h27
-rw-r--r--libs/ardour/ardour/midi_ui.h6
-rw-r--r--libs/ardour/ardour/midiport_manager.h99
-rw-r--r--libs/ardour/ardour/port.h47
-rw-r--r--libs/ardour/ardour/port_engine.h345
-rw-r--r--libs/ardour/ardour/port_manager.h169
-rw-r--r--libs/ardour/ardour/public_diskstream.h4
-rw-r--r--libs/ardour/ardour/rc_configuration.h6
-rw-r--r--libs/ardour/ardour/session.h63
-rw-r--r--libs/ardour/ardour/slave.h26
-rw-r--r--libs/ardour/ardour/ticker.h53
-rw-r--r--libs/ardour/ardour/track.h4
-rw-r--r--libs/ardour/ardour/types.h40
-rw-r--r--libs/ardour/async_midi_port.cc303
-rw-r--r--libs/ardour/audio_diskstream.cc12
-rw-r--r--libs/ardour/audio_port.cc10
-rw-r--r--libs/ardour/audio_track.cc2
-rw-r--r--libs/ardour/audioengine.cc1584
-rw-r--r--libs/ardour/backend_search_path.cc45
-rw-r--r--libs/ardour/buffer_set.cc6
-rw-r--r--libs/ardour/bundle.cc19
-rw-r--r--libs/ardour/capturing_processor.cc2
-rw-r--r--libs/ardour/debug.cc1
-rw-r--r--libs/ardour/delivery.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.cc49
-rw-r--r--libs/ardour/internal_send.cc2
-rw-r--r--libs/ardour/io.cc8
-rw-r--r--libs/ardour/jack_slave.cc33
-rw-r--r--libs/ardour/ladspa_plugin.cc4
-rw-r--r--libs/ardour/ltc_slave.cc24
-rw-r--r--libs/ardour/lv2_plugin.cc4
-rw-r--r--libs/ardour/midi_buffer.cc55
-rw-r--r--libs/ardour/midi_clock_slave.cc21
-rw-r--r--libs/ardour/midi_diskstream.cc22
-rw-r--r--libs/ardour/midi_port.cc74
-rw-r--r--libs/ardour/midi_ui.cc42
-rw-r--r--libs/ardour/midiport_manager.cc170
-rw-r--r--libs/ardour/mtc_slave.cc68
-rw-r--r--libs/ardour/panner_manager.cc1
-rw-r--r--libs/ardour/port.cc259
-rw-r--r--libs/ardour/port_insert.cc10
-rw-r--r--libs/ardour/port_manager.cc659
-rw-r--r--libs/ardour/rc_configuration.cc27
-rw-r--r--libs/ardour/route.cc10
-rw-r--r--libs/ardour/session.cc428
-rw-r--r--libs/ardour/session_export.cc7
-rw-r--r--libs/ardour/session_jack.cc185
-rw-r--r--libs/ardour/session_ltc.cc4
-rw-r--r--libs/ardour/session_midi.cc60
-rw-r--r--libs/ardour/session_process.cc5
-rw-r--r--libs/ardour/session_state.cc380
-rw-r--r--libs/ardour/session_time.cc97
-rw-r--r--libs/ardour/session_transport.cc44
-rw-r--r--libs/ardour/slave.cc4
-rw-r--r--libs/ardour/ticker.cc180
-rw-r--r--libs/ardour/track.cc8
-rw-r--r--libs/ardour/wscript66
75 files changed, 4407 insertions, 2590 deletions
diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h
index eaf6b572fd..311611997f 100644
--- a/libs/ardour/ardour/ardour.h
+++ b/libs/ardour/ardour/ardour.h
@@ -75,9 +75,7 @@ namespace ARDOUR {
bool translations_are_enabled ();
bool set_translations_enabled (bool);
- static inline microseconds_t get_microseconds () {
- return (microseconds_t) jack_get_time();
- }
+ microseconds_t get_microseconds ();
void setup_fpu ();
std::vector<SyncSource> get_available_sync_options();
diff --git a/libs/ardour/ardour/async_midi_port.h b/libs/ardour/ardour/async_midi_port.h
new file mode 100644
index 0000000000..896301b398
--- /dev/null
+++ b/libs/ardour/ardour/async_midi_port.h
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 1998-2010 Paul Barton-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_async_midiport_h__
+#define __libardour_async_midiport_h__
+
+#include <string>
+#include <iostream>
+
+#include "pbd/xml++.h"
+#include "pbd/crossthread.h"
+#include "pbd/signals.h"
+#include "pbd/ringbuffer.h"
+
+#include "evoral/Event.hpp"
+#include "evoral/EventRingBuffer.hpp"
+
+#include "midi++/types.h"
+#include "midi++/parser.h"
+#include "midi++/port.h"
+
+#include "ardour/midi_port.h"
+
+namespace ARDOUR {
+
+ class AsyncMIDIPort : public ARDOUR::MidiPort, public MIDI::Port {
+
+ public:
+ AsyncMIDIPort (std::string const &, PortFlags);
+ ~AsyncMIDIPort ();
+
+ /* called from an RT context */
+
+ void cycle_start (pframes_t nframes);
+ void cycle_end (pframes_t nframes);
+
+ /* called from non-RT context */
+
+ void parse (framecnt_t timestamp);
+ int write (const MIDI::byte *msg, size_t msglen, MIDI::timestamp_t timestamp);
+ int read (MIDI::byte *buf, size_t bufsize);
+ void drain (int check_interval_usecs);
+ int selectable () const {
+#ifdef PLATFORM_WINDOWS
+ return false;
+#else
+ return xthread.selectable();
+#endif
+ }
+
+ static void set_process_thread (pthread_t);
+ static pthread_t get_process_thread () { return _process_thread; }
+ static bool is_process_thread();
+
+ private:
+ bool _currently_in_cycle;
+ MIDI::timestamp_t _last_write_timestamp;
+ RingBuffer< Evoral::Event<double> > output_fifo;
+ Evoral::EventRingBuffer<MIDI::timestamp_t> input_fifo;
+ Glib::Threads::Mutex output_fifo_lock;
+#ifndef PLATFORM_WINDOWS
+ CrossThreadChannel xthread;
+#endif
+
+ int create_port ();
+
+ /** Channel used to signal to the MidiControlUI that input has arrived */
+
+ std::string _connections;
+ PBD::ScopedConnection connect_connection;
+ PBD::ScopedConnection halt_connection;
+ void flush (void* jack_port_buffer);
+ void jack_halted ();
+ void make_connections ();
+ void init (std::string const &, Flags);
+
+ void flush_output_fifo (pframes_t);
+
+ static pthread_t _process_thread;
+};
+
+} // namespace ARDOUR
+
+#endif /* __libardour_async_midiport_h__ */
diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h
new file mode 100644
index 0000000000..ab37bea526
--- /dev/null
+++ b/libs/ardour/ardour/audio_backend.h
@@ -0,0 +1,432 @@
+/*
+ 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;
+
+ /* Return true if the derived class can change the sample rate of the
+ * device in use while the device is already being used. Return false
+ * otherwise. (example: JACK cannot do this as of September 2013)
+ */
+ virtual bool can_change_sample_rate_when_running () const = 0;
+ /* Return true if the derived class can change the buffer size of the
+ * device in use while the device is already being used. Return false
+ * otherwise.
+ */
+ virtual bool can_change_buffer_size_when_running () 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;
+
+ /** Return the name of a control application for the
+ * selected/in-use device. If no such application exists,
+ * or if no device has been selected or is in-use,
+ * return an empty string.
+ */
+ virtual std::string control_app_name() const = 0;
+ /** Launch the control app for the currently in-use or
+ * selected device. May do nothing if the control
+ * app is undefined or cannot be launched.
+ */
+ virtual void launch_control_app () = 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..f87b134e9e 100644
--- a/libs/ardour/ardour/audio_port.h
+++ b/libs/ardour/ardour/audio_port.h
@@ -46,10 +46,10 @@ 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 */
+ /* special access for PortManager 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 ec31dd1ac2..cf4f91d4d0 100644
--- a/libs/ardour/ardour/audioengine.h
+++ b/libs/ardour/ardour/audioengine.h
@@ -33,29 +33,22 @@
#include <glibmm/threads.h>
-#include "pbd/rcu.h"
#include "pbd/signals.h"
#include "pbd/stacktrace.h"
-#ifndef PLATFORM_WINDOWS
-#include <jack/weakjack.h>
-#endif
-
-#include <jack/jack.h>
-#include <jack/transport.h>
-#include <jack/thread.h>
-
#include "ardour/ardour.h"
-
#include "ardour/data_type.h"
#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>
#endif
+class MTDM;
+
namespace ARDOUR {
class InternalPort;
@@ -63,300 +56,190 @@ 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;
-
-#ifdef HAVE_JACK_SESSION
- 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()>, jack_native_thread_t*, size_t stacksize);
- bool stop_process_thread (jack_native_thread_t);
-
-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;
+ 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()
+ Session* session() const { return _session; }
+
+ 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;
+
+ /* 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 *);
+
+ /* latency measurement */
+
+ MTDM* mtdm();
+ void start_latency_detection ();
+ void stop_latency_detection ();
+ void set_latency_input_port (const std::string&);
+ void set_latency_output_port (const std::string&);
+ uint32_t latency_signal_delay () const { return _latency_signal_latency; }
+
+ 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;
+ bool _freewheeling;
+ /// 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;
+ Glib::Threads::Thread* m_meter_thread;
+ ProcessThread* _main_thread;
+ MTDM* _mtdm;
+ bool _measuring_latency;
+ PortEngine::PortHandle _latency_input_port;
+ PortEngine::PortHandle _latency_output_port;
+ framecnt_t _latency_flush_frames;
+ std::string _latency_input_name;
+ std::string _latency_output_name;
+ framecnt_t _latency_signal_latency;
+
+ 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..e8a5082c71
--- /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/buffer_set.h b/libs/ardour/ardour/buffer_set.h
index 12e9dbc63b..26d47682af 100644
--- a/libs/ardour/ardour/buffer_set.h
+++ b/libs/ardour/ardour/buffer_set.h
@@ -70,7 +70,7 @@ public:
void clear();
void attach_buffers (PortSet& ports);
- void get_jack_port_addresses (PortSet &, framecnt_t);
+ void get_backend_port_addresses (PortSet &, framecnt_t);
/* the capacity here is a size_t and has a different interpretation depending
on the DataType of the buffers. for audio, its a frame count. for MIDI
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/debug.h b/libs/ardour/ardour/debug.h
index ae18e59c04..47eee69df5 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -64,6 +64,7 @@ namespace PBD {
extern uint64_t OrderKeys;
extern uint64_t Automation;
extern uint64_t WiimoteControl;
+ extern uint64_t Ports;
}
}
diff --git a/libs/ardour/ardour/directory_names.h b/libs/ardour/ardour/directory_names.h
index cb4701d336..72a456efe6 100644
--- a/libs/ardour/ardour/directory_names.h
+++ b/libs/ardour/ardour/directory_names.h
@@ -39,6 +39,7 @@ extern const char* const surfaces_dir_name;
extern const char* const ladspa_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/diskstream.h b/libs/ardour/ardour/diskstream.h
index 427b52b054..7a4bccbb46 100644
--- a/libs/ardour/ardour/diskstream.h
+++ b/libs/ardour/ardour/diskstream.h
@@ -133,8 +133,8 @@ class Diskstream : public SessionObject, public PublicDiskstream
virtual XMLNode& get_state(void);
virtual int set_state(const XMLNode&, int version);
- virtual void request_jack_monitors_input (bool) {}
- virtual void ensure_jack_monitors_input (bool) {}
+ virtual void request_input_monitoring (bool) {}
+ virtual void ensure_input_monitoring (bool) {}
framecnt_t capture_offset() const { return _capture_offset; }
virtual void set_capture_offset ();
diff --git a/libs/ardour/ardour/graph.h b/libs/ardour/ardour/graph.h
index 0a288d68d3..cac09d34af 100644
--- a/libs/ardour/ardour/graph.h
+++ b/libs/ardour/ardour/graph.h
@@ -92,7 +92,7 @@ protected:
virtual void session_going_away ();
private:
- std::list<jack_native_thread_t> _thread_list;
+ std::list<pthread_t> _thread_list;
volatile bool _quit_threads;
void reset_thread_list ();
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_diskstream.h b/libs/ardour/ardour/midi_diskstream.h
index 34fa0ae79a..b1c126b339 100644
--- a/libs/ardour/ardour/midi_diskstream.h
+++ b/libs/ardour/ardour/midi_diskstream.h
@@ -81,7 +81,7 @@ class MidiDiskstream : public Diskstream
XMLNode& get_state(void);
int set_state(const XMLNode&, int version);
- void ensure_jack_monitors_input (bool);
+ void ensure_input_monitoring (bool);
boost::shared_ptr<SMFSource> write_source () { return _write_source; }
diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h
index 5dc55398cb..00617d90ec 100644
--- a/libs/ardour/ardour/midi_port.h
+++ b/libs/ardour/ardour/midi_port.h
@@ -21,6 +21,8 @@
#ifndef __ardour_midi_port_h__
#define __ardour_midi_port_h__
+#include "midi++/parser.h"
+
#include "ardour/port.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_state_tracker.h"
@@ -56,18 +58,35 @@ class MidiPort : public Port {
MidiBuffer& get_midi_buffer (pframes_t nframes);
+ void set_always_parse (bool yn);
+ MIDI::Parser& self_parser() { return _self_parser; }
+
protected:
- friend class AudioEngine;
+ friend class PortManager;
- MidiPort (const std::string& name, Flags);
+ MidiPort (const std::string& name, PortFlags);
private:
MidiBuffer* _buffer;
bool _has_been_mixed_down;
bool _resolve_required;
bool _input_active;
-
- void resolve_notes (void* jack_buffer, MidiBuffer::TimeType when);
+ bool _always_parse;
+
+ /* Naming this is tricky. AsyncMIDIPort inherits (for now, aug 2013) from
+ * both MIDI::Port, which has _parser, and this (ARDOUR::MidiPort). We
+ * need parsing support in this object, independently of what the
+ * MIDI::Port/AsyncMIDIPort stuff does. Rather than risk errors coming
+ * from not explicitly naming which _parser we want, we will call this
+ * _self_parser for now.
+ *
+ * Ultimately, MIDI::Port should probably go away or be fully integrated
+ * into this object, somehow.
+ */
+
+ MIDI::Parser _self_parser;
+
+ void resolve_notes (void* buffer, MidiBuffer::TimeType when);
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/midi_ui.h b/libs/ardour/ardour/midi_ui.h
index 34e97494be..c15a530057 100644
--- a/libs/ardour/ardour/midi_ui.h
+++ b/libs/ardour/ardour/midi_ui.h
@@ -26,13 +26,11 @@
#include "pbd/signals.h"
#include "pbd/stacktrace.h"
-namespace MIDI {
- class Port;
-}
namespace ARDOUR {
class Session;
+class AsyncMIDIPort;
/* this is mostly a placeholder because I suspect that at some
point we will want to add more members to accomodate
@@ -67,7 +65,7 @@ class MidiControlUI : public AbstractUI<MidiUIRequest>
ARDOUR::Session& _session;
PBD::ScopedConnection rebind_connection;
- bool midi_input_handler (Glib::IOCondition, MIDI::Port*);
+ bool midi_input_handler (Glib::IOCondition, AsyncMIDIPort*);
void reset_ports ();
void clear_ports ();
diff --git a/libs/ardour/ardour/midiport_manager.h b/libs/ardour/ardour/midiport_manager.h
new file mode 100644
index 0000000000..9f93c43d5a
--- /dev/null
+++ b/libs/ardour/ardour/midiport_manager.h
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 1998 Paul Barton-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 __midiport_manager_h__
+#define __midiport_manager_h__
+
+#include <list>
+
+#include <string>
+
+#include "pbd/rcu.h"
+
+#include "midi++/types.h"
+#include "midi++/port.h"
+
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class MidiPort;
+class Port;
+
+class MidiPortManager {
+ public:
+ MidiPortManager();
+ virtual ~MidiPortManager ();
+
+ /* Ports used for control. These are read/written to outside of the
+ * process callback (asynchronously with respect to when data
+ * actually arrives).
+ *
+ * More detail: we do actually read/write data for these ports
+ * inside the process callback, but incoming data is only parsed
+ * and outgoing data is only generated *outside* the process
+ * callback.
+ */
+
+ MIDI::Port* midi_input_port () const { return _midi_input_port; }
+ MIDI::Port* midi_output_port () const { return _midi_output_port; }
+ MIDI::Port* mmc_input_port () const { return _mmc_input_port; }
+ MIDI::Port* mmc_output_port () const { return _mmc_output_port; }
+
+ /* Ports used for synchronization. These have their I/O handled inside the
+ * process callback.
+ */
+
+ boost::shared_ptr<MidiPort> mtc_input_port() const { return _mtc_input_port; }
+ boost::shared_ptr<MidiPort> mtc_output_port() const { return _mtc_output_port; }
+ boost::shared_ptr<MidiPort> midi_clock_input_port() const { return _midi_clock_input_port; }
+ boost::shared_ptr<MidiPort> midi_clock_output_port() const { return _midi_clock_output_port; }
+
+ void set_midi_port_states (const XMLNodeList&);
+ std::list<XMLNode*> get_midi_port_states () const;
+
+ PBD::Signal0<void> PortsChanged;
+
+ protected:
+ /* asynchronously handled ports: MIDI::Port */
+ MIDI::Port* _midi_input_port;
+ MIDI::Port* _midi_output_port;
+ MIDI::Port* _mmc_input_port;
+ MIDI::Port* _mmc_output_port;
+ /* these point to the same objects as the 4 members above,
+ but cast to their ARDOUR::Port base class
+ */
+ boost::shared_ptr<Port> _midi_in;
+ boost::shared_ptr<Port> _midi_out;
+ boost::shared_ptr<Port> _mmc_in;
+ boost::shared_ptr<Port> _mmc_out;
+
+ /* synchronously handled ports: ARDOUR::MidiPort */
+ boost::shared_ptr<MidiPort> _mtc_input_port;
+ boost::shared_ptr<MidiPort> _mtc_output_port;
+ boost::shared_ptr<MidiPort> _midi_clock_input_port;
+ boost::shared_ptr<MidiPort> _midi_clock_output_port;
+
+ void create_ports ();
+
+};
+
+} // namespace MIDI
+
+#endif // __midi_port_manager_h__
diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h
index e225117d94..77bf2b6f71 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;
@@ -141,11 +135,16 @@ public:
virtual void increment_port_buffer_offset (pframes_t n);
+ virtual XMLNode& get_state (void) const;
+ virtual int set_state (const XMLNode&, int version);
+
+ static std::string state_node_name;
+
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 +152,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..71f93ea05e
--- /dev/null
+++ b/libs/ardour/ardour/port_engine.h
@@ -0,0 +1,345 @@
+/*
+ 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;
+
+ virtual bool connected() 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, bool process_callback_safe = true) = 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, bool process_callback_safe = true) = 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, bool process_callback_safe = true) = 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, bool process_callback_safe = true) = 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..6d45597a41
--- /dev/null
+++ b/libs/ardour/ardour/port_manager.h
@@ -0,0 +1,169 @@
+/*
+ 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/midiport_manager.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;
+ typedef std::list<boost::shared_ptr<Port> > PortList;
+
+ 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, bool async = false);
+ boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname, bool async = false);
+ 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>&);
+ int get_ports (DataType, PortList&);
+
+ 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, bool async = false);
+ void port_registration_failure (const std::string& portname);
+
+ /** List of ports to be used between ::cycle_start() and ::cycle_end()
+ */
+ boost::shared_ptr<Ports> _cycle_ports;
+
+ void fade_out (gain_t, gain_t, pframes_t);
+ void silence (pframes_t nframes);
+ void check_monitoring ();
+ /** Signal the start of an audio cycle.
+ * This MUST be called before any reading/writing for this cycle.
+ * Realtime safe.
+ */
+ void cycle_start (pframes_t nframes);
+
+ /** Signal the end of an audio cycle.
+ * This signifies that the cycle began with @ref cycle_start has ended.
+ * This MUST be called at the end of each cycle.
+ * Realtime safe.
+ */
+ void cycle_end (pframes_t nframes);
+};
+
+
+
+} // namespace
+
+#endif /* __libardour_port_manager_h__ */
diff --git a/libs/ardour/ardour/public_diskstream.h b/libs/ardour/ardour/public_diskstream.h
index da481a6dee..125e1a21ce 100644
--- a/libs/ardour/ardour/public_diskstream.h
+++ b/libs/ardour/ardour/public_diskstream.h
@@ -33,8 +33,8 @@ public:
virtual ~PublicDiskstream() {}
virtual boost::shared_ptr<Playlist> playlist () = 0;
- virtual void request_jack_monitors_input (bool) = 0;
- virtual void ensure_jack_monitors_input (bool) = 0;
+ virtual void request_input_monitoring (bool) = 0;
+ virtual void ensure_input_monitoring (bool) = 0;
virtual bool destructive () const = 0;
virtual std::list<boost::shared_ptr<Source> > & last_capture_sources () = 0;
virtual void set_capture_offset () = 0;
diff --git a/libs/ardour/ardour/rc_configuration.h b/libs/ardour/ardour/rc_configuration.h
index ff1c5f035c..e2f68477a1 100644
--- a/libs/ardour/ardour/rc_configuration.h
+++ b/libs/ardour/ardour/rc_configuration.h
@@ -52,7 +52,6 @@ class RCConfiguration : public Configuration
XMLNode * instant_xml (const std::string& str);
XMLNode* control_protocol_state () { return _control_protocol_state; }
- std::list<XMLNode*> midi_port_states () { return _midi_port_states; }
/* define accessor methods */
@@ -81,11 +80,6 @@ class RCConfiguration : public Configuration
#undef CONFIG_VARIABLE_SPECIAL
XMLNode* _control_protocol_state;
-
- /** MIDI port nodes from the RC configuration. We store them so that we can set their
- state once the audio engine and hence ports are up.
- */
- std::list<XMLNode*> _midi_port_states;
};
/* XXX: rename this */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 525dfbd849..43660d41cc 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -110,6 +110,8 @@ class IOProcessor;
class ImportStatus;
class MidiClockTicker;
class MidiControlUI;
+class MidiPortManager;
+class MidiPort;
class MidiRegion;
class MidiSource;
class MidiTrack;
@@ -381,9 +383,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
framecnt_t worst_track_latency () const { return _worst_track_latency; }
framecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
-#ifdef HAVE_JACK_SESSION
- void jack_session_event (jack_session_event_t* event);
-#endif
int save_state (std::string snapshot_name, bool pending = false, bool switch_to_snapshot = false);
int restore_state (std::string snapshot_name);
int save_template (std::string template_name);
@@ -556,6 +555,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
int remove_last_capture ();
+ /** handlers should return 0 for "everything OK", and any other value for
+ * "cannot setup audioengine".
+ */
+ static PBD::Signal1<int,uint32_t> AudioEngineSetupRequired;
+
/** handlers should return -1 for "stop cleanup",
0 for "yes, delete this playlist",
1 for "no, don't delete this playlist".
@@ -813,8 +817,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::shared_ptr<SessionPlaylists> playlists;
void send_mmc_locate (framepos_t);
- int send_full_time_code (framepos_t);
- void send_song_position_pointer (framepos_t);
+ void queue_full_time_code () { _send_timecode_update = true; }
+ void queue_song_position_pointer () { /* currently does nothing */ }
bool step_editing() const { return (_step_editors > 0); }
@@ -863,6 +867,27 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::shared_ptr<IO> ltc_input_io() { return _ltc_input; }
boost::shared_ptr<IO> ltc_output_io() { return _ltc_output; }
+ MIDI::Port* midi_input_port () const;
+ MIDI::Port* midi_output_port () const;
+ MIDI::Port* mmc_output_port () const;
+ MIDI::Port* mmc_input_port () const;
+
+ boost::shared_ptr<MidiPort> midi_clock_output_port () const;
+ boost::shared_ptr<MidiPort> midi_clock_input_port () const;
+ boost::shared_ptr<MidiPort> mtc_output_port () const;
+ boost::shared_ptr<MidiPort> mtc_input_port () const;
+
+ MIDI::MachineControl& mmc() { return *_mmc; }
+
+ /* Callbacks specifically related to JACK, and called directly
+ * from the JACK audio backend.
+ */
+
+#ifdef HAVE_JACK_SESSION
+ void jack_session_event (jack_session_event_t* event);
+#endif
+ void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
+
protected:
friend class AudioEngine;
void set_block_size (pframes_t nframes);
@@ -1046,7 +1071,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::scoped_ptr<SessionDirectory> _session_dir;
void hookup_io ();
- void when_engine_running ();
+ int when_engine_running ();
void graph_reordered ();
/** current snapshot name, without the .ardour suffix */
@@ -1112,8 +1137,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void auto_loop_changed (Location *);
void auto_loop_declick_range (Location *, framepos_t &, framepos_t &);
- void first_stage_init (std::string path, std::string snapshot_name);
- int second_stage_init ();
+ void pre_engine_init (std::string path);
+ int post_engine_init ();
void remove_empty_sounds ();
void setup_midi_control ();
@@ -1216,7 +1241,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();
@@ -1261,6 +1286,13 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void engine_halted ();
void xrun_recovery ();
+ /* These are synchronous and so can only be called from within the process
+ * cycle
+ */
+
+ int send_full_time_code (framepos_t, pframes_t nframes);
+ void send_song_position_pointer (framepos_t);
+
TempoMap *_tempo_map;
void tempo_map_changed (const PBD::PropertyChange&);
@@ -1429,9 +1461,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 reset_jack_connection (jack_client_t* jack);
+ int backend_sync_callback (TransportState, framepos_t);
+
void process_rtop (SessionEvent*);
void update_latency (bool playback);
@@ -1585,6 +1616,14 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void reconnect_ltc_input ();
void reconnect_ltc_output ();
+
+ /* persistent, non-track related MIDI ports */
+ MidiPortManager* _midi_ports;
+ MIDI::MachineControl* _mmc;
+
+ void setup_ltc ();
+ void setup_click ();
+ void setup_bundles ();
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h
index 9862824da9..c60acb1df7 100644
--- a/libs/ardour/ardour/slave.h
+++ b/libs/ardour/ardour/slave.h
@@ -40,14 +40,12 @@
#define PLUSMINUS(A) ( ((A)<0) ? "-" : (((A)>0) ? "+" : "\u00B1") )
#define LEADINGZERO(A) ( (A)<10 ? " " : (A)<100 ? " " : (A)<1000 ? " " : "" )
-namespace MIDI {
- class Port;
-}
-
namespace ARDOUR {
class TempoMap;
class Session;
+class AudioEngine;
+class MidiPort;
/**
* @class Slave
@@ -252,10 +250,10 @@ class TimecodeSlave : public Slave {
class MTC_Slave : public TimecodeSlave {
public:
- MTC_Slave (Session&, MIDI::Port&);
+ MTC_Slave (Session&, MidiPort&);
~MTC_Slave ();
- void rebind (MIDI::Port&);
+ void rebind (MidiPort&);
bool speed_and_position (double&, framepos_t&);
bool locked() const;
@@ -273,7 +271,7 @@ class MTC_Slave : public TimecodeSlave {
private:
Session& session;
- MIDI::Port* port;
+ MidiPort* port;
PBD::ScopedConnectionList port_connections;
PBD::ScopedConnection config_connection;
bool can_notify_on_unknown_rate;
@@ -354,7 +352,7 @@ public:
std::string approximate_current_delta() const;
private:
- void parse_ltc(const jack_nframes_t, const jack_default_audio_sample_t * const, const framecnt_t);
+ void parse_ltc(const pframes_t, const Sample* const, const framecnt_t);
void process_ltc(framepos_t const);
void init_engine_dll (framepos_t, int32_t);
bool detect_discontinuity(LTCFrameExt *, int, bool);
@@ -391,7 +389,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;
@@ -404,13 +402,13 @@ public:
class MIDIClock_Slave : public Slave {
public:
- MIDIClock_Slave (Session&, MIDI::Port&, int ppqn = 24);
+ MIDIClock_Slave (Session&, MidiPort&, int ppqn = 24);
/// Constructor for unit tests
MIDIClock_Slave (ISlaveSessionProxy* session_proxy = 0, int ppqn = 24);
~MIDIClock_Slave ();
- void rebind (MIDI::Port&);
+ void rebind (MidiPort&);
bool speed_and_position (double&, framepos_t&);
bool locked() const;
@@ -426,7 +424,6 @@ class MIDIClock_Slave : public Slave {
protected:
ISlaveSessionProxy* session;
- MIDI::Port* port;
PBD::ScopedConnectionList port_connections;
/// pulses per quarter note for one MIDI clock frame (default 24)
@@ -492,7 +489,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 +499,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/ticker.h b/libs/ardour/ardour/ticker.h
index b6e5376c12..7f0d1987fc 100644
--- a/libs/ardour/ardour/ticker.h
+++ b/libs/ardour/ardour/ticker.h
@@ -27,17 +27,13 @@
#include "ardour/session_handle.h"
-#ifndef TICKER_H_
-#define TICKER_H_
+#ifndef __libardour_ticker_h__
+#define __libardour_ticker_h__
-namespace MIDI {
- class Port;
-}
-
-namespace ARDOUR
-{
+namespace ARDOUR {
class Session;
+class MidiPort;
class MidiClockTicker : public SessionHandlePtr, boost::noncopyable
{
@@ -45,7 +41,7 @@ public:
MidiClockTicker ();
virtual ~MidiClockTicker();
- void tick (const framepos_t& transport_frames);
+ void tick (const framepos_t& transport_frames, pframes_t nframes);
bool has_midi_port() const { return _midi_port != 0; }
@@ -58,9 +54,6 @@ public:
/// slot for the signal session::TransportStateChange
void transport_state_changed();
- /// slot for the signal session::PositionChanged
- void position_changed (framepos_t position);
-
/// slot for the signal session::TransportLooped
void transport_looped();
@@ -70,23 +63,25 @@ public:
/// pulses per quarter note (default 24)
void set_ppqn(int ppqn) { _ppqn = ppqn; }
-private:
- MIDI::Port* _midi_port;
- int _ppqn;
- double _last_tick;
-
- class Position;
- boost::scoped_ptr<Position> _pos;
-
- double one_ppqn_in_frames (framepos_t transport_position);
-
- void send_midi_clock_event (pframes_t offset);
- void send_start_event (pframes_t offset);
- void send_continue_event (pframes_t offset);
- void send_stop_event (pframes_t offset);
- void send_position_event (uint32_t midi_clocks, pframes_t offset);
+ private:
+ boost::shared_ptr<MidiPort> _midi_port;
+ int _ppqn;
+ double _last_tick;
+ bool _send_pos;
+ bool _send_state;
+
+ class Position;
+ boost::scoped_ptr<Position> _pos;
+
+ double one_ppqn_in_frames (framepos_t transport_position);
+
+ void send_midi_clock_event (pframes_t offset, pframes_t nframes);
+ void send_start_event (pframes_t offset, pframes_t nframes);
+ void send_continue_event (pframes_t offset, pframes_t nframes);
+ void send_stop_event (pframes_t offset, pframes_t nframes);
+ void send_position_event (uint32_t midi_clocks, pframes_t offset, pframes_t nframes);
};
-
}
+ // namespace
-#endif /* TICKER_H_ */
+#endif /* __libardour_ticker_h__ */
diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h
index 7159261b51..bdf420763b 100644
--- a/libs/ardour/ardour/track.h
+++ b/libs/ardour/ardour/track.h
@@ -115,8 +115,8 @@ class Track : public Route, public PublicDiskstream
/* PublicDiskstream interface */
boost::shared_ptr<Playlist> playlist ();
- void request_jack_monitors_input (bool);
- void ensure_jack_monitors_input (bool);
+ void request_input_monitoring (bool);
+ void ensure_input_monitoring (bool);
bool destructive () const;
std::list<boost::shared_ptr<Source> > & last_capture_sources ();
void set_capture_offset ();
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index 2115149872..17bffc20e4 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -28,8 +28,6 @@
#include <stdint.h>
#include <inttypes.h>
-#include <jack/types.h>
-#include <jack/midiport.h>
#include "timecode/bbt_time.h"
#include "timecode/time.h"
@@ -53,12 +51,12 @@ namespace ARDOUR {
class Route;
class Region;
- typedef jack_default_audio_sample_t Sample;
- typedef float pan_t;
- typedef float gain_t;
- typedef uint32_t layer_t;
- typedef uint64_t microseconds_t;
- typedef jack_nframes_t pframes_t;
+ typedef float Sample;
+ typedef float pan_t;
+ typedef float gain_t;
+ typedef uint32_t layer_t;
+ typedef uint64_t microseconds_t;
+ typedef uint32_t pframes_t;
/* Any position measured in audio frames.
Assumed to be non-negative but not enforced.
@@ -585,6 +583,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/async_midi_port.cc b/libs/ardour/async_midi_port.cc
new file mode 100644
index 0000000000..77ca62961e
--- /dev/null
+++ b/libs/ardour/async_midi_port.cc
@@ -0,0 +1,303 @@
+/*
+ Copyright (C) 1998 Paul Barton-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.
+
+ $Id$
+*/
+
+#include <iostream>
+
+#include "pbd/error.h"
+#include "pbd/stacktrace.h"
+
+#include "midi++/types.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/midi_buffer.h"
+
+using namespace MIDI;
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+namespace Evoral {
+ template class EventRingBuffer<MIDI::timestamp_t>;
+}
+
+pthread_t AsyncMIDIPort::_process_thread;
+
+#define port_engine AudioEngine::instance()->port_engine()
+
+AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags)
+ : MidiPort (name, flags)
+ , MIDI::Port (name, MIDI::Port::Flags (0))
+ , _currently_in_cycle (false)
+ , _last_write_timestamp (0)
+ , output_fifo (512)
+ , input_fifo (1024)
+ , xthread (true)
+{
+}
+
+AsyncMIDIPort::~AsyncMIDIPort ()
+{
+}
+
+void
+AsyncMIDIPort::flush_output_fifo (pframes_t nframes)
+{
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
+ size_t written;
+
+ output_fifo.get_read_vector (&vec);
+
+ MidiBuffer& mb (get_midi_buffer (nframes));
+
+ if (vec.len[0]) {
+ Evoral::Event<double>* evp = vec.buf[0];
+
+ for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if (vec.len[1]) {
+ Evoral::Event<double>* evp = vec.buf[1];
+
+ for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if ((written = vec.len[0] + vec.len[1]) != 0) {
+ output_fifo.increment_read_idx (written);
+ }
+}
+
+void
+AsyncMIDIPort::cycle_start (pframes_t nframes)
+{
+ _currently_in_cycle = true;
+ MidiPort::cycle_start (nframes);
+
+ /* dump anything waiting in the output FIFO at the start of the port
+ * buffer
+ */
+
+ if (ARDOUR::Port::sends_output()) {
+ flush_output_fifo (nframes);
+ }
+
+ /* copy incoming data from the port buffer into the input FIFO
+ and if necessary wakeup the reader
+ */
+
+ if (ARDOUR::Port::receives_input()) {
+ MidiBuffer& mb (get_midi_buffer (nframes));
+ pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start();
+
+ for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
+ input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer());
+ }
+
+ if (!mb.empty()) {
+ xthread.wakeup ();
+ }
+ }
+
+}
+
+void
+AsyncMIDIPort::cycle_end (pframes_t nframes)
+{
+ if (ARDOUR::Port::sends_output()) {
+ /* move any additional data from output FIFO into the port
+ buffer.
+ */
+ flush_output_fifo (nframes);
+ }
+
+ MidiPort::cycle_end (nframes);
+
+ _currently_in_cycle = false;
+}
+
+/** wait for the output FIFO to be emptied by successive process() callbacks.
+ *
+ * Cannot be called from a processing thread.
+ */
+void
+AsyncMIDIPort::drain (int check_interval_usecs)
+{
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ if (!AudioEngine::instance()->running() || AudioEngine::instance()->session() == 0) {
+ /* no more process calls - it will never drain */
+ return;
+ }
+
+
+ if (is_process_thread()) {
+ error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
+ return;
+ }
+
+ while (1) {
+ output_fifo.get_write_vector (&vec);
+ if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
+ break;
+ }
+ usleep (check_interval_usecs);
+ }
+}
+
+int
+AsyncMIDIPort::write (const MIDI::byte * msg, size_t msglen, MIDI::timestamp_t timestamp)
+{
+ int ret = 0;
+
+ if (!ARDOUR::Port::sends_output()) {
+ return ret;
+ }
+
+ if (!is_process_thread()) {
+
+ /* this is the best estimate of "when" this MIDI data is being
+ * delivered
+ */
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ Glib::Threads::Mutex::Lock lm (output_fifo_lock);
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ output_fifo.get_write_vector (&vec);
+
+ if (vec.len[0] + vec.len[1] < 1) {
+ error << "no space in FIFO for non-process thread MIDI write" << endmsg;
+ return 0;
+ }
+
+ if (vec.len[0]) {
+ if (!vec.buf[0]->owns_buffer()) {
+ vec.buf[0]->set_buffer (0, 0, true);
+ }
+ vec.buf[0]->set (msg, msglen, timestamp);
+ } else {
+ if (!vec.buf[1]->owns_buffer()) {
+ vec.buf[1]->set_buffer (0, 0, true);
+ }
+ vec.buf[1]->set (msg, msglen, timestamp);
+ }
+
+ output_fifo.increment_write_idx (1);
+
+ ret = msglen;
+
+ } else {
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time_at_cycle_start() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ if (timestamp >= _cycle_nframes) {
+ std::cerr << "attempting to write MIDI event of " << msglen << " MIDI::bytes at time "
+ << timestamp << " of " << _cycle_nframes
+ << " (this will not work - needs a code fix)"
+ << std::endl;
+ }
+
+ /* This is the process thread, which makes checking
+ * _currently_in_cycle atomic and safe, since it is only
+ * set from cycle_start() and cycle_end(), also called
+ * only from the process thread.
+ */
+
+ if (_currently_in_cycle) {
+
+ MidiBuffer& mb (get_midi_buffer (_cycle_nframes));
+
+ if (timestamp == 0) {
+ timestamp = _last_write_timestamp;
+ }
+
+ if (mb.push_back (timestamp, msglen, msg)) {
+ ret = msglen;
+ _last_write_timestamp = timestamp;
+
+ } else {
+ cerr << "AsyncMIDIPort (" << ARDOUR::Port::name() << "): write of " << msglen << " @ " << timestamp << " failed\n" << endl;
+ PBD::stacktrace (cerr, 20);
+ ret = 0;
+ }
+ } else {
+ cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
+ PBD::stacktrace (cerr, 20);
+ }
+ }
+
+ return ret;
+}
+
+
+int
+AsyncMIDIPort::read (MIDI::byte *, size_t)
+{
+ if (!ARDOUR::Port::receives_input()) {
+ return 0;
+ }
+
+ timestamp_t time;
+ Evoral::EventType type;
+ uint32_t size;
+ MIDI::byte buffer[input_fifo.capacity()];
+
+ while (input_fifo.read (&time, &type, &size, buffer)) {
+ _parser->set_timestamp (time);
+ for (uint32_t i = 0; i < size; ++i) {
+ _parser->scanner (buffer[i]);
+ }
+ }
+
+ return 0;
+}
+
+void
+AsyncMIDIPort::parse (framecnt_t)
+{
+ MIDI::byte buf[1];
+
+ /* see ::read() to realize why buf is not used */
+ read (buf, sizeof (buf));
+}
+
+void
+AsyncMIDIPort::set_process_thread (pthread_t thr)
+{
+ _process_thread = thr;
+}
+
+bool
+AsyncMIDIPort::is_process_thread()
+{
+ return pthread_equal (pthread_self(), _process_thread);
+}
+
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 622a42edf2..f4d10c2043 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -1752,7 +1752,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 ();
}
@@ -1773,7 +1773,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 ();
@@ -2039,12 +2039,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);
}
}
@@ -2367,13 +2367,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/audio_track.cc b/libs/ardour/audio_track.cc
index 0530dbfce9..a82c109cba 100644
--- a/libs/ardour/audio_track.cc
+++ b/libs/ardour/audio_track.cc
@@ -96,7 +96,7 @@ AudioTrack::set_diskstream (boost::shared_ptr<Diskstream> ds)
}
_diskstream->set_record_enabled (false);
- _diskstream->request_jack_monitors_input (false);
+ _diskstream->request_input_monitoring (false);
DiskstreamChanged (); /* EMIT SIGNAL */
}
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 5e3e5ba9e2..0ff8cd89ef 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -25,24 +25,30 @@
#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 "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/mmc.h"
-#include "midi++/manager.h"
+#include "ardour/async_midi_port.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"
#include "ardour/meter.h"
#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/mtdm.h"
#include "ardour/port.h"
#include "ardour/process_thread.h"
#include "ardour/session.h"
@@ -56,60 +62,50 @@ 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)
+ , _freewheeling (false)
, 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)
+ , _mtdm (0)
+ , _measuring_latency (false)
+ , _latency_input_port (0)
+ , _latency_output_port (0)
+ , _latency_flush_frames (0)
+ , _latency_signal_latency (0)
{
- _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
@@ -126,290 +122,7 @@ _thread_init_callback (void * /*arg*/)
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
- 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 */
+ AsyncMIDIPort::set_process_thread (pthread_self());
}
void
@@ -428,29 +141,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.
@@ -459,7 +175,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;
@@ -482,6 +197,43 @@ AudioEngine::process_callback (pframes_t nframes)
return 0;
}
+ bool return_after_remove_check = false;
+
+ if (_measuring_latency && _mtdm) {
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+
+ if (_latency_input_port && _latency_output_port) {
+ PortEngine& pe (port_engine());
+
+ Sample* in = (Sample*) pe.get_buffer (_latency_input_port, nframes);
+ Sample* out = (Sample*) pe.get_buffer (_latency_output_port, nframes);
+
+ _mtdm->process (nframes, in, out);
+ }
+
+ PortManager::cycle_end (nframes);
+ return_after_remove_check = true;
+
+ } else if (_latency_flush_frames) {
+
+ /* wait for the appropriate duration for the MTDM signal to
+ * drain from the ports before we revert to normal behaviour.
+ */
+
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+ PortManager::cycle_end (nframes);
+
+ if (_latency_flush_frames > nframes) {
+ _latency_flush_frames -= nframes;
+ } else {
+ _latency_flush_frames = 0;
+ }
+
+ return_after_remove_check = true;
+ }
+
if (session_remove_pending) {
/* perform the actual session removal */
@@ -489,7 +241,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;
@@ -518,11 +270,15 @@ AudioEngine::process_callback (pframes_t nframes)
}
}
+ if (return_after_remove_check) {
+ return 0;
+ }
+
if (_session == 0) {
if (!_freewheeling) {
- MIDI::Manager::instance()->cycle_start(nframes);
- MIDI::Manager::instance()->cycle_end();
+ PortManager::cycle_start (nframes);
+ PortManager::cycle_end (nframes);
}
_processed_frames = next_processed_frames;
@@ -533,34 +289,22 @@ AudioEngine::process_callback (pframes_t nframes)
/* tell all relevant objects that we're starting a new cycle */
InternalSend::CycleStart (nframes);
- Port::set_global_port_buffer_offset (0);
- Port::set_cycle_framecnt (nframes);
/* tell all Ports that we're starting a new cycle */
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_start (nframes);
- }
+ PortManager::cycle_start (nframes);
/* test if we are freewheeling and there are freewheel signals connected.
ardour should act normally even when freewheeling unless /it/ is
- exporting
+ exporting (which is what Freewheel.empty() tests for).
*/
if (_freewheeling && !Freewheel.empty()) {
-
Freewheel (nframes);
-
} else {
- MIDI::Manager::instance()->cycle_start(nframes);
-
if (_session) {
_session->process (nframes);
}
-
- MIDI::Manager::instance()->cycle_end();
}
if (_freewheeling) {
@@ -573,52 +317,18 @@ AudioEngine::process_callback (pframes_t nframes)
}
if (last_monitor_check + monitor_check_interval < next_processed_frames) {
-
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- bool x;
-
- if (i->second->last_monitor() != (x = i->second->jack_monitoring_input ())) {
- i->second->set_last_monitor (x);
- /* XXX I think this is dangerous, due to
- a likely mutex in the signal handlers ...
- */
- i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
- }
- }
+
+ PortManager::check_monitoring ();
last_monitor_check = next_processed_frames;
}
if (_session->silent()) {
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- if (i->second->sends_output()) {
- i->second->get_buffer(nframes).silence(nframes);
- }
- }
+ PortManager::silence (nframes);
}
if (session_remove_pending && session_removal_countdown) {
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
-
- if (i->second->sends_output()) {
-
- boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
- if (ap) {
- Sample* s = ap->engine_get_whole_audio_buffer ();
- gain_t g = session_removal_gain;
-
- for (pframes_t n = 0; n < nframes; ++n) {
- *s++ *= g;
- g -= session_removal_gain_step;
- }
- }
- }
- }
+ PortManager::fade_out (session_removal_gain, session_removal_gain_step, nframes);
if (session_removal_countdown > nframes) {
session_removal_countdown -= nframes;
@@ -629,11 +339,7 @@ AudioEngine::process_callback (pframes_t nframes)
session_removal_gain -= (nframes * session_removal_gain_step);
}
- // Finalize ports
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_end (nframes);
- }
+ PortManager::cycle_end (nframes);
_processed_frames = next_processed_frames;
@@ -642,97 +348,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 ()
@@ -757,8 +372,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;
}
@@ -775,19 +391,9 @@ AudioEngine::set_session (Session *s)
if (_session) {
- start_metering_thread ();
-
- pframes_t blocksize = jack_get_buffer_size (_jack);
+ pframes_t blocksize = samples_per_cycle ();
- /* page in as much of the session process code as we
- can before we really start running.
- */
-
- boost::shared_ptr<Ports> p = ports.reader();
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_start (blocksize);
- }
+ PortManager::cycle_start (blocksize);
_session->process (blocksize);
_session->process (blocksize);
@@ -798,9 +404,7 @@ AudioEngine::set_session (Session *s)
_session->process (blocksize);
_session->process (blocksize);
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- i->second->cycle_end (blocksize);
- }
+ PortManager::cycle_end (blocksize);
}
}
@@ -815,6 +419,7 @@ AudioEngine::remove_session ()
if (_session) {
session_remove_pending = true;
+ session_removal_countdown = 0;
session_removed.wait(_process_lock);
}
@@ -825,813 +430,622 @@ 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.
- */
-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> ();
+ drop_backend ();
+
+ try {
+ if (b->second->instantiate (arg1, arg2)) {
+ throw failed_constructor ();
}
- }
- if (!port_is_mine (portname)) {
- /* not an ardour port */
- return boost::shared_ptr<Port> ();
- }
+ _backend = b->second->backend_factory (*this);
+ _impl = b->second->portengine_factory (*this);
- 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 (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)
+/* BACKEND PROXY WRAPPERS */
+
+int
+AudioEngine::start ()
{
- RCUWriter<Ports> writer (ports);
- boost::shared_ptr<Ports> p = writer.get_copy();
- Ports::iterator x = p->find (old_relative_name);
+ if (!_backend) {
+ return -1;
+ }
+
+ if (_running) {
+ return 0;
+ }
+
+ _processed_frames = 0;
+ last_monitor_check = 0;
- if (x != p->end()) {
- boost::shared_ptr<Port> port = x->second;
- p->erase (x);
- p->insert (make_pair (new_relative_name, port));
+ if (_backend->start()) {
+ return -1;
}
-}
-const char **
-AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags)
-{
- 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;
+ _running = true;
+
+ if (_session) {
+ _session->set_frame_rate (_backend->sample_rate());
+
+ if (_session->config.get_jack_time_master()) {
+ _backend->set_time_master (true);
}
}
- return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
+
+ start_metering_thread ();
+
+ Running(); /* EMIT SIGNAL */
+
+ return 0;
}
-void
-AudioEngine::halted_info (jack_status_t code, const char* reason, void *arg)
+int
+AudioEngine::stop ()
{
- /* called from jack shutdown handler */
-
- AudioEngine* ae = static_cast<AudioEngine *> (arg);
- bool was_running = ae->_running;
-
- ae->stop_metering_thread ();
+ if (!_backend) {
+ return 0;
+ }
- ae->_running = false;
- ae->_buffer_size = 0;
- ae->_frame_rate = 0;
- ae->_jack = 0;
+ Glib::Threads::Mutex::Lock lm (_process_lock);
- 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
- }
+ if (_backend->stop ()) {
+ return -1;
+ }
+
+ _running = false;
+ _processed_frames = 0;
+ _measuring_latency = false;
+ _latency_output_port = 0;
+ _latency_input_port = 0;
+ stop_metering_thread ();
+
+ Port::PortDrop ();
+ Stopped (); /* EMIT SIGNAL */
+
+ return 0;
}
-void
-AudioEngine::halted (void *arg)
+int
+AudioEngine::pause ()
{
- cerr << "HALTED by JACK\n";
+ if (!_backend) {
+ return 0;
+ }
+
+ if (_backend->pause ()) {
+ return -1;
+ }
- /* called from jack shutdown handler */
+ _running = false;
+
+ Stopped(); /* EMIT SIGNAL */
+ return 0;
+}
- AudioEngine* ae = static_cast<AudioEngine *> (arg);
- bool was_running = ae->_running;
+int
+AudioEngine::freewheel (bool start_stop)
+{
+ if (!_backend) {
+ return -1;
+ }
- ae->stop_metering_thread ();
+ /* _freewheeling will be set when first Freewheel signal occurs */
- ae->_running = false;
- ae->_buffer_size = 0;
- ae->_frame_rate = 0;
- ae->_jack = 0;
+ return _backend->freewheel (start_stop);
+}
- if (was_running) {
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
- ae->Halted(""); /* EMIT SIGNAL */
+float
+AudioEngine::get_cpu_load() const
+{
+ if (!_backend) {
+ return 0.0;
}
+ return _backend->cpu_load ();
}
-void
-AudioEngine::died ()
+bool
+AudioEngine::is_realtime() const
{
- /* called from a signal handler for SIGPIPE */
-
- stop_metering_thread ();
+ if (!_backend) {
+ return false;
+ }
- _running = false;
- _buffer_size = 0;
- _frame_rate = 0;
- _jack = 0;
+ return _backend->is_realtime();
}
bool
-AudioEngine::can_request_hardware_monitoring ()
+AudioEngine::connected() const
{
- GET_PRIVATE_JACK_POINTER_RET (_jack,false);
- const char ** ports;
-
- if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+ if (!_backend) {
return false;
}
- free (ports);
-
- return true;
+ return _backend->connected();
}
-ChanCount
-AudioEngine::n_physical (unsigned long flags) const
+void
+AudioEngine::transport_start ()
{
- 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;
- }
-
- 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) {
+ return;
}
-
- free (ports);
-
- return c;
+ return _backend->transport_start ();
}
-ChanCount
-AudioEngine::n_physical_inputs () const
+void
+AudioEngine::transport_stop ()
{
- return n_physical (JackPortIsInput);
+ if (!_backend) {
+ return;
+ }
+ return _backend->transport_stop ();
}
-ChanCount
-AudioEngine::n_physical_outputs () const
+TransportState
+AudioEngine::transport_state ()
{
- return n_physical (JackPortIsOutput);
+ if (!_backend) {
+ return TransportStopped;
+ }
+ return _backend->transport_state ();
}
void
-AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy)
+AudioEngine::transport_locate (framepos_t pos)
{
- GET_PRIVATE_JACK_POINTER (_jack);
- const char ** ports;
-
- if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) {
+ if (!_backend) {
return;
}
-
- 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->transport_locate (pos);
}
-/** 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)
+framepos_t
+AudioEngine::transport_frame()
{
- get_physical (type, JackPortIsOutput, ins);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->transport_frame ();
}
-/** 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)
+framecnt_t
+AudioEngine::sample_rate () const
{
- get_physical (type, JackPortIsInput, outs);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->sample_rate ();
}
-void
-AudioEngine::transport_stop ()
+pframes_t
+AudioEngine::samples_per_cycle () const
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_stop (_priv_jack);
+ if (!_backend) {
+ return 0;
+ }
+ return _backend->buffer_size ();
}
-void
-AudioEngine::transport_start ()
+int
+AudioEngine::usecs_per_cycle () const
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_start (_priv_jack);
+ if (!_backend) {
+ return -1;
+ }
+ return _backend->usecs_per_cycle ();
}
-void
-AudioEngine::transport_locate (framepos_t where)
+size_t
+AudioEngine::raw_buffer_size (DataType t)
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_transport_locate (_priv_jack, where);
+ if (!_backend) {
+ return -1;
+ }
+ return _backend->raw_buffer_size (t);
}
-AudioEngine::TransportState
-AudioEngine::transport_state ()
+pframes_t
+AudioEngine::sample_time ()
{
- 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->sample_time ();
}
-int
-AudioEngine::reset_timebase ()
+pframes_t
+AudioEngine::sample_time_at_cycle_start ()
{
- 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_time_at_cycle_start ();
}
-int
-AudioEngine::freewheel (bool onoff)
+pframes_t
+AudioEngine::samples_since_cycle_start ()
{
- 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->samples_since_cycle_start ();
}
-void
-AudioEngine::remove_all_ports ()
+bool
+AudioEngine::get_sync_offset (pframes_t& offset) 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 false;
}
-
- /* clear dead wood list in RCU */
-
- ports.flush ();
-
- port_remove_in_progress = false;
+ return _backend->get_sync_offset (offset);
}
int
-AudioEngine::connect_to_jack (string client_name, string session_uuid)
+AudioEngine::create_process_thread (boost::function<void()> func, pthread_t* thr, size_t stacksize)
{
- 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->create_process_thread (func, thr, stacksize);
+}
- GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
- if (status & JackNameNotUnique) {
- jack_client_name = jack_get_client_name (_priv_jack);
+int
+AudioEngine::set_device_name (const std::string& name)
+{
+ if (!_backend) {
+ return -1;
}
-
- return 0;
+ return _backend->set_device_name (name);
}
int
-AudioEngine::disconnect_from_jack ()
+AudioEngine::set_sample_rate (float sr)
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
-
- if (_running) {
- stop_metering_thread ();
- }
-
- {
- Glib::Threads::Mutex::Lock lm (_process_lock);
- jack_client_close (_priv_jack);
- _jack = 0;
- }
-
- _buffer_size = 0;
- _frame_rate = 0;
- _raw_buffer_sizes.clear();
-
- if (_running) {
- _running = false;
- MIDI::JackMIDIPort::JackHalted (); /* EMIT SIGNAL */
- Stopped(); /* EMIT SIGNAL */
+ if (!_backend) {
+ return -1;
}
-
- return 0;
+ return _backend->set_sample_rate (sr);
}
int
-AudioEngine::reconnect_to_jack ()
+AudioEngine::set_buffer_size (uint32_t bufsiz)
{
- 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->set_buffer_size (bufsiz);
+}
- Ports::iterator i;
-
- boost::shared_ptr<Ports> p = ports.reader ();
-
- for (i = p->begin(); i != p->end(); ++i) {
- if (i->second->reestablish ()) {
- break;
- }
+int
+AudioEngine::set_sample_format (SampleFormat sf)
+{
+ if (!_backend) {
+ return -1;
}
+ return _backend->set_sample_format (sf);
+}
- if (i != p->end()) {
- /* failed */
- remove_all_ports ();
+int
+AudioEngine::set_interleaved (bool yn)
+{
+ if (!_backend) {
return -1;
}
+ return _backend->set_interleaved (yn);
+}
- 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));
+int
+AudioEngine::set_input_channels (uint32_t ic)
+{
+ if (!_backend) {
+ return -1;
}
+ return _backend->set_input_channels (ic);
+}
- last_monitor_check = 0;
-
- set_jack_callbacks ();
-
- if (jack_activate (_priv_jack) == 0) {
- _running = true;
- _has_run = true;
- } else {
+int
+AudioEngine::set_output_channels (uint32_t oc)
+{
+ if (!_backend) {
return -1;
}
+ return _backend->set_output_channels (oc);
+}
- /* re-establish connections */
-
- for (i = p->begin(); i != p->end(); ++i) {
- i->second->reconnect ();
+int
+AudioEngine::set_systemic_input_latency (uint32_t il)
+{
+ if (!_backend) {
+ return -1;
}
-
- MIDI::Manager::instance()->reconnect ();
-
- Running (); /* EMIT SIGNAL*/
-
- start_metering_thread ();
-
- return 0;
+ return _backend->set_systemic_input_latency (il);
}
int
-AudioEngine::request_buffer_size (pframes_t nframes)
+AudioEngine::set_systemic_output_latency (uint32_t ol)
{
- 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_systemic_output_latency (ol);
}
-string
-AudioEngine::make_port_name_relative (string portname) const
+/* END OF BACKEND PROXY API */
+
+void
+AudioEngine::thread_init_callback (void* arg)
{
- string::size_type len;
- string::size_type n;
+ /* make sure that anybody who needs to know about this thread
+ knows about it.
+ */
- len = portname.length();
+ pthread_set_name (X_("audioengine"));
- for (n = 0; n < len; ++n) {
- if (portname[n] == ':') {
- break;
- }
- }
+ PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("AudioEngine"), 4096);
+ PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("AudioEngine"), 128);
- if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
- return portname.substr (n+1);
- }
+ SessionEvent::create_per_thread_pool (X_("AudioEngine"), 512);
- return portname;
+ AsyncMIDIPort::set_process_thread (pthread_self());
+
+ if (arg) {
+ /* the special thread created/managed by the backend */
+ AudioEngine::instance()->_main_thread = new ProcessThread;
+ }
}
-string
-AudioEngine::make_port_name_non_relative (string portname) const
+int
+AudioEngine::sync_callback (TransportState state, framepos_t position)
{
- string str;
-
- if (portname.find_first_of (':') != string::npos) {
- return portname;
+ if (_session) {
+ return _session->backend_sync_callback (state, position);
}
+ return 0;
+}
- str = jack_client_name;
- str += ':';
- str += portname;
-
- return str;
+void
+AudioEngine::freewheel_callback (bool onoff)
+{
+ _freewheeling = onoff;
}
-bool
-AudioEngine::port_is_mine (const string& portname) const
+void
+AudioEngine::latency_callback (bool for_playback)
{
- if (portname.find_first_of (':') != string::npos) {
- if (portname.substr (0, jack_client_name.length ()) != jack_client_name) {
- return false;
- }
+ if (_session) {
+ _session->update_latency (for_playback);
}
- return true;
}
-bool
-AudioEngine::is_realtime () const
+void
+AudioEngine::update_latencies ()
{
- GET_PRIVATE_JACK_POINTER_RET (_jack,false);
- return jack_is_realtime (_priv_jack);
+ if (_backend) {
+ _backend->update_latencies ();
+ }
}
-int
-AudioEngine::create_process_thread (boost::function<void()> f, jack_native_thread_t* thread, size_t stacksize)
+void
+AudioEngine::halted_callback (const char* why)
{
- GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
- ThreadData* td = new ThreadData (this, f, stacksize);
+ stop_metering_thread ();
+ _running = false;
- 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;
+ Port::PortDrop (); /* EMIT SIGNAL */
+ Halted (why); /* EMIT SIGNAL */
}
bool
-AudioEngine::stop_process_thread (jack_native_thread_t thread)
+AudioEngine::setup_required () const
{
- /**
- * can't use GET_PRIVATE_JACK_POINTER_RET (_jack, 0) here
- * because _jack is 0 when this is called. At least for
- * Jack 2 _jack arg is not used so it should be OK
- */
-
-#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined(PLATFORM_WINDOWS)
- if (jack_client_stop_thread (_jack, thread) != 0) {
- error << "AudioEngine: cannot stop process thread" << endmsg;
+ if (_backends.size() == 1 && _backends.begin()->second->already_configured()) {
return false;
}
-#else
- void* status;
- pthread_join (thread, &status);
-#endif
return true;
}
-void*
-AudioEngine::_start_process_thread (void* arg)
+MTDM*
+AudioEngine::mtdm()
{
- ThreadData* td = reinterpret_cast<ThreadData*> (arg);
- boost::function<void()> f = td->f;
- delete td;
-
- f ();
-
- return 0;
+ return _mtdm;
}
-bool
-AudioEngine::port_is_physical (const std::string& portname) const
+void
+AudioEngine::start_latency_detection ()
{
- GET_PRIVATE_JACK_POINTER_RET(_jack, false);
+ PortEngine& pe (port_engine());
- jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
+ delete _mtdm;
+ _mtdm = 0;
- if (!port) {
- return false;
- }
+ /* create the ports we will use to read/write data */
+
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
+ return;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ return;
+ }
- return jack_port_flags (port) & JackPortIsPhysical;
-}
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
+ pe.unregister_port (_latency_output_port);
+ return;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_output_port);
+ return;
+ }
-void
-AudioEngine::request_jack_monitors_input (const std::string& portname, bool yn) const
-{
- GET_PRIVATE_JACK_POINTER(_jack);
+ LatencyRange lr;
+ _latency_signal_latency = 0;
+ lr = pe.get_latency_range (_latency_input_port, false);
+ _latency_signal_latency = lr.max;
+ lr = pe.get_latency_range (_latency_output_port, true);
+ _latency_signal_latency += lr.max;
- jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
+ cerr << "latency signal pathway = " << _latency_signal_latency << endl;
+
+ /* all created and connected, lets go */
+
+ _mtdm = new MTDM (sample_rate());
+ _measuring_latency = true;
+ _latency_flush_frames = samples_per_cycle();
- if (!port) {
- return;
- }
- jack_port_request_monitor (port, yn);
}
void
-AudioEngine::update_latencies ()
+AudioEngine::stop_latency_detection ()
{
- GET_PRIVATE_JACK_POINTER (_jack);
- jack_recompute_total_latencies (_priv_jack);
+ port_engine().unregister_port (_latency_output_port);
+ port_engine().unregister_port (_latency_input_port);
+ _measuring_latency = false;
}
void
-AudioEngine::destroy ()
+AudioEngine::set_latency_output_port (const string& name)
{
- delete _instance;
- _instance = 0;
+ _latency_output_name = name;
}
+void
+AudioEngine::set_latency_input_port (const string& name)
+{
+ _latency_input_name = name;
+}
diff --git a/libs/ardour/backend_search_path.cc b/libs/ardour/backend_search_path.cc
new file mode 100644
index 0000000000..4c5ff40e7d
--- /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/buffer_set.cc b/libs/ardour/buffer_set.cc
index 184e23d1af..e67aee6be7 100644
--- a/libs/ardour/buffer_set.cc
+++ b/libs/ardour/buffer_set.cc
@@ -90,7 +90,7 @@ BufferSet::clear()
/** Set up this BufferSet so that its data structures mirror a PortSet's buffers.
* This is quite expensive and not RT-safe, so it should not be called in a process context;
- * get_jack_port_addresses() will fill in a structure set up by this method.
+ * get_backend_port_addresses() will fill in a structure set up by this method.
*
* XXX: this *is* called in a process context; I'm not sure quite what `should not' means above.
*/
@@ -114,13 +114,13 @@ BufferSet::attach_buffers (PortSet& ports)
_is_mirror = true;
}
-/** Write the JACK port addresses from a PortSet into our data structures. This
+/** Write the backend port addresses from a PortSet into our data structures. This
* call assumes that attach_buffers() has already been called for the same PortSet.
* Does not allocate, so RT-safe BUT you can only call Port::get_buffer() from
* the process() callback tree anyway, so this has to be called in RT context.
*/
void
-BufferSet::get_jack_port_addresses (PortSet& ports, framecnt_t nframes)
+BufferSet::get_backend_port_addresses (PortSet& ports, framecnt_t nframes)
{
assert (_count == ports.count ());
assert (_available == ports.count ());
diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc
index 0ac62d7076..834a98d347 100644
--- a/libs/ardour/bundle.cc
+++ b/libs/ardour/bundle.cc
@@ -443,27 +443,26 @@ Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine)
return true;
}
-/** This must not be called in code executed as a response to a JACK event,
- * as it uses jack_port_get_all_connections().
+/** This must not be called in code executed as a response to a backend event,
+ * as it uses the backend port_get_all_connections().
* @return true if any of this bundle's channels are connected to anything.
*/
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/debug.cc b/libs/ardour/debug.cc
index 51115001cb..dc762299e9 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -61,5 +61,6 @@ uint64_t PBD::DEBUG::TempoMap = PBD::new_debug_bit ("tempomap");
uint64_t PBD::DEBUG::OrderKeys = PBD::new_debug_bit ("orderkeys");
uint64_t PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
uint64_t PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
+uint64_t PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc
index dfbe4c960a..79c44ce94a 100644
--- a/libs/ardour/delivery.cc
+++ b/libs/ardour/delivery.cc
@@ -245,7 +245,7 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf
processing pathway that wants to use this->output_buffers() for some reason.
*/
- output_buffers().get_jack_port_addresses (ports, nframes);
+ output_buffers().get_backend_port_addresses (ports, nframes);
// this Delivery processor is not a derived type, and thus we assume
// we really can modify the buffers passed in (it is almost certainly
diff --git a/libs/ardour/directory_names.cc b/libs/ardour/directory_names.cc
index 6fb15eaabd..f01c024435 100644
--- a/libs/ardour/directory_names.cc
+++ b/libs/ardour/directory_names.cc
@@ -38,6 +38,7 @@ const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
const char* const ladspa_dir_name = X_("ladspa");
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 127546e8fc..2c0c44033d 100644
--- a/libs/ardour/export_graph_builder.cc
+++ b/libs/ardour/export_graph_builder.cc
@@ -56,7 +56,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 ()
@@ -507,7 +507,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 1406bbd43a..8646b819f3 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -35,6 +35,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <time.h>
#ifdef WINDOWS_VST_SUPPORT
#include <fst.h>
@@ -74,11 +75,11 @@
#include "pbd/basename.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "midi++/mmc.h"
#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"
@@ -86,6 +87,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/filesystem_paths.h"
#include "ardour/midi_region.h"
+#include "ardour/midiport_manager.h"
#include "ardour/mix.h"
#include "ardour/panner_manager.h"
#include "ardour/plugin_manager.h"
@@ -342,6 +344,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 ();
+
libardour_initialized = true;
return true;
@@ -350,9 +354,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
void
ARDOUR::init_post_engine ()
{
- /* the MIDI Manager is needed by the ControlProtocolManager */
- MIDI::Manager::create (AudioEngine::instance()->jack());
-
ControlProtocolManager::instance().discover_control_protocols ();
XMLNode* node;
@@ -550,3 +551,43 @@ ARDOUR::get_available_sync_options ()
return ret;
}
+
+/** Return a monotonic value for the number of microseconds that have elapsed
+ * since an arbitrary zero origin.
+ */
+
+#ifdef __MACH__
+/* Thanks Apple for not implementing this basic SUSv2, POSIX.1-2001 function
+ */
+#include <mach/mach_time.h>
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 0
+int
+clock_gettime (int /*clk_id*/, struct timespec *t)
+{
+ static bool initialized = false;
+ static mach_timebase_info_data_t timebase;
+ if (!initialized) {
+ mach_timebase_info(&timebase);
+ initialized = true;
+ }
+ uint64_t time;
+ time = mach_absolute_time();
+ double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom);
+ double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9);
+ t->tv_sec = seconds;
+ t->tv_nsec = nseconds;
+ return 0;
+}
+#endif
+
+microseconds_t
+ARDOUR::get_microseconds ()
+{
+ struct timespec ts;
+ if (clock_gettime (CLOCK_MONOTONIC, &ts) != 0) {
+ /* EEEK! */
+ return 0;
+ }
+ return (microseconds_t) ts.tv_sec * 1000000 + (ts.tv_nsec/1000);
+}
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 a9d37d4315..ebd295411e 100644
--- a/libs/ardour/io.cc
+++ b/libs/ardour/io.cc
@@ -1344,7 +1344,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;
@@ -1378,7 +1378,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);
std::vector<char> buf1(name_size+1);
std::vector<char> buf2(name_size+1);
@@ -1411,7 +1411,7 @@ IO::find_port_hole (const char* base)
*/
for (n = 1; n < 9999; ++n) {
- std::vector<char> buf(jack_port_name_size());
+ std::vector<char> buf (AudioEngine::instance()->port_name_size());
PortSet::iterator i = _ports.begin();
snprintf (&buf[0], jack_port_name_size(), _("%s %u"), base, n);
@@ -1645,7 +1645,7 @@ IO::process_input (boost::shared_ptr<Processor> proc, framepos_t start_frame, fr
return;
}
- _buffers.get_jack_port_addresses (_ports, nframes);
+ _buffers.get_backend_port_addresses (_ports, nframes);
if (proc) {
proc->run (_buffers, start_frame, end_frame, nframes, true);
}
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/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc
index 78e16f9941..8b089929b5 100644
--- a/libs/ardour/ladspa_plugin.cc
+++ b/libs/ardour/ladspa_plugin.cc
@@ -44,8 +44,6 @@
#include "pbd/xml++.h"
#include "pbd/stacktrace.h"
-#include "midi++/manager.h"
-
#include "ardour/session.h"
#include "ardour/ladspa_plugin.h"
#include "ardour/buffer_set.h"
@@ -129,7 +127,9 @@ LadspaPlugin::init (string module_path, uint32_t index, framecnt_t rate)
port_cnt = parameter_count();
_control_data = new LADSPA_Data[port_cnt];
+ memset (_control_data, 0, sizeof (LADSPA_Data) * port_cnt);
_shadow_data = new LADSPA_Data[port_cnt];
+ memset (_shadow_data, 0, sizeof (LADSPA_Data) * port_cnt);
for (i = 0; i < port_cnt; ++i) {
if (LADSPA_IS_PORT_CONTROL(port_descriptor (i))) {
diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc
index ac93ccdb40..eb168c9554 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);
}
}
@@ -150,9 +150,9 @@ LTC_Slave::reset()
}
void
-LTC_Slave::parse_ltc(const jack_nframes_t nframes, const jack_default_audio_sample_t * const in, const ARDOUR::framecnt_t posinfo)
+LTC_Slave::parse_ltc(const pframes_t nframes, const Sample* const in, const framecnt_t posinfo)
{
- jack_nframes_t i;
+ pframes_t i;
unsigned char sound[8192];
if (nframes > 8192) {
/* TODO warn once or wrap, loop conversion below
@@ -413,22 +413,22 @@ LTC_Slave::init_engine_dll (framepos_t pos, int32_t inc)
}
/* main entry point from session_process.cc
- * called from jack_process callback context
- * so it is OK to use jack_port_get_buffer()
+ * called from process callback context
+ * so it is OK to use get_buffer()
*/
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 30134a5dbb..6288616824 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -291,7 +291,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;
@@ -1925,7 +1925,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..d75b861ea1 100644
--- a/libs/ardour/midi_buffer.cc
+++ b/libs/ardour/midi_buffer.cc
@@ -22,6 +22,7 @@
#include "pbd/malign.h"
#include "pbd/compose.h"
#include "pbd/debug.h"
+#include "pbd/stacktrace.h"
#include "ardour/debug.h"
#include "ardour/midi_buffer.h"
@@ -133,6 +134,7 @@ MidiBuffer::push_back(const Evoral::MIDIEvent<TimeType>& ev)
if (_size + stamp_size + ev.size() >= _capacity) {
cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+ PBD::stacktrace (cerr, 20);
return false;
}
@@ -171,7 +173,9 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data)
#endif
if (_size + stamp_size + size >= _capacity) {
- cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+ cerr << "MidiBuffer::push_back2 failed (buffer is full; _size = " << _size << " capacity "
+ << _capacity << " stamp " << stamp_size << " size = " << size << ")" << endl;
+ PBD::stacktrace (cerr, 20);
return false;
}
@@ -190,55 +194,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_clock_slave.cc b/libs/ardour/midi_clock_slave.cc
index 75f3c13f1c..a52854d954 100644
--- a/libs/ardour/midi_clock_slave.cc
+++ b/libs/ardour/midi_clock_slave.cc
@@ -30,6 +30,8 @@
#include "midi++/port.h"
#include "ardour/debug.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/slave.h"
#include "ardour/tempo.h"
@@ -40,7 +42,7 @@ using namespace ARDOUR;
using namespace MIDI;
using namespace PBD;
-MIDIClock_Slave::MIDIClock_Slave (Session& s, MIDI::Port& p, int ppqn)
+MIDIClock_Slave::MIDIClock_Slave (Session& s, MidiPort& p, int ppqn)
: ppqn (ppqn)
, bandwidth (10.0 / 60.0) // 1 BpM = 1 / 60 Hz
{
@@ -63,19 +65,18 @@ MIDIClock_Slave::~MIDIClock_Slave()
}
void
-MIDIClock_Slave::rebind (MIDI::Port& p)
+MIDIClock_Slave::rebind (MidiPort& port)
{
- port_connections.drop_connections();
+ DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_Slave: connecting to port %1\n", port.name()));
- port = &p;
+ port_connections.drop_connections ();
- DEBUG_TRACE (DEBUG::MidiClock, string_compose ("MIDIClock_Slave: connecting to port %1\n", port->name()));
+ port.self_parser().timing.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::update_midi_clock, this, _1, _2));
+ port.self_parser().start.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::start, this, _1, _2));
+ port.self_parser().contineu.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::contineu, this, _1, _2));
+ port.self_parser().stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::stop, this, _1, _2));
+ port.self_parser().position.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::position, this, _1, _2, 3));
- port->parser()->timing.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::update_midi_clock, this, _1, _2));
- port->parser()->start.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::start, this, _1, _2));
- port->parser()->contineu.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::contineu, this, _1, _2));
- port->parser()->stop.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::stop, this, _1, _2));
- port->parser()->position.connect_same_thread (port_connections, boost::bind (&MIDIClock_Slave::position, this, _1, _2, 3));
}
void
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index cf143bf595..39932db8bf 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -201,8 +201,8 @@ MidiDiskstream::non_realtime_input_change ()
seek (_session.transport_frame());
}
- g_atomic_int_set(&_frames_pending_write, 0);
- g_atomic_int_set(&_num_captured_loops, 0);
+ g_atomic_int_set(const_cast<gint*> (&_frames_pending_write), 0);
+ g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
}
int
@@ -375,8 +375,8 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
}
_write_source->mark_write_starting_now(
capture_start_frame, capture_captured, loop_length);
- g_atomic_int_set(&_frames_pending_write, 0);
- g_atomic_int_set(&_num_captured_loops, 0);
+ g_atomic_int_set(const_cast<gint*> (&_frames_pending_write), 0);
+ g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
was_recording = true;
}
}
@@ -445,7 +445,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
break;
}
}
- g_atomic_int_add(&_frames_pending_write, nframes);
+ g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), nframes);
if (buf.size() != 0) {
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
@@ -798,7 +798,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
return 0;
}
- const framecnt_t total = g_atomic_int_get(&_frames_pending_write);
+ const framecnt_t total = g_atomic_int_get(const_cast<gint*> (&_frames_pending_write));
if (total == 0 ||
_capture_buf->read_space() == 0 ||
@@ -833,7 +833,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush)
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg;
return -1;
}
- g_atomic_int_add(&_frames_pending_write, -to_write);
+ g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), -to_write);
}
out:
@@ -1043,7 +1043,7 @@ MidiDiskstream::transport_looped (framepos_t)
the Source and/or entirely after the capture is finished.
*/
if (was_recording) {
- g_atomic_int_add(&_num_captured_loops, 1);
+ g_atomic_int_add(const_cast<gint*> (&_num_captured_loops), 1);
}
}
@@ -1110,7 +1110,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;
@@ -1258,12 +1258,12 @@ MidiDiskstream::allocate_temporary_buffers ()
}
void
-MidiDiskstream::ensure_jack_monitors_input (bool yn)
+MidiDiskstream::ensure_input_monitoring (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..de2263fad6 100644
--- a/libs/ardour/midi_port.cc
+++ b/libs/ardour/midi_port.cc
@@ -26,11 +26,14 @@
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)
, _input_active (true)
+ , _always_parse (false)
{
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
}
@@ -43,12 +46,32 @@ MidiPort::~MidiPort()
void
MidiPort::cycle_start (pframes_t nframes)
{
+ framepos_t now = AudioEngine::instance()->sample_time_at_cycle_start();
+
Port::cycle_start (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));
+ }
+
+ if (_always_parse) {
+ MidiBuffer& mb (get_midi_buffer (nframes));
+
+ /* dump incoming MIDI to parser */
+
+ for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
+ uint8_t* buf = (*b).buffer();
+
+ _self_parser.set_timestamp (now + (*b).time());
+
+ uint32_t limit = (*b).size();
+
+ for (size_t n = 0; n < limit; ++n) {
+ _self_parser.scanner (buf[n]);
+ }
+ }
}
}
@@ -63,31 +86,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);
-
- /* suck all relevant MIDI events from the JACK MIDI port 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 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 +146,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 +157,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 +174,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 +191,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;
}
@@ -202,6 +227,7 @@ MidiPort::reset ()
{
Port::reset ();
delete _buffer;
+ cerr << name() << " new MIDI buffer of size " << AudioEngine::instance()->raw_buffer_size (DataType::MIDI) << endl;
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
}
@@ -210,3 +236,9 @@ MidiPort::set_input_active (bool yn)
{
_input_active = yn;
}
+
+void
+MidiPort::set_always_parse (bool yn)
+{
+ _always_parse = yn;
+}
diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc
index 5822cc509e..e6d49175bd 100644
--- a/libs/ardour/midi_ui.cc
+++ b/libs/ardour/midi_ui.cc
@@ -22,11 +22,11 @@
#include "pbd/pthread_utils.h"
-#include "midi++/manager.h"
-#include "midi++/port.h"
-
+#include "ardour/async_midi_port.h"
#include "ardour/debug.h"
#include "ardour/audioengine.h"
+#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.h"
#include "ardour/midi_ui.h"
#include "ardour/session.h"
#include "ardour/session_event.h"
@@ -48,7 +48,6 @@ MidiControlUI::MidiControlUI (Session& s)
: AbstractUI<MidiUIRequest> (X_("midiui"))
, _session (s)
{
- MIDI::Manager::instance()->PortsChanged.connect_same_thread (rebind_connection, boost::bind (&MidiControlUI::change_midi_ports, this));
_instance = this;
}
@@ -83,20 +82,10 @@ MidiControlUI::do_request (MidiUIRequest* req)
}
}
-void
-MidiControlUI::change_midi_ports ()
-{
- MidiUIRequest* req = get_request (PortChange);
- if (req == 0) {
- return;
- }
- send_request (req);
-}
-
bool
-MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
+MidiControlUI::midi_input_handler (IOCondition ioc, AsyncMIDIPort* port)
{
- DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", port->name()));
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", ((ARDOUR::Port*)port)->name()));
if (ioc & ~IO_IN) {
return false;
@@ -108,8 +97,8 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
CrossThreadChannel::drain (port->selectable());
#endif
- DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
- framepos_t now = _session.engine().frame_time();
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", ((ARDOUR::Port*)port)->name()));
+ framepos_t now = _session.engine().sample_time();
port->parse (now);
}
@@ -130,22 +119,19 @@ MidiControlUI::clear_ports ()
void
MidiControlUI::reset_ports ()
{
- clear_ports ();
-
- boost::shared_ptr<const MIDI::Manager::PortList> plist = MIDI::Manager::instance()->get_midi_ports ();
+ if (port_sources.empty()) {
+ AsyncMIDIPort* async = dynamic_cast<AsyncMIDIPort*> (_session.midi_input_port());
- for (MIDI::Manager::PortList::const_iterator i = plist->begin(); i != plist->end(); ++i) {
-
- if (!(*i)->centrally_parsed()) {
- continue;
+ if (!async) {
+ return;
}
int fd;
- if ((fd = (*i)->selectable ()) >= 0) {
+ if ((fd = async->selectable ()) >= 0) {
Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
-
- psrc->connect (sigc::bind (sigc::mem_fun (this, &MidiControlUI::midi_input_handler), *i));
+
+ psrc->connect (sigc::bind (sigc::mem_fun (this, &MidiControlUI::midi_input_handler), async));
psrc->attach (_main_loop->get_context());
// glibmm hack: for now, store only the GSource*
diff --git a/libs/ardour/midiport_manager.cc b/libs/ardour/midiport_manager.cc
new file mode 100644
index 0000000000..d17401cee8
--- /dev/null
+++ b/libs/ardour/midiport_manager.cc
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-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.
+
+ $Id$
+*/
+
+#include "ardour/audioengine.h"
+#include "ardour/async_midi_port.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/rc_configuration.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace MIDI;
+using namespace PBD;
+
+
+MidiPortManager::MidiPortManager ()
+{
+ create_ports ();
+}
+
+MidiPortManager::~MidiPortManager ()
+{
+ if (_midi_in) {
+ AudioEngine::instance()->unregister_port (_midi_in);
+ }
+ if (_midi_in) {
+ AudioEngine::instance()->unregister_port (_midi_in);
+ }
+ if (_mtc_input_port) {
+ AudioEngine::instance()->unregister_port (_mtc_input_port);
+ }
+ if (_mtc_output_port) {
+ AudioEngine::instance()->unregister_port (_mtc_output_port);
+ }
+ if (_midi_clock_input_port) {
+ AudioEngine::instance()->unregister_port (_midi_clock_input_port);
+ }
+ if (_midi_clock_output_port) {
+ AudioEngine::instance()->unregister_port (_midi_clock_output_port);
+ }
+
+}
+
+void
+MidiPortManager::create_ports ()
+{
+ /* this method is idempotent
+ */
+
+ if (_midi_in) {
+ return;
+ }
+
+ _midi_in = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MIDI control in"), true);
+ _midi_out = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MIDI control out"), true);
+
+ _mmc_in = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MMC in"), true);
+ _mmc_out = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MMC out"), true);
+
+ /* XXX nasty type conversion needed because of the mixed inheritance
+ * required to integrate MIDI::IPMidiPort and ARDOUR::AsyncMIDIPort.
+ *
+ * At some point, we'll move IPMidiPort into Ardour and make it
+ * inherit from ARDOUR::MidiPort not MIDI::Port, and then this
+ * mess can go away
+ */
+
+ _midi_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_in).get();
+ _midi_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out).get();
+
+ _mmc_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_mmc_in).get();
+ _mmc_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_mmc_out).get();
+
+ /* Now register ports used for sync (MTC and MIDI Clock)
+ */
+
+ boost::shared_ptr<ARDOUR::Port> p;
+
+ p = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MTC in"));
+ _mtc_input_port = boost::dynamic_pointer_cast<MidiPort> (p);
+ p = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MTC out"));
+ _mtc_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
+
+ p = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MIDI Clock in"));
+ _midi_clock_input_port = boost::dynamic_pointer_cast<MidiPort> (p);
+ p = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MIDI Clock out"));
+ _midi_clock_output_port= boost::dynamic_pointer_cast<MidiPort> (p);
+
+ /* These ports all need their incoming data handled in
+ * Port::cycle_start() and so ...
+ */
+
+ _mtc_input_port->set_always_parse (true);
+ _mtc_output_port->set_always_parse (true);
+ _midi_clock_input_port->set_always_parse (true);
+ _midi_clock_output_port->set_always_parse (true);
+}
+
+void
+MidiPortManager::set_midi_port_states (const XMLNodeList&nodes)
+{
+ XMLProperty* prop;
+ typedef map<std::string,boost::shared_ptr<Port> > PortMap;
+ PortMap ports;
+ const int version = 0;
+
+ ports.insert (make_pair (_mtc_input_port->name(), _mtc_input_port));
+ ports.insert (make_pair (_mtc_output_port->name(), _mtc_output_port));
+ ports.insert (make_pair (_midi_clock_input_port->name(), _midi_clock_input_port));
+ ports.insert (make_pair (_midi_clock_output_port->name(), _midi_clock_output_port));
+ ports.insert (make_pair (_midi_input_port->name(), _midi_in));
+ ports.insert (make_pair (_midi_output_port->name(), _midi_out));
+ ports.insert (make_pair (_mmc_input_port->name(), _mmc_in));
+ ports.insert (make_pair (_mmc_output_port->name(), _mmc_out));
+
+ for (XMLNodeList::const_iterator n = nodes.begin(); n != nodes.end(); ++n) {
+ if ((prop = (*n)->property (X_("name"))) == 0) {
+ continue;
+ }
+
+ PortMap::iterator p = ports.find (prop->value());
+ if (p == ports.end()) {
+ continue;
+ }
+
+ p->second->set_state (**n, version);
+ }
+}
+
+list<XMLNode*>
+MidiPortManager::get_midi_port_states () const
+{
+ typedef map<std::string,boost::shared_ptr<Port> > PortMap;
+ PortMap ports;
+ list<XMLNode*> s;
+
+ ports.insert (make_pair (_mtc_input_port->name(), _mtc_input_port));
+ ports.insert (make_pair (_mtc_output_port->name(), _mtc_output_port));
+ ports.insert (make_pair (_midi_clock_input_port->name(), _midi_clock_input_port));
+ ports.insert (make_pair (_midi_clock_output_port->name(), _midi_clock_output_port));
+ ports.insert (make_pair (_midi_input_port->name(), _midi_in));
+ ports.insert (make_pair (_midi_output_port->name(), _midi_out));
+ ports.insert (make_pair (_mmc_input_port->name(), _mmc_in));
+ ports.insert (make_pair (_mmc_output_port->name(), _mmc_out));
+
+ for (PortMap::const_iterator p = ports.begin(); p != ports.end(); ++p) {
+ s.push_back (&p->second->get_state());
+ }
+
+ return s;
+}
+
+
diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc
index c41c6d6c94..2c351ab68a 100644
--- a/libs/ardour/mtc_slave.cc
+++ b/libs/ardour/mtc_slave.cc
@@ -25,11 +25,12 @@
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
-#include "midi++/port.h"
+#include "ardour/audioengine.h"
#include "ardour/debug.h"
-#include "ardour/slave.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/session.h"
-#include "ardour/audioengine.h"
+#include "ardour/slave.h"
#include <glibmm/timer.h>
@@ -50,8 +51,9 @@ using namespace Timecode;
*/
const int MTC_Slave::frame_tolerance = 2;
-MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
+MTC_Slave::MTC_Slave (Session& s, MidiPort& p)
: session (s)
+ , port (&p)
{
can_notify_on_unknown_rate = true;
did_reset_tc_format = false;
@@ -72,7 +74,10 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
parse_timecode_offset();
reset (true);
- rebind (p);
+
+ port->self_parser().mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
+ port->self_parser().mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
+ port->self_parser().mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
}
MTC_Slave::~MTC_Slave()
@@ -96,15 +101,12 @@ MTC_Slave::~MTC_Slave()
}
void
-MTC_Slave::rebind (MIDI::Port& p)
+MTC_Slave::rebind (MidiPort& p)
{
port_connections.drop_connections ();
port = &p;
- port->parser()->mtc_time.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_time, this, _1, _2, _3));
- port->parser()->mtc_qtr.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_qtr, this, _1, _2, _3));
- port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
}
void
@@ -156,7 +158,8 @@ MTC_Slave::outside_window (framepos_t pos) const
bool
MTC_Slave::locked () const
{
- return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("locked ? %1 last %2 initstate %3\n", port->self_parser().mtc_locked(), last_inbound_frame, engine_dll_initstate));
+ return port->self_parser().mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0;
}
bool
@@ -257,7 +260,6 @@ MTC_Slave::init_mtc_dll(framepos_t tme, double qtr)
DEBUG_TRACE (DEBUG::MTC, string_compose ("[re-]init MTC DLL %1 %2 %3\n", t0, t1, e2));
}
-
/* called from MIDI parser */
void
MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now)
@@ -308,7 +310,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
a locate command via MMC.
*/
- //DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", pthread_name()));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC::update_mtc_time - TID:%1\n", ::pthread_self()));
TimecodeFormat tc_format;
bool reset_tc = true;
@@ -424,7 +426,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
now, timecode, mtc_frame, was_full, speedup_due_to_tc_mismatch));
if (was_full || outside_window (mtc_frame)) {
- DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC or outside window. - TID:%1\n", pthread_name()));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("update_mtc_time: full TC %1 or outside window %2\n", was_full, outside_window (mtc_frame)));
session.request_locate (mtc_frame, false);
session.request_transport_speed (0);
update_mtc_status (MIDI::MTC_Stopped);
@@ -448,7 +450,7 @@ MTC_Slave::update_mtc_time (const MIDI::byte *msg, bool was_full, framepos_t now
DEBUG_TRACE (DEBUG::MTC, string_compose ("new mtc_frame: %1 | MTC-FpT: %2 A3-FpT:%3\n",
mtc_frame, (4.0*qtr), session.frames_per_timecode_frame()));
- switch (port->parser()->mtc_running()) {
+ switch (port->self_parser().mtc_running()) {
case MTC_Backward:
mtc_frame -= mtc_off;
qtr *= -1.0;
@@ -528,9 +530,10 @@ MTC_Slave::reset_window (framepos_t root)
of acceptable MTC frames wide open. otherwise, shrink it down to just 2 video frames
ahead of the window root (taking direction into account).
*/
+
framecnt_t const d = (quarter_frame_duration * 4 * frame_tolerance);
- switch (port->parser()->mtc_running()) {
+ switch (port->self_parser().mtc_running()) {
case MTC_Forward:
window_begin = root;
transport_direction = 1;
@@ -553,7 +556,7 @@ MTC_Slave::reset_window (framepos_t root)
break;
}
- DEBUG_TRACE (DEBUG::MTC, string_compose ("legal MTC window now %1 .. %2\n", window_begin, window_end));
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("reset MTC window @ %3, now %1 .. %2\n", window_begin, window_end, root));
}
void
@@ -577,11 +580,11 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
}
/* main entry point from session_process.cc
- * in jack_process callback context */
+xo * in process callback context */
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();
@@ -591,11 +594,21 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
read_current (&last);
+ DEBUG_TRACE (DEBUG::MTC, string_compose ("speed&pos: timestamp %1 speed %2 initstate %3 dir %4 tpos %5 now %6 last-in %7\n",
+ last.timestamp,
+ last.speed,
+ engine_dll_initstate,
+ transport_direction,
+ sess_pos,
+ now,
+ last_inbound_frame));
+
/* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */
- if (last.timestamp == 0) { engine_dll_initstate = 0; }
- else if (engine_dll_initstate != transport_direction && last.speed != 0) {
+ 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;
}
@@ -627,9 +640,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
/* interpolate position according to speed and time since last quarter-frame*/
if (speed_flt == 0.0f) {
elapsed = 0;
- }
- else
- {
+ } else {
/* scale elapsed time by the current MTC speed */
elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp));
if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) {
@@ -645,8 +656,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() ));
}
}
@@ -659,14 +670,13 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
*/
if (!session.actively_recording()
&& speed != 0
- && ( (pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()) )
- ) {
+ && ((pos < 0) || (labs(pos - sess_pos) > 3 * session.frame_rate()))) {
engine_dll_initstate = 0;
queue_reset (false);
}
/* provide a .1% deadzone to lock the speed */
- if (fabs(speed - 1.0) <= 0.001)
+ if (fabs (speed - 1.0) <= 0.001)
speed = 1.0;
DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n",
diff --git a/libs/ardour/panner_manager.cc b/libs/ardour/panner_manager.cc
index f757279d29..8611c7bc86 100644
--- a/libs/ardour/panner_manager.cc
+++ b/libs/ardour/panner_manager.cc
@@ -84,6 +84,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 0f28b31b57..d8a32e3467 100644
--- a/libs/ardour/port.cc
+++ b/libs/ardour/port.cc
@@ -32,6 +32,7 @@
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/port.h"
+#include "ardour/port_engine.h"
#include "i18n.h"
@@ -42,13 +43,19 @@ 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;
+std::string Port::state_node_name = X_("Port");
+
+/* 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)
@@ -60,21 +67,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()) {
+ 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 ();
}
-
- 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";
- throw failed_constructor ();
- }
-
+
PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
}
@@ -87,11 +90,10 @@ Port::~Port ()
void
Port::drop ()
{
- if (_jack_port) {
- if (_engine->jack ()) {
- jack_port_unregister (_engine->jack (), _jack_port);
- }
- _jack_port = 0;
+ if (_port_handle) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
+ port_engine.unregister_port (_port_handle);
+ _port_handle = 0;
}
}
@@ -99,18 +101,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;
@@ -122,48 +124,29 @@ 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.
- */
+ if (!port_engine.connected()) {
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);
- }
- }
+ if (!port_engine.connected()) {
+ c.insert (c.end(), _connections.begin(), _connections.end());
+ return c.size();
}
- 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;
@@ -172,9 +155,11 @@ Port::connect (std::string const & other)
}
if (sends_output ()) {
- r = jack_connect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ());
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
+ r = port_engine.connect (our_name, other_name);
} else {
- r = jack_connect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str());
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
+ r = port_engine.connect (other_name, our_name);
}
if (r == 0) {
@@ -187,15 +172,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) {
@@ -204,8 +189,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
@@ -238,21 +223,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
@@ -274,28 +260,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;
@@ -313,12 +294,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) {
@@ -338,14 +319,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,
@@ -354,27 +334,15 @@ 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);
if (!connections.empty()) {
- range.min = ~((jack_nframes_t) 0);
+ range.min = ~((pframes_t) 0);
range.max = 0;
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
@@ -382,21 +350,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",
@@ -440,15 +405,10 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c
int
Port::reestablish ()
{
- jack_client_t* jack = _engine->jack();
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
+ _port_handle = port_engine.register_port (_name, type(), _flags);
- if (!jack) {
- return -1;
- }
-
- _jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0);
-
- if (_jack_port == 0) {
+ if (_port_handle == 0) {
PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
return -1;
}
@@ -464,6 +424,8 @@ Port::reconnect ()
{
/* caller must hold process lock; intended to be used only after reestablish() */
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
+
for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
if (connect (*i)) {
return -1;
@@ -473,7 +435,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)
{
@@ -481,10 +443,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;
}
@@ -492,38 +454,67 @@ Port::set_name (std::string const & n)
return r;
}
-void
-Port::request_jack_monitors_input (bool yn)
+bool
+Port::physically_connected () const
{
- jack_port_request_monitor (_jack_port, yn);
+ return port_engine.physically_connected (_port_handle);
}
-bool
-Port::physically_connected () const
+XMLNode&
+Port::get_state () const
{
- const char** jc = jack_port_get_connections (_jack_port);
+ XMLNode* root = new XMLNode (state_node_name);
- if (jc) {
- for (int i = 0; jc[i]; ++i) {
+ root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
- jack_port_t* port = jack_port_by_name (_engine->jack(), jc[i]);
+ if (receives_input()) {
+ root->add_property (X_("direction"), X_("input"));
+ } else {
+ root->add_property (X_("direction"), X_("output"));
+ }
- 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);
- }
+ vector<string> c;
+
+ get_connections (c);
+
+ for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
+ XMLNode* child = new XMLNode (X_("Connection"));
+ child->add_property (X_("other"), *i);
+ root->add_child_nocopy (*child);
}
- return false;
+ return *root;
}
+int
+Port::set_state (const XMLNode& node, int)
+{
+ const XMLProperty* prop;
+
+ if (node.name() != state_node_name) {
+ return -1;
+ }
+
+ if ((prop = node.property (X_("name"))) != 0) {
+ set_name (prop->value());
+ }
+
+ const XMLNodeList& children (node.children());
+
+ _connections.clear ();
+
+ for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
+
+ if ((*c)->name() != X_("Connection")) {
+ continue;
+ }
+
+ if ((prop = (*c)->property (X_("other"))) == 0) {
+ continue;
+ }
+
+ _connections.insert (prop->value());
+ }
+
+ return 0;
+}
diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc
index b2758b2d57..2519451b89 100644
--- a/libs/ardour/port_insert.cc
+++ b/libs/ardour/port_insert.cc
@@ -49,7 +49,7 @@ PortInsert::PortInsert (Session& s, boost::shared_ptr<Pannable> pannable, boost:
{
_mtdm = 0;
_latency_detect = false;
- _latency_flush_frames = false;
+ _latency_flush_frames = 0;
_measured_latency = 0;
}
@@ -64,7 +64,7 @@ PortInsert::start_latency_detection ()
{
delete _mtdm;
_mtdm = new MTDM (_session.frame_rate());
- _latency_flush_frames = false;
+ _latency_flush_frames = 0;
_latency_detect = true;
_measured_latency = 0;
}
@@ -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..1758ea4a3f
--- /dev/null
+++ b/libs/ardour/port_manager.cc
@@ -0,0 +1,659 @@
+/*
+ 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 "ardour/async_midi_port.h"
+#include "ardour/debug.h"
+#include "ardour/port_manager.h"
+#include "ardour/audio_port.h"
+#include "ardour/midi_port.h"
+#include "ardour/midiport_manager.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 (DataType type, PortList& pl)
+{
+ boost::shared_ptr<Ports> plist = ports.reader();
+ for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
+ if (p->second->type() == type) {
+ pl.push_back (p->second);
+ }
+ }
+ return pl.size();
+}
+
+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, bool async)
+{
+ boost::shared_ptr<Port> newport;
+
+ try {
+ if (dtype == DataType::AUDIO) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
+ portname, input));
+ newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
+ } else if (dtype == DataType::MIDI) {
+ if (async) {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
+ portname, input));
+ newport.reset (new AsyncMIDIPort (portname, (input ? IsInput : IsOutput)));
+ } else {
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
+ portname, input));
+ 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 */
+
+ }
+
+ 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)");
+ }
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
+ return newport;
+}
+
+boost::shared_ptr<Port>
+PortManager::register_input_port (DataType type, const string& portname, bool async)
+{
+ return register_port (type, portname, true, async);
+}
+
+boost::shared_ptr<Port>
+PortManager::register_output_port (DataType type, const string& portname, bool async)
+{
+ return register_port (type, portname, false, async);
+}
+
+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 ...hand-off to the PortEngine
+ */
+ if (_impl) {
+ ret = _impl->connect (s, d);
+ } else {
+ 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 ...hand-off to the PortEngine
+ */
+ if (_impl) {
+ ret = _impl->disconnect (s, d);
+ } else {
+ 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 ();
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
+
+ for (i = p->begin(); i != p->end(); ++i) {
+ if (i->second->reestablish ()) {
+ error << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endmsg;
+ cerr << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endl;
+ break;
+ }
+ }
+
+ if (i != p->end()) {
+ /* failed */
+ remove_all_ports ();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PortManager::reconnect_ports ()
+{
+ boost::shared_ptr<Ports> p = ports.reader ();
+
+ /* re-establish connections */
+
+ DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ i->second->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;
+}
+
+void
+PortManager::cycle_start (pframes_t nframes)
+{
+ Port::set_global_port_buffer_offset (0);
+ Port::set_cycle_framecnt (nframes);
+
+ _cycle_ports = ports.reader ();
+
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->cycle_start (nframes);
+ }
+}
+
+void
+PortManager::cycle_end (pframes_t nframes)
+{
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->cycle_end (nframes);
+ }
+
+ for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
+ p->second->flush_buffers (nframes);
+ }
+
+ _cycle_ports.reset ();
+
+ /* we are done */
+}
+
+void
+PortManager::silence (pframes_t nframes)
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+ if (i->second->sends_output()) {
+ i->second->get_buffer(nframes).silence(nframes);
+ }
+ }
+}
+
+void
+PortManager::check_monitoring ()
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+
+ bool x;
+
+ 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 ...
+ */
+ i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
+ }
+ }
+}
+
+void
+PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
+{
+ for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
+
+ if (i->second->sends_output()) {
+
+ boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
+ if (ap) {
+ Sample* s = ap->engine_get_whole_audio_buffer ();
+ gain_t g = base_gain;
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+ *s++ *= g;
+ g -= gain_step;
+ }
+ }
+ }
+ }
+}
diff --git a/libs/ardour/rc_configuration.cc b/libs/ardour/rc_configuration.cc
index 4733587654..7fe030fb7e 100644
--- a/libs/ardour/rc_configuration.cc
+++ b/libs/ardour/rc_configuration.cc
@@ -27,11 +27,11 @@
#include "pbd/xml++.h"
#include "pbd/file_utils.h"
-#include "midi++/manager.h"
-
+#include "ardour/audioengine.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/diskstream.h"
#include "ardour/filesystem_paths.h"
+#include "ardour/port.h"
#include "ardour/rc_configuration.h"
#include "ardour/session_metadata.h"
@@ -63,13 +63,8 @@ RCConfiguration::RCConfiguration ()
{
}
-
RCConfiguration::~RCConfiguration ()
{
- for (list<XMLNode*>::iterator i = _midi_port_states.begin(); i != _midi_port_states.end(); ++i) {
- delete *i;
- }
-
delete _control_protocol_state;
}
@@ -177,16 +172,6 @@ RCConfiguration::get_state ()
root = new XMLNode("Ardour");
- MIDI::Manager* mm = MIDI::Manager::instance();
-
- if (mm) {
- boost::shared_ptr<const MIDI::Manager::PortList> ports = mm->get_midi_ports();
-
- for (MIDI::Manager::PortList::const_iterator i = ports->begin(); i != ports->end(); ++i) {
- root->add_child_nocopy((*i)->get_state());
- }
- }
-
root->add_child_nocopy (get_variables ());
root->add_child_nocopy (SessionMetadata::Metadata()->get_user_state());
@@ -232,12 +217,6 @@ RCConfiguration::set_state (const XMLNode& root, int version)
XMLNodeConstIterator niter;
XMLNode *node;
- for (list<XMLNode*>::iterator i = _midi_port_states.begin(); i != _midi_port_states.end(); ++i) {
- delete *i;
- }
-
- _midi_port_states.clear ();
-
Stateful::save_extra_xml (root);
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
@@ -250,8 +229,6 @@ RCConfiguration::set_state (const XMLNode& root, int version)
SessionMetadata::Metadata()->set_state (*node, version);
} else if (node->name() == ControlProtocolManager::state_node_name) {
_control_protocol_state = new XMLNode (*node);
- } else if (node->name() == MIDI::Port::state_node_name) {
- _midi_port_states.push_back (new XMLNode (*node));
}
}
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index 232f295df6..026ee4ec5c 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;
}
@@ -3818,13 +3818,13 @@ 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;
all_connections.max = 0;
} else {
- all_connections.min = ~((jack_nframes_t) 0);
+ all_connections.min = ~((pframes_t) 0);
all_connections.max = 0;
/* iterate over all "from" ports and determine the latency range for all of their
@@ -3833,7 +3833,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);
@@ -3898,7 +3898,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 428a5e5274..135df7c2cd 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -49,6 +49,7 @@
#include "ardour/amp.h"
#include "ardour/analyser.h"
+#include "ardour/async_midi_port.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_diskstream.h"
#include "ardour/audio_port.h"
@@ -66,6 +67,7 @@
#include "ardour/debug.h"
#include "ardour/filename_extensions.h"
#include "ardour/graph.h"
+#include "ardour/midiport_manager.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/operations.h"
@@ -85,12 +87,11 @@
#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
#include "ardour/source_factory.h"
+#include "ardour/speakers.h"
#include "ardour/utils.h"
#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
#include "midi++/mmc.h"
-#include "midi++/manager.h"
#include "i18n.h"
@@ -106,6 +107,7 @@ using namespace PBD;
bool Session::_disable_all_loaded_plugins = false;
+PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
PBD::Signal1<void,std::string> Session::Dialog;
PBD::Signal0<int> Session::AskAboutPendingState;
PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
@@ -130,70 +132,184 @@ Session::Session (AudioEngine &eng,
const string& snapshot_name,
BusProfile* bus_profile,
string mix_template)
- : _engine (eng)
+ : playlists (new SessionPlaylists)
+ , _engine (eng)
+ , process_function (&Session::process_with_events)
+ , waiting_for_sync_offset (false)
+ , _base_frame_rate (0)
+ , _current_frame_rate (0)
+ , _nominal_frame_rate (0)
+ , transport_sub_state (0)
+ , _record_status (Disabled)
+ , _transport_frame (0)
+ , _session_range_location (0)
+ , _slave (0)
+ , _silent (false)
+ , _transport_speed (0)
+ , _default_transport_speed (1.0)
+ , _last_transport_speed (0)
, _target_transport_speed (0.0)
+ , auto_play_legal (false)
+ , _last_slave_transport_frame (0)
+ , maximum_output_latency (0)
, _requested_return_frame (-1)
+ , current_block_size (0)
+ , _worst_output_latency (0)
+ , _worst_input_latency (0)
+ , _worst_track_latency (0)
+ , _have_captured (false)
+ , _meter_hold (0)
+ , _meter_falloff (0)
+ , _non_soloed_outs_muted (false)
+ , _listen_cnt (0)
+ , _solo_isolated_cnt (0)
+ , _writable (false)
+ , _was_seamless (Config->get_seamless_loop ())
, _under_nsm_control (false)
- , _session_dir (new SessionDirectory(fullpath))
+ , delta_accumulator_cnt (0)
+ , average_slave_delta (1800) // !!! why 1800 ???
+ , average_dir (0)
+ , have_first_delta_accumulator (false)
+ , _slave_state (Stopped)
+ , post_export_sync (false)
+ , post_export_position (0)
+ , _exporting (false)
+ , _export_started (false)
+ , _export_rolling (false)
+ , _pre_export_mmc_enabled (false)
+ , _name (snapshot_name)
+ , _is_new (true)
+ , _send_qf_mtc (false)
+ , _pframes_since_last_mtc (0)
+ , session_midi_feedback (0)
+ , play_loop (false)
+ , loop_changing (false)
+ , last_loopend (0)
+ , _session_dir (new SessionDirectory (fullpath))
+ , _current_snapshot_name (snapshot_name)
, state_tree (0)
- , _state_of_the_state (Clean)
+ , state_was_pending (false)
+ , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+ , _last_roll_location (0)
+ , _last_roll_or_reversal_location (0)
+ , _last_record_location (0)
+ , pending_locate_roll (false)
+ , pending_locate_frame (0)
+ , pending_locate_flush (false)
+ , pending_abort (false)
+ , pending_auto_loop (false)
, _butler (new Butler (*this))
, _post_transport_work (0)
+ , cumulative_rf_motion (0)
+ , rf_scale (1.0)
+ , _locations (new Locations (*this))
+ , step_speed (0)
+ , outbound_mtc_timecode_frame (0)
+ , next_quarter_frame_to_send (-1)
+ , _frames_per_timecode_frame (0)
+ , _frames_per_hour (0)
+ , _timecode_frames_per_hour (0)
+ , last_timecode_valid (false)
+ , last_timecode_when (0)
, _send_timecode_update (false)
+ , ltc_encoder (0)
, ltc_enc_buf(0)
+ , ltc_buf_off (0)
+ , ltc_buf_len (0)
+ , ltc_speed (0)
+ , ltc_enc_byte (0)
+ , ltc_enc_pos (0)
+ , ltc_enc_cnt (0)
+ , ltc_enc_off (0)
+ , restarting (false)
+ , ltc_prev_cycle (0)
+ , ltc_timecode_offset (0)
+ , ltc_timecode_negative_offset (false)
+ , midi_control_ui (0)
+ , _tempo_map (0)
, _all_route_group (new RouteGroup (*this, "all"))
, routes (new RouteList)
+ , _adding_routes_in_progress (false)
+ , destructive_index (0)
+ , solo_update_disabled (false)
+ , default_fade_steepness (0)
+ , default_fade_msecs (0)
, _total_free_4k_blocks (0)
, _total_free_4k_blocks_uncertain (false)
+ , no_questions_about_missing_files (false)
+ , _playback_load (0)
+ , _capture_load (0)
, _bundles (new BundleList)
, _bundle_xml_node (0)
, _current_trans (0)
+ , _clicking (false)
, click_data (0)
, click_emphasis_data (0)
+ , click_length (0)
+ , click_emphasis_length (0)
+ , _clicks_cleared (0)
+ , _play_range (false)
, main_outs (0)
+ , first_file_data_format_reset (true)
+ , first_file_header_format_reset (true)
+ , have_looped (false)
, _have_rec_enabled_track (false)
+ , _step_editors (0)
, _suspend_timecode_transmission (0)
+ , _speakers (new Speakers)
+ , ignore_route_processor_changes (false)
{
- _locations = new Locations (*this);
- ltc_encoder = NULL;
-
- if (how_many_dsp_threads () > 1) {
- /* For now, only create the graph if we are using >1 DSP threads, as
- it is a bit slower than the old code with 1 thread.
- */
- _process_graph.reset (new Graph (*this));
- }
-
- playlists.reset (new SessionPlaylists);
-
- _all_route_group->set_active (true, this);
-
- interpolation.add_channel_to (0, 0);
-
- if (!eng.connected()) {
- throw failed_constructor();
- }
-
- n_physical_outputs = _engine.n_physical_outputs ();
- n_physical_inputs = _engine.n_physical_inputs ();
-
- first_stage_init (fullpath, snapshot_name);
-
- _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ uint32_t sr = 0;
+ pre_engine_init (fullpath);
+
if (_is_new) {
if (create (mix_template, bus_profile)) {
destroy ();
throw failed_constructor ();
}
+ } else {
+ if (load_state (_current_snapshot_name)) {
+ throw failed_constructor ();
+ }
+
+ /* try to get sample rate from XML state so that we
+ * can influence the SR if we set up the audio
+ * engine.
+ */
+
+ if (state_tree) {
+ const XMLProperty* prop;
+ if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+ sr = atoi (prop->value());
+ }
+ }
+ }
+
+ if (_engine.current_backend() == 0 || _engine.setup_required()) {
+ boost::optional<int> r = AudioEngineSetupRequired (sr);
+ if (r.get_value_or (-1) != 0) {
+ destroy ();
+ throw failed_constructor();
+ }
+ }
+
+ /* at this point the engine should be connected (i.e. interacting
+ with a backend device (or psuedo-device) and available to us
+ for determinining sample rates and other settings.
+ */
+
+ if (!_engine.connected()) {
+ destroy ();
+ throw failed_constructor();
}
- if (second_stage_init ()) {
+ if (post_engine_init ()) {
destroy ();
throw failed_constructor ();
}
- store_recent_sessions(_name, _path);
+ store_recent_sessions (_name, _path);
bool was_dirty = dirty();
@@ -210,6 +326,16 @@ Session::Session (AudioEngine &eng,
EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
_is_new = false;
+
+ /* hook us up to the engine since we are now completely constructed */
+
+ BootMessage (_("Connect to engine"));
+
+ _engine.set_session (this);
+ _engine.reset_timebase ();
+
+ BootMessage (_("Session loading complete"));
+
}
Session::~Session ()
@@ -330,6 +456,8 @@ Session::destroy ()
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
+ delete _mmc;
+ delete _midi_ports;
delete _locations;
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
@@ -340,135 +468,103 @@ Session::destroy ()
}
void
-Session::when_engine_running ()
+Session::setup_ltc ()
{
- string first_physical_output;
-
- BootMessage (_("Set block size and sample rate"));
-
- set_block_size (_engine.frames_per_cycle());
- set_frame_rate (_engine.frame_rate());
-
- BootMessage (_("Using configuration"));
-
- boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
- boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
-
- Config->map_parameters (ff);
- config.map_parameters (ft);
-
- /* every time we reconnect, recompute worst case output latencies */
-
- _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
-
- if (synced_to_jack()) {
- _engine.transport_stop ();
+ XMLNode* child = 0;
+
+ _ltc_input.reset (new IO (*this, _("LTC In"), IO::Input));
+ _ltc_output.reset (new IO (*this, _("LTC Out"), IO::Output));
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-In")) != 0) {
+ _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_input ();
}
-
- if (config.get_jack_time_master()) {
- _engine.transport_locate (_transport_frame);
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-Out")) != 0) {
+ _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ } else {
+ {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+ }
+ reconnect_ltc_output ();
}
+
+ /* fix up names of LTC ports because we don't want the normal
+ * IO style of NAME/TYPE-{in,out}N
+ */
+
+ _ltc_input->nth (0)->set_name (_("LTC-in"));
+ _ltc_output->nth (0)->set_name (_("LTC-out"));
+}
- _clicking = false;
+void
+Session::setup_click ()
+{
+ XMLNode* child = 0;
- try {
- XMLNode* child = 0;
+ _clicking = false;
+ _click_io.reset (new ClickIO (*this, "click"));
+ _click_gain.reset (new Amp (*this));
+ _click_gain->activate ();
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
- _ltc_input.reset (new IO (*this, _("LTC In"), IO::Input));
- _ltc_output.reset (new IO (*this, _("LTC Out"), IO::Output));
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-In")) != 0) {
- _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
- } else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_input ();
- }
+ /* existing state for Click */
+ int c = 0;
- if (state_tree && (child = find_named_node (*state_tree->root(), "LTC-Out")) != 0) {
- _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
+ if (Stateful::loading_state_version < 3000) {
+ c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
} else {
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
- }
- reconnect_ltc_output ();
- }
-
- /* fix up names of LTC ports because we don't want the normal
- * IO style of NAME/TYPE-{in,out}N
- */
-
- _ltc_input->nth (0)->set_name (_("LTC-in"));
- _ltc_output->nth (0)->set_name (_("LTC-out"));
-
- _click_io.reset (new ClickIO (*this, "click"));
- _click_gain.reset (new Amp (*this));
- _click_gain->activate ();
-
- if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
-
- /* existing state for Click */
- int c = 0;
-
- if (Stateful::loading_state_version < 3000) {
- c = _click_io->set_state_2X (*child->children().front(), Stateful::loading_state_version, false);
- } else {
- const XMLNodeList& children (child->children());
- XMLNodeList::const_iterator i = children.begin();
- if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
- ++i;
- if (i != children.end()) {
- c = _click_gain->set_state (**i, Stateful::loading_state_version);
- }
+ const XMLNodeList& children (child->children());
+ XMLNodeList::const_iterator i = children.begin();
+ if ((c = _click_io->set_state (**i, Stateful::loading_state_version)) == 0) {
+ ++i;
+ if (i != children.end()) {
+ c = _click_gain->set_state (**i, Stateful::loading_state_version);
}
}
+ }
- if (c == 0) {
- _clicking = Config->get_clicking ();
+ if (c == 0) {
+ _clicking = Config->get_clicking ();
- } else {
+ } else {
- error << _("could not setup Click I/O") << endmsg;
- _clicking = false;
- }
+ error << _("could not setup Click I/O") << endmsg;
+ _clicking = false;
+ }
- } else {
+ } else {
- /* default state for Click: dual-mono to first 2 physical outputs */
+ /* default state for Click: dual-mono to first 2 physical outputs */
- vector<string> outs;
- _engine.get_physical_outputs (DataType::AUDIO, outs);
+ vector<string> outs;
+ _engine.get_physical_outputs (DataType::AUDIO, outs);
- for (uint32_t physport = 0; physport < 2; ++physport) {
- if (outs.size() > physport) {
- if (_click_io->add_port (outs[physport], this)) {
- // relax, even though its an error
- }
+ for (uint32_t physport = 0; physport < 2; ++physport) {
+ if (outs.size() > physport) {
+ if (_click_io->add_port (outs[physport], this)) {
+ // relax, even though its an error
}
}
-
- if (_click_io->n_ports () > ChanCount::ZERO) {
- _clicking = Config->get_clicking ();
- }
}
- }
- catch (failed_constructor& err) {
- error << _("cannot setup Click I/O") << endmsg;
- }
-
- BootMessage (_("Compute I/O Latencies"));
-
- if (_clicking) {
- // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO?
+ if (_click_io->n_ports () > ChanCount::ZERO) {
+ _clicking = Config->get_clicking ();
+ }
}
+}
- BootMessage (_("Set up standard connections"));
-
+void
+Session::setup_bundles ()
+{
vector<string> inputs[DataType::num_types];
vector<string> outputs[DataType::num_types];
for (uint32_t i = 0; i < DataType::num_types; ++i) {
@@ -567,6 +663,37 @@ Session::when_engine_running ()
add_bundle (c);
}
+}
+
+int
+Session::when_engine_running ()
+{
+ /* every time we reconnect, recompute worst case output latencies */
+
+ _engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
+
+ if (synced_to_jack()) {
+ _engine.transport_stop ();
+ }
+
+ if (config.get_jack_time_master()) {
+ _engine.transport_locate (_transport_frame);
+ }
+
+
+ try {
+ BootMessage (_("Set up LTC"));
+ setup_ltc ();
+ BootMessage (_("Set up Click"));
+ setup_click ();
+ BootMessage (_("Set up standard connections"));
+ setup_bundles ();
+ }
+
+ catch (failed_constructor& err) {
+ return -1;
+ }
+
BootMessage (_("Setup signal flow and plugins"));
/* Reset all panners */
@@ -584,7 +711,8 @@ Session::when_engine_running ()
as it will set states for ports which the ControlProtocolManager creates.
*/
- MIDI::Manager::instance()->set_port_states (Config->midi_port_states ());
+ // XXX set state of MIDI::Port's
+ // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
/* And this must be done after the MIDI::Manager::set_port_states as
* it will try to make connections whose details are loaded by set_port_states.
@@ -609,11 +737,7 @@ Session::when_engine_running ()
initialize_latencies ();
- /* hook us up to the engine */
-
- BootMessage (_("Connect to engine"));
- _engine.set_session (this);
- _engine.reset_timebase ();
+ return 0;
}
void
@@ -874,7 +998,12 @@ Session::hookup_io ()
/* Tell all IO objects to connect themselves together */
IO::enable_connecting ();
- MIDI::JackMIDIPort::MakeConnections ();
+
+ /* Now tell all "floating" ports to connect to whatever
+ they should be connected to.
+ */
+
+ AudioEngine::instance()->reconnect_ports ();
/* Anyone who cares about input state, wake up and do something */
@@ -936,7 +1065,7 @@ Session::set_track_monitor_input_status (bool yn)
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
if (tr && tr->record_enabled ()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
- tr->request_jack_monitors_input (yn);
+ tr->request_input_monitoring (yn);
}
}
}
@@ -1169,7 +1298,7 @@ Session::enable_record ()
if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
_last_record_location = _transport_frame;
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
set_track_monitor_input_status (true);
@@ -1190,7 +1319,7 @@ Session::disable_record (bool rt_context, bool force)
if ((!Config->get_latched_record_enable () && !play_loop) || force) {
g_atomic_int_set (&_record_status, Disabled);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
} else {
if (rs == Recording) {
g_atomic_int_set (&_record_status, Enabled);
@@ -1244,7 +1373,7 @@ Session::maybe_enable_record ()
enable_record ();
}
} else {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
RecordStateChanged (); /* EMIT SIGNAL */
}
@@ -1341,6 +1470,7 @@ Session::set_frame_rate (framecnt_t frames_per_second)
*/
_base_frame_rate = frames_per_second;
+ _nominal_frame_rate = frames_per_second;
sync_time_vars();
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 0db4fc33bb..62eb61ab83 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -21,7 +21,6 @@
#include "pbd/error.h"
#include <glibmm/threads.h>
-#include <midi++/manager.h>
#include <midi++/mmc.h>
#include "ardour/audioengine.h"
@@ -93,8 +92,8 @@ Session::pre_export ()
/* disable MMC output early */
- _pre_export_mmc_enabled = MIDI::Manager::instance()->mmc()->send_enabled ();
- MIDI::Manager::instance()->mmc()->enable_send (false);
+ _pre_export_mmc_enabled = _mmc->send_enabled ();
+ _mmc->enable_send (false);
return 0;
}
@@ -237,7 +236,7 @@ Session::finalize_audio_export ()
export_freewheel_connection.disconnect();
- MIDI::Manager::instance()->mmc()->enable_send (_pre_export_mmc_enabled);
+ _mmc->enable_send (_pre_export_mmc_enabled);
/* maybe write CUE/TOC */
diff --git a/libs/ardour/session_jack.cc b/libs/ardour/session_jack.cc
new file mode 100644
index 0000000000..af8a93fec3
--- /dev/null
+++ b/libs/ardour/session_jack.cc
@@ -0,0 +1,185 @@
+/*
+ Copyright (C) 1999-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.
+
+*/
+
+
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
+#include <time.h>
+
+#include <glibmm/miscutils.h>
+
+#include "jack/jack.h"
+#include "jack/session.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/filename_extensions.h"
+#include "ardour/session.h"
+#include "ardour/session_directory.h"
+#include "ardour/tempo.h"
+
+using namespace ARDOUR;
+using std::string;
+
+#ifdef HAVE_JACK_SESSION
+void
+Session::jack_session_event (jack_session_event_t* event)
+{
+ char timebuf[128], *tmp;
+ time_t n;
+ struct tm local_time;
+
+ time (&n);
+ localtime_r (&n, &local_time);
+ strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
+
+ while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; }
+
+ if (event->type == JackSessionSaveTemplate)
+ {
+ if (save_template( timebuf )) {
+ event->flags = JackSessionSaveError;
+ } else {
+ string cmd ("ardour3 -P -U ");
+ cmd += event->client_uuid;
+ cmd += " -T ";
+ cmd += timebuf;
+
+ event->command_line = strdup (cmd.c_str());
+ }
+ }
+ else
+ {
+ if (save_state (timebuf)) {
+ event->flags = JackSessionSaveError;
+ } else {
+ std::string xml_path (_session_dir->root_path());
+ std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
+ xml_path = Glib::build_filename (xml_path, legalized_filename);
+
+ string cmd ("ardour3 -P -U ");
+ cmd += event->client_uuid;
+ cmd += " \"";
+ cmd += xml_path;
+ cmd += '\"';
+
+ event->command_line = strdup (cmd.c_str());
+ }
+ }
+
+ /* 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 */
+ }
+
+ jack_session_event_free( event );
+}
+#endif
+
+void
+Session::jack_timebase_callback (jack_transport_state_t /*state*/,
+ pframes_t /*nframes*/,
+ jack_position_t* pos,
+ int /*new_position*/)
+{
+ Timecode::BBT_Time bbt;
+
+ /* BBT info */
+
+ if (_tempo_map) {
+
+ TempoMetric metric (_tempo_map->metric_at (_transport_frame));
+
+ try {
+ _tempo_map->bbt_time_rt (_transport_frame, bbt);
+
+ pos->bar = bbt.bars;
+ pos->beat = bbt.beats;
+ pos->tick = bbt.ticks;
+
+ // XXX still need to set bar_start_tick
+
+ pos->beats_per_bar = metric.meter().divisions_per_bar();
+ pos->beat_type = metric.meter().note_divisor();
+ pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat;
+ pos->beats_per_minute = metric.tempo().beats_per_minute();
+
+ pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
+
+ } catch (...) {
+ /* no message */
+ }
+ }
+
+#ifdef HAVE_JACK_VIDEO_SUPPORT
+ //poke audio video ratio so Ardour can track Video Sync
+ pos->audio_frames_per_video_frame = frame_rate() / timecode_frames_per_second();
+ pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio);
+#endif
+
+#if 0
+ /* Timecode info */
+
+ pos->timecode_offset = config.get_timecode_offset();
+ t.timecode_frame_rate = timecode_frames_per_second();
+ pos->valid = jack_position_bits_t (pos->valid | JackPositionTimecode;
+
+ if (_transport_speed) {
+
+ if (play_loop) {
+
+ Location* location = _locations.auto_loop_location();
+
+ if (location) {
+
+ t.transport_state = JackTransportLooping;
+ t.loop_start = location->start();
+ t.loop_end = location->end();
+ t.valid = jack_transport_bits_t (t.valid | JackTransportLoop);
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ }
+#endif
+}
+
diff --git a/libs/ardour/session_ltc.cc b/libs/ardour/session_ltc.cc
index 2671bfccf4..2ce25b5a48 100644
--- a/libs/ardour/session_ltc.cc
+++ b/libs/ardour/session_ltc.cc
@@ -55,7 +55,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 : \
@@ -564,7 +564,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_midi.cc b/libs/ardour/session_midi.cc
index 9a7ca5b734..63cba68df1 100644
--- a/libs/ardour/session_midi.cc
+++ b/libs/ardour/session_midi.cc
@@ -29,7 +29,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
@@ -40,6 +39,7 @@
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
+#include "ardour/midi_port.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/session.h"
@@ -348,7 +348,7 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
* @param t time to send.
*/
int
-Session::send_full_time_code (framepos_t const t)
+Session::send_full_time_code (framepos_t const t, pframes_t nframes)
{
/* This function could easily send at a given frame offset, but would
* that be useful? Does ardour do sub-block accurate locating? [DR] */
@@ -423,10 +423,9 @@ Session::send_full_time_code (framepos_t const t)
msg[8] = timecode.frames;
// Send message at offset 0, sent time is for the start of this cycle
- if (MIDI::Manager::instance()->mtc_output_port()->midimsg (msg, sizeof (msg), 0)) {
- error << _("Session: could not send full MIDI time code") << endmsg;
- return -1;
- }
+
+ MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer (nframes));
+ mb.push_back (0, sizeof (msg), msg);
_pframes_since_last_mtc = 0;
return 0;
@@ -469,7 +468,7 @@ Session::send_midi_time_code_for_cycle (framepos_t start_frame, framepos_t end_f
next_quarter_frame_to_send, quarter_frame_duration));
if (rint(outbound_mtc_timecode_frame + (next_quarter_frame_to_send * quarter_frame_duration)) < _transport_frame) {
- send_full_time_code (_transport_frame);
+ send_full_time_code (_transport_frame, nframes);
return 0;
}
@@ -515,9 +514,10 @@ Session::send_midi_time_code_for_cycle (framepos_t start_frame, framepos_t end_f
ARDOUR::pframes_t const out_stamp = (msg_time - start_frame) / _transport_speed;
assert (out_stamp < nframes);
- if (MIDI::Manager::instance()->mtc_output_port()->midimsg (mtc_msg, 2, out_stamp)) {
+ MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer(nframes));
+ if (!mb.push_back (out_stamp, 2, mtc_msg)) {
error << string_compose(_("Session: cannot send quarter-frame MTC message (%1)"), strerror (errno))
- << endmsg;
+ << endmsg;
return -1;
}
@@ -602,3 +602,45 @@ Session::start_midi_thread ()
return 0;
}
+MIDI::Port*
+Session::midi_input_port () const
+{
+ return _midi_ports->midi_input_port ();
+}
+MIDI::Port*
+Session::midi_output_port () const
+{
+ return _midi_ports->midi_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::midi_clock_output_port () const
+{
+ return _midi_ports->midi_clock_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::midi_clock_input_port () const
+{
+ return _midi_ports->midi_clock_input_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::mtc_output_port () const
+{
+ return _midi_ports->mtc_output_port ();
+}
+boost::shared_ptr<MidiPort>
+Session::mtc_input_port () const
+{
+ return _midi_ports->mtc_input_port ();
+}
+
+MIDI::Port*
+Session::mmc_output_port () const
+{
+ return _midi_ports->mmc_output_port ();
+}
+
+MIDI::Port*
+Session::mmc_input_port () const
+{
+ return _midi_ports->mmc_input_port ();
+}
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index cecbd88f65..3c46a2e0a6 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -40,7 +40,6 @@
#include "ardour/ticker.h"
#include "ardour/types.h"
-#include "midi++/manager.h"
#include "midi++/mmc.h"
#include "i18n.h"
@@ -85,7 +84,7 @@ Session::process (pframes_t nframes)
try {
if (!_engine.freewheeling() && Config->get_send_midi_clock() && transport_speed() == 1.0f && midi_clock->has_midi_port()) {
- midi_clock->tick (transport_at_start);
+ midi_clock->tick (transport_at_start, nframes);
}
} catch (...) {
/* don't bother with a message */
@@ -325,7 +324,7 @@ Session::process_with_events (pframes_t nframes)
* and prepare for rolling)
*/
if (_send_timecode_update) {
- send_full_time_code (_transport_frame);
+ send_full_time_code (_transport_frame, nframes);
}
if (!process_can_proceed()) {
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 9699a7c37f..64f8e387be 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 1999-2002 Paul Davis
+ Copyright (C) 1999-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
@@ -59,7 +59,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "evoral/SMF.hpp"
@@ -88,6 +87,7 @@
#include "ardour/control_protocol_manager.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
+#include "ardour/graph.h"
#include "ardour/location.h"
#include "ardour/midi_model.h"
#include "ardour/midi_patch_manager.h"
@@ -126,112 +126,47 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-/** @param snapshot_name Snapshot name, without the .ardour prefix */
void
-Session::first_stage_init (string fullpath, string snapshot_name)
+Session::pre_engine_init (string fullpath)
{
- if (fullpath.length() == 0) {
+ if (fullpath.empty()) {
destroy ();
throw failed_constructor();
}
- _path = canonical_path (fullpath);
+ /* discover canonical fullpath */
- if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
- _path += G_DIR_SEPARATOR;
+ char buf[PATH_MAX+1];
+ if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
+ error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
+ destroy ();
+ throw failed_constructor();
}
- /* these two are just provisional settings. set_state()
- will likely override them.
- */
-
- _name = _current_snapshot_name = snapshot_name;
+ _path = string(buf);
+
+ /* we require _path to end with a dir separator */
- set_history_depth (Config->get_history_depth());
+ if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
+ _path += G_DIR_SEPARATOR;
+ }
- _current_frame_rate = _engine.frame_rate ();
- _nominal_frame_rate = _current_frame_rate;
- _base_frame_rate = _current_frame_rate;
+ /* is it new ? */
- _tempo_map = new TempoMap (_current_frame_rate);
- _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+ _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+ /* finish initialization that can't be done in a normal C++ constructor
+ definition.
+ */
- _non_soloed_outs_muted = false;
- _listen_cnt = 0;
- _solo_isolated_cnt = 0;
+ timerclear (&last_mmc_step);
g_atomic_int_set (&processing_prohibited, 0);
- _transport_speed = 0;
- _default_transport_speed = 1.0;
- _last_transport_speed = 0;
- _target_transport_speed = 0;
- auto_play_legal = false;
- transport_sub_state = 0;
- _transport_frame = 0;
- _requested_return_frame = -1;
- _session_range_location = 0;
g_atomic_int_set (&_record_status, Disabled);
- loop_changing = false;
- play_loop = false;
- have_looped = false;
- _last_roll_location = 0;
- _last_roll_or_reversal_location = 0;
- _last_record_location = 0;
- pending_locate_frame = 0;
- pending_locate_roll = false;
- pending_locate_flush = false;
- state_was_pending = false;
- set_next_event ();
- outbound_mtc_timecode_frame = 0;
- next_quarter_frame_to_send = -1;
- current_block_size = 0;
- solo_update_disabled = false;
- _have_captured = false;
- _worst_output_latency = 0;
- _worst_input_latency = 0;
- _worst_track_latency = 0;
- _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
- _was_seamless = Config->get_seamless_loop ();
- _slave = 0;
- _send_qf_mtc = false;
- _pframes_since_last_mtc = 0;
g_atomic_int_set (&_playback_load, 100);
g_atomic_int_set (&_capture_load, 100);
- _play_range = false;
- _exporting = false;
- pending_abort = false;
- _adding_routes_in_progress = false;
- destructive_index = 0;
- first_file_data_format_reset = true;
- first_file_header_format_reset = true;
- post_export_sync = false;
- midi_control_ui = 0;
- _step_editors = 0;
- no_questions_about_missing_files = false;
- _speakers.reset (new Speakers);
- _clicks_cleared = 0;
- ignore_route_processor_changes = false;
- _pre_export_mmc_enabled = false;
-
- AudioDiskstream::allocate_working_buffers();
-
- /* default short fade = 15ms */
-
- SndFileSource::setup_standard_crossfades (*this, frame_rate());
-
- last_mmc_step.tv_sec = 0;
- last_mmc_step.tv_usec = 0;
- step_speed = 0.0;
-
- /* click sounds are unset by default, which causes us to internal
- waveforms for clicks.
- */
-
- click_length = 0;
- click_emphasis_length = 0;
- _clicking = false;
-
- process_function = &Session::process_with_events;
+ set_next_event ();
+ _all_route_group->set_active (true, this);
+ interpolation.add_channel_to (0, 0);
if (config.get_use_video_sync()) {
waiting_for_sync_offset = true;
@@ -239,32 +174,26 @@ Session::first_stage_init (string fullpath, string snapshot_name)
waiting_for_sync_offset = false;
}
- last_timecode_when = 0;
- last_timecode_valid = false;
-
- sync_time_vars ();
-
last_rr_session_dir = session_dirs.begin();
- refresh_disk_space ();
+
+ set_history_depth (Config->get_history_depth());
+
+ if (how_many_dsp_threads () > 1) {
+ /* For now, only create the graph if we are using >1 DSP threads, as
+ it is a bit slower than the old code with 1 thread.
+ */
+ _process_graph.reset (new Graph (*this));
+ }
/* default: assume simple stereo speaker configuration */
_speakers->setup_default_speakers (2);
- /* slave stuff */
-
- average_slave_delta = 1800; // !!! why 1800 ????
- have_first_delta_accumulator = false;
- delta_accumulator_cnt = 0;
- _slave_state = Stopped;
-
_solo_cut_control.reset (new ProxyControllable (_("solo cut control (dB)"), PBD::Controllable::GainLike,
boost::bind (&RCConfiguration::set_solo_mute_gain, Config, _1),
boost::bind (&RCConfiguration::get_solo_mute_gain, Config)));
add_controllable (_solo_cut_control);
- _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
-
/* These are all static "per-class" signals */
SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
@@ -277,87 +206,101 @@ Session::first_stage_init (string fullpath, string snapshot_name)
Delivery::disable_panners ();
IO::disable_connecting ();
+
+ AudioFileSource::set_peak_dir (_session_dir->peak_path());
}
int
-Session::second_stage_init ()
+Session::post_engine_init ()
{
- AudioFileSource::set_peak_dir (_session_dir->peak_path());
+ BootMessage (_("Set block size and sample rate"));
- if (!_is_new) {
- if (load_state (_current_snapshot_name)) {
- return -1;
- }
- }
+ set_block_size (_engine.samples_per_cycle());
+ set_frame_rate (_engine.sample_rate());
+
+ n_physical_outputs = _engine.n_physical_outputs ();
+ n_physical_inputs = _engine.n_physical_inputs ();
+ BootMessage (_("Using configuration"));
+
+ _midi_ports = new MidiPortManager;
+ setup_midi_machine_control ();
+
if (_butler->start_thread()) {
return -1;
}
-
+
if (start_midi_thread ()) {
return -1;
}
+
+ setup_click_sounds (0);
+ setup_midi_control ();
- setup_midi_machine_control ();
-
- // set_state() will call setup_raid_path(), but if it's a new session we need
- // to call setup_raid_path() here.
-
- if (state_tree) {
- if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
- return -1;
- }
- } else {
- setup_raid_path(_path);
- }
+ _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
+ _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
- /* we can't save till after ::when_engine_running() is called,
- because otherwise we save state with no connections made.
- therefore, we reset _state_of_the_state because ::set_state()
- will have cleared it.
+ try {
+ /* tempo map requires sample rate knowledge */
- we also have to include Loading so that any events that get
- generated between here and the end of ::when_engine_running()
- will be processed directly rather than queued.
- */
+ _tempo_map = new TempoMap (_current_frame_rate);
+ _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+
+ /* MidiClock requires a tempo map */
- _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
+ midi_clock = new MidiClockTicker ();
+ midi_clock->set_session (this);
- _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
- _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
- setup_click_sounds (0);
- setup_midi_control ();
+ /* crossfades require sample rate knowledge */
- /* Pay attention ... */
+ SndFileSource::setup_standard_crossfades (*this, frame_rate());
+ _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
+
+ AudioDiskstream::allocate_working_buffers();
+ refresh_disk_space ();
+
+ /* we're finally ready to call set_state() ... all objects have
+ * been created, the engine is running.
+ */
+
+ if (state_tree) {
+ if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
+ return -1;
+ }
+ } else {
+ // set_state() will call setup_raid_path(), but if it's a new session we need
+ // to call setup_raid_path() here.
+ setup_raid_path (_path);
+ }
- _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
- _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
+ /* ENGINE */
- midi_clock = new MidiClockTicker ();
- midi_clock->set_session (this);
+ boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
+ boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
+
+ Config->map_parameters (ff);
+ config.map_parameters (ft);
- try {
when_engine_running ();
- }
-
- /* handle this one in a different way than all others, so that its clear what happened */
- catch (AudioEngine::PortRegistrationFailure& err) {
+ _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
+ _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
+
+ } catch (AudioEngine::PortRegistrationFailure& err) {
+ /* handle this one in a different way than all others, so that its clear what happened */
error << err.what() << endmsg;
return -1;
- }
-
- catch (...) {
+ } catch (...) {
return -1;
}
BootMessage (_("Reset Remote Controls"));
- send_full_time_code (0);
+ // send_full_time_code (0);
_engine.transport_locate (0);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
+ _mmc->send (MIDI::MachineControlCommand (Timecode::Time ()));
MIDI::Name::MidiPatchManager::instance().set_session (this);
@@ -366,18 +309,16 @@ Session::second_stage_init ()
_state_of_the_state = Clean;
- Port::set_connecting_blocked (false);
-
DirtyChanged (); /* EMIT SIGNAL */
- if (state_was_pending) {
- save_state (_current_snapshot_name);
+ if (_is_new) {
+ save_state ("");
+ } else if (state_was_pending) {
+ save_state ("");
remove_pending_capture_state ();
state_was_pending = false;
}
- BootMessage (_("Session loading complete"));
-
return 0;
}
@@ -604,8 +545,6 @@ Session::create (const string& session_template, BusProfile* bus_profile)
add_monitor_section ();
}
- save_state ("");
-
return 0;
}
@@ -684,62 +623,6 @@ Session::remove_state (string snapshot_name)
}
}
-#ifdef HAVE_JACK_SESSION
-void
-Session::jack_session_event (jack_session_event_t * event)
-{
- char timebuf[128], *tmp;
- time_t n;
- struct tm local_time;
-
- time (&n);
- localtime_r (&n, &local_time);
- strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
-
- while ((tmp = strchr(timebuf, ':'))) { *tmp = '.'; }
-
- if (event->type == JackSessionSaveTemplate)
- {
- if (save_template( timebuf )) {
- event->flags = JackSessionSaveError;
- } else {
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " -T ";
- cmd += timebuf;
-
- event->command_line = strdup (cmd.c_str());
- }
- }
- else
- {
- if (save_state (timebuf)) {
- event->flags = JackSessionSaveError;
- } else {
- std::string xml_path (_session_dir->root_path());
- std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
- xml_path = Glib::build_filename (xml_path, legalized_filename);
-
- string cmd ("ardour3 -P -U ");
- cmd += event->client_uuid;
- cmd += " \"";
- cmd += xml_path;
- cmd += '\"';
-
- event->command_line = strdup (cmd.c_str());
- }
- }
-
- jack_session_reply (_engine.jack(), event);
-
- if (event->type == JackSessionSaveAndQuit) {
- Quit (); /* EMIT SIGNAL */
- }
-
- jack_session_event_free( event );
-}
-#endif
-
/** @param snapshot_name Name to save under, without .ardour / .pending prefix */
int
Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
@@ -1034,6 +917,15 @@ Session::state (bool full_state)
/* various options */
+ list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
+ if (!midi_port_nodes.empty()) {
+ XMLNode* midi_port_stuff = new XMLNode ("MIDIPorts");
+ for (list<XMLNode*>::const_iterator n = midi_port_nodes.begin(); n != midi_port_nodes.end(); ++n) {
+ midi_port_stuff->add_child_nocopy (**n);
+ }
+ node->add_child_nocopy (*midi_port_stuff);
+ }
+
node->add_child_nocopy (config.get_variables ());
node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
@@ -1239,6 +1131,11 @@ Session::set_state (const XMLNode& node, int version)
Evoral::init_event_id_counter (atoi (prop->value()));
}
+
+ if ((child = find_named_node (node, "MIDIPorts")) != 0) {
+ _midi_ports->set_midi_port_states (child->children());
+ }
+
IO::disable_connecting ();
Stateful::save_extra_xml (node);
@@ -3453,11 +3350,11 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
- MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
+ _mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
} else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
- MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
+ _mmc->set_send_device_id (Config->get_mmc_send_device_id());
} else if (p == "midi-control") {
@@ -3520,7 +3417,7 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "send-mmc") {
- MIDI::Manager::instance()->mmc()->enable_send (Config->get_send_mmc ());
+ _mmc->enable_send (Config->get_send_mmc ());
} else if (p == "midi-feedback") {
@@ -3578,13 +3475,13 @@ Session::config_changed (std::string p, bool ours)
} else if (p == "initial-program-change") {
- if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) {
+ if (_mmc->output_port() && Config->get_initial_program_change() >= 0) {
MIDI::byte buf[2];
buf[0] = MIDI::program; // channel zero by default
buf[1] = (Config->get_initial_program_change() & 0x7f);
- MIDI::Manager::instance()->mmc()->output_port()->midimsg (buf, sizeof (buf), 0);
+ _mmc->output_port()->midimsg (buf, sizeof (buf), 0);
}
} else if (p == "solo-mute-override") {
// catch_up_on_solo_mute_override ();
@@ -3648,27 +3545,28 @@ Session::load_diskstreams_2X (XMLNode const & node, int)
void
Session::setup_midi_machine_control ()
{
- MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-
- mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
- mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
- mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
- mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
- mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
- mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
- mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
- mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
- mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
- mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
- mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
- mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
+ _mmc = new MIDI::MachineControl;
+ _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
+
+ _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
+ _mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
+ _mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
+ _mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
+ _mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
+ _mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
+ _mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
+ _mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
+ _mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
+ _mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
+ _mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
+ _mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
/* also handle MIDI SPP because its so common */
- mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
- mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
- mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
+ _mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
+ _mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
+ _mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
}
boost::shared_ptr<Controllable>
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
index 18805afa90..0f2186c09b 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::backend_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,93 +211,13 @@ 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;
}
-void
-Session::jack_timebase_callback (jack_transport_state_t /*state*/,
- pframes_t /*nframes*/,
- jack_position_t* pos,
- int /*new_position*/)
-{
- Timecode::BBT_Time bbt;
-
- /* BBT info */
-
- if (_tempo_map) {
-
- TempoMetric metric (_tempo_map->metric_at (_transport_frame));
-
- try {
- _tempo_map->bbt_time_rt (_transport_frame, bbt);
-
- pos->bar = bbt.bars;
- pos->beat = bbt.beats;
- pos->tick = bbt.ticks;
-
- // XXX still need to set bar_start_tick
-
- pos->beats_per_bar = metric.meter().divisions_per_bar();
- pos->beat_type = metric.meter().note_divisor();
- pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat;
- pos->beats_per_minute = metric.tempo().beats_per_minute();
-
- pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
-
- } catch (...) {
- /* no message */
- }
- }
-
-#ifdef HAVE_JACK_VIDEO_SUPPORT
- //poke audio video ratio so Ardour can track Video Sync
- pos->audio_frames_per_video_frame = frame_rate() / timecode_frames_per_second();
- pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio);
-#endif
-
-#if 0
- /* Timecode info */
-
- pos->timecode_offset = config.get_timecode_offset();
- t.timecode_frame_rate = timecode_frames_per_second();
- pos->valid = jack_position_bits_t (pos->valid | JackPositionTimecode;
-
- if (_transport_speed) {
-
- if (play_loop) {
-
- Location* location = _locations.auto_loop_location();
-
- if (location) {
-
- t.transport_state = JackTransportLooping;
- t.loop_start = location->start();
- t.loop_end = location->end();
- t.valid = jack_transport_bits_t (t.valid | JackTransportLoop);
-
- } else {
-
- t.loop_start = 0;
- t.loop_end = 0;
- t.transport_state = JackTransportRolling;
-
- }
-
- } else {
-
- t.loop_start = 0;
- t.loop_end = 0;
- t.transport_state = JackTransportRolling;
-
- }
-
- }
-#endif
-}
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..ab6d68fd67 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -33,7 +33,6 @@
#include "midi++/mmc.h"
#include "midi++/port.h"
-#include "midi++/manager.h"
#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
@@ -610,11 +609,23 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
have_looped = false;
- if (!_engine.freewheeling()) {
- send_full_time_code (_transport_frame);
+ /* don't bother with this stuff if we're disconnected from the engine,
+ because there will be no process callbacks to deliver stuff from
+ */
+
+ if (_engine.connected() && !_engine.freewheeling()) {
+ // need to queue this in the next RT cycle
+ _send_timecode_update = true;
if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+
+ /* This (::non_realtime_stop()) gets called by main
+ process thread, which will lead to confusion
+ when calling AsyncMIDIPort::write().
+
+ Something must be done. XXX
+ */
send_mmc_locate (_transport_frame);
}
}
@@ -1260,7 +1271,7 @@ Session::start_transport ()
Timecode::Time time;
timecode_time_subframes (_transport_frame, time);
if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
}
}
@@ -1338,8 +1349,9 @@ Session::use_sync_source (Slave* new_slave)
_slave = new_slave;
DEBUG_TRACE (DEBUG::Slave, string_compose ("set new slave to %1\n", _slave));
-
- send_full_time_code (_transport_frame);
+
+ // need to queue this for next process() cycle
+ _send_timecode_update = true;
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
@@ -1380,7 +1392,7 @@ Session::switch_to_sync_source (SyncSource src)
}
try {
- new_slave = new MTC_Slave (*this, *MIDI::Manager::instance()->mtc_input_port());
+ new_slave = new MTC_Slave (*this, *_midi_ports->mtc_input_port());
}
catch (failed_constructor& err) {
@@ -1409,7 +1421,7 @@ Session::switch_to_sync_source (SyncSource src)
}
try {
- new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
+ new_slave = new MIDIClock_Slave (*this, *_midi_ports->midi_clock_input_port(), 24);
}
catch (failed_constructor& err) {
@@ -1426,7 +1438,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 +1628,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)
{
@@ -1646,7 +1648,7 @@ Session::send_mmc_locate (framepos_t t)
if (!_engine.freewheeling()) {
Timecode::Time time;
timecode_time_subframes (t, time);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+ _mmc->send (MIDI::MachineControlCommand (time));
}
}
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/ticker.cc b/libs/ardour/ticker.cc
index f32cdf9415..9a29df2641 100644
--- a/libs/ardour/ticker.cc
+++ b/libs/ardour/ticker.cc
@@ -19,13 +19,12 @@
#include "pbd/compose.h"
#include "pbd/stacktrace.h"
-#include "midi++/port.h"
-#include "midi++/jack_midi_port.h"
-#include "midi++/manager.h"
-
#include "evoral/midi_events.h"
+#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/ticker.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
@@ -95,16 +94,16 @@ public:
MidiClockTicker::MidiClockTicker ()
- : _midi_port (0)
- , _ppqn (24)
+ : _ppqn (24)
, _last_tick (0.0)
+ , _send_pos (false)
+ , _send_state (false)
{
_pos.reset (new Position());
}
MidiClockTicker::~MidiClockTicker()
{
- _midi_port = 0;
_pos.reset (0);
}
@@ -115,7 +114,6 @@ MidiClockTicker::set_session (Session* s)
if (_session) {
_session->TransportStateChange.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_state_changed, this));
- _session->PositionChanged.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::position_changed, this, _1));
_session->TransportLooped.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::transport_looped, this));
_session->Located.connect_same_thread (_session_connections, boost::bind (&MidiClockTicker::session_located, this));
@@ -139,41 +137,20 @@ MidiClockTicker::session_located()
return;
}
- if (_pos->speed == 0.0f) {
- uint32_t where = llrint (_pos->midi_beats);
- send_position_event (where, 0);
- } else if (_pos->speed == 1.0f) {
-#if 1
- /* Experimental. To really do this and have accuracy, the
- stop/locate/continue sequence would need queued to send immediately
- before the next midi clock. */
-
- send_stop_event (0);
-
- if (_pos->frame == 0) {
- send_start_event (0);
- } else {
- uint32_t where = llrint (_pos->midi_beats);
- send_position_event (where, 0);
- send_continue_event (0);
- }
-#endif
- } else {
- /* Varispeed not supported */
- }
+ _send_pos = true;
}
void
MidiClockTicker::session_going_away ()
{
SessionHandlePtr::session_going_away();
- _midi_port = 0;
+ _midi_port.reset ();
}
void
MidiClockTicker::update_midi_clock_port()
{
- _midi_port = MIDI::Manager::instance()->midi_clock_output_port();
+ _midi_port = _session->midi_clock_output_port();
}
void
@@ -204,49 +181,12 @@ MidiClockTicker::transport_state_changed()
return;
}
- if (_pos->speed == 1.0f) {
-
- if (_session->get_play_loop()) {
- assert(_session->locations()->auto_loop_location());
-
- if (_pos->frame == _session->locations()->auto_loop_location()->start()) {
- send_start_event(0);
- } else {
- send_continue_event(0);
- }
-
- } else if (_pos->frame == 0) {
- send_start_event(0);
- } else {
- send_continue_event(0);
- }
-
- // send_midi_clock_event (0);
-
- } else if (_pos->speed == 0.0f) {
- send_stop_event (0);
- send_position_event (llrint (_pos->midi_beats), 0);
- }
+ _send_state = true;
// tick (_pos->frame);
}
void
-MidiClockTicker::position_changed (framepos_t)
-{
-#if 0
- const double speed = _session->transport_speed();
- DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Transport Position Change: %1, speed: %2\n", position, speed));
-
- if (speed == 0.0f && Config->get_send_midi_clock()) {
- send_position_event (position, 0);
- }
-
- _last_tick = position;
-#endif
-}
-
-void
MidiClockTicker::transport_looped()
{
Location* loop_location = _session->locations()->auto_loop_location();
@@ -270,18 +210,68 @@ MidiClockTicker::transport_looped()
}
void
-MidiClockTicker::tick (const framepos_t& /* transport_frame */)
+MidiClockTicker::tick (const framepos_t& /* transport_frame */, pframes_t nframes)
{
if (!Config->get_send_midi_clock() || _session == 0 || _session->transport_speed() != 1.0f || _midi_port == 0) {
return;
}
- MIDI::JackMIDIPort* mp = dynamic_cast<MIDI::JackMIDIPort*> (_midi_port);
- if (! mp) {
- return;
+ if (_send_pos) {
+ if (_pos->speed == 0.0f) {
+ uint32_t where = llrint (_pos->midi_beats);
+ send_position_event (where, 0, nframes);
+ } else if (_pos->speed == 1.0f) {
+#if 1
+ /* Experimental. To really do this and have accuracy, the
+ stop/locate/continue sequence would need queued to send immediately
+ before the next midi clock. */
+
+ send_stop_event (0, nframes);
+
+ if (_pos->frame == 0) {
+ send_start_event (0, nframes);
+ } else {
+ uint32_t where = llrint (_pos->midi_beats);
+ send_position_event (where, 0, nframes);
+ send_continue_event (0, nframes);
+ }
+#endif
+ } else {
+ /* Varispeed not supported */
+ }
+
+ _send_pos = true;
}
- const framepos_t end = _pos->frame + mp->nframes_this_cycle();
+ if (_send_state) {
+ if (_pos->speed == 1.0f) {
+ if (_session->get_play_loop()) {
+ assert(_session->locations()->auto_loop_location());
+
+ if (_pos->frame == _session->locations()->auto_loop_location()->start()) {
+ send_start_event (0, nframes);
+ } else {
+ send_continue_event (0, nframes);
+ }
+
+ } else if (_pos->frame == 0) {
+ send_start_event (0, nframes);
+ } else {
+ send_continue_event (0, nframes);
+ }
+
+ // send_midi_clock_event (0);
+
+ } else if (_pos->speed == 0.0f) {
+ send_stop_event (0, nframes);
+ send_position_event (llrint (_pos->midi_beats), 0, nframes);
+ }
+
+ _send_state = false;
+ }
+
+
+ const framepos_t end = _pos->frame + nframes;
double iter = _last_tick;
while (true) {
@@ -291,14 +281,14 @@ MidiClockTicker::tick (const framepos_t& /* transport_frame */)
DEBUG_TRACE (DEBUG::MidiClock,
string_compose ("Tick: iter: %1, last tick time: %2, next tick time: %3, offset: %4, cycle length: %5\n",
- iter, _last_tick, next_tick, next_tick_offset, mp ? mp->nframes_this_cycle() : 0));
+ iter, _last_tick, next_tick, next_tick_offset, nframes));
- if (!mp || (next_tick_offset >= mp->nframes_this_cycle())) {
+ if (next_tick_offset >= nframes) {
break;
}
if (next_tick_offset >= 0) {
- send_midi_clock_event (next_tick_offset);
+ send_midi_clock_event (next_tick_offset, nframes);
}
iter = next_tick;
@@ -308,7 +298,6 @@ MidiClockTicker::tick (const framepos_t& /* transport_frame */)
_pos->frame = end;
}
-
double
MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
{
@@ -322,7 +311,7 @@ MidiClockTicker::one_ppqn_in_frames (framepos_t transport_position)
}
void
-MidiClockTicker::send_midi_clock_event (pframes_t offset)
+MidiClockTicker::send_midi_clock_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -330,12 +319,13 @@ MidiClockTicker::send_midi_clock_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Tick with offset %1\n", offset));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CLOCK };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_CLOCK };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_start_event (pframes_t offset)
+MidiClockTicker::send_start_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -343,12 +333,13 @@ MidiClockTicker::send_start_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Start %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_START };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_START };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_continue_event (pframes_t offset)
+MidiClockTicker::send_continue_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -356,12 +347,13 @@ MidiClockTicker::send_continue_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Continue %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_CONTINUE };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_CONTINUE };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_stop_event (pframes_t offset)
+MidiClockTicker::send_stop_event (pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -369,12 +361,13 @@ MidiClockTicker::send_stop_event (pframes_t offset)
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Stop %1\n", _last_tick));
- static uint8_t _midi_clock_tick[1] = { MIDI_CMD_COMMON_STOP };
- _midi_port->write (_midi_clock_tick, 1, offset);
+ static uint8_t tick_byte = { MIDI_CMD_COMMON_STOP };
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 1, &tick_byte);
}
void
-MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset)
+MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset, pframes_t nframes)
{
if (!_midi_port) {
return;
@@ -391,7 +384,8 @@ MidiClockTicker::send_position_event (uint32_t midi_beats, pframes_t offset)
msg[1] = midi_beats & 0x007f;
msg[2] = midi_beats >> 7;
- _midi_port->midimsg (msg, sizeof (msg), offset);
+ MidiBuffer& mb (_midi_port->get_midi_buffer (nframes));
+ mb.push_back (offset, 3, &msg[0]);
DEBUG_TRACE (DEBUG::MidiClock, string_compose ("Song Position Sent: %1\n", midi_beats));
}
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
index f02863393e..57239cb841 100644
--- a/libs/ardour/track.cc
+++ b/libs/ardour/track.cc
@@ -550,15 +550,15 @@ Track::playlist ()
}
void
-Track::request_jack_monitors_input (bool m)
+Track::request_input_monitoring (bool m)
{
- _diskstream->request_jack_monitors_input (m);
+ _diskstream->request_input_monitoring (m);
}
void
-Track::ensure_jack_monitors_input (bool m)
+Track::ensure_input_monitoring (bool m)
{
- _diskstream->ensure_jack_monitors_input (m);
+ _diskstream->ensure_input_monitoring (m);
}
bool
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 87dce06a2a..485a2284bc 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -1,10 +1,11 @@
-#!/usr/bin/env python
+1#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
from waflib import Options
import os
import sys
import re
import subprocess
+import sys
# default state file version for this build
CURRENT_SESSION_FILE_VERSION = 3001
@@ -22,6 +23,7 @@ path_prefix = 'libs/ardour/'
libardour_sources = [
'amp.cc',
'analyser.cc',
+ 'async_midi_port.cc',
'audio_buffer.cc',
'audio_diskstream.cc',
'audio_library.cc',
@@ -44,6 +46,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',
@@ -128,6 +131,7 @@ libardour_sources = [
'midi_stretch.cc',
'midi_track.cc',
'midi_ui.cc',
+ 'midiport_manager.cc',
'mix.cc',
'monitor_processor.cc',
'mtc_slave.cc',
@@ -151,6 +155,7 @@ libardour_sources = [
'plugin_manager.cc',
'port.cc',
'port_insert.cc',
+ 'port_manager.cc',
'port_set.cc',
'process_thread.cc',
'processor.cc',
@@ -178,6 +183,7 @@ libardour_sources = [
'session_events.cc',
'session_export.cc',
'session_handle.cc',
+ 'session_jack.cc',
'session_ltc.cc',
'session_metadata.cc',
'session_midi.cc',
@@ -259,6 +265,10 @@ def configure(conf):
atleast_version='0.1.0')
autowaf.check_pkg(conf, 'sigc++-2.0', uselib_store='SIGCPP',
atleast_version='2.0')
+
+ if re.search ("linux", sys.platform) != None:
+ autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+
if Options.options.lv2:
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2',
atleast_version='1.0.0', mandatory=True)
@@ -303,60 +313,6 @@ def configure(conf):
conf.check(header_name='unistd.h', define_name='HAVE_UNISTD',mandatory=False)
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-void callback(jack_status_t code, const char* reason, void* arg) { return; }
-int main(int argc, char **argv) {
- jack_client_t* c;
- jack_on_info_shutdown(c, callback, (void*) 0);
- return 0;
-}''',
- uselib= [ 'JACK' ],
- msg = 'Checking for jack_on_info_shutdown',
- define_name = 'HAVE_JACK_ON_INFO_SHUTDOWN',
- okmsg = 'present')
-
- missing_jack_message = 'missing - a version of JACK that supports jack_port_set_latency_range() is required to compile Ardour3.'
-
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-int main(int argc, char **argv) {
- jack_port_t* p;
- jack_latency_range_t r;
- jack_port_set_latency_range(p, JackCaptureLatency, &r);
- return 0;
-}''',
- uselib = [ 'JACK' ],
- msg = 'Checking for new JACK latency API',
- okmsg = 'present',
- mandatory = True,
- errmsg = missing_jack_message)
-
- conf.check_cc(fragment = '''
-#include <jack/jack.h>
-int main(int argc, char **argv) {
- jack_port_type_get_buffer_size((jack_client_t*)0, "");
- return 0;
-}''',
- uselib = [ 'JACK' ],
- msg = 'Checking for new jack_port_type_get_buffer_size',
- okmsg = 'present',
- mandatory = True,
- errmsg = missing_jack_message)
-
- conf.check_cc(fragment = '''
-#include <jack/transport.h>
-int main(int argc, char** argv) {
- jack_position_t pos;
- pos.valid & JackVideoFrameOffset;
- return 0;
-}''',
- uselib= [ 'JACK' ],
- msg = 'Checking for JackVideoFrameOffset',
- define_name = 'HAVE_JACK_VIDEO_SUPPORT',
- mandatory = False,
- okmsg = 'present')
-
if flac_supported():
conf.define ('HAVE_FLAC', 1)
if ogg_supported():