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