1 /* 2 * Copyright (C) 2007-2016 David Robillard <d@drobilla.net> 3 * Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com> 4 * Copyright (C) 2008-2017 Paul Davis <paul@linuxaudiosystems.com> 5 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net> 6 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com> 7 * Copyright (C) 2016 Robin Gareus <robin@gareus.org> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 */ 23 24 #ifndef __ardour_midi_model_h__ 25 #define __ardour_midi_model_h__ 26 27 #include <deque> 28 #include <queue> 29 #include <utility> 30 31 #include <boost/utility.hpp> 32 #include <glibmm/threads.h> 33 34 #include "pbd/command.h" 35 36 #include "ardour/automatable_sequence.h" 37 #include "ardour/libardour_visibility.h" 38 #include "ardour/libardour_visibility.h" 39 #include "ardour/types.h" 40 #include "ardour/types.h" 41 #include "ardour/variant.h" 42 43 #include "evoral/Note.h" 44 #include "evoral/Sequence.h" 45 46 namespace ARDOUR { 47 48 class Session; 49 class MidiSource; 50 51 /** This is a higher level (than MidiBuffer) model of MIDI data, with separate 52 * representations for notes (instead of just unassociated note on/off events) 53 * and controller data. Controller data is represented as part of the 54 * Automatable base (i.e. in a map of AutomationList, keyed by Parameter). 55 * Because of this MIDI controllers and automatable controllers/widgets/etc 56 * are easily interchangeable. 57 */ 58 class LIBARDOUR_API MidiModel : public AutomatableSequence<Temporal::Beats> { 59 public: 60 typedef Temporal::Beats TimeType; 61 62 MidiModel (boost::shared_ptr<MidiSource>); 63 note_mode()64 NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); } set_note_mode(NoteMode mode)65 void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); }; 66 67 class LIBARDOUR_API DiffCommand : public Command { 68 public: 69 70 DiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name); 71 name()72 const std::string& name () const { return _name; } 73 74 virtual void operator() () = 0; 75 virtual void undo () = 0; 76 77 virtual int set_state (const XMLNode&, int version) = 0; 78 virtual XMLNode & get_state () = 0; 79 model()80 boost::shared_ptr<MidiModel> model() const { return _model; } 81 82 protected: 83 boost::shared_ptr<MidiModel> _model; 84 const std::string _name; 85 86 }; 87 88 class LIBARDOUR_API NoteDiffCommand : public DiffCommand { 89 public: 90 NoteDiffCommand(boost::shared_ptr<MidiModel> m,const std::string & name)91 NoteDiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name) : DiffCommand (m, name) {} 92 NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node); 93 94 enum Property { 95 NoteNumber, 96 Velocity, 97 StartTime, 98 Length, 99 Channel 100 }; 101 102 void operator() (); 103 void undo (); 104 105 int set_state (const XMLNode&, int version); 106 XMLNode & get_state (); 107 108 void add (const NotePtr note); 109 void remove (const NotePtr note); 110 void side_effect_remove (const NotePtr note); 111 change(const NotePtr note,Property prop,uint8_t new_value)112 void change (const NotePtr note, Property prop, uint8_t new_value) { 113 change(note, prop, Variant(new_value)); 114 } 115 change(const NotePtr note,Property prop,TimeType new_time)116 void change (const NotePtr note, Property prop, TimeType new_time) { 117 change(note, prop, Variant(new_time)); 118 } 119 120 void change (const NotePtr note, Property prop, const Variant& new_value); 121 adds_or_removes()122 bool adds_or_removes() const { 123 return !_added_notes.empty() || !_removed_notes.empty(); 124 } 125 126 NoteDiffCommand& operator+= (const NoteDiffCommand& other); 127 128 static Variant get_value (const NotePtr note, Property prop); 129 130 static Variant::Type value_type (Property prop); 131 132 struct NoteChange { 133 NoteDiffCommand::Property property; 134 NotePtr note; 135 uint32_t note_id; 136 Variant old_value; 137 Variant new_value; 138 }; 139 140 typedef std::list<NoteChange> ChangeList; 141 typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList; 142 changes()143 const ChangeList& changes() const { return _changes; } added_notes()144 const NoteList& added_notes() const { return _added_notes; } removed_notes()145 const NoteList& removed_notes() const { return _removed_notes; } 146 147 private: 148 ChangeList _changes; 149 NoteList _added_notes; 150 NoteList _removed_notes; 151 152 std::set<NotePtr> side_effect_removals; 153 154 XMLNode &marshal_change(const NoteChange&); 155 NoteChange unmarshal_change(XMLNode *xml_note); 156 157 XMLNode &marshal_note(const NotePtr note); 158 NotePtr unmarshal_note(XMLNode *xml_note); 159 }; 160 161 /* Currently this class only supports changes of sys-ex time, but could be expanded */ 162 class LIBARDOUR_API SysExDiffCommand : public DiffCommand { 163 public: 164 SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node); 165 166 enum Property { 167 Time, 168 }; 169 170 int set_state (const XMLNode&, int version); 171 XMLNode & get_state (); 172 173 void remove (SysExPtr sysex); 174 void operator() (); 175 void undo (); 176 177 void change (boost::shared_ptr<Evoral::Event<TimeType> >, TimeType); 178 179 private: 180 struct Change { ChangeChange181 Change () : sysex_id (0) {} 182 boost::shared_ptr<Evoral::Event<TimeType> > sysex; 183 gint sysex_id; 184 SysExDiffCommand::Property property; 185 TimeType old_time; 186 TimeType new_time; 187 }; 188 189 typedef std::list<Change> ChangeList; 190 ChangeList _changes; 191 192 std::list<SysExPtr> _removed; 193 194 XMLNode & marshal_change (const Change &); 195 Change unmarshal_change (XMLNode *); 196 }; 197 198 class LIBARDOUR_API PatchChangeDiffCommand : public DiffCommand { 199 public: 200 PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const std::string &); 201 PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const XMLNode &); 202 203 int set_state (const XMLNode &, int version); 204 XMLNode & get_state (); 205 206 void operator() (); 207 void undo (); 208 209 void add (PatchChangePtr); 210 void remove (PatchChangePtr); 211 void change_time (PatchChangePtr, TimeType); 212 void change_channel (PatchChangePtr, uint8_t); 213 void change_program (PatchChangePtr, uint8_t); 214 void change_bank (PatchChangePtr, int); 215 216 enum Property { 217 Time, 218 Channel, 219 Program, 220 Bank 221 }; 222 223 private: 224 struct Change { 225 PatchChangePtr patch; 226 Property property; 227 gint patch_id; 228 TimeType old_time; 229 union { 230 uint8_t old_channel; 231 int old_bank; 232 uint8_t old_program; 233 }; 234 TimeType new_time; 235 union { 236 uint8_t new_channel; 237 uint8_t new_program; 238 int new_bank; 239 }; 240 ChangeChange241 Change() : patch_id (-1) {} 242 }; 243 244 typedef std::list<Change> ChangeList; 245 ChangeList _changes; 246 247 std::list<PatchChangePtr> _added; 248 std::list<PatchChangePtr> _removed; 249 250 XMLNode & marshal_change (const Change &); 251 Change unmarshal_change (XMLNode *); 252 253 XMLNode & marshal_patch_change (constPatchChangePtr); 254 PatchChangePtr unmarshal_patch_change (XMLNode *); 255 }; 256 257 /** Start a new NoteDiff command. 258 * 259 * This has no side-effects on the model or Session, the returned command 260 * can be held on to for as long as the caller wishes, or discarded without 261 * formality, until apply_command is called and ownership is taken. 262 */ 263 MidiModel::NoteDiffCommand* new_note_diff_command (const std::string& name = "midi edit"); 264 /** Start a new SysExDiff command */ 265 MidiModel::SysExDiffCommand* new_sysex_diff_command (const std::string& name = "midi edit"); 266 267 /** Start a new PatchChangeDiff command */ 268 MidiModel::PatchChangeDiffCommand* new_patch_change_diff_command (const std::string& name = "midi edit"); 269 270 /** Apply a command. 271 * 272 * Ownership of cmd is taken, it must not be deleted by the caller. 273 * The command will constitute one item on the undo stack. 274 */ 275 void apply_command (Session& session, Command* cmd); 276 apply_command(Session * session,Command * cmd)277 void apply_command (Session* session, Command* cmd) { if (session) { apply_command (*session, cmd); } } 278 279 /** Apply a command as part of a larger reversible transaction 280 * 281 * Ownership of cmd is taken, it must not be deleted by the caller. 282 * The command will constitute one item on the undo stack. 283 */ 284 void apply_command_as_subcommand (Session& session, Command* cmd); 285 286 bool sync_to_source (const Glib::Threads::Mutex::Lock& source_lock); 287 288 bool write_to(boost::shared_ptr<MidiSource> source, 289 const Glib::Threads::Mutex::Lock& source_lock); 290 291 bool write_section_to(boost::shared_ptr<MidiSource> source, 292 const Glib::Threads::Mutex::Lock& source_lock, 293 Temporal::Beats begin = Temporal::Beats(), 294 Temporal::Beats end = std::numeric_limits<Temporal::Beats>::max(), 295 bool offset_events = false); 296 297 // MidiModel doesn't use the normal AutomationList serialisation code 298 // since controller data is stored in the .mid 299 XMLNode& get_state(); set_state(const XMLNode &)300 int set_state(const XMLNode&) { return 0; } 301 302 PBD::Signal0<void> ContentsChanged; 303 PBD::Signal1<void, double> ContentsShifted; 304 305 boost::shared_ptr<const MidiSource> midi_source (); 306 void set_midi_source (boost::shared_ptr<MidiSource>); 307 308 boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr); 309 PatchChangePtr find_patch_change (Evoral::event_id_t); 310 boost::shared_ptr<Evoral::Note<TimeType> > find_note (Evoral::event_id_t); 311 boost::shared_ptr<Evoral::Event<TimeType> > find_sysex (Evoral::event_id_t); 312 313 InsertMergePolicy insert_merge_policy () const; 314 void set_insert_merge_policy (InsertMergePolicy); 315 316 boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id); 317 318 void insert_silence_at_start (TimeType); 319 void transpose (NoteDiffCommand *, const NotePtr, int); 320 321 protected: 322 int resolve_overlaps_unlocked (const NotePtr, void* arg = 0); 323 324 private: 325 struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl { WriteLockImplWriteLockImpl326 WriteLockImpl(Glib::Threads::Mutex::Lock* slock, Glib::Threads::RWLock& s, Glib::Threads::Mutex& c) 327 : AutomatableSequence<TimeType>::WriteLockImpl(s, c) 328 , source_lock (slock) 329 {} ~WriteLockImplWriteLockImpl330 ~WriteLockImpl() { 331 delete source_lock; 332 } 333 Glib::Threads::Mutex::Lock* source_lock; 334 }; 335 336 public: 337 WriteLock edit_lock(); 338 339 private: 340 friend class DeltaCommand; 341 342 void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle); 343 void source_automation_state_changed (Evoral::Parameter, AutoState); 344 void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle); 345 void automation_list_automation_state_changed (Evoral::Parameter, AutoState); 346 347 void control_list_marked_dirty (); 348 349 PBD::ScopedConnectionList _midi_source_connections; 350 351 // We cannot use a boost::shared_ptr here to avoid a retain cycle 352 boost::weak_ptr<MidiSource> _midi_source; 353 InsertMergePolicy _insert_merge_policy; 354 }; 355 356 } /* namespace ARDOUR */ 357 358 #endif /* __ardour_midi_model_h__ */ 359 360