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