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)
68 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
69 : Playlist (other, start, dur, name, hidden)
71 /* this constructor does NOT notify others (session) */
74 MidiPlaylist::~MidiPlaylist ()
76 GoingAway (); /* EMIT SIGNAL */
78 /* drop connections to signals */
83 struct RegionSortByLayer {
84 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
85 return a->layer() < b->layer();
89 template<typename Time>
90 struct EventsSortByTime {
91 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
92 return a->time() < b->time();
96 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
98 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
100 /* this function is never called from a realtime thread, so
101 its OK to block (for short intervals).
104 Glib::RecMutex::Lock rm (region_lock);
105 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
107 nframes_t end = start + dur - 1;
109 _read_data_count = 0;
111 // relevent regions overlapping start <--> end
112 vector< boost::shared_ptr<Region> > regs;
113 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
114 vector<TrackerInfo> tracker_info;
115 uint32_t note_cnt = 0;
117 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
118 if ((*i)->coverage (start, end) != OverlapNone) {
121 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
122 if (t != _note_trackers.end()) {
124 /* add it the set of trackers we will do note resolution
125 on, and remove it from the list we are keeping
126 around, because we don't need it anymore.
129 tracker_info.push_back (TrackerInfo (t->second, (*i)->last_frame()));
130 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1\n", (*i)->name()));
131 note_cnt += (t->second->on());
132 _note_trackers.erase (t);
137 if (note_cnt == 0 && !tracker_info.empty()) {
138 /* trackers to dispose of, but they have no notes in them */
139 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
140 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
143 tracker_info.clear ();
146 if (regs.size() == 1 && tracker_info.empty()) {
148 /* just a single region - read directly into dst */
150 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
152 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
156 NoteTrackers::iterator t = _note_trackers.find (mr.get());
157 MidiStateTracker* tracker;
158 bool new_tracker = false;
160 if (t == _note_trackers.end()) {
161 tracker = new MidiStateTracker;
163 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
166 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
169 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
170 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
173 pair<Region*,MidiStateTracker*> newpair;
174 newpair.first = mr.get();
175 newpair.second = tracker;
176 _note_trackers.insert (newpair);
177 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
180 _read_data_count += mr->read_data_count();
185 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
186 sorted EventSink, sort and then insert into dst.
189 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
191 Evoral::EventList<nframes_t> evlist;
193 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
194 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
195 (*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_STR_SET(a,**x);
202 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
205 RegionSortByLayer layer_cmp;
206 sort(regs.begin(), regs.end(), layer_cmp);
208 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
210 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
211 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
216 NoteTrackers::iterator t = _note_trackers.find (mr.get());
217 MidiStateTracker* tracker;
218 bool new_tracker = false;
221 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()));
223 if (t == _note_trackers.end()) {
224 tracker = new MidiStateTracker;
226 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
229 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
233 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
234 _read_data_count += mr->read_data_count();
236 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
237 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
238 DEBUG_STR_SET(a,**x);
239 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
241 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
244 pair<Region*,MidiStateTracker*> newpair;
245 newpair.first = mr.get();
246 newpair.second = tracker;
247 _note_trackers.insert (newpair);
248 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
252 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
253 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
254 DEBUG_STR_SET(a,**x);
255 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
258 if (!evlist.empty()) {
260 /* sort the event list */
261 EventsSortByTime<nframes_t> time_cmp;
262 evlist.sort (time_cmp);
265 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
266 Evoral::Event<nframes_t>* ev (*e);
267 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
273 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
278 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
280 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
281 NoteTrackers::iterator t = _note_trackers.find (region.get());
283 /* GACK! THREAD SAFETY! */
285 if (t != _note_trackers.end()) {
287 _note_trackers.erase (t);
293 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
295 /* MIDI regions have no dependents (crossfades) */
299 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
301 /* No MIDI crossfading (yet?), so nothing to do here */
305 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
307 /* MIDI regions have no dependents (crossfades) */
312 MidiPlaylist::set_state (const XMLNode& node, int version)
317 Playlist::set_state (node, version);
326 MidiPlaylist::dump () const
328 boost::shared_ptr<Region> r;
330 cerr << "Playlist \"" << _name << "\" " << endl
331 << regions.size() << " regions "
334 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
336 cerr << " " << r->name() << " @ " << r << " ["
337 << r->start() << "+" << r->length()
347 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
349 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
350 bool changed = false;
353 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
360 RegionLock rlock (this);
361 RegionList::iterator i;
362 RegionList::iterator tmp;
364 for (i = regions.begin(); i != regions.end(); ) {
369 if ((*i) == region) {
380 /* overload this, it normally means "removed", not destroyed */
381 notify_region_removed (region);
387 set<Evoral::Parameter>
388 MidiPlaylist::contained_automation()
390 /* this function is never called from a realtime thread, so
391 its OK to block (for short intervals).
394 Glib::RecMutex::Lock rm (region_lock);
396 set<Evoral::Parameter> ret;
398 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
399 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
401 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
402 c != mr->model()->controls().end(); ++c) {
403 ret.insert(c->first);
412 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
414 if (in_flush || in_set_state) {
418 // Feeling rather uninterested today, but thanks for the heads up anyway!
420 Change our_interests = Change (/*MidiRegion::FadeInChanged|
421 MidiRegion::FadeOutChanged|
422 MidiRegion::FadeInActiveChanged|
423 MidiRegion::FadeOutActiveChanged|
424 MidiRegion::EnvelopeActiveChanged|
425 MidiRegion::ScaleAmplitudeChanged|
426 MidiRegion::EnvelopeChanged*/);
427 bool parent_wants_notify;
429 parent_wants_notify = Playlist::region_changed (what_changed, region);
431 if ((parent_wants_notify || (what_changed & our_interests))) {