diff options
author | Carl Hetherington <carl@carlh.net> | 2010-12-28 18:19:40 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2010-12-28 18:19:40 +0000 |
commit | f8ebb4582d4d881fbda75a6bc9cd9c50f5c921f3 (patch) | |
tree | 90608a3cb424866b2a164c354d1665a7b0a9d79a /libs/evoral | |
parent | 390f18c1152f2007b790a77c873b50ef48209f44 (diff) |
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
Diffstat (limited to 'libs/evoral')
-rw-r--r-- | libs/evoral/evoral/Sequence.hpp | 65 | ||||
-rw-r--r-- | libs/evoral/src/SMF.cpp | 13 | ||||
-rw-r--r-- | libs/evoral/src/Sequence.cpp | 126 |
3 files changed, 180 insertions, 24 deletions
diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index abc147af8d..77d06a673e 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -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 }; - - 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; + 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; + /** 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; diff --git a/libs/evoral/src/SMF.cpp b/libs/evoral/src/SMF.cpp index e58090f2a3..8cc14290f7 100644 --- a/libs/evoral/src/SMF.cpp +++ b/libs/evoral/src/SMF.cpp @@ -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); diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp index b71e6920d8..78c04a6759 100644 --- a/libs/evoral/src/Sequence.cpp +++ b/libs/evoral/src/Sequence.cpp @@ -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()), @@ -874,6 +968,19 @@ Sequence<Time>::append_sysex_unlocked(const MIDIEvent<Time>& ev, event_id_t /* e } 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 |