Actually added the code mentioned in my last commit. Whoops.
[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
24 #include <stdlib.h>
25
26 #include <sigc++/bind.h>
27
28 #include <ardour/types.h>
29 #include <ardour/configuration.h>
30 #include <ardour/midi_playlist.h>
31 #include <ardour/midi_region.h>
32 #include <ardour/session.h>
33
34 #include <pbd/error.h>
35
36 #include "i18n.h"
37
38 using namespace ARDOUR;
39 using namespace sigc;
40 using namespace std;
41
42 MidiPlaylist::State::~State ()
43 {}
44
45 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
46                 : Playlist (session, node, hidden)
47 {
48         in_set_state = true;
49         set_state (node);
50         in_set_state = false;
51
52         save_state (_("initial state"));
53
54         if (!hidden) {
55                 PlaylistCreated (this); /* EMIT SIGNAL */
56         }
57 }
58
59 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
60                 : Playlist (session, name, hidden)
61 {
62         save_state (_("initial state"));
63
64         if (!hidden) {
65                 PlaylistCreated (this); /* EMIT SIGNAL */
66         }
67
68 }
69
70 MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden)
71                 : Playlist (other, name, hidden)
72 {
73         save_state (_("initial state"));
74
75         /*
76         list<Region*>::const_iterator in_o  = other.regions.begin();
77         list<Region*>::iterator in_n = regions.begin();
78
79         while (in_o != other.regions.end()) {
80                 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
81
82                 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
83                         if ( &(*xfades)->in() == ar) {
84                                 // We found one! Now copy it!
85
86                                 list<Region*>::const_iterator out_o = other.regions.begin();
87                                 list<Region*>::const_iterator out_n = regions.begin();
88
89                                 while (out_o != other.regions.end()) {
90
91                                         MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
92
93                                         if ( &(*xfades)->out() == ar2) {
94                                                 MidiRegion *in  = dynamic_cast<MidiRegion*>( (*in_n) );
95                                                 MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
96                                                 Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
97                                                 add_crossfade(*new_fade);
98                                                 break;
99                                         }
100
101                                         out_o++;
102                                         out_n++;
103                                 }
104                                 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
105                         }
106                 }
107
108                 in_o++;
109                 in_n++;
110         }
111 */
112         if (!hidden) {
113                 PlaylistCreated (this); /* EMIT SIGNAL */
114         }
115 }
116
117 MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden)
118                 : Playlist (other, start, cnt, name, hidden)
119 {
120         save_state (_("initial state"));
121
122         /* this constructor does NOT notify others (session) */
123 }
124
125 MidiPlaylist::~MidiPlaylist ()
126 {
127         set <Region*> all_regions;
128
129         GoingAway (this);
130
131         /* find every region we've ever used, and add it to the set of
132            all regions.
133         */
134
135         for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
136                 all_regions.insert (*x);
137         }
138
139         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
140
141                 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
142
143                 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
144                         all_regions.insert (*r);
145                 }
146
147                 delete apstate;
148         }
149
150         /* delete every region */
151
152         for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
153                 (*ar)->unlock_sources ();
154                 delete *ar;
155         }
156
157 }
158
159 struct RegionSortByLayer
160 {
161         bool operator() (Region *a, Region *b)
162         {
163                 return a->layer() < b->layer();
164         }
165 };
166
167 jack_nframes_t
168 MidiPlaylist::read (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t start,
169                      jack_nframes_t cnt, unsigned chan_n)
170 {
171         jack_nframes_t ret = cnt;
172         jack_nframes_t end;
173         jack_nframes_t read_frames;
174         jack_nframes_t skip_frames;
175
176         /* optimizing this memset() away involves a lot of conditionals
177            that may well cause more of a hit due to cache misses 
178            and related stuff than just doing this here.
179            
180            it would be great if someone could measure this
181            at some point.
182
183            one way or another, parts of the requested area
184            that are not written to by Region::region_at()
185            for all Regions that cover the area need to be
186            zeroed.
187         */
188
189         memset (buf, 0, sizeof (unsigned char) * cnt);
190
191         /* this function is never called from a realtime thread, so
192            its OK to block (for short intervals).
193         */
194
195         Glib::Mutex::Lock rm (region_lock);
196
197         end =  start + cnt - 1;
198
199         read_frames = 0;
200         skip_frames = 0;
201         _read_data_count = 0;
202
203         map<uint32_t,vector<Region*> > relevant_regions;
204         vector<uint32_t> relevant_layers;
205
206         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
207                 if ((*i)->coverage (start, end) != OverlapNone) {
208
209                         relevant_regions[(*i)->layer()].push_back (*i);
210                         relevant_layers.push_back ((*i)->layer());
211                 }
212         }
213
214         //      RegionSortByLayer layer_cmp;
215         //      relevant_regions.sort (layer_cmp);
216
217
218         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
219
220                 // FIXME: Should be vector<MidiRegion*>
221                 vector<Region*>& r (relevant_regions[*l]);
222
223                 for (vector<Region*>::iterator i = r.begin(); i != r.end(); ++i) {
224                         MidiRegion* const mr = dynamic_cast<MidiRegion*>(*i);
225                         assert(mr);
226                         mr->read_at (buf, mixdown_buffer, workbuf, start, cnt, chan_n, read_frames, skip_frames);
227                         _read_data_count += mr->read_data_count();
228                 }
229
230         }
231
232         return ret;
233 }
234
235
236 void
237 MidiPlaylist::remove_dependents (Region& region)
238 {
239         MidiRegion* r = dynamic_cast<MidiRegion*> (&region);
240
241         if (r == 0) {
242                 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
243                 << endmsg;
244                 return;
245         }
246
247 }
248
249
250 void
251 MidiPlaylist::flush_notifications ()
252 {
253         Playlist::flush_notifications();
254
255         if (in_flush) {
256                 return;
257         }
258
259         in_flush = true;
260
261         in_flush = false;
262 }
263
264 void
265 MidiPlaylist::refresh_dependents (Region& r)
266 {
267         MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
268
269         if (ar == 0) {
270                 return;
271         }
272 }
273
274 void
275 MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
276 {
277         /*
278         MidiRegion *orig  = dynamic_cast<MidiRegion*>(o);
279         MidiRegion *left  = dynamic_cast<MidiRegion*>(l);
280         MidiRegion *right = dynamic_cast<MidiRegion*>(r);
281
282         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
283                 Crossfades::iterator tmp;
284                 tmp = x;
285                 ++tmp;
286
287                 Crossfade *fade = 0;
288
289                 if ((*x)->_in == orig) {
290                         if (! (*x)->covers(right->position())) {
291                                 fade = new Crossfade( *(*x), left, (*x)->_out);
292                         } else {
293                                 // Overlap, the crossfade is copied on the left side of the right region instead
294                                 fade = new Crossfade( *(*x), right, (*x)->_out);
295                         }
296                 }
297
298                 if ((*x)->_out == orig) {
299                         if (! (*x)->covers(right->position())) {
300                                 fade = new Crossfade( *(*x), (*x)->_in, right);
301                         } else {
302                                 // Overlap, the crossfade is copied on the right side of the left region instead
303                                 fade = new Crossfade( *(*x), (*x)->_in, left);
304                         }
305                 }
306
307                 if (fade) {
308                         _crossfades.remove( (*x) );
309                         add_crossfade (*fade);
310                 }
311                 x = tmp;
312         }*/
313 }
314
315 void
316 MidiPlaylist::check_dependents (Region& r, bool norefresh)
317 {
318         MidiRegion* other;
319         MidiRegion* region;
320         MidiRegion* top;
321         MidiRegion* bottom;
322
323         if (in_set_state || in_partition) {
324                 return;
325         }
326
327         if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
328                 PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
329                 << endmsg;
330                 return;
331         }
332
333         if (!norefresh) {
334                 refresh_dependents (r);
335         }
336
337         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
338
339                 other = dynamic_cast<MidiRegion*> (*i);
340
341                 if (other == region) {
342                         continue;
343                 }
344
345                 if (other->muted() || region->muted()) {
346                         continue;
347                 }
348
349                 if (other->layer() < region->layer()) {
350                         top = region;
351                         bottom = other;
352                 } else {
353                         top = other;
354                         bottom = region;
355                 }
356
357         }
358 }
359
360
361 int
362 MidiPlaylist::set_state (const XMLNode& node)
363 {
364         /*
365         XMLNode *child;
366         XMLNodeList nlist;
367         XMLNodeConstIterator niter;
368
369         if (!in_set_state) {
370                 Playlist::set_state (node);
371         }
372
373         nlist = node.children();
374
375         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
376
377                 child = *niter;
378
379         }*/
380
381         return 0;
382 }
383
384 void
385 MidiPlaylist::drop_all_states ()
386 {
387         set<Region*> all_regions;
388
389         /* find every region we've ever used, and add it to the set of
390            all regions. same for xfades;
391         */
392
393         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
394
395                 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
396
397                 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
398                         all_regions.insert (*r);
399                 }
400         }
401
402         /* now remove from the "all" lists every region that is in the current list. */
403
404         for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
405                 set
406                         <Region*>::iterator x = all_regions.find (*i);
407                 if (x != all_regions.end()) {
408                         all_regions.erase (x);
409                 }
410         }
411
412         /* delete every region that is left - these are all things that are part of our "history" */
413
414         for (set
415                 <Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
416                 (*ar)->unlock_sources ();
417                 delete *ar;
418         }
419
420         /* Now do the generic thing ... */
421
422         StateManager::drop_all_states ();
423 }
424
425 StateManager::State*
426 MidiPlaylist::state_factory (std::string why) const
427 {
428         State* state = new State (why);
429
430         state->regions = regions;
431         state->region_states.clear ();
432         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
433                 state->region_states.push_back ((*i)->get_memento());
434         }
435
436         return state;
437 }
438
439 Change
440 MidiPlaylist::restore_state (StateManager::State& state)
441 {
442         {
443                 RegionLock rlock (this);
444                 State* apstate = dynamic_cast<State*> (&state);
445
446                 in_set_state = true;
447
448                 regions = apstate->regions;
449
450                 for (list<UndoAction>::iterator s = apstate->
451                                                     region_states.begin();
452                         s != apstate->region_states.end();
453                         ++s) {
454                         (*s) ();
455                 }
456
457                 in_set_state = false;
458         }
459
460         notify_length_changed ();
461         return Change (~0);
462 }
463
464 UndoAction
465 MidiPlaylist::get_memento () const
466 {
467         return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
468 }
469
470
471 XMLNode&
472 MidiPlaylist::state (bool full_state)
473 {
474         XMLNode& node = Playlist::state (full_state);
475
476         return node;
477 }
478
479 void
480 MidiPlaylist::dump () const
481 {
482         Region *r;
483
484         cerr << "Playlist \"" << _name << "\" " << endl
485         << regions.size() << " regions "
486         << endl;
487
488         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
489                 r = *i;
490                 cerr << "  " << r->name() << " @ " << r << " ["
491                 << r->start() << "+" << r->length()
492                 << "] at "
493                 << r->position()
494                 << " on layer "
495                 << r->layer ()
496                 << endl;
497         }
498 }
499
500 bool
501 MidiPlaylist::destroy_region (Region* region)
502 {
503         MidiRegion* r = dynamic_cast<MidiRegion*> (region);
504         bool changed = false;
505
506         if (r == 0) {
507                 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
508                 << endmsg;
509                 /*NOTREACHED*/
510                 return false;
511         }
512
513         {
514                 RegionLock rlock (this);
515                 RegionList::iterator i;
516                 RegionList::iterator tmp;
517
518                 for (i = regions.begin(); i != regions.end(); ) {
519
520                         tmp = i;
521                         ++tmp;
522
523                         if ((*i) == region) {
524                                 (*i)->unlock_sources ();
525                                 regions.erase (i);
526                                 changed = true;
527                         }
528
529                         i = tmp;
530                 }
531         }
532
533         for (StateMap::iterator s = states.begin(); s != states.end(); ) {
534                 StateMap::iterator tmp;
535
536                 tmp = s;
537                 ++tmp;
538
539                 State* astate = dynamic_cast<State*> (*s);
540
541                 list<UndoAction>::iterator rsi, rsitmp;
542                 RegionList::iterator ri, ritmp;
543
544                 for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
545                         ri != astate->regions.end() && rsi != astate->region_states.end();) {
546
547
548                         ritmp = ri;
549                         ++ritmp;
550
551                         rsitmp = rsi;
552                         ++rsitmp;
553
554                         if (region == (*ri)) {
555                                 astate->regions.erase (ri);
556                                 astate->region_states.erase (rsi);
557                         }
558
559                         ri = ritmp;
560                         rsi = rsitmp;
561                 }
562
563                 s = tmp;
564         }
565
566
567         if (changed) {
568                 /* overload this, it normally means "removed", not destroyed */
569                 notify_region_removed (region);
570         }
571
572         return changed;
573 }
574
575
576 void
577 MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
578 {
579         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
580
581                 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
582
583                 if (ar) {
584                         if (Config->get_use_overlap_equivalency()) {
585                                 if (ar->overlap_equivalent (other)) {
586                                         results.push_back (ar);
587                                 } else if (ar->equivalent (other)) {
588                                         results.push_back (ar);
589                                 }
590                         }
591                 }
592         }
593 }
594
595 void
596 MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
597 {
598         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
599
600                 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
601
602                 if (ar && ar->region_list_equivalent (other)) {
603                         results.push_back (ar);
604                 }
605         }
606 }
607
608 bool
609 MidiPlaylist::region_changed (Change what_changed, Region* region)
610 {
611         if (in_flush || in_set_state) {
612                 return false;
613         }
614
615         Change our_interests = Change (/*MidiRegion::FadeInChanged|
616                                        MidiRegion::FadeOutChanged|
617                                        MidiRegion::FadeInActiveChanged|
618                                        MidiRegion::FadeOutActiveChanged|
619                                        MidiRegion::EnvelopeActiveChanged|
620                                        MidiRegion::ScaleAmplitudeChanged|
621                                        MidiRegion::EnvelopeChanged*/);
622         bool parent_wants_notify;
623
624         parent_wants_notify = Playlist::region_changed (what_changed, region);
625
626         maybe_save_state (_("region modified"));
627
628         if ((parent_wants_notify || (what_changed & our_interests))) {
629                 notify_modified ();
630         }
631
632         return true;
633 }
634