2 Copyright (C) 2006 Paul Davis
3 Written by Dave Robillard, 2006
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.
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.
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.
26 #include <sigc++/bind.h>
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>
34 #include <pbd/error.h>
38 using namespace ARDOUR;
42 MidiPlaylist::State::~State ()
45 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
46 : Playlist (session, node, hidden)
52 save_state (_("initial state"));
55 PlaylistCreated (this); /* EMIT SIGNAL */
59 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
60 : Playlist (session, name, hidden)
62 save_state (_("initial state"));
65 PlaylistCreated (this); /* EMIT SIGNAL */
70 MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden)
71 : Playlist (other, name, hidden)
73 save_state (_("initial state"));
76 list<Region*>::const_iterator in_o = other.regions.begin();
77 list<Region*>::iterator in_n = regions.begin();
79 while (in_o != other.regions.end()) {
80 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
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!
86 list<Region*>::const_iterator out_o = other.regions.begin();
87 list<Region*>::const_iterator out_n = regions.begin();
89 while (out_o != other.regions.end()) {
91 MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
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);
104 // cerr << "HUH!? second region in the crossfade not found!" << endl;
113 PlaylistCreated (this); /* EMIT SIGNAL */
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)
120 save_state (_("initial state"));
122 /* this constructor does NOT notify others (session) */
125 MidiPlaylist::~MidiPlaylist ()
127 set <Region*> all_regions;
131 /* find every region we've ever used, and add it to the set of
135 for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
136 all_regions.insert (*x);
139 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
141 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
143 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
144 all_regions.insert (*r);
150 /* delete every region */
152 for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
153 (*ar)->unlock_sources ();
159 struct RegionSortByLayer
161 bool operator() (Region *a, Region *b)
163 return a->layer() < b->layer();
167 /** FIXME: semantics of return value? */
169 MidiPlaylist::read (RawMidi *buf, RawMidi *mixdown_buffer, jack_nframes_t start,
170 jack_nframes_t cnt, unsigned chan_n)
172 /* this function is never called from a realtime thread, so
173 its OK to block (for short intervals).
176 Glib::Mutex::Lock rm (region_lock);
178 jack_nframes_t ret = 0;
179 jack_nframes_t end = start + cnt - 1;
180 jack_nframes_t read_frames = 0;
181 jack_nframes_t skip_frames = 0;
183 _read_data_count = 0;
185 vector<MidiRegion*> regs; // relevent regions overlapping start <--> end
187 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
188 MidiRegion* const mr = dynamic_cast<MidiRegion*>(*i);
189 if (mr && mr->coverage (start, end) != OverlapNone) {
194 RegionSortByLayer layer_cmp;
195 sort(regs.begin(), regs.end(), layer_cmp);
197 for (vector<MidiRegion*>::iterator i = regs.begin(); i != regs.end(); ++i) {
198 (*i)->read_at (buf, mixdown_buffer, start, cnt, chan_n, read_frames, skip_frames);
199 ret += (*i)->read_data_count();
202 _read_data_count += ret;
209 MidiPlaylist::remove_dependents (Region& region)
211 MidiRegion* r = dynamic_cast<MidiRegion*> (®ion);
214 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
223 MidiPlaylist::flush_notifications ()
225 Playlist::flush_notifications();
237 MidiPlaylist::refresh_dependents (Region& r)
239 MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
247 MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
250 MidiRegion *orig = dynamic_cast<MidiRegion*>(o);
251 MidiRegion *left = dynamic_cast<MidiRegion*>(l);
252 MidiRegion *right = dynamic_cast<MidiRegion*>(r);
254 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
255 Crossfades::iterator tmp;
261 if ((*x)->_in == orig) {
262 if (! (*x)->covers(right->position())) {
263 fade = new Crossfade( *(*x), left, (*x)->_out);
265 // Overlap, the crossfade is copied on the left side of the right region instead
266 fade = new Crossfade( *(*x), right, (*x)->_out);
270 if ((*x)->_out == orig) {
271 if (! (*x)->covers(right->position())) {
272 fade = new Crossfade( *(*x), (*x)->_in, right);
274 // Overlap, the crossfade is copied on the right side of the left region instead
275 fade = new Crossfade( *(*x), (*x)->_in, left);
280 _crossfades.remove( (*x) );
281 add_crossfade (*fade);
288 MidiPlaylist::check_dependents (Region& r, bool norefresh)
295 if (in_set_state || in_partition) {
299 if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
300 PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
306 refresh_dependents (r);
309 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
311 other = dynamic_cast<MidiRegion*> (*i);
313 if (other == region) {
317 if (other->muted() || region->muted()) {
321 if (other->layer() < region->layer()) {
334 MidiPlaylist::set_state (const XMLNode& node)
339 XMLNodeConstIterator niter;
342 Playlist::set_state (node);
345 nlist = node.children();
347 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
357 MidiPlaylist::drop_all_states ()
359 set<Region*> all_regions;
361 /* find every region we've ever used, and add it to the set of
362 all regions. same for xfades;
365 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
367 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
369 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
370 all_regions.insert (*r);
374 /* now remove from the "all" lists every region that is in the current list. */
376 for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
378 <Region*>::iterator x = all_regions.find (*i);
379 if (x != all_regions.end()) {
380 all_regions.erase (x);
384 /* delete every region that is left - these are all things that are part of our "history" */
387 <Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
388 (*ar)->unlock_sources ();
392 /* Now do the generic thing ... */
394 StateManager::drop_all_states ();
398 MidiPlaylist::state_factory (std::string why) const
400 State* state = new State (why);
402 state->regions = regions;
403 state->region_states.clear ();
404 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
405 state->region_states.push_back ((*i)->get_memento());
412 MidiPlaylist::restore_state (StateManager::State& state)
415 RegionLock rlock (this);
416 State* apstate = dynamic_cast<State*> (&state);
420 regions = apstate->regions;
422 for (list<UndoAction>::iterator s = apstate->
423 region_states.begin();
424 s != apstate->region_states.end();
429 in_set_state = false;
432 notify_length_changed ();
437 MidiPlaylist::get_memento () const
439 return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
444 MidiPlaylist::state (bool full_state)
446 XMLNode& node = Playlist::state (full_state);
452 MidiPlaylist::dump () const
456 cerr << "Playlist \"" << _name << "\" " << endl
457 << regions.size() << " regions "
460 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
462 cerr << " " << r->name() << " @ " << r << " ["
463 << r->start() << "+" << r->length()
473 MidiPlaylist::destroy_region (Region* region)
475 MidiRegion* r = dynamic_cast<MidiRegion*> (region);
476 bool changed = false;
479 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
486 RegionLock rlock (this);
487 RegionList::iterator i;
488 RegionList::iterator tmp;
490 for (i = regions.begin(); i != regions.end(); ) {
495 if ((*i) == region) {
496 (*i)->unlock_sources ();
505 for (StateMap::iterator s = states.begin(); s != states.end(); ) {
506 StateMap::iterator tmp;
511 State* astate = dynamic_cast<State*> (*s);
513 list<UndoAction>::iterator rsi, rsitmp;
514 RegionList::iterator ri, ritmp;
516 for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
517 ri != astate->regions.end() && rsi != astate->region_states.end();) {
526 if (region == (*ri)) {
527 astate->regions.erase (ri);
528 astate->region_states.erase (rsi);
540 /* overload this, it normally means "removed", not destroyed */
541 notify_region_removed (region);
549 MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
551 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
553 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
556 if (Config->get_use_overlap_equivalency()) {
557 if (ar->overlap_equivalent (other)) {
558 results.push_back (ar);
559 } else if (ar->equivalent (other)) {
560 results.push_back (ar);
568 MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
570 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
572 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
574 if (ar && ar->region_list_equivalent (other)) {
575 results.push_back (ar);
581 MidiPlaylist::region_changed (Change what_changed, Region* region)
583 if (in_flush || in_set_state) {
587 Change our_interests = Change (/*MidiRegion::FadeInChanged|
588 MidiRegion::FadeOutChanged|
589 MidiRegion::FadeInActiveChanged|
590 MidiRegion::FadeOutActiveChanged|
591 MidiRegion::EnvelopeActiveChanged|
592 MidiRegion::ScaleAmplitudeChanged|
593 MidiRegion::EnvelopeChanged*/);
594 bool parent_wants_notify;
596 parent_wants_notify = Playlist::region_changed (what_changed, region);
598 maybe_save_state (_("region modified"));
600 if ((parent_wants_notify || (what_changed & our_interests))) {