Fix needlessley explodey MidiPlaylist constructor.
[ardour.git] / libs / ardour / midi_playlist.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3     Written by Dave Robillard, 2006
4
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.
9
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.
14
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.
18 */
19
20 #include <cassert>
21
22 #include <algorithm>
23 #include <iostream>
24
25 #include <stdlib.h>
26 #include <sigc++/bind.h>
27
28 #include "evoral/EventList.hpp"
29
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"
37
38 #include "pbd/error.h"
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43 using namespace sigc;
44 using namespace std;
45
46 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
47         : Playlist (session, node, DataType::MIDI, hidden)
48         , _note_mode(Sustained)
49 {
50         const XMLProperty* prop = node.property("type");
51         assert(prop && DataType(prop->value()) == DataType::MIDI);
52
53         in_set_state++;
54         set_state (node, Stateful::loading_state_version);
55         in_set_state--;
56 }
57
58 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
59         : Playlist (session, name, DataType::MIDI, hidden)
60 {
61 }
62
63 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
64         : Playlist (other, name, hidden)
65 {
66 }
67
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)
70 {
71         /* this constructor does NOT notify others (session) */
72 }
73
74 MidiPlaylist::~MidiPlaylist ()
75 {
76         GoingAway (); /* EMIT SIGNAL */
77
78         /* drop connections to signals */
79
80         notify_callbacks ();
81 }
82
83 struct RegionSortByLayer {
84     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
85             return a->layer() < b->layer();
86     }
87 };
88
89 template<typename Time>
90 struct EventsSortByTime {
91     bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
92             return a->time() < b->time();
93     }
94 };
95
96 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
97 nframes_t
98 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
99 {
100         /* this function is never called from a realtime thread, so
101            its OK to block (for short intervals).
102         */
103
104         Glib::RecMutex::Lock rm (region_lock);
105         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2  +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
106
107         nframes_t end = start + dur - 1;
108
109         _read_data_count = 0;
110
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;
116
117         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
118                 if ((*i)->coverage (start, end) != OverlapNone) {
119                         regs.push_back(*i);
120                 } else {
121                         NoteTrackers::iterator t = _note_trackers.find ((*i).get());
122                         if (t != _note_trackers.end()) {
123
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.
127                                 */
128                                 
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);
133                         }
134                 }
135         }
136
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) {
141                         delete (*t).first;
142                 }
143                 tracker_info.clear ();
144         }
145                         
146         if (regs.size() == 1 && tracker_info.empty()) {
147         
148                 /* just a single region - read directly into dst */
149
150                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
151                 
152                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
153
154                 if (mr) {
155
156                         NoteTrackers::iterator t = _note_trackers.find (mr.get());
157                         MidiStateTracker* tracker;
158                         bool new_tracker = false;
159
160                         if (t == _note_trackers.end()) {
161                                 tracker = new MidiStateTracker;
162                                 new_tracker = true;
163                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
164                         } else {
165                                 tracker = t->second;
166                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
167                         }
168                         
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()));
171                         
172                         if (new_tracker) {
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");
178                         }
179
180                         _read_data_count += mr->read_data_count();
181                 }
182                 
183         } else {
184
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.
187                 */
188
189                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
190                 
191                 Evoral::EventList<nframes_t> evlist;
192
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);
196                         delete (*t).first;
197                 }
198                 
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)));
203                 }                       
204                 
205                 RegionSortByLayer layer_cmp;
206                 sort(regs.begin(), regs.end(), layer_cmp);
207                 
208                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
209                 
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);
212                         if (!mr) {
213                                 continue;
214                         }
215
216                         NoteTrackers::iterator t = _note_trackers.find (mr.get());
217                         MidiStateTracker* tracker;
218                         bool new_tracker = false;
219
220                         
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()));
222
223                         if (t == _note_trackers.end()) {
224                                 tracker = new MidiStateTracker;
225                                 new_tracker = true;
226                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
227                         } else {
228                                 tracker = t->second;
229                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
230                         }
231
232                                 
233                         mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
234                         _read_data_count += mr->read_data_count();
235
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)));
240                         }                       
241                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
242
243                         if (new_tracker) {
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");
249                         } 
250                 }
251
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)));
256                 }                       
257                 
258                 if (!evlist.empty()) {
259                 
260                         /* sort the event list */
261                         EventsSortByTime<nframes_t> time_cmp;
262                         evlist.sort (time_cmp);
263                         
264                         /* write into dst */
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());
268                                 delete ev;
269                         }
270                 }
271         }
272
273         DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
274         return dur;
275 }
276
277 void
278 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
279 {
280         /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
281         NoteTrackers::iterator t = _note_trackers.find (region.get());
282
283         /* GACK! THREAD SAFETY! */
284
285         if (t != _note_trackers.end()) {
286                 delete t->second;
287                 _note_trackers.erase (t);
288         }
289 }
290
291
292 void
293 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
294 {
295         /* MIDI regions have no dependents (crossfades) */
296 }
297
298 void
299 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
300 {
301         /* No MIDI crossfading (yet?), so nothing to do here */
302 }
303
304 void
305 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
306 {
307         /* MIDI regions have no dependents (crossfades) */
308 }
309
310
311 int
312 MidiPlaylist::set_state (const XMLNode& node, int version)
313 {
314         in_set_state++;
315         freeze ();
316
317         Playlist::set_state (node, version);
318
319         thaw();
320         in_set_state--;
321
322         return 0;
323 }
324
325 void
326 MidiPlaylist::dump () const
327 {
328         boost::shared_ptr<Region> r;
329
330         cerr << "Playlist \"" << _name << "\" " << endl
331         << regions.size() << " regions "
332         << endl;
333
334         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
335                 r = *i;
336                 cerr << "  " << r->name() << " @ " << r << " ["
337                 << r->start() << "+" << r->length()
338                 << "] at "
339                 << r->position()
340                 << " on layer "
341                 << r->layer ()
342                 << endl;
343         }
344 }
345
346 bool
347 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
348 {
349         boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
350         bool changed = false;
351
352         if (r == 0) {
353                 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
354                 << endmsg;
355                 /*NOTREACHED*/
356                 return false;
357         }
358
359         {
360                 RegionLock rlock (this);
361                 RegionList::iterator i;
362                 RegionList::iterator tmp;
363
364                 for (i = regions.begin(); i != regions.end(); ) {
365
366                         tmp = i;
367                         ++tmp;
368
369                         if ((*i) == region) {
370                                 regions.erase (i);
371                                 changed = true;
372                         }
373
374                         i = tmp;
375                 }
376         }
377
378
379         if (changed) {
380                 /* overload this, it normally means "removed", not destroyed */
381                 notify_region_removed (region);
382         }
383
384         return changed;
385 }
386
387 set<Evoral::Parameter>
388 MidiPlaylist::contained_automation()
389 {
390         /* this function is never called from a realtime thread, so
391            its OK to block (for short intervals).
392         */
393
394         Glib::RecMutex::Lock rm (region_lock);
395
396         set<Evoral::Parameter> ret;
397
398         for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
399                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
400
401                 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
402                                 c != mr->model()->controls().end(); ++c) {
403                         ret.insert(c->first);
404                 }
405         }
406
407         return ret;
408 }
409
410
411 bool
412 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
413 {
414         if (in_flush || in_set_state) {
415                 return false;
416         }
417
418         // Feeling rather uninterested today, but thanks for the heads up anyway!
419
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;
428
429         parent_wants_notify = Playlist::region_changed (what_changed, region);
430
431         if ((parent_wants_notify || (what_changed & our_interests))) {
432                 notify_modified ();
433         }
434
435         return true;
436 }
437