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));
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 ()

View file

@ -35,9 +35,14 @@ public:
MidiModel(size_t size=0);
~MidiModel();
void clear() { _events.clear(); }
/** Resizes vector if necessary (NOT realtime safe) */
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 size_t n_events() const { return _events.size(); }

View file

@ -57,7 +57,6 @@ public:
protected:
/* playlist "callbacks" */
void flush_notifications ();
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);
private:
XMLNode& state (bool full_state);
void dump () const;
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 (SourceList &, const XMLNode&);
private:
friend class Playlist;
private:
nframes_t _read_at (const SourceList&, MidiRingBuffer& dst,
nframes_t position,
@ -89,6 +86,10 @@ class MidiRegion : public Region
void recompute_at_start ();
void recompute_at_end ();
protected:
int set_live_state (const XMLNode&, Change&, bool send);
};
} /* namespace ARDOUR */

View file

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

View file

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

View file

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

View file

@ -31,6 +31,7 @@
#include <pbd/basename.h>
#include <pbd/xml++.h>
#include <pbd/enumwriter.h>
#include <ardour/midi_region.h>
#include <ardour/session.h>
@ -174,29 +175,18 @@ 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) {
snprintf (buf2, sizeof(buf2), "source-%d", n);
_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);
child = node.add_child ("Envelope");
if ( ! full) {
child->add_property ("default", "yes");
}
if (full && _extra_xml) {
node.add_child_copy (*_extra_xml);
}
@ -205,32 +195,52 @@ 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 child items */
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode *child;
//XMLProperty *prop;
child = (*niter);
/** Hello, children */
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 ()
{

View file

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

View file

@ -24,6 +24,8 @@
#include <ardour/playlist.h>
#include <ardour/audioplaylist.h>
#include <ardour/audio_track.h>
#include <ardour/midi_playlist.h>
#include <ardour/midi_track.h>
#include <ardour/tempo.h>
#include <ardour/audiosource.h>
#include <ardour/audioregion.h>
@ -93,11 +95,11 @@ Session::memento_command_factory(XMLNode *n)
return new MementoCommand<Locations>(_locations, before, after);
} else if (obj_T == typeid (TempoMap).name()) {
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())) {
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);
} else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) {
if (automation_lists.count(id))

View file

@ -1110,7 +1110,7 @@ Session::set_state (const XMLNode& node)
/* Object loading order:
MIDI
MIDI Control
Path
extra
Options/Config
@ -1397,7 +1397,6 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
nchans = atoi (prop->value().c_str());
}
if ((prop = node.property ("name")) == 0) {
cerr << "no name for this region\n";
abort ();
@ -1461,7 +1460,6 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
}
}
return region;
}
@ -1477,7 +1475,7 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
const XMLProperty* prop;
boost::shared_ptr<Source> source;
boost::shared_ptr<MidiSource> ms;
MidiRegion::SourceList sources;
SourceList sources;
uint32_t nchans = 1;
if (node.name() != X_("Region")) {
@ -1487,6 +1485,11 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
if ((prop = node.property (X_("channels"))) != 0) {
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
assert(nchans == 1);
@ -1515,6 +1518,17 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
try {
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;
}
@ -1534,8 +1548,6 @@ Session::get_sources_as_xml ()
node->add_child_nocopy (i->second->get_state());
}
/* XXX get MIDI and other sources here */
return *node;
}

View file

@ -754,3 +754,33 @@ SMFSource::read_var_len() const
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();
}