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();
168 MidiPlaylist::read (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t start,
169 jack_nframes_t cnt, unsigned chan_n)
171 jack_nframes_t ret = cnt;
173 jack_nframes_t read_frames;
174 jack_nframes_t skip_frames;
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.
180 it would be great if someone could measure this
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
189 memset (buf, 0, sizeof (unsigned char) * cnt);
191 /* this function is never called from a realtime thread, so
192 its OK to block (for short intervals).
195 Glib::Mutex::Lock rm (region_lock);
197 end = start + cnt - 1;
201 _read_data_count = 0;
203 map<uint32_t,vector<Region*> > relevant_regions;
204 vector<uint32_t> relevant_layers;
206 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
207 if ((*i)->coverage (start, end) != OverlapNone) {
209 relevant_regions[(*i)->layer()].push_back (*i);
210 relevant_layers.push_back ((*i)->layer());
214 // RegionSortByLayer layer_cmp;
215 // relevant_regions.sort (layer_cmp);
218 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
220 // FIXME: Should be vector<MidiRegion*>
221 vector<Region*>& r (relevant_regions[*l]);
223 for (vector<Region*>::iterator i = r.begin(); i != r.end(); ++i) {
224 MidiRegion* const mr = dynamic_cast<MidiRegion*>(*i);
226 mr->read_at (buf, mixdown_buffer, workbuf, start, cnt, chan_n, read_frames, skip_frames);
227 _read_data_count += mr->read_data_count();
237 MidiPlaylist::remove_dependents (Region& region)
239 MidiRegion* r = dynamic_cast<MidiRegion*> (®ion);
242 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
251 MidiPlaylist::flush_notifications ()
253 Playlist::flush_notifications();
265 MidiPlaylist::refresh_dependents (Region& r)
267 MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
275 MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
278 MidiRegion *orig = dynamic_cast<MidiRegion*>(o);
279 MidiRegion *left = dynamic_cast<MidiRegion*>(l);
280 MidiRegion *right = dynamic_cast<MidiRegion*>(r);
282 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
283 Crossfades::iterator tmp;
289 if ((*x)->_in == orig) {
290 if (! (*x)->covers(right->position())) {
291 fade = new Crossfade( *(*x), left, (*x)->_out);
293 // Overlap, the crossfade is copied on the left side of the right region instead
294 fade = new Crossfade( *(*x), right, (*x)->_out);
298 if ((*x)->_out == orig) {
299 if (! (*x)->covers(right->position())) {
300 fade = new Crossfade( *(*x), (*x)->_in, right);
302 // Overlap, the crossfade is copied on the right side of the left region instead
303 fade = new Crossfade( *(*x), (*x)->_in, left);
308 _crossfades.remove( (*x) );
309 add_crossfade (*fade);
316 MidiPlaylist::check_dependents (Region& r, bool norefresh)
323 if (in_set_state || in_partition) {
327 if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
328 PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
334 refresh_dependents (r);
337 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
339 other = dynamic_cast<MidiRegion*> (*i);
341 if (other == region) {
345 if (other->muted() || region->muted()) {
349 if (other->layer() < region->layer()) {
362 MidiPlaylist::set_state (const XMLNode& node)
367 XMLNodeConstIterator niter;
370 Playlist::set_state (node);
373 nlist = node.children();
375 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
385 MidiPlaylist::drop_all_states ()
387 set<Region*> all_regions;
389 /* find every region we've ever used, and add it to the set of
390 all regions. same for xfades;
393 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
395 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
397 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
398 all_regions.insert (*r);
402 /* now remove from the "all" lists every region that is in the current list. */
404 for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
406 <Region*>::iterator x = all_regions.find (*i);
407 if (x != all_regions.end()) {
408 all_regions.erase (x);
412 /* delete every region that is left - these are all things that are part of our "history" */
415 <Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
416 (*ar)->unlock_sources ();
420 /* Now do the generic thing ... */
422 StateManager::drop_all_states ();
426 MidiPlaylist::state_factory (std::string why) const
428 State* state = new State (why);
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());
440 MidiPlaylist::restore_state (StateManager::State& state)
443 RegionLock rlock (this);
444 State* apstate = dynamic_cast<State*> (&state);
448 regions = apstate->regions;
450 for (list<UndoAction>::iterator s = apstate->
451 region_states.begin();
452 s != apstate->region_states.end();
457 in_set_state = false;
460 notify_length_changed ();
465 MidiPlaylist::get_memento () const
467 return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
472 MidiPlaylist::state (bool full_state)
474 XMLNode& node = Playlist::state (full_state);
480 MidiPlaylist::dump () const
484 cerr << "Playlist \"" << _name << "\" " << endl
485 << regions.size() << " regions "
488 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
490 cerr << " " << r->name() << " @ " << r << " ["
491 << r->start() << "+" << r->length()
501 MidiPlaylist::destroy_region (Region* region)
503 MidiRegion* r = dynamic_cast<MidiRegion*> (region);
504 bool changed = false;
507 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
514 RegionLock rlock (this);
515 RegionList::iterator i;
516 RegionList::iterator tmp;
518 for (i = regions.begin(); i != regions.end(); ) {
523 if ((*i) == region) {
524 (*i)->unlock_sources ();
533 for (StateMap::iterator s = states.begin(); s != states.end(); ) {
534 StateMap::iterator tmp;
539 State* astate = dynamic_cast<State*> (*s);
541 list<UndoAction>::iterator rsi, rsitmp;
542 RegionList::iterator ri, ritmp;
544 for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
545 ri != astate->regions.end() && rsi != astate->region_states.end();) {
554 if (region == (*ri)) {
555 astate->regions.erase (ri);
556 astate->region_states.erase (rsi);
568 /* overload this, it normally means "removed", not destroyed */
569 notify_region_removed (region);
577 MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
579 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
581 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
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);
596 MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
598 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
600 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
602 if (ar && ar->region_list_equivalent (other)) {
603 results.push_back (ar);
609 MidiPlaylist::region_changed (Change what_changed, Region* region)
611 if (in_flush || in_set_state) {
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;
624 parent_wants_notify = Playlist::region_changed (what_changed, region);
626 maybe_save_state (_("region modified"));
628 if ((parent_wants_notify || (what_changed & our_interests))) {