diff options
author | Robin Gareus <robin@gareus.org> | 2014-06-08 11:18:18 +0200 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2014-06-08 11:18:18 +0200 |
commit | 81182b5bf69e06f67b3ccbca46860ac8e6387d2f (patch) | |
tree | 23c2165f66167a0ffbafcb5de3367a5ae2266755 | |
parent | 6416a429a888fcb18b9aa5a4aed1d6d8c9a6da1f (diff) |
prepare MIDI latency measurement (backend)
-rw-r--r-- | gtk2_ardour/engine_dialog.cc | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/audioengine.h | 18 | ||||
-rw-r--r-- | libs/ardour/ardour/mididm.h | 62 | ||||
-rw-r--r-- | libs/ardour/audioengine.cc | 119 | ||||
-rw-r--r-- | libs/ardour/mididm.cc | 88 | ||||
-rw-r--r-- | libs/ardour/wscript | 1 |
6 files changed, 251 insertions, 39 deletions
diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc index 845c6da228..af6ebc28c1 100644 --- a/gtk2_ardour/engine_dialog.cc +++ b/gtk2_ardour/engine_dialog.cc @@ -1699,7 +1699,7 @@ EngineControl::start_latency_detection () ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text()); ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text()); - if (ARDOUR::AudioEngine::instance()->start_latency_detection () == 0) { + if (ARDOUR::AudioEngine::instance()->start_latency_detection (false) == 0) { lm_results.set_markup (string_compose (results_markup, _("Detecting ..."))); latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 100); lm_measure_label.set_text (_("Cancel")); 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', |