diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 020a9bf66e..ff35b1a550 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -140,7 +140,8 @@ public: static PBD::Signal0 PortDrop; static PBD::Signal0 PortSignalDrop; - static void set_speed_ratio (double s); + static void set_varispeed_ratio (double s); //< varispeed playback + static bool set_engine_ratio (double session, double engine); //< SR mismatch static void set_cycle_samplecnt (pframes_t n); static samplecnt_t port_offset() { return _global_port_buffer_offset; } @@ -158,11 +159,13 @@ public: static pframes_t cycle_nframes () { return _cycle_nframes; } static double speed_ratio () { return _speed_ratio; } + static double engine_ratio () { return _engine_ratio; } + static double resample_ratio () { return _resample_ratio; } // == _speed_ratio * _engine_ratio static uint32_t resampler_quality () { return _resampler_quality; } static uint32_t resampler_latency () { return _resampler_latency; } static bool can_varispeed () { return _resampler_latency > 0; } - static void setup_resampler (uint32_t q = 17); + static bool setup_resampler (uint32_t q = 17); protected: @@ -179,6 +182,8 @@ protected: LatencyRange _private_capture_latency; static double _speed_ratio; + static double _engine_ratio; + static double _resample_ratio; // = _speed_ratio * _engine_ratio (cached) private: std::string _name; ///< port short name diff --git a/libs/ardour/ardour/port_manager.h b/libs/ardour/ardour/port_manager.h index 4ef36563fc..4ebcd62a2d 100644 --- a/libs/ardour/ardour/port_manager.h +++ b/libs/ardour/ardour/port_manager.h @@ -128,6 +128,9 @@ public: uint32_t port_name_size () const; std::string my_name () const; + size_t total_port_count () const { return _ports.reader ()->size (); } + size_t session_port_count () const; + #ifndef NDEBUG void list_cycle_ports () const; void list_all_ports () const; diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index eac9932b6c..2bfe036a42 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -234,7 +234,7 @@ AudioEngine::process_callback (pframes_t nframes) { TimerRAII tr (dsp_stats[ProcessCallback]); Glib::Threads::Mutex::Lock tm (_process_lock, Glib::Threads::TRY_LOCK); - Port::set_speed_ratio (1.0); + Port::set_varispeed_ratio (1.0); PT_TIMING_REF; PT_TIMING_CHECK (1); @@ -490,7 +490,7 @@ AudioEngine::process_callback (pframes_t nframes) */ double catch_speed = tmm.pre_process_transport_masters (nframes, sample_time_at_cycle_start()); catch_speed = _session->plan_master_strategy (nframes, tmm.get_current_speed_in_process_context(), tmm.get_current_position_in_process_context(), catch_speed); - Port::set_speed_ratio (catch_speed); + Port::set_varispeed_ratio (catch_speed); DEBUG_TRACE (DEBUG::Slave, string_compose ("transport master (current=%1) gives speed %2 (ports using %3)\n", tmm.current() ? tmm.current()->name() : string("[]"), catch_speed, Port::speed_ratio())); #if 0 // USE FOR DEBUG ONLY diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 831289bc16..f5ff712a3a 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -123,7 +123,7 @@ MidiPort::get_midi_buffer (pframes_t nframes) continue; } - timestamp = floor (timestamp * _speed_ratio); + timestamp = floor (timestamp * resample_ratio ()); /* check that the event is in the acceptable time range */ if ((timestamp < (_global_port_buffer_offset)) || @@ -241,7 +241,7 @@ MidiPort::resolve_notes (void* port_buffer, MidiBuffer::TimeType when) for (uint8_t channel = 0; channel <= 0xF; channel++) { uint8_t ev[3] = { ((uint8_t) (MIDI_CMD_CONTROL | channel)), MIDI_CTL_SUSTAIN, 0 }; - pframes_t tme = floor (when / _speed_ratio); + pframes_t tme = floor (when / resample_ratio ()); /* we need to send all notes off AND turn the * sustain/damper pedal off to handle synths @@ -282,7 +282,7 @@ MidiPort::flush_buffers (pframes_t nframes) port_buffer = port_engine.get_buffer (_port_handle, nframes); } - double speed_ratio = (flags () & TransportGenerator) ? 1.0 : _speed_ratio; + double speed_ratio = (flags () & TransportGenerator) ? 1.0 : resample_ratio (); for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) { diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 410886d4a3..8033e449f2 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -46,6 +46,8 @@ bool Port::_connecting_blocked = false; pframes_t Port::_global_port_buffer_offset = 0; pframes_t Port::_cycle_nframes = 0; double Port::_speed_ratio = 1.0; +double Port::_engine_ratio = 1.0; +double Port::_resample_ratio = 1.0; std::string Port::state_node_name = X_("Port"); uint32_t Port::_resampler_quality = 17; uint32_t Port::_resampler_latency = 16; // = _resampler_quality - 1; @@ -706,20 +708,19 @@ Port::set_state (const XMLNode& node, int) return 0; } -/* static */ void +/* static */ bool Port::setup_resampler (uint32_t q) { /* configure at application start (Ardour::init) */ - static bool setup_done = false; - if (setup_done) { - return; + if (port_manager && port_manager->session_port_count() > 0) { + return false; } - setup_done = true; + if (q == 0) { /* no vari-speed */ _resampler_quality = 0; _resampler_latency = 0; - return; + return true; } // range constrained in VMResampler::setup if (q < 8) { @@ -730,22 +731,49 @@ Port::setup_resampler (uint32_t q) } _resampler_quality = q; _resampler_latency = q - 1; + return true; } +/*static*/ bool +Port::set_engine_ratio (double session_rate, double engine_rate) +{ + bool rv = true; + if (session_rate > 0 && engine_rate > 0 && can_varispeed ()) { + _engine_ratio = session_rate / engine_rate; + } else { + _engine_ratio = 1.0; + rv = false; + } + + /* constrain range to provide for additional vari-speed. + * but do allow 384000 / 44100 = 8.7 + */ + if (_engine_ratio < 0.11 || _engine_ratio > 9) { + _engine_ratio = 1.0; + rv = false; + } + + /* apply constraints, and calc _resample_ratio */ + set_varispeed_ratio (_speed_ratio); + return rv; +} /*static*/ void -Port::set_speed_ratio (double s) { +Port::set_varispeed_ratio (double s) { if (s == 0.0 || !can_varispeed ()) { /* no resampling when stopped */ _speed_ratio = 1.0; } else { /* see VMResampler::set_rratio() for min/max range */ - _speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.02, fabs (s))); + _speed_ratio = std::min (16.0, std::max (0.02, fabs (s * _engine_ratio))) / _engine_ratio; + _speed_ratio = std::min ((double) Config->get_max_transport_speed(), _speed_ratio); } + /* cache overall speed */ + _resample_ratio = _speed_ratio * _engine_ratio; } /*static*/ void Port::set_cycle_samplecnt (pframes_t n) { - _cycle_nframes = floor (n * _speed_ratio); + _cycle_nframes = floor (n * resample_ratio ()); } diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc index 943e6bc04b..2f70c696e1 100644 --- a/libs/ardour/port_manager.cc +++ b/libs/ardour/port_manager.cc @@ -552,6 +552,19 @@ PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlag return _backend->get_ports (port_name_pattern, type, flags, s); } +size_t +PortManager::session_port_count () const +{ + size_t cnt = 0; + for (auto const& p : *_ports.reader ()) { + if (p.second->flags () & TransportSyncPort) { + continue; + } + ++cnt; + } + return cnt; +} + void PortManager::port_registration_failure (const std::string& portname) { @@ -1230,7 +1243,7 @@ PortManager::cycle_start (pframes_t nframes, Session* s) if (s) { tl = s->rt_tasklist (); } - if (tl && fabs (Port::speed_ratio ()) != 1.0) { + if (tl && fabs (Port::resample_ratio ()) != 1.0) { for (Ports::iterator p = _cycle_ports->begin (); p != _cycle_ports->end (); ++p) { if (!(p->second->flags () & TransportSyncPort)) { tl->push_back (boost::bind (&Port::cycle_start, p->second, nframes)); @@ -1256,7 +1269,7 @@ PortManager::cycle_end (pframes_t nframes, Session* s) if (s) { tl = s->rt_tasklist (); } - if (tl && fabs (Port::speed_ratio ()) != 1.0) { + if (tl && fabs (Port::resample_ratio ()) != 1.0) { for (Ports::iterator p = _cycle_ports->begin (); p != _cycle_ports->end (); ++p) { if (!(p->second->flags () & TransportSyncPort)) { tl->push_back (boost::bind (&Port::cycle_end, p->second, nframes)); @@ -1274,7 +1287,7 @@ PortManager::cycle_end (pframes_t nframes, Session* s) for (Ports::iterator p = _cycle_ports->begin (); p != _cycle_ports->end (); ++p) { /* AudioEngine::split_cycle flushes buffers until Port::port_offset. * Now only flush remaining events (after Port::port_offset) */ - p->second->flush_buffers (nframes * Port::speed_ratio () - Port::port_offset ()); + p->second->flush_buffers (nframes * Port::resample_ratio () - Port::port_offset ()); } _cycle_ports.reset (); @@ -1374,7 +1387,7 @@ PortManager::cycle_end_fade_out (gain_t base_gain, gain_t gain_step, pframes_t n if (s) { tl = s->rt_tasklist (); } - if (tl && fabs (Port::speed_ratio ()) != 1.0) { + if (tl && fabs (Port::resample_ratio ()) != 1.0) { for (Ports::iterator p = _cycle_ports->begin (); p != _cycle_ports->end (); ++p) { if (!(p->second->flags () & TransportSyncPort)) { tl->push_back (boost::bind (&Port::cycle_end, p->second, nframes));