summaryrefslogtreecommitdiff
path: root/libs/evoral/evoral/Sequence.hpp
blob: bc3831df6bc592b63db0a4656bc1aa676e243638 (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
/* 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/thread.h>
#include "evoral/types.hpp"
#include "evoral/Note.hpp"
#include "evoral/Parameter.hpp"
#include "evoral/ControlSet.hpp"
#include "evoral/ControlList.hpp"
#include "evoral/PatchChange.hpp"

namespace Evoral {

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 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 Sequence : virtual public ControlSet {
public:
	Sequence(const TypeMap& type_map);
	Sequence(const Sequence<Time>& other);

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

public:

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

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

	virtual ReadLock  read_lock() const { return ReadLock(new Glib::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 = 0);

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

	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();
		}
	};

	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();
		}
	};

	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() > b->end_time();
		}
	};

	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 Sequence<Time>::Notes& n);

	typedef std::vector< boost::shared_ptr< Event<Time> > > 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 const_iterator {
	public:
		const_iterator();
		const_iterator(const Sequence<Time>& seq, Time t, bool, std::set<Evoral::Parameter> const &);
		~const_iterator();

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

		void invalidate();

		const Event<Time>& operator*()  const { return *_event;  }
		const boost::shared_ptr< Event<Time> > operator->() const  { return _event; }
		const boost::shared_ptr< Event<Time> > get_event_pointer() { 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>;

		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 = 0,
		bool force_discrete = false,
		std::set<Evoral::Parameter> const & f = std::set<Evoral::Parameter> ()) const {
		return const_iterator (*this, t, force_discrete, f);
	}
	
	const const_iterator& end() const { return _end_iter; }

	typename Notes::const_iterator note_lower_bound (Time t) const;
	typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const;

	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);

	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::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]; }

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 (NotePtr, Evoral::event_id_t);
	void append_note_off_unlocked(NotePtr);
	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;

	virtual void control_list_marked_dirty ();

	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> std::ostream& operator<<(std::ostream& o, const Evoral::Sequence<Time>& s) { s.dump (o); return o; }

#endif // EVORAL_SEQUENCE_HPP