summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour/ardour')
-rw-r--r--libs/ardour/ardour/midi_model.h21
-rw-r--r--libs/ardour/ardour/midi_playlist.h36
-rw-r--r--libs/ardour/ardour/midi_region.h6
-rw-r--r--libs/ardour/ardour/midi_source.h8
-rw-r--r--libs/ardour/ardour/note_fixer.h102
5 files changed, 151 insertions, 22 deletions
diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h
index 4214431280..b2e018ca3b 100644
--- a/libs/ardour/ardour/midi_model.h
+++ b/libs/ardour/ardour/midi_model.h
@@ -126,7 +126,6 @@ public:
static Variant::Type value_type (Property prop);
- private:
struct NoteChange {
NoteDiffCommand::Property property;
NotePtr note;
@@ -135,12 +134,17 @@ public:
Variant new_value;
};
- typedef std::list<NoteChange> ChangeList;
- ChangeList _changes;
-
+ typedef std::list<NoteChange> ChangeList;
typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
- NoteList _added_notes;
- NoteList _removed_notes;
+
+ const ChangeList& changes() const { return _changes; }
+ const NoteList& added_notes() const { return _added_notes; }
+ const NoteList& removed_notes() const { return _removed_notes; }
+
+ private:
+ ChangeList _changes;
+ NoteList _added_notes;
+ NoteList _removed_notes;
std::set<NotePtr> side_effect_removals;
@@ -285,6 +289,8 @@ public:
void insert_silence_at_start (TimeType);
void transpose (TimeType, TimeType, int);
+ std::set<WeakNotePtr>& active_notes() { return _active_notes; }
+
protected:
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
@@ -302,7 +308,6 @@ private:
public:
WriteLock edit_lock();
- WriteLock write_lock();
private:
friend class DeltaCommand;
@@ -319,6 +324,8 @@ private:
// We cannot use a boost::shared_ptr here to avoid a retain cycle
boost::weak_ptr<MidiSource> _midi_source;
InsertMergePolicy _insert_merge_policy;
+
+ std::set<WeakNotePtr> _active_notes;
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h
index f49593bd85..614a5e1c1f 100644
--- a/libs/ardour/ardour/midi_playlist.h
+++ b/libs/ardour/ardour/midi_playlist.h
@@ -23,8 +23,15 @@
#include <vector>
#include <list>
+#include <boost/utility.hpp>
+
#include "ardour/ardour.h"
+#include "ardour/midi_model.h"
+#include "ardour/midi_state_tracker.h"
+#include "ardour/note_fixer.h"
#include "ardour/playlist.h"
+#include "evoral/Beats.hpp"
+#include "evoral/Note.hpp"
#include "evoral/Parameter.hpp"
namespace Evoral {
@@ -34,10 +41,10 @@ template<typename Time> class EventSink;
namespace ARDOUR
{
-class Session;
+class BeatsFramesConverter;
class MidiRegion;
+class Session;
class Source;
-class MidiStateTracker;
template<typename T> class MidiRingBuffer;
@@ -80,6 +87,15 @@ public:
std::set<Evoral::Parameter> contained_automation();
+ /** Handle a region edit during read.
+ *
+ * This must be called before the command is applied to the model. Events
+ * are injected into the playlist output to compensate for edits to active
+ * notes and maintain coherent output and tracker state.
+ */
+ void region_edited(boost::shared_ptr<Region> region,
+ const MidiModel::NoteDiffCommand* cmd);
+
/** Clear all note trackers. */
void reset_note_trackers ();
@@ -91,18 +107,24 @@ public:
void resolve_note_trackers (Evoral::EventSink<framepos_t>& dst, framepos_t time);
protected:
-
void remove_dependents (boost::shared_ptr<Region> region);
private:
- void dump () const;
+ typedef Evoral::Note<Evoral::Beats> Note;
+ typedef Evoral::Event<framepos_t> Event;
- bool region_changed (const PBD::PropertyChange&, boost::shared_ptr<Region>);
+ struct RegionTracker : public boost::noncopyable {
+ MidiStateTracker tracker; ///< Active note tracker
+ NoteFixer fixer; ///< Edit compensation
+ };
- NoteMode _note_mode;
+ typedef std::map< Region*, boost::shared_ptr<RegionTracker> > NoteTrackers;
+
+ void dump () const;
- typedef std::map<Region*,MidiStateTracker*> NoteTrackers;
NoteTrackers _note_trackers;
+ NoteMode _note_mode;
+ framepos_t _read_end;
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
index ece23b65f0..f7e6c97ea0 100644
--- a/libs/ardour/ardour/midi_region.h
+++ b/libs/ardour/ardour/midi_region.h
@@ -32,11 +32,6 @@ class XMLNode;
namespace ARDOUR {
namespace Properties {
- /* this is pseudo-property: nothing has this as an actual
- property, but it allows us to signal changes to the
- MidiModel used by the MidiRegion
- */
- LIBARDOUR_API extern PBD::PropertyDescriptor<void*> midi_data;
LIBARDOUR_API extern PBD::PropertyDescriptor<Evoral::Beats> start_beats;
LIBARDOUR_API extern PBD::PropertyDescriptor<Evoral::Beats> length_beats;
}
@@ -141,7 +136,6 @@ class LIBARDOUR_API MidiRegion : public Region
void model_changed ();
void model_automation_state_changed (Evoral::Parameter const &);
- void model_contents_changed ();
void set_start_beats_from_start_frames ();
void update_after_tempo_map_change ();
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index 8a0c13681d..156f3dbfa0 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -154,8 +154,12 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
virtual void load_model(const Glib::Threads::Mutex::Lock& lock, bool force_reload=false) = 0;
virtual void destroy_model(const Glib::Threads::Mutex::Lock& lock) = 0;
- /** Reset cached information (like iterators) when things have changed. */
- void invalidate(const Glib::Threads::Mutex::Lock& lock);
+ /** Reset cached information (like iterators) when things have changed.
+ * @param lock Source lock, which must be held by caller.
+ * @param notes If non-NULL, currently active notes are added to this set.
+ */
+ void invalidate(const Glib::Threads::Mutex::Lock& lock,
+ std::set<Evoral::Sequence<Evoral::Beats>::WeakNotePtr>* notes=NULL);
void set_note_mode(const Glib::Threads::Mutex::Lock& lock, NoteMode mode);
diff --git a/libs/ardour/ardour/note_fixer.h b/libs/ardour/ardour/note_fixer.h
new file mode 100644
index 0000000000..09f45cdec7
--- /dev/null
+++ b/libs/ardour/ardour/note_fixer.h
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2015 Paul Davis
+ Author: David Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_note_fixer_h__
+#define __ardour_note_fixer_h__
+
+#include <list>
+
+#include <boost/utility.hpp>
+
+#include "ardour/midi_model.h"
+#include "ardour/types.h"
+#include "evoral/Beats.hpp"
+#include "evoral/Note.hpp"
+
+namespace Evoral { template<typename Time> class EventSink; }
+
+namespace ARDOUR {
+
+class BeatsFramesConverter;
+class MidiStateTracker;
+class TempoMap;
+
+/** A tracker and compensator for note edit operations.
+ *
+ * This monitors edit operations sent to a model that affect active notes
+ * during a read, and maintains a queue of synthetic events that should be sent
+ * at the start of the next read to maintain coherent MIDI state.
+ */
+class NoteFixer : public boost::noncopyable
+{
+public:
+ typedef Evoral::Note<Evoral::Beats> Note;
+
+ ~NoteFixer();
+
+ /** Clear all internal state. */
+ void clear();
+
+ /** Handle a region edit during read.
+ *
+ * This must be called before the command is applied to the model. Events
+ * are enqueued to compensate for edits which should be later sent with
+ * emit() at the start of the next read.
+ *
+ * @param cmd Command to compensate for.
+ * @param origin Timeline position of edited source.
+ * @param pos Current read position (last read end).
+ */
+ void prepare(TempoMap& tempo_map,
+ const MidiModel::NoteDiffCommand* cmd,
+ framepos_t origin,
+ framepos_t pos,
+ std::set< boost::weak_ptr<Note> >& active_notes);
+
+ /** Emit any pending edit compensation events.
+ *
+ * @param dst Destination for events.
+ * @param pos Timestamp to be used for every event, should be the start of
+ * the read block immediately following any calls to prepare().
+ * @param tracker Tracker to update with emitted events.
+ */
+ void emit(Evoral::EventSink<framepos_t>& dst,
+ framepos_t pos,
+ MidiStateTracker& tracker);
+
+private:
+ typedef Evoral::Event<framepos_t> Event;
+ typedef std::list<Event*> Events;
+
+ /** Copy a beats event to a frames event with the given time stamp. */
+ Event* copy_event(framepos_t time, const Evoral::Event<Evoral::Beats>& ev);
+
+ /** Return true iff `note` is active at `pos`. */
+ bool note_is_active(const BeatsFramesConverter& converter,
+ boost::shared_ptr<Note> note,
+ framepos_t pos);
+
+ Events _events;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_note_fixer_h__ */
+
+