X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_region.cc;h=26eb0e5054b0e9226220df4629a7f6f773423143;hb=4ee42dc455ce5015801a5db7a2b86a5a71a8ec25;hp=e9d75e3d068a1644bcfd0998c07400340067706c;hpb=b7f3a6350783ffda019c22f74dd67e7d619b387b;p=ardour.git diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index e9d75e3d06..26eb0e5054 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -1,6 +1,5 @@ /* - Copyright (C) 2006 Paul Davis - Written by Dave Robillard, 2006 + Copyright (C) 2000-2006 Paul Davis 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 @@ -15,6 +14,8 @@ 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. + + $Id: midiregion.cc 746 2006-08-02 02:44:23Z drobilla $ */ #include @@ -30,6 +31,7 @@ #include #include +#include #include #include @@ -37,6 +39,8 @@ #include #include #include +#include +#include #include "i18n.h" #include @@ -44,273 +48,106 @@ using namespace std; using namespace ARDOUR; -MidiRegionState::MidiRegionState (string why) - : RegionState (why) +/** Basic MidiRegion constructor (one channel) */ +MidiRegion::MidiRegion (boost::shared_ptr src, nframes_t start, nframes_t length) + : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::MIDI, 0, Region::Flag(Region::DefaultFlags|Region::External)) { + assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } -MidiRegion::MidiRegion (MidiSource& src, jack_nframes_t start, jack_nframes_t length, bool announce) - : Region (start, length, PBD::basename_nosuffix(src.name()), 0, Region::Flag(Region::DefaultFlags|Region::External)) +/* Basic MidiRegion constructor (one channel) */ +MidiRegion::MidiRegion (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) + : Region (src, start, length, name, DataType::MIDI, layer, flags) { - /* basic MidiRegion constructor */ - - sources.push_back (&src); - master_sources.push_back (&src); - src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - - save_state ("initial state"); - - if (announce) { - CheckNewRegion (this); /* EMIT SIGNAL */ - } + assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } -MidiRegion::MidiRegion (MidiSource& src, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags, bool announce) - : Region (start, length, name, layer, flags) +/* Basic MidiRegion constructor (many channels) */ +MidiRegion::MidiRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) + : Region (srcs, start, length, name, DataType::MIDI, layer, flags) { - /* basic MidiRegion constructor */ - - sources.push_back (&src); - master_sources.push_back (&src); - src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - - save_state ("initial state"); - - if (announce) { - CheckNewRegion (this); /* EMIT SIGNAL */ - } + assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } -MidiRegion::MidiRegion (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags, bool announce) - : Region (start, length, name, layer, flags) -{ - /* basic MidiRegion constructor */ -#if 0 - for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { - sources.push_back (*i); - master_sources.push_back (*i); - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - } +/** Create a new MidiRegion, that is part of an existing one */ +MidiRegion::MidiRegion (boost::shared_ptr other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags) + : Region (other, offset, length, name, layer, flags) { - /* create a new MidiRegion, that is part of an existing one */ - - set unique_srcs; - - for (SourceList::const_iterator i= other.sources.begin(); i != other.sources.end(); ++i) { - sources.push_back (*i); - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - unique_srcs.insert (*i); - } - - for (SourceList::const_iterator i = other.master_sources.begin(); i != other.master_sources.end(); ++i) { - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - } - master_sources.push_back (*i); - } - - save_state ("initial state"); - - if (announce) { - CheckNewRegion (this); /* EMIT SIGNAL */ - } -#endif + assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } -MidiRegion::MidiRegion (const MidiRegion &other) +MidiRegion::MidiRegion (boost::shared_ptr other) : Region (other) { - /* Pure copy constructor */ - - set unique_srcs; - - for (SourceList::const_iterator i = other.sources.begin(); i != other.sources.end(); ++i) { - sources.push_back (*i); - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - unique_srcs.insert (*i); - } - - for (SourceList::const_iterator i = other.master_sources.begin(); i != other.master_sources.end(); ++i) { - master_sources.push_back (*i); - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - } - } - - save_state ("initial state"); - - /* NOTE: no CheckNewRegion signal emitted here. This is the copy constructor */ + assert(_name.find("/") == string::npos); + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); } -MidiRegion::MidiRegion (MidiSource& src, const XMLNode& node) - : Region (node) +MidiRegion::MidiRegion (boost::shared_ptr src, const XMLNode& node) + : Region (src, node) { - sources.push_back (&src); - master_sources.push_back (&src); - src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - if (set_state (node)) { throw failed_constructor(); } - save_state ("initial state"); - - CheckNewRegion (this); /* EMIT SIGNAL */ + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); + assert(_name.find("/") == string::npos); + assert(_type == DataType::MIDI); } -MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node) - : Region (node) +MidiRegion::MidiRegion (const SourceList& srcs, const XMLNode& node) + : Region (srcs, node) { - /* basic MidiRegion constructor */ - - set unique_srcs; - - for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { - sources.push_back (*i); - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - unique_srcs.insert (*i); - } - - for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) { - master_sources.push_back (*i); - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted)); - } - } - if (set_state (node)) { throw failed_constructor(); } - save_state ("initial state"); - - CheckNewRegion (this); /* EMIT SIGNAL */ + midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source)); + assert(_name.find("/") == string::npos); + assert(_type == DataType::MIDI); } MidiRegion::~MidiRegion () { - GoingAway (this); -} - -StateManager::State* -MidiRegion::state_factory (std::string why) const -{ - MidiRegionState* state = new MidiRegionState (why); - - Region::store_state (*state); - - return state; -} - -Change -MidiRegion::restore_state (StateManager::State& sstate) -{ - MidiRegionState* state = dynamic_cast (&sstate); - - Change what_changed = Region::restore_and_return_flags (*state); - - if (_flags != Flag (state->_flags)) { - - //uint32_t old_flags = _flags; - - _flags = Flag (state->_flags); - - } - - /* XXX need a way to test stored state versus current for envelopes */ - - what_changed = Change (what_changed); - - return what_changed; -} - -UndoAction -MidiRegion::get_memento() const -{ - return sigc::bind (mem_fun (*(const_cast (this)), &StateManager::use_state), _current_state_id); } -bool -MidiRegion::verify_length (jack_nframes_t len) +nframes_t +MidiRegion::read_at (MidiRingBuffer& out, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const { - for (uint32_t n=0; n < sources.size(); ++n) { - if (_start > sources[n]->length() - len) { - return false; - } - } - return true; -} - -bool -MidiRegion::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length) -{ - for (uint32_t n=0; n < sources.size(); ++n) { - if (new_length > sources[n]->length() - new_start) { - return false; - } - } - return true; -} -bool -MidiRegion::verify_start (jack_nframes_t pos) -{ - for (uint32_t n=0; n < sources.size(); ++n) { - if (pos > sources[n]->length() - _length) { - return false; - } - } - return true; -} - -bool -MidiRegion::verify_start_mutable (jack_nframes_t& new_start) -{ - for (uint32_t n=0; n < sources.size(); ++n) { - if (new_start > sources[n]->length() - _length) { - new_start = sources[n]->length() - _length; - } - } - return true; + return _read_at (_sources, out, position, dur, chan_n, mode); } -jack_nframes_t -MidiRegion::read_at (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t position, - jack_nframes_t cnt, - uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const +nframes_t +MidiRegion::master_read_at (MidiRingBuffer& out, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const { - return _read_at (sources, buf, mixdown_buffer, workbuf, position, cnt, chan_n, read_frames, skip_frames); + return _read_at (_master_sources, out, position, dur, chan_n, mode); } -jack_nframes_t -MidiRegion::master_read_at (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t position, - jack_nframes_t cnt, uint32_t chan_n) const +nframes_t +MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const { - return _read_at (master_sources, buf, mixdown_buffer, workbuf, position, cnt, chan_n, 0, 0); -} + //cerr << _name << "._read_at(" << position << ") - " << _position << " duration: " << dur << endl; -jack_nframes_t -MidiRegion::_read_at (const SourceList& srcs, unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, - jack_nframes_t position, jack_nframes_t cnt, - uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const -{ - jack_nframes_t internal_offset; - jack_nframes_t buf_offset; - jack_nframes_t to_read; + nframes_t internal_offset = 0; + nframes_t src_offset = 0; + nframes_t to_read = 0; /* precondition: caller has verified that we cover the desired section */ - if (chan_n >= sources.size()) { - return 0; /* read nothing */ - } + assert(chan_n == 0); if (position < _position) { internal_offset = 0; - buf_offset = _position - position; - cnt -= buf_offset; + src_offset = _position - position; + dur -= src_offset; } else { internal_offset = position - _position; - buf_offset = 0; + src_offset = 0; } if (internal_offset >= _length) { @@ -318,16 +155,12 @@ MidiRegion::_read_at (const SourceList& srcs, unsigned char *buf, unsigned char } - if ((to_read = min (cnt, _length - internal_offset)) == 0) { + if ((to_read = min (dur, _length - internal_offset)) == 0) { return 0; /* read nothing */ } - if (opaque()) { - /* overwrite whatever is there */ - mixdown_buffer = buf + buf_offset; - } else { - mixdown_buffer += buf_offset; - } + // FIXME: non-opaque MIDI regions not yet supported + assert(opaque()); if (muted()) { return 0; /* read nothing */ @@ -335,53 +168,50 @@ MidiRegion::_read_at (const SourceList& srcs, unsigned char *buf, unsigned char _read_data_count = 0; - if (srcs[chan_n]->read (mixdown_buffer, _start + internal_offset, to_read, workbuf) != to_read) { + boost::shared_ptr src = midi_source(chan_n); + src->set_note_mode(mode); + + if (src->midi_read ( + // the destination buffer + dst, + // where to start reading in the region + _start + internal_offset, + // how many bytes + to_read, + // the offset in the output buffer + _position - _start + ) != to_read) { return 0; /* "read nothing" */ } - _read_data_count += srcs[chan_n]->read_data_count(); + _read_data_count += src->read_data_count(); - if (!opaque()) { - - /* gack. the things we do for users. - */ - - buf += buf_offset; - - for (jack_nframes_t n = 0; n < to_read; ++n) { - buf[n] += mixdown_buffer[n]; - } - } - return to_read; } -XMLNode& -MidiRegion::get_state () -{ - return state (true); -} - XMLNode& MidiRegion::state (bool full) { XMLNode& node (Region::state (full)); - //XMLNode *child; char buf[64]; char buf2[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof (buf), "0x%x", (int) _flags); - node.add_property ("flags", buf); + node.add_property ("flags", enum_2_string (_flags)); - for (uint32_t n=0; n < sources.size(); ++n) { + // XXX these should move into Region + + for (uint32_t n=0; n < _sources.size(); ++n) { snprintf (buf2, sizeof(buf2), "source-%d", n); - snprintf (buf, sizeof(buf), "%" PRIu64, sources[n]->id()); + _sources[n]->id().print (buf, sizeof(buf)); node.add_property (buf2, buf); } - snprintf (buf, sizeof (buf), "%u", (uint32_t) sources.size()); - node.add_property ("channels", buf); + for (uint32_t n=0; n < _master_sources.size(); ++n) { + snprintf (buf2, sizeof(buf2), "master-source-%d", n); + _master_sources[n]->id().print (buf, sizeof (buf)); + node.add_property (buf2, buf); + } if (full && _extra_xml) { node.add_child_copy (*_extra_xml); @@ -391,41 +221,84 @@ MidiRegion::state (bool full) } int -MidiRegion::set_state (const XMLNode& node) +MidiRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send) { - const XMLNodeList& nlist = node.children(); const XMLProperty *prop; LocaleGuard lg (X_("POSIX")); - Region::set_state (node); + Region::set_live_state (node, what_changed, false); + uint32_t old_flags = _flags; + if ((prop = node.property ("flags")) != 0) { - _flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16)); + _flags = Flag (string_2_enum (prop->value(), _flags)); + + //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16)); _flags = Flag (_flags & ~Region::LeftOfSplit); _flags = Flag (_flags & ~Region::RightOfSplit); } - /* Now find envelope description and other misc child items */ - - for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { - - XMLNode *child; - //XMLProperty *prop; - - child = (*niter); + if ((old_flags ^ _flags) & Muted) { + what_changed = Change (what_changed|MuteChanged); + } + if ((old_flags ^ _flags) & Opaque) { + what_changed = Change (what_changed|OpacityChanged); + } + if ((old_flags ^ _flags) & Locked) { + what_changed = Change (what_changed|LockChanged); + } + + if (send) { + send_change (what_changed); } return 0; } +int +MidiRegion::set_state (const XMLNode& node) +{ + /* Region::set_state() calls the virtual set_live_state(), + which will get us back to AudioRegion::set_live_state() + to handle the relevant stuff. + */ + + return Region::set_state (node); +} + +void +MidiRegion::recompute_at_end () +{ + /* our length has changed + * (non destructively) "chop" notes that pass the end boundary, to + * prevent stuck notes. + */ +} + +void +MidiRegion::recompute_at_start () +{ + /* as above, but the shift was from the front + * maybe bump currently active note's note-ons up so they sound here? + * that could be undesireable in certain situations though.. maybe + * remove the note entirely, including it's note off? something needs to + * be done to keep the played MIDI sane to avoid messing up voices of + * polyhonic things etc........ + */ +} + int MidiRegion::separate_by_channel (Session& session, vector& v) const { + // Separate by MIDI channel? bit different from audio since this is separating based + // on the actual contained data and destructively modifies and creates new sources.. + +#if 0 SourceList srcs; string new_name; - for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) { + for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) { srcs.clear (); srcs.push_back (*i); @@ -440,208 +313,37 @@ MidiRegion::separate_by_channel (Session& session, vector& v) const v.push_back (new MidiRegion (srcs, _start, _length, new_name, _layer, _flags)); } +#endif - return 0; -} - -void -MidiRegion::source_deleted (Source* ignored) -{ - delete this; -} - -void -MidiRegion::lock_sources () -{ - SourceList::iterator i; - set unique_srcs; - - for (i = sources.begin(); i != sources.end(); ++i) { - unique_srcs.insert (*i); - (*i)->use (); - } - - for (i = master_sources.begin(); i != master_sources.end(); ++i) { - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->use (); - } - } -} - -void -MidiRegion::unlock_sources () -{ - SourceList::iterator i; - set unique_srcs; - - for (i = sources.begin(); i != sources.end(); ++i) { - unique_srcs.insert (*i); - (*i)->release (); - } - - for (i = master_sources.begin(); i != master_sources.end(); ++i) { - if (unique_srcs.find (*i) == unique_srcs.end()) { - (*i)->release (); - } - } -} - -vector -MidiRegion::master_source_names () -{ - SourceList::iterator i; - - vector names; - for (i = master_sources.begin(); i != master_sources.end(); ++i) { - names.push_back((*i)->name()); - } - - return names; -} - -bool -MidiRegion::region_list_equivalent (const MidiRegion& other) const -{ - return size_equivalent (other) && source_equivalent (other) && _name == other._name; -} - -bool -MidiRegion::source_equivalent (const MidiRegion& other) const -{ - SourceList::const_iterator i; - SourceList::const_iterator io; - - for (i = sources.begin(), io = other.sources.begin(); i != sources.end() && io != other.sources.end(); ++i, ++io) { - if ((*i)->id() != (*io)->id()) { - return false; - } - } - - for (i = master_sources.begin(), io = other.master_sources.begin(); i != master_sources.end() && io != other.master_sources.end(); ++i, ++io) { - if ((*i)->id() != (*io)->id()) { - return false; - } - } - - return true; -} - -bool -MidiRegion::overlap_equivalent (const MidiRegion& other) const -{ - return coverage (other.first_frame(), other.last_frame()) != OverlapNone; -} - -bool -MidiRegion::equivalent (const MidiRegion& other) const -{ - return _start == other._start && - _position == other._position && - _length == other._length; -} - -bool -MidiRegion::size_equivalent (const MidiRegion& other) const -{ - return _start == other._start && - _length == other._length; + // Actually, I would prefer not if that's alright + return -1; } -#if 0 int -MidiRegion::exportme (Session& session, AudioExportSpecification& spec) +MidiRegion::exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&) { - const jack_nframes_t blocksize = 4096; - jack_nframes_t to_read; - int status = -1; - - spec.channels = sources.size(); - - if (spec.prepare (blocksize, session.frame_rate())) { - goto out; - } - - spec.pos = 0; - spec.total_frames = _length; - - while (spec.pos < _length && !spec.stop) { - - - /* step 1: interleave */ - - to_read = min (_length - spec.pos, blocksize); - - if (spec.channels == 1) { - - if (sources.front()->read (spec.dataF, _start + spec.pos, to_read, 0) != to_read) { - goto out; - } - - } else { - - Sample buf[blocksize]; - - for (uint32_t chan = 0; chan < spec.channels; ++chan) { - - if (sources[chan]->read (buf, _start + spec.pos, to_read, 0) != to_read) { - goto out; - } - - for (jack_nframes_t x = 0; x < to_read; ++x) { - spec.dataF[chan+(x*spec.channels)] = buf[x]; - } - } - } - - if (spec.process (to_read)) { - goto out; - } - - spec.pos += to_read; - spec.progress = (double) spec.pos /_length; - - } - - status = 0; - - out: - spec.running = false; - spec.status = status; - spec.clear(); - - return status; + return -1; } -#endif -Region* -MidiRegion::get_parent() +boost::shared_ptr +MidiRegion::midi_source (uint32_t n) const { -#if 0 - Region* r = 0; - - if (_playlist) { - r = _playlist->session().find_whole_file_parent (*this); - } - - return r; -#endif - return NULL; + // Guaranteed to succeed (use a static cast?) + return boost::dynamic_pointer_cast(source(n)); } -bool -MidiRegion::speed_mismatch (float sr) const +void +MidiRegion::switch_source(boost::shared_ptr src) { -#if 0 - if (sources.empty()) { - /* impossible, but ... */ - return false; - } + boost::shared_ptr msrc = boost::dynamic_pointer_cast(src); + if (!msrc) + return; - float fsr = sources.front()->sample_rate(); + // MIDI regions have only one source + _sources.clear(); + _sources.push_back(msrc); - return fsr == sr; -#endif - return false; + set_name(msrc->name()); }