2 Copyright (C) 2006 Paul Davis
3 Written by Dave Robillard, 2006
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "evoral/EventList.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/types.h"
31 #include "ardour/configuration.h"
32 #include "ardour/midi_playlist.h"
33 #include "ardour/midi_region.h"
34 #include "ardour/session.h"
35 #include "ardour/midi_ring_buffer.h"
37 #include "pbd/error.h"
41 using namespace ARDOUR;
45 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
46 : Playlist (session, node, DataType::MIDI, hidden)
47 , _note_mode(Sustained)
49 const XMLProperty* prop = node.property("type");
50 assert(prop && DataType(prop->value()) == DataType::MIDI);
53 set_state (node, Stateful::loading_state_version);
57 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
58 : Playlist (session, name, DataType::MIDI, hidden)
59 , _note_mode(Sustained)
63 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
64 : Playlist (other, name, hidden)
65 , _note_mode(other->_note_mode)
69 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
70 : Playlist (other, start, dur, name, hidden)
71 , _note_mode(other->_note_mode)
73 /* this constructor does NOT notify others (session) */
76 MidiPlaylist::~MidiPlaylist ()
80 template<typename Time>
81 struct EventsSortByTime {
82 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
83 return a->time() < b->time();
87 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
89 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
91 /* this function is never called from a realtime thread, so
92 its OK to block (for short intervals).
95 Glib::RecMutex::Lock rm (region_lock);
96 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
98 nframes_t end = start + dur - 1;
100 _read_data_count = 0;
102 // relevent regions overlapping start <--> end
103 vector< boost::shared_ptr<Region> > regs;
104 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
105 vector<TrackerInfo> tracker_info;
106 uint32_t note_cnt = 0;
108 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
109 if ((*i)->coverage (start, end) != OverlapNone) {
112 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
113 if (t != _note_trackers.end()) {
115 /* add it the set of trackers we will do note resolution
116 on, and remove it from the list we are keeping
117 around, because we don't need it anymore.
119 if the end of the region (where we want to theoretically resolve notes)
120 is outside the current read range, then just do it at the start
124 nframes64_t resolve_at = (*i)->last_frame();
125 if (resolve_at >= end) {
129 tracker_info.push_back (TrackerInfo (t->second, resolve_at));
130 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
131 note_cnt += (t->second->on());
132 _note_trackers.erase (t);
137 if (note_cnt == 0 && !tracker_info.empty()) {
138 /* trackers to dispose of, but they have no notes in them */
139 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
140 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
143 tracker_info.clear ();
146 if (regs.size() == 1 && tracker_info.empty()) {
148 /* just a single region - read directly into dst */
150 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
152 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
156 NoteTrackers::iterator t = _note_trackers.find (mr.get());
157 MidiStateTracker* tracker;
158 bool new_tracker = false;
160 if (t == _note_trackers.end()) {
161 tracker = new MidiStateTracker;
163 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
166 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
169 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
170 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
173 pair<Region*,MidiStateTracker*> newpair;
174 newpair.first = mr.get();
175 newpair.second = tracker;
176 _note_trackers.insert (newpair);
177 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
180 _read_data_count += mr->read_data_count();
185 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
186 sorted EventSink, sort and then insert into dst.
189 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
191 Evoral::EventList<nframes_t> evlist;
193 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
194 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
195 (*t).first->resolve_notes (evlist, (*t).second);
200 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
201 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
202 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
206 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
208 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
209 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
214 NoteTrackers::iterator t = _note_trackers.find (mr.get());
215 MidiStateTracker* tracker;
216 bool new_tracker = false;
219 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()));
221 if (t == _note_trackers.end()) {
222 tracker = new MidiStateTracker;
224 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
227 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
231 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
232 _read_data_count += mr->read_data_count();
235 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
236 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
237 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
239 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
243 pair<Region*,MidiStateTracker*> newpair;
244 newpair.first = mr.get();
245 newpair.second = tracker;
246 _note_trackers.insert (newpair);
247 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
251 if (!evlist.empty()) {
253 /* sort the event list */
254 EventsSortByTime<nframes_t> time_cmp;
255 evlist.sort (time_cmp);
258 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
259 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
260 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
264 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
265 Evoral::Event<nframes_t>* ev (*e);
266 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
273 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
278 MidiPlaylist::clear_note_trackers ()
280 Glib::RecMutex::Lock rm (region_lock);
281 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
284 _note_trackers.clear ();
288 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
290 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
291 NoteTrackers::iterator t = _note_trackers.find (region.get());
293 /* GACK! THREAD SAFETY! */
295 if (t != _note_trackers.end()) {
297 _note_trackers.erase (t);
303 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
305 /* MIDI regions have no dependents (crossfades) */
309 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
311 /* No MIDI crossfading (yet?), so nothing to do here */
315 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
317 /* MIDI regions have no dependents (crossfades) */
322 MidiPlaylist::set_state (const XMLNode& node, int version)
327 Playlist::set_state (node, version);
336 MidiPlaylist::dump () const
338 boost::shared_ptr<Region> r;
340 cerr << "Playlist \"" << _name << "\" " << endl
341 << regions.size() << " regions "
344 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
346 cerr << " " << r->name() << " @ " << r << " ["
347 << r->start() << "+" << r->length()
357 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
359 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
360 bool changed = false;
363 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
370 RegionLock rlock (this);
371 RegionList::iterator i;
372 RegionList::iterator tmp;
374 for (i = regions.begin(); i != regions.end(); ) {
379 if ((*i) == region) {
390 /* overload this, it normally means "removed", not destroyed */
391 notify_region_removed (region);
397 set<Evoral::Parameter>
398 MidiPlaylist::contained_automation()
400 /* this function is never called from a realtime thread, so
401 its OK to block (for short intervals).
404 Glib::RecMutex::Lock rm (region_lock);
406 set<Evoral::Parameter> ret;
408 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
409 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
411 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
412 c != mr->model()->controls().end(); ++c) {
413 ret.insert(c->first);
422 MidiPlaylist::region_changed (const PBD::PropertyChange& what_changed, boost::shared_ptr<Region> region)
424 if (in_flush || in_set_state) {
428 // Feeling rather uninterested today, but thanks for the heads up anyway!
430 PBD::PropertyChange our_interests;
432 bool parent_wants_notify;
434 parent_wants_notify = Playlist::region_changed (what_changed, region);
436 if (parent_wants_notify || what_changed.contains (our_interests)) {
437 notify_contents_changed ();