Implement dynamic input port meters

This is a revision of the initial API 8ba7df9105, to
address issues with dynamica ports (fa791ae9cc).

* Group meter + scope/monitor per port (atomic access)
* Use shared pointer to allow access while ports change
* Use RCU to update port map
* Add/remove ports without clearing the map
* Add signal to notify GUI about Input Port changes
This commit is contained in:
Robin Gareus 2021-01-16 06:45:12 +01:00
parent c5b986f420
commit 097e5a7145
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
3 changed files with 192 additions and 82 deletions

View file

@ -91,13 +91,25 @@ public:
typedef std::map<std::string, boost::shared_ptr<Port>, SortByPortName> Ports;
typedef std::list<boost::shared_ptr<Port> > PortList;
typedef boost::shared_ptr<CircularSampleBuffer> AudioPortScope;
typedef std::map<std::string, AudioPortScope, SortByPortName> AudioPortScopes;
typedef std::map<std::string, DPM, SortByPortName> AudioPortMeters;
typedef boost::shared_ptr<CircularSampleBuffer> AudioPortScope;
typedef boost::shared_ptr<CircularEventBuffer> MIDIPortMonitor;
typedef boost::shared_ptr<DPM> AudioPortMeter;
typedef boost::shared_ptr<MPM> MIDIPortMeter;
typedef boost::shared_ptr<CircularEventBuffer> MIDIPortMonitor;
typedef std::map<std::string, MIDIPortMonitor, SortByPortName> MIDIPortMonitors;
typedef std::map<std::string, MPM, SortByPortName> MIDIPortMeters;
struct AudioInputPort {
AudioInputPort (samplecnt_t);
AudioPortScope scope;
AudioPortMeter meter;
};
struct MIDIInputPort {
MIDIInputPort (samplecnt_t);
MIDIPortMonitor monitor;
MIDIPortMeter meter;
};
typedef std::map<std::string, AudioInputPort, SortByPortName> AudioInputPorts;
typedef std::map<std::string, MIDIInputPort, SortByPortName> MIDIInputPorts;
PortManager ();
virtual ~PortManager () {}
@ -231,25 +243,13 @@ public:
*/
PBD::Signal5<void, boost::weak_ptr<Port>, std::string, boost::weak_ptr<Port>, std::string, bool> PortConnectedOrDisconnected;
PBD::Signal3<void, DataType, std::vector<std::string>, bool> PhysInputChanged;
/* Input port meters and monitors */
void reset_input_meters ();
AudioPortMeters const& input_meters () const
{
return _audio_port_meters;
}
AudioPortScopes const& input_scopes () const
{
return _audio_port_scopes;
}
MIDIPortMeters const& midi_meters () const
{
return _midi_port_meters;
}
MIDIPortMonitors const& midi_monitors () const
{
return _midi_port_monitors;
}
AudioInputPorts audio_input_ports () const;
MIDIInputPorts midi_input_ports () const;
protected:
boost::shared_ptr<AudioBackend> _backend;
@ -296,6 +296,7 @@ private:
void fill_midi_port_info_locked ();
void load_port_info ();
void save_port_info ();
void update_input_ports (bool);
struct PortID {
PortID (boost::shared_ptr<AudioBackend>, DataType, bool, std::string const&);
@ -359,11 +360,9 @@ private:
PortInfo _port_info;
bool _midi_info_dirty;
AudioPortMeters _audio_port_meters;
AudioPortScopes _audio_port_scopes;
MIDIPortMeters _midi_port_meters;
MIDIPortMonitors _midi_port_monitors;
volatile gint _reset_meters;
SerializedRCUManager<AudioInputPorts> _audio_input_ports;
SerializedRCUManager<MIDIInputPorts> _midi_input_ports;
volatile gint _reset_meters;
};
} // namespace ARDOUR

View file

@ -1396,19 +1396,6 @@ LuaBindings::common (lua_State* L)
.endNamespace () /* ARDOUR::MidiModel */
.beginClass <PortManager::DPM> ("DPM")
.addVoidConstructor ()
.addFunction ("reset", &PortManager::DPM::reset)
.addData ("level", &PortManager::DPM::level, false)
.addData ("peak", &PortManager::DPM::peak, false)
.endClass ()
.beginClass <PortManager::MPM> ("MPM")
.addVoidConstructor ()
.addFunction ("reset", &PortManager::MPM::reset)
.addFunction ("active", &PortManager::MPM::active)
.endClass ()
.beginClass <Plugin::PresetRecord> ("PresetRecord")
.addVoidConstructor ()
.addData ("uri", &Plugin::PresetRecord::uri, false)
@ -2357,7 +2344,6 @@ LuaBindings::common (lua_State* L)
.addFunction ("n_physical_outputs", &PortManager::n_physical_outputs)
.addFunction ("n_physical_inputs", &PortManager::n_physical_inputs)
.addFunction ("reset_input_meters", &PortManager::reset_input_meters)
.addFunction ("input_meters", &PortManager::input_meters)
.addRefFunction ("get_connections", &PortManager::get_connections)
.addRefFunction ("get_ports", (int (PortManager::*)(DataType, PortManager::PortList&))&PortManager::get_ports)
.addRefFunction ("get_backend_ports", (int (PortManager::*)(const std::string&, DataType, PortFlags, std::vector<std::string>&))&PortManager::get_ports)

View file

@ -18,6 +18,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <algorithm>
#include <vector>
#ifdef COMPILER_MSVC
@ -55,6 +56,17 @@ using namespace PBD;
using std::string;
using std::vector;
PortManager::AudioInputPort::AudioInputPort (samplecnt_t sz)
: scope (AudioPortScope (new CircularSampleBuffer (sz)))
, meter (AudioPortMeter (new DPM))
{}
PortManager::MIDIInputPort::MIDIInputPort (samplecnt_t sz)
: monitor (MIDIPortMonitor (new CircularEventBuffer (sz)))
, meter (MIDIPortMeter (new MPM))
{}
PortManager::PortID::PortID (boost::shared_ptr<AudioBackend> b, DataType dt, bool in, std::string const& pn)
: backend (b->name ())
, port_name (pn)
@ -130,6 +142,9 @@ PortManager::PortManager ()
, _port_remove_in_progress (false)
, _port_deletions_pending (8192) /* ick, arbitrary sizing */
, _midi_info_dirty (true)
, _audio_input_ports (new AudioInputPorts)
, _midi_input_ports (new MIDIInputPorts)
, _reset_meters (0)
{
load_port_info ();
}
@ -721,12 +736,7 @@ PortManager::reestablish_ports ()
set_pretty_names (port_names, DataType::MIDI, false);
}
_audio_port_scopes.clear();
_audio_port_meters.clear();
_midi_port_monitors.clear();
_midi_port_meters.clear();
run_input_meters (0, 0);
update_input_ports (true);
return 0;
}
@ -817,14 +827,121 @@ PortManager::registration_callback ()
return;
}
{
Glib::Threads::Mutex::Lock lm (_port_info_mutex);
_midi_info_dirty = true;
}
update_input_ports (false);
PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
}
void
PortManager::update_input_ports (bool clear)
{
std::vector<std::string> audio_ports;
std::vector<std::string> midi_ports;
std::vector<std::string> new_audio;
std::vector<std::string> old_audio;
std::vector<std::string> new_midi;
std::vector<std::string> old_midi;
get_physical_inputs (DataType::AUDIO, audio_ports);
get_physical_inputs (DataType::MIDI, midi_ports);
if (clear) {
new_audio = audio_ports;
new_midi = midi_ports;
} else {
boost::shared_ptr<AudioInputPorts> aip = _audio_input_ports.reader ();
/* find new audio ports */
for (std::vector<std::string>::iterator p = audio_ports.begin(); p != audio_ports.end(); ++p) {
if (port_is_mine (*p) || !_backend->get_port_by_name (*p)) {
continue;
}
if (aip->find (*p) == aip->end ()) {
new_audio.push_back (*p);
}
}
/* find stale audio ports */
for (AudioInputPorts::iterator p = aip->begin(); p != aip->end(); ++p) {
if (std::find (audio_ports.begin (), audio_ports.end (), p->first) == audio_ports.end ()) {
old_audio.push_back (p->first);
}
}
boost::shared_ptr<MIDIInputPorts> mip = _midi_input_ports.reader ();
/* find new MIDI ports */
for (std::vector<std::string>::iterator p = midi_ports.begin(); p != midi_ports.end(); ++p) {
if (port_is_mine (*p) || !_backend->get_port_by_name (*p)) {
continue;
}
if (mip->find (*p) == mip->end ()) {
new_midi.push_back (*p);
}
}
/* find stale audio ports */
for (MIDIInputPorts::iterator p = mip->begin(); p != mip->end(); ++p) {
if (std::find (midi_ports.begin (), midi_ports.end (), p->first) == midi_ports.end ()) {
old_midi.push_back (p->first);
}
}
}
if (!new_audio.empty () || !old_audio.empty () || clear) {
RCUWriter<AudioInputPorts> apwr (_audio_input_ports);
boost::shared_ptr<AudioInputPorts> apw = apwr.get_copy ();
if (clear) {
apw->clear ();
} else {
for (std::vector<std::string>::const_iterator p = old_audio.begin(); p != old_audio.end(); ++p) {
apw->erase (*p);
}
}
for (std::vector<std::string>::const_iterator p = new_audio.begin(); p != new_audio.end(); ++p) {
if (port_is_mine (*p) || !_backend->get_port_by_name (*p)) {
continue;
}
apw->insert (make_pair (*p, AudioInputPort (24288))); // 2^19 ~ 1MB / port
}
}
if (!new_midi.empty () || !old_midi.empty () || clear) {
RCUWriter<MIDIInputPorts> mpwr (_midi_input_ports);
boost::shared_ptr<MIDIInputPorts> mpw = mpwr.get_copy ();
if (clear) {
mpw->clear ();
} else {
for (std::vector<std::string>::const_iterator p = old_midi.begin(); p != old_midi.end(); ++p) {
mpw->erase (*p);
}
}
for (std::vector<std::string>::const_iterator p = new_midi.begin(); p != new_midi.end(); ++p) {
if (port_is_mine (*p) || !_backend->get_port_by_name (*p)) {
continue;
}
mpw->insert (make_pair (*p, MIDIInputPort (32)));
}
}
if (clear) {
/* don't send notifcation for initial setup */
return;
}
if (!old_audio.empty ()) {
PhysInputChanged (DataType::AUDIO, old_audio, false);
}
if (!old_midi.empty ()) {
PhysInputChanged (DataType::MIDI, old_midi, false);
}
if (!new_audio.empty ()) {
PhysInputChanged (DataType::AUDIO, new_audio, true);
}
if (!new_midi.empty ()) {
PhysInputChanged (DataType::MIDI, new_midi, true);
}
}
bool
PortManager::can_request_input_monitoring () const
{
@ -1589,6 +1706,20 @@ PortManager::reset_input_meters ()
g_atomic_int_set (&_reset_meters, 1);
}
PortManager::AudioInputPorts
PortManager::audio_input_ports () const
{
boost::shared_ptr<AudioInputPorts> p = _audio_input_ports.reader ();
return *p;
}
PortManager::MIDIInputPorts
PortManager::midi_input_ports () const
{
boost::shared_ptr<MIDIInputPorts> p = _midi_input_ports.reader ();
return *p;
}
/* Cache dB -> coefficient calculation for dB/sec falloff.
* @n_samples engine-buffer-size
* @rate engine sample-rate
@ -1635,6 +1766,9 @@ static FallOffCache falloff_cache;
void
PortManager::run_input_meters (pframes_t n_samples, samplecnt_t rate)
{
if (n_samples == 0) {
return;
}
const bool reset = g_atomic_int_compare_and_exchange (&_reset_meters, 1, 0);
const float falloff = falloff_cache.calc (n_samples, rate);
@ -1650,20 +1784,16 @@ PortManager::run_input_meters (pframes_t n_samples, samplecnt_t rate)
if (!ph) {
continue;
}
if (n_samples == 0) {
/* allocate using default c'tor */
_audio_port_meters[*p];
_audio_port_scopes[*p] = AudioPortScope (new CircularSampleBuffer (524288)); // 2^19 ~ 1MB / port
continue;
}
if (_audio_port_meters.find (*p) == _audio_port_meters.end () || _audio_port_scopes.find (*p) == _audio_port_scopes.end ()) {
boost::shared_ptr<AudioInputPorts> aip = _audio_input_ports.reader ();
AudioInputPorts::iterator ai = aip->find (*p);
if (ai == aip->end ()) {
/* do not allocate ports during normal operation */
continue;
}
if (reset) {
_audio_port_meters[*p].reset ();
ai->second.meter->reset ();
}
Sample* buf = (Sample*) _backend->get_buffer (ph, n_samples);
@ -1671,19 +1801,19 @@ PortManager::run_input_meters (pframes_t n_samples, samplecnt_t rate)
continue;
}
_audio_port_scopes[*p]->write (buf, n_samples);
ai->second.scope->write (buf, n_samples);
/* falloff */
if (_audio_port_meters[*p].level > 1e-10) {
_audio_port_meters[*p].level *= falloff;
if (ai->second.meter->level > 1e-10) {
ai->second.meter->level *= falloff;
} else {
_audio_port_meters[*p].level = 0;
ai->second.meter->level = 0;
}
_audio_port_meters[*p].level = compute_peak (buf, n_samples, reset ? 0 : _audio_port_meters[*p].level);
_audio_port_meters[*p].level = std::min (_audio_port_meters[*p].level, 100.f); // cut off at +40dBFS for falloff.
_audio_port_meters[*p].peak = std::max (_audio_port_meters[*p].peak, _audio_port_meters[*p].level);
float level = ai->second.meter->level;
level = compute_peak (buf, n_samples, reset ? 0 : level);
ai->second.meter->level = std::min (level, 100.f); // cut off at +40dBFS for falloff.
ai->second.meter->peak = std::max (ai->second.meter->peak, level);
}
/* MIDI */
@ -1698,24 +1828,19 @@ PortManager::run_input_meters (pframes_t n_samples, samplecnt_t rate)
continue;
}
if (n_samples == 0) {
/* allocate using default c'tor */
_midi_port_meters[*p];
_midi_port_monitors[*p] = MIDIPortMonitor (new CircularEventBuffer (32));
continue;
}
if (_midi_port_meters.find (*p) == _midi_port_meters.end () || _midi_port_monitors.find (*p) == _midi_port_monitors.end ()) {
boost::shared_ptr<MIDIInputPorts> mip = _midi_input_ports.reader ();
MIDIInputPorts::iterator mi = mip->find (*p);
if (mi == mip->end ()) {
/* do not allocate ports during normal operation */
continue;
}
for (size_t i = 0; i < 17; ++i) {
/* falloff */
if (_midi_port_meters[*p].chn_active[i] > 1e-10) {
_midi_port_meters[*p].chn_active[i] *= falloff;
if (mi->second.meter->chn_active[i] > 1e-10) {
mi->second.meter->chn_active[i] *= falloff;
} else {
_midi_port_meters[*p].chn_active[i] = 0;
mi->second.meter->chn_active[i] = 0;
}
}
@ -1732,12 +1857,12 @@ PortManager::run_input_meters (pframes_t n_samples, samplecnt_t rate)
continue;
}
if ((buf[0] & 0xf0) == 0xf0) {
_midi_port_meters[*p].chn_active[16] = 1.0;
mi->second.meter->chn_active[16] = 1.0;
} else {
int chn = (buf[0] & 0x0f);
_midi_port_meters[*p].chn_active[chn] = 1.0;
mi->second.meter->chn_active[chn] = 1.0;
}
_midi_port_monitors[*p]->write (buf, size);
mi->second.monitor->write (buf, size);
}
}
}