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/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)
62 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
63 : Playlist (other, name, hidden)
68 list<Region*>::const_iterator in_o = other.regions.begin();
69 list<Region*>::iterator in_n = regions.begin();
71 while (in_o != other.regions.end()) {
72 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
74 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
75 if ( &(*xfades)->in() == ar) {
76 // We found one! Now copy it!
78 list<Region*>::const_iterator out_o = other.regions.begin();
79 list<Region*>::const_iterator out_n = regions.begin();
81 while (out_o != other.regions.end()) {
83 MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
85 if ( &(*xfades)->out() == ar2) {
86 MidiRegion *in = dynamic_cast<MidiRegion*>( (*in_n) );
87 MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
88 Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
89 add_crossfade(*new_fade);
96 // cerr << "HUH!? second region in the crossfade not found!" << endl;
106 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
107 : Playlist (other, start, dur, name, hidden)
109 /* this constructor does NOT notify others (session) */
112 MidiPlaylist::~MidiPlaylist ()
114 GoingAway (); /* EMIT SIGNAL */
116 /* drop connections to signals */
121 struct RegionSortByLayer {
122 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
123 return a->layer() < b->layer();
127 template<typename Time>
128 struct EventsSortByTime {
129 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
130 return a->time() < b->time();
134 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
136 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
138 /* this function is never called from a realtime thread, so
139 its OK to block (for short intervals).
142 Glib::RecMutex::Lock rm (region_lock);
143 cerr << "++++++" << start << " .. " << start + dur << " channel " << chan_n << " +++++++++++++++++++++++++++++++++++++++++++++++\n";
145 nframes_t end = start + dur - 1;
147 _read_data_count = 0;
149 // relevent regions overlapping start <--> end
150 vector< boost::shared_ptr<Region> > regs;
151 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
152 vector<TrackerInfo> tracker_info;
153 uint32_t note_cnt = 0;
155 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
156 if ((*i)->coverage (start, end) != OverlapNone) {
159 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
160 if (t != _note_trackers.end()) {
162 /* add it the set of trackers we will do note resolution
163 on, and remove it from the list we are keeping
164 around, because we don't need it anymore.
167 tracker_info.push_back (TrackerInfo (t->second, (*i)->last_frame()));
168 cerr << "time to resolve & remove tracker for " << (*i)->name() << endl;
169 note_cnt += (t->second->on());
170 _note_trackers.erase (t);
175 if (note_cnt == 0 && !tracker_info.empty()) {
176 /* trackers to dispose of, but they have no notes in them */
177 cerr << "Clearing " << tracker_info.size() << " empty trackers\n";
178 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
181 tracker_info.clear ();
184 if (regs.size() == 1 && tracker_info.empty()) {
186 /* just a single region - read directly into dst */
188 cerr << "Single region (" << regs.front()->name() << ") read, no out-of-bound region tracking info\n";
190 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
194 NoteTrackers::iterator t = _note_trackers.find (mr.get());
195 MidiStateTracker* tracker;
196 bool new_tracker = false;
198 if (t == _note_trackers.end()) {
199 tracker = new MidiStateTracker;
201 cerr << "\tBEFORE: new tracker\n";
204 cerr << "\tBEFORE: tracker says there are " << tracker->on() << " on notes\n";
207 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
208 cerr << "\tAFTER: tracker says there are " << tracker->on() << " on notes\n";
211 pair<Region*,MidiStateTracker*> newpair;
212 newpair.first = mr.get();
213 newpair.second = tracker;
214 _note_trackers.insert (newpair);
215 cerr << "\tadded tracker to trackers\n";
218 _read_data_count += mr->read_data_count();
223 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
224 sorted EventSink, sort and then insert into dst.
227 cerr << regs.size() << " regions to read, plus " << tracker_info.size() << " trackers\n";
229 Evoral::EventList<nframes_t> evlist;
231 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
232 cerr << "Resolve " << (*t).first->on() << " notes\n";
233 (*t).first->resolve_notes (evlist, (*t).second);
237 cerr << "After resolution we now have " << evlist.size() << " events\n";
238 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
239 cerr << '\t' << **x << endl;
242 RegionSortByLayer layer_cmp;
243 sort(regs.begin(), regs.end(), layer_cmp);
245 cerr << "for " << start << " .. " << start+dur-1 << " We have " << regs.size() << " regions to consider\n";
247 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
248 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
253 NoteTrackers::iterator t = _note_trackers.find (mr.get());
254 MidiStateTracker* tracker;
255 bool new_tracker = false;
257 cerr << "Before " << mr->name() << " (" << mr->position() << " .. " << mr->last_frame() << ") we now have " << evlist.size() << " events\n";
259 if (t == _note_trackers.end()) {
260 tracker = new MidiStateTracker;
262 cerr << "\tBEFORE: tracker says there are " << tracker->on() << " on notes\n";
265 cerr << "\tBEFORE: tracker says there are " << tracker->on() << " on notes\n";
268 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
269 _read_data_count += mr->read_data_count();
271 cerr << "After " << mr->name() << " (" << mr->position() << " .. " << mr->last_frame() << ") we now have " << evlist.size() << " events\n";
272 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
273 cerr << '\t' << **x << endl;
275 cerr << "\tAFTER: tracker says there are " << tracker->on() << " on notes\n";
278 pair<Region*,MidiStateTracker*> newpair;
279 newpair.first = mr.get();
280 newpair.second = tracker;
281 _note_trackers.insert (newpair);
282 cerr << "\tadded tracker to trackers\n";
286 cerr << "Final we now have " << evlist.size() << " events\n";
287 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
288 cerr << '\t' << **x << endl;
291 if (!evlist.empty()) {
293 /* sort the event list */
294 EventsSortByTime<nframes_t> time_cmp;
295 evlist.sort (time_cmp);
297 cerr << "Post sort: we now have " << evlist.size() << " events\n";
298 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
299 cerr << '\t' << **x << endl;
303 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
304 Evoral::Event<nframes_t>* ev (*e);
305 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
311 cerr << "-------------------------------------------------------------\n";
316 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
318 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
319 NoteTrackers::iterator t = _note_trackers.find (region.get());
321 /* GACK! THREAD SAFETY! */
323 if (t != _note_trackers.end()) {
325 _note_trackers.erase (t);
331 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
333 /* MIDI regions have no dependents (crossfades) */
337 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
339 /* No MIDI crossfading (yet?), so nothing to do here */
343 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
345 /* MIDI regions have no dependents (crossfades) */
350 MidiPlaylist::set_state (const XMLNode& node, int version)
355 Playlist::set_state (node, version);
364 MidiPlaylist::dump () const
366 boost::shared_ptr<Region> r;
368 cerr << "Playlist \"" << _name << "\" " << endl
369 << regions.size() << " regions "
372 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
374 cerr << " " << r->name() << " @ " << r << " ["
375 << r->start() << "+" << r->length()
385 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
387 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
388 bool changed = false;
391 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
398 RegionLock rlock (this);
399 RegionList::iterator i;
400 RegionList::iterator tmp;
402 for (i = regions.begin(); i != regions.end(); ) {
407 if ((*i) == region) {
418 /* overload this, it normally means "removed", not destroyed */
419 notify_region_removed (region);
425 set<Evoral::Parameter>
426 MidiPlaylist::contained_automation()
428 /* this function is never called from a realtime thread, so
429 its OK to block (for short intervals).
432 Glib::RecMutex::Lock rm (region_lock);
434 set<Evoral::Parameter> ret;
436 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
437 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
439 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
440 c != mr->model()->controls().end(); ++c) {
441 ret.insert(c->first);
450 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
452 if (in_flush || in_set_state) {
456 // Feeling rather uninterested today, but thanks for the heads up anyway!
458 Change our_interests = Change (/*MidiRegion::FadeInChanged|
459 MidiRegion::FadeOutChanged|
460 MidiRegion::FadeInActiveChanged|
461 MidiRegion::FadeOutActiveChanged|
462 MidiRegion::EnvelopeActiveChanged|
463 MidiRegion::ScaleAmplitudeChanged|
464 MidiRegion::EnvelopeChanged*/);
465 bool parent_wants_notify;
467 parent_wants_notify = Playlist::region_changed (what_changed, region);
469 if ((parent_wants_notify || (what_changed & our_interests))) {