X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fardour%2Fmidi_model.h;h=d0a05e131d5a83fd04f64a84fc1bf9fc6dcd0380;hb=fac2c044d10fa4737ee391c77215f6fb1fc285e2;hp=2879514d5a49e8656d64c206d3115c649bd3f92b;hpb=c80e9d4ac9b8d345f2a03ad802e3ee9fedfddf10;p=ardour.git diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 2879514d5a..d0a05e131d 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 @@ -18,161 +18,289 @@ */ -#ifndef __ardour_midi_model_h__ +#ifndef __ardour_midi_model_h__ #define __ardour_midi_model_h__ #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; - - -/** This is a slightly higher level (than MidiBuffer) model of MIDI note data. - * Currently it only represents note data, which is represented as complete - * note events (ie with a start time and a duration) rather than separate - * note on and off events (controller data is not here since it's represented - * as an AutomationList) +class MidiSource; + +/** 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 { +class MidiModel : public AutomatableSequence { public: - struct Note { - Note(double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40); - Note(const Note& copy); - - const MidiModel::Note& operator=(const MidiModel::Note& copy); - - inline bool operator==(const Note& other) - { return time() == other.time() && note() == other.note(); } - - inline double time() const { return _on_event.time(); } - inline double end_time() const { return _off_event.time(); } - inline uint8_t note() const { return _on_event.note(); } - inline uint8_t velocity() const { return _on_event.velocity(); } - inline double duration() const { return _off_event.time() - _on_event.time(); } - - inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; } - inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } - inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } - inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; } - - inline MidiEvent& on_event() { return _on_event; } - inline MidiEvent& off_event() { return _off_event; } - - inline const MidiEvent& on_event() const { return _on_event; } - inline const MidiEvent& off_event() const { return _off_event; } + typedef Evoral::MusicalTime TimeType; - private: - // Event buffers are self-contained - MidiEvent _on_event; - MidiEvent _off_event; - }; + MidiModel (boost::shared_ptr); + + NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } + void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; + + class DiffCommand : public Command { + public: + + DiffCommand (boost::shared_ptr m, const std::string& name); + + const std::string& name () const { return _name; } - MidiModel(Session& s, size_t size=0); + virtual void operator() () = 0; + virtual void undo () = 0; - void clear() { _notes.clear(); } + virtual int set_state (const XMLNode&, int version) = 0; + virtual XMLNode & get_state () = 0; - NoteMode note_mode() const { return _note_mode; } - void set_note_mode(NoteMode mode) { _note_mode = mode; } + boost::shared_ptr model() const { return _model; } - void start_write(); - bool currently_writing() const { return _writing; } - void end_write(bool delete_stuck=false); + protected: + boost::shared_ptr _model; + const std::string _name; - size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const; + }; + + class NoteDiffCommand : public DiffCommand { + public: - /** Resizes vector if necessary (NOT realtime safe) */ - void append(const MidiBuffer& data); - - /** Resizes vector if necessary (NOT realtime safe) */ - void append(double time, size_t size, const Byte* in_buffer); - - inline const Note& note_at(unsigned i) const { return _notes[i]; } + NoteDiffCommand (boost::shared_ptr m, const std::string& name) : DiffCommand (m, name) {} + NoteDiffCommand (boost::shared_ptr m, const XMLNode& node); - inline size_t n_notes() const { return _notes.size(); } + enum Property { + NoteNumber, + Velocity, + StartTime, + Length, + Channel + }; - typedef std::vector Notes; - - inline static bool note_time_comparator (const Note& a, const Note& b) { - return a.time() < b.time(); - } + void operator() (); + void undo (); - struct LaterNoteEndComparator { - typedef const Note* value_type; - inline bool operator()(const Note* const a, const Note* const b) { - return a->end_time() > b->end_time(); + 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); + void change (const NotePtr note, Property prop, TimeType new_time); + + bool adds_or_removes() const { + return !_added_notes.empty() || !_removed_notes.empty(); } + + NoteDiffCommand& operator+= (const NoteDiffCommand& other); + + private: + struct NoteChange { + NoteDiffCommand::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); + }; + + /* Currently this class only supports changes of sys-ex time, but could be expanded */ + class 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 (); + + void operator() (); + void undo (); + + void change (boost::shared_ptr >, TimeType); + + private: + struct Change { + boost::shared_ptr > sysex; + SysExDiffCommand::Property property; + TimeType old_time; + TimeType new_time; + }; + + typedef std::list ChangeList; + ChangeList _changes; + + XMLNode & marshal_change (const Change &); + Change unmarshal_change (XMLNode *); }; - 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 PatchChangeDiffCommand : public DiffCommand { public: - DeltaCommand (MidiModel& m, const std::string& name) - : Command(name), _model(m), _name(name) {} - //DeltaCommand (MidiModel&, const XMLNode& node); + PatchChangeDiffCommand (boost::shared_ptr, const std::string &); + PatchChangeDiffCommand (boost::shared_ptr, const XMLNode &); + + int set_state (const XMLNode &, int version); + XMLNode & get_state (); + + void operator() (); + void undo (); - const std::string& name() const { return _name; } - - void operator()(); - void undo(); - - /*int set_state (const XMLNode&); - XMLNode& get_state ();*/ + 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); - void add(const Note& note); - void remove(const Note& note); + enum Property { + Time, + Channel, + Program, + Bank + }; private: - MidiModel& _model; - std::string _name; - std::list _added_notes; - std::list _removed_notes; + 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 ChangeList; + ChangeList _changes; + + 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"); - void apply_command(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_as_subcommand (Session& session, Command* cmd); + + 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; } + + PBD::Signal0 ContentsChanged; + + 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); + + boost::shared_ptr control_factory(const Evoral::Parameter& id); + + void insert_silence_at_start (TimeType); + void transpose (TimeType, TimeType, int); + +protected: + int resolve_overlaps_unlocked (const NotePtr, void* arg = 0); - sigc::signal ContentsChanged; - private: - friend class DeltaCommand; - void add_note(const Note& note); - void remove_note(const Note& note); + 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; + }; - bool is_sorted() const; +public: + WriteLock edit_lock(); + WriteLock write_lock(); - void append_note_on(double time, uint8_t note, uint8_t velocity); - void append_note_off(double time, uint8_t note); +private: + friend class DeltaCommand; - Session& _session; + 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); - Notes _notes; - NoteMode _note_mode; - - typedef std::vector WriteNotes; - WriteNotes _write_notes; - bool _writing; - - // note state for read(): - - typedef std::priority_queue, - LaterNoteEndComparator> ActiveNotes; + void control_list_marked_dirty (); - mutable ActiveNotes _active_notes; + PBD::ScopedConnectionList _midi_source_connections; + + // We cannot use a boost::shared_ptr here to avoid a retain cycle + boost::weak_ptr _midi_source; + InsertMergePolicy _insert_merge_policy; }; } /* namespace ARDOUR */ +/* This is a very long comment and stuff oh my god it's so long what are we going to do oh no oh no*/ + #endif /* __ardour_midi_model_h__ */