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, DataType::MIDI, hidden)
48 const XMLProperty* prop = node.property("type");
49 assert(prop && DataType(prop->value()) == DataType::MIDI);
55 save_state (_("initial state"));
58 PlaylistCreated (this); /* EMIT SIGNAL */
62 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
63 : Playlist (session, name, DataType::MIDI, hidden)
65 save_state (_("initial state"));
68 PlaylistCreated (this); /* EMIT SIGNAL */
73 MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden)
74 : Playlist (other, name, hidden)
77 save_state (_("initial state"));
80 list<Region*>::const_iterator in_o = other.regions.begin();
81 list<Region*>::iterator in_n = regions.begin();
83 while (in_o != other.regions.end()) {
84 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
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!
90 list<Region*>::const_iterator out_o = other.regions.begin();
91 list<Region*>::const_iterator out_n = regions.begin();
93 while (out_o != other.regions.end()) {
95 MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
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);
108 // cerr << "HUH!? second region in the crossfade not found!" << endl;
117 PlaylistCreated (this); /* EMIT SIGNAL */
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)
124 save_state (_("initial state"));
126 /* this constructor does NOT notify others (session) */
129 MidiPlaylist::~MidiPlaylist ()
131 set <Region*> all_regions;
135 /* find every region we've ever used, and add it to the set of
139 for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
140 all_regions.insert (*x);
143 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
145 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
147 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
148 all_regions.insert (*r);
154 /* delete every region */
156 for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
157 (*ar)->unlock_sources ();
163 struct RegionSortByLayer
165 bool operator() (Region *a, Region *b)
167 return a->layer() < b->layer();
171 /** FIXME: semantics of return value? */
173 MidiPlaylist::read (RawMidi *buf, RawMidi *mixdown_buffer, jack_nframes_t start,
174 jack_nframes_t cnt, unsigned chan_n)
176 /* this function is never called from a realtime thread, so
177 its OK to block (for short intervals).
180 Glib::Mutex::Lock rm (region_lock);
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;
187 _read_data_count = 0;
189 vector<MidiRegion*> regs; // relevent regions overlapping start <--> end
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) {
198 RegionSortByLayer layer_cmp;
199 sort(regs.begin(), regs.end(), layer_cmp);
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();
206 _read_data_count += ret;
213 MidiPlaylist::remove_dependents (Region& region)
215 MidiRegion* r = dynamic_cast<MidiRegion*> (®ion);
218 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
227 MidiPlaylist::flush_notifications ()
229 Playlist::flush_notifications();
241 MidiPlaylist::refresh_dependents (Region& r)
243 MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
251 MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
253 throw; // I don't wanna
255 MidiRegion *orig = dynamic_cast<MidiRegion*>(o);
256 MidiRegion *left = dynamic_cast<MidiRegion*>(l);
257 MidiRegion *right = dynamic_cast<MidiRegion*>(r);
259 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
260 Crossfades::iterator tmp;
266 if ((*x)->_in == orig) {
267 if (! (*x)->covers(right->position())) {
268 fade = new Crossfade( *(*x), left, (*x)->_out);
270 // Overlap, the crossfade is copied on the left side of the right region instead
271 fade = new Crossfade( *(*x), right, (*x)->_out);
275 if ((*x)->_out == orig) {
276 if (! (*x)->covers(right->position())) {
277 fade = new Crossfade( *(*x), (*x)->_in, right);
279 // Overlap, the crossfade is copied on the right side of the left region instead
280 fade = new Crossfade( *(*x), (*x)->_in, left);
285 _crossfades.remove( (*x) );
286 add_crossfade (*fade);
293 MidiPlaylist::check_dependents (Region& r, bool norefresh)
300 if (in_set_state || in_partition) {
304 if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
305 PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
311 refresh_dependents (r);
314 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
316 other = dynamic_cast<MidiRegion*> (*i);
318 if (other == region) {
322 if (other->muted() || region->muted()) {
326 if (other->layer() < region->layer()) {
339 MidiPlaylist::set_state (const XMLNode& node)
342 Playlist::set_state (node);
345 // Actually Charles, I don't much care for children
348 XMLNodeList nlist = node.children();
350 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
352 XMLNode* const child = *niter;
360 MidiPlaylist::drop_all_states ()
362 set<Region*> all_regions;
364 /* find every region we've ever used, and add it to the set of
365 all regions. same for xfades;
368 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
370 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
372 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
373 all_regions.insert (*r);
377 /* now remove from the "all" lists every region that is in the current list. */
379 for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
381 <Region*>::iterator x = all_regions.find (*i);
382 if (x != all_regions.end()) {
383 all_regions.erase (x);
387 /* delete every region that is left - these are all things that are part of our "history" */
390 <Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
391 (*ar)->unlock_sources ();
395 /* Now do the generic thing ... */
397 StateManager::drop_all_states ();
401 MidiPlaylist::state_factory (std::string why) const
403 State* state = new State (why);
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());
415 MidiPlaylist::restore_state (StateManager::State& state)
418 RegionLock rlock (this);
419 State* apstate = dynamic_cast<State*> (&state);
423 regions = apstate->regions;
425 for (list<UndoAction>::iterator s = apstate->
426 region_states.begin();
427 s != apstate->region_states.end();
432 in_set_state = false;
435 notify_length_changed ();
440 MidiPlaylist::get_memento () const
442 return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
447 MidiPlaylist::state (bool full_state)
449 XMLNode& node = Playlist::state (full_state);
455 MidiPlaylist::dump () const
459 cerr << "Playlist \"" << _name << "\" " << endl
460 << regions.size() << " regions "
463 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
465 cerr << " " << r->name() << " @ " << r << " ["
466 << r->start() << "+" << r->length()
476 MidiPlaylist::destroy_region (Region* region)
478 MidiRegion* r = dynamic_cast<MidiRegion*> (region);
479 bool changed = false;
482 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
489 RegionLock rlock (this);
490 RegionList::iterator i;
491 RegionList::iterator tmp;
493 for (i = regions.begin(); i != regions.end(); ) {
498 if ((*i) == region) {
499 (*i)->unlock_sources ();
508 for (StateMap::iterator s = states.begin(); s != states.end(); ) {
509 StateMap::iterator tmp;
514 State* astate = dynamic_cast<State*> (*s);
516 list<UndoAction>::iterator rsi, rsitmp;
517 RegionList::iterator ri, ritmp;
519 for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
520 ri != astate->regions.end() && rsi != astate->region_states.end();) {
529 if (region == (*ri)) {
530 astate->regions.erase (ri);
531 astate->region_states.erase (rsi);
543 /* overload this, it normally means "removed", not destroyed */
544 notify_region_removed (region);
552 MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
554 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
556 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
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);
571 MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
573 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
575 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
577 if (ar && ar->region_list_equivalent (other)) {
578 results.push_back (ar);
584 MidiPlaylist::region_changed (Change what_changed, Region* region)
586 if (in_flush || in_set_state) {
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;
599 parent_wants_notify = Playlist::region_changed (what_changed, region);
601 maybe_save_state (_("region modified"));
603 if ((parent_wants_notify || (what_changed & our_interests))) {