summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2014-06-08 11:18:18 +0200
committerRobin Gareus <robin@gareus.org>2014-06-08 11:18:18 +0200
commit81182b5bf69e06f67b3ccbca46860ac8e6387d2f (patch)
tree23c2165f66167a0ffbafcb5de3367a5ae2266755 /libs
parent6416a429a888fcb18b9aa5a4aed1d6d8c9a6da1f (diff)
prepare MIDI latency measurement (backend)
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/audioengine.h18
-rw-r--r--libs/ardour/ardour/mididm.h62
-rw-r--r--libs/ardour/audioengine.cc119
-rw-r--r--libs/ardour/mididm.cc88
-rw-r--r--libs/ardour/wscript1
5 files changed, 250 insertions, 38 deletions
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h
index dd57f81e90..7eeae8f205 100644
--- a/libs/ardour/ardour/audioengine.h
+++ b/libs/ardour/ardour/audioengine.h
@@ -54,6 +54,7 @@ namespace ARDOUR {
class InternalPort;
class MidiPort;
+class MIDIDM;
class Port;
class Session;
class ProcessThread;
@@ -191,14 +192,24 @@ public:
/* latency measurement */
- MTDM* mtdm();
+ MTDM* mtdm() { return _mtdm; }
+ MIDIDM* mididm() { return _mididm; }
+
int prepare_for_latency_measurement ();
- int start_latency_detection ();
+ int start_latency_detection (bool);
void stop_latency_detection ();
void set_latency_input_port (const std::string&);
void set_latency_output_port (const std::string&);
uint32_t latency_signal_delay () const { return _latency_signal_latency; }
+ enum LatencyMeasurement {
+ MeasureNone,
+ MeasureAudio,
+ MeasureMIDI
+ };
+
+ LatencyMeasurement measuring_latency () const { return _measuring_latency; }
+
private:
AudioEngine ();
@@ -221,7 +232,8 @@ public:
Glib::Threads::Thread* m_meter_thread;
ProcessThread* _main_thread;
MTDM* _mtdm;
- bool _measuring_latency;
+ MIDIDM* _mididm;
+ LatencyMeasurement _measuring_latency;
PortEngine::PortHandle _latency_input_port;
PortEngine::PortHandle _latency_output_port;
framecnt_t _latency_flush_frames;
diff --git a/libs/ardour/ardour/mididm.h b/libs/ardour/ardour/mididm.h
new file mode 100644
index 0000000000..7c7211300c
--- /dev/null
+++ b/libs/ardour/ardour/mididm.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * 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_mididm_h__
+#define __libardour_mididm_h__
+
+#include "ardour/types.h"
+#include "ardour/libardour_visibility.h"
+
+namespace ARDOUR {
+
+class PortEngine;
+
+class LIBARDOUR_API MIDIDM
+{
+public:
+
+ MIDIDM (framecnt_t sample_rate);
+
+ int process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out);
+
+ framecnt_t latency (void) { return _cnt_total > 10 ? _avg_delay : 0; }
+ framecnt_t processed (void) { return _cnt_total; }
+ double deviation (void) { return _cnt_total > 1 ? sqrt(_var_s / ((double)(_cnt_total - 1))) : 0; }
+ bool ok (void) { return _cnt_total > 200; }
+ bool have_signal (void) { return (_monotonic_cnt - _last_signal_tme) < (uint64_t) _sample_rate ; }
+
+private:
+
+ framecnt_t _sample_rate;
+
+ uint64_t _monotonic_cnt;
+ uint64_t _last_signal_tme;
+
+ uint64_t _cnt_total;
+ uint64_t _dly_total;
+ uint32_t _min_delay;
+ uint32_t _max_delay;
+ double _avg_delay;
+ double _var_m;
+ double _var_s;
+
+};
+
+}
+
+#endif /* __libardour_mididm_h__ */
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 2436cc82d0..d424300c49 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -48,6 +48,7 @@
#include "ardour/meter.h"
#include "ardour/midi_port.h"
#include "ardour/midiport_manager.h"
+#include "ardour/mididm.h"
#include "ardour/mtdm.h"
#include "ardour/port.h"
#include "ardour/process_thread.h"
@@ -73,7 +74,8 @@ AudioEngine::AudioEngine ()
, m_meter_thread (0)
, _main_thread (0)
, _mtdm (0)
- , _measuring_latency (false)
+ , _mididm (0)
+ , _measuring_latency (MeasureNone)
, _latency_input_port (0)
, _latency_output_port (0)
, _latency_flush_frames (0)
@@ -195,7 +197,7 @@ AudioEngine::process_callback (pframes_t nframes)
bool return_after_remove_check = false;
- if (_measuring_latency && _mtdm) {
+ if (_measuring_latency == MeasureAudio && _mtdm) {
/* run a normal cycle from the perspective of the PortManager
so that we get silence on all registered ports.
@@ -218,6 +220,28 @@ AudioEngine::process_callback (pframes_t nframes)
PortManager::cycle_end (nframes);
return_after_remove_check = true;
+ } else if (_measuring_latency == MeasureMIDI && _mididm) {
+ /* run a normal cycle from the perspective of the PortManager
+ so that we get silence on all registered ports.
+
+ we overwrite the silence on the two ports used for latency
+ measurement.
+ */
+
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+
+ if (_latency_input_port && _latency_output_port) {
+ PortEngine& pe (port_engine());
+
+ _mididm->process (nframes, pe,
+ pe.get_buffer (_latency_input_port, nframes),
+ pe.get_buffer (_latency_output_port, nframes));
+ }
+
+ PortManager::cycle_end (nframes);
+ return_after_remove_check = true;
+
} else if (_latency_flush_frames) {
/* wait for the appropriate duration for the MTDM signal to
@@ -660,7 +684,7 @@ AudioEngine::stop (bool for_latency)
_running = false;
_processed_frames = 0;
- _measuring_latency = false;
+ _measuring_latency = MeasureNone;
_latency_output_port = 0;
_latency_input_port = 0;
_started_for_latency = false;
@@ -1029,12 +1053,6 @@ AudioEngine::setup_required () const
return true;
}
-MTDM*
-AudioEngine::mtdm()
-{
- return _mtdm;
-}
-
int
AudioEngine::prepare_for_latency_measurement ()
{
@@ -1052,7 +1070,7 @@ AudioEngine::prepare_for_latency_measurement ()
}
int
-AudioEngine::start_latency_detection ()
+AudioEngine::start_latency_detection (bool for_midi)
{
if (!running()) {
if (prepare_for_latency_measurement ()) {
@@ -1065,6 +1083,9 @@ AudioEngine::start_latency_detection ()
delete _mtdm;
_mtdm = 0;
+ delete _mididm;
+ _mididm = 0;
+
/* find the ports we will connect to */
PortEngine::PortHandle out = pe.get_port_by_name (_latency_output_name);
@@ -1076,27 +1097,57 @@ AudioEngine::start_latency_detection ()
}
/* create the ports we will use to read/write data */
-
- if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
- stop (true);
- return -1;
- }
- if (pe.connect (_latency_output_port, _latency_output_name)) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
- }
+ if (for_midi) {
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::MIDI, IsOutput)) == 0) {
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::MIDI, IsInput)) == 0) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ _mididm = new MIDIDM (sample_rate());
+
+ } else {
+
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ _mtdm = new MTDM (sample_rate());
- const string portname ("latency_in");
- if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
- }
- if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
}
LatencyRange lr;
@@ -1107,10 +1158,8 @@ AudioEngine::start_latency_detection ()
_latency_signal_latency += lr.max;
/* all created and connected, lets go */
-
- _mtdm = new MTDM (sample_rate());
- _measuring_latency = true;
- _latency_flush_frames = samples_per_cycle();
+ _latency_flush_frames = samples_per_cycle();
+ _measuring_latency = for_midi ? MeasureMIDI : MeasureAudio;
return 0;
}
@@ -1118,7 +1167,7 @@ AudioEngine::start_latency_detection ()
void
AudioEngine::stop_latency_detection ()
{
- _measuring_latency = false;
+ _measuring_latency = MeasureNone;
if (_latency_output_port) {
port_engine().unregister_port (_latency_output_port);
diff --git a/libs/ardour/mididm.cc b/libs/ardour/mididm.cc
new file mode 100644
index 0000000000..6549544efb
--- /dev/null
+++ b/libs/ardour/mididm.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ardour/mididm.h"
+#include "ardour/port_engine.h"
+
+using namespace ARDOUR;
+
+MIDIDM::MIDIDM (framecnt_t sample_rate)
+ : _sample_rate (sample_rate)
+ , _monotonic_cnt (sample_rate)
+ , _last_signal_tme (0)
+ , _cnt_total (0)
+ , _dly_total (0)
+ , _min_delay (INT32_MAX)
+ , _max_delay (0)
+ , _avg_delay (0)
+ , _var_m (0)
+ , _var_s (0)
+{
+
+}
+
+
+int MIDIDM::process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out)
+{
+ /* send midi event */
+ uint8_t obuf[3];
+ obuf[0] = 0xf2;
+ obuf[1] = (_monotonic_cnt) & 0x7f;
+ obuf[2] = (_monotonic_cnt >> 7) & 0x7f;
+
+ pe.midi_clear(midi_out);
+ pe.midi_event_put (midi_out, 0, obuf, 3);
+
+ /* process incoming */
+ const pframes_t nevents = pe.get_midi_event_count (midi_in);
+ for (pframes_t n = 0; n < nevents; ++n) {
+ pframes_t timestamp;
+ size_t size;
+ uint8_t* buf;
+ pe.midi_event_get (timestamp, size, &buf, midi_in, n);
+
+ if (size != 3 || buf[0] != 0xf2 ) continue;
+
+ _last_signal_tme = _monotonic_cnt;
+
+ /* calculate time difference */
+#define MODX (16384) // 1<<(2*7)
+#define MASK (0x3fff) // MODX - 1
+ const int64_t tc = (_monotonic_cnt + timestamp) & MASK;
+ const int64_t ti = (buf[2] << 7) | buf[1];
+ const int64_t tdiff = (MODX + tc - ti) % MODX;
+
+ /* running variance */
+ if (_cnt_total == 0) {
+ _var_m = tdiff;
+ } else {
+ const double var_m1 = _var_m;
+ _var_m = _var_m + ((double)tdiff - _var_m) / (double)(_cnt_total + 1);
+ _var_s = _var_s + ((double)tdiff - _var_m) * ((double)tdiff - var_m1);
+ }
+ /* average and mix/max */
+ ++_cnt_total;
+ _dly_total += tdiff;
+ _avg_delay = _dly_total / _cnt_total;
+ if (tdiff < _min_delay) _min_delay = tdiff;
+ if (tdiff > _max_delay) _max_delay = tdiff;
+ }
+
+ _monotonic_cnt += nframes;
+ return 0;
+}
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 85326e98f8..e403020175 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -130,6 +130,7 @@ libardour_sources = [
'mix.cc',
'monitor_processor.cc',
'mtc_slave.cc',
+ 'mididm.cc',
'mtdm.cc',
'mute_master.cc',
'onset_detector.cc',