* Add SysEx Support to MidiModel / SMF
authorHans Baier <hansfbaier@googlemail.com>
Fri, 6 Feb 2009 20:31:00 +0000 (20:31 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Fri, 6 Feb 2009 20:31:00 +0000 (20:31 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@4492 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/types.h
libs/ardour/event_type_map.cc
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/SMF.cpp
libs/evoral/src/Sequence.cpp

index f98b3d993a2e213bb65942879326d242d4e820f2..7532c63312e762215bfae821516ecafe981898c4 100644 (file)
@@ -91,6 +91,7 @@ namespace ARDOUR {
                MidiPgmChangeAutomation = 0x21,
                MidiPitchBenderAutomation = 0x22,
                MidiChannelPressureAutomation = 0x23,
+               MidiSystemExclusiveAutomation = 0x24,
                FadeInAutomation = 0x40,
                FadeOutAutomation = 0x80,
                EnvelopeAutomation = 0x100
index 3b4fefdceacc8a3cf3a6c6b5913d151bd93899e0..3f36aa6632eba6fc76a659d4451135fb27aa7323 100644 (file)
@@ -52,6 +52,7 @@ EventTypeMap::parameter_midi_type(const Evoral::Parameter& param) const
        case MidiPgmChangeAutomation:       return MIDI_CMD_PGM_CHANGE; break; 
        case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break; 
        case MidiPitchBenderAutomation:     return MIDI_CMD_BENDER; break; 
+       case MidiSystemExclusiveAutomation: return MIDI_CMD_COMMON_SYSEX; break;
        default: return 0;
        }
 }
@@ -64,6 +65,7 @@ EventTypeMap::midi_event_type(uint8_t status) const
        case MIDI_CMD_PGM_CHANGE:       return MidiPgmChangeAutomation; break;
        case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break;
        case MIDI_CMD_BENDER:           return MidiPitchBenderAutomation; break;
+       case MIDI_CMD_COMMON_SYSEX:     return MidiSystemExclusiveAutomation; break;
        default: return 0;
        }
 }
index b0d0d51e10bf734cde82312d8dd0bbb1bc536576..d78c69df5790664bf00818447c428bb677249de4 100644 (file)
@@ -111,6 +111,11 @@ public:
        inline       Notes& notes()       { return _notes; }
        inline const Notes& notes() const { return _notes; }
 
+       // useful for storing SysEx / Meta events
+       typedef std::vector< boost::shared_ptr< Event<T> > > SysExes;
+       inline       SysExes& sysexes()       { return _sysexes; }
+       inline const SysExes& sysexes() const { return _sysexes; }
+
        /** Read iterator */
        class const_iterator {
        public:
@@ -132,6 +137,8 @@ public:
 
        private:
                friend class Sequence<T>;
+               
+               enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX };
 
                const Sequence<T>*            _seq;
                boost::shared_ptr< Event<T> > _event;
@@ -145,17 +152,18 @@ public:
 
                typedef std::vector<ControlIterator> ControlIterators;
 
-               bool                           _is_end;
-               bool                           _locked;
-               typename Notes::const_iterator _note_iter;
-               ControlIterators               _control_iters;
-               ControlIterators::iterator     _control_iter;
+               bool                             _is_end;
+               bool                             _locked;
+               typename Notes::const_iterator   _note_iter;
+               typename SysExes::const_iterator _sysex_iter;
+               ControlIterators                 _control_iters;
+               ControlIterators::iterator       _control_iter;
        };
        
        const_iterator        begin(T t=0) const { return const_iterator(*this, t); }
        const const_iterator& end()        const { return _end_iter; }
        
-       void read_seek(T t)    { _read_iter = begin(t); }
+       void         read_seek(T t)    { _read_iter = begin(t); }
        T    read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; }
 
        bool control_to_midi_event(boost::shared_ptr< Event<T> >& ev,
@@ -184,6 +192,7 @@ private:
        void append_note_on_unlocked(uint8_t chan, T time, uint8_t note, uint8_t velocity);
        void append_note_off_unlocked(uint8_t chan, T time, uint8_t note);
        void append_control_unlocked(const Parameter& param, T time, double value);
+       void append_sysex_unlocked(const MIDIEvent<T>& ev);
 
        mutable Glib::RWLock _lock;
 
@@ -191,6 +200,8 @@ private:
        
        Notes _notes;
        
+       SysExes _sysexes;
+       
        typedef std::vector<size_t> WriteNotes;
        WriteNotes _write_notes[16];
        bool       _writing;
index f5fff726caccae38adaa59f803fe913a05d5dc53..3ea100fd39fda534747870757b8247e7fc2ba4bd 100644 (file)
@@ -249,10 +249,33 @@ SMF<T>::read_event(uint32_t* delta_t, uint32_t* size, uint8_t** buf) const
                }
        }
        
-       const int event_size = midi_event_size((unsigned char)status);
+       int event_size = midi_event_size((unsigned char)status);
        if (event_size <= 0) {
-               *size = 0;
-               return 0;
+               // if sysex, determine the size of the event
+               if ((status & 0xF0) == MIDI_CMD_COMMON_SYSEX) {
+                       fpos_t after_sysex_status_byte_position;
+                       int success = fgetpos(_fd, &after_sysex_status_byte_position);
+                       assert(success == 0);
+                       event_size = 1;
+                       
+                       while(true) {
+                               int byte = fgetc(_fd);
+                               if (byte == EOF) {
+                                       // premature end
+                                       return -1;
+                               }
+                               ++event_size;
+                               if ((byte & 0xff) == MIDI_CMD_COMMON_SYSEX_END) {
+                                       break;
+                               }
+                       }
+                       
+                       success = fsetpos(_fd, &after_sysex_status_byte_position);
+                       assert(success == 0);
+               } else {
+                       *size = 0;
+                       return 0;
+               }
        }
        
        // Make sure we have enough scratch buffer
@@ -265,11 +288,11 @@ SMF<T>::read_event(uint32_t* delta_t, uint32_t* size, uint8_t** buf) const
        if (event_size > 1)
                fread((*buf) + 1, 1, *size - 1, _fd);
 
-       /*printf("SMF %s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size);
+       printf("SMF read event: delta = %u, size = %u, data = ",  *delta_t, *size);
        for (size_t i=0; i < *size; ++i) {
                printf("%X ", (*buf)[i]);
        }
-       printf("\n");*/
+       printf("\n");
        
        return (int)*size;
 }
index 2fc7bfb39ac69b0a3aefefe9f86ab95911fbf456..cc102f6c228b88962a2a918baa6ddafc4df13877 100644 (file)
@@ -62,8 +62,8 @@ struct null_ostream : public std::ostream {
 };
 static null_ostream nullout;
 
-//static ostream& debugout = cout;
-static ostream& debugout = nullout;
+static ostream& debugout = cout;
+//static ostream& debugout = nullout;
 static ostream& errorout = cerr;
 
 // Read iterator (const_iterator)
@@ -91,6 +91,18 @@ Sequence<T>::const_iterator::const_iterator(const Sequence<T>& seq, T t)
                        break;
                }
        }
+       assert(_note_iter == seq.notes().end() || (*_note_iter)->time() >= t);
+       
+       // find first sysex which begins after t
+       _sysex_iter = seq.sysexes().end();
+       for (typename Sequence<T>::SysExes::const_iterator i = seq.sysexes().begin();
+                       i != seq.sysexes().end(); ++i) {
+               if ((*i)->time() >= t) {
+                       _sysex_iter = i;
+                       break;
+               }
+       }
+       assert(_sysex_iter == seq.sysexes().end() || (*_sysex_iter)->time() >= t);
 
        ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
 
@@ -130,19 +142,65 @@ Sequence<T>::const_iterator::const_iterator(const Sequence<T>& seq, T t)
                        // now _control_iter points to the last Element in _control_iters
                }
        }
+       
+#define MAKE_SURE_ADDING_SYSEXES_PRESERVES_OLD_SEMANTICS 1
+#if MAKE_SURE_ADDING_SYSEXES_PRESERVES_OLD_SEMANTICS
+       MIDIMessageType original_type       = NIL;
+       assert (!earliest_control.list || earliest_control.x >= t);
+       
+              if (_note_iter != seq.notes().end()
+                              && (*_note_iter)->on_event().time() >= t
+                              && (!earliest_control.list
+                                      || (*_note_iter)->on_event().time() < earliest_control.x)) {
+                  original_type = NOTE_ON;
+              } else {
+                  original_type = CONTROL;
+              }
+#endif
+       
+       MIDIMessageType type       = NIL;
+       T       earliest_t = t;
 
-       if (_note_iter != seq.notes().end()
-                       && (*_note_iter)->on_event().time() >= t
-                       && (!earliest_control.list
-                               || (*_note_iter)->on_event().time() < earliest_control.x)) {
-               debugout << "Reading note on event @ " << (*_note_iter)->on_event().time() << endl;
-               _event = boost::shared_ptr< Event<T> >(new Event<T>((*_note_iter)->on_event(), true));
+       // if the note comes before anything else set the iterator to the note
+       if (_note_iter != seq.notes().end() && (*_note_iter)->on_event().time() >= t) {
+               type = NOTE_ON;
+               earliest_t = (*_note_iter)->on_event().time();
+       }
+            
+       if (earliest_control.list && 
+           earliest_control.x >= t &&
+           earliest_control.x <= earliest_t) {
+               type = CONTROL;
+               earliest_t = earliest_control.x;
+       }
+       
+       if (_sysex_iter != seq.sysexes().end() &&
+           (*_sysex_iter)->time() >= t &&
+           (*_sysex_iter)->time() <= earliest_t) {
+               type = SYSEX;
+               earliest_t = (*_sysex_iter)->time();
+       }
+       
+#if MAKE_SURE_ADDING_SYSEXES_PRESERVES_OLD_SEMANTICS
+       assert (type == original_type || type == SYSEX);
+#endif
+       
+       if (type == NOTE_ON) {
+               debugout << "Reading note on event @ " << earliest_t << endl;
+               // initialize the event pointer with a new event
+               _event = boost::shared_ptr< Event<T> >(new Event<T>((*_note_iter)->on_event(), true));          
                _active_notes.push(*_note_iter);
                ++_note_iter;
                _control_iter = _control_iters.end();
-       } else if (earliest_control.list) {
-               debugout << "Reading control event @ " << earliest_control.x << endl;
+       } else if (type == CONTROL) {
+               debugout << "Reading control event @ " << earliest_t << endl;
                seq.control_to_midi_event(_event, earliest_control);
+       } else if (type == SYSEX) {
+               debugout << "Reading system exclusive event @ " << earliest_t << endl;
+               // initialize the event pointer with a new event
+               _event = boost::shared_ptr< Event<T> >(new Event<T>(*(*_sysex_iter), true));
+               ++_sysex_iter;
+               _control_iter = _control_iters.end();           
        }
 
        if ( (! _event.get()) || _event->size() == 0) {
@@ -189,11 +247,19 @@ Sequence<T>::const_iterator::operator++()
        //debugout << "const_iterator::operator++: " << _event->to_string() << endl;
 
        if (! (ev.is_note() || ev.is_cc() || ev.is_pgm_change()
-                               || ev.is_pitch_bender() || ev.is_channel_pressure()) ) {
+                               || ev.is_pitch_bender() || ev.is_channel_pressure() || ev.is_sysex()) ) {
                errorout << "Unknown event type: " << hex << int(ev.buffer()[0])
                        << int(ev.buffer()[1]) << int(ev.buffer()[2]) << endl;
        }
-       assert((ev.is_note() || ev.is_cc() || ev.is_pgm_change() || ev.is_pitch_bender() || ev.is_channel_pressure()));
+       
+       assert((
+               ev.is_note() || 
+               ev.is_cc() || 
+               ev.is_pgm_change() || 
+               ev.is_pitch_bender() || 
+               ev.is_channel_pressure() ||
+               ev.is_sysex()
+             ));
 
        // Increment past current control event
        if (!ev.is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) {
@@ -222,29 +288,36 @@ Sequence<T>::const_iterator::operator++()
                }
        }
 
-       enum Type { NIL, NOTE_ON, NOTE_OFF, CONTROL };
-
-       Type type = NIL;
-       T    t    = 0;
+       MIDIMessageType type       = NIL;
+       T       earliest_t = 0;
 
        // Next earliest note on
        if (_note_iter != _seq->notes().end()) {
                type = NOTE_ON;
-               t = (*_note_iter)->time();
+               earliest_t = (*_note_iter)->time();
        }
 
        // Use the next earliest note off iff it's earlier than the note on
        if (!_seq->percussive() && (! _active_notes.empty())) {
-               if (type == NIL || _active_notes.top()->end_time() <= t) {
+               if (type == NIL || _active_notes.top()->end_time() <= earliest_t) {
                        type = NOTE_OFF;
-                       t = _active_notes.top()->end_time();
+                       earliest_t = _active_notes.top()->end_time();
                }
        }
 
        // Use the next earliest controller iff it's earlier than the note event
        if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX) {
-               if (type == NIL || _control_iter->x < t) {
+               if (type == NIL || _control_iter->x <= earliest_t) {
                        type = CONTROL;
+                       earliest_t = _control_iter->x;
+               }
+       }
+       
+       // Use the next earliest SysEx iff it's earlier than the controller
+       if (_sysex_iter != _seq->sysexes().end()) {
+               if (type == NIL || (*_sysex_iter)->time() <= earliest_t) {
+                       type = SYSEX;
+                       earliest_t = (*_sysex_iter)->time();
                }
        }
 
@@ -260,6 +333,10 @@ Sequence<T>::const_iterator::operator++()
        } else if (type == CONTROL) {
                debugout << "Iterator = control" << endl;
                _seq->control_to_midi_event(_event, *_control_iter);
+       } else if (type == SYSEX) {
+               debugout << "Iterator = SysEx" << endl;
+               *_event =*(*_sysex_iter);
+               ++_sysex_iter;
        } else {
                debugout << "Iterator = End" << endl;
                _is_end = true;
@@ -294,6 +371,7 @@ Sequence<T>::const_iterator::operator=(const const_iterator& other)
        _is_end        = other._is_end;
        _locked        = other._locked;
        _note_iter     = other._note_iter;
+       _sysex_iter    = other._sysex_iter;
        _control_iters = other._control_iters;
        size_t index   = other._control_iter - other._control_iters.begin();
        _control_iter  = _control_iters.begin() + index;
@@ -388,6 +466,8 @@ Sequence<T>::control_to_midi_event(boost::shared_ptr< Event<T> >& ev, const Cont
 {
        assert(iter.list.get());
        const uint32_t event_type = iter.list->parameter().type();
+       
+       // initialize the event pointer with a new event, if necessary
        if (!ev) {
                ev = boost::shared_ptr< Event<T> >(new Event<T>(event_type, 0, 3, NULL, true));
        }
@@ -552,6 +632,8 @@ Sequence<T>::append(const Event<T>& event)
                                ev.velocity());
        } else if (ev.is_note_off()) {
                append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
+       } else if (ev.is_sysex()) {
+               append_sysex_unlocked(ev);
        } else if (!_type_map.type_is_midi(ev.event_type())) {
                printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
                for (size_t i=0; i < ev.size(); ++i) {
@@ -658,6 +740,20 @@ Sequence<T>::append_control_unlocked(const Parameter& param, T time, double valu
        c->list()->rt_add(time, value);
 }
 
+template<typename T>
+void
+Sequence<T>::append_sysex_unlocked(const MIDIEvent<T>& ev)
+{
+       debugout << this << " SysEx @ " << ev.time() << " \t= \t [ " << hex;
+       for (size_t i=0; i < ev.size(); ++i) {
+               debugout << int(ev.buffer()[i]) << " ";
+       }
+       debugout << "]" << endl;
+
+       boost::shared_ptr<MIDIEvent<T> > event(new MIDIEvent<T>(ev, true));
+       _sysexes.push_back(event);
+}
+
 template<typename T>
 void
 Sequence<T>::add_note_unlocked(const boost::shared_ptr< Note<T> > note)