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>
33 #include <ardour/midi_ring_buffer.h>
35 #include <pbd/error.h>
39 using namespace ARDOUR;
43 MidiPlaylist::State::~State ()
46 MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
47 : Playlist (session, node, DataType::MIDI, hidden)
49 const XMLProperty* prop = node.property("type");
50 assert(prop && DataType(prop->value()) == DataType::MIDI);
56 save_state (_("initial state"));
59 PlaylistCreated (this); /* EMIT SIGNAL */
63 MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
64 : Playlist (session, name, DataType::MIDI, hidden)
66 save_state (_("initial state"));
69 PlaylistCreated (this); /* EMIT SIGNAL */
74 MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden)
75 : Playlist (other, name, hidden)
78 save_state (_("initial state"));
81 list<Region*>::const_iterator in_o = other.regions.begin();
82 list<Region*>::iterator in_n = regions.begin();
84 while (in_o != other.regions.end()) {
85 MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
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!
91 list<Region*>::const_iterator out_o = other.regions.begin();
92 list<Region*>::const_iterator out_n = regions.begin();
94 while (out_o != other.regions.end()) {
96 MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
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);
109 // cerr << "HUH!? second region in the crossfade not found!" << endl;
118 PlaylistCreated (this); /* EMIT SIGNAL */
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)
125 save_state (_("initial state"));
127 /* this constructor does NOT notify others (session) */
130 MidiPlaylist::~MidiPlaylist ()
132 set <Region*> all_regions;
136 /* find every region we've ever used, and add it to the set of
140 for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
141 all_regions.insert (*x);
144 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
146 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
148 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
149 all_regions.insert (*r);
155 /* delete every region */
157 for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
158 (*ar)->unlock_sources ();
164 struct RegionSortByLayer
166 bool operator() (Region *a, Region *b)
168 return a->layer() < b->layer();
172 /** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
174 MidiPlaylist::read (MidiRingBuffer& dst, jack_nframes_t start,
175 jack_nframes_t dur, unsigned chan_n)
177 /* this function is never called from a realtime thread, so
178 its OK to block (for short intervals).
181 Glib::Mutex::Lock rm (region_lock);
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;
188 //_read_data_count = 0;
190 vector<MidiRegion*> regs; // relevent regions overlapping start <--> end
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) {
199 RegionSortByLayer layer_cmp;
200 sort(regs.begin(), regs.end(), layer_cmp);
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();
208 _read_data_count += ret;
216 MidiPlaylist::remove_dependents (Region& region)
218 MidiRegion* r = dynamic_cast<MidiRegion*> (®ion);
221 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
230 MidiPlaylist::flush_notifications ()
232 Playlist::flush_notifications();
244 MidiPlaylist::refresh_dependents (Region& r)
246 MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
254 MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
256 throw; // I don't wanna
258 MidiRegion *orig = dynamic_cast<MidiRegion*>(o);
259 MidiRegion *left = dynamic_cast<MidiRegion*>(l);
260 MidiRegion *right = dynamic_cast<MidiRegion*>(r);
262 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
263 Crossfades::iterator tmp;
269 if ((*x)->_in == orig) {
270 if (! (*x)->covers(right->position())) {
271 fade = new Crossfade( *(*x), left, (*x)->_out);
273 // Overlap, the crossfade is copied on the left side of the right region instead
274 fade = new Crossfade( *(*x), right, (*x)->_out);
278 if ((*x)->_out == orig) {
279 if (! (*x)->covers(right->position())) {
280 fade = new Crossfade( *(*x), (*x)->_in, right);
282 // Overlap, the crossfade is copied on the right side of the left region instead
283 fade = new Crossfade( *(*x), (*x)->_in, left);
288 _crossfades.remove( (*x) );
289 add_crossfade (*fade);
296 MidiPlaylist::check_dependents (Region& r, bool norefresh)
303 if (in_set_state || in_partition) {
307 if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
308 PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
314 refresh_dependents (r);
317 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
319 other = dynamic_cast<MidiRegion*> (*i);
321 if (other == region) {
325 if (other->muted() || region->muted()) {
329 if (other->layer() < region->layer()) {
342 MidiPlaylist::set_state (const XMLNode& node)
345 Playlist::set_state (node);
348 // Actually Charles, I don't much care for children
351 XMLNodeList nlist = node.children();
353 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
355 XMLNode* const child = *niter;
363 MidiPlaylist::drop_all_states ()
365 set<Region*> all_regions;
367 /* find every region we've ever used, and add it to the set of
368 all regions. same for xfades;
371 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
373 MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
375 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
376 all_regions.insert (*r);
380 /* now remove from the "all" lists every region that is in the current list. */
382 for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
384 <Region*>::iterator x = all_regions.find (*i);
385 if (x != all_regions.end()) {
386 all_regions.erase (x);
390 /* delete every region that is left - these are all things that are part of our "history" */
392 for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
393 (*ar)->unlock_sources ();
397 /* Now do the generic thing ... */
399 StateManager::drop_all_states ();
403 MidiPlaylist::state_factory (std::string why) const
405 State* state = new State (why);
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());
417 MidiPlaylist::restore_state (StateManager::State& state)
420 RegionLock rlock (this);
421 State* apstate = dynamic_cast<State*> (&state);
425 regions = apstate->regions;
427 for (list<UndoAction>::iterator s = apstate->
428 region_states.begin();
429 s != apstate->region_states.end();
434 in_set_state = false;
437 notify_length_changed ();
442 MidiPlaylist::get_memento () const
444 return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
449 MidiPlaylist::state (bool full_state)
451 XMLNode& node = Playlist::state (full_state);
457 MidiPlaylist::dump () const
461 cerr << "Playlist \"" << _name << "\" " << endl
462 << regions.size() << " regions "
465 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
467 cerr << " " << r->name() << " @ " << r << " ["
468 << r->start() << "+" << r->length()
478 MidiPlaylist::destroy_region (Region* region)
480 MidiRegion* r = dynamic_cast<MidiRegion*> (region);
481 bool changed = false;
484 PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
491 RegionLock rlock (this);
492 RegionList::iterator i;
493 RegionList::iterator tmp;
495 for (i = regions.begin(); i != regions.end(); ) {
500 if ((*i) == region) {
501 (*i)->unlock_sources ();
510 for (StateMap::iterator s = states.begin(); s != states.end(); ) {
511 StateMap::iterator tmp;
516 State* astate = dynamic_cast<State*> (*s);
518 list<UndoAction>::iterator rsi, rsitmp;
519 RegionList::iterator ri, ritmp;
521 for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
522 ri != astate->regions.end() && rsi != astate->region_states.end();) {
531 if (region == (*ri)) {
532 astate->regions.erase (ri);
533 astate->region_states.erase (rsi);
545 /* overload this, it normally means "removed", not destroyed */
546 notify_region_removed (region);
554 MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
556 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
558 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
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);
573 MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
575 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
577 MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
579 if (ar && ar->region_list_equivalent (other)) {
580 results.push_back (ar);
586 MidiPlaylist::region_changed (Change what_changed, Region* region)
588 if (in_flush || in_set_state) {
592 // Feeling rather uninterested today, but thanks for the heads up anyway!
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;
603 parent_wants_notify = Playlist::region_changed (what_changed, region);
605 maybe_save_state (_("region modified"));
607 if ((parent_wants_notify || (what_changed & our_interests))) {