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