* MidiModel::const_iterator::operator++: added AUTOMATION type

* automatable.cc/parameter.cc: Added friendly names for the new Midi parameter types
* fixed a failed assertion problem (note on channel != note off channel), but have no idea how :)
* changed lots of whitespace :|


git-svn-id: svn://localhost/ardour2/branches/3.0@3309 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Hans Baier 2008-05-03 06:37:22 +00:00
parent fb1fbf71af
commit b0d49651a7
9 changed files with 320 additions and 337 deletions

View file

@ -227,6 +227,9 @@ private:
bool _writing; bool _writing;
bool _edited; bool _edited;
typedef std::vector< boost::shared_ptr<const ARDOUR::AutomationList> > AutomationLists;
AutomationLists _dirty_automations;
const const_iterator _end_iter; const const_iterator _end_iter;
mutable nframes_t _next_read; mutable nframes_t _next_read;

View file

@ -198,14 +198,21 @@ Automatable::describe_parameter (Parameter param)
{ {
/* derived classes like PluginInsert should override this */ /* derived classes like PluginInsert should override this */
if (param == Parameter(GainAutomation)) if (param == Parameter(GainAutomation)) {
return _("Fader"); return _("Fader");
else if (param.type() == PanAutomation) } else if (param.type() == PanAutomation) {
return (string_compose(_("Pan %1"), param.id())); return (string_compose(_("Pan %1"), param.id()));
else if (param.type() == MidiCCAutomation) } else if (param.type() == MidiCCAutomation) {
return string_compose("CC %1", param.id()); return string_compose("CC %1 [%2]", param.id(), int(param.channel()) + 1);
else } else if (param.type() == MidiPgmChangeAutomation) {
return string_compose("Program [%1]", int(param.channel()) + 1);
} else if (param.type() == MidiPitchBenderAutomation) {
return string_compose("Bender [%1]", int(param.channel()) + 1);
} else if (param.type() == MidiChannelAftertouchAutomation) {
return string_compose("Aftertouch [%1]", int(param.channel()) + 1);
} else {
return param.to_string(); return param.to_string();
}
} }
void void
@ -455,7 +462,11 @@ Automatable::transport_stopped (nframes_t now)
boost::shared_ptr<AutomationControl> boost::shared_ptr<AutomationControl>
Automatable::control_factory(boost::shared_ptr<AutomationList> list) Automatable::control_factory(boost::shared_ptr<AutomationList> list)
{ {
if (list->parameter().type() == MidiCCAutomation) { if (
list->parameter().type() == MidiCCAutomation ||
list->parameter().type() == MidiPgmChangeAutomation ||
list->parameter().type() == MidiChannelAftertouchAutomation
) {
// FIXME: this will die horribly if this is not a MidiTrack // FIXME: this will die horribly if this is not a MidiTrack
return boost::shared_ptr<AutomationControl>(new MidiTrack::MidiControl((MidiTrack*)this, list)); return boost::shared_ptr<AutomationControl>(new MidiTrack::MidiControl((MidiTrack*)this, list));
} else { } else {

View file

@ -1436,6 +1436,10 @@ AutomationList::get_state ()
XMLNode& XMLNode&
AutomationList::state (bool full) AutomationList::state (bool full)
{ {
cerr << "getting ";
if(full)
cerr << "full ";
cerr << "state for AutomationList " << _parameter.to_string() << " list size: " << size() << endl;
XMLNode* root = new XMLNode (X_("AutomationList")); XMLNode* root = new XMLNode (X_("AutomationList"));
char buf[64]; char buf[64];
LocaleGuard lg (X_("POSIX")); LocaleGuard lg (X_("POSIX"));

View file

@ -1,22 +1,22 @@
/* /*
Copyright (C) 2007 Paul Davis Copyright (C) 2007 Paul Davis
Written by Dave Robillard, 2007 Written by Dave Robillard, 2007
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#define __STDC_LIMIT_MACROS 1 #define __STDC_LIMIT_MACROS 1
@ -35,42 +35,31 @@
using namespace std; using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
void MidiModel::write_lock() {
void
MidiModel::write_lock()
{
_lock.writer_lock(); _lock.writer_lock();
_automation_lock.lock(); _automation_lock.lock();
} }
void void MidiModel::write_unlock() {
MidiModel::write_unlock()
{
_lock.writer_unlock(); _lock.writer_unlock();
_automation_lock.unlock(); _automation_lock.unlock();
} }
void void MidiModel::read_lock() const {
MidiModel::read_lock() const
{
_lock.reader_lock(); _lock.reader_lock();
/*_automation_lock.lock();*/ /*_automation_lock.lock();*/
} }
void void MidiModel::read_unlock() const {
MidiModel::read_unlock() const
{
_lock.reader_unlock(); _lock.reader_unlock();
/*_automation_lock.unlock();*/ /*_automation_lock.unlock();*/
} }
// Read iterator (const_iterator) // Read iterator (const_iterator)
MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) :
: _model(&model) _model(&model), _is_end( (t == DBL_MAX) || model.empty()),
, _is_end( (t == DBL_MAX) || model.empty()) _locked( !_is_end) {
, _locked( ! _is_end)
{
//cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl; //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
if (_is_end) if (_is_end)
@ -91,13 +80,13 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
_control_iters.reserve(model.controls().size()); _control_iters.reserve(model.controls().size());
for (Automatable::Controls::const_iterator i = model.controls().begin(); for (Automatable::Controls::const_iterator i = model.controls().begin();
i != model.controls().end(); ++i) { i != model.controls().end(); ++i) {
assert( assert(
i->first.type() == MidiCCAutomation || i->first.type() == MidiCCAutomation ||
i->first.type() == MidiPgmChangeAutomation || i->first.type() == MidiPgmChangeAutomation ||
i->first.type() == MidiPitchBenderAutomation || i->first.type() == MidiPitchBenderAutomation ||
i->first.type() == MidiChannelAftertouchAutomation i->first.type() == MidiChannelAftertouchAutomation
); );
double x, y; double x, y;
@ -132,9 +121,9 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
} }
if (earliest_control.automation_list && earliest_control.x < _event.time()) if (earliest_control.automation_list && earliest_control.x < _event.time())
model.control_to_midi_event(_event, earliest_control); model.control_to_midi_event(_event, earliest_control);
else else
_control_iter = _control_iters.end(); _control_iter = _control_iters.end();
if (_event.size() == 0) { if (_event.size() == 0) {
//cerr << "Created MIDI iterator @ " << t << " is at end." << endl; //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
@ -150,23 +139,20 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
} }
} }
MidiModel::const_iterator::~const_iterator() {
MidiModel::const_iterator::~const_iterator()
{
if (_locked) { if (_locked) {
_model->read_unlock(); _model->read_unlock();
} }
} }
const MidiModel::const_iterator& const MidiModel::const_iterator& MidiModel::const_iterator::operator++()
MidiModel::const_iterator::operator++()
{ {
if (_is_end) if (_is_end)
throw std::logic_error("Attempt to iterate past end of MidiModel"); throw std::logic_error("Attempt to iterate past end of MidiModel");
/*cerr << "const_iterator::operator++: _event type:" << hex << "0x" << int(_event.type()) /*cerr << "const_iterator::operator++: _event type:" << hex << "0x" << int(_event.type())
<< " buffer: 0x" << int(_event.buffer()[0]) << " 0x" << int(_event.buffer()[1]) << " buffer: 0x" << int(_event.buffer()[0]) << " 0x" << int(_event.buffer()[1])
<< " 0x" << int(_event.buffer()[2]) << endl;*/ << " 0x" << int(_event.buffer()[2]) << endl;*/
if(! (_event.is_note() || _event.is_cc() || _event.is_pgm_change() || _event.is_pitch_bender() || _event.is_channel_aftertouch()) ) { if(! (_event.is_note() || _event.is_cc() || _event.is_pgm_change() || _event.is_pitch_bender() || _event.is_channel_aftertouch()) ) {
cerr << "FAILED event buffer: " << hex << int(_event.buffer()[0]) << int(_event.buffer()[1]) << int(_event.buffer()[2]) << endl; cerr << "FAILED event buffer: " << hex << int(_event.buffer()[0]) << int(_event.buffer()[1]) << int(_event.buffer()[2]) << endl;
@ -181,7 +167,7 @@ MidiModel::const_iterator::operator++()
// v--- this crashes because of a null pointer in the stl containers linked list chain // v--- this crashes because of a null pointer in the stl containers linked list chain
// the crash occurs in _control_iter->automation_list->size(); // the crash occurs in _control_iter->automation_list->size();
const bool ret = _control_iter->automation_list->rt_safe_earliest_event_unlocked( const bool ret = _control_iter->automation_list->rt_safe_earliest_event_unlocked(
_control_iter->x, DBL_MAX, x, y, false); _control_iter->x, DBL_MAX, x, y, false);
if (ret) { if (ret) {
//cerr << "Incremented " << _control_iter->automation_list->parameter().id() << " to " << x << endl; //cerr << "Incremented " << _control_iter->automation_list->parameter().id() << " to " << x << endl;
@ -207,10 +193,10 @@ MidiModel::const_iterator::operator++()
} }
} }
enum Type { NIL, NOTE_ON, NOTE_OFF, AUTOMATION }; enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
Type type = NIL; Type type = NIL;
double t = 0; double t = 0;
// Next earliest note on // Next earliest note on
if (_note_iter != _model->notes().end()) { if (_note_iter != _model->notes().end()) {
@ -230,8 +216,8 @@ MidiModel::const_iterator::operator++()
if (_control_iter != _control_iters.end() if (_control_iter != _control_iters.end()
&& _control_iter->x != DBL_MAX && _control_iter->x != DBL_MAX
&& _control_iter != old_control_iter) && _control_iter != old_control_iter)
if (type == NIL || _control_iter->x < t) if (type == NIL || _control_iter->x < t)
type = AUTOMATION; type = AUTOMATION;
if (type == NOTE_ON) { if (type == NOTE_ON) {
cerr << "********** MIDI Iterator = note on" << endl; cerr << "********** MIDI Iterator = note on" << endl;
@ -250,27 +236,23 @@ MidiModel::const_iterator::operator++()
_is_end = true; _is_end = true;
} }
assert(_is_end || _event.size() > 0); assert(_is_end || _event.size()> 0);
return *this; return *this;
} }
bool MidiModel::const_iterator::operator==(const const_iterator& other) const
bool
MidiModel::const_iterator::operator==(const const_iterator& other) const
{ {
if (_is_end || other._is_end) if (_is_end || other._is_end)
return (_is_end == other._is_end); return (_is_end == other._is_end);
else else
return (_event == other._event); return (_event == other._event);
} }
MidiModel::const_iterator& MidiModel::const_iterator::operator=(const const_iterator& other)
MidiModel::const_iterator&
MidiModel::const_iterator::operator=(const const_iterator& other)
{ {
if (_locked && _model != other._model) if (_locked && _model != other._model)
_model->read_unlock(); _model->read_unlock();
assert( ! other._event.owns_buffer()); assert( ! other._event.owns_buffer());
@ -288,20 +270,13 @@ MidiModel::const_iterator::operator=(const const_iterator& other)
return *this; return *this;
} }
// MidiModel // MidiModel
MidiModel::MidiModel(MidiSource *s, size_t size) MidiModel::MidiModel(MidiSource *s, size_t size) :
: Automatable(s->session(), "midi model") Automatable(s->session(), "midi model"), _notes(size),
, _notes(size) _note_mode(Sustained), _writing(false), _edited(false), _end_iter(
, _note_mode(Sustained) *this, DBL_MAX), _next_read(UINT32_MAX), _read_iter(*this,
, _writing(false) DBL_MAX), _midi_source(s) {
, _edited(false)
, _end_iter(*this, DBL_MAX)
, _next_read(UINT32_MAX)
, _read_iter(*this, DBL_MAX)
, _midi_source(s)
{
assert(_end_iter._is_end); assert(_end_iter._is_end);
assert( ! _end_iter._locked); assert( ! _end_iter._locked);
} }
@ -310,9 +285,8 @@ MidiModel::MidiModel(MidiSource *s, size_t size)
* adding \a stamp_offset to each event's timestamp. * adding \a stamp_offset to each event's timestamp.
* \return number of events written to \a dst * \return number of events written to \a dst
*/ */
size_t size_t MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes,
MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const nframes_t stamp_offset, nframes_t negative_stamp_offset) const {
{
//cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl; //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
//cerr << this << " MM # notes: " << n_notes() << endl; //cerr << this << " MM # notes: " << n_notes() << endl;
@ -328,16 +302,17 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
_next_read = start + nframes; _next_read = start + nframes;
while (_read_iter != end() && _read_iter->time() < start + nframes) { while (_read_iter != end() && _read_iter->time() < start + nframes) {
assert(_read_iter->size() > 0); assert(_read_iter->size()> 0);
dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset, _read_iter->size(), _read_iter->buffer()); dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset,
_read_iter->size(), _read_iter->buffer());
/* /*
cerr << this << " MidiModel::read event @ " << _read_iter->time() cerr << this << " MidiModel::read event @ " << _read_iter->time()
<< " type: " << hex << int(_read_iter->type()) << dec << " type: " << hex << int(_read_iter->type()) << dec
<< " note: " << int(_read_iter->note()) << " note: " << int(_read_iter->note())
<< " velocity: " << int(_read_iter->velocity()) << " velocity: " << int(_read_iter->velocity())
<< endl; << endl;
*/ */
++_read_iter; ++_read_iter;
++read_events; ++read_events;
@ -346,11 +321,9 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
return read_events; return read_events;
} }
bool MidiModel::control_to_midi_event(MIDI::Event& ev,
bool const MidiControlIterator& iter) const {
MidiModel::control_to_midi_event(MIDI::Event& ev, const MidiControlIterator& iter) const switch (iter.automation_list->parameter().type()) {
{
switch(iter.automation_list->parameter().type()) {
case MidiCCAutomation: case MidiCCAutomation:
if (ev.size() < 3) if (ev.size() < 3)
ev.set_buffer((Byte*)malloc(3), true); ev.set_buffer((Byte*)malloc(3), true);
@ -404,7 +377,8 @@ MidiModel::control_to_midi_event(MIDI::Event& ev, const MidiControlIterator& ite
assert(iter.automation_list->parameter().channel() < 16); assert(iter.automation_list->parameter().channel() < 16);
assert(iter.automation_list->parameter().id() <= INT8_MAX); assert(iter.automation_list->parameter().id() <= INT8_MAX);
assert(iter.y <= INT8_MAX); assert(iter.y <= INT8_MAX);
ev.buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.automation_list->parameter().channel(); ev.buffer()[0]
= MIDI_CMD_CHANNEL_PRESSURE + iter.automation_list->parameter().channel();
ev.buffer()[1] = (Byte)iter.y; ev.buffer()[1] = (Byte)iter.y;
ev.buffer()[2] = 0; ev.buffer()[2] = 0;
ev.time() = iter.x; ev.time() = iter.x;
@ -416,7 +390,6 @@ MidiModel::control_to_midi_event(MIDI::Event& ev, const MidiControlIterator& ite
} }
} }
/** Begin a write of events to the model. /** Begin a write of events to the model.
* *
* If \a mode is Sustained, complete notes with duration are constructed as note * If \a mode is Sustained, complete notes with duration are constructed as note
@ -424,35 +397,31 @@ MidiModel::control_to_midi_event(MIDI::Event& ev, const MidiControlIterator& ite
* stored; note off events are discarded entirely and all contained notes will * stored; note off events are discarded entirely and all contained notes will
* have duration 0. * have duration 0.
*/ */
void void MidiModel::start_write() {
MidiModel::start_write()
{
//cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl; //cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
write_lock(); write_lock();
_writing = true; _writing = true;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
_write_notes[i].clear(); _write_notes[i].clear();
_dirty_automations.clear();
write_unlock(); write_unlock();
} }
/** Finish a write of events to the model. /** Finish a write of events to the model.
* *
* If \a delete_stuck is true and the current mode is Sustained, note on events * If \a delete_stuck is true and the current mode is Sustained, note on events
* that were never resolved with a corresonding note off will be deleted. * that were never resolved with a corresonding note off will be deleted.
* Otherwise they will remain as notes with duration 0. * Otherwise they will remain as notes with duration 0.
*/ */
void void MidiModel::end_write(bool delete_stuck) {
MidiModel::end_write(bool delete_stuck)
{
write_lock(); write_lock();
assert(_writing); assert(_writing);
//cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n"; //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
if (_note_mode == Sustained && delete_stuck) { if (_note_mode == Sustained && delete_stuck) {
for (Notes::iterator n = _notes.begin(); n != _notes.end() ; ) { for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
if ((*n)->duration() == 0) { if ((*n)->duration() == 0) {
cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl; cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
n = _notes.erase(n); n = _notes.erase(n);
@ -472,20 +441,23 @@ MidiModel::end_write(bool delete_stuck)
_write_notes[i].clear(); _write_notes[i].clear();
} }
for(AutomationLists::const_iterator i = _dirty_automations.begin(); i != _dirty_automations.end(); ++i) {
(*i)->Dirty.emit();
(*i)->lookup_cache().left = -1;
(*i)->search_cache().left = -1;
}
_writing = false; _writing = false;
write_unlock(); write_unlock();
} }
/** Append \a in_event to model. NOT realtime safe. /** Append \a in_event to model. NOT realtime safe.
* *
* Timestamps of events in \a buf are expected to be relative to * Timestamps of events in \a buf are expected to be relative to
* the start of this model (t=0) and MUST be monotonically increasing * the start of this model (t=0) and MUST be monotonically increasing
* and MUST be >= the latest event currently in the model. * and MUST be >= the latest event currently in the model.
*/ */
void void MidiModel::append(const MIDI::Event& ev) {
MidiModel::append(const MIDI::Event& ev)
{
write_lock(); write_lock();
_edited = true; _edited = true;
@ -495,17 +467,23 @@ MidiModel::append(const MIDI::Event& ev)
assert(_writing); assert(_writing);
if (ev.is_note_on()) { if (ev.is_note_on()) {
append_note_on_unlocked(ev.channel(), ev.time(), ev.note(), ev.velocity()); append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
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_cc()) { } else if (ev.is_cc()) {
append_automation_event_unlocked(MidiCCAutomation, ev.channel(), ev.time(), ev.cc_number(), ev.cc_value()); append_automation_event_unlocked(MidiCCAutomation, ev.channel(),
ev.time(), ev.cc_number(), ev.cc_value());
} else if (ev.is_pgm_change()) { } else if (ev.is_pgm_change()) {
append_automation_event_unlocked(MidiPgmChangeAutomation, ev.channel(), ev.time(), ev.pgm_number(), 0); append_automation_event_unlocked(MidiPgmChangeAutomation, ev.channel(),
ev.time(), ev.pgm_number(), 0);
} else if (ev.is_pitch_bender()) { } else if (ev.is_pitch_bender()) {
append_automation_event_unlocked(MidiPitchBenderAutomation, ev.channel(), ev.time(), ev.pitch_bender_lsb(), ev.pitch_bender_msb()); append_automation_event_unlocked(MidiPitchBenderAutomation,
ev.channel(), ev.time(), ev.pitch_bender_lsb(),
ev.pitch_bender_msb());
} else if (ev.is_channel_aftertouch()) { } else if (ev.is_channel_aftertouch()) {
append_automation_event_unlocked(MidiChannelAftertouchAutomation, ev.channel(), ev.time(), ev.channel_aftertouch(), 0); append_automation_event_unlocked(MidiChannelAftertouchAutomation,
ev.channel(), ev.time(), ev.channel_aftertouch(), 0);
} else { } else {
printf("MM Unknown event type %X\n", ev.type()); printf("MM Unknown event type %X\n", ev.type());
} }
@ -513,12 +491,10 @@ MidiModel::append(const MIDI::Event& ev)
write_unlock(); write_unlock();
} }
void MidiModel::append_note_on_unlocked(uint8_t chan, double time,
void uint8_t note_num, uint8_t velocity) {
MidiModel::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num, uint8_t velocity)
{
/*cerr << "MidiModel " << this << " chan " << (int)chan << /*cerr << "MidiModel " << this << " chan " << (int)chan <<
" note " << (int)note_num << " on @ " << time << endl;*/ " note " << (int)note_num << " on @ " << time << endl;*/
assert(chan < 16); assert(chan < 16);
assert(_writing); assert(_writing);
@ -529,16 +505,14 @@ MidiModel::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num,
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl; //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
_write_notes[chan].push_back(_notes.size() - 1); _write_notes[chan].push_back(_notes.size() - 1);
}/* else { }/* else {
cerr << "MM Percussive: NOT appending active note on" << endl; cerr << "MM Percussive: NOT appending active note on" << endl;
}*/ }*/
} }
void MidiModel::append_note_off_unlocked(uint8_t chan, double time,
void uint8_t note_num) {
MidiModel::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
{
/*cerr << "MidiModel " << this << " chan " << (int)chan << /*cerr << "MidiModel " << this << " chan " << (int)chan <<
" note " << (int)note_num << " off @ " << time << endl;*/ " note " << (int)note_num << " off @ " << time << endl;*/
assert(chan < 16); assert(chan < 16);
assert(_writing); assert(_writing);
@ -556,7 +530,8 @@ MidiModel::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
bool resolved = false; bool resolved = false;
for (WriteNotes::iterator n = _write_notes[chan].begin(); n != _write_notes[chan].end(); ++n) { for (WriteNotes::iterator n = _write_notes[chan].begin(); n
!= _write_notes[chan].end(); ++n) {
Note& note = *_notes[*n].get(); Note& note = *_notes[*n].get();
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl; //cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
if (note.note() == note_num) { if (note.note() == note_num) {
@ -571,13 +546,11 @@ MidiModel::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
if (!resolved) if (!resolved)
cerr << "MidiModel " << this << " spurious note off chan " << (int)chan cerr << "MidiModel " << this << " spurious note off chan " << (int)chan
<< ", note " << (int)note_num << " @ " << time << endl; << ", note " << (int)note_num << " @ " << time << endl;
} }
void MidiModel::append_automation_event_unlocked(AutomationType type,
void uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte) {
MidiModel::append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte)
{
//cerr << "MidiModel " << this << " chan " << (int)chan << //cerr << "MidiModel " << this << " chan " << (int)chan <<
// " CC " << (int)number << " = " << (int)value << " @ " << time << endl; // " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
@ -588,7 +561,7 @@ MidiModel::append_automation_event_unlocked(AutomationType type, uint8_t chan, d
uint32_t id = 0; uint32_t id = 0;
switch(type) { switch (type) {
case MidiCCAutomation: case MidiCCAutomation:
id = first_byte; id = first_byte;
value = double(second_byte); value = double(second_byte);
@ -609,35 +582,32 @@ MidiModel::append_automation_event_unlocked(AutomationType type, uint8_t chan, d
Parameter param(type, id, chan); Parameter param(type, id, chan);
boost::shared_ptr<AutomationControl> control = Automatable::control(param, true); boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
control->list()->fast_simple_add(time, value); control->list()->fast_simple_add(time, value);
cerr << "control list size after fast simple add: " << control->list()->size() << endl;
} }
void void MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note) {
MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
{
//cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl; //cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
_edited = true; _edited = true;
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator); Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
note_time_comparator);
_notes.insert(i, note); _notes.insert(i, note);
} }
void MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note) {
void
MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
{
_edited = true; _edited = true;
//cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl; //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
for(Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) { for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
Note& _n = *(*n); Note& _n = *(*n);
const Note& _note = *note; const Note& _note = *note;
// TODO: There is still the issue, that after restarting ardour // TODO: There is still the issue, that after restarting ardour
// persisted undo does not work, because of rounding errors in the // persisted undo does not work, because of rounding errors in the
// event times after saving/restoring to/from MIDI files // event times after saving/restoring to/from MIDI files
cerr << "======================================= " << endl; cerr << "======================================= " << endl;
cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl; cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl; cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
cerr << "Equal: " << bool(_n == _note) << endl; cerr << "Equal: " << bool(_n == _note) << endl;
cerr << endl << endl; cerr << endl << endl;
if(_n == _note) { if (_n == _note) {
_notes.erase(n); _notes.erase(n);
// we have to break here, because erase invalidates all iterators, ie. n itself // we have to break here, because erase invalidates all iterators, ie. n itself
break; break;
@ -647,9 +617,7 @@ MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
/** Slow! for debugging only. */ /** Slow! for debugging only. */
#ifndef NDEBUG #ifndef NDEBUG
bool bool MidiModel::is_sorted() const {
MidiModel::is_sorted() const
{
bool t = 0; bool t = 0;
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
if ((*n)->time() < t) if ((*n)->time() < t)
@ -667,22 +635,17 @@ MidiModel::is_sorted() const
* can be held on to for as long as the caller wishes, or discarded without * can be held on to for as long as the caller wishes, or discarded without
* formality, until apply_command is called and ownership is taken. * formality, until apply_command is called and ownership is taken.
*/ */
MidiModel::DeltaCommand* MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name) {
MidiModel::new_delta_command(const string name) DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
{
DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
return cmd; return cmd;
} }
/** Apply a command. /** Apply a command.
* *
* Ownership of cmd is taken, it must not be deleted by the caller. * Ownership of cmd is taken, it must not be deleted by the caller.
* The command will constitute one item on the undo stack. * The command will constitute one item on the undo stack.
*/ */
void void MidiModel::apply_command(Command* cmd) {
MidiModel::apply_command(Command* cmd)
{
_session.begin_reversible_command(cmd->name()); _session.begin_reversible_command(cmd->name());
(*cmd)(); (*cmd)();
assert(is_sorted()); assert(is_sorted());
@ -690,82 +653,71 @@ MidiModel::apply_command(Command* cmd)
_edited = true; _edited = true;
} }
// MidiEditCommand // MidiEditCommand
MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const std::string& name) MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
: Command(name), _model(m), _name(name) const std::string& name) :
{ Command(name), _model(m), _name(name) {
} }
MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m, const XMLNode& node) MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
: _model(m) const XMLNode& node) :
{ _model(m) {
set_state(node); set_state(node);
} }
void void MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note) {
MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note)
{
//cerr << "MEC: apply" << endl; //cerr << "MEC: apply" << endl;
_removed_notes.remove(note); _removed_notes.remove(note);
_added_notes.push_back(note); _added_notes.push_back(note);
} }
void MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note) {
void
MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note)
{
//cerr << "MEC: remove" << endl; //cerr << "MEC: remove" << endl;
_added_notes.remove(note); _added_notes.remove(note);
_removed_notes.push_back(note); _removed_notes.push_back(note);
} }
void MidiModel::DeltaCommand::operator()()
void
MidiModel::DeltaCommand::operator()()
{ {
// This could be made much faster by using a priority_queue for added and // This could be made much faster by using a priority_queue for added and
// removed notes (or sort here), and doing a single iteration over _model // removed notes (or sort here), and doing a single iteration over _model
// Need to reset iterator to drop the read lock it holds, or we'll deadlock // Need to reset iterator to drop the read lock it holds, or we'll deadlock
const bool reset_iter = (_model->_read_iter.locked()); const bool reset_iter = (_model->_read_iter.locked());
const double iter_time = _model->_read_iter->time(); const double iter_time = _model->_read_iter->time();
if (reset_iter) if (reset_iter)
_model->_read_iter = _model->end(); // drop read lock _model->_read_iter = _model->end(); // drop read lock
assert( ! _model->_read_iter.locked()); assert( ! _model->_read_iter.locked());
_model->write_lock(); _model->write_lock();
for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
_model->add_note_unlocked(*i); _model->add_note_unlocked(*i);
for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
_model->remove_note_unlocked(*i); _model->remove_note_unlocked(*i);
_model->write_unlock(); _model->write_unlock();
if (reset_iter) if (reset_iter)
_model->_read_iter = const_iterator(*_model.get(), iter_time); _model->_read_iter = const_iterator(*_model.get(), iter_time);
_model->ContentsChanged(); /* EMIT SIGNAL */ _model->ContentsChanged(); /* EMIT SIGNAL */
} }
void MidiModel::DeltaCommand::undo() {
void
MidiModel::DeltaCommand::undo()
{
// This could be made much faster by using a priority_queue for added and // This could be made much faster by using a priority_queue for added and
// removed notes (or sort here), and doing a single iteration over _model // removed notes (or sort here), and doing a single iteration over _model
// Need to reset iterator to drop the read lock it holds, or we'll deadlock // Need to reset iterator to drop the read lock it holds, or we'll deadlock
const bool reset_iter = (_model->_read_iter.locked()); const bool reset_iter = (_model->_read_iter.locked());
const double iter_time = _model->_read_iter->time(); const double iter_time = _model->_read_iter->time();
if (reset_iter) if (reset_iter)
_model->_read_iter = _model->end(); // drop read lock _model->_read_iter = _model->end(); // drop read lock
@ -774,10 +726,12 @@ MidiModel::DeltaCommand::undo()
_model->write_lock(); _model->write_lock();
for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i
!= _added_notes.end(); ++i)
_model->remove_note_unlocked(*i); _model->remove_note_unlocked(*i);
for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) for (std::list< boost::shared_ptr<Note> >::iterator i =
_removed_notes.begin(); i != _removed_notes.end(); ++i)
_model->add_note_unlocked(*i); _model->add_note_unlocked(*i);
_model->write_unlock(); _model->write_unlock();
@ -788,9 +742,8 @@ MidiModel::DeltaCommand::undo()
_model->ContentsChanged(); /* EMIT SIGNAL */ _model->ContentsChanged(); /* EMIT SIGNAL */
} }
XMLNode & XMLNode & MidiModel::DeltaCommand::marshal_note(
MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> note) const boost::shared_ptr<Note> note) {
{
XMLNode *xml_note = new XMLNode("note"); XMLNode *xml_note = new XMLNode("note");
ostringstream note_str(ios::ate); ostringstream note_str(ios::ate);
note_str << int(note->note()); note_str << int(note->note());
@ -815,9 +768,8 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> note)
return *xml_note; return *xml_note;
} }
boost::shared_ptr<Note> boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(
MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note) XMLNode *xml_note) {
{
unsigned int note; unsigned int note;
istringstream note_str(xml_note->property("note")->value()); istringstream note_str(xml_note->property("note")->value());
note_str >> note; note_str >> note;
@ -846,10 +798,8 @@ MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
#define REMOVED_NOTES_ELEMENT "removed_notes" #define REMOVED_NOTES_ELEMENT "removed_notes"
#define DELTA_COMMAND_ELEMENT "DeltaCommand" #define DELTA_COMMAND_ELEMENT "DeltaCommand"
int int MidiModel::DeltaCommand::set_state(const XMLNode& delta_command) {
MidiModel::DeltaCommand::set_state (const XMLNode& delta_command) if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
{
if(delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
return 1; return 1;
} }
@ -857,7 +807,7 @@ MidiModel::DeltaCommand::set_state (const XMLNode& delta_command)
XMLNode *added_notes = delta_command.child(ADDED_NOTES_ELEMENT); XMLNode *added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
XMLNodeList notes = added_notes->children(); XMLNodeList notes = added_notes->children();
transform(notes.begin(), notes.end(), back_inserter(_added_notes), transform(notes.begin(), notes.end(), back_inserter(_added_notes),
sigc::mem_fun(*this, &DeltaCommand::unmarshal_note)); sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
_removed_notes.clear(); _removed_notes.clear();
XMLNode *removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT); XMLNode *removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
@ -868,37 +818,31 @@ MidiModel::DeltaCommand::set_state (const XMLNode& delta_command)
return 0; return 0;
} }
XMLNode& XMLNode& MidiModel::DeltaCommand::get_state() {
MidiModel::DeltaCommand::get_state ()
{
XMLNode *delta_command = new XMLNode(DELTA_COMMAND_ELEMENT); XMLNode *delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
delta_command->add_property("midi_source", _model->midi_source()->id().to_s()); delta_command->add_property("midi_source", _model->midi_source()->id().to_s());
XMLNode *added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT); XMLNode *added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
for_each(_added_notes.begin(), _added_notes.end(), for_each(_added_notes.begin(), _added_notes.end(), sigc::compose(
sigc::compose(sigc::mem_fun(*added_notes, &XMLNode::add_child_nocopy), sigc::mem_fun(*added_notes, &XMLNode::add_child_nocopy),
sigc::mem_fun(*this, &DeltaCommand::marshal_note))); sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
XMLNode *removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT); XMLNode *removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
for_each(_removed_notes.begin(), _removed_notes.end(), for_each(_removed_notes.begin(), _removed_notes.end(), sigc::compose(
sigc::compose(sigc::mem_fun(*removed_notes, &XMLNode::add_child_nocopy), sigc::mem_fun(*removed_notes, &XMLNode::add_child_nocopy),
sigc::mem_fun(*this, &DeltaCommand::marshal_note))); sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
return *delta_command; return *delta_command;
} }
struct EventTimeComparator { struct EventTimeComparator {
typedef const MIDI::Event* value_type; typedef const MIDI::Event* value_type;
inline bool operator()(const MIDI::Event* a, inline bool operator()(const MIDI::Event* a, const MIDI::Event* b) const {
const MIDI::Event* b) const {
return a->time() >= b->time(); return a->time() >= b->time();
} }
}; };
bool bool MidiModel::write_to(boost::shared_ptr<MidiSource> source) {
MidiModel::write_to(boost::shared_ptr<MidiSource> source)
{
cerr << "Writing model to " << source->name() << endl; cerr << "Writing model to " << source->name() << endl;
/* This could be done using a temporary MidiRingBuffer and using /* This could be done using a temporary MidiRingBuffer and using
@ -920,9 +864,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
EventTimeComparator comp; EventTimeComparator comp;
typedef std::priority_queue< typedef std::priority_queue<
const MIDI::Event*, const MIDI::Event*,
std::deque<const MIDI::Event*>, std::deque<const MIDI::Event*>,
EventTimeComparator> MidiEvents; EventTimeComparator> MidiEvents;
MidiEvents events(comp); MidiEvents events(comp);
@ -934,8 +878,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
// Write any pending note offs earlier than this note on // Write any pending note offs earlier than this note on
while ( ! active_notes.empty() ) { while ( !active_notes.empty() ) {
const boost::shared_ptr<const Note> earliest_off = active_notes.top(); const boost::shared_ptr<const Note> earliest_off =
active_notes.top();
const MIDI::Event& off_ev = earliest_off->off_event(); const MIDI::Event& off_ev = earliest_off->off_event();
if (off_ev.time() <= (*n)->time()) { if (off_ev.time() <= (*n)->time()) {
events.push(&off_ev); events.push(&off_ev);
@ -952,16 +897,14 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
} }
// Write any trailing note offs // Write any trailing note offs
while ( ! active_notes.empty() ) { while ( !active_notes.empty() ) {
events.push(&active_notes.top()->off_event()); events.push(&active_notes.top()->off_event());
active_notes.pop(); active_notes.pop();
} }
while(!events.empty()) { while (!events.empty()) {
source->append_event_unlocked(Frames, *events.top()); source->append_event_unlocked(Frames, *events.top());
cerr << "MidiModel::write_to appending event with time:" << dec << int(events.top()->time()) << hex //cerr << "MidiModel::write_to appending event with time:" << dec << int(events.top()->time()) << hex << " buffer: 0x" << int(events.top()->buffer()[0]) << " 0x" << int(events.top()->buffer()[1]) << " 0x" << int(events.top()->buffer()[2]) << endl;
<< " buffer: 0x" << int(events.top()->buffer()[0]) << " 0x" << int(events.top()->buffer()[1])
<< " 0x" << int(events.top()->buffer()[2]) << endl;
events.pop(); events.pop();
} }
@ -972,21 +915,15 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
return true; return true;
} }
XMLNode& XMLNode& MidiModel::get_state() {
MidiModel::get_state()
{
XMLNode *node = new XMLNode("MidiModel"); XMLNode *node = new XMLNode("MidiModel");
return *node; return *node;
} }
const MidiSource * const MidiSource * MidiModel::midi_source() const {
MidiModel::midi_source() const
{
return _midi_source; return _midi_source;
} }
void void MidiModel::set_midi_source(MidiSource *source) {
MidiModel::set_midi_source(MidiSource *source)
{
_midi_source = source; _midi_source = source;
} }

View file

@ -724,11 +724,35 @@ MidiTrack::write_immediate_event(size_t size, const Byte* buf)
void void
MidiTrack::MidiControl::set_value(float val) MidiTrack::MidiControl::set_value(float val)
{ {
assert(val >= 0); assert(val >= _list->parameter().min());
assert(val <= 127.0); assert(val <= _list->parameter().max());
if ( ! _list->automation_playback()) { if ( ! _list->automation_playback()) {
Byte ev[3] = { MIDI_CMD_CONTROL, _list->parameter().id(), (int)val }; Byte ev[3] = { _list->parameter().channel(), int(val), 0.0 };
switch(AutomationType type = _list->parameter().type()) {
case MidiCCAutomation:
ev[0] += MIDI_CMD_CONTROL;
ev[1] = _list->parameter().id();
ev[2] = int(val);
break;
case MidiPgmChangeAutomation:
ev[0] += MIDI_CMD_PGM_CHANGE;
break;
case MidiChannelAftertouchAutomation:
ev[0] += MIDI_CMD_CHANNEL_PRESSURE;
break;
case MidiPitchBenderAutomation:
ev[0] += MIDI_CMD_BENDER;
ev[1] = 0x7F & int(val);
ev[2] = 0x7F & (int(val) >> 7);
break;
default:
assert(false);
}
_route->write_immediate_event(3, ev); _route->write_immediate_event(3, ev);
} }

View file

@ -40,6 +40,7 @@ Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
assert(duration() == d); assert(duration() == d);
assert(note() == n); assert(note() == n);
assert(velocity() == v); assert(velocity() == v);
assert(_on_event.channel() == _off_event.channel());
} }
@ -64,6 +65,8 @@ Note::Note(const Note& copy)
assert(note() == copy.note()); assert(note() == copy.note());
assert(velocity() == copy.velocity()); assert(velocity() == copy.velocity());
assert(duration() == copy.duration()); assert(duration() == copy.duration());
assert(_on_event.channel() == _off_event.channel());
assert(channel() == copy.channel());
} }
@ -86,6 +89,8 @@ Note::operator=(const Note& copy)
assert(note() == copy.note()); assert(note() == copy.note());
assert(velocity() == copy.velocity()); assert(velocity() == copy.velocity());
assert(duration() == copy.duration()); assert(duration() == copy.duration());
assert(_on_event.channel() == _off_event.channel());
assert(channel() == copy.channel());
return *this; return *this;
} }

View file

@ -87,13 +87,13 @@ Parameter::to_string() const
} else if (_type == PluginAutomation) { } else if (_type == PluginAutomation) {
return string_compose("parameter-%1", _id); return string_compose("parameter-%1", _id);
} else if (_type == MidiCCAutomation) { } else if (_type == MidiCCAutomation) {
return string_compose("midicc-%1-%2", _channel, _id); return string_compose("midicc-%1-%2", int(_channel), _id);
} else if (_type == MidiPgmChangeAutomation) { } else if (_type == MidiPgmChangeAutomation) {
return string_compose("midi_pgm_change-%1", _channel); return string_compose("midi-pgm-change-%1", int(_channel));
} else if (_type == MidiPitchBenderAutomation) { } else if (_type == MidiPitchBenderAutomation) {
return string_compose("midi_pitch_bender-%1", _channel); return string_compose("midi-pitch-bender-%1", int(_channel));
} else if (_type == MidiChannelAftertouchAutomation) { } else if (_type == MidiChannelAftertouchAutomation) {
return string_compose("midi_channel_aftertouch-%1", _channel); return string_compose("midi-channel-aftertouch-%1", int(_channel));
} else { } else {
PBD::warning << "Uninitialized Parameter to_string() called." << endmsg; PBD::warning << "Uninitialized Parameter to_string() called." << endmsg;
return ""; return "";

View file

@ -451,8 +451,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
void void
SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev) SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& 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());
(unsigned)ev.channel(), ev.time(), ev.size());
for (size_t i=0; i < ev.size(); ++i) { for (size_t i=0; i < ev.size(); ++i) {
printf("%X ", ev.buffer()[i]); printf("%X ", ev.buffer()[i]);
} }