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