Less crash-happy MIDI reading on weird MIDI files.
authorDavid Robillard <d@drobilla.net>
Tue, 19 Feb 2008 03:24:44 +0000 (03:24 +0000)
committerDavid Robillard <d@drobilla.net>
Tue, 19 Feb 2008 03:24:44 +0000 (03:24 +0000)
Make "show existing automation" create/show automation tracks for all contained CC in MIDI tracks.
Fix staggered time when importing multi-track MIDI files.

git-svn-id: svn://localhost/ardour2/branches/3.0@3086 d708f5d6-7413-0410-9779-e7cbd77b26cf

12 files changed:
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_audio_import.cc
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/midi_time_axis.h
libs/ardour/ardour/midi_playlist.h
libs/ardour/ardour/smf_reader.h
libs/ardour/automation_event.cc
libs/ardour/import.cc
libs/ardour/midi_model.cc
libs/ardour/midi_playlist.cc
libs/ardour/smf_reader.cc
libs/ardour/smf_source.cc

index 14439f54a6354fede94cb3d2db368d1e16d68a7a..4d4ab99046904cdd89810ac68970367dafd5febc 100644 (file)
@@ -529,7 +529,7 @@ Editor::register_actions ()
 
        /* the next two are duplicate items with different names for use in two different contexts */
 
-       ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Media"), mem_fun (*this, &Editor::external_audio_dialog));
+       ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Import Existing Media"), mem_fun (*this, &Editor::external_audio_dialog));
 
        act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
        ActionManager::session_sensitive_actions.push_back (act);
index 3e1cf4f6485cf4dd88448b4e2083173b8ed90989..e13eb81181d5a58264da75c8ab443455af99f080 100644 (file)
@@ -734,7 +734,9 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
                }
        }
 
-       cout << "TARGET REGIONS: " << target_regions << endl;
+       // kludge (for MIDI we're abusing "channel" for "track" here)
+       if (paths.front().rfind(".mid") != Glib::ustring::npos)
+               target_regions = -1;
 
        if (target_regions == 1) {
 
index cb72363f8857cede3f947a373c9fe619f2c40e52..2c7429c85a5aa8540d24e14c2ae4849b5d44f2a5 100644 (file)
@@ -262,7 +262,8 @@ MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
 
 
 void
-MidiTimeAxisView::update_range() {
+MidiTimeAxisView::update_range()
+{
        MidiGhostRegion* mgr;
 
        for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
@@ -272,6 +273,20 @@ MidiTimeAxisView::update_range() {
        }
 }
 
+void
+MidiTimeAxisView::show_existing_automation ()
+{
+       if (midi_track()) {
+               const set<Parameter> params = midi_track()->midi_diskstream()->
+                               midi_playlist()->contained_automation();
+
+               for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i)
+                       create_automation_child(*i, true);
+       }
+
+       RouteTimeAxisView::show_existing_automation ();
+}
+
 /** Prompt for a controller with a dialog and add an automation track for it
  */
 void
@@ -298,7 +313,8 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
 {
        if (param.type() == MidiCCAutomation) {
        
-               /* FIXME: don't create AutomationList for track itself */
+               /* FIXME: don't create AutomationList for track itself
+                * (not actually needed or used, since the automation is region-ey) */
 
                boost::shared_ptr<AutomationControl> c = _route->control(param);
 
@@ -308,6 +324,10 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
                        _route->add_control(c);
                }
 
+               AutomationTracks::iterator existing = _automation_tracks.find(param);
+               if (existing != _automation_tracks.end())
+                       return;
+
                boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
                                _route, _route, c,
                                editor,
index efc987760b153c9e570adb04119156d5c90848f0..a91bd01d216909078a72a920369b05332c4762d0 100644 (file)
@@ -65,6 +65,7 @@ class MidiTimeAxisView : public RouteTimeAxisView
        guint32 show_at (double y, int& nth, Gtk::VBox *parent);
        void hide ();
 
+       void show_existing_automation ();
        void add_controller_track ();
        void create_automation_child (ARDOUR::Parameter param, bool show);
 
index 5838f5addd0cfd1bd698a328568e664a38d082f6..dcc202bbf4edc735b7753bd003e34332713e47cc 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <ardour/ardour.h>
 #include <ardour/playlist.h>
+#include <ardour/parameter.h>
 
 namespace ARDOUR
 {
@@ -56,6 +57,8 @@ public:
        
        void set_note_mode (NoteMode m) { _note_mode = m; }
 
+       std::set<Parameter> contained_automation();
+
 protected:
 
        /* playlist "callbacks" */
index 806844004e9296a8e289f0e5670d41c0b65a7aef..a96ef6bf7f87272637837c82b910dc86d15d6218 100644 (file)
@@ -65,13 +65,13 @@ public:
                throw (std::logic_error, PrematureEOF, CorruptFile);
        
        void close();
+       
+       static uint32_t read_var_len(FILE* fd) throw (PrematureEOF);
 
 protected:
        /** size of SMF header, including MTrk chunk header */
        static const uint32_t HEADER_SIZE = 22;
 
-       uint32_t read_var_len() const throw(PrematureEOF);
-
        std::string _filename;
        FILE*       _fd;
        //TimeUnit    _unit;
index 3a80dddc76b59d3f7c769e67d5081a9a9051c6d8..ccf3c35104920d5bd590d7c5bedf5ed5bf905298 100644 (file)
@@ -1029,9 +1029,9 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
 {
        /* Only do the range lookup if x is in a different range than last time
         * this was called (or if the search cache has been marked "dirty" (left<0) */
-       if ((_search_cache.left < 0) ||
+       if (!_events.empty() && ((_search_cache.left < 0) ||
                        ((_search_cache.left > start) ||
-                        (_search_cache.right < end))) {
+                        (_search_cache.right < end)))) {
 
                const ControlEvent start_point (start, 0);
                const ControlEvent end_point (end, 0);
@@ -1140,7 +1140,9 @@ AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end
 {
        //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
 
-       if (_events.size() < 2)
+       if (_events.size() == 0)
+               return false;
+       else if (_events.size() == 1)
                return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
 
        // Hack to avoid infinitely repeating the same event
index b028e1c1a7cdf03dcd702c1a5c4080162341e2f5..60fb5eddb878ac88a1799efa49bdf1453a963d17 100644 (file)
@@ -270,10 +270,6 @@ write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
 {
        MidiEvent ev(0.0, 4, NULL, true);
 
-       uint64_t t       = 0;
-       uint32_t delta_t = 0;
-       uint32_t size    = 0;
-
        status.progress = 0.0f;
 
        try {
@@ -283,6 +279,10 @@ write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
                boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]);
                
                source->seek_to_track(i);
+       
+               uint64_t t       = 0;
+               uint32_t delta_t = 0;
+               uint32_t size    = 0;
                
                while (!status.cancel) {
 
index 00fcd4402129b072ac4c31ce872e8c792a92fe86..44416713f8d3b8f2322870aac09f2d89c0a7bb09 100644 (file)
@@ -276,12 +276,6 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
                //cerr << "Using cached iterator at " << _next_read << endl;
        }
 
-       if (_read_iter == end()) {
-               //cerr << this << " MM::read: at end @ " << _read_iter->time() << endl;
-       } else {
-               //cerr << this << " MM::read: at " << _read_iter->time() << endl;
-       }
-
        _next_read = start + nframes;
 
        while (_read_iter != end() && _read_iter->time() < start + nframes) {
index d8e59efd4aa3caff86a2f84e4a77539adf6c81ba..d258d495247db4b54aba0418ee9399b69976b1dd 100644 (file)
@@ -265,6 +265,30 @@ MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
        return changed;
 }
 
+set<Parameter>
+MidiPlaylist::contained_automation()
+{
+       /* this function is never called from a realtime thread, so
+          its OK to block (for short intervals).
+       */
+
+       Glib::Mutex::Lock rm (region_lock);
+
+       set<Parameter> ret;
+
+       for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
+               boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
+
+               for (Automatable::Controls::iterator c = mr->controls().begin();
+                               c != mr->controls().end(); ++c) {
+                       ret.insert(c->first);
+               }
+       }
+
+       return ret;
+}
+
+
 bool
 MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
 {
index 21ca370e6cd5e69b7789f27cc009f1fb73d025da..20204f1d32d09779c6b73116e15cf31358c82305 100644 (file)
@@ -194,7 +194,7 @@ SMFReader::read_event(size_t    buf_len,
        static uint8_t  last_status = 0;
        static uint32_t last_size   = 0;
 
-       *delta_time = read_var_len();
+       *delta_time = read_var_len(_fd);
        int status = fgetc(_fd);
        if (status == EOF)
                throw PrematureEOF();
@@ -222,7 +222,7 @@ SMFReader::read_event(size_t    buf_len,
                if (feof(_fd))
                        throw PrematureEOF();
                uint8_t type = fgetc(_fd);
-               const uint32_t size = read_var_len();
+               const uint32_t size = read_var_len(_fd);
                /*cerr.flags(ios::hex);
                cerr << "SMF - meta 0x" << (int)type << ", size = ";
                cerr.flags(ios::dec);
@@ -269,20 +269,20 @@ SMFReader::close()
 
 
 uint32_t
-SMFReader::read_var_len() const throw(PrematureEOF)
+SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
 {
-       if (feof(_fd))
+       if (feof(fd))
                throw PrematureEOF();
 
        uint32_t value;
        uint8_t  c;
 
-       if ( (value = getc(_fd)) & 0x80 ) {
+       if ( (value = getc(fd)) & 0x80 ) {
                value &= 0x7F;
                do {
-                       if (feof(_fd))
+                       if (feof(fd))
                                throw PrematureEOF();
-                       value = (value << 7) + ((c = getc(_fd)) & 0x7F);
+                       value = (value << 7) + ((c = getc(fd)) & 0x7F);
                } while (c & 0x80);
        }
 
index 4903cf384f2229e6b0b01bf4d8efa0377b596d9c..85cebf2b09ba8300236a62a05dec4c7ad6d9718b 100644 (file)
@@ -38,6 +38,7 @@
 #include <ardour/midi_util.h>
 #include <ardour/tempo.h>
 #include <ardour/audioengine.h>
+#include <ardour/smf_reader.h>
 
 #include "i18n.h"
 
@@ -272,20 +273,31 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const
        assert(size);
        assert(buf);
 
-       *delta_t = read_var_len();
-       assert(!feof(_fd));
+       try {
+               *delta_t = SMFReader::read_var_len(_fd);
+       } catch (...) {
+               return -1; // Premature EOF
+       }
+       
+       if (feof(_fd)) {
+               return -1; // Premature EOF
+       }
 
        const int status = fgetc(_fd);
-       assert(status != EOF); // FIXME die gracefully
+
+       if (status == EOF) {
+               return -1; // Premature EOF
+       }
 
        //printf("Status @ %X = %X\n", (unsigned)ftell(_fd) - 1, status);
 
        if (status == 0xFF) {
-               assert(!feof(_fd));
+               if (feof(_fd)) {
+                       return -1; // Premature EOF
+               }
                const int type = fgetc(_fd);
                if ((unsigned char)type == 0x2F) {
-                       //cerr << _name << " hit EOT" << endl;
-                       return -1;
+                       return -1; // hit end of track
                } else {
                        *size = 0;
                        return 0;
@@ -444,12 +456,12 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
 void
 SMFSource::append_event_unlocked(const MidiEvent& ev)
 {
-       printf("%s - append chan = %u, time = %lf, size = %u, data = ", _path.c_str(),
+       /*printf("%s - append chan = %u, time = %lf, size = %u, data = ", _path.c_str(),
                        (unsigned)ev.channel(), ev.time(), ev.size());
        for (size_t i=0; i < ev.size(); ++i) {
                printf("%X ", ev.buffer()[i]);
        }
-       printf("\n");
+       printf("\n");*/
 
        assert(ev.time() >= 0);
        assert(ev.time() >= _last_ev_time);
@@ -838,25 +850,6 @@ SMFSource::write_var_len(uint32_t value)
        return ret;
 }
 
-uint32_t
-SMFSource::read_var_len() const
-{
-       assert(!feof(_fd));
-
-       uint32_t value;
-       unsigned char c;
-
-       if ( (value = getc(_fd)) & 0x80 ) {
-               value &= 0x7F;
-               do {
-                       assert(!feof(_fd));
-                       value = (value << 7) + ((c = getc(_fd)) & 0x7F);
-               } while (c & 0x80);
-       }
-
-       return value;
-}
-
 void
 SMFSource::load_model(bool lock, bool force_reload)
 {