#include <sys/time.h>
#include <regex.h>
+#include <glibmm.h>
+
#include "dummy_audiobackend.h"
+
#include "pbd/error.h"
+#include "ardour/port_manager.h"
#include "i18n.h"
using namespace ARDOUR;
static std::string s_instance_name;
size_t DummyAudioBackend::_max_buffer_size = 8192;
-DummyAudioBackend::DummyAudioBackend (AudioEngine& e)
- : AudioBackend (e)
+DummyAudioBackend::DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info)
+ : AudioBackend (e, info)
, _running (false)
, _freewheeling (false)
, _samplerate (48000)
- , _audio_buffersize (1024)
+ , _samples_per_period (1024)
, _dsp_load (0)
, _n_inputs (0)
, _n_outputs (0)
+ , _n_midi_inputs (0)
+ , _n_midi_outputs (0)
, _systemic_input_latency (0)
, _systemic_output_latency (0)
, _processed_samples (0)
+ , _port_change_flag (false)
{
_instance_name = s_instance_name;
+ pthread_mutex_init (&_port_callback_mutex, 0);
}
DummyAudioBackend::~DummyAudioBackend ()
{
+ pthread_mutex_destroy (&_port_callback_mutex);
}
/* AUDIOBACKEND API */
if (bs <= 0 || bs >= _max_buffer_size) {
return -1;
}
- _audio_buffersize = bs;
+ _samples_per_period = bs;
engine.buffer_size_change (bs);
return 0;
}
uint32_t
DummyAudioBackend::buffer_size () const
{
- return _audio_buffersize;
+ return _samples_per_period;
}
bool
DummyAudioBackend::enumerate_midi_options () const
{
std::vector<std::string> m;
- m.push_back (_("None"));
+ m.push_back (_("1 in, 1 out"));
+ m.push_back (_("2 in, 2 out"));
+ m.push_back (_("8 in, 8 out"));
return m;
}
int
-DummyAudioBackend::set_midi_option (const std::string&)
+DummyAudioBackend::set_midi_option (const std::string& opt)
{
- return -1;
+ if (opt == _("1 in, 1 out")) {
+ _n_midi_inputs = _n_midi_outputs = 1;
+ }
+ else if (opt == _("2 in, 2 out")) {
+ _n_midi_inputs = _n_midi_outputs = 2;
+ }
+ else if (opt == _("8 in, 8 out")) {
+ _n_midi_inputs = _n_midi_outputs = 8;
+ }
+ else {
+ _n_midi_inputs = _n_midi_outputs = 0;
+ }
+ return 0;
}
std::string
DummyAudioBackend::midi_option () const
{
- return "";
+ return ""; // TODO
}
/* State Control */
PBD::error << _("DummyAudioBackend: already active.") << endmsg;
return -1;
}
- if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
- PBD::error << _("DummyAudioBackend: cannot start.") << endmsg;
- }
- int timeout = 5000;
- while (!_running && --timeout > 0) { usleep (1000); }
+ if (_ports.size()) {
+ PBD::warning << _("DummyAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg;
+ _ports.clear();
+ }
- if (timeout == 0 || !_running) {
- PBD::error << _("DummyAudioBackend: failed to start process thread.") << endmsg;
+ if (register_system_ports()) {
+ PBD::error << _("DummyAudioBackend: failed to register system ports.") << endmsg;
return -1;
}
+ engine.sample_rate_change (_samplerate);
+ engine.buffer_size_change (_samples_per_period);
+
if (engine.reestablish_ports ()) {
PBD::error << _("DummyAudioBackend: Could not re-establish ports.") << endmsg;
stop ();
}
engine.reconnect_ports ();
+ _port_change_flag = false;
+
+ if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
+ PBD::error << _("DummyAudioBackend: cannot start.") << endmsg;
+ }
+
+ int timeout = 5000;
+ while (!_running && --timeout > 0) { Glib::usleep (1000); }
+
+ if (timeout == 0 || !_running) {
+ PBD::error << _("DummyAudioBackend: failed to start process thread.") << endmsg;
+ return -1;
+ }
+
return 0;
}
{
void *status;
if (!_running) {
- return -1;
+ return 0;
}
_running = false;
PBD::error << _("DummyAudioBackend: failed to terminate.") << endmsg;
return -1;
}
+ unregister_system_ports();
return 0;
}
int
DummyAudioBackend::freewheel (bool onoff)
{
- if (onoff != _freewheeling) {
+ if (onoff == _freewheeling) {
return 0;
}
_freewheeling = onoff;
{
switch (t) {
case DataType::AUDIO:
- return _max_buffer_size * sizeof(Sample);
+ return _samples_per_period * sizeof(Sample);
case DataType::MIDI:
return _max_buffer_size; // XXX not really limited
}
if (pthread_create (&thread_id, &attr, dummy_process_thread, td)) {
PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
+ pthread_attr_destroy (&attr);
return -1;
}
+ pthread_attr_destroy (&attr);
_threads.push_back (thread_id);
return 0;
{
for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
{
-#ifdef COMPILER_MINGW
- if (*i == GetCurrentThread ()) {
- return true;
- }
-#else // pthreads
if (pthread_equal (*i, pthread_self ()) != 0) {
return true;
}
-#endif
}
return false;
}
void
DummyAudioBackend::update_latencies ()
{
+ // trigger latency callback in RT thread (locked graph)
+ port_connect_add_remove_callback();
}
/* PORTENGINE API */
{
if (name.size () == 0) { return 0; }
if (flags & IsPhysical) { return 0; }
- if (find_port (_instance_name + ":" + name) != NULL) {
- PBD::error << _("DummyBackend::register_port: Port already exists.") << endmsg;
+ return add_port (_instance_name + ":" + name, type, flags);
+}
+
+PortEngine::PortHandle
+DummyAudioBackend::add_port (
+ const std::string& name,
+ ARDOUR::DataType type,
+ ARDOUR::PortFlags flags)
+{
+ assert(name.size ());
+ if (find_port (name)) {
+ PBD::error << _("DummyBackend::register_port: Port already exists:")
+ << " (" << name << ")" << endmsg;
return 0;
}
-
DummyPort* port = NULL;
switch (type) {
case DataType::AUDIO:
- port = new DummyAudioPort (_instance_name + ":" + name, flags);
+ port = new DummyAudioPort (*this, name, flags);
break;
case DataType::MIDI:
- port = new DummyMidiPort (_instance_name + ":" + name, flags);
+ port = new DummyMidiPort (*this, name, flags);
break;
default:
PBD::error << _("DummyBackend::register_port: Invalid Data Type.") << endmsg;
if (!valid_port (port_handle)) {
PBD::error << _("DummyBackend::unregister_port: Invalid Port.") << endmsg;
}
- DummyPort* port = (DummyPort*)port_handle;
- std::vector<DummyPort*>::iterator i = std::find (_ports.begin (), _ports.end (), (DummyPort*)port_handle);
+ DummyPort* port = static_cast<DummyPort*>(port_handle);
+ std::vector<DummyPort*>::iterator i = std::find (_ports.begin (), _ports.end (), static_cast<DummyPort*>(port_handle));
if (i == _ports.end ()) {
PBD::error << _("DummyBackend::unregister_port: Failed to find port") << endmsg;
return;
}
+ disconnect_all(port_handle);
_ports.erase (i);
delete port;
}
+int
+DummyAudioBackend::register_system_ports()
+{
+ LatencyRange lr;
+
+ const int a_ins = _n_inputs > 0 ? _n_inputs : 8;
+ const int a_out = _n_outputs > 0 ? _n_outputs : 8;
+ const int m_ins = _n_midi_inputs > 0 ? _n_midi_inputs : 2;
+ const int m_out = _n_midi_outputs > 0 ? _n_midi_outputs : 2;
+
+ /* audio ports */
+ lr.min = lr.max = _samples_per_period + _systemic_input_latency;
+ for (int i = 1; i <= a_ins; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:capture_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, false, lr);
+ }
+
+ lr.min = lr.max = _samples_per_period + _systemic_output_latency;
+ for (int i = 1; i <= a_out; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, true, lr);
+ }
+
+ /* midi ports */
+ lr.min = lr.max = _samples_per_period + _systemic_input_latency;
+ for (int i = 1; i <= m_ins; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, false, lr);
+ }
+
+ lr.min = lr.max = _samples_per_period + _systemic_output_latency;
+ for (int i = 1; i <= m_out; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, true, lr);
+ }
+ return 0;
+}
+
+void
+DummyAudioBackend::unregister_system_ports()
+{
+ size_t i = 0;
+ while (i < _ports.size ()) {
+ DummyPort* port = _ports[i];
+ if (port->is_physical () && port->is_terminal ()) {
+ port->disconnect_all ();
+ _ports.erase (_ports.begin() + i);
+ } else {
+ ++i;
+ }
+ }
+}
+
int
DummyAudioBackend::connect (const std::string& src, const std::string& dst)
{
uint32_t event_index)
{
assert (buf && port_buffer);
- DummyMidiBuffer& source = * (DummyMidiBuffer*)port_buffer;
+ DummyMidiBuffer& source = * static_cast<DummyMidiBuffer*>(port_buffer);
if (event_index >= source.size ()) {
return -1;
}
const uint8_t* buffer, size_t size)
{
assert (buffer && port_buffer);
- DummyMidiBuffer& dst = * (DummyMidiBuffer*)port_buffer;
+ DummyMidiBuffer& dst = * static_cast<DummyMidiBuffer*>(port_buffer);
if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) {
fprintf (stderr, "DummyMidiBuffer: it's too late for this event.\n");
return -1;
{
for (size_t i = 0; i < _ports.size (); ++i) {
DummyPort* port = _ports[i];
- if ((port->type () == type) && port->is_output () && port->is_physical ()) {
+ if ((port->type () == type) && port->is_input () && port->is_physical ()) {
port_names.push_back (port->name ());
}
}
{
for (size_t i = 0; i < _ports.size (); ++i) {
DummyPort* port = _ports[i];
- if ((port->type () == type) && port->is_input () && port->is_physical ()) {
+ if ((port->type () == type) && port->is_output () && port->is_physical ()) {
port_names.push_back (port->name ());
}
}
_running = true;
_processed_samples = 0;
- struct timeval clock1, clock2;
- ::gettimeofday (&clock1, NULL);
+ manager.registration_callback();
+ manager.graph_order_callback();
+
+ uint64_t clock1, clock2;
+ clock1 = g_get_monotonic_time();
while (_running) {
- if (engine.process_callback (_audio_buffersize)) {
+ if (engine.process_callback (_samples_per_period)) {
return 0;
}
- _processed_samples += _audio_buffersize;
+ _processed_samples += _samples_per_period;
if (!_freewheeling) {
- ::gettimeofday (&clock2, NULL);
- const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000000 + (clock2.tv_usec - clock1.tv_usec);
- const int nomial_time = 1000000 * _audio_buffersize / _samplerate;
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time = clock2 - clock1;
+ const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
_dsp_load = elapsed_time / (float) nomial_time;
if (elapsed_time < nomial_time) {
- ::usleep (nomial_time - elapsed_time);
+ Glib::usleep (nomial_time - elapsed_time);
} else {
- ::usleep (100); // don't hog cpu
+ Glib::usleep (100); // don't hog cpu
}
} else {
_dsp_load = 1.0;
- ::usleep (100); // don't hog cpu
+ Glib::usleep (100); // don't hog cpu
+ }
+ clock1 = g_get_monotonic_time();
+
+ bool connections_changed = false;
+ bool ports_changed = false;
+ if (!pthread_mutex_trylock (&_port_callback_mutex)) {
+ if (_port_change_flag) {
+ ports_changed = true;
+ _port_change_flag = false;
+ }
+ if (!_port_connection_queue.empty ()) {
+ connections_changed = true;
+ }
+ while (!_port_connection_queue.empty ()) {
+ PortConnectData *c = _port_connection_queue.back ();
+ manager.connect_callback (c->a, c->b, c->c);
+ _port_connection_queue.pop_back ();
+ delete c;
+ }
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+ if (ports_changed) {
+ manager.registration_callback();
+ }
+ if (connections_changed) {
+ manager.graph_order_callback();
+ }
+ if (connections_changed || ports_changed) {
+ engine.latency_callback(false);
+ engine.latency_callback(true);
}
- ::gettimeofday (&clock1, NULL);
+
}
_running = false;
return 0;
static boost::shared_ptr<DummyAudioBackend> _instance;
+static boost::shared_ptr<AudioBackend> backend_factory (AudioEngine& e);
+static int instantiate (const std::string& arg1, const std::string& /* arg2 */);
+static int deinstantiate ();
+static bool already_configured ();
+
+static ARDOUR::AudioBackendInfo _descriptor = {
+ "Dummy",
+ instantiate,
+ deinstantiate,
+ backend_factory,
+ already_configured,
+};
+
static boost::shared_ptr<AudioBackend>
backend_factory (AudioEngine& e)
{
if (!_instance) {
- _instance.reset (new DummyAudioBackend (e));
+ _instance.reset (new DummyAudioBackend (e, _descriptor));
}
return _instance;
}
return false;
}
-static ARDOUR::AudioBackendInfo _descriptor = {
- "Dummy",
- instantiate,
- deinstantiate,
- backend_factory,
- already_configured,
-};
-
extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
{
return &_descriptor;
/******************************************************************************/
-DummyPort::DummyPort (const std::string& name, PortFlags flags)
- : _name (name)
+DummyPort::DummyPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : _dummy_backend (b)
+ , _name (name)
, _flags (flags)
{
_capture_latency_range.min = 0;
_capture_latency_range.max = 0;
_playback_latency_range.min = 0;
_playback_latency_range.max = 0;
+ _dummy_backend.port_connect_add_remove_callback();
}
DummyPort::~DummyPort () {
disconnect_all ();
+ _dummy_backend.port_connect_add_remove_callback();
}
}
if (is_connected (port)) {
+#if 0 // don't bother to warn about this for now. just ignore it
PBD::error << _("DummyPort::connect (): ports are already connected:")
<< " (" << name () << ") -> (" << port->name () << ")"
<< endmsg;
+#endif
return -1;
}
_connections.push_back (port);
if (callback) {
port->_connect (this, false);
+ _dummy_backend.port_connect_callback (name(), port->name(), true);
}
}
if (callback) {
port->_disconnect (this, false);
+ _dummy_backend.port_connect_callback (name(), port->name(), false);
}
}
{
while (!_connections.empty ()) {
_connections.back ()->_disconnect (this, false);
+ _dummy_backend.port_connect_callback (name(), _connections.back ()->name(), false);
_connections.pop_back ();
}
}
/******************************************************************************/
-DummyAudioPort::DummyAudioPort (const std::string& name, PortFlags flags)
- : DummyPort (name, flags)
+DummyAudioPort::DummyAudioPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : DummyPort (b, name, flags)
{
memset (_buffer, 0, sizeof (_buffer));
}
}
}
}
+ } else if (is_output () && is_physical () && is_terminal()) {
+ memset (_buffer, 0, n_samples * sizeof (Sample));
}
return _buffer;
}
-DummyMidiPort::DummyMidiPort (const std::string& name, PortFlags flags)
- : DummyPort (name, flags)
+DummyMidiPort::DummyMidiPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : DummyPort (b, name, flags)
{
_buffer.clear ();
}
DummyMidiPort::~DummyMidiPort () { }
+struct MidiEventSorter {
+ bool operator() (const boost::shared_ptr<DummyMidiEvent>& a, const boost::shared_ptr<DummyMidiEvent>& b) {
+ return *a < *b;
+ }
+};
+
void* DummyMidiPort::get_buffer (pframes_t /* nframes */)
{
if (is_input ()) {
_buffer.push_back (boost::shared_ptr<DummyMidiEvent>(new DummyMidiEvent (**it)));
}
}
- std::sort (_buffer.begin (), _buffer.end ());
+ std::sort (_buffer.begin (), _buffer.end (), MidiEventSorter());
+ } else if (is_output () && is_physical () && is_terminal()) {
+ _buffer.clear ();
}
return &_buffer;
}