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