Allow trim of midi regions to before the start of the source. Fixes #3156.
[ardour.git] / libs / evoral / evoral / RingBuffer.hpp
1 /* This file is part of Evoral.
2  * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
3  *
4  * Evoral is free software; you can redistribute it and/or modify it under the
5  * terms of the GNU General Public License as published by the Free Software
6  * Foundation; either version 2 of the License, or (at your option) any later
7  * version.
8  *
9  * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16  */
17
18 #ifndef EVORAL_RING_BUFFER_HPP
19 #define EVORAL_RING_BUFFER_HPP
20
21 #include <cassert>
22 #include <iostream>
23 #include <glib.h>
24
25 namespace Evoral {
26
27
28 /** A lock-free RingBuffer.
29  * Read/Write realtime safe.
30  * Single-reader Single-writer thread safe.
31  */
32 template <typename Time>
33 class RingBuffer {
34 public:
35
36         /** @param size Size in bytes.
37          */
38         RingBuffer(size_t size)
39                 : _size(size)
40                 , _buf(new Time[size])
41         {
42                 reset();
43                 assert(read_space() == 0);
44                 assert(write_space() == size - 1);
45         }
46
47         virtual ~RingBuffer() {
48                 delete[] _buf;
49         }
50
51         /** Reset(empty) the ringbuffer.
52          * NOT thread safe.
53          */
54         void reset() {
55                 g_atomic_int_set(&_write_ptr, 0);
56                 g_atomic_int_set(&_read_ptr, 0);
57         }
58
59         /** Calculate remaining space for writing
60          */
61         size_t write_space() const {
62                 const size_t w = g_atomic_int_get(&_write_ptr);
63                 const size_t r = g_atomic_int_get(&_read_ptr);
64
65                 if (w > r) {
66                         return ((r - w + _size) % _size) - 1;
67                 } else if (w < r) {
68                         return (r - w) - 1;
69                 } else {
70                         return _size - 1;
71                 }
72         }
73
74         /** Calculate how much still can be read
75          */
76         size_t read_space() const {
77                 const size_t w = g_atomic_int_get(&_write_ptr);
78                 const size_t r = g_atomic_int_get(&_read_ptr);
79
80                 if (w > r) {
81                         return w - r;
82                 } else {
83                         return (w - r + _size) % _size;
84                 }
85         }
86
87         /** Report the buffers size
88          */
89         size_t capacity() const { return _size; }
90
91         /** Peek at the ringbuffer (read w/o advancing read pointer).
92          * @return how much has been peeked (read cannot exceed the end
93          * of the buffer):
94          * <pre>
95          * |-------------------------R=============================|
96          *            read-pointer---^
97          * </pre>
98          */
99         size_t peek(size_t size, Time* dst);
100
101         /** Peek at the ringbuffer (read w/o advancing read pointer).
102          * @return how much has been peeked (wraps around if read exceeds
103          * the end of the buffer):
104          * <pre>
105          * |===========--------------R=============================|
106          *            read-pointer---^
107          * </pre>
108          */
109         bool   full_peek(size_t size, Time* dst);
110
111         /** Read from the ringbuffer. (advances read pointer)
112          * @return how much has been read (read cannot exceed the end
113          * of the buffer):
114          */
115         size_t read(size_t size, Time* dst);
116
117         /** Read from the ringbuffer. (advances read pointer)
118          * @return how much has been peeked (wraps around if read exceeds
119          * the end of the buffer):
120          */
121         bool   full_read(size_t size, Time* dst);
122
123         /** Advance read pointer by size
124          */
125         bool   skip(size_t size);
126
127         void   write(size_t size, const Time* src);
128
129 protected:
130         mutable int _write_ptr;
131         mutable int _read_ptr;
132
133         size_t _size; ///< Size (capacity) in bytes
134         Time*  _buf;  ///< size, event, size, event...
135 };
136
137
138 /** Peek at the ringbuffer (read w/o advancing read pointer).
139  *
140  * Note that a full read may not be done if the data wraps around.
141  * Caller must check return value and call again if necessary, or use the
142  * full_peek method which does this automatically.
143  */
144 template<typename Time>
145 size_t
146 RingBuffer<Time>::peek(size_t size, Time* dst)
147 {
148         const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
149
150         const size_t read_size = (priv_read_ptr + size < _size)
151                         ? size
152                         : _size - priv_read_ptr;
153
154         memcpy(dst, &_buf[priv_read_ptr], read_size);
155
156         return read_size;
157 }
158
159
160 template<typename Time>
161 bool
162 RingBuffer<Time>::full_peek(size_t size, Time* dst)
163 {
164         if (read_space() < size) {
165                 return false;
166         }
167
168         const size_t read_size = peek(size, dst);
169
170         if (read_size < size) {
171                 peek(size - read_size, dst + read_size);
172         }
173
174         return true;
175 }
176
177
178 /** Read from the ringbuffer.
179  *
180  * Note that a full read may not be done if the data wraps around.
181  * Caller must check return value and call again if necessary, or use the
182  * full_read method which does this automatically.
183  */
184 template<typename Time>
185 size_t
186 RingBuffer<Time>::read(size_t size, Time* dst)
187 {
188         const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
189
190         const size_t read_size = (priv_read_ptr + size < _size)
191                         ? size
192                         : _size - priv_read_ptr;
193
194         memcpy(dst, &_buf[priv_read_ptr], read_size);
195
196         g_atomic_int_set(&_read_ptr, (priv_read_ptr + read_size) % _size);
197
198         return read_size;
199 }
200
201
202 template<typename Time>
203 bool
204 RingBuffer<Time>::full_read(size_t size, Time* dst)
205 {
206         if (read_space() < size) {
207                 return false;
208         }
209
210         const size_t read_size = read(size, dst);
211
212         if (read_size < size) {
213                 read(size - read_size, dst + read_size);
214         }
215
216         return true;
217 }
218
219
220 template<typename Time>
221 bool
222 RingBuffer<Time>::skip(size_t size)
223 {
224         if (read_space() < size) {
225                 std::cerr << "WARNING: Attempt to skip past end of MIDI ring buffer" << std::endl;
226                 return false;
227         }
228
229         const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
230         g_atomic_int_set(&_read_ptr, (priv_read_ptr + size) % _size);
231
232         return true;
233 }
234
235
236 template<typename Time>
237 inline void
238 RingBuffer<Time>::write(size_t size, const Time* src)
239 {
240         const size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);
241
242         if (priv_write_ptr + size <= _size) {
243                 memcpy(&_buf[priv_write_ptr], src, size);
244                 g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size);
245         } else {
246                 const size_t this_size = _size - priv_write_ptr;
247                 assert(this_size < size);
248                 assert(priv_write_ptr + this_size <= _size);
249                 memcpy(&_buf[priv_write_ptr], src, this_size);
250                 memcpy(&_buf[0], src+this_size, size - this_size);
251                 g_atomic_int_set(&_write_ptr, size - this_size);
252         }
253 }
254
255
256 } // namespace Evoral
257
258 #endif // EVORAL_RING_BUFFER_HPP
259
260