Fix MIDI region loading.

Add model loading and destroying to SMFSource.
Load and display MIDI region data on session load.


git-svn-id: svn://localhost/ardour2/trunk@1947 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2007-06-03 20:06:01 +00:00
parent 41c128155a
commit b0e91bfa08
14 changed files with 187 additions and 126 deletions

View file

@ -86,6 +86,14 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
_region->StateChanged.connect (mem_fun(*this, &MidiRegionView::region_changed)); _region->StateChanged.connect (mem_fun(*this, &MidiRegionView::region_changed));
set_colors (); set_colors ();
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (_region);
mr->midi_source(0)->load_model();
begin_write();
for (size_t i=0; i < mr->midi_source(0)->model().n_events(); ++i)
add_event(mr->midi_source(0)->model().event_at(i));
end_write();
} }
MidiRegionView::~MidiRegionView () MidiRegionView::~MidiRegionView ()

View file

@ -35,9 +35,14 @@ public:
MidiModel(size_t size=0); MidiModel(size_t size=0);
~MidiModel(); ~MidiModel();
void clear() { _events.clear(); }
/** Resizes vector if necessary (NOT realtime safe) */ /** Resizes vector if necessary (NOT realtime safe) */
void append(const MidiBuffer& data); void append(const MidiBuffer& data);
/** Resizes vector if necessary (NOT realtime safe) */
void append(const MidiEvent& ev);
inline const MidiEvent& event_at(unsigned i) const { return _events[i]; } inline const MidiEvent& event_at(unsigned i) const { return _events[i]; }
inline size_t n_events() const { return _events.size(); } inline size_t n_events() const { return _events.size(); }

View file

@ -57,7 +57,6 @@ public:
protected: protected:
/* playlist "callbacks" */ /* playlist "callbacks" */
void flush_notifications ();
void finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right); void finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right);
@ -66,7 +65,6 @@ protected:
void remove_dependents (boost::shared_ptr<Region> region); void remove_dependents (boost::shared_ptr<Region> region);
private: private:
XMLNode& state (bool full_state);
void dump () const; void dump () const;
bool region_changed (Change, boost::shared_ptr<Region>); bool region_changed (Change, boost::shared_ptr<Region>);

View file

@ -78,9 +78,6 @@ class MidiRegion : public Region
MidiRegion (boost::shared_ptr<MidiSource>, const XMLNode&); MidiRegion (boost::shared_ptr<MidiSource>, const XMLNode&);
MidiRegion (SourceList &, const XMLNode&); MidiRegion (SourceList &, const XMLNode&);
private:
friend class Playlist;
private: private:
nframes_t _read_at (const SourceList&, MidiRingBuffer& dst, nframes_t _read_at (const SourceList&, MidiRingBuffer& dst,
nframes_t position, nframes_t position,
@ -89,6 +86,10 @@ class MidiRegion : public Region
void recompute_at_start (); void recompute_at_start ();
void recompute_at_end (); void recompute_at_end ();
protected:
int set_live_state (const XMLNode&, Change&, bool send);
}; };
} /* namespace ARDOUR */ } /* namespace ARDOUR */

View file

@ -69,6 +69,9 @@ class MidiSource : public Source
XMLNode& get_state (); XMLNode& get_state ();
int set_state (const XMLNode&); int set_state (const XMLNode&);
virtual void load_model(bool lock=true) = 0;
virtual void destroy_model() = 0;
MidiModel& model() { return _model; } MidiModel& model() { return _model; }
protected: protected:

View file

@ -75,10 +75,11 @@ public:
protected: protected:
XMLNode& state (bool full); XMLNode& state (bool full);
int _set_state (const XMLNode&, bool call_base);
private: private:
int set_diskstream (boost::shared_ptr<MidiDiskstream> ds); int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
void set_state_part_two (); void set_state_part_two ();
void set_state_part_three (); void set_state_part_three ();
}; };

View file

@ -87,6 +87,9 @@ class SMFSource : public MidiSource {
int set_state (const XMLNode&); int set_state (const XMLNode&);
void seek_to(nframes_t time); void seek_to(nframes_t time);
void load_model(bool lock=true);
void destroy_model();
private: private:

View file

@ -63,3 +63,26 @@ MidiModel::append(const MidiBuffer& buf)
} }
} }
/** Append \a in_event to model. NOT (even remotely) realtime safe.
*
* Timestamps of events in \a buf are expected to be relative to
* the start of this model (t=0) and MUST be monotonically increasing
* and MUST be >= the latest event currently in the model.
*
* Events in buf are deep copied.
*/
void
MidiModel::append(const MidiEvent& in_event)
{
assert(_events.empty() || in_event.time >= _events.back().time);
_events.push_back(in_event);
MidiEvent& my_event = _events.back();
assert(my_event.time == in_event.time);
assert(my_event.size == in_event.size);
my_event.buffer = new Byte[my_event.size];
memcpy(my_event.buffer, in_event.buffer, my_event.size);
}

View file

@ -46,9 +46,9 @@ MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
const XMLProperty* prop = node.property("type"); const XMLProperty* prop = node.property("type");
assert(prop && DataType(prop->value()) == DataType::MIDI); assert(prop && DataType(prop->value()) == DataType::MIDI);
in_set_state = true; in_set_state++;
set_state (node); set_state (node);
in_set_state = false; in_set_state--;
} }
MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden) MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
@ -109,6 +109,10 @@ MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes
MidiPlaylist::~MidiPlaylist () MidiPlaylist::~MidiPlaylist ()
{ {
GoingAway (); /* EMIT SIGNAL */ GoingAway (); /* EMIT SIGNAL */
/* drop connections to signals */
notify_callbacks ();
} }
struct RegionSortByLayer { struct RegionSortByLayer {
@ -159,26 +163,14 @@ MidiPlaylist::read (MidiRingBuffer& dst, nframes_t start,
void void
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region) MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
{ {
/* MIDI regions have no dependents (crossfades) */
} }
void
MidiPlaylist::flush_notifications ()
{
Playlist::flush_notifications();
if (in_flush) {
return;
}
in_flush = true;
in_flush = false;
}
void void
MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> r) MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
{ {
/* MIDI regions have no dependents (crossfades) */
} }
void void
@ -226,38 +218,24 @@ MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> original, boost::
void void
MidiPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh) MidiPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
{ {
/* MIDI regions have no dependents (crossfades) */
} }
int int
MidiPlaylist::set_state (const XMLNode& node) MidiPlaylist::set_state (const XMLNode& node)
{ {
if (!in_set_state) { in_set_state++;
Playlist::set_state (node); freeze ();
}
// Actually Charles, I don't much care for children Playlist::set_state (node);
/*
XMLNodeList nlist = node.children();
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { thaw();
in_set_state--;
XMLNode* const child = *niter;
}*/
return 0; return 0;
} }
XMLNode&
MidiPlaylist::state (bool full_state)
{
XMLNode& node = Playlist::state (full_state);
return node;
}
void void
MidiPlaylist::dump () const MidiPlaylist::dump () const
{ {

View file

@ -31,6 +31,7 @@
#include <pbd/basename.h> #include <pbd/basename.h>
#include <pbd/xml++.h> #include <pbd/xml++.h>
#include <pbd/enumwriter.h>
#include <ardour/midi_region.h> #include <ardour/midi_region.h>
#include <ardour/session.h> #include <ardour/session.h>
@ -174,29 +175,18 @@ XMLNode&
MidiRegion::state (bool full) MidiRegion::state (bool full)
{ {
XMLNode& node (Region::state (full)); XMLNode& node (Region::state (full));
XMLNode *child;
char buf[64]; char buf[64];
char buf2[64]; char buf2[64];
LocaleGuard lg (X_("POSIX")); LocaleGuard lg (X_("POSIX"));
snprintf (buf, sizeof (buf), "0x%x", (int) _flags); node.add_property ("flags", enum_2_string (_flags));
node.add_property ("flags", buf);
for (uint32_t n=0; n < _sources.size(); ++n) { for (uint32_t n=0; n < _sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "source-%d", n); snprintf (buf2, sizeof(buf2), "source-%d", n);
_sources[n]->id().print (buf, sizeof(buf)); _sources[n]->id().print (buf, sizeof(buf));
node.add_property (buf2, buf); node.add_property (buf2, buf);
} }
snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
node.add_property ("channels", buf);
child = node.add_child ("Envelope");
if ( ! full) {
child->add_property ("default", "yes");
}
if (full && _extra_xml) { if (full && _extra_xml) {
node.add_child_copy (*_extra_xml); node.add_child_copy (*_extra_xml);
} }
@ -205,32 +195,52 @@ MidiRegion::state (bool full)
} }
int 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; const XMLProperty *prop;
LocaleGuard lg (X_("POSIX")); 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) { 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 child items */ if ((old_flags ^ _flags) & Muted) {
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) { what_changed = Change (what_changed|MuteChanged);
}
XMLNode *child; if ((old_flags ^ _flags) & Opaque) {
//XMLProperty *prop; what_changed = Change (what_changed|OpacityChanged);
}
child = (*niter); if ((old_flags ^ _flags) & Locked) {
what_changed = Change (what_changed|LockChanged);
/** Hello, children */ }
if (send) {
send_change (what_changed);
} }
return 0; 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 void
MidiRegion::recompute_at_end () MidiRegion::recompute_at_end ()
{ {

View file

@ -21,6 +21,8 @@
#include <sigc++/retype_return.h> #include <sigc++/retype_return.h>
#include <sigc++/bind.h> #include <sigc++/bind.h>
#include <pbd/enumwriter.h>
#include <ardour/midi_track.h> #include <ardour/midi_track.h>
#include <ardour/midi_diskstream.h> #include <ardour/midi_diskstream.h>
#include <ardour/session.h> #include <ardour/session.h>
@ -72,10 +74,7 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo
MidiTrack::MidiTrack (Session& sess, const XMLNode& node) MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
: Track (sess, node) : Track (sess, node)
{ {
_freeze_record.state = NoFreeze; _set_state(node, false);
set_state (node);
_declickable = true;
_saved_meter_point = _meter_point;
set_input_minimum(ChanCount(DataType::MIDI, 1)); set_input_minimum(ChanCount(DataType::MIDI, 1));
set_input_maximum(ChanCount(DataType::MIDI, 1)); set_input_maximum(ChanCount(DataType::MIDI, 1));
@ -140,23 +139,22 @@ MidiTrack::midi_diskstream() const
int int
MidiTrack::set_state (const XMLNode& node) MidiTrack::set_state (const XMLNode& node)
{
return _set_state (node, true);
}
int
MidiTrack::_set_state (const XMLNode& node, bool call_base)
{ {
const XMLProperty *prop; const XMLProperty *prop;
XMLNodeConstIterator iter; XMLNodeConstIterator iter;
if (Route::set_state (node)) { if (Route::_set_state (node, call_base)) {
return -1; return -1;
} }
if ((prop = node.property (X_("mode"))) != 0) { if ((prop = node.property (X_("mode"))) != 0) {
if (prop->value() == X_("normal")) { _mode = TrackMode (string_2_enum (prop->value(), _mode));
_mode = Normal;
} else if (prop->value() == X_("destructive")) {
_mode = Destructive;
} else {
warning << string_compose ("unknown midi track mode \"%1\" seen and ignored", prop->value()) << endmsg;
_mode = Normal;
}
} else { } else {
_mode = Normal; _mode = Normal;
} }
@ -193,12 +191,9 @@ MidiTrack::set_state (const XMLNode& node)
for (niter = nlist.begin(); niter != nlist.end(); ++niter){ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
child = *niter; child = *niter;
if (child->name() == X_("remote_control")) { if (child->name() == X_("recenable")) {
if ((prop = child->property (X_("id"))) != 0) { _rec_enable_control.set_state (*child);
int32_t x; _session.add_controllable (&_rec_enable_control);
sscanf (prop->value().c_str(), "%d", &x);
set_remote_control_id (x);
}
} }
} }
@ -221,8 +216,7 @@ MidiTrack::state(bool full_state)
freeze_node = new XMLNode (X_("freeze-info")); freeze_node = new XMLNode (X_("freeze-info"));
freeze_node->add_property ("playlist", _freeze_record.playlist->name()); freeze_node->add_property ("playlist", _freeze_record.playlist->name());
snprintf (buf, sizeof (buf), "%d", (int) _freeze_record.state); freeze_node->add_property ("state", enum_2_string (_freeze_record.state));
freeze_node->add_property ("state", buf);
for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) { for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) {
inode = new XMLNode (X_("insert")); inode = new XMLNode (X_("insert"));
@ -239,31 +233,12 @@ MidiTrack::state(bool full_state)
/* Alignment: act as a proxy for the diskstream */ /* Alignment: act as a proxy for the diskstream */
XMLNode* align_node = new XMLNode (X_("alignment")); XMLNode* align_node = new XMLNode (X_("alignment"));
switch (_diskstream->alignment_style()) { AlignStyle as = _diskstream->alignment_style ();
case ExistingMaterial: align_node->add_property (X_("style"), enum_2_string (as));
snprintf (buf, sizeof (buf), X_("existing"));
break;
case CaptureTime:
snprintf (buf, sizeof (buf), X_("capture"));
break;
}
align_node->add_property (X_("style"), buf);
root.add_child_nocopy (*align_node); root.add_child_nocopy (*align_node);
XMLNode* remote_control_node = new XMLNode (X_("remote_control")); root.add_property (X_("mode"), enum_2_string (_mode));
snprintf (buf, sizeof (buf), "%d", _remote_control_id);
remote_control_node->add_property (X_("id"), buf);
root.add_child_nocopy (*remote_control_node);
switch (_mode) {
case Normal:
root.add_property (X_("mode"), X_("normal"));
break;
case Destructive:
root.add_property (X_("mode"), X_("destructive"));
break;
}
/* we don't return diskstream state because we don't /* we don't return diskstream state because we don't
own the diskstream exclusively. control of the diskstream own the diskstream exclusively. control of the diskstream
state is ceded to the Session, even if we create the state is ceded to the Session, even if we create the
@ -272,6 +247,8 @@ MidiTrack::state(bool full_state)
_diskstream->id().print (buf, sizeof(buf)); _diskstream->id().print (buf, sizeof(buf));
root.add_property ("diskstream-id", buf); root.add_property ("diskstream-id", buf);
root.add_child_nocopy (_rec_enable_control.get_state());
return root; return root;
} }
@ -314,7 +291,7 @@ MidiTrack::set_state_part_two ()
} }
if ((prop = fnode->property (X_("state"))) != 0) { if ((prop = fnode->property (X_("state"))) != 0) {
_freeze_record.state = (FreezeState) atoi (prop->value().c_str()); _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state));
} }
XMLNodeConstIterator citer; XMLNodeConstIterator citer;
@ -341,11 +318,21 @@ MidiTrack::set_state_part_two ()
if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) { if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) {
if ((prop = fnode->property (X_("style"))) != 0) { if ((prop = fnode->property (X_("style"))) != 0) {
if (prop->value() == "existing") {
_diskstream->set_persistent_align_style (ExistingMaterial); /* fix for older sessions from before EnumWriter */
} else if (prop->value() == "capture") {
_diskstream->set_persistent_align_style (CaptureTime); string pstr;
if (prop->value() == "capture") {
pstr = "CaptureTime";
} else if (prop->value() == "existing") {
pstr = "ExistingMaterial";
} else {
pstr = prop->value();
} }
AlignStyle as = AlignStyle (string_2_enum (pstr, as));
_diskstream->set_persistent_align_style (as);
} }
} }
return; return;

View file

@ -24,6 +24,8 @@
#include <ardour/playlist.h> #include <ardour/playlist.h>
#include <ardour/audioplaylist.h> #include <ardour/audioplaylist.h>
#include <ardour/audio_track.h> #include <ardour/audio_track.h>
#include <ardour/midi_playlist.h>
#include <ardour/midi_track.h>
#include <ardour/tempo.h> #include <ardour/tempo.h>
#include <ardour/audiosource.h> #include <ardour/audiosource.h>
#include <ardour/audioregion.h> #include <ardour/audioregion.h>
@ -93,11 +95,11 @@ Session::memento_command_factory(XMLNode *n)
return new MementoCommand<Locations>(_locations, before, after); return new MementoCommand<Locations>(_locations, before, after);
} else if (obj_T == typeid (TempoMap).name()) { } else if (obj_T == typeid (TempoMap).name()) {
return new MementoCommand<TempoMap>(*_tempo_map, before, after); return new MementoCommand<TempoMap>(*_tempo_map, before, after);
} else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) { } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name() || obj_T == typeid (MidiPlaylist).name()) {
if (boost::shared_ptr<Playlist> pl = playlist_by_name(child->property("name")->value())) { if (boost::shared_ptr<Playlist> pl = playlist_by_name(child->property("name")->value())) {
return new MementoCommand<Playlist>(*(pl.get()), before, after); return new MementoCommand<Playlist>(*(pl.get()), before, after);
} }
} else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) { } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name() || obj_T == typeid(MidiTrack).name()) {
return new MementoCommand<Route>(*route_by_id(id), before, after); return new MementoCommand<Route>(*route_by_id(id), before, after);
} else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) { } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) {
if (automation_lists.count(id)) if (automation_lists.count(id))

View file

@ -1110,7 +1110,7 @@ Session::set_state (const XMLNode& node)
/* Object loading order: /* Object loading order:
MIDI MIDI Control
Path Path
extra extra
Options/Config Options/Config
@ -1397,7 +1397,6 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
nchans = atoi (prop->value().c_str()); nchans = atoi (prop->value().c_str());
} }
if ((prop = node.property ("name")) == 0) { if ((prop = node.property ("name")) == 0) {
cerr << "no name for this region\n"; cerr << "no name for this region\n";
abort (); abort ();
@ -1461,7 +1460,6 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
} }
} }
return region; return region;
} }
@ -1477,7 +1475,7 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
const XMLProperty* prop; const XMLProperty* prop;
boost::shared_ptr<Source> source; boost::shared_ptr<Source> source;
boost::shared_ptr<MidiSource> ms; boost::shared_ptr<MidiSource> ms;
MidiRegion::SourceList sources; SourceList sources;
uint32_t nchans = 1; uint32_t nchans = 1;
if (node.name() != X_("Region")) { if (node.name() != X_("Region")) {
@ -1487,6 +1485,11 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
if ((prop = node.property (X_("channels"))) != 0) { if ((prop = node.property (X_("channels"))) != 0) {
nchans = atoi (prop->value().c_str()); nchans = atoi (prop->value().c_str());
} }
if ((prop = node.property ("name")) == 0) {
cerr << "no name for this region\n";
abort ();
}
// Multiple midi channels? that's just crazy talk // Multiple midi channels? that's just crazy talk
assert(nchans == 1); assert(nchans == 1);
@ -1515,6 +1518,17 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
try { try {
boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node))); boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node)));
/* a final detail: this is the one and only place that we know how long missing files are */
if (region->whole_file()) {
for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
if (sfp) {
sfp->set_length (region->length());
}
}
}
return region; return region;
} }
@ -1534,8 +1548,6 @@ Session::get_sources_as_xml ()
node->add_child_nocopy (i->second->get_state()); node->add_child_nocopy (i->second->get_state());
} }
/* XXX get MIDI and other sources here */
return *node; return *node;
} }

View file

@ -754,3 +754,33 @@ SMFSource::read_var_len() const
return value; return value;
} }
void
SMFSource::load_model(bool lock)
{
if (lock)
Glib::Mutex::Lock lm (_lock);
_model.clear();
fseek(_fd, _header_size, 0);
nframes_t time = 0;
MidiEvent ev;
int ret;
while ((ret = read_event(ev)) >= 0) {
time += ev.time;
ev.time = time;
if (ret > 0) { // didn't skip (meta) event
_model.append(ev);
}
}
}
void
SMFSource::destroy_model()
{
_model.clear();
}