part-way through getting the audioengine changes to compile

This commit is contained in:
Paul Davis 2013-07-24 19:29:45 -04:00
parent 3d95822716
commit 9ac6bb9bef
43 changed files with 891 additions and 878 deletions

View file

@ -33,16 +33,16 @@ class AudioEngine;
class AudioBackend {
public:
enum State {
Stopped = 0x1,
Running = 0x2,
Paused = 0x4,
Freewheeling = 0x8,
};
AudioBackend (AudioEngine& e) : engine (e), _state (Stopped) {}
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 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.
@ -261,7 +261,7 @@ class AudioBackend {
virtual TransportState transport_state () { return TransportStopped; }
/** Attempt to locate the transport to @param pos
*/
virtual void transport_locate (framepos_t 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)
*/
@ -275,11 +275,11 @@ class AudioBackend {
* 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 set_time_master (bool /*yn*/) { return 0; }
virtual framecnt_t sample_rate () const;
virtual pframes_t samples_per_cycle () const;
virtual int usecs_per_cycle () const { return _usecs_per_cycle; }
virtual int usecs_per_cycle () const { return 1000000 * (samples_per_cycle() / sample_rate()); }
virtual size_t raw_buffer_size (DataType t);
/* Process time */
@ -326,7 +326,7 @@ class AudioBackend {
* 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 0; }
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
@ -338,17 +338,6 @@ class AudioBackend {
private:
AudioEngine& engine;
State _state;
std::string _target_device;
float _target_sample_rate;
uint32_t _target_buffer_size;
SampleFormat _target_sample_format;
bool _target_interleaved;
uint32_t _target_input_channels;
uint32_t _target_output_channels;
uin32_t _target_systemic_input_latency;
uin32_t _target_systemic_input_latency;
};
}

View file

@ -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) {
@ -159,7 +159,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 */

View file

@ -48,7 +48,7 @@ class AudioPort : public Port
protected:
friend class AudioEngine;
AudioPort (std::string const &, Flags);
AudioPort (std::string const &, PortFlags);
/* special access for engine only */
Sample* engine_get_whole_audio_buffer ();

View file

@ -47,6 +47,7 @@
#include "ardour/session_handle.h"
#include "ardour/types.h"
#include "ardour/chan_count.h"
#include "ardour/port_manager.h"
#ifdef HAVE_JACK_SESSION
#include <jack/session.h>
@ -61,37 +62,60 @@ class Session;
class ProcessThread;
class AudioBackend;
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);
static AudioEngine* create (const std::string& client_name, const std::string& session_uuid);
virtual ~AudioEngine ();
static int discover_backends();
int discover_backends();
std::vector<std::string> available_backends() const;
std::string current_backend_name () const;
int set_backend (const std::string&);
ProcessThread* main_thread() const { return _main_thread; }
std::string client_name() const { return backend_client_name; }
/* 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);
int stop (bool forever = false);
int start ();
int pause ();
int freewheel (bool onoff);
/* 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);
framecnt_t processed_frames() const { return _processed_frames; }
float get_cpu_load();
void set_session (Session *);
void remove_session (); // not a replacement for SessionHandle::session_going_away()
@ -161,8 +185,11 @@ public:
int process_callback (pframes_t nframes);
private:
AudioEngine (const std::string& client_name, const std::string& session_uuid);
static AudioEngine* _instance;
AudioBackend* _backend;
Glib::Threads::Mutex _process_lock;
Glib::Threads::Cond session_removed;
bool session_remove_pending;
@ -187,6 +214,8 @@ public:
Glib::Threads::Thread* m_meter_thread;
ProcessThread* _main_thread;
std::string backend_client_name;
std::string backend_session_uuid;
void meter_thread ();
void start_metering_thread ();
@ -196,6 +225,11 @@ public:
void parameter_changed (const std::string&);
PBD::ScopedConnection config_connection;
typedef std::map<std::string,AudioBackend*> BackendMap;
BackendMap _backends;
AudioBackend* backend_discover (const std::string&);
void drop_backend ();
};
} // namespace ARDOUR

View file

@ -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__ */

View file

@ -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__

View file

@ -38,6 +38,7 @@ extern const char* const route_templates_dir_name;
extern const char* const surfaces_dir_name;
extern const char* const user_config_dir_name;
extern const char* const panner_dir_name;
extern const char* const backend_dir_name;
};

View file

@ -93,6 +93,18 @@ ifdef HAVE_JACK_SESSION
ChanCount n_physical (unsigned long) const;
void get_physical (DataType, unsigned long, std::vector<std::string> &);
/* pffooo */
std::string _target_device;
float _target_sample_rate;
uint32_t _target_buffer_size;
SampleFormat _target_sample_format;
bool _target_interleaved;
uint32_t _target_input_channels;
uint32_t _target_output_channels;
uin32_t _target_systemic_input_latency;
uin32_t _target_systemic_input_latency;
};
}

View file

@ -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);

View file

@ -59,7 +59,7 @@ class MidiPort : public Port {
protected:
friend class AudioEngine;
MidiPort (const std::string& name, Flags);
MidiPort (const std::string& name, PortFlags);
private:
MidiBuffer* _buffer;

View file

@ -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,8 +86,8 @@ public:
virtual int connect (Port *);
int disconnect (Port *);
void request_monitor_input (bool);
void ensure_monitor_input (bool);
void request_input_monitoring (bool);
void ensure_input_monitoring (bool);
bool monitoring_input () const;
int reestablish ();
int reconnect ();
@ -99,7 +95,7 @@ public:
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;
@ -122,8 +118,6 @@ public:
bool physically_connected () const;
static void set_engine (AudioEngine *);
PBD::Signal1<void,bool> MonitorInputChanged;
static PBD::Signal2<void,boost::shared_ptr<Port>,boost::shared_ptr<Port> > PostDisconnect;
static PBD::Signal0<void> PortDrop;
@ -143,9 +137,9 @@ public:
protected:
Port (std::string const &, DataType, Flags);
Port (std::string const &, DataType, PortFlags);
jack_port_t* _jack_port; ///< JACK port
PortEngine::PortHandle _port_handle;
static bool _connecting_blocked;
static pframes_t _global_port_buffer_offset; /* access only from process() tree */
@ -156,15 +150,13 @@ protected:
jack_latency_range_t _private_playback_latency;
jack_latency_range_t _private_capture_latency;
static AudioEngine* _engine; ///< the AudioEngine
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;

View file

@ -0,0 +1,139 @@
/*
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 {
/** 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() {}
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;
virtual bool connected() const = 0;
virtual int set_port_name (PortHandle, const std::string&) = 0;
virtual std::string get_port_name (PortHandle) const = 0;
virtual PortHandle* get_port_by_name (const std::string&) const = 0;
virtual PortHandle register_port (const std::string&, DataType::Symbol, ARDOUR::PortFlags) = 0;
virtual void unregister_port (PortHandle) = 0;
virtual bool connected (PortHandle) = 0;
virtual int disconnect_all (PortHandle) = 0;
virtual bool connected_to (PortHandle, const std::string&) = 0;
virtual int get_connections (PortHandle, std::vector<std::string>&) = 0;
virtual bool physically_connected (PortHandle) = 0;
virtual int connect (PortHandle, const std::string&) = 0;
virtual int disconnect (PortHandle, const std::string&) = 0;
/* MIDI */
virtual void midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index) = 0;
virtual int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size) = 0;
virtual uint32_t get_midi_event_count (void* port_buffer);
virtual void midi_clear (void* port_buffer);
/* Monitoring */
virtual bool can_monitor_input() const = 0;
virtual int request_input_monitoring (PortHandle, bool) = 0;
virtual int ensure_input_monitoring (PortHandle, bool) = 0;
virtual bool monitoring_input (PortHandle) = 0;
/* Latency management
*/
struct LatencyRange {
uint32_t min;
uint32_t max;
};
virtual void set_latency_range (PortHandle, int dir, LatencyRange) = 0;
virtual LatencyRange get_latency_range (PortHandle, int dir) = 0;
virtual LatencyRange get_connected_latency_range (PortHandle, int dir) = 0;
virtual void* get_buffer (PortHandle, pframes_t) = 0;
virtual pframes_t last_frame_time () const = 0;
};
}
#endif /* __libardour_port_engine_h__ */

View file

@ -32,11 +32,11 @@
#include "pbd/rcu.h"
#include "ardour/chan_count.h"
#include "ardour/port.h"
#include "ardour/port_engine.h"
namespace ARDOUR {
class Port;
class PortManager
{
public:
@ -44,36 +44,44 @@ class PortManager
PortManager();
virtual ~PortManager() {}
PortEngine& port_engine() { return *_impl; }
/* Port registration */
virtual boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname) = 0;
virtual boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname) = 0;
virtual int unregister_port (boost::shared_ptr<Port>) = 0;
boost::shared_ptr<Port> register_input_port (DataType, const std::string& portname);
boost::shared_ptr<Port> register_output_port (DataType, const std::string& portname);
int unregister_port (boost::shared_ptr<Port>);
/* Port connectivity */
virtual int connect (const std::string& source, const std::string& destination) = 0;
virtual int disconnect (const std::string& source, const std::string& destination) = 0;
virtual int disconnect (boost::shared_ptr<Port>) = 0;
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>);
bool connected (const std::string&);
/* other Port management */
virtual bool port_is_physical (const std::string&) const = 0;
virtual void get_physical_outputs (DataType type, std::vector<std::string>&) = 0;
virtual void get_physical_inputs (DataType type, std::vector<std::string>&) = 0;
virtual boost::shared_ptr<Port> get_port_by_name (const std::string &) = 0;
virtual void port_renamed (const std::string&, const std::string&) = 0;
virtual ChanCount n_physical_outputs () const = 0;
virtual ChanCount n_physical_inputs () const = 0;
virtual const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags) = 0;
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>&);
boost::shared_ptr<Port> get_port_by_name (const std::string &);
void port_renamed (const std::string&, const std::string&);
ChanCount n_physical_outputs () const;
ChanCount n_physical_inputs () const;
const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags);
void remove_all_ports ();
/* per-Port monitoring */
virtual bool can_request_input_monitoring () const = 0;
virtual void request_input_monitoring (const std::string&, bool) const = 0;
bool can_request_input_monitoring () const;
void request_input_monitoring (const std::string&, bool) const;
void ensure_input_monitoring (const std::string&, bool) const;
std::string make_port_name_relative (const std::string&) const;
std::string make_port_name_non_relative (const std::string&) const;
bool port_is_mine (const std::string&) const;
class PortRegistrationFailure : public std::exception {
public:
@ -82,23 +90,17 @@ class PortManager
~PortRegistrationFailure () throw () {}
virtual const char *what() const throw () { return reason.c_str(); }
const char *what() const throw () { return reason.c_str(); }
private:
std::string reason;
};
protected:
typedef void* PortHandle;
PortHandle register (const std::string&, DataType type, Port::Flags);
void unregister (PortHandle);
bool connected (PortHandle);
int disconnect_all (PortHandle);
bool connected_to (PortHandle, const std::string);
int get_connections (PortHandle, std::vector<std::string>&);
private:
protected:
PortEngine* _impl;
SerializedRCUManager<Ports> ports;
boost::shared_ptr<Port> register_port (DataType type, const std::string& portname, bool input);
void port_registration_failure (const std::string& portname);
};

View file

@ -584,6 +584,25 @@ 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 = 1,
IsOutput = 2,
};
} // namespace ARDOUR

View file

@ -1729,7 +1729,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 ();
}
@ -1750,7 +1750,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 ();
@ -2016,12 +2016,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);
}
}
@ -2344,13 +2344,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)

View file

@ -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);
}

View file

@ -25,11 +25,14 @@
#include <sstream>
#include <glibmm/timer.h>
#include <glibmm/pattern.h>
#include <glibmm/module.h>
#include "pbd/epa.h"
#include "pbd/file_utils.h"
#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
#include "pbd/unknown_type.h"
#include "pbd/epa.h"
#include <jack/weakjack.h>
@ -39,7 +42,9 @@
#include "midi++/manager.h"
#include "ardour/audio_port.h"
#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/backend_search_path.h"
#include "ardour/buffer.h"
#include "ardour/cycle_timer.h"
#include "ardour/internal_send.h"
@ -58,8 +63,9 @@ using namespace PBD;
gint AudioEngine::m_meter_exit;
AudioEngine* AudioEngine::_instance = 0;
AudioEngine::AudioEngine ()
: session_remove_pending (false)
AudioEngine::AudioEngine (const std::string& bcn, const std::string& bsu)
: _backend (0)
, session_remove_pending (false)
, session_removal_countdown (-1)
, monitor_check_interval (INT32_MAX)
, last_monitor_check (0)
@ -70,14 +76,16 @@ AudioEngine::AudioEngine ()
, port_remove_in_progress (false)
, m_meter_thread (0)
, _main_thread (0)
, backend_client_name (bcn)
, backend_session_uuid (bsu)
{
g_atomic_int_set (&m_meter_exit, 0);
Port::set_engine (this);
}
AudioEngine::~AudioEngine ()
{
drop_backend ();
config_connection.disconnect ();
{
@ -88,18 +96,38 @@ AudioEngine::~AudioEngine ()
}
AudioEngine*
AudioEngine::create ()
AudioEngine::create (const std::string& bcn, const std::string& bsu)
{
if (_instance) {
return _instance;
}
return new AudioEngine;
return new AudioEngine (bcn, bsu);
}
jack_client_t*
AudioEngine::jack() const
void
AudioEngine::drop_backend ()
{
return _jack;
if (_backend) {
_backend->stop ();
delete _backend;
_backend = 0;
}
}
int
AudioEngine::set_backend (const std::string& name)
{
BackendMap::iterator b = _backends.find (name);
if (b == _backends.end()) {
return -1;
}
drop_backend ();
_backend = b->second;
return 0;
}
void
@ -120,70 +148,6 @@ _thread_init_callback (void * /*arg*/)
}
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;
}
void
AudioEngine::split_cycle (pframes_t offset)
@ -208,7 +172,6 @@ AudioEngine::split_cycle (pframes_t offset)
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;
@ -436,7 +399,7 @@ AudioEngine::set_session (Session *s)
start_metering_thread ();
pframes_t blocksize = jack_get_buffer_size (_jack);
pframes_t blocksize = _backend->get_buffer_size ();
/* page in as much of the session process code as we
can before we really start running.
@ -484,288 +447,6 @@ AudioEngine::remove_session ()
remove_all_ports ();
}
void
AudioEngine::port_registration_failure (const std::string& portname)
{
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;
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);
}
throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
}
boost::shared_ptr<Port>
AudioEngine::register_port (DataType dtype, const string& portname, bool input)
{
boost::shared_ptr<Port> newport;
try {
if (dtype == DataType::AUDIO) {
newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput)));
} else if (dtype == DataType::MIDI) {
newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput)));
} else {
throw PortRegistrationFailure("unable to create port (unknown type)");
}
RCUWriter<Ports> writer (ports);
boost::shared_ptr<Ports> ps = writer.get_copy ();
ps->insert (make_pair (make_port_name_relative (portname), newport));
/* writer goes out of scope, forces update */
return newport;
}
catch (PortRegistrationFailure& err) {
throw err;
} catch (std::exception& e) {
throw PortRegistrationFailure(string_compose(
_("unable to create port: %1"), e.what()).c_str());
} catch (...) {
throw PortRegistrationFailure("unable to create port (unknown error)");
}
}
boost::shared_ptr<Port>
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)
{
return register_port (type, portname, false);
}
int
AudioEngine::unregister_port (boost::shared_ptr<Port> port)
{
/* caller must hold process lock */
if (!_running) {
/* probably happening when the engine has been halted by JACK,
in which case, there is nothing we can do here.
*/
return 0;
}
{
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;
}
int
AudioEngine::connect (const string& source, const string& destination)
{
int ret;
if (!_running) {
if (!_has_run) {
fatal << _("connect called before engine was started") << endmsg;
/*NOTREACHED*/
} else {
return -1;
}
}
string s = make_port_name_non_relative (source);
string d = make_port_name_non_relative (destination);
boost::shared_ptr<Port> src = get_port_by_name (s);
boost::shared_ptr<Port> dst = get_port_by_name (d);
if (src) {
ret = src->connect (d);
} else if (dst) {
ret = dst->connect (s);
} else {
/* neither port is known to us, and this API isn't intended for use as a general patch bay */
ret = -1;
}
if (ret > 0) {
/* already exists - no error, no warning */
} else if (ret < 0) {
error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
source, s, destination, d)
<< endmsg;
}
return ret;
}
int
AudioEngine::disconnect (const string& source, const string& destination)
{
int ret;
if (!_running) {
if (!_has_run) {
fatal << _("disconnect called before engine was started") << endmsg;
/*NOTREACHED*/
} else {
return -1;
}
}
string s = make_port_name_non_relative (source);
string d = make_port_name_non_relative (destination);
boost::shared_ptr<Port> src = get_port_by_name (s);
boost::shared_ptr<Port> dst = get_port_by_name (d);
if (src) {
ret = src->disconnect (d);
} else if (dst) {
ret = dst->disconnect (s);
} else {
/* neither port is known to us, and this API isn't intended for use as a general patch bay */
ret = -1;
}
return ret;
}
int
AudioEngine::disconnect (boost::shared_ptr<Port> port)
{
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 ();
}
ARDOUR::framecnt_t
AudioEngine::frame_rate () const
{
GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
if (_frame_rate == 0) {
return (_frame_rate = jack_get_sample_rate (_priv_jack));
} else {
return _frame_rate;
}
}
size_t
AudioEngine::raw_buffer_size (DataType t)
{
std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
return (s != _raw_buffer_sizes.end()) ? s->second : 0;
}
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;
}
}
/** @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> ();
}
}
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 (jack_port_name (x->second->jack_port()));
if (check != rel) {
x->second->set_name (check);
}
return x->second;
}
return boost::shared_ptr<Port> ();
}
void
AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
{
RCUWriter<Ports> writer (ports);
boost::shared_ptr<Ports> p = writer.get_copy();
Ports::iterator x = p->find (old_relative_name);
if (x != p->end()) {
boost::shared_ptr<Port> port = x->second;
p->erase (x);
p->insert (make_pair (new_relative_name, port));
}
}
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;
}
}
return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
}
void
AudioEngine::died ()
@ -777,104 +458,11 @@ AudioEngine::died ()
_running = false;
_buffer_size = 0;
_frame_rate = 0;
_jack = 0;
}
bool
AudioEngine::can_request_hardware_monitoring ()
{
GET_PRIVATE_JACK_POINTER_RET (_jack,false);
const char ** ports;
if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
return false;
}
free (ports);
return true;
}
ChanCount
AudioEngine::n_physical (unsigned long flags) const
{
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);
}
}
free (ports);
return c;
}
ChanCount
AudioEngine::n_physical_inputs () const
{
return n_physical (JackPortIsInput);
}
ChanCount
AudioEngine::n_physical_outputs () const
{
return n_physical (JackPortIsOutput);
}
void
AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy)
{
GET_PRIVATE_JACK_POINTER (_jack);
const char ** ports;
if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) {
return;
}
if (ports) {
for (uint32_t i = 0; ports[i]; ++i) {
if (strstr (ports[i], "Midi-Through")) {
continue;
}
phy.push_back (ports[i]);
}
free (ports);
}
}
/** 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)
{
get_physical (type, JackPortIsOutput, ins);
}
/** 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)
{
get_physical (type, JackPortIsInput, outs);
}
int
AudioEngine::reset_timebase ()
{
GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
if (_session) {
if (_session->config.get_jack_time_master()) {
_backend->set_time_master (true);
@ -885,93 +473,6 @@ AudioEngine::reset_timebase ()
return 0;
}
void
AudioEngine::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
AudioEngine::make_port_name_relative (string portname) const
{
string::size_type len;
string::size_type n;
len = portname.length();
for (n = 0; n < len; ++n) {
if (portname[n] == ':') {
break;
}
}
if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
return portname.substr (n+1);
}
return portname;
}
string
AudioEngine::make_port_name_non_relative (string portname) const
{
string str;
if (portname.find_first_of (':') != string::npos) {
return portname;
}
str = jack_client_name;
str += ':';
str += portname;
return str;
}
bool
AudioEngine::port_is_mine (const string& portname) const
{
if (portname.find_first_of (':') != string::npos) {
if (portname.substr (0, jack_client_name.length ()) != jack_client_name) {
return false;
}
}
return true;
}
bool
AudioEngine::port_is_physical (const std::string& portname) const
{
GET_PRIVATE_JACK_POINTER_RET(_jack, false);
jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
if (!port) {
return false;
}
return jack_port_flags (port) & JackPortIsPhysical;
}
void
AudioEngine::destroy ()
@ -986,6 +487,8 @@ AudioEngine::discover_backends ()
vector<std::string> backend_modules;
AudioBackend* backend;
_backends.clear ();
Glib::PatternSpec so_extension_pattern("*.so");
Glib::PatternSpec dylib_extension_pattern("*.dylib");
@ -1002,10 +505,12 @@ AudioEngine::discover_backends ()
_backends.insert (make_pair (backend->name(), backend));
}
}
return _backends.size();
}
AudioBackend*
AudioEngine::backend_discover (string path)
AudioEngine::backend_discover (const string& path)
{
Glib::Module* module = new Glib::Module(path);
AudioBackend* (*dfunc)(void);
@ -1030,3 +535,202 @@ AudioEngine::backend_discover (string path)
return backend;
}
/* BACKEND PROXY WRAPPERS */
int
AudioEngine::start ()
{
if (!_backend) {
return -1;
}
if (!_running) {
if (_session) {
BootMessage (_("Connect session to engine"));
_session->set_frame_rate (_backend->sample_rate());
}
_processed_frames = 0;
last_monitor_check = 0;
if (_backend->start() == 0) {
_running = true;
_has_run = true;
Running(); /* EMIT SIGNAL */
} else {
/* should report error? */
}
}
return _running ? 0 : -1;
}
int
AudioEngine::stop ()
{
if (!_backend) {
return 0;
}
return _backend->stop ();
}
int
AudioEngine::pause ()
{
if (!_backend) {
return 0;
}
return _backend->pause ();
}
int
AudioEngine::freewheel (bool start_stop)
{
if (!_backend) {
return -1;
}
/* _freewheeling will be set when first Freewheel signal occurs */
return _backend->freewheel (start_stop);
}
float
AudioEngine::get_cpu_load() const
{
if (!_backend) {
return 0.0;
}
return _backend->get_cpu_load ();
}
void
AudioEngine::transport_start ()
{
if (!_backend) {
return;
}
return _backend->transport_start ();
}
void
AudioEngine::transport_stop ()
{
if (!_backend) {
return;
}
return _backend->transport_stop ();
}
TransportState
AudioEngine::transport_state ()
{
if (!_backend) {
return TransportStopped;
}
return _backend->transport_state ();
}
void
AudioEngine::transport_locate (framepos_t pos)
{
if (!_backend) {
return;
}
return _backend->transport_locate (pos);
}
framepos_t
AudioEngine::transport_frame()
{
if (!_backend) {
return 0;
}
return _backend->transport_frame ();
}
framecnt_t
AudioEngine::sample_rate () const
{
if (!_backend) {
return 0;
}
return _backend->sample_rate ();
}
pframes_t
AudioEngine::samples_per_cycle () const
{
if (!_backend) {
return 0;
}
return _backend->samples_per_cycle ();
}
int
AudioEngine::usecs_per_cycle () const
{
if (!_backend) {
return -1;
}
return _backend->start ();
}
size_t
AudioEngine::raw_buffer_size (DataType t)
{
if (!_backend) {
return -1;
}
return _backend->raw_buffer_size (t);
}
pframes_t
AudioEngine::sample_time ()
{
if (!_backend) {
return 0;
}
return _backend->sample_time ();
}
pframes_t
AudioEngine::sample_time_at_cycle_start ()
{
if (!_backend) {
return 0;
}
return _backend->sample_time_at_cycle_start ();
}
pframes_t
AudioEngine::samples_since_cycle_start ()
{
if (!_backend) {
return 0;
}
return _backend->samples_since_cycle_start ();
}
bool
AudioEngine::get_sync_offset (pframes_t& offset) const
{
if (!_backend) {
return false;
}
return _backend->get_sync_offset (offset);
}
int
AudioEngine::create_process_thread (boost::function<void()> func, pthread_t* thr, size_t stacksize)
{
if (!_backend) {
return -1;
}
return _backend->create_process_thread (func, thr, stacksize);
}

View file

@ -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

View file

@ -454,16 +454,13 @@ Bundle::connected_to_anything (AudioEngine& engine)
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 (engine.connected (ports[j])) {
return true;
}
}
}

View file

@ -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 ();
}

View file

@ -37,6 +37,7 @@ const char* const templates_dir_name = X_("templates");
const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
const char* const panner_dir_name = X_("panners");
const char* const backend_dir_name = X_("backends");
/* these should end up using variants of PROGRAM_NAME */
#ifdef __APPLE__

View file

@ -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)

View file

@ -54,7 +54,7 @@ ExportGraphBuilder::ExportGraphBuilder (Session const & session)
: session (session)
, thread_pool (hardware_concurrency())
{
process_buffer_frames = session.engine().frames_per_cycle();
process_buffer_frames = session.engine().samples_per_cycle();
}
ExportGraphBuilder::~ExportGraphBuilder ()
@ -505,7 +505,7 @@ ExportGraphBuilder::ChannelConfig::ChannelConfig (ExportGraphBuilder & parent, F
config = new_config;
framecnt_t max_frames = parent.session.engine().frames_per_cycle();
framecnt_t max_frames = parent.session.engine().samples_per_cycle();
interleaver.reset (new Interleaver<Sample> ());
interleaver->init (new_config.channel_config->get_n_chans(), max_frames);

View file

@ -338,7 +338,7 @@ void
ARDOUR::init_post_engine ()
{
/* the MIDI Manager is needed by the ControlProtocolManager */
MIDI::Manager::create (AudioEngine::instance()->jack());
MIDI::Manager::create (AudioEngine::instance()->port_engine());
ControlProtocolManager::instance().discover_control_protocols ();

View file

@ -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;
}

View file

@ -6,12 +6,43 @@
int
JACKAudioBackend::start ()
{
Glib::Threads::Mutex::Lock lm (_state_lock);
GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
if (running()) {
/* already running */
return 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
@ -70,6 +101,39 @@ JACKAudioBackend::get_parameters (Parameters& params) const
return 0;
}
/* parameters */
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;
}
}
ARDOUR::framecnt_t
AudioEngine::frame_rate () const
{
GET_PRIVATE_JACK_POINTER_RET (_jack, 0);
if (_frame_rate == 0) {
return (_frame_rate = jack_get_sample_rate (_priv_jack));
} else {
return _frame_rate;
}
}
size_t
AudioEngine::raw_buffer_size (DataType t)
{
std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
return (s != _raw_buffer_sizes.end()) ? s->second : 0;
}
/*--- private support methods ---*/
int
@ -539,8 +603,8 @@ JACKAudioBackend::process_thread ()
GET_PRIVATE_JACK_POINTER_RET(_jack,0);
pframes_t nframes = jack_cycle_wait (_priv_jack);
if (process_callback (nframes)) {
if (engine.process_callback (nframes)) {
return 0;
}

View file

@ -420,15 +420,15 @@ bool
LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
{
bool engine_init_called = false;
framepos_t now = session.engine().frame_time_at_cycle_start();
framepos_t now = session.engine().sample_time_at_cycle_start();
framepos_t sess_pos = session.transport_frame(); // corresponds to now
framecnt_t nframes = session.engine().frames_per_cycle();
framecnt_t nframes = session.engine().samples_per_cycle();
jack_default_audio_sample_t *in;
Sample* in;
boost::shared_ptr<Port> ltcport = session.ltc_input_port();
in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltcport->jack_port(), nframes);
in = (Sample*) AudioEngine::instance()->port_engine().get_buffer (ltcport->port_handle(), nframes);
frameoffset_t skip = now - (monotonic_cnt + nframes);
monotonic_cnt = now;
@ -441,7 +441,7 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
else if (engine_dll_initstate != transport_direction && ltc_speed != 0) {
engine_dll_initstate = transport_direction;
init_engine_dll(last_ltc_frame + rint(ltc_speed * double(2 * nframes + now - last_timestamp)),
session.engine().frames_per_cycle());
session.engine().samples_per_cycle());
engine_init_called = true;
}
@ -521,8 +521,8 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos)
t0 = t1;
t1 += b * e + e2;
e2 += c * e;
speed_flt = (t1 - t0) / double(session.engine().frames_per_cycle());
DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().frames_per_cycle() ));
speed_flt = (t1 - t0) / double(session.engine().samples_per_cycle());
DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", t0, t1, e, speed_flt, e2 - session.engine().samples_per_cycle() ));
} else {
DEBUG_TRACE (DEBUG::LTC, string_compose ("LTC adjusting elapsed (no DLL) from %1 by %2\n", elapsed, (2 * nframes * ltc_speed)));
speed_flt = 0;

View file

@ -190,55 +190,6 @@ MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data)
return true;
}
/** Push an event into the buffer.
*
* Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
* That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
* Realtime safe.
* @return false if operation failed (not enough room)
*/
bool
MidiBuffer::push_back(const jack_midi_event_t& ev)
{
const size_t stamp_size = sizeof(TimeType);
if (_size + stamp_size + ev.size >= _capacity) {
cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
return false;
}
if (!Evoral::midi_event_is_valid(ev.buffer, ev.size)) {
cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl;
return false;
}
#ifndef NDEBUG
if (DEBUG::MidiIO & PBD::debug_bits) {
DEBUG_STR_DECL(a);
DEBUG_STR_APPEND(a, string_compose ("midibuffer %1 push jack event @ %2 sz %3 ", this, ev.time, ev.size));
for (size_t i=0; i < ev.size; ++i) {
DEBUG_STR_APPEND(a,hex);
DEBUG_STR_APPEND(a,"0x");
DEBUG_STR_APPEND(a,(int)ev.buffer[i]);
DEBUG_STR_APPEND(a,' ');
}
DEBUG_STR_APPEND(a,'\n');
DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str());
}
#endif
uint8_t* const write_loc = _data + _size;
*((TimeType*)write_loc) = ev.time;
memcpy(write_loc + stamp_size, ev.buffer, ev.size);
_size += stamp_size + ev.size;
_silent = false;
return true;
}
/** Reserve space for a new event in the buffer.
*
* This call is for copying MIDI directly into the buffer, the data location

View file

@ -1097,7 +1097,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;
@ -1250,7 +1250,7 @@ MidiDiskstream::ensure_jack_monitors_input (bool yn)
boost::shared_ptr<MidiPort> sp = _source_port.lock ();
if (sp) {
sp->ensure_jack_monitors_input (yn);
sp->ensure_input_monitoring (yn);
}
}

View file

@ -26,7 +26,9 @@
using namespace ARDOUR;
using namespace std;
MidiPort::MidiPort (const std::string& name, Flags flags)
#define port_engine AudioEngine::instance()->port_engine()
MidiPort::MidiPort (const std::string& name, PortFlags flags)
: Port (name, DataType::MIDI, flags)
, _has_been_mixed_down (false)
, _resolve_required (false)
@ -48,7 +50,7 @@ MidiPort::cycle_start (pframes_t nframes)
_buffer->clear ();
if (sends_output ()) {
jack_midi_clear_buffer (jack_port_get_buffer (_jack_port, nframes));
port_engine.midi_clear (port_engine.get_buffer (_port_handle, nframes));
}
}
@ -63,31 +65,33 @@ MidiPort::get_midi_buffer (pframes_t nframes)
if (_input_active) {
void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
const pframes_t event_count = jack_midi_get_event_count (jack_buffer);
void* buffer = port_engine.get_buffer (_port_handle, nframes);
const pframes_t event_count = port_engine.get_midi_event_count (buffer);
/* suck all relevant MIDI events from the JACK MIDI port buffer
/* suck all relevant MIDI events from the MIDI port buffer
into our MidiBuffer
*/
for (pframes_t i = 0; i < event_count; ++i) {
jack_midi_event_t ev;
pframes_t timestamp;
size_t size;
uint8_t* buf;
jack_midi_event_get (&ev, jack_buffer, i);
port_engine.midi_event_get (timestamp, size, &buf, buffer, i);
if (ev.buffer[0] == 0xfe) {
if (buf[0] == 0xfe) {
/* throw away active sensing */
continue;
}
/* check that the event is in the acceptable time range */
if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) &&
(ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
_buffer->push_back (ev);
if ((timestamp >= (_global_port_buffer_offset + _port_buffer_offset)) &&
(timestamp < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
_buffer->push_back (timestamp, size, buf);
} else {
cerr << "Dropping incoming MIDI at time " << ev.time << "; offset="
cerr << "Dropping incoming MIDI at time " << timestamp << "; offset="
<< _global_port_buffer_offset << " limit="
<< (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n";
}
@ -121,7 +125,7 @@ MidiPort::cycle_split ()
}
void
MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when)
MidiPort::resolve_notes (void* port_buffer, MidiBuffer::TimeType when)
{
for (uint8_t channel = 0; channel <= 0xF; channel++) {
@ -132,13 +136,13 @@ MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when)
* that prioritize sustain over AllNotesOff
*/
if (jack_midi_event_write (jack_buffer, when, ev, 3) != 0) {
if (port_engine.midi_event_put (port_buffer, when, ev, 3) != 0) {
cerr << "failed to deliver sustain-zero on channel " << channel << " on port " << name() << endl;
}
ev[1] = MIDI_CTL_ALL_NOTES_OFF;
if (jack_midi_event_write (jack_buffer, 0, ev, 3) != 0) {
if (port_engine.midi_event_put (port_buffer, 0, ev, 3) != 0) {
cerr << "failed to deliver ALL NOTES OFF on channel " << channel << " on port " << name() << endl;
}
}
@ -149,11 +153,11 @@ MidiPort::flush_buffers (pframes_t nframes)
{
if (sends_output ()) {
void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
void* port_buffer = port_engine.get_buffer (_port_handle, nframes);
if (_resolve_required) {
/* resolve all notes at the start of the buffer */
resolve_notes (jack_buffer, 0);
resolve_notes (port_buffer, 0);
_resolve_required = false;
}
@ -166,7 +170,7 @@ MidiPort::flush_buffers (pframes_t nframes)
assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset));
if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) {
if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
if (port_engine.midi_event_put (port_buffer, (pframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
cerr << "write failed, drop flushed note off on the floor, time "
<< ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl;
}

View file

@ -107,7 +107,7 @@ MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
CrossThreadChannel::drain (port->selectable());
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
framepos_t now = _session.engine().frame_time();
framepos_t now = _session.engine().sample_time();
port->parse (now);
}

View file

@ -579,7 +579,7 @@ MTC_Slave::init_engine_dll (framepos_t pos, framepos_t inc)
bool
MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
{
framepos_t now = session.engine().frame_time_at_cycle_start();
framepos_t now = session.engine().sample_time_at_cycle_start();
framepos_t sess_pos = session.transport_frame(); // corresponds to now
//sess_pos -= session.engine().frames_since_cycle_start();
@ -593,7 +593,7 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
if (last.timestamp == 0) { engine_dll_initstate = 0; }
else if (engine_dll_initstate != transport_direction && last.speed != 0) {
engine_dll_initstate = transport_direction;
init_engine_dll(last.position, session.engine().frames_per_cycle());
init_engine_dll(last.position, session.engine().samples_per_cycle());
engine_dll_reinitialized = true;
}
@ -643,8 +643,8 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos)
te0 = te1;
te1 += be * e + ee2;
ee2 += ce * e;
speed_flt = (te1 - te0) / double(session.engine().frames_per_cycle());
DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().frames_per_cycle() ));
speed_flt = (te1 - te0) / double(session.engine().samples_per_cycle());
DEBUG_TRACE (DEBUG::MTC, string_compose ("engine DLL t0:%1 t1:%2 err:%3 spd:%4 ddt:%5\n", te0, te1, e, speed_flt, ee2 - session.engine().samples_per_cycle() ));
}
}

View file

@ -30,6 +30,7 @@
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/port.h"
#include "ardour/port_engine.h"
#include "i18n.h"
@ -40,13 +41,17 @@ using namespace PBD;
PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
PBD::Signal0<void> Port::PortDrop;
AudioEngine* Port::_engine = 0;
bool Port::_connecting_blocked = false;
pframes_t Port::_global_port_buffer_offset = 0;
pframes_t Port::_cycle_nframes = 0;
/* a handy define to shorten what would otherwise be a needlessly verbose
* repeated phrase
*/
#define port_engine AudioEngine::instance()->port_engine()
/** @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)
@ -64,15 +69,15 @@ Port::Port (std::string const & n, DataType t, Flags f)
assert (_name.find_first_of (':') == std::string::npos);
if (!_engine->connected()) {
if (!port_engine.connected()) {
throw failed_constructor ();
}
if ((_jack_port = jack_port_register (_engine->jack (), _name.c_str (), t.to_jack_type (), _flags, 0)) == 0) {
if ((_jack_port = port_engine.register_port (_name, t.to_port_type (), _flags)) == 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));
}
@ -85,11 +90,9 @@ Port::~Port ()
void
Port::drop ()
{
if (_jack_port) {
if (_engine->jack ()) {
jack_port_unregister (_engine->jack (), _jack_port);
}
_jack_port = 0;
if (_port_handle) {
port_engine.unregister_port (port_handle);
_port_handle = 0;
}
}
@ -97,7 +100,7 @@ Port::drop ()
bool
Port::connected () const
{
return (jack_port_connected (_jack_port) != 0);
return (port_engine.connected (_port_handle) != 0);
}
int
@ -191,9 +194,9 @@ Port::disconnect (std::string const & other)
int r = 0;
if (sends_output ()) {
r = jack_disconnect (_engine->jack (), this_fullname.c_str (), other_fullname.c_str ());
r = _engine->disconnect (this_fullname, other_fullname);
} else {
r = jack_disconnect (_engine->jack (), other_fullname.c_str (), this_fullname.c_str ());
r = _engine->disconnect (other_fullname, this_fullname);
}
if (r == 0) {
@ -236,27 +239,22 @@ Port::disconnect (Port* o)
}
void
Port::set_engine (AudioEngine* e)
Port::request_input_monitoring (bool yn)
{
_engine = e;
port_eengine.request_input_monitoring (_port_handle, yn);
}
void
Port::request_monitor_input (bool yn)
Port::ensure_input_monitoring (bool yn)
{
jack_port_request_monitor (_jack_port, yn);
}
void
Port::ensure_monitor_input (bool yn)
{
jack_port_ensure_monitor (_jack_port, yn);
port_engine.ensure_input_monitoring (_port_handle, yn);
}
bool
Port::monitoring_input () const
{
return jack_port_monitoring_input (_jack_port);
return port_engine.monitoring_input (_port_handle);
}
void

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python
1#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
from waflib import Options
import os
@ -432,6 +432,25 @@ def build(bld):
elif bld.env['build_target'] == 'x86_64':
obj.source += [ 'sse_functions_xmm.cc', 'sse_functions_64bit.s' ]
# the JACK audio backend
obj = bld.shlib (features = 'c cxx cshlib cxxshlib', source='jack_audiobackend.cc')
obj.cxxflags = [ '-fPIC' ]
obj.name = 'jack_audiobackend'
obj.target = 'jack_audiobackend'
obj.uselib = [ 'JACK' ]
obj.use = [ 'ardour' ]
obj.vnum = '1.0.0'
obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
obj.includes = [ '.' ]
obj.defines = [
'PACKAGE="' + I18N_PACKAGE + '"',
'DATA_DIR="' + os.path.normpath(bld.env['DATADIR']) + '"',
'CONFIG_DIR="' + os.path.normpath(bld.env['SYSCONFDIR']) + '"',
'PROGRAM_NAME="' + bld.env['PROGRAM_NAME'] + '"',
]
# i18n
if bld.is_defined('ENABLE_NLS'):
mo_files = bld.path.ant_glob('po/*.mo')

View file

@ -22,9 +22,6 @@
#include <fcntl.h>
#include <errno.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include "pbd/xml++.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
@ -45,37 +42,34 @@ template class EventRingBuffer<timestamp_t>;
}
pthread_t JackMIDIPort::_process_thread;
Signal0<void> JackMIDIPort::JackHalted;
Signal0<void> JackMIDIPort::EngineHalted;
Signal0<void> JackMIDIPort::MakeConnections;
JackMIDIPort::JackMIDIPort (string const & name, Flags flags, jack_client_t* jack_client)
JackMIDIPort::JackMIDIPort (string const & name, Flags flags, ARDOUR::PortEngine& pengine)
: Port (name, flags)
, _port_engine (pengine)
, _port_handle (0)
, _currently_in_cycle (false)
, _nframes_this_cycle (0)
, _jack_client (jack_client)
, _jack_port (0)
, _last_write_timestamp (0)
, output_fifo (512)
, input_fifo (1024)
, xthread (true)
{
assert (jack_client);
init (name, flags);
}
JackMIDIPort::JackMIDIPort (const XMLNode& node, jack_client_t* jack_client)
JackMIDIPort::JackMIDIPort (const XMLNode& node, ARDOUR::PortEngine& pengine)
: Port (node)
, _port_engine (pengine)
, _port_handle (0)
, _currently_in_cycle (false)
, _nframes_this_cycle (0)
, _jack_client (jack_client)
, _jack_port (0)
, _last_write_timestamp (0)
, output_fifo (512)
, input_fifo (1024)
, xthread (true)
{
assert (jack_client);
Descriptor desc (node);
init (desc.tag, desc.flags);
set_state (node);
@ -89,17 +83,15 @@ JackMIDIPort::init (const string& /*name*/, Flags /*flags*/)
}
MakeConnections.connect_same_thread (connect_connection, boost::bind (&JackMIDIPort::make_connections, this));
JackHalted.connect_same_thread (halt_connection, boost::bind (&JackMIDIPort::jack_halted, this));
EngineHalted.connect_same_thread (halt_connection, boost::bind (&JackMIDIPort::engine_halted, this));
}
JackMIDIPort::~JackMIDIPort ()
{
if (_jack_port) {
if (_jack_client) {
jack_port_unregister (_jack_client, _jack_port);
_jack_port = 0;
}
if (_port_handle) {
_port_engine.unregister_port (_port_handle);
_port_handle = 0;
}
}
@ -143,7 +135,7 @@ JackMIDIPort::parse (framecnt_t timestamp)
void
JackMIDIPort::cycle_start (pframes_t nframes)
{
assert (_jack_port);
assert (_port_handle);
_currently_in_cycle = true;
_nframes_this_cycle = nframes;
@ -151,21 +143,23 @@ JackMIDIPort::cycle_start (pframes_t nframes)
assert(_nframes_this_cycle == nframes);
if (sends_output()) {
void *buffer = jack_port_get_buffer (_jack_port, nframes);
void *buffer = _port_engine.get_buffer (_port_handle, nframes);
jack_midi_clear_buffer (buffer);
flush (buffer);
}
if (receives_input()) {
void* jack_buffer = jack_port_get_buffer(_jack_port, nframes);
const pframes_t event_count = jack_midi_get_event_count(jack_buffer);
void* buffer = _port_engine.get_buffer (_port_handle, nframes);
const pframes_t event_count = _port_engine.get_midi_event_count (buffer);
jack_midi_event_t ev;
timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
pframes_t time;
size_t size;
uint8_t* buf;
timestamp_t cycle_start_frame = _port_engine.last_frame_time ();
for (pframes_t i = 0; i < event_count; ++i) {
jack_midi_event_get (&ev, jack_buffer, i);
input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
_port_engine.midi_event_get (time, size, &buf, buffer, i);
input_fifo.write (cycle_start_frame + time, (Evoral::EventType) 0, size, buf);
}
if (event_count) {
@ -178,7 +172,7 @@ void
JackMIDIPort::cycle_end ()
{
if (sends_output()) {
flush (jack_port_get_buffer (_jack_port, _nframes_this_cycle));
flush (_port_engine.get_buffer (_port_handle, _nframes_this_cycle));
}
_currently_in_cycle = false;
@ -186,10 +180,9 @@ JackMIDIPort::cycle_end ()
}
void
JackMIDIPort::jack_halted ()
JackMIDIPort::engine_halted ()
{
_jack_client = 0;
_jack_port = 0;
_port_handle = 0;
}
void
@ -216,7 +209,7 @@ JackMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp)
{
int ret = 0;
if (!_jack_client || !_jack_port) {
if (!_port_handle) {
/* poof ! make it just vanish into thin air, since we are no
longer connected to JACK.
*/
@ -268,18 +261,18 @@ JackMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp)
if (timestamp == 0) {
timestamp = _last_write_timestamp;
}
if ((ret = jack_midi_event_write (jack_port_get_buffer (_jack_port, _nframes_this_cycle),
timestamp, msg, msglen)) == 0) {
if ((ret = _port_engine.midi_event_put (_port_engine.get_buffer (_port_handle, _nframes_this_cycle),
timestamp, msg, msglen)) == 0) {
ret = msglen;
_last_write_timestamp = timestamp;
} else {
cerr << "write of " << msglen << " @ " << timestamp << " failed, port holds "
<< jack_midi_get_event_count (jack_port_get_buffer (_jack_port, _nframes_this_cycle))
<< " port is " << _jack_port
<< _port_engine.get_midi_event_count (_port_engine.get_buffer (_port_handle, _nframes_this_cycle))
<< " port is " << _port_handle
<< " ntf = " << _nframes_this_cycle
<< " buf = " << jack_port_get_buffer (_jack_port, _nframes_this_cycle)
<< " buf = " << _port_engine.get_buffer (_port_handle, _nframes_this_cycle)
<< " ret = " << ret
<< endl;
PBD::stacktrace (cerr, 20);
@ -305,7 +298,7 @@ JackMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp)
}
void
JackMIDIPort::flush (void* jack_port_buffer)
JackMIDIPort::flush (void* port_buffer)
{
RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
size_t written;
@ -320,8 +313,7 @@ JackMIDIPort::flush (void* jack_port_buffer)
Evoral::Event<double>* evp = vec.buf[0];
for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
jack_midi_event_write (jack_port_buffer,
(timestamp_t) evp->time(), evp->buffer(), evp->size());
_port_engine.midi_event_put (port_buffer, (timestamp_t) evp->time(), evp->buffer(), evp->size());
}
}
@ -329,8 +321,7 @@ JackMIDIPort::flush (void* jack_port_buffer)
Evoral::Event<double>* evp = vec.buf[1];
for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
jack_midi_event_write (jack_port_buffer,
(timestamp_t) evp->time(), evp->buffer(), evp->size());
_port_engine.midi_event_put (port_buffer, (timestamp_t) evp->time(), evp->buffer(), evp->size());
}
}
@ -364,8 +355,21 @@ JackMIDIPort::read (byte *, size_t)
int
JackMIDIPort::create_port ()
{
_jack_port = jack_port_register(_jack_client, _tagname.c_str(), JACK_DEFAULT_MIDI_TYPE, _flags, 0);
return _jack_port == 0 ? -1 : 0;
ARDOUR::PortFlags f = ARDOUR::PortFlags (0);
/* convert MIDI::Port::Flags to ARDOUR::PortFlags ... sigh */
if (_flags & IsInput) {
f = ARDOUR::PortFlags (f | ARDOUR::IsInput);
}
if (_flags & IsOutput) {
f = ARDOUR::PortFlags (f | ARDOUR::IsOutput);
}
_port_handle = _port_engine.register_port (_tagname, ARDOUR::DataType::MIDI, f);
return _port_handle == 0 ? -1 : 0;
}
XMLNode&
@ -386,18 +390,16 @@ JackMIDIPort::get_state () const
write (device_inquiry, sizeof (device_inquiry), 0);
#endif
if (_jack_port) {
if (_port_handle) {
const char** jc = jack_port_get_connections (_jack_port);
vector<string> connections;
_port_engine.get_connections (_port_handle, connections);
string connection_string;
if (jc) {
for (int i = 0; jc[i]; ++i) {
if (i > 0) {
connection_string += ',';
}
connection_string += jc[i];
for (vector<string>::iterator i = connections.begin(); i != connections.end(); ++i) {
if (i != connections.begin()) {
connection_string += ',';
}
free (jc);
connection_string += *i;
}
if (!connection_string.empty()) {
@ -435,14 +437,8 @@ JackMIDIPort::make_connections ()
vector<string> ports;
split (_connections, ports, ',');
for (vector<string>::iterator x = ports.begin(); x != ports.end(); ++x) {
if (_jack_client) {
if (receives_input()) {
jack_connect (_jack_client, (*x).c_str(), jack_port_name (_jack_port));
} else {
jack_connect (_jack_client, jack_port_name (_jack_port), (*x).c_str());
}
/* ignore failures */
}
_port_engine.connect (_port_handle, *x);
/* ignore failures */
}
}
@ -462,9 +458,8 @@ JackMIDIPort::is_process_thread()
}
void
JackMIDIPort::reestablish (jack_client_t* jack)
JackMIDIPort::reestablish ()
{
_jack_client = jack;
int const r = create_port ();
if (r) {

View file

@ -36,17 +36,17 @@ using namespace PBD;
Manager *Manager::theManager = 0;
Manager::Manager (jack_client_t* jack)
Manager::Manager (ARDOUR::PortEngine& eng)
: _ports (new PortList)
{
_mmc = new MachineControl (this, jack);
_mmc = new MachineControl (this, eng);
_mtc_input_port = add_port (new MIDI::JackMIDIPort ("MTC in", Port::IsInput, jack));
_mtc_output_port = add_port (new MIDI::JackMIDIPort ("MTC out", Port::IsOutput, jack));
_midi_input_port = add_port (new MIDI::JackMIDIPort ("MIDI control in", Port::IsInput, jack));
_midi_output_port = add_port (new MIDI::JackMIDIPort ("MIDI control out", Port::IsOutput, jack));
_midi_clock_input_port = add_port (new MIDI::JackMIDIPort ("MIDI clock in", Port::IsInput, jack));
_midi_clock_output_port = add_port (new MIDI::JackMIDIPort ("MIDI clock out", Port::IsOutput, jack));
_mtc_input_port = add_port (new MIDI::JackMIDIPort ("MTC in", Port::IsInput, eng));
_mtc_output_port = add_port (new MIDI::JackMIDIPort ("MTC out", Port::IsOutput, eng));
_midi_input_port = add_port (new MIDI::JackMIDIPort ("MIDI control in", Port::IsInput, eng));
_midi_output_port = add_port (new MIDI::JackMIDIPort ("MIDI control out", Port::IsOutput, eng));
_midi_clock_input_port = add_port (new MIDI::JackMIDIPort ("MIDI clock in", Port::IsInput, eng));
_midi_clock_output_port = add_port (new MIDI::JackMIDIPort ("MIDI clock out", Port::IsOutput, eng));
}
Manager::~Manager ()
@ -113,14 +113,14 @@ Manager::cycle_end()
/** Re-register ports that disappear on JACK shutdown */
void
Manager::reestablish (jack_client_t* jack)
Manager::reestablish ()
{
boost::shared_ptr<PortList> pr = _ports.reader ();
for (PortList::const_iterator p = pr->begin(); p != pr->end(); ++p) {
JackMIDIPort* pp = dynamic_cast<JackMIDIPort*> (*p);
if (pp) {
pp->reestablish (jack);
pp->reestablish ();
}
}
}
@ -157,10 +157,10 @@ Manager::port (string const & n)
}
void
Manager::create (jack_client_t* jack)
Manager::create (ARDOUR::PortEngine& eng)
{
assert (theManager == 0);
theManager = new Manager (jack);
theManager = new Manager (eng);
}
void

View file

@ -22,8 +22,6 @@
#include <string>
#include <iostream>
#include <jack/types.h>
#include "pbd/xml++.h"
#include "pbd/crossthread.h"
#include "pbd/signals.h"
@ -36,6 +34,8 @@
#include "midi++/parser.h"
#include "midi++/port.h"
#include "ardour/port_engine.h"
namespace MIDI {
class Channel;
@ -43,8 +43,8 @@ class PortRequest;
class JackMIDIPort : public Port {
public:
JackMIDIPort (std::string const &, Port::Flags, jack_client_t *);
JackMIDIPort (const XMLNode&, jack_client_t *);
JackMIDIPort (std::string const &, Port::Flags, ARDOUR::PortEngine&);
JackMIDIPort (const XMLNode&, ARDOUR::PortEngine&);
~JackMIDIPort ();
XMLNode& get_state () const;
@ -61,7 +61,7 @@ class JackMIDIPort : public Port {
pframes_t nframes_this_cycle() const { return _nframes_this_cycle; }
void reestablish (jack_client_t *);
void reestablish ();
void reconnect ();
static void set_process_thread (pthread_t);
@ -69,13 +69,14 @@ class JackMIDIPort : public Port {
static bool is_process_thread();
static PBD::Signal0<void> MakeConnections;
static PBD::Signal0<void> JackHalted;
static PBD::Signal0<void> EngineHalted;
private:
ARDOUR::PortEngine& _port_engine;
ARDOUR::PortEngine::PortHandle _port_handle;
bool _currently_in_cycle;
pframes_t _nframes_this_cycle;
jack_client_t* _jack_client;
jack_port_t* _jack_port;
timestamp_t _last_write_timestamp;
RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo;
@ -89,8 +90,8 @@ private:
std::string _connections;
PBD::ScopedConnection connect_connection;
PBD::ScopedConnection halt_connection;
void flush (void* jack_port_buffer);
void jack_halted ();
void flush (void* port_buffer);
void engine_halted ();
void make_connections ();
void init (std::string const &, Flags);

View file

@ -29,6 +29,10 @@
#include "midi++/types.h"
#include "midi++/port.h"
namespace ARDOUR {
class PortEngine;
}
namespace MIDI {
class MachineControl;
@ -69,14 +73,14 @@ class Manager {
boost::shared_ptr<const PortList> get_midi_ports() const { return _ports.reader (); }
static void create (jack_client_t* jack);
static void create (ARDOUR::PortEngine&);
static Manager *instance () {
return theManager;
}
static void destroy ();
void reestablish (jack_client_t *);
void reestablish ();
void reconnect ();
PBD::Signal0<void> PortsChanged;
@ -84,7 +88,7 @@ class Manager {
private:
/* This is a SINGLETON pattern */
Manager (jack_client_t *);
Manager (ARDOUR::PortEngine&);
static Manager *theManager;
MIDI::MachineControl* _mmc;

View file

@ -22,11 +22,17 @@
#include <jack/types.h>
#include "timecode/time.h"
#include "pbd/signals.h"
#include "pbd/ringbuffer.h"
#include "midi++/types.h"
#include "midi++/parser.h"
namespace ARDOUR {
class PortEngine;
}
namespace MIDI {
class Port;
@ -89,7 +95,7 @@ class MachineControl
cmdResume = 0x7F
};
MachineControl (Manager *, jack_client_t *);
MachineControl (Manager *, ARDOUR::PortEngine&);
Port* input_port() { return _input_port; }
Port* output_port() { return _output_port; }

View file

@ -22,7 +22,9 @@
#include <map>
#include "timecode/time.h"
#include "pbd/error.h"
#include "midi++/mmc.h"
#include "midi++/port.h"
#include "midi++/jack_midi_port.h"
@ -196,15 +198,15 @@ static void build_mmc_cmd_map ()
}
MachineControl::MachineControl (Manager* m, jack_client_t* jack)
MachineControl::MachineControl (Manager* m, ARDOUR::PortEngine& pengine)
{
build_mmc_cmd_map ();
_receive_device_id = 0x7f;
_send_device_id = 0x7f;
_input_port = m->add_port (new JackMIDIPort ("MMC in", Port::IsInput, jack));
_output_port = m->add_port (new JackMIDIPort ("MMC out", Port::IsOutput, jack));
_input_port = m->add_port (new JackMIDIPort ("MMC in", Port::IsInput, pengine));
_output_port = m->add_port (new JackMIDIPort ("MMC out", Port::IsOutput, pengine));
_input_port->parser()->mmc.connect_same_thread (port_connections, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
_input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this));

View file

@ -26,6 +26,7 @@ out = 'build'
path_prefix = 'libs/midi++2/'
libmidi_sources = [
'midi.cc',
'channel.cc',
@ -68,7 +69,7 @@ def build(bld):
obj.cxxflags = [ '-fPIC', '-DWITH_JACK_MIDI' ]
# everybody loves JACK
obj.export_includes = ['.']
obj.includes = ['.', '../surfaces/control_protocol']
obj.includes = ['.', '../surfaces/control_protocol', '../ardour' ]
obj.name = 'libmidipp'
obj.target = 'midipp'
obj.uselib = 'GLIBMM SIGCPP XML JACK OSX'

View file

@ -1236,7 +1236,7 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port)
}
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name()));
framepos_t now = session->engine().frame_time();
framepos_t now = session->engine().sample_time();
port->parse (now);
}

View file

@ -59,10 +59,10 @@ SurfacePort::SurfacePort (Surface& s)
_input_port = new MIDI::IPMIDIPort (_surface->mcp().ipmidi_base() +_surface->number());
_output_port = _input_port;
} else {
jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack();
ARDOUR::PortEngine& port_engine (ARDOUR::AudioEngine::instance()->port_engine());
_input_port = new MIDI::JackMIDIPort (string_compose (_("%1 in"), _surface->name()), MIDI::Port::IsInput, jack);
_output_port =new MIDI::JackMIDIPort (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack);
_input_port = new MIDI::JackMIDIPort (string_compose (_("%1 in"), _surface->name()), MIDI::Port::IsInput, port_engine);
_output_port =new MIDI::JackMIDIPort (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, port_engine);
/* MackieControl has its own thread for handling input from the input
* port, and we don't want anything handling output from the output