basic functionality for hardware latency measurement

This commit is contained in:
Paul Davis
2013-09-10 22:58:33 -04:00
parent 209e4bdcae
commit 676ff80697
6 changed files with 307 additions and 6 deletions

View File

@@ -36,12 +36,15 @@
#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/mtdm.h"
#include "ardour/rc_configuration.h"
#include "ardour/types.h"
#include "pbd/convert.h"
#include "pbd/error.h"
#include "engine_dialog.h"
#include "gui_thread.h"
#include "i18n.h"
using namespace std;
@@ -63,6 +66,9 @@ EngineControl::EngineControl ()
, ports_adjustment (128, 8, 1024, 1, 16)
, ports_spinner (ports_adjustment)
, control_app_button (_("Launch Control App"))
, lm_measure_button (_("Measure latency"))
, lm_use_button (_("Use results"))
, lm_table (5, 2)
, basic_packer (9, 3)
, ignore_changes (0)
, _desired_sample_rate (0)
@@ -89,6 +95,15 @@ EngineControl::EngineControl ()
if (audio_setup) {
set_state (*audio_setup);
}
ARDOUR::AudioEngine::instance()->Stopped.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::disable_latency_tab, this), gui_context());
if (!ARDOUR::AudioEngine::instance()->connected()) {
ARDOUR::AudioEngine::instance()->Running.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::enable_latency_tab, this), gui_context());
disable_latency_tab ();
} else {
enable_latency_tab ();
}
}
void
@@ -131,7 +146,7 @@ EngineControl::build_notebook ()
row = 0;
const AttachOptions xopt = AttachOptions (FILL|EXPAND);
AttachOptions xopt = AttachOptions (FILL|EXPAND);
label = manage (left_aligned_label (_("Audio System:")));
basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
@@ -200,8 +215,59 @@ EngineControl::build_notebook ()
midi_packer.set_border_width (12);
/* latency measurement tab */
lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
row = 0;
lm_table.set_row_spacings (12);
lm_table.attach (lm_title, 0, 2, row, row+1, xopt, (AttachOptions) 0);
row++;
lm_preamble.set_width_chars (60);
lm_preamble.set_line_wrap (true);
lm_preamble.set_markup (_("This tool will allow you to <i>precisely</i> measure the signal delay \
within your audio hardware setup that is not controlled by Ardour or its audio backend.\n\n\
Connect the two channels that you select below using either a cable or (less ideally) a speaker \
and microphone.\n\n\
Once the channels are connected, click the \"Measure latency\" button.\n\n\
When you are satisfied with the results, click the \"Use results\" button to use them with your audio \
setup parameters. <i>Note: they will not take effect until you restart</i>"));
lm_table.attach (lm_preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
row++;
label = manage (left_aligned_label (_("Output channel")));
lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
lm_table.attach (lm_output_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
label = manage (left_aligned_label (_("Input channel")));
lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
lm_table.attach (lm_input_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
xopt = AttachOptions(0);
lm_measure_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::latency_button_toggled));
lm_table.attach (lm_measure_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
lm_table.attach (lm_results, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
++row;
lm_table.attach (lm_use_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
++row;
lm_results.set_text ("Measured results: 786 samples");
lm_vbox.pack_start (lm_table, false, false);
/* pack it all up */
notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
notebook.pages().push_back (TabElem (midi_hbox, _("MIDI")));
notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
notebook.set_border_width (12);
notebook.set_tab_pos (POS_RIGHT);
@@ -225,6 +291,28 @@ EngineControl::~EngineControl ()
}
void
EngineControl::disable_latency_tab ()
{
vector<string> empty;
set_popdown_strings (lm_output_channel_combo, empty);
set_popdown_strings (lm_input_channel_combo, empty);
}
void
EngineControl::enable_latency_tab ()
{
vector<string> outputs;
ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
set_popdown_strings (lm_output_channel_combo, outputs);
lm_output_channel_combo.set_active_text (outputs.front());
vector<string> inputs;
ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
set_popdown_strings (lm_input_channel_combo, inputs);
lm_input_channel_combo.set_active_text (inputs.front());
}
void
EngineControl::backend_changed ()
{
@@ -861,3 +949,95 @@ EngineControl::set_desired_sample_rate (uint32_t sr)
_desired_sample_rate = sr;
device_changed ();
}
/* latency measurement */
void
EngineControl::update_latency_display ()
{
ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
if (sample_rate == 0) {
lm_results.set_text (_("Disconnected from audio engine"));
} else {
char buf[64];
//snprintf (buf, sizeof (buf), "%10.3lf frames %10.3lf ms",
//(float)_pi->latency(), (float)_pi->latency() * 1000.0f/sample_rate);
strcpy (buf, "got something");
lm_results.set_text(buf);
}
}
bool
EngineControl::check_latency_measurement ()
{
MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
static uint32_t cnt = 0;
if (mtdm->resolve () < 0) {
cerr << "no resolution\n";
string txt = _("No signal detected ");
uint32_t dots = cnt++%10;
for (uint32_t i = 0; i < dots; ++i) {
txt += '.';
}
lm_results.set_text (txt);
return true;
}
if (mtdm->err () > 0.3) {
mtdm->invert ();
mtdm->resolve ();
}
char buf[128];
ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
if (sample_rate == 0) {
lm_results.set_text (_("Disconnected from audio engine"));
ARDOUR::AudioEngine::instance()->stop_latency_detection ();
return false;
}
snprintf (buf, sizeof (buf), "%10.3lf frames %10.3lf ms", mtdm->del (), mtdm->del () * 1000.0f/sample_rate);
bool solid = true;
if (mtdm->err () > 0.2) {
strcat (buf, " ??");
solid = false;
}
if (mtdm->inv ()) {
strcat (buf, " (Inv)");
solid = false;
}
if (solid) {
// _pi->set_measured_latency (rint (mtdm->del()));
lm_measure_button.set_active (false);
strcat (buf, " (set)");
}
lm_results.set_text (buf);
return true;
}
void
EngineControl::latency_button_toggled ()
{
if (lm_measure_button.get_active ()) {
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());
cerr << "latency detection on " << lm_input_channel_combo.get_active_text() << " => " << lm_output_channel_combo.get_active_text() << endl;
ARDOUR::AudioEngine::instance()->start_latency_detection ();
lm_results.set_text (_("Detecting ..."));
latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
} else {
ARDOUR::AudioEngine::instance()->stop_latency_detection ();
latency_timeout.disconnect ();
update_latency_display ();
}
}

View File

@@ -33,9 +33,11 @@
#include <gtkmm/buttonbox.h>
#include <gtkmm/button.h>
#include "pbd/signals.h"
#include "ardour_dialog.h"
class EngineControl : public ArdourDialog {
class EngineControl : public ArdourDialog, public PBD::ScopedConnectionList {
public:
EngineControl ();
~EngineControl ();
@@ -71,6 +73,18 @@ class EngineControl : public ArdourDialog {
Gtk::Button control_app_button;
/* latency measurement */
Gtk::ComboBoxText lm_output_channel_combo;
Gtk::ComboBoxText lm_input_channel_combo;
Gtk::ToggleButton lm_measure_button;
Gtk::Button lm_use_button;
Gtk::Label lm_title;
Gtk::Label lm_preamble;
Gtk::Label lm_results;
Gtk::Table lm_table;
Gtk::VBox lm_vbox;
/* JACK specific */
Gtk::CheckButton realtime_button;
@@ -156,6 +170,14 @@ class EngineControl : public ArdourDialog {
void manage_control_app_sensitivity ();
int push_state_to_backend (bool start);
uint32_t _desired_sample_rate;
/* latency measurement */
void latency_button_toggled ();
bool check_latency_measurement ();
void update_latency_display ();
sigc::connection latency_timeout;
void enable_latency_tab ();
void disable_latency_tab ();
};
#endif /* __gtk2_ardour_engine_dialog_h__ */

View File

@@ -47,6 +47,8 @@
#include <jack/session.h>
#endif
class MTDM;
namespace ARDOUR {
class InternalPort;
@@ -182,6 +184,14 @@ public:
/* sets up the process callback thread */
static void thread_init_callback (void *);
/* latency measurement */
MTDM* mtdm();
void start_latency_detection ();
void stop_latency_detection ();
void set_latency_input_port (const std::string&);
void set_latency_output_port (const std::string&);
private:
AudioEngine ();
@@ -205,7 +215,12 @@ public:
framecnt_t _processed_frames;
Glib::Threads::Thread* m_meter_thread;
ProcessThread* _main_thread;
MTDM* _mtdm;
bool _measuring_latency;
PortEngine::PortHandle _latency_input_port;
PortEngine::PortHandle _latency_output_port;
framecnt_t _latency_flush_frames;
void meter_thread ();
void start_metering_thread ();
void stop_metering_thread ();

View File

@@ -50,6 +50,7 @@
#include "ardour/meter.h"
#include "ardour/midi_port.h"
#include "ardour/midiport_manager.h"
#include "ardour/mtdm.h"
#include "ardour/port.h"
#include "ardour/process_thread.h"
#include "ardour/session.h"
@@ -73,6 +74,11 @@ AudioEngine::AudioEngine ()
, _processed_frames (0)
, m_meter_thread (0)
, _main_thread (0)
, _mtdm (0)
, _measuring_latency (false)
, _latency_input_port (0)
, _latency_output_port (0)
, _latency_flush_frames (0)
{
g_atomic_int_set (&m_meter_exit, 0);
discover_backends ();
@@ -192,6 +198,43 @@ AudioEngine::process_callback (pframes_t nframes)
return 0;
}
/* If measuring latency, do it now and get out of here */
if (_measuring_latency && _mtdm) {
// PortManager::cycle_start (nframes);
// PortManager::silence (nframes);
if (_latency_input_port && _latency_output_port) {
PortEngine& pe (port_engine());
Sample* in = (Sample*) pe.get_buffer (_latency_input_port, nframes);
Sample* out = (Sample*) pe.get_buffer (_latency_output_port, nframes);
_mtdm->process (nframes, in, out);
}
// PortManager::cycle_end (nframes);
return 0;
} else if (_latency_flush_frames) {
/* wait for the appropriate duration for the MTDM signal to
* drain from the ports before we revert to normal behaviour.
*/
PortManager::cycle_start (nframes);
PortManager::silence (nframes);
PortManager::cycle_end (nframes);
if (_latency_flush_frames > nframes) {
_latency_flush_frames -= nframes;
} else {
_latency_flush_frames = 0;
}
return 0;
}
if (session_remove_pending) {
/* perform the actual session removal */
@@ -581,6 +624,9 @@ AudioEngine::stop ()
_running = false;
_processed_frames = 0;
_measuring_latency = false;
_latency_output_port = 0;
_latency_input_port = 0;
stop_metering_thread ();
Port::PortDrop ();
@@ -930,3 +976,41 @@ AudioEngine::setup_required () const
return true;
}
MTDM*
AudioEngine::mtdm()
{
return _mtdm;
}
void
AudioEngine::start_latency_detection ()
{
delete _mtdm;
_mtdm = new MTDM (sample_rate());
_measuring_latency = true;
_latency_flush_frames = samples_per_cycle();
}
void
AudioEngine::stop_latency_detection ()
{
port_engine().unregister_port (_latency_output_port);
port_engine().unregister_port (_latency_input_port);
_measuring_latency = false;
}
void
AudioEngine::set_latency_output_port (const string& name)
{
_latency_output_port = port_engine().register_port ("latency_out", DataType::AUDIO, IsOutput);
port_engine().connect (_latency_output_port, name);
}
void
AudioEngine::set_latency_input_port (const string& name)
{
const string portname ("latency_in");
_latency_input_port = port_engine().register_port (portname, DataType::AUDIO, IsInput);
port_engine().connect (name, make_port_name_non_relative (portname));
}

View File

@@ -88,7 +88,7 @@ MidiPort::get_midi_buffer (pframes_t nframes)
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 MIDI port buffer
into our MidiBuffer
*/

View File

@@ -49,7 +49,7 @@ PortInsert::PortInsert (Session& s, boost::shared_ptr<Pannable> pannable, boost:
{
_mtdm = 0;
_latency_detect = false;
_latency_flush_frames = false;
_latency_flush_frames = 0;
_measured_latency = 0;
}
@@ -64,7 +64,7 @@ PortInsert::start_latency_detection ()
{
delete _mtdm;
_mtdm = new MTDM (_session.frame_rate());
_latency_flush_frames = false;
_latency_flush_frames = 0;
_latency_detect = true;
_measured_latency = 0;
}