From b7f3a6350783ffda019c22f74dd67e7d619b387b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 26 Jun 2006 20:29:45 +0000 Subject: Actually added the code mentioned in my last commit. Whoops. git-svn-id: svn://localhost/ardour2/branches/midi@643 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/midi_playlist.cc | 634 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 libs/ardour/midi_playlist.cc (limited to 'libs/ardour/midi_playlist.cc') diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc new file mode 100644 index 0000000000..007856e3a7 --- /dev/null +++ b/libs/ardour/midi_playlist.cc @@ -0,0 +1,634 @@ +/* + Copyright (C) 2006 Paul Davis + Written by Dave Robillard, 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "i18n.h" + +using namespace ARDOUR; +using namespace sigc; +using namespace std; + +MidiPlaylist::State::~State () +{} + +MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden) + : Playlist (session, node, hidden) +{ + in_set_state = true; + set_state (node); + in_set_state = false; + + save_state (_("initial state")); + + if (!hidden) { + PlaylistCreated (this); /* EMIT SIGNAL */ + } +} + +MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden) + : Playlist (session, name, hidden) +{ + save_state (_("initial state")); + + if (!hidden) { + PlaylistCreated (this); /* EMIT SIGNAL */ + } + +} + +MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden) + : Playlist (other, name, hidden) +{ + save_state (_("initial state")); + + /* + list::const_iterator in_o = other.regions.begin(); + list::iterator in_n = regions.begin(); + + while (in_o != other.regions.end()) { + MidiRegion *ar = dynamic_cast( (*in_o) ); + + for (list::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) { + if ( &(*xfades)->in() == ar) { + // We found one! Now copy it! + + list::const_iterator out_o = other.regions.begin(); + list::const_iterator out_n = regions.begin(); + + while (out_o != other.regions.end()) { + + MidiRegion *ar2 = dynamic_cast( (*out_o) ); + + if ( &(*xfades)->out() == ar2) { + MidiRegion *in = dynamic_cast( (*in_n) ); + MidiRegion *out = dynamic_cast( (*out_n) ); + Crossfade *new_fade = new Crossfade( *(*xfades), in, out); + add_crossfade(*new_fade); + break; + } + + out_o++; + out_n++; + } + // cerr << "HUH!? second region in the crossfade not found!" << endl; + } + } + + in_o++; + in_n++; + } +*/ + if (!hidden) { + PlaylistCreated (this); /* EMIT SIGNAL */ + } +} + +MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden) + : Playlist (other, start, cnt, name, hidden) +{ + save_state (_("initial state")); + + /* this constructor does NOT notify others (session) */ +} + +MidiPlaylist::~MidiPlaylist () +{ + set all_regions; + + GoingAway (this); + + /* find every region we've ever used, and add it to the set of + all regions. + */ + + for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) { + all_regions.insert (*x); + } + + for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { + + MidiPlaylist::State* apstate = dynamic_cast (*i); + + for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) { + all_regions.insert (*r); + } + + delete apstate; + } + + /* delete every region */ + + for (set::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { + (*ar)->unlock_sources (); + delete *ar; + } + +} + +struct RegionSortByLayer +{ + bool operator() (Region *a, Region *b) + { + return a->layer() < b->layer(); + } +}; + +jack_nframes_t +MidiPlaylist::read (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t start, + jack_nframes_t cnt, unsigned chan_n) +{ + jack_nframes_t ret = cnt; + jack_nframes_t end; + jack_nframes_t read_frames; + jack_nframes_t skip_frames; + + /* optimizing this memset() away involves a lot of conditionals + that may well cause more of a hit due to cache misses + and related stuff than just doing this here. + + it would be great if someone could measure this + at some point. + + one way or another, parts of the requested area + that are not written to by Region::region_at() + for all Regions that cover the area need to be + zeroed. + */ + + memset (buf, 0, sizeof (unsigned char) * cnt); + + /* this function is never called from a realtime thread, so + its OK to block (for short intervals). + */ + + Glib::Mutex::Lock rm (region_lock); + + end = start + cnt - 1; + + read_frames = 0; + skip_frames = 0; + _read_data_count = 0; + + map > relevant_regions; + vector relevant_layers; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->coverage (start, end) != OverlapNone) { + + relevant_regions[(*i)->layer()].push_back (*i); + relevant_layers.push_back ((*i)->layer()); + } + } + + // RegionSortByLayer layer_cmp; + // relevant_regions.sort (layer_cmp); + + + for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { + + // FIXME: Should be vector + vector& r (relevant_regions[*l]); + + for (vector::iterator i = r.begin(); i != r.end(); ++i) { + MidiRegion* const mr = dynamic_cast(*i); + assert(mr); + mr->read_at (buf, mixdown_buffer, workbuf, start, cnt, chan_n, read_frames, skip_frames); + _read_data_count += mr->read_data_count(); + } + + } + + return ret; +} + + +void +MidiPlaylist::remove_dependents (Region& region) +{ + MidiRegion* r = dynamic_cast (®ion); + + if (r == 0) { + PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist") + << endmsg; + return; + } + +} + + +void +MidiPlaylist::flush_notifications () +{ + Playlist::flush_notifications(); + + if (in_flush) { + return; + } + + in_flush = true; + + in_flush = false; +} + +void +MidiPlaylist::refresh_dependents (Region& r) +{ + MidiRegion* ar = dynamic_cast(&r); + + if (ar == 0) { + return; + } +} + +void +MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r) +{ + /* + MidiRegion *orig = dynamic_cast(o); + MidiRegion *left = dynamic_cast(l); + MidiRegion *right = dynamic_cast(r); + + for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { + Crossfades::iterator tmp; + tmp = x; + ++tmp; + + Crossfade *fade = 0; + + if ((*x)->_in == orig) { + if (! (*x)->covers(right->position())) { + fade = new Crossfade( *(*x), left, (*x)->_out); + } else { + // Overlap, the crossfade is copied on the left side of the right region instead + fade = new Crossfade( *(*x), right, (*x)->_out); + } + } + + if ((*x)->_out == orig) { + if (! (*x)->covers(right->position())) { + fade = new Crossfade( *(*x), (*x)->_in, right); + } else { + // Overlap, the crossfade is copied on the right side of the left region instead + fade = new Crossfade( *(*x), (*x)->_in, left); + } + } + + if (fade) { + _crossfades.remove( (*x) ); + add_crossfade (*fade); + } + x = tmp; + }*/ +} + +void +MidiPlaylist::check_dependents (Region& r, bool norefresh) +{ + MidiRegion* other; + MidiRegion* region; + MidiRegion* top; + MidiRegion* bottom; + + if (in_set_state || in_partition) { + return; + } + + if ((region = dynamic_cast (&r)) == 0) { + PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist") + << endmsg; + return; + } + + if (!norefresh) { + refresh_dependents (r); + } + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + other = dynamic_cast (*i); + + if (other == region) { + continue; + } + + if (other->muted() || region->muted()) { + continue; + } + + if (other->layer() < region->layer()) { + top = region; + bottom = other; + } else { + top = other; + bottom = region; + } + + } +} + + +int +MidiPlaylist::set_state (const XMLNode& node) +{ + /* + XMLNode *child; + XMLNodeList nlist; + XMLNodeConstIterator niter; + + if (!in_set_state) { + Playlist::set_state (node); + } + + nlist = node.children(); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + child = *niter; + + }*/ + + return 0; +} + +void +MidiPlaylist::drop_all_states () +{ + set all_regions; + + /* find every region we've ever used, and add it to the set of + all regions. same for xfades; + */ + + for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { + + MidiPlaylist::State* apstate = dynamic_cast (*i); + + for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) { + all_regions.insert (*r); + } + } + + /* now remove from the "all" lists every region that is in the current list. */ + + for (list::iterator i = regions.begin(); i != regions.end(); ++i) { + set + ::iterator x = all_regions.find (*i); + if (x != all_regions.end()) { + all_regions.erase (x); + } + } + + /* delete every region that is left - these are all things that are part of our "history" */ + + for (set + ::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { + (*ar)->unlock_sources (); + delete *ar; + } + + /* Now do the generic thing ... */ + + StateManager::drop_all_states (); +} + +StateManager::State* +MidiPlaylist::state_factory (std::string why) const +{ + State* state = new State (why); + + state->regions = regions; + state->region_states.clear (); + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + state->region_states.push_back ((*i)->get_memento()); + } + + return state; +} + +Change +MidiPlaylist::restore_state (StateManager::State& state) +{ + { + RegionLock rlock (this); + State* apstate = dynamic_cast (&state); + + in_set_state = true; + + regions = apstate->regions; + + for (list::iterator s = apstate-> + region_states.begin(); + s != apstate->region_states.end(); + ++s) { + (*s) (); + } + + in_set_state = false; + } + + notify_length_changed (); + return Change (~0); +} + +UndoAction +MidiPlaylist::get_memento () const +{ + return sigc::bind (mem_fun (*(const_cast (this)), &StateManager::use_state), _current_state_id); +} + + +XMLNode& +MidiPlaylist::state (bool full_state) +{ + XMLNode& node = Playlist::state (full_state); + + return node; +} + +void +MidiPlaylist::dump () const +{ + Region *r; + + cerr << "Playlist \"" << _name << "\" " << endl + << regions.size() << " regions " + << endl; + + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + r = *i; + cerr << " " << r->name() << " @ " << r << " [" + << r->start() << "+" << r->length() + << "] at " + << r->position() + << " on layer " + << r->layer () + << endl; + } +} + +bool +MidiPlaylist::destroy_region (Region* region) +{ + MidiRegion* r = dynamic_cast (region); + bool changed = false; + + if (r == 0) { + PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist") + << endmsg; + /*NOTREACHED*/ + return false; + } + + { + RegionLock rlock (this); + RegionList::iterator i; + RegionList::iterator tmp; + + for (i = regions.begin(); i != regions.end(); ) { + + tmp = i; + ++tmp; + + if ((*i) == region) { + (*i)->unlock_sources (); + regions.erase (i); + changed = true; + } + + i = tmp; + } + } + + for (StateMap::iterator s = states.begin(); s != states.end(); ) { + StateMap::iterator tmp; + + tmp = s; + ++tmp; + + State* astate = dynamic_cast (*s); + + list::iterator rsi, rsitmp; + RegionList::iterator ri, ritmp; + + for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); + ri != astate->regions.end() && rsi != astate->region_states.end();) { + + + ritmp = ri; + ++ritmp; + + rsitmp = rsi; + ++rsitmp; + + if (region == (*ri)) { + astate->regions.erase (ri); + astate->region_states.erase (rsi); + } + + ri = ritmp; + rsi = rsitmp; + } + + s = tmp; + } + + + if (changed) { + /* overload this, it normally means "removed", not destroyed */ + notify_region_removed (region); + } + + return changed; +} + + +void +MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector& results) +{ + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + MidiRegion* ar = dynamic_cast (*i); + + if (ar) { + if (Config->get_use_overlap_equivalency()) { + if (ar->overlap_equivalent (other)) { + results.push_back (ar); + } else if (ar->equivalent (other)) { + results.push_back (ar); + } + } + } + } +} + +void +MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector& results) +{ + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + MidiRegion* ar = dynamic_cast (*i); + + if (ar && ar->region_list_equivalent (other)) { + results.push_back (ar); + } + } +} + +bool +MidiPlaylist::region_changed (Change what_changed, Region* region) +{ + if (in_flush || in_set_state) { + return false; + } + + Change our_interests = Change (/*MidiRegion::FadeInChanged| + MidiRegion::FadeOutChanged| + MidiRegion::FadeInActiveChanged| + MidiRegion::FadeOutActiveChanged| + MidiRegion::EnvelopeActiveChanged| + MidiRegion::ScaleAmplitudeChanged| + MidiRegion::EnvelopeChanged*/); + bool parent_wants_notify; + + parent_wants_notify = Playlist::region_changed (what_changed, region); + + maybe_save_state (_("region modified")); + + if ((parent_wants_notify || (what_changed & our_interests))) { + notify_modified (); + } + + return true; +} + -- cgit v1.2.3