* recording/playing/saving/loading program changes basically seems to work now
git-svn-id: svn://localhost/ardour2/branches/3.0@3288 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
d9de6f2c4c
commit
0fc3087d7d
|
@ -110,7 +110,7 @@ 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; }
|
||||||
|
|
||||||
typedef std::vector<MIDI::Event> PgmChanges;
|
typedef std::vector< boost::shared_ptr<MIDI::Event> > PgmChanges;
|
||||||
inline PgmChanges& pgm_changes() { return _pgm_changes; }
|
inline PgmChanges& pgm_changes() { return _pgm_changes; }
|
||||||
inline const PgmChanges& pgm_changes() const { return _pgm_changes; }
|
inline const PgmChanges& pgm_changes() const { return _pgm_changes; }
|
||||||
|
|
||||||
|
@ -219,6 +219,7 @@ private:
|
||||||
void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
|
void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
|
||||||
void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
|
void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
|
||||||
void append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value);
|
void append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value);
|
||||||
|
void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number);
|
||||||
|
|
||||||
mutable Glib::RWLock _lock;
|
mutable Glib::RWLock _lock;
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
|
||||||
if (_note_iter != model.notes().end()) {
|
if (_note_iter != model.notes().end()) {
|
||||||
_event = MIDI::Event((*_note_iter)->on_event(), false);
|
_event = MIDI::Event((*_note_iter)->on_event(), false);
|
||||||
_active_notes.push(*_note_iter);
|
_active_notes.push(*_note_iter);
|
||||||
cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
|
//cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
|
||||||
++_note_iter;
|
++_note_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,16 +133,16 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
|
||||||
|
|
||||||
_pgm_change_iter = model.pgm_changes().end();
|
_pgm_change_iter = model.pgm_changes().end();
|
||||||
// find first program change which begins after t
|
// find first program change which begins after t
|
||||||
for (vector<MIDI::Event>::const_iterator i = model.pgm_changes().begin(); i != model.pgm_changes().end(); ++i) {
|
for (PgmChanges::const_iterator i = model.pgm_changes().begin(); i != model.pgm_changes().end(); ++i) {
|
||||||
if (i->time() >= t) {
|
if ((*i)->time() >= t) {
|
||||||
_pgm_change_iter = i;
|
_pgm_change_iter = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_pgm_change_iter != model.pgm_changes().end()) {
|
if(_pgm_change_iter != model.pgm_changes().end()) {
|
||||||
if(_pgm_change_iter->time() <= _event.time()) {
|
if((*_pgm_change_iter)->time() <= _event.time()) {
|
||||||
_event = MIDI::Event((*_pgm_change_iter), true);
|
_event = MIDI::Event(*(*_pgm_change_iter), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,15 @@ 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");
|
||||||
|
|
||||||
assert(_event.is_note() || _event.is_cc());
|
cerr << "const_iterator::operator++: _event type:" << hex << "0x" << int(_event.type())
|
||||||
|
<< " buffer: 0x" << int(_event.buffer()[0]) << " 0x" << int(_event.buffer()[1])
|
||||||
|
<< " 0x" << int(_event.buffer()[2]) << endl;
|
||||||
|
|
||||||
|
if(! (_event.is_note() || _event.is_cc() || _event.is_pgm_change())) {
|
||||||
|
cerr << "FAILED pgm change vector size: " << _model->pgm_changes().size() << endl;
|
||||||
|
cerr << "FAILED event buffer: " << hex << int(_event.buffer()[0]) << int(_event.buffer()[1]) << int(_event.buffer()[2]) << endl;
|
||||||
|
}
|
||||||
|
assert(_event.is_note() || _event.is_cc() || _event.is_pgm_change());
|
||||||
|
|
||||||
// TODO: This code crashes at the marked section
|
// TODO: This code crashes at the marked section
|
||||||
/*
|
/*
|
||||||
|
@ -237,9 +245,12 @@ MidiModel::const_iterator::operator++()
|
||||||
type = CC;
|
type = CC;
|
||||||
*/
|
*/
|
||||||
if(_pgm_change_iter != _model->pgm_changes().end()) {
|
if(_pgm_change_iter != _model->pgm_changes().end()) {
|
||||||
if(_pgm_change_iter->time() <= t) {
|
if((*_pgm_change_iter)->time() <= t) {
|
||||||
type = PGM_CHANGE;
|
type = PGM_CHANGE;
|
||||||
t = _pgm_change_iter->time();
|
t = (*_pgm_change_iter)->time();
|
||||||
|
cerr << "operator++ got PGM CHANGE with time " << t << " and type " << hex
|
||||||
|
<< int((*_pgm_change_iter)->type()) << " and channel " << int((*_pgm_change_iter)->channel())
|
||||||
|
<< " and program " << int((*_pgm_change_iter)->pgm_number()) << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +268,8 @@ MidiModel::const_iterator::operator++()
|
||||||
_model->control_to_midi_event(_event, *_control_iter);
|
_model->control_to_midi_event(_event, *_control_iter);
|
||||||
} else if (type == PGM_CHANGE) {
|
} else if (type == PGM_CHANGE) {
|
||||||
cerr << "********** MIDI Iterator = program change" << endl;
|
cerr << "********** MIDI Iterator = program change" << endl;
|
||||||
_event = MIDI::Event(*_pgm_change_iter, true);
|
_event = MIDI::Event(*(*_pgm_change_iter), false);
|
||||||
|
++_pgm_change_iter;
|
||||||
} else {
|
} else {
|
||||||
cerr << "********** MIDI Iterator = END" << endl;
|
cerr << "********** MIDI Iterator = END" << endl;
|
||||||
_is_end = true;
|
_is_end = true;
|
||||||
|
@ -327,16 +339,16 @@ MidiModel::MidiModel(MidiSource *s, size_t size)
|
||||||
size_t
|
size_t
|
||||||
MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const
|
MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, 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;
|
||||||
|
|
||||||
size_t read_events = 0;
|
size_t read_events = 0;
|
||||||
|
|
||||||
if (start != _next_read) {
|
if (start != _next_read) {
|
||||||
_read_iter = const_iterator(*this, (double)start);
|
_read_iter = const_iterator(*this, (double)start);
|
||||||
cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
//cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
||||||
} else {
|
} else {
|
||||||
cerr << "Using cached iterator at " << _next_read << endl;
|
//cerr << "Using cached iterator at " << _next_read << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
_next_read = start + nframes;
|
_next_read = start + nframes;
|
||||||
|
@ -344,11 +356,13 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, 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 << " MM::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;
|
||||||
}
|
}
|
||||||
|
@ -451,18 +465,23 @@ MidiModel::append(const MIDI::Event& ev)
|
||||||
{
|
{
|
||||||
write_lock();
|
write_lock();
|
||||||
_edited = true;
|
_edited = true;
|
||||||
|
|
||||||
|
cerr << "MidiModel::append event type: " << hex << "0x" << int(ev.type()) << endl;
|
||||||
|
|
||||||
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
||||||
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_cc_unlocked(ev.channel(), ev.time(), ev.cc_number(), ev.cc_value());
|
append_cc_unlocked(ev.channel(), ev.time(), ev.cc_number(), ev.cc_value());
|
||||||
else
|
} else if (ev.is_pgm_change()) {
|
||||||
|
append_pgm_change_unlocked(ev.channel(), ev.time(), ev.pgm_number());
|
||||||
|
} else {
|
||||||
printf("MM Unknown event type %X\n", ev.type());
|
printf("MM Unknown event type %X\n", ev.type());
|
||||||
|
}
|
||||||
|
|
||||||
write_unlock();
|
write_unlock();
|
||||||
}
|
}
|
||||||
|
@ -532,8 +551,8 @@ MidiModel::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
|
||||||
void
|
void
|
||||||
MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value)
|
MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
assert(chan < 16);
|
assert(chan < 16);
|
||||||
assert(_writing);
|
assert(_writing);
|
||||||
|
@ -545,6 +564,25 @@ MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t
|
||||||
control->list()->fast_simple_add(time, (double)value);
|
control->list()->fast_simple_add(time, (double)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiModel::append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number)
|
||||||
|
{
|
||||||
|
cerr << "MidiModel::append_pgm_change_unlocked: channel " << int(chan) << " time: " << time << " program number: " << int(number) <<endl;
|
||||||
|
assert(chan < 16);
|
||||||
|
assert(_writing);
|
||||||
|
_edited = true;
|
||||||
|
|
||||||
|
boost::shared_ptr<MIDI::Event> event_ptr(new MIDI::Event(time, 3, 0, true));
|
||||||
|
event_ptr->set_type(MIDI_CMD_PGM_CHANGE);
|
||||||
|
event_ptr->set_channel(chan);
|
||||||
|
event_ptr->set_pgm_number(number);
|
||||||
|
_pgm_changes.push_back(event_ptr);
|
||||||
|
cerr << "MidiModel::append_pgm_change_unlocked: appended pgm change" << endl;
|
||||||
|
for(PgmChanges::iterator i = _pgm_changes.begin(); i != _pgm_changes.end(); ++i) {
|
||||||
|
cerr << "_pgm_changes contents: channel " << int((*i)->channel()) << dec << " time: " << int((*i)->time()) << hex << " program number: " << int(int((*i)->pgm_number())) <<endl;
|
||||||
|
}
|
||||||
|
//<< int(_pgm_changes.) << " time: " << time << " program number: " << int(number) <<endl;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
|
MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
|
||||||
|
@ -820,10 +858,18 @@ MidiModel::DeltaCommand::get_state ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct EventTimeComparator {
|
||||||
|
typedef const MIDI::Event* value_type;
|
||||||
|
inline bool operator()(const MIDI::Event* a,
|
||||||
|
const MIDI::Event* b) const {
|
||||||
|
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
|
||||||
* MidiModel::read and MidiSource::write, but this is more efficient
|
* MidiModel::read and MidiSource::write, but this is more efficient
|
||||||
|
@ -845,9 +891,39 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
||||||
|
|
||||||
read_lock();
|
read_lock();
|
||||||
|
|
||||||
LaterNoteEndComparator cmp;
|
//LaterNoteEndComparator cmp;
|
||||||
ActiveNotes active_notes(cmp);
|
//ActiveNotes active_notes(cmp);
|
||||||
|
|
||||||
|
EventTimeComparator comp;
|
||||||
|
typedef std::priority_queue<
|
||||||
|
MIDI::Event*,
|
||||||
|
std::deque<MIDI::Event*>,
|
||||||
|
EventTimeComparator> MidiEvents;
|
||||||
|
|
||||||
|
MidiEvents events(comp);
|
||||||
|
|
||||||
|
|
||||||
|
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
|
||||||
|
events.push(&(*n)->on_event());
|
||||||
|
events.push(&(*n)->off_event());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PgmChanges::const_iterator p = _pgm_changes.begin(); p != _pgm_changes.end(); ++p) {
|
||||||
|
events.push((*p).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!events.empty()) {
|
||||||
|
source->append_event_unlocked(Frames, *events.top());
|
||||||
|
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;
|
||||||
|
events.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Why sort manyally, when a priority queue does the job for us,
|
||||||
|
* or am I missing something???
|
||||||
|
*
|
||||||
// Foreach note
|
// Foreach note
|
||||||
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
|
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
|
||||||
|
|
||||||
|
@ -862,6 +938,13 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write program changes preceding this note on
|
||||||
|
if(p != _pgm_changes.end() && ((*p)->time() <= (*n)->time())) {
|
||||||
|
const MIDI::Event& pgm_change_event = *(*p);
|
||||||
|
source->append_event_unlocked(Frames, pgm_change_event);
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
|
||||||
// Write this note on
|
// Write this note on
|
||||||
source->append_event_unlocked(Frames, (*n)->on_event());
|
source->append_event_unlocked(Frames, (*n)->on_event());
|
||||||
|
@ -874,7 +957,8 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
||||||
source->append_event_unlocked(Frames, active_notes.top()->off_event());
|
source->append_event_unlocked(Frames, active_notes.top()->off_event());
|
||||||
active_notes.pop();
|
active_notes.pop();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
_edited = false;
|
_edited = false;
|
||||||
|
|
||||||
read_unlock();
|
read_unlock();
|
||||||
|
|
|
@ -451,12 +451,12 @@ 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]);
|
||||||
}
|
}
|
||||||
printf("\n");*/
|
printf("\n");
|
||||||
|
|
||||||
assert(ev.time() >= 0);
|
assert(ev.time() >= 0);
|
||||||
assert(ev.time() >= _last_ev_time);
|
assert(ev.time() >= _last_ev_time);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user