X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fardour%2Fmidi_model.h;h=e959535a36798f90c9aedfffe2d6806f370d0e0e;hb=f25d9b122046d9ccf81108afc2fb466a32f9cbcc;hp=2481aa8d342f63d2a6cdd4bc78cdcc120a77c75d;hpb=7a9b4a0aa2f5ccdcd9257a0df12917c0e3b1fd91;p=ardour.git diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 2481aa8d34..e959535a36 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -18,7 +18,7 @@ */ -#ifndef __ardour_midi_model_h__ +#ifndef __ardour_midi_model_h__ #define __ardour_midi_model_h__ #include @@ -26,224 +26,163 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include "pbd/command.h" +#include "ardour/types.h" +#include "ardour/midi_buffer.h" +#include "ardour/midi_ring_buffer.h" +#include "ardour/automatable_sequence.h" +#include "ardour/types.h" +#include "evoral/Note.hpp" +#include "evoral/Sequence.hpp" namespace ARDOUR { class Session; class MidiSource; - -/** - * This class keeps track of the current x and y for a control - */ -class MidiControlIterator { -public: - boost::shared_ptr automation_list; - double x; - double y; - - MidiControlIterator(boost::shared_ptr a_list, - double a_x, - double a_y) - : automation_list(a_list) - , x(a_x) - , y(a_y) - {} -}; - /** This is a higher level (than MidiBuffer) model of MIDI data, with separate * representations for notes (instead of just unassociated note on/off events) * and controller data. Controller data is represented as part of the * Automatable base (i.e. in a map of AutomationList, keyed by Parameter). + * Because of this MIDI controllers and automatable controllers/widgets/etc + * are easily interchangeable. */ -class MidiModel : public boost::noncopyable, public Automatable { +class MidiModel : public AutomatableSequence { public: - MidiModel(MidiSource* s, size_t size=0); - - void write_lock(); - void write_unlock(); - - void read_lock() const; - void read_unlock() const; - - void clear(); + typedef Evoral::MusicalTime TimeType; - NoteMode note_mode() const { return _note_mode; } - void set_note_mode(NoteMode mode) { _note_mode = mode; } + MidiModel(MidiSource* s); - void start_write(); - bool writing() const { return _writing; } - void end_write(bool delete_stuck=false); + NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } + void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; - size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; - - /** Resizes vector if necessary (NOT realtime safe) */ - void append(const MIDI::Event& ev); - - inline const boost::shared_ptr note_at(unsigned i) const { return _notes[i]; } - inline const boost::shared_ptr note_at(unsigned i) { return _notes[i]; } - - inline size_t n_notes() const { return _notes.size(); } - inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; } - - inline static bool note_time_comparator (const boost::shared_ptr a, - const boost::shared_ptr b) { - return a->time() < b->time(); - } - - struct LaterNoteEndComparator { - typedef const Note* value_type; - inline bool operator()(const boost::shared_ptr a, - const boost::shared_ptr b) const { - return a->end_time() > b->end_time(); - } - }; - - typedef std::vector< boost::shared_ptr > Notes; - inline Notes& notes() { return _notes; } - inline const Notes& notes() const { return _notes; } - - /** Add/Remove notes. - * Technically all operations can be implemented as one of these. - */ - class DeltaCommand : public Command - { + class DiffCommand : public Command { public: - DeltaCommand (boost::shared_ptr m, const std::string& name); - DeltaCommand (boost::shared_ptr, const XMLNode& node); + enum Property { + NoteNumber, + Velocity, + StartTime, + Length, + Channel + }; + + DiffCommand (boost::shared_ptr m, const std::string& name); + DiffCommand (boost::shared_ptr m, const XMLNode& node); const std::string& name() const { return _name; } - + void operator()(); void undo(); - - int set_state (const XMLNode&); + + int set_state (const XMLNode&, int version); XMLNode& get_state (); - void add(const boost::shared_ptr note); - void remove(const boost::shared_ptr note); - - private: - XMLNode &marshal_note(const boost::shared_ptr note); - boost::shared_ptr unmarshal_note(XMLNode *xml_note); - - boost::shared_ptr _model; - const std::string _name; - - typedef std::list< boost::shared_ptr > NoteList; - + void add (const NotePtr note); + void remove (const NotePtr note); + void side_effect_remove (const NotePtr note); + + void change (const NotePtr note, Property prop, uint8_t new_value); + void change (const NotePtr note, Property prop, TimeType new_time); + + bool adds_or_removes() const { + return !_added_notes.empty() || !_removed_notes.empty(); + } + + DiffCommand& operator+= (const DiffCommand& other); + boost::shared_ptr model() const { return _model; } + + private: + boost::shared_ptr _model; + const std::string _name; + + struct NoteChange { + DiffCommand::Property property; + NotePtr note; + union { + uint8_t old_value; + TimeType old_time; + }; + union { + uint8_t new_value; + TimeType new_time; + }; + }; + + typedef std::list ChangeList; + ChangeList _changes; + + typedef std::list< boost::shared_ptr< Evoral::Note > > NoteList; NoteList _added_notes; NoteList _removed_notes; + + std::set side_effect_removals; + + XMLNode &marshal_change(const NoteChange&); + NoteChange unmarshal_change(XMLNode *xml_note); + + XMLNode &marshal_note(const NotePtr note); + NotePtr unmarshal_note(XMLNode *xml_note); }; - MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); - void apply_command(Command* cmd); + MidiModel::DiffCommand* new_diff_command(const std::string name="midi edit"); + void apply_command(Session& session, Command* cmd); + void apply_command_as_subcommand(Session& session, Command* cmd); - bool edited() const { return _edited; } - void set_edited(bool yn) { _edited = yn; } + bool sync_to_source (); bool write_to(boost::shared_ptr source); - + bool write_section_to (boost::shared_ptr source, Evoral::MusicalTime begin = Evoral::MinMusicalTime, + Evoral::MusicalTime end = Evoral::MaxMusicalTime); + // MidiModel doesn't use the normal AutomationList serialisation code // since controller data is stored in the .mid XMLNode& get_state(); int set_state(const XMLNode&) { return 0; } - sigc::signal ContentsChanged; - - /** Read iterator */ - class const_iterator { - public: - const_iterator(const MidiModel& model, double t); - ~const_iterator(); - - inline bool locked() const { return _locked; } - - const MIDI::Event& operator*() const { return *_event; } - const boost::shared_ptr operator->() const { return _event; } - const boost::shared_ptr get_event_pointer() { return _event; } - - const const_iterator& operator++(); // prefix only - bool operator==(const const_iterator& other) const; - bool operator!=(const const_iterator& other) const { return ! operator==(other); } - - const_iterator& operator=(const const_iterator& other); - - private: - friend class MidiModel; - - const MidiModel* _model; - boost::shared_ptr _event; - - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; - - mutable ActiveNotes _active_notes; - - bool _is_end; - bool _locked; - Notes::const_iterator _note_iter; - std::vector _control_iters; - std::vector::iterator _control_iter; - }; - - const_iterator begin() const { return const_iterator(*this, 0); } - const const_iterator& end() const { return _end_iter; } - + PBD::Signal0 ContentsChanged; + const MidiSource* midi_source() const { return _midi_source; } - void set_midi_source(MidiSource* source) { _midi_source = source; } - bool control_to_midi_event(boost::shared_ptr& ev, const MidiControlIterator& iter) const; - -private: - friend class DeltaCommand; - void add_note_unlocked(const boost::shared_ptr note); - void remove_note_unlocked(const boost::shared_ptr note); + void set_midi_source (MidiSource *); - friend class const_iterator; + boost::shared_ptr > find_note (NotePtr); + boost::shared_ptr > find_note (gint note_id); -#ifndef NDEBUG - bool is_sorted() const; -#endif + InsertMergePolicy insert_merge_policy () const; + void set_insert_merge_policy (InsertMergePolicy); - 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_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte); - void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number); + boost::shared_ptr control_factory(const Evoral::Parameter& id); - mutable Glib::RWLock _lock; +protected: + int resolve_overlaps_unlocked (const NotePtr, void* arg = 0); - Notes _notes; - - NoteMode _note_mode; - - typedef std::vector WriteNotes; - WriteNotes _write_notes[16]; - bool _writing; - bool _edited; - - typedef std::vector< boost::shared_ptr > AutomationLists; - AutomationLists _dirty_automations; +private: + struct WriteLockImpl : public AutomatableSequence::WriteLockImpl { + WriteLockImpl(Glib::Mutex::Lock* source_lock, Glib::RWLock& s, Glib::Mutex& c) + : AutomatableSequence::WriteLockImpl(s, c) + , source_lock(source_lock) + {} + ~WriteLockImpl() { + delete source_lock; + } + Glib::Mutex::Lock* source_lock; + }; - const const_iterator _end_iter; +public: + virtual WriteLock edit_lock(); + virtual WriteLock write_lock(); - mutable nframes_t _next_read; - mutable const_iterator _read_iter; +private: + friend class DeltaCommand; - typedef std::priority_queue< - boost::shared_ptr, std::deque< boost::shared_ptr >, - LaterNoteEndComparator> - ActiveNotes; + void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle); + void source_automation_state_changed (Evoral::Parameter, AutoState); + void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle); + void automation_list_automation_state_changed (Evoral::Parameter, AutoState); + PBD::ScopedConnectionList _midi_source_connections; + // We cannot use a boost::shared_ptr here to avoid a retain cycle MidiSource* _midi_source; + InsertMergePolicy _insert_merge_policy; }; } /* namespace ARDOUR */