2 Copyright (C) 2006 Paul Davis
3 Author: David Robillard
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 "pbd/error.h"
29 #include "evoral/EventList.hpp"
31 #include "ardour/configuration.h"
32 #include "ardour/debug.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_playlist.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_ring_buffer.h"
37 #include "ardour/session.h"
38 #include "ardour/types.h"
42 using namespace ARDOUR;
46 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
47 : Playlist (session, node, DataType::MIDI, hidden)
48 , _note_mode(Sustained)
51 const XMLProperty* prop = node.property("type");
52 assert(prop && DataType(prop->value()) == DataType::MIDI);
56 if (set_state (node, Stateful::loading_state_version)) {
57 throw failed_constructor ();
62 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
63 : Playlist (session, name, DataType::MIDI, hidden)
64 , _note_mode(Sustained)
68 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
69 : Playlist (other, name, hidden)
70 , _note_mode(other->_note_mode)
74 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, framepos_t start, framecnt_t dur, string name, bool hidden)
75 : Playlist (other, start, dur, name, hidden)
76 , _note_mode(other->_note_mode)
78 /* this constructor does NOT notify others (session) */
81 MidiPlaylist::~MidiPlaylist ()
85 template<typename Time>
86 struct EventsSortByTime {
87 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
88 return a->time() < b->time();
92 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
94 MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framecnt_t dur, unsigned chan_n)
96 /* this function is never called from a realtime thread, so
97 its OK to block (for short intervals).
100 Glib::RecMutex::Lock rm (region_lock);
101 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
103 framepos_t end = start + dur - 1;
105 _read_data_count = 0;
107 // relevent regions overlapping start <--> end
108 vector< boost::shared_ptr<Region> > regs;
109 typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
110 vector<TrackerInfo> tracker_info;
111 uint32_t note_cnt = 0;
113 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
114 if ((*i)->coverage (start, end) != OverlapNone) {
117 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
118 if (t != _note_trackers.end()) {
120 /* add it the set of trackers we will do note resolution
121 on, and remove it from the list we are keeping
122 around, because we don't need it anymore.
124 if the end of the region (where we want to theoretically resolve notes)
125 is outside the current read range, then just do it at the start
129 framepos_t resolve_at = (*i)->last_frame();
130 if (resolve_at < start || resolve_at >= end) {
134 tracker_info.push_back (TrackerInfo (t->second, resolve_at));
135 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
136 note_cnt += (t->second->on());
137 _note_trackers.erase (t);
142 if (note_cnt == 0 && !tracker_info.empty()) {
143 /* trackers to dispose of, but they have no notes in them */
144 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
145 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
148 tracker_info.clear ();
151 if (regs.size() == 1 && tracker_info.empty()) {
153 /* just a single region - read directly into dst */
155 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
157 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
161 NoteTrackers::iterator t = _note_trackers.find (mr.get());
162 MidiStateTracker* tracker;
163 bool new_tracker = false;
165 if (t == _note_trackers.end()) {
166 tracker = new MidiStateTracker;
168 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
171 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
174 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
175 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
178 pair<Region*,MidiStateTracker*> newpair;
179 newpair.first = mr.get();
180 newpair.second = tracker;
181 _note_trackers.insert (newpair);
182 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
185 _read_data_count += mr->read_data_count();
190 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
191 sorted EventSink, sort and then insert into dst.
194 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
196 Evoral::EventList<framepos_t> evlist;
198 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
199 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
200 (*t).first->resolve_notes (evlist, (*t).second);
205 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
206 for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
207 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
211 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
213 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
214 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
219 NoteTrackers::iterator t = _note_trackers.find (mr.get());
220 MidiStateTracker* tracker;
221 bool new_tracker = false;
224 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()));
226 if (t == _note_trackers.end()) {
227 tracker = new MidiStateTracker;
229 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
232 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
236 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
237 _read_data_count += mr->read_data_count();
240 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
241 for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
242 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
244 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
248 pair<Region*,MidiStateTracker*> newpair;
249 newpair.first = mr.get();
250 newpair.second = tracker;
251 _note_trackers.insert (newpair);
252 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
256 if (!evlist.empty()) {
258 /* sort the event list */
259 EventsSortByTime<framepos_t> time_cmp;
260 evlist.sort (time_cmp);
263 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
264 for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
265 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
269 for (Evoral::EventList<framepos_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
270 Evoral::Event<framepos_t>* ev (*e);
271 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
278 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
283 MidiPlaylist::clear_note_trackers ()
285 Glib::RecMutex::Lock rm (region_lock);
286 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
289 _note_trackers.clear ();
293 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
295 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
296 NoteTrackers::iterator t = _note_trackers.find (region.get());
298 /* GACK! THREAD SAFETY! */
300 if (t != _note_trackers.end()) {
302 _note_trackers.erase (t);
308 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
310 /* MIDI regions have no dependents (crossfades) */
314 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
316 /* No MIDI crossfading (yet?), so nothing to do here */
320 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
322 /* MIDI regions have no dependents (crossfades) */
327 MidiPlaylist::set_state (const XMLNode& node, int version)
332 if (Playlist::set_state (node, version)) {
343 MidiPlaylist::dump () const
345 boost::shared_ptr<Region> r;
347 cerr << "Playlist \"" << _name << "\" " << endl
348 << regions.size() << " regions "
351 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
353 cerr << " " << r->name() << " @ " << r << " ["
354 << r->start() << "+" << r->length()
364 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
366 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
372 bool changed = false;
375 RegionLock rlock (this);
376 RegionList::iterator i;
377 RegionList::iterator tmp;
379 for (i = regions.begin(); i != regions.end(); ) {
384 if ((*i) == region) {
395 /* overload this, it normally means "removed", not destroyed */
396 notify_region_removed (region);
402 set<Evoral::Parameter>
403 MidiPlaylist::contained_automation()
405 /* this function is never called from a realtime thread, so
406 its OK to block (for short intervals).
409 Glib::RecMutex::Lock rm (region_lock);
411 set<Evoral::Parameter> ret;
413 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
414 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
416 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
417 c != mr->model()->controls().end(); ++c) {
418 ret.insert(c->first);
427 MidiPlaylist::region_changed (const PBD::PropertyChange& what_changed, boost::shared_ptr<Region> region)
429 if (in_flush || in_set_state) {
433 PBD::PropertyChange our_interests;
434 our_interests.add (Properties::midi_data);
436 bool parent_wants_notify = Playlist::region_changed (what_changed, region);
438 if (parent_wants_notify || what_changed.contains (our_interests)) {
439 notify_contents_changed ();