d02079c7157dd576057f0a03168cd7444de4b4c7
[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         throw; // nope
67
68         /*
69         list<Region*>::const_iterator in_o  = other.regions.begin();
70         list<Region*>::iterator in_n = regions.begin();
71
72         while (in_o != other.regions.end()) {
73                 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
74
75                 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
76                         if ( &(*xfades)->in() == ar) {
77                                 // We found one! Now copy it!
78
79                                 list<Region*>::const_iterator out_o = other.regions.begin();
80                                 list<Region*>::const_iterator out_n = regions.begin();
81
82                                 while (out_o != other.regions.end()) {
83
84                                         MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
85
86                                         if ( &(*xfades)->out() == ar2) {
87                                                 MidiRegion *in  = dynamic_cast<MidiRegion*>( (*in_n) );
88                                                 MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
89                                                 Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
90                                                 add_crossfade(*new_fade);
91                                                 break;
92                                         }
93
94                                         out_o++;
95                                         out_n++;
96                                 }
97                                 // cerr << "HUH!? second region in the crossfade not found!" << endl;
98                         }
99                 }
100
101                 in_o++;
102                 in_n++;
103         }
104 */
105 }
106
107 MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
108                 : Playlist (other, start, dur, name, hidden)
109 {
110         /* this constructor does NOT notify others (session) */
111 }
112
113 MidiPlaylist::~MidiPlaylist ()
114 {
115         GoingAway (); /* EMIT SIGNAL */
116
117         /* drop connections to signals */
118
119         notify_callbacks ();
120 }
121
122 struct RegionSortByLayer {
123     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
124             return a->layer() < b->layer();
125     }
126 };
127
128 template<typename Time>
129 struct EventsSortByTime {
130     bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
131             return a->time() < b->time();
132     }
133 };
134
135 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
136 nframes_t
137 MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur, unsigned chan_n)
138 {
139         /* this function is never called from a realtime thread, so
140            its OK to block (for short intervals).
141         */
142
143         Glib::RecMutex::Lock rm (region_lock);
144         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2  +++++++++++++++++++++++++++++++++++++++++++++++\n", start, start + dur));
145
146         nframes_t end = start + dur - 1;
147
148         _read_data_count = 0;
149
150         // relevent regions overlapping start <--> end
151         vector< boost::shared_ptr<Region> > regs;
152         typedef pair<MidiStateTracker*,nframes64_t> TrackerInfo;
153         vector<TrackerInfo> tracker_info;
154         uint32_t note_cnt = 0;
155
156         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
157                 if ((*i)->coverage (start, end) != OverlapNone) {
158                         regs.push_back(*i);
159                 } else {
160                         NoteTrackers::iterator t = _note_trackers.find ((*i).get());
161                         if (t != _note_trackers.end()) {
162
163                                 /* add it the set of trackers we will do note resolution
164                                    on, and remove it from the list we are keeping 
165                                    around, because we don't need it anymore.
166                                 */
167                                 
168                                 tracker_info.push_back (TrackerInfo (t->second, (*i)->last_frame()));
169                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("time to resolve & remove tracker for %1\n", (*i)->name()));
170                                 note_cnt += (t->second->on());
171                                 _note_trackers.erase (t);
172                         }
173                 }
174         }
175
176         if (note_cnt == 0 && !tracker_info.empty()) {
177                 /* trackers to dispose of, but they have no notes in them */
178                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Clearing %1 empty trackers\n", tracker_info.size()));
179                 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
180                         delete (*t).first;
181                 }
182                 tracker_info.clear ();
183         }
184                         
185         if (regs.size() == 1 && tracker_info.empty()) {
186         
187                 /* just a single region - read directly into dst */
188
189                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, no out-of-bound region tracking info\n", regs.front()->name()));
190                 
191                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
192
193                 if (mr) {
194
195                         NoteTrackers::iterator t = _note_trackers.find (mr.get());
196                         MidiStateTracker* tracker;
197                         bool new_tracker = false;
198
199                         if (t == _note_trackers.end()) {
200                                 tracker = new MidiStateTracker;
201                                 new_tracker = true;
202                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
203                         } else {
204                                 tracker = t->second;
205                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes", tracker->on()));
206                         }
207                         
208                         mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
209                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes", tracker->on()));
210                         
211                         if (new_tracker) {
212                                 pair<Region*,MidiStateTracker*> newpair;
213                                 newpair.first = mr.get();
214                                 newpair.second = tracker;
215                                 _note_trackers.insert (newpair);
216                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
217                         }
218
219                         _read_data_count += mr->read_data_count();
220                 }
221                 
222         } else {
223
224                 /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically 
225                    sorted EventSink, sort and then insert into dst.
226                 */
227
228                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
229                 
230                 Evoral::EventList<nframes_t> evlist;
231
232                 for (vector<TrackerInfo>::iterator t = tracker_info.begin(); t != tracker_info.end(); ++t) {
233                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Resolve %1 notes\n", (*t).first->on()));
234                         (*t).first->resolve_notes (evlist, (*t).second);
235                         delete (*t).first;
236                 }
237                 
238                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After resolution we now have %1 events\n",  evlist.size()));
239                 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
240                         DEBUG_STR_SET(a,**x);
241                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
242                 }                       
243                 
244                 RegionSortByLayer layer_cmp;
245                 sort(regs.begin(), regs.end(), layer_cmp);
246                 
247                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
248                 
249                 for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
250                         boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
251                         if (!mr) {
252                                 continue;
253                         }
254
255                         NoteTrackers::iterator t = _note_trackers.find (mr.get());
256                         MidiStateTracker* tracker;
257                         bool new_tracker = false;
258
259                         
260                         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()));
261
262                         if (t == _note_trackers.end()) {
263                                 tracker = new MidiStateTracker;
264                                 new_tracker = true;
265                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
266                         } else {
267                                 tracker = t->second;
268                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
269                         }
270
271                                 
272                         mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
273                         _read_data_count += mr->read_data_count();
274
275                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
276                         for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
277                                 DEBUG_STR_SET(a,**x);
278                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
279                         }                       
280                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
281
282                         if (new_tracker) {
283                                 pair<Region*,MidiStateTracker*> newpair;
284                                 newpair.first = mr.get();
285                                 newpair.second = tracker;
286                                 _note_trackers.insert (newpair);
287                                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
288                         } 
289                 }
290
291                 DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n",  evlist.size()));
292                 for (Evoral::EventList<nframes_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
293                         DEBUG_STR_SET(a,**x);
294                         DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", DEBUG_STR(a)));
295                 }                       
296                 
297                 if (!evlist.empty()) {
298                 
299                         /* sort the event list */
300                         EventsSortByTime<nframes_t> time_cmp;
301                         evlist.sort (time_cmp);
302                         
303                         /* write into dst */
304                         for (Evoral::EventList<nframes_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
305                                 Evoral::Event<nframes_t>* ev (*e);
306                                 dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
307                                 delete ev;
308                         }
309                 }
310         }
311
312         DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
313         return dur;
314 }
315
316 void
317 MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
318 {
319         /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
320         NoteTrackers::iterator t = _note_trackers.find (region.get());
321
322         /* GACK! THREAD SAFETY! */
323
324         if (t != _note_trackers.end()) {
325                 delete t->second;
326                 _note_trackers.erase (t);
327         }
328 }
329
330
331 void
332 MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
333 {
334         /* MIDI regions have no dependents (crossfades) */
335 }
336
337 void
338 MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
339 {
340         /* No MIDI crossfading (yet?), so nothing to do here */
341 }
342
343 void
344 MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
345 {
346         /* MIDI regions have no dependents (crossfades) */
347 }
348
349
350 int
351 MidiPlaylist::set_state (const XMLNode& node, int version)
352 {
353         in_set_state++;
354         freeze ();
355
356         Playlist::set_state (node, version);
357
358         thaw();
359         in_set_state--;
360
361         return 0;
362 }
363
364 void
365 MidiPlaylist::dump () const
366 {
367         boost::shared_ptr<Region> r;
368
369         cerr << "Playlist \"" << _name << "\" " << endl
370         << regions.size() << " regions "
371         << endl;
372
373         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
374                 r = *i;
375                 cerr << "  " << r->name() << " @ " << r << " ["
376                 << r->start() << "+" << r->length()
377                 << "] at "
378                 << r->position()
379                 << " on layer "
380                 << r->layer ()
381                 << endl;
382         }
383 }
384
385 bool
386 MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
387 {
388         boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
389         bool changed = false;
390
391         if (r == 0) {
392                 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
393                 << endmsg;
394                 /*NOTREACHED*/
395                 return false;
396         }
397
398         {
399                 RegionLock rlock (this);
400                 RegionList::iterator i;
401                 RegionList::iterator tmp;
402
403                 for (i = regions.begin(); i != regions.end(); ) {
404
405                         tmp = i;
406                         ++tmp;
407
408                         if ((*i) == region) {
409                                 regions.erase (i);
410                                 changed = true;
411                         }
412
413                         i = tmp;
414                 }
415         }
416
417
418         if (changed) {
419                 /* overload this, it normally means "removed", not destroyed */
420                 notify_region_removed (region);
421         }
422
423         return changed;
424 }
425
426 set<Evoral::Parameter>
427 MidiPlaylist::contained_automation()
428 {
429         /* this function is never called from a realtime thread, so
430            its OK to block (for short intervals).
431         */
432
433         Glib::RecMutex::Lock rm (region_lock);
434
435         set<Evoral::Parameter> ret;
436
437         for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
438                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
439
440                 for (Automatable::Controls::iterator c = mr->model()->controls().begin();
441                                 c != mr->model()->controls().end(); ++c) {
442                         ret.insert(c->first);
443                 }
444         }
445
446         return ret;
447 }
448
449
450 bool
451 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
452 {
453         if (in_flush || in_set_state) {
454                 return false;
455         }
456
457         // Feeling rather uninterested today, but thanks for the heads up anyway!
458
459         Change our_interests = Change (/*MidiRegion::FadeInChanged|
460                                        MidiRegion::FadeOutChanged|
461                                        MidiRegion::FadeInActiveChanged|
462                                        MidiRegion::FadeOutActiveChanged|
463                                        MidiRegion::EnvelopeActiveChanged|
464                                        MidiRegion::ScaleAmplitudeChanged|
465                                        MidiRegion::EnvelopeChanged*/);
466         bool parent_wants_notify;
467
468         parent_wants_notify = Playlist::region_changed (what_changed, region);
469
470         if ((parent_wants_notify || (what_changed & our_interests))) {
471                 notify_modified ();
472         }
473
474         return true;
475 }
476