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 template<typename Time>
84 struct EventsSortByTime {
85 bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
86 return a->time() < b->time();
90 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
92 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
94 /* this function is never called from a realtime thread, so
95 its OK to block (for short intervals).
98 Glib::RecMutex::Lock rm (region_lock);
99 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
101 nframes_t end = start + dur - 1;
103 _read_data_count = 0;
105 // relevent regions overlapping start <--> end
106 vector< boost::shared_ptr<Region> > regs;
107 typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
108 vector<TrackerInfo> tracker_info;
109 uint32_t note_cnt = 0;
111 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
112 if ((*i)->coverage (start, end) != OverlapNone) {
115 NoteTrackers::iterator t = _note_trackers.find ((*i).get());
116 if (t != _note_trackers.end()) {
118 /* add it the set of trackers we will do note resolution
119 on, and remove it from the list we are keeping
120 around, because we don't need it anymore.
122 if the end of the region (where we want to theoretically resolve notes)
123 is outside the current read range, then just do it at the start
127 nframes64_t resolve_at = (*i)->last_frame();
128 if (resolve_at >= end) {
132 tracker_info.push_back (TrackerInfo (t->second, resolve_at));
133 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1 @ %2\n", (*i)->name(), resolve_at));
134 note_cnt += (t->second->on());
135 _note_trackers.erase (t);
140 if (note_cnt == 0 && !tracker_info.empty()) {
141 /* trackers to dispose of, but they have no notes in them */
142 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
143 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
146 tracker_info.clear ();
149 if (regs.size() == 1 && tracker_info.empty()) {
151 /* just a single region - read directly into dst */
153 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
155 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
159 NoteTrackers::iterator t = _note_trackers.find (mr.get());
160 MidiStateTracker* tracker;
161 bool new_tracker = false;
163 if (t == _note_trackers.end()) {
164 tracker = new MidiStateTracker;
166 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
169 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
172 mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
173 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
176 pair<Region*,MidiStateTracker*> newpair;
177 newpair.first = mr.get();
178 newpair.second = tracker;
179 _note_trackers.insert (newpair);
180 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
183 _read_data_count += mr->read_data_count();
188 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
189 sorted EventSink, sort and then insert into dst.
192 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
194 Evoral::EventList<nframes_t> evlist;
196 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
197 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
198 (*t).first->resolve_notes (evlist, (*t).second);
203 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n", evlist.size()));
204 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
205 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
209 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
211 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
212 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
217 NoteTrackers::iterator t = _note_trackers.find (mr.get());
218 MidiStateTracker* tracker;
219 bool new_tracker = false;
222 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()));
224 if (t == _note_trackers.end()) {
225 tracker = new MidiStateTracker;
227 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
230 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
234 mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
235 _read_data_count += mr->read_data_count();
238 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
239 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
240 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
242 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
246 pair<Region*,MidiStateTracker*> newpair;
247 newpair.first = mr.get();
248 newpair.second = tracker;
249 _note_trackers.insert (newpair);
250 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
254 if (!evlist.empty()) {
256 /* sort the event list */
257 EventsSortByTime<nframes_t> time_cmp;
258 evlist.sort (time_cmp);
261 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
262 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
263 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
267 for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
268 Evoral::Event<nframes_t>* ev (*e);
269 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
276 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
281 MidiPlaylist::clear_note_trackers ()
283 Glib::RecMutex::Lock rm (region_lock);
284 for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
287 _note_trackers.clear ();
291 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
293 /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
294 NoteTrackers::iterator t = _note_trackers.find (region.get());
296 /* GACK! THREAD SAFETY! */
298 if (t != _note_trackers.end()) {
300 _note_trackers.erase (t);
306 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
308 /* MIDI regions have no dependents (crossfades) */
312 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
314 /* No MIDI crossfading (yet?), so nothing to do here */
318 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
320 /* MIDI regions have no dependents (crossfades) */
325 MidiPlaylist::set_state (const XMLNode& node, int version)
330 Playlist::set_state (node, version);
339 MidiPlaylist::dump () const
341 boost::shared_ptr<Region> r;
343 cerr << "Playlist \"" << _name << "\" " << endl
344 << regions.size() << " regions "
347 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
349 cerr << " " << r->name() << " @ " << r << " ["
350 << r->start() << "+" << r->length()
360 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
362 boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
363 bool changed = false;
366 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
373 RegionLock rlock (this);
374 RegionList::iterator i;
375 RegionList::iterator tmp;
377 for (i = regions.begin(); i != regions.end(); ) {
382 if ((*i) == region) {
393 /* overload this, it normally means "removed", not destroyed */
394 notify_region_removed (region);
400 set<Evoral::Parameter>
401 MidiPlaylist::contained_automation()
403 /* this function is never called from a realtime thread, so
404 its OK to block (for short intervals).
407 Glib::RecMutex::Lock rm (region_lock);
409 set<Evoral::Parameter> ret;
411 for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
412 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
414 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
415 c != mr->model()->controls().end(); ++c) {
416 ret.insert(c->first);
425 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
427 if (in_flush || in_set_state) {
431 // Feeling rather uninterested today, but thanks for the heads up anyway!
433 Change our_interests = Change (/*MidiRegion::FadeInChanged|
434 MidiRegion::FadeOutChanged|
435 MidiRegion::FadeInActiveChanged|
436 MidiRegion::FadeOutActiveChanged|
437 MidiRegion::EnvelopeActiveChanged|
438 MidiRegion::ScaleAmplitudeChanged|
439 MidiRegion::EnvelopeChanged*/);
440 bool parent_wants_notify;
442 parent_wants_notify = Playlist::region_changed (what_changed, region);
444 if ((parent_wants_notify || (what_changed & our_interests))) {