basic functionality for hardware latency measurement
This commit is contained in:
@@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user