summaryrefslogtreecommitdiff
path: root/libs/evoral/evoral/Sequence.hpp
blob: 7c0818a7fb26459dbd39d02a58f7a558a5278d1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* This file is part of Evoral.
 * Copyright (C) 2008 David Robillard <http://drobilla.net>
 * Copyright (C) 2000-2008 Paul Davis
 *
 * Evoral is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef EVORAL_SEQUENCE_HPP
#define EVORAL_SEQUENCE_HPP

#include <vector>
#include <queue>
#include <set>
#include <list>
#include <utility>
#include <boost/shared_ptr.hpp>
#include <glibmm/threads.h>

#include "evoral/visibility.h"
#include "evoral/Note.hpp"
#include "evoral/ControlSet.hpp"
#include "evoral/ControlList.hpp"
#include "evoral/PatchChange.hpp"

namespace Evoral {

class Parameter;
class TypeMap;
template<typename Time> class EventSink;
template<typename Time> class Note;
template<typename Time> class Event;

/** An iterator over (the x axis of) a 2-d double coordinate space.
 */
class /*LIBEVORAL_API*/ ControlIterator {
public:
	ControlIterator(boost::shared_ptr<const ControlList> al, double ax, double ay)
		: list(al)
		, x(ax)
		, y(ay)
	{}

	boost::shared_ptr<const ControlList> list;
	double x;
	double y;
};


/** This is a higher level view of events, with separate representations for
 * notes (instead of just unassociated note on/off events) and controller data.
 * Controller data is represented as a list of time-stamped float values. */
template<typename Time>
class LIBEVORAL_API Sequence : virtual public ControlSet {
public:
	Sequence(const TypeMap& type_map);
	Sequence(const Sequence<Time>& other);

protected:
	struct WriteLockImpl {
		WriteLockImpl(Glib::Threads::RWLock& s, Glib::Threads::Mutex& c)
			: sequence_lock(new Glib::Threads::RWLock::WriterLock(s))
			, control_lock(new Glib::Threads::Mutex::Lock(c)) { }
		~WriteLockImpl() {
			delete sequence_lock;
			delete control_lock;
		}
		Glib::Threads::RWLock::WriterLock* sequence_lock;
		Glib::Threads::Mutex::Lock*        control_lock;
	};

public:

	typedef typename boost::shared_ptr<Evoral::Note<Time> >       NotePtr;
	typedef typename boost::weak_ptr<Evoral::Note<Time> >         WeakNotePtr;
	typedef typename boost::shared_ptr<const Evoral::Note<Time> > constNotePtr;

	typedef boost::shared_ptr<Glib::Threads::RWLock::ReaderLock> ReadLock;
	typedef boost::shared_ptr<WriteLockImpl>                     WriteLock;

	virtual ReadLock  read_lock() const { return ReadLock(new Glib::Threads::RWLock::ReaderLock(_lock)); }
	virtual WriteLock write_lock()      { return WriteLock(new WriteLockImpl(_lock, _control_lock)); }

	void clear();

	bool percussive() const     { return _percussive; }
	void set_percussive(bool p) { _percussive = p; }

	void start_write();
	bool writing() const { return _writing; }

	enum StuckNoteOption {
		Relax,
		DeleteStuckNotes,
		ResolveStuckNotes
	};

	void end_write (StuckNoteOption, Time when = Time());

	void append(const Event<Time>& ev, Evoral::event_id_t evid);

	const TypeMap& type_map() const { return _type_map; }

	inline size_t n_notes() const { return _notes.size(); }
	inline bool   empty()   const { return _notes.empty() && _sysexes.empty() && _patch_changes.empty() && ControlSet::controls_empty(); }

	inline static bool note_time_comparator(const boost::shared_ptr< const Note<Time> >& a,
	                                        const boost::shared_ptr< const Note<Time> >& b) {
		return a->time() < b->time();
	}

	struct NoteNumberComparator {
		inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
		                       const boost::shared_ptr< const Note<Time> > b) const {
			return a->note() < b->note();
		}
	};

	struct EarlierNoteComparator {
		inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
		                       const boost::shared_ptr< const Note<Time> > b) const {
			return a->time() < b->time();
		}
	};

#if 0 // NOT USED
	struct LaterNoteComparator {
		typedef const Note<Time>* value_type;
		inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
		                       const boost::shared_ptr< const Note<Time> > b) const {
			return a->time() > b->time();
		}
	};
#endif

	struct LaterNoteEndComparator {
		typedef const Note<Time>* value_type;
		inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
		                       const boost::shared_ptr< const Note<Time> > b) const {
			return a->end_time().to_double() > b->end_time().to_double();
		}
	};

	typedef std::multiset<NotePtr, EarlierNoteComparator> Notes;
	inline       Notes& notes()       { return _notes; }
	inline const Notes& notes() const { return _notes; }

	enum NoteOperator {
		PitchEqual,
		PitchLessThan,
		PitchLessThanOrEqual,
		PitchGreater,
		PitchGreaterThanOrEqual,
		VelocityEqual,
		VelocityLessThan,
		VelocityLessThanOrEqual,
		VelocityGreater,
		VelocityGreaterThanOrEqual,
	};

	void get_notes (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;

	void remove_overlapping_notes ();
	void trim_overlapping_notes ();
	void remove_duplicate_notes ();

	enum OverlapPitchResolution {
		LastOnFirstOff,
		FirstOnFirstOff
	};

	bool overlapping_pitches_accepted() const { return _overlapping_pitches_accepted; }
	void overlapping_pitches_accepted(bool yn)  { _overlapping_pitches_accepted = yn; }
	OverlapPitchResolution overlap_pitch_resolution() const { return _overlap_pitch_resolution; }
	void set_overlap_pitch_resolution(OverlapPitchResolution opr);

	void set_notes (const typename Sequence<Time>::Notes& n);

	typedef boost::shared_ptr< Event<Time> > SysExPtr;
	typedef boost::shared_ptr<const Event<Time> > constSysExPtr;

	struct EarlierSysExComparator {
		inline bool operator() (constSysExPtr a, constSysExPtr b) const {
			return a->time() < b->time();
		}
	};

	typedef std::multiset<SysExPtr, EarlierSysExComparator> SysExes;
	inline       SysExes& sysexes()       { return _sysexes; }
	inline const SysExes& sysexes() const { return _sysexes; }

	typedef boost::shared_ptr<PatchChange<Time> > PatchChangePtr;
	typedef boost::shared_ptr<const PatchChange<Time> > constPatchChangePtr;

	struct EarlierPatchChangeComparator {
		inline bool operator() (constPatchChangePtr a, constPatchChangePtr b) const {
			return a->time() < b->time();
		}
	};

	typedef std::multiset<PatchChangePtr, EarlierPatchChangeComparator> PatchChanges;
	inline       PatchChanges& patch_changes ()       { return _patch_changes; }
	inline const PatchChanges& patch_changes () const { return _patch_changes; }

	void dump (std::ostream&) const;

private:
	typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
public:

	/** Read iterator */
	class LIBEVORAL_API const_iterator {
	public:
		const_iterator();
		const_iterator(const Sequence<Time>&              seq,
		               Time                               t,
		               bool                               force_discrete,
		               const std::set<Evoral::Parameter>& filtered,
		               const std::set<WeakNotePtr>*       active_notes=NULL);

		inline bool valid() const { return !_is_end && _event; }

		void invalidate(std::set<WeakNotePtr>* notes);

		const Event<Time>& operator*() const { return *_event;  }
		const boost::shared_ptr< const Event<Time> > operator->() const { return _event; }

		const const_iterator& operator++(); // prefix only

		bool operator==(const const_iterator& other) const;
		bool operator!=(const const_iterator& other) const { return ! operator==(other); }

		const_iterator& operator=(const const_iterator& other);

	private:
		friend class Sequence<Time>;

		Time choose_next(Time earliest_t);
		void set_event();

		typedef std::vector<ControlIterator> ControlIterators;
		enum MIDIMessageType { NIL, NOTE_ON, NOTE_OFF, CONTROL, SYSEX, PATCH_CHANGE };

		const Sequence<Time>*                 _seq;
		boost::shared_ptr< Event<Time> >      _event;
		mutable ActiveNotes                   _active_notes;
		/** If the iterator is pointing at a patch change, this is the index of the
		 *  sub-message within that change.
		 */
		int                                   _active_patch_change_message;
		MIDIMessageType                       _type;
		bool                                  _is_end;
		typename Sequence::ReadLock           _lock;
		typename Notes::const_iterator        _note_iter;
		typename SysExes::const_iterator      _sysex_iter;
		typename PatchChanges::const_iterator _patch_change_iter;
		ControlIterators                      _control_iters;
		ControlIterators::iterator            _control_iter;
		bool                                  _force_discrete;
	};

	const_iterator begin (
		Time                               t              = Time(),
		bool                               force_discrete = false,
		const std::set<Evoral::Parameter>& f              = std::set<Evoral::Parameter>(),
		const std::set<WeakNotePtr>*       active_notes   = NULL) const {
		return const_iterator (*this, t, force_discrete, f, active_notes);
	}

	const const_iterator& end() const { return _end_iter; }

	// CONST iterator implementations (x3)
	typename Notes::const_iterator note_lower_bound (Time t) const;
	typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const;
	typename SysExes::const_iterator sysex_lower_bound (Time t) const;

	// NON-CONST iterator implementations (x3)
	typename Notes::iterator note_lower_bound (Time t);
	typename PatchChanges::iterator patch_change_lower_bound (Time t);
	typename SysExes::iterator sysex_lower_bound (Time t);

	bool control_to_midi_event(boost::shared_ptr< Event<Time> >& ev,
	                           const ControlIterator&            iter) const;

	bool edited() const      { return _edited; }
	void set_edited(bool yn) { _edited = yn; }

	bool overlaps (const NotePtr& ev,
	               const NotePtr& ignore_this_note) const;
	bool contains (const NotePtr& ev) const;

	bool add_note_unlocked (const NotePtr note, void* arg = 0);
	void remove_note_unlocked(const constNotePtr note);

	void add_patch_change_unlocked (const PatchChangePtr);
	void remove_patch_change_unlocked (const constPatchChangePtr);

	void add_sysex_unlocked (const SysExPtr);
	void remove_sysex_unlocked (const SysExPtr);

	uint8_t lowest_note()  const { return _lowest_note; }
	uint8_t highest_note() const { return _highest_note; }


protected:
	bool                   _edited;
	bool                   _overlapping_pitches_accepted;
	OverlapPitchResolution _overlap_pitch_resolution;
	mutable Glib::Threads::RWLock   _lock;
	bool                   _writing;

	virtual int resolve_overlaps_unlocked (const NotePtr, void* /* arg */ = 0) {
		return 0;
	}

	typedef std::multiset<NotePtr, NoteNumberComparator>  Pitches;
	inline       Pitches& pitches(uint8_t chan)       { return _pitches[chan&0xf]; }
	inline const Pitches& pitches(uint8_t chan) const { return _pitches[chan&0xf]; }

	virtual void control_list_marked_dirty ();

private:
	friend class const_iterator;

	bool overlaps_unlocked (const NotePtr& ev, const NotePtr& ignore_this_note) const;
	bool contains_unlocked (const NotePtr& ev) const;

	void append_note_on_unlocked(const MIDIEvent<Time>& event, Evoral::event_id_t);
	void append_note_off_unlocked(const MIDIEvent<Time>& event);
	void append_control_unlocked(const Parameter& param, Time time, double value, Evoral::event_id_t);
	void append_sysex_unlocked(const MIDIEvent<Time>& ev, Evoral::event_id_t);
	void append_patch_change_unlocked(const PatchChange<Time>&, Evoral::event_id_t);

	void get_notes_by_pitch (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;
	void get_notes_by_velocity (Notes&, NoteOperator, uint8_t val, int chan_mask = 0) const;

	const TypeMap& _type_map;

	Notes        _notes;       // notes indexed by time
	Pitches      _pitches[16]; // notes indexed by channel+pitch
	SysExes      _sysexes;
	PatchChanges _patch_changes;

	typedef std::multiset<NotePtr, EarlierNoteComparator> WriteNotes;
	WriteNotes _write_notes[16];

	/** Current bank number on each channel so that we know what
	 *  to put in PatchChange events when program changes are
	 *  seen.
	 */
	int _bank[16];

	const   const_iterator _end_iter;
	bool                   _percussive;

	uint8_t _lowest_note;
	uint8_t _highest_note;
};


} // namespace Evoral

template<typename Time> /*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream& o, const Evoral::Sequence<Time>& s) { s.dump (o); return o; }


#endif // EVORAL_SEQUENCE_HPP