a grab bag of changes correcting and improving the way MIDI note on/off tracking is done. may/should fix a number of problem with spurious note-offs under a variety of circumstances
git-svn-id: svn://localhost/ardour2/branches/3.0@11074 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
bda0f938fb
commit
5558b3cf06
|
@ -65,6 +65,8 @@ class MidiDiskstream : public Diskstream
|
|||
void get_playback (MidiBuffer& dst, framecnt_t);
|
||||
|
||||
void set_record_enabled (bool yn);
|
||||
|
||||
void reset_tracker ();
|
||||
|
||||
boost::shared_ptr<MidiPlaylist> midi_playlist () { return boost::dynamic_pointer_cast<MidiPlaylist>(_playlist); }
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
return g_atomic_int_get(&_channel_mask) & 0x0000FFFF;
|
||||
}
|
||||
|
||||
void reset_tracker ();
|
||||
|
||||
protected:
|
||||
inline bool is_channel_event(uint8_t event_type_byte) {
|
||||
// mask out channel information
|
||||
|
|
|
@ -107,6 +107,8 @@ public:
|
|||
PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded;
|
||||
boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
|
||||
|
||||
void set_monitoring (MonitorChoice);
|
||||
|
||||
void set_input_active (bool);
|
||||
bool input_active () const;
|
||||
PBD::Signal0<void> InputActiveChanged;
|
||||
|
|
|
@ -141,6 +141,8 @@ class Plugin : public PBD::StatefulDestructible, public Latent
|
|||
}
|
||||
|
||||
void realtime_handle_transport_stopped ();
|
||||
void realtime_locate ();
|
||||
void monitoring_changed ();
|
||||
|
||||
struct PresetRecord {
|
||||
PresetRecord () : user (true) {}
|
||||
|
@ -257,6 +259,8 @@ private:
|
|||
bool _have_pending_stop_events;
|
||||
PresetRecord _last_preset;
|
||||
bool _parameter_changed_since_last_preset;
|
||||
|
||||
void resolve_midi ();
|
||||
};
|
||||
|
||||
PluginPtr find_plugin(ARDOUR::Session&, std::string unique_id, ARDOUR::PluginType);
|
||||
|
|
|
@ -77,6 +77,8 @@ class PluginInsert : public Processor
|
|||
bool is_midi_instrument() const;
|
||||
|
||||
void realtime_handle_transport_stopped ();
|
||||
void realtime_locate ();
|
||||
void monitoring_changed ();
|
||||
|
||||
struct PluginControl : public AutomationControl
|
||||
{
|
||||
|
|
|
@ -87,6 +87,13 @@ class Processor : public SessionObject, public Automatable, public Latent
|
|||
virtual void realtime_handle_transport_stopped () {}
|
||||
virtual void realtime_locate () {}
|
||||
|
||||
/* most processors won't care about this, but plugins that
|
||||
receive MIDI or similar data from an input source that
|
||||
may suddenly go "quiet" because of monitoring changes
|
||||
need to know about it.
|
||||
*/
|
||||
virtual void monitoring_changed() {}
|
||||
|
||||
/* note: derived classes should implement state(), NOT get_state(), to allow
|
||||
us to merge C++ inheritance and XML lack-of-inheritance reasonably
|
||||
smoothly.
|
||||
|
|
|
@ -737,16 +737,16 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
|
|||
start = loop_start + ((start - loop_start) % loop_length);
|
||||
//cerr << "to " << start << endl;
|
||||
}
|
||||
//cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
|
||||
// cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
|
||||
}
|
||||
|
||||
while (dur) {
|
||||
|
||||
/* take any loop into account. we can't read past the end of the loop. */
|
||||
|
||||
if (loc && (loop_end - start < dur)) {
|
||||
if (loc && (loop_end - start <= dur)) {
|
||||
this_read = loop_end - start;
|
||||
//cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl;
|
||||
// cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl;
|
||||
reloop = true;
|
||||
} else {
|
||||
reloop = false;
|
||||
|
@ -1102,9 +1102,7 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
|
|||
|
||||
no_capture_stuff_to_do:
|
||||
|
||||
if (_playlist) {
|
||||
midi_playlist()->clear_note_trackers ();
|
||||
}
|
||||
reset_tracker ();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1134,6 +1132,10 @@ MidiDiskstream::transport_looped (framepos_t transport_frame)
|
|||
last_recordable_frame = max_framepos;
|
||||
was_recording = true;
|
||||
}
|
||||
|
||||
if (!Config->get_seamless_loop()) {
|
||||
reset_tracker ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1477,3 +1479,15 @@ MidiDiskstream::get_gui_feed_buffer () const
|
|||
b->copy (_gui_feed_buffer);
|
||||
return b;
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::reset_tracker ()
|
||||
{
|
||||
_playback_buf->reset_tracker ();
|
||||
|
||||
boost::shared_ptr<MidiPlaylist> mp (midi_playlist());
|
||||
|
||||
if (mp) {
|
||||
mp->clear_note_trackers ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,8 +83,16 @@ MidiPlaylist::~MidiPlaylist ()
|
|||
}
|
||||
|
||||
template<typename Time>
|
||||
struct EventsSortByTime {
|
||||
struct EventsSortByTimeAndType {
|
||||
bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
|
||||
if (a->time() == b->time()) {
|
||||
if (EventTypeMap::instance().type_is_midi (a->event_type()) && EventTypeMap::instance().type_is_midi (b->event_type())) {
|
||||
/* negate return value since we must return whether
|
||||
* or not a should sort before b, not b before a
|
||||
*/
|
||||
return !MidiBuffer::second_simultaneous_midi_byte_is_first (a->buffer()[0], b->buffer()[0]);
|
||||
}
|
||||
}
|
||||
return a->time() < b->time();
|
||||
}
|
||||
};
|
||||
|
@ -98,59 +106,50 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
|
|||
*/
|
||||
|
||||
Glib::RecMutex::Lock rm (region_lock);
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++ %3 trackers +++++++++++++++++\n",
|
||||
start, start + dur, _note_trackers.size()));
|
||||
|
||||
framepos_t end = start + dur - 1;
|
||||
|
||||
// relevent regions overlapping start <--> end
|
||||
vector< boost::shared_ptr<Region> > regs;
|
||||
vector< boost::shared_ptr<Region> > ended;
|
||||
typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
|
||||
vector<TrackerInfo> tracker_info;
|
||||
uint32_t note_cnt = 0;
|
||||
NoteTrackers::iterator t;
|
||||
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
if ((*i)->coverage (start, end) != OverlapNone) {
|
||||
regs.push_back(*i);
|
||||
} else {
|
||||
NoteTrackers::iterator t = _note_trackers.find ((*i).get());
|
||||
if (t != _note_trackers.end()) {
|
||||
|
||||
/* add it the set of trackers we will do note resolution
|
||||
on, and remove it from the list we are keeping
|
||||
around, because we don't need it anymore.
|
||||
/* in this call to coverage, the return value indicates the
|
||||
* overlap status of the read range (start...end) WRT to
|
||||
* the region.
|
||||
*/
|
||||
|
||||
if the end of the region (where we want to theoretically resolve notes)
|
||||
is outside the current read range, then just do it at the start
|
||||
of this read range.
|
||||
*/
|
||||
switch ((*i)->coverage (start, end)) {
|
||||
case OverlapStart:
|
||||
case OverlapInternal:
|
||||
case OverlapExternal:
|
||||
regs.push_back (*i);
|
||||
break;
|
||||
|
||||
framepos_t resolve_at = (*i)->last_frame();
|
||||
if (resolve_at < start || resolve_at >= end) {
|
||||
resolve_at = start;
|
||||
}
|
||||
|
||||
tracker_info.push_back (TrackerInfo (t->second, resolve_at));
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
|
||||
note_cnt += (t->second->on());
|
||||
_note_trackers.erase (t);
|
||||
}
|
||||
case OverlapEnd:
|
||||
/* this region ends within the read range */
|
||||
regs.push_back (*i);
|
||||
ended.push_back (*i);
|
||||
break;
|
||||
default:
|
||||
/* we don't care */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (note_cnt == 0 && !tracker_info.empty()) {
|
||||
/* trackers to dispose of, but they have no notes in them */
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
|
||||
for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
|
||||
delete (*t).first;
|
||||
}
|
||||
tracker_info.clear ();
|
||||
}
|
||||
|
||||
if (regs.size() == 1 && tracker_info.empty()) {
|
||||
if (regs.size() == 1 &&
|
||||
(ended.empty() || (ended.size() == 1 && ended.front() == regs.front()))) {
|
||||
|
||||
/* just a single region - read directly into dst */
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, ended during this read %2\n", regs.front()->name(),
|
||||
ended.size()));
|
||||
|
||||
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
|
||||
|
||||
|
@ -172,12 +171,22 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
|
|||
mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
|
||||
|
||||
if (new_tracker) {
|
||||
pair<Region*,MidiStateTracker*> newpair;
|
||||
newpair.first = mr.get();
|
||||
newpair.second = tracker;
|
||||
_note_trackers.insert (newpair);
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
|
||||
if (!ended.empty()) {
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
|
||||
mr->name(), ((new_tracker) ? "new" : "old")));
|
||||
tracker->resolve_notes (dst, mr->last_frame());
|
||||
delete tracker;
|
||||
if (!new_tracker) {
|
||||
_note_trackers.erase (t);
|
||||
}
|
||||
} else {
|
||||
if (new_tracker) {
|
||||
pair<Region*,MidiStateTracker*> newpair;
|
||||
newpair.first = mr.get();
|
||||
newpair.second = tracker;
|
||||
_note_trackers.insert (newpair);
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,23 +200,12 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
|
|||
|
||||
Evoral::EventList<framepos_t> evlist;
|
||||
|
||||
for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
|
||||
(*t).first->resolve_notes (evlist, (*t).second);
|
||||
delete (*t).first;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
|
||||
for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
|
||||
|
||||
for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
|
||||
|
||||
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
|
||||
|
||||
if (!mr) {
|
||||
continue;
|
||||
}
|
||||
|
@ -216,7 +214,6 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
|
|||
MidiStateTracker* tracker;
|
||||
bool new_tracker = false;
|
||||
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Before %1 (%2 .. %3) we now have %4 events\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
|
||||
|
||||
if (t == _note_trackers.end()) {
|
||||
|
@ -238,21 +235,39 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
|
|||
}
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
|
||||
#endif
|
||||
if (find (ended.begin(), ended.end(), *i) != ended.end()) {
|
||||
|
||||
if (new_tracker) {
|
||||
pair<Region*,MidiStateTracker*> newpair;
|
||||
newpair.first = mr.get();
|
||||
newpair.second = tracker;
|
||||
_note_trackers.insert (newpair);
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
|
||||
/* the region ended within the read range, so
|
||||
* resolve any dangling notes (i.e. notes whose
|
||||
* end is beyond the end of the region).
|
||||
*/
|
||||
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
|
||||
mr->name(), ((new_tracker) ? "new" : "old")));
|
||||
|
||||
tracker->resolve_notes (evlist, (*i)->last_frame());
|
||||
delete tracker;
|
||||
if (!new_tracker) {
|
||||
_note_trackers.erase (t);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (new_tracker) {
|
||||
pair<Region*,MidiStateTracker*> newpair;
|
||||
newpair.first = mr.get();
|
||||
newpair.second = tracker;
|
||||
_note_trackers.insert (newpair).first;
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!evlist.empty()) {
|
||||
|
||||
/* sort the event list */
|
||||
EventsSortByTime<framepos_t> time_cmp;
|
||||
evlist.sort (time_cmp);
|
||||
EventsSortByTimeAndType<framepos_t> cmp;
|
||||
evlist.sort (cmp);
|
||||
|
||||
#ifndef NDEBUG
|
||||
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
|
||||
|
|
|
@ -270,5 +270,12 @@ MidiRingBuffer<T>::dump(ostream& str)
|
|||
delete [] buf;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MidiRingBuffer<T>::reset_tracker ()
|
||||
{
|
||||
_tracker.reset ();
|
||||
}
|
||||
|
||||
template class MidiRingBuffer<framepos_t>;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/stacktrace.h"
|
||||
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/event_type_map.h"
|
||||
|
@ -57,15 +58,23 @@ MidiStateTracker::track_note_onoffs (const Evoral::MIDIEvent<MidiBuffer::TimeTyp
|
|||
void
|
||||
MidiStateTracker::add (uint8_t note, uint8_t chn)
|
||||
{
|
||||
if (_active_notes[note+128 * chn] == 0) {
|
||||
++_on;
|
||||
}
|
||||
++_active_notes[note + 128 * chn];
|
||||
++_on;
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("MST @ %1 ON %2/%3 total on %4\n",
|
||||
this, (int) note, (int) chn, _on));
|
||||
|
||||
if (_active_notes[note+128 * chn] > 1) {
|
||||
cerr << this << " note " << (int) note << '/' << (int) chn << " was already on, now at " << (int) _active_notes[note+128*chn] << endl;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 ON %2/%3 voices %5 total on %4\n",
|
||||
this, (int) note, (int) chn, _on,
|
||||
(int) _active_notes[note+128 * chn]));
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::remove (uint8_t note, uint8_t chn)
|
||||
{
|
||||
{
|
||||
switch (_active_notes[note + 128 * chn]) {
|
||||
case 0:
|
||||
break;
|
||||
|
@ -75,11 +84,12 @@ MidiStateTracker::remove (uint8_t note, uint8_t chn)
|
|||
break;
|
||||
default:
|
||||
--_active_notes [note + 128 * chn];
|
||||
break;
|
||||
break;
|
||||
|
||||
}
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("MST @ %1 OFF %2/%3 total on %4\n",
|
||||
this, (int) note, (int) chn, _on));
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1 OFF %2/%3 current voices = %5 total on %4\n",
|
||||
this, (int) note, (int) chn, _on,
|
||||
(int) _active_notes[note+128 * chn]));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -102,9 +112,10 @@ MidiStateTracker::track (const MidiBuffer::iterator &from, const MidiBuffer::ite
|
|||
if (ev.type() == MIDI_CTL_ALL_NOTES_OFF) {
|
||||
cerr << "State tracker sees ALL_NOTES_OFF, silenceing " << sizeof (_active_notes) << endl;
|
||||
memset (_active_notes, 0, sizeof (_active_notes));
|
||||
_on = 0;
|
||||
} else {
|
||||
track_note_onoffs (ev);
|
||||
}
|
||||
|
||||
track_note_onoffs (ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +134,9 @@ MidiStateTracker::resolve_notes (MidiBuffer &dst, framepos_t time)
|
|||
uint8_t buffer[3] = { MIDI_CMD_NOTE_OFF | channel, note, 0 };
|
||||
Evoral::MIDIEvent<MidiBuffer::TimeType> noteoff
|
||||
(MIDI_CMD_NOTE_OFF, time, 3, buffer, false);
|
||||
/* note that we do not care about failure from
|
||||
push_back() ... should we warn someone ?
|
||||
*/
|
||||
dst.push_back (noteoff);
|
||||
_active_notes[note + 128 * channel]--;
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: MB-resolved note %2/%3 at %4\n",
|
||||
|
@ -150,6 +164,9 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<framepos_t> &dst, framepos_t
|
|||
buf[0] = MIDI_CMD_NOTE_OFF|channel;
|
||||
buf[1] = note;
|
||||
buf[2] = 0;
|
||||
/* note that we do not care about failure from
|
||||
write() ... should we warn someone ?
|
||||
*/
|
||||
dst.write (time, EventTypeMap::instance().midi_event_type (buf[0]), 3, buf);
|
||||
_active_notes[note + 128 * channel]--;
|
||||
DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: EVS-resolved note %2/%3 at %4\n",
|
||||
|
|
|
@ -109,11 +109,11 @@ MidiTrack::set_diskstream (boost::shared_ptr<Diskstream> ds)
|
|||
{
|
||||
Track::set_diskstream (ds);
|
||||
|
||||
midi_diskstream()->reset_tracker ();
|
||||
|
||||
_diskstream->set_track (this);
|
||||
_diskstream->set_destructive (_mode == Destructive);
|
||||
|
||||
_diskstream->set_record_enabled (false);
|
||||
//_diskstream->monitor_input (false);
|
||||
|
||||
_diskstream_data_recorded_connection.disconnect ();
|
||||
boost::shared_ptr<MidiDiskstream> mds = boost::dynamic_pointer_cast<MidiDiskstream> (ds);
|
||||
|
@ -391,6 +391,7 @@ void
|
|||
MidiTrack::realtime_locate ()
|
||||
{
|
||||
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
|
||||
|
||||
if (!lm.locked ()) {
|
||||
return;
|
||||
}
|
||||
|
@ -398,6 +399,8 @@ MidiTrack::realtime_locate ()
|
|||
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
|
||||
(*i)->realtime_locate ();
|
||||
}
|
||||
|
||||
midi_diskstream()->reset_tracker ();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -734,3 +737,14 @@ MidiTrack::act_on_mute ()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiTrack::set_monitoring (MonitorChoice mc)
|
||||
{
|
||||
Track::set_monitoring (mc);
|
||||
|
||||
boost::shared_ptr<MidiDiskstream> md (midi_diskstream());
|
||||
|
||||
if (md) {
|
||||
md->reset_tracker ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ Plugin::Plugin (AudioEngine& e, Session& s)
|
|||
, _have_pending_stop_events (false)
|
||||
, _parameter_changed_since_last_preset (false)
|
||||
{
|
||||
_pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096);
|
||||
}
|
||||
|
||||
Plugin::Plugin (const Plugin& other)
|
||||
|
@ -83,12 +84,11 @@ Plugin::Plugin (const Plugin& other)
|
|||
, _have_pending_stop_events (false)
|
||||
, _parameter_changed_since_last_preset (false)
|
||||
{
|
||||
|
||||
_pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096);
|
||||
}
|
||||
|
||||
Plugin::~Plugin ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -242,8 +242,10 @@ Plugin::connect_and_run (BufferSet& bufs,
|
|||
if (bufs.count().n_midi() > 0) {
|
||||
|
||||
/* Track notes that we are sending to the plugin */
|
||||
|
||||
MidiBuffer& b = bufs.get_midi (0);
|
||||
bool looped;
|
||||
|
||||
_tracker.track (b.begin(), b.end(), looped);
|
||||
|
||||
if (_have_pending_stop_events) {
|
||||
|
@ -258,12 +260,29 @@ Plugin::connect_and_run (BufferSet& bufs,
|
|||
|
||||
void
|
||||
Plugin::realtime_handle_transport_stopped ()
|
||||
{
|
||||
resolve_midi ();
|
||||
}
|
||||
|
||||
void
|
||||
Plugin::realtime_locate ()
|
||||
{
|
||||
resolve_midi ();
|
||||
}
|
||||
|
||||
void
|
||||
Plugin::monitoring_changed ()
|
||||
{
|
||||
resolve_midi ();
|
||||
}
|
||||
|
||||
void
|
||||
Plugin::resolve_midi ()
|
||||
{
|
||||
/* Create note-offs for any active notes and put them in _pending_stop_events, to be picked
|
||||
up on the next call to connect_and_run ().
|
||||
*/
|
||||
|
||||
_pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096);
|
||||
_pending_stop_events.get_midi(0).clear ();
|
||||
_tracker.resolve_notes (_pending_stop_events.get_midi (0), 0);
|
||||
_have_pending_stop_events = true;
|
||||
|
@ -345,3 +364,5 @@ Plugin::set_info (PluginInfoPtr info)
|
|||
{
|
||||
_info = info;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1282,3 +1282,19 @@ PluginInsert::realtime_handle_transport_stopped ()
|
|||
(*i)->realtime_handle_transport_stopped ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluginInsert::realtime_locate ()
|
||||
{
|
||||
for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
|
||||
(*i)->realtime_locate ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluginInsert::monitoring_changed ()
|
||||
{
|
||||
for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
|
||||
(*i)->monitoring_changed ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -890,6 +890,11 @@ Track::set_monitoring (MonitorChoice mc)
|
|||
{
|
||||
if (mc != _monitoring) {
|
||||
_monitoring = mc;
|
||||
|
||||
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
|
||||
(*i)->monitoring_changed ();
|
||||
}
|
||||
|
||||
MonitoringChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user