Fix MIDI region loading.
authorDavid Robillard <d@drobilla.net>
Sun, 3 Jun 2007 20:06:01 +0000 (20:06 +0000)
committerDavid Robillard <d@drobilla.net>
Sun, 3 Jun 2007 20:06:01 +0000 (20:06 +0000)
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

14 files changed:
gtk2_ardour/midi_region_view.cc
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_playlist.h
libs/ardour/ardour/midi_region.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/smf_source.h
libs/ardour/midi_model.cc
libs/ardour/midi_playlist.cc
libs/ardour/midi_region.cc
libs/ardour/midi_track.cc
libs/ardour/session_command.cc
libs/ardour/session_state.cc
libs/ardour/smf_source.cc

index fe2947bc42261678e787604ec1a56d597c519ae4..c3a7e1830dfbd5c6f7c464d0f12641d47fc8890a 100644 (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 ()
index 5d343d7fa91e2bc509d0c5d1417856bc3c7e16a6..509504a472f6db8d63f02b144ae7a6941e1dcb2e 100644 (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(); }
index 44f04b781b2c5810a18bf007d43721d5ee7cb997..3162debbd5489a1eb13211dc9d54f4c09ae27dee 100644 (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>);
index 2b783a92a8cd77438ee041c5fb34fc411b230961..83487e456a6065bae0fa661cb040a8238125a669 100644 (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 */
index 40ef87f8c9741931eba0b9a167e63a9089e338ae..fd5aa15376a33a77072e256395d771e17f885aa9 100644 (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:
index 0dad40a973b996d27bbb343bd1b8c6f6693ec95e..c88878c16764ce21a8a74e3c9d61fe8d1ce25799 100644 (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 ();
 };
index 6ab81a2ac39182d07e6a8834acb959cb7cea1210..ed1a0edb7671a16323de5d788975912649c0854d 100644 (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:
 
index 1509b5c86665dc4fadb05c591ff6a2762463cbdd..f12d91ba8a66cb2d73abe513afc17ec15097a68e 100644 (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);
+}
+
index f99ef299a76ffe6fb1c8ef4ee298153fa445242e..0aa37790347c4fb12840673cb100ccef549f4316 100644 (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);
-       }
-
-       // Actually Charles, I don't much care for children
-       
-       /*
-       XMLNodeList nlist = node.children();
-
-       for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+       in_set_state++;
+       freeze ();
 
-               XMLNode* const child = *niter;
+       Playlist::set_state (node);
 
-       }*/
+       thaw();
+       in_set_state--;
 
        return 0;
 }
 
-XMLNode&
-MidiPlaylist::state (bool full_state)
-{
-       XMLNode& node = Playlist::state (full_state);
-
-       return node;
-}
-
 void
 MidiPlaylist::dump () const
 {
index 27861677fd47a7ca2a3a52757c3dbf1cc5ff4e24..860d0ec67271b4fd86e9066d9126f4d207906ab5 100644 (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 ()
 {
index 226476e380762944e67db016157a48d2da99d428..cf08cc9072716feaa4c2b13928a371b1b6fd6827 100644 (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;
index 10f319ace8c68903c15910089be2fc18600fa61a..534f2ffe81a4dbd2f239d6bc14996b6848cccf4c 100644 (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))
index e2043899c5f4406c52d356b46f34c68e61ae166d..285e3fd4c45edb8d08b8c818eb5284519a16a51f 100644 (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;
 }
 
index 061a2f12344ee80df2627073312c61a54e2b1c42..e48e89a6e5802fdd7ec1fbdec818a626d23cd876 100644 (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();
+}
+