mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 08:36:32 +01:00
prepare MIDI latency measurement (backend)
This commit is contained in:
parent
6416a429a8
commit
81182b5bf6
6 changed files with 251 additions and 39 deletions
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
62
libs/ardour/ardour/mididm.h
Normal file
62
libs/ardour/ardour/mididm.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
88
libs/ardour/mididm.cc
Normal file
88
libs/ardour/mididm.cc
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -130,6 +130,7 @@ libardour_sources = [
|
|||
'mix.cc',
|
||||
'monitor_processor.cc',
|
||||
'mtc_slave.cc',
|
||||
'mididm.cc',
|
||||
'mtdm.cc',
|
||||
'mute_master.cc',
|
||||
'onset_detector.cc',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue