Unify program change and bank handling so that they are manipulated together.

git-svn-id: svn://localhost/ardour2/branches/3.0@8346 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2010-12-28 18:19:40 +00:00
parent 390f18c115
commit f8ebb4582d
19 changed files with 930 additions and 254 deletions

View file

@ -172,8 +172,66 @@ public:
Change unmarshal_change (XMLNode *);
};
class PatchChangeDiffCommand : public DiffCommand {
public:
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const std::string &);
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const XMLNode &);
int set_state (const XMLNode &, int version);
XMLNode & get_state ();
void operator() ();
void undo ();
void add (PatchChangePtr);
void remove (PatchChangePtr);
void change_time (PatchChangePtr, TimeType);
void change_channel (PatchChangePtr, uint8_t);
void change_program (PatchChangePtr, uint8_t);
void change_bank (PatchChangePtr, int);
enum Property {
Time,
Channel,
Program,
Bank
};
private:
struct Change {
PatchChangePtr patch;
Property property;
union {
TimeType old_time;
uint8_t old_channel;
int old_bank;
uint8_t old_program;
};
union {
uint8_t new_channel;
TimeType new_time;
uint8_t new_program;
int new_bank;
};
};
typedef std::list<Change> ChangeList;
ChangeList _changes;
std::list<PatchChangePtr> _added;
std::list<PatchChangePtr> _removed;
XMLNode & marshal_change (const Change &);
Change unmarshal_change (XMLNode *);
XMLNode & marshal_patch_change (constPatchChangePtr);
PatchChangePtr unmarshal_patch_change (XMLNode *);
};
MidiModel::NoteDiffCommand* new_note_diff_command (const std::string name = "midi edit");
MidiModel::SysExDiffCommand* new_sysex_diff_command (const std::string name = "midi edit");
MidiModel::PatchChangeDiffCommand* new_patch_change_diff_command (const std::string name = "midi edit");
void apply_command (Session& session, Command* cmd);
void apply_command_as_subcommand (Session& session, Command* cmd);
@ -193,6 +251,7 @@ public:
void set_midi_source (boost::shared_ptr<MidiSource>);
boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
PatchChangePtr find_patch_change (Evoral::event_id_t);
boost::shared_ptr<Evoral::Note<TimeType> > find_note (gint note_id);
boost::shared_ptr<Evoral::Event<TimeType> > find_sysex (gint);

View file

@ -114,6 +114,7 @@ setup_enum_writer ()
MuteMaster::MutePoint _MuteMaster_MutePoint;
MidiModel::NoteDiffCommand::Property _MidiModel_NoteDiffCommand_Property;
MidiModel::SysExDiffCommand::Property _MidiModel_SysExDiffCommand_Property;
MidiModel::PatchChangeDiffCommand::Property _MidiModel_PatchChangeDiffCommand_Property;
WaveformScale _WaveformScale;
WaveformShape _WaveformShape;
QuantizeType _QuantizeType;
@ -536,6 +537,11 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (MidiModel::SysExDiffCommand, Time);
REGISTER (_MidiModel_SysExDiffCommand_Property);
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Time);
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Program);
REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Bank);
REGISTER (_MidiModel_PatchChangeDiffCommand_Property);
REGISTER_ENUM(Linear);
REGISTER_ENUM(Logarithmic);

View file

@ -70,6 +70,16 @@ MidiModel::new_sysex_diff_command (const string name)
return new SysExDiffCommand (ms->model(), name);
}
/** Start a new PatchChangeDiff command */
MidiModel::PatchChangeDiffCommand*
MidiModel::new_patch_change_diff_command (const string name)
{
boost::shared_ptr<MidiSource> ms = _midi_source.lock ();
assert (ms);
return new PatchChangeDiffCommand (ms->model(), name);
}
/** Apply a command.
*
@ -107,6 +117,10 @@ MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
#define SIDE_EFFECT_REMOVALS_ELEMENT "SideEffectRemovals"
#define SYSEX_DIFF_COMMAND_ELEMENT "SysExDiffCommand"
#define DIFF_SYSEXES_ELEMENT "ChangedSysExes"
#define PATCH_CHANGE_DIFF_COMMAND_ELEMENT "PatchChangeDiffCommand"
#define ADDED_PATCH_CHANGES_ELEMENT "AddedPatchChanges"
#define REMOVED_PATCH_CHANGES_ELEMENT "RemovedPatchChanges"
#define DIFF_PATCH_CHANGES_ELEMENT "ChangedPatchChanges"
MidiModel::DiffCommand::DiffCommand(boost::shared_ptr<MidiModel> m, const std::string& name)
: Command (name)
@ -890,6 +904,415 @@ MidiModel::SysExDiffCommand::get_state ()
return *diff_command;
}
MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
: DiffCommand (m, name)
{
assert (_model);
}
MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
: DiffCommand (m, "")
{
assert (_model);
set_state (node, Stateful::loading_state_version);
}
void
MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
{
_added.push_back (p);
}
void
MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
{
_removed.push_back (p);
}
void
MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
{
Change c;
c.property = Time;
c.patch = patch;
c.old_time = patch->time ();
c.new_time = t;
_changes.push_back (c);
}
void
MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
{
Change c;
c.property = Channel;
c.patch = patch;
c.old_channel = patch->channel ();
c.new_channel = channel;
_changes.push_back (c);
}
void
MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
{
Change c;
c.property = Program;
c.patch = patch;
c.old_program = patch->program ();
c.new_program = program;
_changes.push_back (c);
}
void
MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
{
Change c;
c.property = Bank;
c.patch = patch;
c.old_bank = patch->bank ();
c.new_bank = bank;
_changes.push_back (c);
}
void
MidiModel::PatchChangeDiffCommand::operator() ()
{
{
MidiModel::WriteLock lock (_model->edit_lock ());
for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
_model->add_patch_change_unlocked (*i);
}
for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
_model->remove_patch_change_unlocked (*i);
}
set<PatchChangePtr> temporary_removals;
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
switch (i->property) {
case Time:
if (temporary_removals.find (i->patch) == temporary_removals.end()) {
_model->remove_patch_change_unlocked (i->patch);
temporary_removals.insert (i->patch);
}
i->patch->set_time (i->new_time);
break;
case Channel:
i->patch->set_channel (i->new_channel);
break;
case Program:
i->patch->set_program (i->new_program);
break;
case Bank:
i->patch->set_bank (i->new_bank);
break;
}
}
for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
_model->add_patch_change_unlocked (*i);
}
}
_model->ContentsChanged (); /* EMIT SIGNAL */
}
void
MidiModel::PatchChangeDiffCommand::undo ()
{
{
MidiModel::WriteLock lock (_model->edit_lock());
for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
_model->remove_patch_change_unlocked (*i);
}
for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
_model->add_patch_change_unlocked (*i);
}
set<PatchChangePtr> temporary_removals;
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
switch (i->property) {
case Time:
if (temporary_removals.find (i->patch) == temporary_removals.end()) {
_model->remove_patch_change_unlocked (i->patch);
temporary_removals.insert (i->patch);
}
i->patch->set_time (i->old_time);
break;
case Channel:
i->patch->set_channel (i->old_channel);
break;
case Program:
i->patch->set_program (i->old_program);
break;
case Bank:
i->patch->set_bank (i->old_bank);
break;
}
}
for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
_model->add_patch_change_unlocked (*i);
}
}
_model->ContentsChanged (); /* EMIT SIGNAL */
}
XMLNode &
MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
{
XMLNode* n = new XMLNode ("patch-change");
{
ostringstream s (ios::ate);
s << int (p->id ());
n->add_property ("id", s.str());
}
{
ostringstream s (ios::ate);
s << p->time ();
n->add_property ("time", s.str ());
}
{
ostringstream s (ios::ate);
s << int (p->channel ());
n->add_property ("channel", s.str ());
}
{
ostringstream s (ios::ate);
s << int (p->program ());
n->add_property ("program", s.str ());
}
{
ostringstream s (ios::ate);
s << int (p->bank ());
n->add_property ("bank", s.str ());
}
return *n;
}
XMLNode&
MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
{
XMLNode* n = new XMLNode (X_("Change"));
n->add_property (X_("property"), enum_2_string (c.property));
{
ostringstream s (ios::ate);
if (c.property == Time) {
s << c.old_time;
} else if (c.property == Channel) {
s << c.old_channel;
} else if (c.property == Program) {
s << int (c.old_program);
} else if (c.property == Bank) {
s << c.old_bank;
}
n->add_property (X_("old"), s.str ());
}
{
ostringstream s (ios::ate);
if (c.property == Time) {
s << c.new_time;
} else if (c.property == Channel) {
s << c.new_channel;
} else if (c.property == Program) {
s << int (c.new_program);
} else if (c.property == Bank) {
s << c.new_bank;
}
n->add_property (X_("new"), s.str ());
}
{
ostringstream s;
s << c.patch->id ();
n->add_property ("id", s.str ());
}
return *n;
}
MidiModel::PatchChangePtr
MidiModel::PatchChangeDiffCommand::unmarshal_patch_change (XMLNode* n)
{
XMLProperty* prop;
Evoral::event_id_t id;
Evoral::MusicalTime time = 0;
uint8_t channel = 0;
uint8_t program = 0;
int bank = 0;
if ((prop = n->property ("id")) != 0) {
istringstream s (prop->value());
s >> id;
}
if ((prop = n->property ("time")) != 0) {
istringstream s (prop->value ());
s >> time;
}
if ((prop = n->property ("channel")) != 0) {
istringstream s (prop->value ());
s >> channel;
}
if ((prop = n->property ("program")) != 0) {
istringstream s (prop->value ());
s >> program;
}
if ((prop = n->property ("bank")) != 0) {
istringstream s (prop->value ());
s >> bank;
}
PatchChangePtr p (new Evoral::PatchChange<TimeType> (time, channel, program, bank));
p->set_id (id);
return p;
}
MidiModel::PatchChangeDiffCommand::Change
MidiModel::PatchChangeDiffCommand::unmarshal_change (XMLNode* n)
{
XMLProperty* prop;
Change c;
prop = n->property ("property");
assert (prop);
c.property = (Property) string_2_enum (prop->value(), c.property);
prop = n->property ("id");
assert (prop);
Evoral::event_id_t const id = atoi (prop->value().c_str());
prop = n->property ("old");
assert (prop);
{
istringstream s (prop->value ());
if (c.property == Time) {
s >> c.old_time;
} else if (c.property == Channel) {
s >> c.old_channel;
} else if (c.property == Program) {
s >> c.old_program;
} else if (c.property == Bank) {
s >> c.old_bank;
}
}
prop = n->property ("new");
assert (prop);
{
istringstream s (prop->value ());
if (c.property == Time) {
s >> c.new_time;
} else if (c.property == Channel) {
s >> c.new_channel;
} else if (c.property == Program) {
s >> c.new_program;
} else if (c.property == Bank) {
s >> c.new_bank;
}
}
c.patch = _model->find_patch_change (id);
assert (c.patch);
return c;
}
int
MidiModel::PatchChangeDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
{
if (diff_command.name() != PATCH_CHANGE_DIFF_COMMAND_ELEMENT) {
return 1;
}
_added.clear ();
XMLNode* added = diff_command.child (ADDED_PATCH_CHANGES_ELEMENT);
if (added) {
XMLNodeList p = added->children ();
transform (p.begin(), p.end(), back_inserter (_added), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
}
_removed.clear ();
XMLNode* removed = diff_command.child (REMOVED_PATCH_CHANGES_ELEMENT);
if (removed) {
XMLNodeList p = removed->children ();
transform (p.begin(), p.end(), back_inserter (_removed), boost::bind (&PatchChangeDiffCommand::unmarshal_patch_change, this, _1));
}
_changes.clear ();
XMLNode* changed = diff_command.child (DIFF_PATCH_CHANGES_ELEMENT);
if (changed) {
XMLNodeList p = changed->children ();
transform (p.begin(), p.end(), back_inserter (_changes), boost::bind (&PatchChangeDiffCommand::unmarshal_change, this, _1));
}
return 0;
}
XMLNode &
MidiModel::PatchChangeDiffCommand::get_state ()
{
XMLNode* diff_command = new XMLNode (PATCH_CHANGE_DIFF_COMMAND_ELEMENT);
diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
XMLNode* added = diff_command->add_child (ADDED_PATCH_CHANGES_ELEMENT);
for_each (_added.begin(), _added.end(),
boost::bind (
boost::bind (&XMLNode::add_child_nocopy, added, _1),
boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
)
);
XMLNode* removed = diff_command->add_child (REMOVED_PATCH_CHANGES_ELEMENT);
for_each (_removed.begin(), _removed.end(),
boost::bind (
boost::bind (&XMLNode::add_child_nocopy, removed, _1),
boost::bind (&PatchChangeDiffCommand::marshal_patch_change, this, _1)
)
);
XMLNode* changes = diff_command->add_child (DIFF_PATCH_CHANGES_ELEMENT);
for_each (_changes.begin(), _changes.end(),
boost::bind (
boost::bind (&XMLNode::add_child_nocopy, changes, _1),
boost::bind (&PatchChangeDiffCommand::marshal_change, this, _1)
)
);
return *diff_command;
}
/** Write all of the model to a MidiSource (i.e. save the model).
* This is different from manually using read to write to a source in that
* note off events are written regardless of the track mode. This is so the
@ -1084,6 +1507,18 @@ MidiModel::find_note (gint note_id)
return NotePtr();
}
MidiModel::PatchChangePtr
MidiModel::find_patch_change (Evoral::event_id_t id)
{
for (PatchChanges::iterator i = patch_changes().begin(); i != patch_changes().end(); ++i) {
if ((*i)->id() == id) {
return *i;
}
}
return PatchChangePtr ();
}
boost::shared_ptr<Evoral::Event<MidiModel::TimeType> >
MidiModel::find_sysex (gint sysex_id)
{

View file

@ -3196,6 +3196,28 @@ Session::restore_history (string snapshot_name)
error << _("Failed to downcast MidiSource for NoteDiffCommand") << endmsg;
}
} else if (n->name() == "SysExDiffCommand") {
PBD::ID id (n->property("midi-source")->value());
boost::shared_ptr<MidiSource> midi_source =
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
if (midi_source) {
ut->add_command (new MidiModel::SysExDiffCommand (midi_source->model(), *n));
} else {
error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
}
} else if (n->name() == "PatchChangeDiffCommand") {
PBD::ID id (n->property("midi-source")->value());
boost::shared_ptr<MidiSource> midi_source =
boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
if (midi_source) {
ut->add_command (new MidiModel::PatchChangeDiffCommand (midi_source->model(), *n));
} else {
error << _("Failed to downcast MidiSource for PatchChangeDiffCommand") << endmsg;
}
} else if (n->name() == "StatefulDiffCommand") {
if ((c = stateful_diff_command_factory (n))) {
ut->add_command (c);

View file

@ -31,6 +31,7 @@
#include "evoral/Parameter.hpp"
#include "evoral/ControlSet.hpp"
#include "evoral/ControlList.hpp"
#include "evoral/PatchChange.hpp"
namespace Evoral {
@ -101,7 +102,7 @@ public:
void append(const Event<Time>& ev, Evoral::event_id_t evid);
inline size_t n_notes() const { return _notes.size(); }
inline bool empty() const { return _notes.size() == 0 && ControlSet::controls_empty(); }
inline bool empty() const { return _notes.empty() && _sysexes.empty() && _patch_changes.empty() && ControlSet::controls_empty(); }
inline static bool note_time_comparator(const boost::shared_ptr< const Note<Time> >& a,
const boost::shared_ptr< const Note<Time> >& b) {
@ -177,6 +178,19 @@ public:
inline SysExes& sysexes() { return _sysexes; }
inline const SysExes& sysexes() const { return _sysexes; }
typedef boost::shared_ptr<PatchChange<Time> > PatchChangePtr;
typedef boost::shared_ptr<const PatchChange<Time> > constPatchChangePtr;
struct EarlierPatchChangeComparator {
inline bool operator() (constPatchChangePtr a, constPatchChangePtr b) const {
return a->time() < b->time();
}
};
typedef std::multiset<PatchChangePtr, EarlierPatchChangeComparator> PatchChanges;
inline PatchChanges& patch_changes () { return _patch_changes; }
inline const PatchChanges& patch_changes () const { return _patch_changes; }
private:
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
public:
@ -208,19 +222,24 @@ public:
friend class Sequence<Time>;
typedef std::vector<ControlIterator> ControlIterators;
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX };
enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE };
const Sequence<Time>* _seq;
boost::shared_ptr< Event<Time> > _event;
mutable ActiveNotes _active_notes;
MIDIMessageType _type;
bool _is_end;
typename Sequence::ReadLock _lock;
typename Notes::const_iterator _note_iter;
typename SysExes::const_iterator _sysex_iter;
ControlIterators _control_iters;
ControlIterators::iterator _control_iter;
bool _force_discrete;
const Sequence<Time>* _seq;
boost::shared_ptr< Event<Time> > _event;
mutable ActiveNotes _active_notes;
/** If the iterator is pointing at a patch change, this is the index of the
* sub-message within that change.
*/
int _active_patch_change_message;
MIDIMessageType _type;
bool _is_end;
typename Sequence::ReadLock _lock;
typename Notes::const_iterator _note_iter;
typename SysExes::const_iterator _sysex_iter;
typename PatchChanges::const_iterator _patch_change_iter;
ControlIterators _control_iters;
ControlIterators::iterator _control_iter;
bool _force_discrete;
};
const_iterator begin (
@ -233,6 +252,7 @@ public:
const const_iterator& end() const { return _end_iter; }
typename Notes::const_iterator note_lower_bound (Time t) const;
typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const;
bool control_to_midi_event(boost::shared_ptr< Event<Time> >& ev,
const ControlIterator& iter) const;
@ -247,6 +267,9 @@ public:
bool add_note_unlocked (const NotePtr note, void* arg = 0);
void remove_note_unlocked(const constNotePtr note);
void add_patch_change_unlocked (const PatchChangePtr);
void remove_patch_change_unlocked (const constPatchChangePtr);
uint8_t lowest_note() const { return _lowest_note; }
uint8_t highest_note() const { return _highest_note; }
@ -276,6 +299,7 @@ private:
void append_note_off_unlocked(NotePtr);
void append_control_unlocked(const Parameter& param, Time time, double value, Evoral::event_id_t);
void append_sysex_unlocked(const MIDIEvent<Time>& ev, Evoral::event_id_t);
void append_patch_change_unlocked (const PatchChange<Time>&, Evoral::event_id_t);
void get_notes_by_pitch (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;
void get_notes_by_velocity (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;
@ -284,13 +308,20 @@ private:
const TypeMap& _type_map;
Notes _notes; // notes indexed by time
Pitches _pitches[16]; // notes indexed by channel+pitch
SysExes _sysexes;
Notes _notes; // notes indexed by time
Pitches _pitches[16]; // notes indexed by channel+pitch
SysExes _sysexes;
PatchChanges _patch_changes;
typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
WriteNotes _write_notes[16];
/** Current bank number on each channel so that we know what
* to put in PatchChange events when program changes are
* seen.
*/
int _bank[16];
const const_iterator _end_iter;
bool _percussive;

View file

@ -281,10 +281,18 @@ SMF::append_event_delta(uint32_t delta_t, uint32_t size, const uint8_t* buf, eve
smf_event_t* event;
/* XXX july 2010: currently only store event ID's for notes
/* XXX july 2010: currently only store event ID's for notes, program changes and bank changes
*/
if (((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON || ((buf[0] & 0xf0) == MIDI_CMD_NOTE_OFF)) && note_id >= 0) {
uint8_t const c = buf[0] & 0xf0;
bool const store_id = (
c == MIDI_CMD_NOTE_ON ||
c == MIDI_CMD_NOTE_OFF ||
c == MIDI_CMD_PGM_CHANGE ||
(c == MIDI_CMD_CONTROL && (buf[1] == MIDI_CTL_MSB_BANK || buf[1] == MIDI_CTL_LSB_BANK))
);
if (store_id && note_id >= 0) {
int idlen;
int lenlen;
uint8_t idbuf[16];
@ -293,7 +301,6 @@ SMF::append_event_delta(uint32_t delta_t, uint32_t size, const uint8_t* buf, eve
event = smf_event_new ();
assert(event != NULL);
/* generate VLQ representation of note ID */
idlen = smf_format_vlq (idbuf, sizeof(idbuf), note_id);

View file

@ -64,10 +64,12 @@ Sequence<Time>::const_iterator::const_iterator()
template<typename Time>
Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t, bool force_discrete, std::set<Evoral::Parameter> const & filtered)
: _seq(&seq)
, _active_patch_change_message (0)
, _type(NIL)
, _is_end((t == DBL_MAX) || seq.empty())
, _note_iter(seq.notes().end())
, _sysex_iter(seq.sysexes().end())
, _patch_change_iter(seq.patch_changes().end())
, _control_iter(_control_iters.end())
, _force_discrete (force_discrete)
{
@ -94,6 +96,15 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
}
assert(_sysex_iter == seq.sysexes().end() || (*_sysex_iter)->time() >= t);
// Find first patch event at or after t
for (typename Sequence<Time>::PatchChanges::const_iterator i = seq.patch_changes().begin(); i != seq.patch_changes().end(); ++i) {
if ((*i)->time() >= t) {
_patch_change_iter = i;
break;
}
}
assert (_patch_change_iter == seq.patch_changes().end() || (*_patch_change_iter)->time() >= t);
// Find first control event after t
ControlIterator earliest_control(boost::shared_ptr<ControlList>(), DBL_MAX, 0.0);
_control_iters.reserve(seq._controls.size());
@ -163,6 +174,11 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
earliest_t = (*_sysex_iter)->time();
}
if (_patch_change_iter != seq.patch_changes().end() && ((*_patch_change_iter)->time() < earliest_t || _type == NIL)) {
_type = PATCH_CHANGE;
earliest_t = (*_patch_change_iter)->time ();
}
if (_control_iter != _control_iters.end()
&& earliest_control.list && earliest_control.x >= t
&& (earliest_control.x < earliest_t || _type == NIL)) {
@ -185,6 +201,10 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at control event @ %1\n", earliest_t));
seq.control_to_midi_event(_event, earliest_control);
break;
case PATCH_CHANGE:
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Starting at patch change event @ %1\n", earliest_t));
_event = boost::shared_ptr<Event<Time> > (new Event<Time> ((*_patch_change_iter)->message (_active_patch_change_message), true));
break;
default:
break;
}
@ -194,12 +214,14 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
_type = NIL;
_is_end = true;
} else {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%x : 0x%x @ %f\n",
DEBUG_TRACE (DEBUG::Sequence, string_compose ("New iterator = 0x%1 : 0x%2 @ %3\n",
(int)_event->event_type(),
(int)((MIDIEvent<Time>*)_event.get())->type(),
_event->time()));
assert(midi_event_is_valid(_event->buffer(), _event->size()));
}
}
template<typename Time>
@ -219,6 +241,8 @@ Sequence<Time>::const_iterator::invalidate()
if (_seq) {
_note_iter = _seq->notes().end();
_sysex_iter = _seq->sysexes().end();
_patch_change_iter = _seq->patch_changes().end();
_active_patch_change_message = 0;
}
_control_iter = _control_iters.end();
_lock.reset();
@ -291,6 +315,13 @@ Sequence<Time>::const_iterator::operator++()
case SYSEX:
++_sysex_iter;
break;
case PATCH_CHANGE:
++_active_patch_change_message;
if (_active_patch_change_message == (*_patch_change_iter)->messages()) {
++_patch_change_iter;
_active_patch_change_message = 0;
}
break;
default:
assert(false);
}
@ -329,6 +360,14 @@ Sequence<Time>::const_iterator::operator++()
}
}
// Use the next earliest patch change iff it's earlier than the SysEx
if (_patch_change_iter != _seq->patch_changes().end()) {
if (_type == NIL || (*_patch_change_iter)->time() < earliest_t) {
_type = PATCH_CHANGE;
earliest_t = (*_patch_change_iter)->time();
}
}
// Set event to reflect new position
switch (_type) {
case NOTE_ON:
@ -350,6 +389,10 @@ Sequence<Time>::const_iterator::operator++()
DEBUG_TRACE(DEBUG::Sequence, "iterator = sysex\n");
*_event = *(*_sysex_iter);
break;
case PATCH_CHANGE:
DEBUG_TRACE(DEBUG::Sequence, "iterator = patch change\n");
*_event = (*_patch_change_iter)->message (_active_patch_change_message);
break;
default:
DEBUG_TRACE(DEBUG::Sequence, "iterator = end\n");
_is_end = true;
@ -386,8 +429,10 @@ Sequence<Time>::const_iterator::operator=(const const_iterator& other)
_is_end = other._is_end;
_note_iter = other._note_iter;
_sysex_iter = other._sysex_iter;
_patch_change_iter = other._patch_change_iter;
_control_iters = other._control_iters;
_force_discrete = other._force_discrete;
_active_patch_change_message = other._active_patch_change_message;
if (other._lock)
_lock = _seq->read_lock();
@ -421,6 +466,10 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sequence constructed: %1\n", this));
assert(_end_iter._is_end);
assert( ! _end_iter._lock);
for (int i = 0; i < 16; ++i) {
_bank[i] = 0;
}
}
template<typename Time>
@ -446,6 +495,15 @@ Sequence<Time>::Sequence(const Sequence<Time>& other)
_sysexes.push_back (n);
}
for (typename PatchChanges::const_iterator i = other._patch_changes.begin(); i != other._patch_changes.end(); ++i) {
PatchChangePtr n (new PatchChange<Time> (**i));
_patch_changes.insert (n);
}
for (int i = 0; i < 16; ++i) {
_bank[i] = other._bank[i];
}
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Sequence copied: %1\n", this));
assert(_end_iter._is_end);
assert(! _end_iter._lock);
@ -697,6 +755,34 @@ Sequence<Time>::remove_note_unlocked(const constNotePtr note)
}
}
template<typename Time>
void
Sequence<Time>::add_patch_change_unlocked (PatchChangePtr p)
{
_patch_changes.insert (p);
if (p->id () < 0) {
p->set_id (Evoral::next_event_id ());
}
}
template<typename Time>
void
Sequence<Time>::remove_patch_change_unlocked (const constPatchChangePtr p)
{
typename Sequence<Time>::PatchChanges::iterator i = patch_change_lower_bound (p->time ());
while (i != _patch_changes.end() && (*i)->time() == p->time()) {
typename Sequence<Time>::PatchChanges::iterator tmp = i;
++tmp;
if (*i == p) {
_patch_changes.erase (i);
}
i = tmp;
}
}
/** Append \a ev to model. NOT realtime safe.
*
* The timestamp of event is expected to be relative to
@ -730,14 +816,22 @@ Sequence<Time>::append(const Event<Time>& event, event_id_t evid)
append_note_off_unlocked (note);
} else if (ev.is_sysex()) {
append_sysex_unlocked(ev, evid);
} else if (ev.is_cc() && (ev.cc_number() == MIDI_CTL_MSB_BANK || ev.cc_number() == MIDI_CTL_LSB_BANK)) {
/* note bank numbers in our _bank[] array, so that we can write an event when the program change arrives */
if (ev.cc_number() == MIDI_CTL_MSB_BANK) {
_bank[ev.channel()] &= (0x7f << 7);
_bank[ev.channel()] |= ev.cc_value() << 7;
} else {
_bank[ev.channel()] &= 0x7f;
_bank[ev.channel()] |= ev.cc_value();
}
} else if (ev.is_cc()) {
append_control_unlocked(
Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()),
ev.time(), ev.cc_value(), evid);
} else if (ev.is_pgm_change()) {
append_control_unlocked(
Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
ev.time(), ev.pgm_number(), evid);
/* write a patch change with this program change and any previously set-up bank number */
append_patch_change_unlocked (PatchChange<Time> (ev.time(), ev.channel(), ev.pgm_number(), _bank[ev.channel()]), evid);
} else if (ev.is_pitch_bender()) {
append_control_unlocked(
Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()),
@ -873,6 +967,19 @@ Sequence<Time>::append_sysex_unlocked(const MIDIEvent<Time>& ev, event_id_t /* e
_sysexes.push_back(event);
}
template<typename Time>
void
Sequence<Time>::append_patch_change_unlocked (const PatchChange<Time>& ev, event_id_t id)
{
PatchChangePtr p (new PatchChange<Time> (ev));
if (p->id() < 0) {
p->set_id (id);
}
_patch_changes.insert (p);
}
template<typename Time>
bool
Sequence<Time>::contains (const NotePtr& note) const
@ -956,6 +1063,17 @@ Sequence<Time>::note_lower_bound (Time t) const
return i;
}
/** Return the earliest patch change with time >= t */
template<typename Time>
typename Sequence<Time>::PatchChanges::const_iterator
Sequence<Time>::patch_change_lower_bound (Time t) const
{
PatchChangePtr search (new PatchChange<Time> (t, 0, 0, 0));
typename Sequence<Time>::PatchChanges::const_iterator i = _patch_changes.lower_bound (search);
assert (i == _patch_changes.end() || (*i)->time() >= t);
return i;
}
template<typename Time>
void
Sequence<Time>::get_notes (Notes& n, NoteOperator op, uint8_t val, int chan_mask) const