switch MIDI Clock slave code to use DEBUG_TRACE; don't make it require start/stop...
[ardour2.git] / libs / ardour / midi_playlist.cc
blob1721647c7cff69489eb0db09c9472b9486f2efe2
1 /*
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.
20 #include <cassert>
22 #include <algorithm>
23 #include <iostream>
25 #include <stdlib.h>
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"
39 #include "i18n.h"
41 using namespace ARDOUR;
42 using namespace std;
44 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
45 : Playlist (session, node, DataType::MIDI, hidden)
46 , _note_mode(Sustained)
48 const XMLProperty* prop = node.property("type");
49 assert(prop && DataType(prop->value()) == DataType::MIDI);
51 in_set_state++;
52 set_state (node, Stateful::loading_state_version);
53 in_set_state--;
56 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
57 : Playlist (session, name, DataType::MIDI, hidden)
61 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
62 : Playlist (other, name, hidden)
66 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
67 : Playlist (other, start, dur, name, hidden)
69 /* this constructor does NOT notify others (session) */
72 MidiPlaylist::~MidiPlaylist ()
76 template<typename Time>
77 struct EventsSortByTime {
78 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
79 return a->time() < b->time();
83 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
84 nframes_t
85 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
87 /* this function is never called from a realtime thread, so
88 its OK to block (for short intervals).
91 Glib::RecMutex::Lock rm (region_lock);
92 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
94 nframes_t end = start + dur - 1;
96 _read_data_count = 0;
98 // relevent regions overlapping start <--> end
99 vector< boost::shared_ptr<Region> > regs;
100 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
101 vector<TrackerInfo> tracker_info;
102 uint32_t note_cnt = 0;
104 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
105 if ((*i)->coverage (start, end) != OverlapNone) {
106 regs.push_back(*i);
107 } else {
108 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
109 if (t != _note_trackers.end()) {
111 /* add it the set of trackers we will do note resolution
112 on, and remove it from the list we are keeping
113 around, because we don't need it anymore.
115 if the end of the region (where we want to theoretically resolve notes)
116 is outside the current read range, then just do it at the start
117 of this read range.
120 nframes64_t resolve_at = (*i)->last_frame();
121 if (resolve_at >= end) {
122 resolve_at = start;
125 tracker_info.push_back (TrackerInfo (t->second, resolve_at));
126 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
127 note_cnt += (t->second->on());
128 _note_trackers.erase (t);
133 if (note_cnt == 0 && !tracker_info.empty()) {
134 /* trackers to dispose of, but they have no notes in them */
135 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
136 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
137 delete (*t).first;
139 tracker_info.clear ();
142 if (regs.size() == 1 && tracker_info.empty()) {
144 /* just a single region - read directly into dst */
146 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
148 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
150 if (mr) {
152 NoteTrackers::iterator t = _note_trackers.find (mr.get());
153 MidiStateTracker* tracker;
154 bool new_tracker = false;
156 if (t == _note_trackers.end()) {
157 tracker = new MidiStateTracker;
158 new_tracker = true;
159 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
160 } else {
161 tracker = t->second;
162 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
165 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
166 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
168 if (new_tracker) {
169 pair<Region*,MidiStateTracker*> newpair;
170 newpair.first = mr.get();
171 newpair.second = tracker;
172 _note_trackers.insert (newpair);
173 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
176 _read_data_count += mr->read_data_count();
179 } else {
181 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
182 sorted EventSink, sort and then insert into dst.
185 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
187 Evoral::EventList<nframes_t> evlist;
189 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
190 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
191 (*t).first->resolve_notes (evlist, (*t).second);
192 delete (*t).first;
195 #ifndef NDEBUG
196 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
197 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
198 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
200 #endif
202 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
204 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
205 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
206 if (!mr) {
207 continue;
210 NoteTrackers::iterator t = _note_trackers.find (mr.get());
211 MidiStateTracker* tracker;
212 bool new_tracker = false;
215 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()));
217 if (t == _note_trackers.end()) {
218 tracker = new MidiStateTracker;
219 new_tracker = true;
220 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
221 } else {
222 tracker = t->second;
223 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
227 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
228 _read_data_count += mr->read_data_count();
230 #ifndef NDEBUG
231 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
232 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
233 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
235 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
236 #endif
238 if (new_tracker) {
239 pair<Region*,MidiStateTracker*> newpair;
240 newpair.first = mr.get();
241 newpair.second = tracker;
242 _note_trackers.insert (newpair);
243 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
247 if (!evlist.empty()) {
249 /* sort the event list */
250 EventsSortByTime<nframes_t> time_cmp;
251 evlist.sort (time_cmp);
253 #ifndef NDEBUG
254 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
255 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
256 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
258 #endif
259 /* write into dst */
260 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
261 Evoral::Event<nframes_t>* ev (*e);
262 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
263 delete ev;
269 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
270 return dur;
273 void
274 MidiPlaylist::clear_note_trackers ()
276 Glib::RecMutex::Lock rm (region_lock);
277 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
278 delete n->second;
280 _note_trackers.clear ();
283 void
284 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
286 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
287 NoteTrackers::iterator t = _note_trackers.find (region.get());
289 /* GACK! THREAD SAFETY! */
291 if (t != _note_trackers.end()) {
292 delete t->second;
293 _note_trackers.erase (t);
298 void
299 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
301 /* MIDI regions have no dependents (crossfades) */
304 void
305 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
307 /* No MIDI crossfading (yet?), so nothing to do here */
310 void
311 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
313 /* MIDI regions have no dependents (crossfades) */
318 MidiPlaylist::set_state (const XMLNode& node, int version)
320 in_set_state++;
321 freeze ();
323 Playlist::set_state (node, version);
325 thaw();
326 in_set_state--;
328 return 0;
331 void
332 MidiPlaylist::dump () const
334 boost::shared_ptr<Region> r;
336 cerr << "Playlist \"" << _name << "\" " << endl
337 << regions.size() << " regions "
338 << endl;
340 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
341 r = *i;
342 cerr << " " << r->name() << " @ " << r << " ["
343 << r->start() << "+" << r->length()
344 << "] at "
345 << r->position()
346 << " on layer "
347 << r->layer ()
348 << endl;
352 bool
353 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
355 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
356 bool changed = false;
358 if (r == 0) {
359 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
360 << endmsg;
361 /*NOTREACHED*/
362 return false;
366 RegionLock rlock (this);
367 RegionList::iterator i;
368 RegionList::iterator tmp;
370 for (i = regions.begin(); i != regions.end(); ) {
372 tmp = i;
373 ++tmp;
375 if ((*i) == region) {
376 regions.erase (i);
377 changed = true;
380 i = tmp;
385 if (changed) {
386 /* overload this, it normally means "removed", not destroyed */
387 notify_region_removed (region);
390 return changed;
393 set<Evoral::Parameter>
394 MidiPlaylist::contained_automation()
396 /* this function is never called from a realtime thread, so
397 its OK to block (for short intervals).
400 Glib::RecMutex::Lock rm (region_lock);
402 set<Evoral::Parameter> ret;
404 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
405 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
407 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
408 c != mr->model()->controls().end(); ++c) {
409 ret.insert(c->first);
413 return ret;
417 bool
418 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
420 if (in_flush || in_set_state) {
421 return false;
424 // Feeling rather uninterested today, but thanks for the heads up anyway!
426 Change our_interests = Change (/*MidiRegion::FadeInChanged|
427 MidiRegion::FadeOutChanged|
428 MidiRegion::FadeInActiveChanged|
429 MidiRegion::FadeOutActiveChanged|
430 MidiRegion::EnvelopeActiveChanged|
431 MidiRegion::ScaleAmplitudeChanged|
432 MidiRegion::EnvelopeChanged*/);
433 bool parent_wants_notify;
435 parent_wants_notify = Playlist::region_changed (what_changed, region);
437 if ((parent_wants_notify || (what_changed & our_interests))) {
438 notify_contents_changed ();
441 return true;