X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fardour%2Fmidi_model.h;h=e74bc26c36204bd51caf09c3615a39444ef22a08;hb=6bd583803ea1daf19a1c088766122b5cdf340171;hp=cebc435435b6e96b6d3ed976f12e2273a2ab3645;hpb=d98c8e8fa4bb18cbbf2a18b82b11d9949bb00890;p=ardour.git diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index cebc435435..e74bc26c36 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -1,6 +1,6 @@ /* Copyright (C) 2007 Paul Davis - Author: Dave Robillard + Author: David Robillard 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 @@ -21,17 +21,22 @@ #ifndef __ardour_midi_model_h__ #define __ardour_midi_model_h__ -#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/libardour_visibility.h" +#include "ardour/libardour_visibility.h" +#include "ardour/types.h" #include "ardour/types.h" +#include "ardour/variant.h" + #include "evoral/Note.hpp" #include "evoral/Sequence.hpp" @@ -47,57 +52,42 @@ class MidiSource; * Because of this MIDI controllers and automatable controllers/widgets/etc * are easily interchangeable. */ -class MidiModel : public AutomatableSequence { +class LIBARDOUR_API MidiModel : public AutomatableSequence { public: - typedef double TimeType; + typedef Temporal::Beats TimeType; - MidiModel(MidiSource* s, size_t size=0); + MidiModel (boost::shared_ptr); NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; - /** Add/Remove notes. - * Technically all note operations can be implemented as one of these, but - * a custom command can be more efficient. - */ - class DeltaCommand : public Command { + class LIBARDOUR_API DiffCommand : public Command { public: - DeltaCommand (boost::shared_ptr m, const std::string& name); - DeltaCommand (boost::shared_ptr m, const XMLNode& node); - const std::string& name() const { return _name; } + DiffCommand (boost::shared_ptr m, const std::string& name); - void operator()(); - void undo(); + const std::string& name () const { return _name; } - int set_state (const XMLNode&, int version); - XMLNode& get_state (); + virtual void operator() () = 0; + virtual void undo () = 0; - void add(const boost::shared_ptr< Evoral::Note > note); - void remove(const boost::shared_ptr< Evoral::Note > note); + virtual int set_state (const XMLNode&, int version) = 0; + virtual XMLNode & get_state () = 0; - private: - XMLNode &marshal_note(const boost::shared_ptr< Evoral::Note > note); - boost::shared_ptr< Evoral::Note > unmarshal_note(XMLNode *xml_note); + boost::shared_ptr model() const { return _model; } + protected: boost::shared_ptr _model; const std::string _name; - typedef std::list< boost::shared_ptr< Evoral::Note > > NoteList; - - NoteList _added_notes; - NoteList _removed_notes; }; - - /** Change note properties. - * More efficient than DeltaCommand and has the important property that - * it leaves the objects in the MidiModel (Notes) the same, thus - * enabling selection and other state to persist across command - * do/undo/redo. - */ - class DiffCommand : public Command { + class LIBARDOUR_API NoteDiffCommand : public DiffCommand { public: + + NoteDiffCommand (boost::shared_ptr m, const std::string& name) : DiffCommand (m, name) {} + NoteDiffCommand (boost::shared_ptr m, const XMLNode& node); + enum Property { NoteNumber, Velocity, @@ -106,85 +96,236 @@ public: Channel }; - DiffCommand (boost::shared_ptr m, const std::string& name); - DiffCommand (boost::shared_ptr m, const XMLNode& node); + void operator() (); + void undo (); + + int set_state (const XMLNode&, int version); + XMLNode & get_state (); + + 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) { + change(note, prop, Variant(new_value)); + } + + void change (const NotePtr note, Property prop, TimeType new_time) { + change(note, prop, Variant(new_time)); + } + + void change (const NotePtr note, Property prop, const Variant& new_value); + + bool adds_or_removes() const { + return !_added_notes.empty() || !_removed_notes.empty(); + } + + NoteDiffCommand& operator+= (const NoteDiffCommand& other); + + static Variant get_value (const NotePtr note, Property prop); + + static Variant::Type value_type (Property prop); + + struct NoteChange { + NoteDiffCommand::Property property; + NotePtr note; + uint32_t note_id; + Variant old_value; + Variant new_value; + }; + + typedef std::list ChangeList; + typedef std::list< boost::shared_ptr< Evoral::Note > > NoteList; + + const ChangeList& changes() const { return _changes; } + const NoteList& added_notes() const { return _added_notes; } + const NoteList& removed_notes() const { return _removed_notes; } + + private: + ChangeList _changes; + NoteList _added_notes; + NoteList _removed_notes; - const std::string& name() const { return _name; } + std::set side_effect_removals; - void operator()(); - void undo(); + XMLNode &marshal_change(const NoteChange&); + NoteChange unmarshal_change(XMLNode *xml_note); + + XMLNode &marshal_note(const NotePtr note); + NotePtr unmarshal_note(XMLNode *xml_note); + }; + + /* Currently this class only supports changes of sys-ex time, but could be expanded */ + class LIBARDOUR_API SysExDiffCommand : public DiffCommand { + public: + SysExDiffCommand (boost::shared_ptr m, const XMLNode& node); + + enum Property { + Time, + }; int set_state (const XMLNode&, int version); - XMLNode& get_state (); + XMLNode & get_state (); + + void remove (SysExPtr sysex); + void operator() (); + void undo (); - void change (const boost::shared_ptr > note, - Property prop, uint8_t new_value); - void change (const boost::shared_ptr > note, - Property prop, TimeType new_time); + void change (boost::shared_ptr >, TimeType); private: - boost::shared_ptr _model; - const std::string _name; + struct Change { + Change () : sysex_id (0) {} + boost::shared_ptr > sysex; + gint sysex_id; + SysExDiffCommand::Property property; + TimeType old_time; + TimeType new_time; + }; + + typedef std::list ChangeList; + ChangeList _changes; + + std::list _removed; + + XMLNode & marshal_change (const Change &); + Change unmarshal_change (XMLNode *); + }; + + class LIBARDOUR_API PatchChangeDiffCommand : public DiffCommand { + public: + PatchChangeDiffCommand (boost::shared_ptr, const std::string &); + PatchChangeDiffCommand (boost::shared_ptr, const XMLNode &); + + int set_state (const XMLNode &, int version); + XMLNode & get_state (); - struct NotePropertyChange { - DiffCommand::Property property; - boost::shared_ptr< Evoral::Note > note; + 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; + gint patch_id; + TimeType old_time; union { - uint8_t old_value; - TimeType old_time; + uint8_t old_channel; + int old_bank; + uint8_t old_program; }; + TimeType new_time; union { - uint8_t new_value; - TimeType new_time; + uint8_t new_channel; + uint8_t new_program; + int new_bank; }; + + Change() : patch_id (-1) {} }; - typedef std::list ChangeList; + typedef std::list ChangeList; ChangeList _changes; - XMLNode &marshal_change(const NotePropertyChange&); - NotePropertyChange unmarshal_change(XMLNode *xml_note); + std::list _added; + std::list _removed; + + XMLNode & marshal_change (const Change &); + Change unmarshal_change (XMLNode *); + + XMLNode & marshal_patch_change (constPatchChangePtr); + PatchChangePtr unmarshal_patch_change (XMLNode *); }; - MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); - 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); + 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 (Session* session, Command* cmd) { if (session) { apply_command (*session, cmd); } } + void apply_command_as_subcommand (Session& session, Command* cmd); - bool write_to(boost::shared_ptr source); + bool sync_to_source (const Glib::Threads::Mutex::Lock& source_lock); + + bool write_to(boost::shared_ptr source, + const Glib::Threads::Mutex::Lock& source_lock); + + bool write_section_to(boost::shared_ptr source, + const Glib::Threads::Mutex::Lock& source_lock, + Temporal::Beats begin = Temporal::Beats(), + Temporal::Beats end = std::numeric_limits::max(), + bool offset_events = false); // 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; + PBD::Signal0 ContentsChanged; + PBD::Signal1 ContentsShifted; + + boost::shared_ptr midi_source (); + void set_midi_source (boost::shared_ptr); + + boost::shared_ptr > find_note (NotePtr); + PatchChangePtr find_patch_change (Evoral::event_id_t); + boost::shared_ptr > find_note (gint note_id); + boost::shared_ptr > find_sysex (gint); + + InsertMergePolicy insert_merge_policy () const; + void set_insert_merge_policy (InsertMergePolicy); - const MidiSource* midi_source() const { return _midi_source; } - void set_midi_source(MidiSource* source) { _midi_source = source; } + boost::shared_ptr control_factory(const Evoral::Parameter& id); - boost::shared_ptr > find_note (boost::shared_ptr >); + void insert_silence_at_start (TimeType); + void transpose (NoteDiffCommand *, const NotePtr, int); + +protected: + int resolve_overlaps_unlocked (const NotePtr, void* arg = 0); 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) + struct WriteLockImpl : public AutomatableSequence::WriteLockImpl { + WriteLockImpl(Glib::Threads::Mutex::Lock* slock, Glib::Threads::RWLock& s, Glib::Threads::Mutex& c) + : AutomatableSequence::WriteLockImpl(s, c) + , source_lock (slock) {} ~WriteLockImpl() { delete source_lock; } - Glib::Mutex::Lock* source_lock; + Glib::Threads::Mutex::Lock* source_lock; }; public: - virtual WriteLock write_lock(); + WriteLock edit_lock(); private: friend class DeltaCommand; + 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); + + void control_list_marked_dirty (); + + PBD::ScopedConnectionList _midi_source_connections; + // We cannot use a boost::shared_ptr here to avoid a retain cycle - MidiSource* _midi_source; + boost::weak_ptr _midi_source; + InsertMergePolicy _insert_merge_policy; }; } /* namespace ARDOUR */