Move EventRingBuffer to libardour.
[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 <queue>
25 #include <deque>
26 #include <utility>
27 #include <boost/utility.hpp>
28 #include <glibmm/threads.h>
29 #include "pbd/command.h"
30 #include "ardour/libardour_visibility.h"
31 #include "ardour/types.h"
32 #include "ardour/midi_buffer.h"
33 #include "ardour/automatable_sequence.h"
34 #include "ardour/libardour_visibility.h"
35 #include "ardour/types.h"
36 #include "evoral/Note.hpp"
37 #include "evoral/Sequence.hpp"
38
39 namespace ARDOUR {
40
41 class Session;
42 class MidiSource;
43
44 /** This is a higher level (than MidiBuffer) model of MIDI data, with separate
45  * representations for notes (instead of just unassociated note on/off events)
46  * and controller data.  Controller data is represented as part of the
47  * Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
48  * Because of this MIDI controllers and automatable controllers/widgets/etc
49  * are easily interchangeable.
50  */
51 class LIBARDOUR_API MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
52 public:
53         typedef Evoral::MusicalTime TimeType;
54
55         MidiModel (boost::shared_ptr<MidiSource>);
56
57         NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
58         void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
59
60         class LIBARDOUR_API DiffCommand : public Command {
61         public:
62
63                 DiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
64
65                 const std::string& name () const { return _name; }
66
67                 virtual void operator() () = 0;
68                 virtual void undo () = 0;
69
70                 virtual int set_state (const XMLNode&, int version) = 0;
71                 virtual XMLNode & get_state () = 0;
72
73                 boost::shared_ptr<MidiModel> model() const { return _model; }
74
75         protected:
76                 boost::shared_ptr<MidiModel> _model;
77                 const std::string            _name;
78
79         };
80
81         class LIBARDOUR_API NoteDiffCommand : public DiffCommand {
82         public:
83
84                 NoteDiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name) : DiffCommand (m, name) {}
85                 NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
86
87                 enum Property {
88                         NoteNumber,
89                         Velocity,
90                         StartTime,
91                         Length,
92                         Channel
93                 };
94
95                 void operator() ();
96                 void undo ();
97
98                 int set_state (const XMLNode&, int version);
99                 XMLNode & get_state ();
100
101                 void add (const NotePtr note);
102                 void remove (const NotePtr note);
103                 void side_effect_remove (const NotePtr note);
104
105                 void change (const NotePtr note, Property prop, uint8_t new_value);
106                 void change (const NotePtr note, Property prop, TimeType new_time);
107
108                 bool adds_or_removes() const {
109                         return !_added_notes.empty() || !_removed_notes.empty();
110                 }
111
112                 NoteDiffCommand& operator+= (const NoteDiffCommand& other);
113
114         private:
115                 struct NoteChange {
116                         NoteDiffCommand::Property property;
117                         NotePtr note;
118                         uint32_t note_id;
119                         uint8_t  old_value;  // or...
120                         TimeType old_time;   // this
121                         uint8_t  new_value;  // or...
122                         TimeType new_time;   // this
123                 };
124
125                 typedef std::list<NoteChange> ChangeList;
126                 ChangeList _changes;
127
128                 typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
129                 NoteList _added_notes;
130                 NoteList _removed_notes;
131
132                 std::set<NotePtr> side_effect_removals;
133
134                 XMLNode &marshal_change(const NoteChange&);
135                 NoteChange unmarshal_change(XMLNode *xml_note);
136
137                 XMLNode &marshal_note(const NotePtr note);
138                 NotePtr unmarshal_note(XMLNode *xml_note);
139         };
140
141         /* Currently this class only supports changes of sys-ex time, but could be expanded */
142         class LIBARDOUR_API SysExDiffCommand : public DiffCommand {
143         public:
144                 SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
145
146                 enum Property {
147                         Time,
148                 };
149
150                 int set_state (const XMLNode&, int version);
151                 XMLNode & get_state ();
152
153                 void remove (SysExPtr sysex);
154                 void operator() ();
155                 void undo ();
156
157                 void change (boost::shared_ptr<Evoral::Event<TimeType> >, TimeType);
158
159         private:
160                 struct Change {
161                         boost::shared_ptr<Evoral::Event<TimeType> > sysex;
162                         gint sysex_id;
163                         SysExDiffCommand::Property property;
164                         TimeType old_time;
165                         TimeType new_time;
166                 };
167
168                 typedef std::list<Change> ChangeList;
169                 ChangeList _changes;
170
171                 std::list<SysExPtr> _removed;
172
173                 XMLNode & marshal_change (const Change &);
174                 Change unmarshal_change (XMLNode *);
175         };
176
177         class LIBARDOUR_API PatchChangeDiffCommand : public DiffCommand {
178         public:
179                 PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const std::string &);
180                 PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const XMLNode &);
181
182                 int set_state (const XMLNode &, int version);
183                 XMLNode & get_state ();
184
185                 void operator() ();
186                 void undo ();
187
188                 void add (PatchChangePtr);
189                 void remove (PatchChangePtr);
190                 void change_time (PatchChangePtr, TimeType);
191                 void change_channel (PatchChangePtr, uint8_t);
192                 void change_program (PatchChangePtr, uint8_t);
193                 void change_bank (PatchChangePtr, int);
194
195                 enum Property {
196                         Time,
197                         Channel,
198                         Program,
199                         Bank
200                 };
201
202         private:
203                 struct Change {
204                         PatchChangePtr patch;
205                         Property       property;
206                         gint           patch_id;
207                         TimeType       old_time;
208                         union {
209                                 uint8_t    old_channel;
210                                 int        old_bank;
211                                 uint8_t    old_program;
212                         };
213                         TimeType       new_time;
214                         union {
215                                 uint8_t    new_channel;
216                                 uint8_t    new_program;
217                                 int        new_bank;
218                         };
219
220                     Change() : patch_id (-1) {}
221                 };
222
223                 typedef std::list<Change> ChangeList;
224                 ChangeList _changes;
225
226                 std::list<PatchChangePtr> _added;
227                 std::list<PatchChangePtr> _removed;
228
229                 XMLNode & marshal_change (const Change &);
230                 Change unmarshal_change (XMLNode *);
231
232                 XMLNode & marshal_patch_change (constPatchChangePtr);
233                 PatchChangePtr unmarshal_patch_change (XMLNode *);
234         };
235
236         MidiModel::NoteDiffCommand* new_note_diff_command (const std::string name = "midi edit");
237         MidiModel::SysExDiffCommand* new_sysex_diff_command (const std::string name = "midi edit");
238         MidiModel::PatchChangeDiffCommand* new_patch_change_diff_command (const std::string name = "midi edit");
239         void apply_command (Session& session, Command* cmd);
240         void apply_command_as_subcommand (Session& session, Command* cmd);
241
242         bool sync_to_source ();
243         bool write_to(boost::shared_ptr<MidiSource> source);
244         bool write_section_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
245         Evoral::MusicalTime end = Evoral::MaxMusicalTime);
246
247         // MidiModel doesn't use the normal AutomationList serialisation code
248         // since controller data is stored in the .mid
249         XMLNode& get_state();
250         int set_state(const XMLNode&) { return 0; }
251
252         PBD::Signal0<void> ContentsChanged;
253
254         boost::shared_ptr<const MidiSource> midi_source ();
255         void set_midi_source (boost::shared_ptr<MidiSource>);
256
257         boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
258         PatchChangePtr find_patch_change (Evoral::event_id_t);
259         boost::shared_ptr<Evoral::Note<TimeType> > find_note (gint note_id);
260         boost::shared_ptr<Evoral::Event<TimeType> > find_sysex (gint);
261
262         InsertMergePolicy insert_merge_policy () const;
263         void set_insert_merge_policy (InsertMergePolicy);
264
265         boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
266
267         void insert_silence_at_start (TimeType);
268         void transpose (TimeType, TimeType, int);
269
270 protected:
271         int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
272
273 private:
274         struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
275                 WriteLockImpl(Glib::Threads::Mutex::Lock* slock, Glib::Threads::RWLock& s, Glib::Threads::Mutex& c)
276                         : AutomatableSequence<TimeType>::WriteLockImpl(s, c)
277                         , source_lock (slock)
278                 {}
279                 ~WriteLockImpl() {
280                         delete source_lock;
281                 }
282                 Glib::Threads::Mutex::Lock* source_lock;
283         };
284
285 public:
286         WriteLock edit_lock();
287         WriteLock write_lock();
288
289 private:
290         friend class DeltaCommand;
291
292         void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
293         void source_automation_state_changed (Evoral::Parameter, AutoState);
294         void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
295         void automation_list_automation_state_changed (Evoral::Parameter, AutoState);
296
297         void control_list_marked_dirty ();
298
299         PBD::ScopedConnectionList _midi_source_connections;
300
301         // We cannot use a boost::shared_ptr here to avoid a retain cycle
302         boost::weak_ptr<MidiSource> _midi_source;
303         InsertMergePolicy _insert_merge_policy;
304 };
305
306 } /* namespace ARDOUR */
307
308 #endif /* __ardour_midi_model_h__ */
309