-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjackringbuffer.h
322 lines (284 loc) · 11.8 KB
/
jackringbuffer.h
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
#ifndef JACKRINGBUFFER_H
#define JACKRINGBUFFER_H
/*
Copyright 2011 Arne Jacobs <[email protected]>
This file is part of elektrocillin.
Elektrocillin 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 3 of the License, or
(at your option) any later version.
Elektrocillin 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 Elektrocillin. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QObject>
#include <QDataStream>
#include <QByteArray>
#include <QSet>
#include <jack/ringbuffer.h>
#include <jack/types.h>
/**
This is a C++ abstraction over jack_ringbuffer_t.
It is a template class that allows reading and writing of structs of a given type T.
T has to be a data type that can be copied just by copying the memory that its objects
use (with size equal to sizeof(T)).
E.g., classes with constructor and/or destructor generally don't qualify.
Simple structs generally do.
*/
template<class T> class JackRingBuffer
{
public:
JackRingBuffer(size_t size)
{
ringBuffer = jack_ringbuffer_create(size * sizeof(T));
}
~JackRingBuffer()
{
jack_ringbuffer_free(ringBuffer);
}
/**
@return The number of elements of type T that can be read from the ring buffer.
*/
size_t readSpace() const
{
return jack_ringbuffer_read_space(ringBuffer) / sizeof(T);
}
/**
@return The number of elements of type T that can be written to the ring buffer.
*/
size_t writeSpace() const
{
return jack_ringbuffer_write_space(ringBuffer) / sizeof(T);
}
/**
Reads a number of entries from the ring buffer.
Calling this method does not advance the read pointer, i.e.,
calling this multiple times successively always returns the same.
Note: this method involves copying the data from the ring buffer
to the given buffer, thus do not use it more often than necessary.
@param buffer pointer to a buffer of type T where data will be copied to.
It has to be large enough for at least n elements of type T.
@param n the number of elements to be read from the ring buffer.
*/
void peek(T *buffer, size_t n)
{
jack_ringbuffer_peek(ringBuffer, (char*)buffer, n * sizeof(T));
}
/**
Takes a look at the next element in the ring buffer.
This is a shortcut for peek(T *buffer, size_t n) with n=1.
The compiler can optimize this to not copy from the ring buffer
twice, i.e., it will directly copy the head of the ring buffer
to the caller. Note that it will still be copied once, thus
as with peek(T *buffer, size_t n) do not use it more often than
necessary.
@return the first entry in the ring buffer.
*/
T peek()
{
T element;
jack_ringbuffer_peek(ringBuffer, (char*)&element, sizeof(T));
return element;
}
/**
Reads a number of entries from the ring buffer.
This is similar to peek(), but it advances the read pointer after
reading.
@param buffer pointer to a buffer of type T where data will be copied to.
has to be large enough for at least n elements of type T.
@param n the number of elements to be copied to the given buffer.
*/
void read(T *buffer, size_t n)
{
jack_ringbuffer_read(ringBuffer, (char*)buffer, n * sizeof(T));
}
/**
Reads one element from the ring buffer.
This is a shortcut for read(T *buffer, size_t n) with n=1.
The compiler can optimize this to not copy from the ring buffer
twice, i.e., it will directly copy the first element in the ring buffer
to the caller. After reading the read pointer will be advanced by one step.
@return the first entry in the ring buffer.
*/
T read()
{
T element;
jack_ringbuffer_read(ringBuffer, (char*)&element, sizeof(T));
return element;
}
/**
Advances the read pointer of the ring buffer without reading from it.
Use this, e.g., if you have used peek before.
@param n the number of elements by which the read pointer should be advanced.
*/
void readAdvance(size_t n)
{
jack_ringbuffer_read_advance(ringBuffer, n * sizeof(T));
}
/**
Writes the given elements to the ring buffer and advances the write
pointer.
@param buffer pointer to a buffer of type T where data will be copied from.
It has to contain at least n elements of type T.
@param n the number of elements to be written to the ring buffer.
*/
void write(const T *buffer, size_t n)
{
jack_ringbuffer_write(ringBuffer, (const char*)buffer, n * sizeof(T));
}
/**
Writes one element to the ring buffer.
This is a shortcut for write(const T *buffer, size_t n) with n=1.
After writing the read pointer will be advanced by one step.
@param element the element to write to the ring buffer.
*/
void write(const T &element)
{
jack_ringbuffer_write(ringBuffer, (const char*)&element, sizeof(T));
}
/**
Advances the write pointer of the ring buffer without writing something to it.
@param n the number of elements by which the write pointer should be advanced.
*/
void writeAdvance(size_t n)
{
jack_ringbuffer_write_advance(ringBuffer, n * sizeof(T));
}
/**
Resets the ring buffer. Note: this method is NOT thread-safe.
*/
void reset()
{
jack_ringbuffer_reset(ringBuffer);
}
private:
jack_ringbuffer_t * ringBuffer;
};
class RingBufferEvent {
public:
virtual ~RingBufferEvent() {}
};
/**
This class enables lock-free communication between two threads, one
"sender" thread and one "receiver" thread.
The communication is based on "events". Any event that derives from
RingBufferEvent can be sent through an object of this class. One
instance only enables communication into one direction, thus you'll
need two instances of this class to have a two-way communcation
between two threads.
Using this class basically follows these steps:
0.) The receiver thread periodically calls hasEvents()
1.) The sender thread calls sendEvent()
2.) The receiver is not actively notified that a new event has been
sent. Instead, its next call to hasEvents() returns true
3.) The receiver calls readEvent() to get a pointer to the new event
4.) The receiver does something using the event's data
5.) When finished, the receiver calls returnEvent()
6.) During the next call to sendEvent() (or when the ring buffer is
is deleted), the returned event is deleted
Typically the sender thread creates the event objects and deletes them
if they have been processed. To make sure that this is so, you should
call sendEvent() only from the sender thread, and also delete the
ring buffer from the sending thread.
Note: deleting an object of this class is not thread-safe. Make sure
that noone except the deleting thread is accessing the ring buffer
or any events which have been put into the ring buffer and which have
not yet been returned, before you delete it!
Note: It is probably not a good idea to use this class for
communication between a Jack process thread (as sender) and another
thread, because there is memory allocation (object creation) and
deletion involved, which would happen in the Jack process thread.
*/
class RingBuffer
{
public:
/**
Initializes the ring buffer with the given size.
@param ringBufferSize specifies the maximum number of events
that can be in the ring buffer at the same time. I.e., the maximum
number of events that can be sent via sendEvent() without any of
them being retrieved via readEvent()
*/
RingBuffer(size_t ringBufferSize);
/**
Deletes all events that not yet been deleted yet (this can happen for
example when sendEvent() is not called anymore after events have been
returned).
Note: deleting an object of this class is not thread-safe. Make sure
that noone except the deleting thread is accessing the ring buffer
or any events which have been put into the ring buffer and which have
not yet been returned, before you delete it!
*/
~RingBuffer();
/**
Tests if any events have been sent by the sender thread.
This method is meant to be called by the receiver, but it can also
safely be called by any other thread.
@return true, iff any events are in the ring buffer
*/
bool hasEvents();
/**
Allows to look at the event time of the first event in the ring buffer.
Call hasEvents() before to make sure that there really is an event in
the buffer.
Calling this does not remove the first event from the ring buffer.
This method is meant to be called by the receiver, but it can also
safely be called by any other thread.
@return the time of the first event in the buffer, as given to
sendEvent() when the event was sent
*/
jack_nframes_t peekEventTime();
/**
Puts an event into the ring buffer. This method is meant to be called
by the sender thread.
The ownership of the given event is transferred to the ring buffer. It
is NOT safe to access the event after calling this method. The given
event will be deleted by the ring buffer after it has been processed
by the receiver.
@param event a pointer to the event which should be put into the ring
buffer. Ownership of the event is transferred to the ring buffer, and
it is NOT safe to access it after calling this method
@param time a time value, e.g., as given by Jack, which allows the event
to be associated with a given time point. Note that this class does
not take responsibility of sorting the given events temporally. The
given value is not interpreted in any way. Its meaning does depend
solely on what the sender and receiver define and agree upon
*/
bool sendEvent(RingBufferEvent *event, jack_nframes_t time);
/**
Reads and returns an event from the ring buffer. Call hasEvents()
before to make sure that there really is an event in the buffer.
This method is meant to be called by the receiver.
The returned event will be removed from the ring buffer.
Call returnEvent() after you have processed the event to make sure
that it is deleted properly.
@param time a reference to where the event's time will be stored by
this method
@return a pointer to the first event in the buffer
*/
RingBufferEvent * readEvent(jack_nframes_t &time);
/**
This is an overloaded function which can be used when the time
associated with an event is not relevant to the receiver.
See readEvent(jack_nframes_t &) for further information.
@return a pointer to the first event in the buffer
*/
RingBufferEvent * readEvent();
/**
This method is meant to be called by the receiver after
it has processed an event read from the buffer and doesn't
need access to it anymore.
The given event will be deleted during the next call to sendEvent()
(i.e., in the sender's thread) or when the ring buffer is deleted,
whichever comes first.
@param event a pointer to the event which should be deleted
*/
void returnEvent(RingBufferEvent *event);
private:
jack_ringbuffer_t *ringBuffer, *ringBufferReturn;
QSet<RingBufferEvent*> undeletedEvents;
};
#endif // JACKRINGBUFFER_H