From 3d594b460b1d0fdbc6799ded15201174d9a2c03c Mon Sep 17 00:00:00 2001 From: Hans Baier Date: Fri, 6 Feb 2009 20:31:00 +0000 Subject: [PATCH 1/1] * Add SysEx Support to MidiModel / SMF git-svn-id: svn://localhost/ardour2/branches/3.0@4492 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/types.h | 1 + libs/ardour/event_type_map.cc | 2 + libs/evoral/evoral/Sequence.hpp | 23 ++++-- libs/evoral/src/SMF.cpp | 33 ++++++-- libs/evoral/src/Sequence.cpp | 136 +++++++++++++++++++++++++++----- 5 files changed, 164 insertions(+), 31 deletions(-) diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index f98b3d993a..7532c63312 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -91,6 +91,7 @@ namespace ARDOUR { MidiPgmChangeAutomation = 0x21, MidiPitchBenderAutomation = 0x22, MidiChannelPressureAutomation = 0x23, + MidiSystemExclusiveAutomation = 0x24, FadeInAutomation = 0x40, FadeOutAutomation = 0x80, EnvelopeAutomation = 0x100 diff --git a/libs/ardour/event_type_map.cc b/libs/ardour/event_type_map.cc index 3b4fefdcea..3f36aa6632 100644 --- a/libs/ardour/event_type_map.cc +++ b/libs/ardour/event_type_map.cc @@ -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; } } diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index b0d0d51e10..d78c69df57 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -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 > > 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; + + enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX }; const Sequence* _seq; boost::shared_ptr< Event > _event; @@ -145,17 +152,18 @@ public: typedef std::vector 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 >& 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& ev); mutable Glib::RWLock _lock; @@ -191,6 +200,8 @@ private: Notes _notes; + SysExes _sysexes; + typedef std::vector WriteNotes; WriteNotes _write_notes[16]; bool _writing; diff --git a/libs/evoral/src/SMF.cpp b/libs/evoral/src/SMF.cpp index f5fff726ca..3ea100fd39 100644 --- a/libs/evoral/src/SMF.cpp +++ b/libs/evoral/src/SMF.cpp @@ -249,10 +249,33 @@ SMF::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::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; } diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp index 2fc7bfb39a..cc102f6c22 100644 --- a/libs/evoral/src/Sequence.cpp +++ b/libs/evoral/src/Sequence.cpp @@ -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::const_iterator::const_iterator(const Sequence& 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::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(), DBL_MAX, 0.0); @@ -130,19 +142,65 @@ Sequence::const_iterator::const_iterator(const Sequence& 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 >(new Event((*_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 >(new Event((*_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 >(new Event(*(*_sysex_iter), true)); + ++_sysex_iter; + _control_iter = _control_iters.end(); } if ( (! _event.get()) || _event->size() == 0) { @@ -189,11 +247,19 @@ Sequence::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::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::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::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::control_to_midi_event(boost::shared_ptr< Event >& 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 >(new Event(event_type, 0, 3, NULL, true)); } @@ -552,6 +632,8 @@ Sequence::append(const Event& 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::append_control_unlocked(const Parameter& param, T time, double valu c->list()->rt_add(time, value); } +template +void +Sequence::append_sysex_unlocked(const MIDIEvent& 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 > event(new MIDIEvent(ev, true)); + _sysexes.push_back(event); +} + template void Sequence::add_note_unlocked(const boost::shared_ptr< Note > note) -- 2.30.2