2 * Copyright (C) 2000 Paul Davis & Benno Senoner
3 * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #ifndef playback_buffer_h
21 #define playback_buffer_h
27 #include "pbd/libpbd_visibility.h"
28 #include "pbd/spinlock.h"
33 class /*LIBPBD_API*/ PlaybackBuffer
36 static guint power_of_two_size (guint sz) {
38 for (power_of_two = 1; 1U << power_of_two < sz; ++power_of_two);
39 return 1U << power_of_two;
42 PlaybackBuffer (guint sz, guint res = 8191)
46 size = power_of_two_size (sz);
50 g_atomic_int_set (&read_idx, 0);
54 virtual ~PlaybackBuffer () {
59 T *buffer () { return buf; }
61 guint bufsize () const { return size; }
65 /* writer, when seeking, may block */
66 Glib::Threads::Mutex::Lock lm (_reset_lock);
67 SpinLock sl (_reservation_lock);
68 g_atomic_int_set (&write_idx, g_atomic_int_get (&read_idx));
69 g_atomic_int_set (&reserved, 0);
73 guint write_space () const {
76 w = g_atomic_int_get (&write_idx);
77 r = g_atomic_int_get (&read_idx);
82 rv = (r - w + size) & size_mask;
88 /* it may hapen that the read/invalidation-pointer moves backwards
89 * e.g. after rec-stop, declick fade-out.
90 * At the same time the butler may already have written data.
91 * (it's safe as long as the disk-reader does not move backwards by more
93 * XXX disk-reading de-click should not move the invalidation-pointer
95 if (rv > reservation) {
96 return rv - 1 - reservation;
102 guint read_space () const {
105 w = g_atomic_int_get (&write_idx);
106 r = g_atomic_int_get (&read_idx);
111 return (w - r + size) & size_mask;
116 guint read (T *dest, guint cnt, bool commit = true, guint offset = 0);
119 guint write (T const * src, guint cnt);
121 guint write_zero (guint cnt);
123 guint increment_write_ptr (guint cnt)
125 cnt = std::min (cnt, write_space ());
126 g_atomic_int_set (&write_idx, (g_atomic_int_get (&write_idx) + cnt) & size_mask);
133 SpinLock sl (_reservation_lock);
134 g_atomic_int_set (&read_idx, g_atomic_int_get (&write_idx));
135 g_atomic_int_set (&reserved, 0);
139 guint decrement_read_ptr (guint cnt)
141 SpinLock sl (_reservation_lock);
142 guint r = g_atomic_int_get (&read_idx);
143 guint res = g_atomic_int_get (&reserved);
145 cnt = std::min (cnt, res);
147 r = (r + size - cnt) & size_mask;
150 g_atomic_int_set (&read_idx, r);
151 g_atomic_int_set (&reserved, res);
157 guint increment_read_ptr (guint cnt)
159 cnt = std::min (cnt, read_space ());
161 SpinLock sl (_reservation_lock);
162 g_atomic_int_set (&read_idx, (g_atomic_int_get (&read_idx) + cnt) & size_mask);
163 g_atomic_int_set (&reserved, std::min (reservation, g_atomic_int_get (&reserved) + cnt));
169 bool can_seek (int64_t cnt) {
171 return read_space() >= cnt;
174 return g_atomic_int_get (&reserved) >= -cnt;
181 guint read_ptr() const { return read_idx; }
182 guint reserved_size() const { return reserved; }
190 mutable gint write_idx;
191 mutable gint read_idx;
192 mutable gint reserved;
194 /* spinlock will be used to update write_idx and reserved in sync */
195 spinlock_t _reservation_lock;
196 /* reset_lock is used to prevent concurrent reading and reset (seek, transport reversal etc). */
197 Glib::Threads::Mutex _reset_lock;
200 template<class T> /*LIBPBD_API*/ guint
201 PlaybackBuffer<T>::write (T const *src, guint cnt)
203 guint w = g_atomic_int_get (&write_idx);
204 const guint free_cnt = write_space ();
210 const guint to_write = cnt > free_cnt ? free_cnt : cnt;
211 const guint cnt2 = w + to_write;
216 n2 = cnt2 & size_mask;
222 memcpy (&buf[w], src, n1 * sizeof (T));
223 w = (w + n1) & size_mask;
226 memcpy (buf, src+n1, n2 * sizeof (T));
230 g_atomic_int_set (&write_idx, w);
234 template<class T> /*LIBPBD_API*/ guint
235 PlaybackBuffer<T>::write_zero (guint cnt)
237 guint w = g_atomic_int_get (&write_idx);
238 const guint free_cnt = write_space ();
244 const guint to_write = cnt > free_cnt ? free_cnt : cnt;
245 const guint cnt2 = w + to_write;
250 n2 = cnt2 & size_mask;
256 memset (&buf[w], 0, n1 * sizeof (T));
257 w = (w + n1) & size_mask;
260 memset (buf, 0, n2 * sizeof (T));
264 g_atomic_int_set (&write_idx, w);
268 template<class T> /*LIBPBD_API*/ guint
269 PlaybackBuffer<T>::read (T *dest, guint cnt, bool commit, guint offset)
271 Glib::Threads::Mutex::Lock lm (_reset_lock, Glib::Threads::TRY_LOCK);
273 /* seek, reset in progress */
277 guint r = g_atomic_int_get (&read_idx);
278 const guint w = g_atomic_int_get (&write_idx);
280 guint free_cnt = (w > r) ? (w - r) : ((w - r + size) & size_mask);
282 if (!commit && offset > 0) {
283 if (offset > free_cnt) {
287 r = (r + offset) & size_mask;
290 const guint to_read = cnt > free_cnt ? free_cnt : cnt;
292 const guint cnt2 = r + to_read;
297 n2 = cnt2 & size_mask;
303 memcpy (dest, &buf[r], n1 * sizeof (T));
304 r = (r + n1) & size_mask;
307 memcpy (dest + n1, buf, n2 * sizeof (T));
312 SpinLock sl (_reservation_lock);
313 g_atomic_int_set (&read_idx, r);
314 g_atomic_int_set (&reserved, std::min (reservation, g_atomic_int_get (&reserved) + to_read));
319 } /* end namespace */
321 #endif /* __ringbuffer_h__ */