summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour/midi_ring_buffer.h
blob: e8b499397d50e5a5f026bd8bbd4c6b4c734f7317 (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
/*
    Copyright (C) 2006 Paul Davis 

    This program 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.

    This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __ardour_midi_ring_buffer_h__
#define __ardour_midi_ring_buffer_h__

#include <algorithm>
#include <ardour/types.h>
#include <pbd/ringbufferNPT.h>
#include <ardour/buffer.h>

namespace ARDOUR {

/** A MIDI RingBuffer
 * (necessary because MIDI events are variable sized so a generic RB won't do).
 *
 * ALL publically accessible sizes refer to event COUNTS.  What actually goes
 * on in here is none of the callers business :)
 */
class MidiRingBuffer {
public:
	MidiRingBuffer (size_t size)
		: _size(size)
		, _max_event_size(MidiBuffer::max_event_size())
		, _ev_buf(new MidiEvent[size])
		, _raw_buf(new RawMidi[size * _max_event_size])
	{
		reset ();
		assert(read_space() == 0);
		assert(write_space() == size - 1);
	}
	
	virtual ~MidiRingBuffer() {
		delete[] _ev_buf;
		delete[] _raw_buf;
	}

	void reset () {
		/* !!! NOT THREAD SAFE !!! */
		g_atomic_int_set (&_write_ptr, 0);
		g_atomic_int_set (&_read_ptr, 0);
	}

	size_t write_space () {
		size_t w, r;
		
		w = g_atomic_int_get (&_write_ptr);
		r = g_atomic_int_get (&_read_ptr);
		
		if (w > r) {
			return ((r - w + _size) % _size) - 1;
		} else if (w < r) {
			return (r - w) - 1;
		} else {
			return _size - 1;
		}
	}
	
	size_t read_space () {
		size_t w, r;
		
		w = g_atomic_int_get (&_write_ptr);
		r = g_atomic_int_get (&_read_ptr);
		
		if (w > r) {
			return w - r;
		} else {
			return (w - r + _size) % _size;
		}
	}

	size_t capacity() const { return _size; }

	/** Read one event and appends it to @a out. */
	//size_t read(MidiBuffer& out);

	/** Write one event (@a in) */
	size_t write(const MidiEvent& in); // deep copies in

	/** Read events all events up to time @a end into @a out, leaving stamps intact.
	 * Any events before @a start will be dropped. */
	size_t read(MidiBuffer& out, jack_nframes_t start, jack_nframes_t end);

	/** Write all events from @a in, applying @a offset to all time stamps */
	size_t write(const MidiBuffer& in, jack_nframes_t offset = 0);

	inline void clear_event(size_t index);

private:

	// _event_ indices
	mutable gint _write_ptr;
	mutable gint _read_ptr;
	
	size_t     _size;           // size (capacity) in events
	size_t     _max_event_size; // ratio of raw_buf size to ev_buf size
	MidiEvent* _ev_buf;         // these point into...
	RawMidi*   _raw_buf;        // this

};

/** Just for sanity checking */
inline void
MidiRingBuffer::clear_event(size_t index)
{
	memset(&_ev_buf[index].buffer, 0, _max_event_size);
	_ev_buf[index].time = 0;
	_ev_buf[index].size = 0;
	_ev_buf[index].buffer = 0;

}

inline size_t
MidiRingBuffer::write (const MidiEvent& ev)
{
	//static jack_nframes_t last_write_time = 0;
	
	assert(ev.size > 0);

	size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);

	if (write_space () == 0) {
		return 0;
	} else {
		//assert(ev.time >= last_write_time);

		const size_t raw_index = priv_write_ptr * _max_event_size;

		MidiEvent* const write_ev = &_ev_buf[priv_write_ptr];
		*write_ev = ev;

		memcpy(&_raw_buf[raw_index], ev.buffer, ev.size);
		write_ev->buffer = &_raw_buf[raw_index];
        g_atomic_int_set(&_write_ptr, (priv_write_ptr + 1) % _size);
		
		//printf("MRB - wrote %xd %d %d with time %u at index %zu (raw index %zu)\n",
		//	write_ev->buffer[0], write_ev->buffer[1], write_ev->buffer[2], write_ev->time,
		//	priv_write_ptr, raw_index);
		
		assert(write_ev->size = ev.size);

		//last_write_time = ev.time;
		//printf("(W) read space: %zu\n", read_space());

		return 1;
	}
}

inline size_t
MidiRingBuffer::read(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
{
	if (read_space() == 0)
		return 0;

	size_t         priv_read_ptr = g_atomic_int_get(&_read_ptr);
	jack_nframes_t time          = _ev_buf[priv_read_ptr].time;
	size_t         count         = 0;
	size_t         limit         = read_space();

	while (time <= end && limit > 0) {
		MidiEvent* const read_ev = &_ev_buf[priv_read_ptr];
		if (time >= start) {
			dst.push_back(*read_ev);
			//printf("MRB - read %#X %d %d with time %u at index %zu\n",
			//	read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
			//	priv_read_ptr);
		} else {
			printf("MRB - SKIPPING - %#X %d %d with time %u at index %zu\n",
				read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
				priv_read_ptr);
			break;
		}

		clear_event(priv_read_ptr);

		++count;
		--limit;
		
		priv_read_ptr = (priv_read_ptr + 1) % _size;
		
		assert(read_ev->time <= end);
		time = _ev_buf[priv_read_ptr].time;
	}
	
	g_atomic_int_set(&_read_ptr, priv_read_ptr);
	
	//printf("(R) read space: %zu\n", read_space());

	return count;
}

inline size_t
MidiRingBuffer::write(const MidiBuffer& in, jack_nframes_t offset)
{
	size_t num_events = in.size();
	size_t to_write = std::min(write_space(), num_events);

	// FIXME: double copy :/
	for (size_t i=0; i < to_write; ++i) {
		MidiEvent ev = in[i];
		ev.time += offset;
		write(ev);
	}

	return to_write;
}

} // namespace ARDOUR

#endif // __ardour_midi_ring_buffer_h__