summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/types.h1
-rw-r--r--libs/ardour/event_type_map.cc2
-rw-r--r--libs/evoral/evoral/Sequence.hpp23
-rw-r--r--libs/evoral/src/SMF.cpp33
-rw-r--r--libs/evoral/src/Sequence.cpp136
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<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;
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<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;
}
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<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) {
@@ -660,6 +742,20 @@ Sequence<T>::append_control_unlocked(const Parameter& param, T time, double valu
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)
{
debugout << this << " add note " << (int)note->note() << " @ " << note->time() << endl;