mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-12 17:46:34 +01:00
* Add SysEx Support to MidiModel / SMF
git-svn-id: svn://localhost/ardour2/branches/3.0@4492 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
67d545c080
commit
3d594b460b
5 changed files with 164 additions and 31 deletions
|
|
@ -91,6 +91,7 @@ namespace ARDOUR {
|
||||||
MidiPgmChangeAutomation = 0x21,
|
MidiPgmChangeAutomation = 0x21,
|
||||||
MidiPitchBenderAutomation = 0x22,
|
MidiPitchBenderAutomation = 0x22,
|
||||||
MidiChannelPressureAutomation = 0x23,
|
MidiChannelPressureAutomation = 0x23,
|
||||||
|
MidiSystemExclusiveAutomation = 0x24,
|
||||||
FadeInAutomation = 0x40,
|
FadeInAutomation = 0x40,
|
||||||
FadeOutAutomation = 0x80,
|
FadeOutAutomation = 0x80,
|
||||||
EnvelopeAutomation = 0x100
|
EnvelopeAutomation = 0x100
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ EventTypeMap::parameter_midi_type(const Evoral::Parameter& param) const
|
||||||
case MidiPgmChangeAutomation: return MIDI_CMD_PGM_CHANGE; break;
|
case MidiPgmChangeAutomation: return MIDI_CMD_PGM_CHANGE; break;
|
||||||
case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break;
|
case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break;
|
||||||
case MidiPitchBenderAutomation: return MIDI_CMD_BENDER; break;
|
case MidiPitchBenderAutomation: return MIDI_CMD_BENDER; break;
|
||||||
|
case MidiSystemExclusiveAutomation: return MIDI_CMD_COMMON_SYSEX; break;
|
||||||
default: return 0;
|
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_PGM_CHANGE: return MidiPgmChangeAutomation; break;
|
||||||
case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break;
|
case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break;
|
||||||
case MIDI_CMD_BENDER: return MidiPitchBenderAutomation; break;
|
case MIDI_CMD_BENDER: return MidiPitchBenderAutomation; break;
|
||||||
|
case MIDI_CMD_COMMON_SYSEX: return MidiSystemExclusiveAutomation; break;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,11 @@ public:
|
||||||
inline Notes& notes() { return _notes; }
|
inline Notes& notes() { return _notes; }
|
||||||
inline const Notes& notes() const { 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 */
|
/** Read iterator */
|
||||||
class const_iterator {
|
class const_iterator {
|
||||||
public:
|
public:
|
||||||
|
|
@ -133,6 +138,8 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class Sequence<T>;
|
friend class Sequence<T>;
|
||||||
|
|
||||||
|
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX };
|
||||||
|
|
||||||
const Sequence<T>* _seq;
|
const Sequence<T>* _seq;
|
||||||
boost::shared_ptr< Event<T> > _event;
|
boost::shared_ptr< Event<T> > _event;
|
||||||
|
|
||||||
|
|
@ -145,17 +152,18 @@ public:
|
||||||
|
|
||||||
typedef std::vector<ControlIterator> ControlIterators;
|
typedef std::vector<ControlIterator> ControlIterators;
|
||||||
|
|
||||||
bool _is_end;
|
bool _is_end;
|
||||||
bool _locked;
|
bool _locked;
|
||||||
typename Notes::const_iterator _note_iter;
|
typename Notes::const_iterator _note_iter;
|
||||||
ControlIterators _control_iters;
|
typename SysExes::const_iterator _sysex_iter;
|
||||||
ControlIterators::iterator _control_iter;
|
ControlIterators _control_iters;
|
||||||
|
ControlIterators::iterator _control_iter;
|
||||||
};
|
};
|
||||||
|
|
||||||
const_iterator begin(T t=0) const { return const_iterator(*this, t); }
|
const_iterator begin(T t=0) const { return const_iterator(*this, t); }
|
||||||
const const_iterator& end() const { return _end_iter; }
|
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; }
|
T read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; }
|
||||||
|
|
||||||
bool control_to_midi_event(boost::shared_ptr< Event<T> >& ev,
|
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_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_note_off_unlocked(uint8_t chan, T time, uint8_t note);
|
||||||
void append_control_unlocked(const Parameter& param, T time, double value);
|
void append_control_unlocked(const Parameter& param, T time, double value);
|
||||||
|
void append_sysex_unlocked(const MIDIEvent<T>& ev);
|
||||||
|
|
||||||
mutable Glib::RWLock _lock;
|
mutable Glib::RWLock _lock;
|
||||||
|
|
||||||
|
|
@ -191,6 +200,8 @@ private:
|
||||||
|
|
||||||
Notes _notes;
|
Notes _notes;
|
||||||
|
|
||||||
|
SysExes _sysexes;
|
||||||
|
|
||||||
typedef std::vector<size_t> WriteNotes;
|
typedef std::vector<size_t> WriteNotes;
|
||||||
WriteNotes _write_notes[16];
|
WriteNotes _write_notes[16];
|
||||||
bool _writing;
|
bool _writing;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
if (event_size <= 0) {
|
||||||
*size = 0;
|
// if sysex, determine the size of the event
|
||||||
return 0;
|
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
|
// 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)
|
if (event_size > 1)
|
||||||
fread((*buf) + 1, 1, *size - 1, _fd);
|
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) {
|
for (size_t i=0; i < *size; ++i) {
|
||||||
printf("%X ", (*buf)[i]);
|
printf("%X ", (*buf)[i]);
|
||||||
}
|
}
|
||||||
printf("\n");*/
|
printf("\n");
|
||||||
|
|
||||||
return (int)*size;
|
return (int)*size;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ struct null_ostream : public std::ostream {
|
||||||
};
|
};
|
||||||
static null_ostream nullout;
|
static null_ostream nullout;
|
||||||
|
|
||||||
//static ostream& debugout = cout;
|
static ostream& debugout = cout;
|
||||||
static ostream& debugout = nullout;
|
//static ostream& debugout = nullout;
|
||||||
static ostream& errorout = cerr;
|
static ostream& errorout = cerr;
|
||||||
|
|
||||||
// Read iterator (const_iterator)
|
// Read iterator (const_iterator)
|
||||||
|
|
@ -91,6 +91,18 @@ Sequence<T>::const_iterator::const_iterator(const Sequence<T>& seq, T t)
|
||||||
break;
|
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);
|
ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
|
||||||
|
|
||||||
|
|
@ -131,18 +143,64 @@ Sequence<T>::const_iterator::const_iterator(const Sequence<T>& seq, T t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_note_iter != seq.notes().end()
|
#define MAKE_SURE_ADDING_SYSEXES_PRESERVES_OLD_SEMANTICS 1
|
||||||
&& (*_note_iter)->on_event().time() >= t
|
#if MAKE_SURE_ADDING_SYSEXES_PRESERVES_OLD_SEMANTICS
|
||||||
&& (!earliest_control.list
|
MIDIMessageType original_type = NIL;
|
||||||
|| (*_note_iter)->on_event().time() < earliest_control.x)) {
|
assert (!earliest_control.list || earliest_control.x >= t);
|
||||||
debugout << "Reading note on event @ " << (*_note_iter)->on_event().time() << endl;
|
|
||||||
|
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 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));
|
_event = boost::shared_ptr< Event<T> >(new Event<T>((*_note_iter)->on_event(), true));
|
||||||
_active_notes.push(*_note_iter);
|
_active_notes.push(*_note_iter);
|
||||||
++_note_iter;
|
++_note_iter;
|
||||||
_control_iter = _control_iters.end();
|
_control_iter = _control_iters.end();
|
||||||
} else if (earliest_control.list) {
|
} else if (type == CONTROL) {
|
||||||
debugout << "Reading control event @ " << earliest_control.x << endl;
|
debugout << "Reading control event @ " << earliest_t << endl;
|
||||||
seq.control_to_midi_event(_event, earliest_control);
|
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) {
|
if ( (! _event.get()) || _event->size() == 0) {
|
||||||
|
|
@ -189,11 +247,19 @@ Sequence<T>::const_iterator::operator++()
|
||||||
//debugout << "const_iterator::operator++: " << _event->to_string() << endl;
|
//debugout << "const_iterator::operator++: " << _event->to_string() << endl;
|
||||||
|
|
||||||
if (! (ev.is_note() || ev.is_cc() || ev.is_pgm_change()
|
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])
|
errorout << "Unknown event type: " << hex << int(ev.buffer()[0])
|
||||||
<< int(ev.buffer()[1]) << int(ev.buffer()[2]) << endl;
|
<< 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
|
// Increment past current control event
|
||||||
if (!ev.is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) {
|
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 };
|
MIDIMessageType type = NIL;
|
||||||
|
T earliest_t = 0;
|
||||||
Type type = NIL;
|
|
||||||
T t = 0;
|
|
||||||
|
|
||||||
// Next earliest note on
|
// Next earliest note on
|
||||||
if (_note_iter != _seq->notes().end()) {
|
if (_note_iter != _seq->notes().end()) {
|
||||||
type = NOTE_ON;
|
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
|
// Use the next earliest note off iff it's earlier than the note on
|
||||||
if (!_seq->percussive() && (! _active_notes.empty())) {
|
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;
|
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
|
// 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 (_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;
|
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) {
|
} else if (type == CONTROL) {
|
||||||
debugout << "Iterator = control" << endl;
|
debugout << "Iterator = control" << endl;
|
||||||
_seq->control_to_midi_event(_event, *_control_iter);
|
_seq->control_to_midi_event(_event, *_control_iter);
|
||||||
|
} else if (type == SYSEX) {
|
||||||
|
debugout << "Iterator = SysEx" << endl;
|
||||||
|
*_event =*(*_sysex_iter);
|
||||||
|
++_sysex_iter;
|
||||||
} else {
|
} else {
|
||||||
debugout << "Iterator = End" << endl;
|
debugout << "Iterator = End" << endl;
|
||||||
_is_end = true;
|
_is_end = true;
|
||||||
|
|
@ -294,6 +371,7 @@ Sequence<T>::const_iterator::operator=(const const_iterator& other)
|
||||||
_is_end = other._is_end;
|
_is_end = other._is_end;
|
||||||
_locked = other._locked;
|
_locked = other._locked;
|
||||||
_note_iter = other._note_iter;
|
_note_iter = other._note_iter;
|
||||||
|
_sysex_iter = other._sysex_iter;
|
||||||
_control_iters = other._control_iters;
|
_control_iters = other._control_iters;
|
||||||
size_t index = other._control_iter - other._control_iters.begin();
|
size_t index = other._control_iter - other._control_iters.begin();
|
||||||
_control_iter = _control_iters.begin() + index;
|
_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());
|
assert(iter.list.get());
|
||||||
const uint32_t event_type = iter.list->parameter().type();
|
const uint32_t event_type = iter.list->parameter().type();
|
||||||
|
|
||||||
|
// initialize the event pointer with a new event, if necessary
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
ev = boost::shared_ptr< Event<T> >(new Event<T>(event_type, 0, 3, NULL, true));
|
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());
|
ev.velocity());
|
||||||
} else if (ev.is_note_off()) {
|
} else if (ev.is_note_off()) {
|
||||||
append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
|
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())) {
|
} else if (!_type_map.type_is_midi(ev.event_type())) {
|
||||||
printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
|
printf("WARNING: Sequence: Unknown event type %X: ", ev.event_type());
|
||||||
for (size_t i=0; i < ev.size(); ++i) {
|
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);
|
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>
|
template<typename T>
|
||||||
void
|
void
|
||||||
Sequence<T>::add_note_unlocked(const boost::shared_ptr< Note<T> > note)
|
Sequence<T>::add_note_unlocked(const boost::shared_ptr< Note<T> > note)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue