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.
26 #include <sigc++/bind.h>
28 #include "evoral/EventList.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/types.h"
32 #include "ardour/configuration.h"
33 #include "ardour/midi_playlist.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/session.h"
36 #include "ardour/midi_ring_buffer.h"
38 #include "pbd/error.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)
50 const XMLProperty* prop = node.property("type");
51 assert(prop && DataType(prop->value()) == DataType::MIDI);
54 set_state (node, Stateful::loading_state_version);
58 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
59 : Playlist (session, name, DataType::MIDI, hidden)
63 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
64 : Playlist (other, name, hidden)
69 list<Region*>::const_iterator in_o = other.regions.begin();
70 list<Region*>::iterator in_n = regions.begin();
72 while (in_o != other.regions.end()) {
73 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
75 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
76 if ( &(*xfades)->in() == ar) {
77 // We found one! Now copy it!
79 list<Region*>::const_iterator out_o = other.regions.begin();
80 list<Region*>::const_iterator out_n = regions.begin();
82 while (out_o != other.regions.end()) {
84 MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
86 if ( &(*xfades)->out() == ar2) {
87 MidiRegion *in = dynamic_cast<MidiRegion*>( (*in_n) );
88 MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
89 Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
90 add_crossfade(*new_fade);
97 // cerr << "HUH!? second region in the crossfade not found!" << endl;
107 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
108 : Playlist (other, start, dur, name, hidden)
110 /* this constructor does NOT notify others (session) */
113 MidiPlaylist::~MidiPlaylist ()
115 GoingAway (); /* EMIT SIGNAL */
117 /* drop connections to signals */
122 struct RegionSortByLayer {
123 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
124 return a->layer() < b->layer();
128 template<typename Time>
129 struct EventsSortByTime {
130 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
131 return a->time() < b->time();
135 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
137 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
139 /* this function is never called from a realtime thread, so
140 its OK to block (for short intervals).
143 Glib::RecMutex::Lock rm (region_lock);
144 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
146 nframes_t end = start + dur - 1;
148 _read_data_count = 0;
150 // relevent regions overlapping start <--> end
151 vector< boost::shared_ptr<Region> > regs;
152 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
153 vector<TrackerInfo> tracker_info;
154 uint32_t note_cnt = 0;
156 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
157 if ((*i)->coverage (start, end) != OverlapNone) {
160 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
161 if (t != _note_trackers.end()) {
163 /* add it the set of trackers we will do note resolution
164 on, and remove it from the list we are keeping
165 around, because we don't need it anymore.
168 tracker_info.push_back (TrackerInfo (t->second, (*i)->last_frame()));
169 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1\n", (*i)->name()));
170 note_cnt += (t->second->on());
171 _note_trackers.erase (t);
176 if (note_cnt == 0 && !tracker_info.empty()) {
177 /* trackers to dispose of, but they have no notes in them */
178 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
179 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
182 tracker_info.clear ();
185 if (regs.size() == 1 && tracker_info.empty()) {
187 /* just a single region - read directly into dst */
189 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
191 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
195 NoteTrackers::iterator t = _note_trackers.find (mr.get());
196 MidiStateTracker* tracker;
197 bool new_tracker = false;
199 if (t == _note_trackers.end()) {
200 tracker = new MidiStateTracker;
202 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
205 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
208 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
209 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
212 pair<Region*,MidiStateTracker*> newpair;
213 newpair.first = mr.get();
214 newpair.second = tracker;
215 _note_trackers.insert (newpair);
216 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
219 _read_data_count += mr->read_data_count();
224 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
225 sorted EventSink, sort and then insert into dst.
228 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
230 Evoral::EventList<nframes_t> evlist;
232 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
233 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
234 (*t).first->resolve_notes (evlist, (*t).second);
238 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
239 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
240 DEBUG_STR_SET(a,**x);
241 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
244 RegionSortByLayer layer_cmp;
245 sort(regs.begin(), regs.end(), layer_cmp);
247 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
249 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
250 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
255 NoteTrackers::iterator t = _note_trackers.find (mr.get());
256 MidiStateTracker* tracker;
257 bool new_tracker = false;
260 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()));
262 if (t == _note_trackers.end()) {
263 tracker = new MidiStateTracker;
265 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
268 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
272 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
273 _read_data_count += mr->read_data_count();
275 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
276 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
277 DEBUG_STR_SET(a,**x);
278 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
280 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
283 pair<Region*,MidiStateTracker*> newpair;
284 newpair.first = mr.get();
285 newpair.second = tracker;
286 _note_trackers.insert (newpair);
287 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
291 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
292 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
293 DEBUG_STR_SET(a,**x);
294 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
297 if (!evlist.empty()) {
299 /* sort the event list */
300 EventsSortByTime<nframes_t> time_cmp;
301 evlist.sort (time_cmp);
304 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
305 Evoral::Event<nframes_t>* ev (*e);
306 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
312 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
317 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
319 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
320 NoteTrackers::iterator t = _note_trackers.find (region.get());
322 /* GACK! THREAD SAFETY! */
324 if (t != _note_trackers.end()) {
326 _note_trackers.erase (t);
332 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
334 /* MIDI regions have no dependents (crossfades) */
338 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
340 /* No MIDI crossfading (yet?), so nothing to do here */
344 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
346 /* MIDI regions have no dependents (crossfades) */
351 MidiPlaylist::set_state (const XMLNode& node, int version)
356 Playlist::set_state (node, version);
365 MidiPlaylist::dump () const
367 boost::shared_ptr<Region> r;
369 cerr << "Playlist \"" << _name << "\" " << endl
370 << regions.size() << " regions "
373 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
375 cerr << " " << r->name() << " @ " << r << " ["
376 << r->start() << "+" << r->length()
386 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
388 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
389 bool changed = false;
392 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
399 RegionLock rlock (this);
400 RegionList::iterator i;
401 RegionList::iterator tmp;
403 for (i = regions.begin(); i != regions.end(); ) {
408 if ((*i) == region) {
419 /* overload this, it normally means "removed", not destroyed */
420 notify_region_removed (region);
426 set<Evoral::Parameter>
427 MidiPlaylist::contained_automation()
429 /* this function is never called from a realtime thread, so
430 its OK to block (for short intervals).
433 Glib::RecMutex::Lock rm (region_lock);
435 set<Evoral::Parameter> ret;
437 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
438 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
440 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
441 c != mr->model()->controls().end(); ++c) {
442 ret.insert(c->first);
451 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
453 if (in_flush || in_set_state) {
457 // Feeling rather uninterested today, but thanks for the heads up anyway!
459 Change our_interests = Change (/*MidiRegion::FadeInChanged|
460 MidiRegion::FadeOutChanged|
461 MidiRegion::FadeInActiveChanged|
462 MidiRegion::FadeOutActiveChanged|
463 MidiRegion::EnvelopeActiveChanged|
464 MidiRegion::ScaleAmplitudeChanged|
465 MidiRegion::EnvelopeChanged*/);
466 bool parent_wants_notify;
468 parent_wants_notify = Playlist::region_changed (what_changed, region);
470 if ((parent_wants_notify || (what_changed & our_interests))) {