summaryrefslogtreecommitdiff
path: root/libs/ardour/midi_playlist.cc
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-03-01 13:33:25 -0500
committerDavid Robillard <d@drobilla.net>2015-03-05 17:30:31 -0500
commita8aae56d92699e4545b5f8a69742f9a1c75ad238 (patch)
treeae3c14f1b78fbfddf126b44e533692b137d93f28 /libs/ardour/midi_playlist.cc
parent09f1571fc0c9dd164601cfd3d12fac31a084b9f6 (diff)
Handle edits while playing precisely.
This avoids stuck notes if active notes are edited, but without stopping all active notes in the region on any edit as before. This implementation injects note ons in places that aren't actually note starts. Depending on how percussive the instrument is, this may not be desired. In the future, an option for this would be an improvement, but there are other places where "start notes in the middle" is a reasonable option. I think that should be handled universally if we're to do it at all, so not considering it a part of this fix for now.
Diffstat (limited to 'libs/ardour/midi_playlist.cc')
-rw-r--r--libs/ardour/midi_playlist.cc101
1 files changed, 52 insertions, 49 deletions
diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc
index 60753d0baa..398234e2ba 100644
--- a/libs/ardour/midi_playlist.cc
+++ b/libs/ardour/midi_playlist.cc
@@ -17,21 +17,22 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <cassert>
-
#include <algorithm>
+#include <cassert>
+#include <cstdlib>
#include <iostream>
#include <utility>
-#include <stdlib.h>
-
#include "evoral/EventList.hpp"
+#include "ardour/beats_frames_converter.h"
#include "ardour/debug.h"
#include "ardour/midi_model.h"
#include "ardour/midi_playlist.h"
#include "ardour/midi_region.h"
+#include "ardour/midi_source.h"
#include "ardour/midi_state_tracker.h"
+#include "ardour/session.h"
#include "ardour/types.h"
#include "i18n.h"
@@ -43,6 +44,7 @@ using namespace std;
MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
: Playlist (session, node, DataType::MIDI, hidden)
, _note_mode(Sustained)
+ , _read_end(0)
{
#ifndef NDEBUG
const XMLProperty* prop = node.property("type");
@@ -61,12 +63,14 @@ MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
: Playlist (session, name, DataType::MIDI, hidden)
, _note_mode(Sustained)
+ , _read_end(0)
{
}
MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
: Playlist (other, name, hidden)
, _note_mode(other->_note_mode)
+ , _read_end(0)
{
}
@@ -77,6 +81,7 @@ MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other,
bool hidden)
: Playlist (other, start, dur, name, hidden)
, _note_mode(other->_note_mode)
+ , _read_end(0)
{
}
@@ -103,13 +108,18 @@ struct EventsSortByTimeAndType {
framecnt_t
MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framecnt_t dur, unsigned chan_n)
{
+ typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
+
Playlist::RegionReadLock rl (this);
DEBUG_TRACE (DEBUG::MidiPlaylistIO,
string_compose ("---- MidiPlaylist::read %1 .. %2 (%3 trackers) ----\n",
start, start + dur, _note_trackers.size()));
- typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
+ /* First, emit any queued edit fixup events at start. */
+ for (NoteTrackers::iterator t = _note_trackers.begin(); t != _note_trackers.end(); ++t) {
+ t->second->fixer.emit(dst, _read_end, t->second->tracker);
+ }
/* Find relevant regions that overlap [start..end] */
const framepos_t end = start + dur - 1;
@@ -158,11 +168,11 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
}
/* Get the existing note tracker for this region, or create a new one. */
- NoteTrackers::iterator t = _note_trackers.find (mr.get());
- MidiStateTracker* tracker = NULL;
- bool new_tracker = false;
+ NoteTrackers::iterator t = _note_trackers.find (mr.get());
+ bool new_tracker = false;
+ boost::shared_ptr<RegionTracker> tracker;
if (t == _note_trackers.end()) {
- tracker = new MidiStateTracker;
+ tracker = boost::shared_ptr<RegionTracker>(new RegionTracker);
new_tracker = true;
DEBUG_TRACE (DEBUG::MidiPlaylistIO,
string_compose ("\tPre-read %1 (%2 .. %3): new tracker\n",
@@ -171,13 +181,13 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
tracker = t->second;
DEBUG_TRACE (DEBUG::MidiPlaylistIO,
string_compose ("\tPre-read %1 (%2 .. %3): %4 active notes\n",
- mr->name(), mr->position(), mr->last_frame(), tracker->on()));
+ mr->name(), mr->position(), mr->last_frame(), tracker->tracker.on()));
}
- /** Read from region into target. */
- mr->read_at (tgt, start, dur, chan_n, _note_mode, tracker);
+ /* Read from region into target. */
+ mr->read_at (tgt, start, dur, chan_n, _note_mode, &tracker->tracker);
DEBUG_TRACE (DEBUG::MidiPlaylistIO,
- string_compose ("\tPost-read: %1 active notes\n", tracker->on()));
+ string_compose ("\tPost-read: %1 active notes\n", tracker->tracker.on()));
if (find (ended.begin(), ended.end(), *i) != ended.end()) {
/* Region ended within the read range, so resolve any active notes
@@ -187,8 +197,7 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
string_compose ("\t%1 ended, resolve notes and delete (%2) tracker\n",
mr->name(), ((new_tracker) ? "new" : "old")));
- tracker->resolve_notes (tgt, (*i)->last_frame());
- delete tracker;
+ tracker->tracker.resolve_notes (tgt, (*i)->last_frame());
if (!new_tracker) {
_note_trackers.erase (t);
}
@@ -216,17 +225,40 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
}
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "---- End MidiPlaylist::read ----\n");
+ _read_end = start + dur;
return dur;
}
void
+MidiPlaylist::region_edited(boost::shared_ptr<Region> region,
+ const MidiModel::NoteDiffCommand* cmd)
+{
+ typedef MidiModel::NoteDiffCommand Command;
+
+ boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
+ if (!mr || !_session.transport_rolling()) {
+ return;
+ }
+
+ /* Take write lock to prevent concurrency with read(). */
+ Playlist::RegionWriteLock lock(this);
+
+ NoteTrackers::iterator t = _note_trackers.find(mr.get());
+ if (t == _note_trackers.end()) {
+ return; /* Region is not currently active, nothing to do. */
+ }
+
+ /* Queue any necessary edit compensation events. */
+ t->second->fixer.prepare(
+ _session.tempo_map(), cmd, mr->position() - mr->start(),
+ _read_end, mr->midi_source()->model()->active_notes());
+}
+
+void
MidiPlaylist::reset_note_trackers ()
{
Playlist::RegionWriteLock rl (this, false);
- for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
- delete n->second;
- }
DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 reset all note trackers\n", name()));
_note_trackers.clear ();
}
@@ -237,8 +269,7 @@ MidiPlaylist::resolve_note_trackers (Evoral::EventSink<framepos_t>& dst, framepo
Playlist::RegionWriteLock rl (this, false);
for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
- n->second->resolve_notes(dst, time);
- delete n->second;
+ n->second->tracker.resolve_notes(dst, time);
}
DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 resolve all note trackers\n", name()));
_note_trackers.clear ();
@@ -248,14 +279,7 @@ void
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
{
/* MIDI regions have no dependents (crossfades) but we might be tracking notes */
- NoteTrackers::iterator t = _note_trackers.find (region.get());
-
- /* GACK! THREAD SAFETY! */
-
- if (t != _note_trackers.end()) {
- delete t->second;
- _note_trackers.erase (t);
- }
+ _note_trackers.erase(region.get());
}
int
@@ -355,24 +379,3 @@ MidiPlaylist::contained_automation()
return ret;
}
-
-
-bool
-MidiPlaylist::region_changed (const PBD::PropertyChange& what_changed, boost::shared_ptr<Region> region)
-{
- if (in_flush || in_set_state) {
- return false;
- }
-
- PBD::PropertyChange our_interests;
- our_interests.add (Properties::midi_data);
-
- bool parent_wants_notify = Playlist::region_changed (what_changed, region);
-
- if (parent_wants_notify || what_changed.contains (our_interests)) {
- notify_contents_changed ();
- }
-
- return true;
-}
-