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;
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);
52 set_state (node, Stateful::loading_state_version);
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 /* drop connections to signals */
79 template<typename Time>
80 struct EventsSortByTime {
81 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
82 return a->time() < b->time();
86 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
88 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
90 /* this function is never called from a realtime thread, so
91 its OK to block (for short intervals).
94 Glib::RecMutex::Lock rm (region_lock);
95 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
97 nframes_t end = start + dur - 1;
101 // relevent regions overlapping start <--> end
102 vector< boost::shared_ptr<Region> > regs;
103 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
104 vector<TrackerInfo> tracker_info;
105 uint32_t note_cnt = 0;
107 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
108 if ((*i)->coverage (start, end) != OverlapNone) {
111 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
112 if (t != _note_trackers.end()) {
114 /* add it the set of trackers we will do note resolution
115 on, and remove it from the list we are keeping
116 around, because we don't need it anymore.
118 if the end of the region (where we want to theoretically resolve notes)
119 is outside the current read range, then just do it at the start
123 nframes64_t resolve_at = (*i)->last_frame();
124 if (resolve_at >= end) {
128 tracker_info.push_back (TrackerInfo (t->second, resolve_at));
129 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
130 note_cnt += (t->second->on());
131 _note_trackers.erase (t);
136 if (note_cnt == 0 && !tracker_info.empty()) {
137 /* trackers to dispose of, but they have no notes in them */
138 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
139 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
142 tracker_info.clear ();
145 if (regs.size() == 1 && tracker_info.empty()) {
147 /* just a single region - read directly into dst */
149 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
151 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
155 NoteTrackers::iterator t = _note_trackers.find (mr.get());
156 MidiStateTracker* tracker;
157 bool new_tracker = false;
159 if (t == _note_trackers.end()) {
160 tracker = new MidiStateTracker;
162 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
165 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
168 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
169 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
172 pair<Region*,MidiStateTracker*> newpair;
173 newpair.first = mr.get();
174 newpair.second = tracker;
175 _note_trackers.insert (newpair);
176 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
179 _read_data_count += mr->read_data_count();
184 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
185 sorted EventSink, sort and then insert into dst.
188 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
190 Evoral::EventList<nframes_t> evlist;
192 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
193 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
194 (*t).first->resolve_notes (evlist, (*t).second);
199 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
200 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
201 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
205 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
207 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
208 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
213 NoteTrackers::iterator t = _note_trackers.find (mr.get());
214 MidiStateTracker* tracker;
215 bool new_tracker = false;
218 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()));
220 if (t == _note_trackers.end()) {
221 tracker = new MidiStateTracker;
223 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
226 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
230 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
231 _read_data_count += mr->read_data_count();
234 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
235 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
236 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
238 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
242 pair<Region*,MidiStateTracker*> newpair;
243 newpair.first = mr.get();
244 newpair.second = tracker;
245 _note_trackers.insert (newpair);
246 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
250 if (!evlist.empty()) {
252 /* sort the event list */
253 EventsSortByTime<nframes_t> time_cmp;
254 evlist.sort (time_cmp);
257 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
258 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
259 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
263 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
264 Evoral::Event<nframes_t>* ev (*e);
265 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
272 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
277 MidiPlaylist::clear_note_trackers ()
279 Glib::RecMutex::Lock rm (region_lock);
280 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
283 _note_trackers.clear ();
287 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
289 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
290 NoteTrackers::iterator t = _note_trackers.find (region.get());
292 /* GACK! THREAD SAFETY! */
294 if (t != _note_trackers.end()) {
296 _note_trackers.erase (t);
302 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
304 /* MIDI regions have no dependents (crossfades) */
308 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
310 /* No MIDI crossfading (yet?), so nothing to do here */
314 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
316 /* MIDI regions have no dependents (crossfades) */
321 MidiPlaylist::set_state (const XMLNode& node, int version)
326 Playlist::set_state (node, version);
335 MidiPlaylist::dump () const
337 boost::shared_ptr<Region> r;
339 cerr << "Playlist \"" << _name << "\" " << endl
340 << regions.size() << " regions "
343 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
345 cerr << " " << r->name() << " @ " << r << " ["
346 << r->start() << "+" << r->length()
356 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
358 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
359 bool changed = false;
362 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
369 RegionLock rlock (this);
370 RegionList::iterator i;
371 RegionList::iterator tmp;
373 for (i = regions.begin(); i != regions.end(); ) {
378 if ((*i) == region) {
389 /* overload this, it normally means "removed", not destroyed */
390 notify_region_removed (region);
396 set<Evoral::Parameter>
397 MidiPlaylist::contained_automation()
399 /* this function is never called from a realtime thread, so
400 its OK to block (for short intervals).
403 Glib::RecMutex::Lock rm (region_lock);
405 set<Evoral::Parameter> ret;
407 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
408 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
410 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
411 c != mr->model()->controls().end(); ++c) {
412 ret.insert(c->first);
421 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
423 if (in_flush || in_set_state) {
427 // Feeling rather uninterested today, but thanks for the heads up anyway!
429 Change our_interests = Change (/*MidiRegion::FadeInChanged|
430 MidiRegion::FadeOutChanged|
431 MidiRegion::FadeInActiveChanged|
432 MidiRegion::FadeOutActiveChanged|
433 MidiRegion::EnvelopeActiveChanged|
434 MidiRegion::ScaleAmplitudeChanged|
435 MidiRegion::EnvelopeChanged*/);
436 bool parent_wants_notify;
438 parent_wants_notify = Playlist::region_changed (what_changed, region);
440 if ((parent_wants_notify || (what_changed & our_interests))) {