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
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #ifndef playback_buffer_h
21 #define playback_buffer_h
26 #include "pbd/libpbd_visibility.h"
27 #include "pbd/spinlock.h"
32 class /*LIBPBD_API*/ PlaybackBuffer
35 PlaybackBuffer (int32_t sz, guint res = 8191)
42 for (power_of_two = 1; 1U << power_of_two < sz; ++power_of_two);
43 size = 1 << power_of_two;
52 virtual ~PlaybackBuffer () {
56 /* non-linear write needs to reset() the buffer and set the
57 * position that write() will commence at */
58 void reset (int64_t start = 0) {
59 /* writer, when seeking, may block */
60 Glib::Threads::Mutex::Lock lm (_reset_lock);
61 SpinLock sl (_writepos_lock);
64 g_atomic_int_set (&write_idx, g_atomic_int_get (&read_idx));
67 guint write_space () const {
70 w = g_atomic_int_get (&write_idx);
71 r = g_atomic_int_get (&read_idx);
76 rv = (r - w + size) & size_mask;
82 /* it may hapen that the read/invalidation-pointer moves backwards
83 * e.g. after rec-stop, declick fade-out.
84 * At the same time the butler may already have written data.
85 * (it's safe as long as the disk-reader does not move backwards by more
87 * XXX disk-reading de-click should not move the invalidation-pointer
89 if (rv > reservation) {
90 return rv - 1 - reservation;
95 guint read_space () const {
98 w = g_atomic_int_get (&write_idx);
99 r = g_atomic_int_get (&read_idx);
104 return (w - r + size) & size_mask;
108 guint read (T *dest, guint cnt, bool commit = true);
109 guint write (T const * src, guint cnt);
111 T *buffer () { return buf; }
112 guint bufsize () const { return size; }
114 guint get_write_idx () const { return g_atomic_int_get (&write_idx); }
115 guint get_read_idx () const { return g_atomic_int_get (&read_idx); }
117 void read_flush () { g_atomic_int_set (&read_idx, g_atomic_int_get (&write_idx)); }
119 void increment_read_ptr (guint cnt) {
120 cnt = std::min (cnt, read_space ());
121 g_atomic_int_set (&read_idx, (g_atomic_int_get (&read_idx) + cnt) & size_mask);
130 int64_t write_pos; // samplepos_t
132 mutable gint write_idx; // corresponds to (write_pos)
133 mutable gint read_idx;
136 /* spinlock will be used to update write_pos and write_idx in sync */
137 mutable spinlock_t _writepos_lock;
138 /* reset_lock is used to prevent concurrent reading and reset (seek, transport reversal etc). */
139 Glib::Threads::Mutex _reset_lock;
143 template<class T> /*LIBPBD_API*/ guint
144 PlaybackBuffer<T>::write (T const *src, guint cnt)
146 guint w = g_atomic_int_get (&write_idx);
147 const guint free_cnt = write_space ();
153 const guint to_write = cnt > free_cnt ? free_cnt : cnt;
154 const guint cnt2 = w + to_write;
159 n2 = cnt2 & size_mask;
165 memcpy (&buf[w], src, n1 * sizeof (T));
166 w = (w + n1) & size_mask;
169 memcpy (buf, src+n1, n2 * sizeof (T));
174 SpinLock sl (_writepos_lock);
175 write_pos += to_write;
176 g_atomic_int_set (&write_idx, w);
181 template<class T> /*LIBPBD_API*/ guint
182 PlaybackBuffer<T>::read (T *dest, guint cnt, bool commit)
184 Glib::Threads::Mutex::Lock lm (_reset_lock, Glib::Threads::TRY_LOCK);
186 /* seek, reset in progress */
190 guint r = g_atomic_int_get (&read_idx);
191 const guint w = g_atomic_int_get (&write_idx);
193 const guint free_cnt = (w > r) ? (w - r) : ((w - r + size) & size_mask);
194 const guint to_read = cnt > free_cnt ? free_cnt : cnt;
196 const guint cnt2 = r + to_read;
201 n2 = cnt2 & size_mask;
207 memcpy (dest, &buf[r], n1 * sizeof (T));
208 r = (r + n1) & size_mask;
211 memcpy (dest + n1, buf, n2 * sizeof (T));
216 /* set read-pointer to position of last read's end */
217 g_atomic_int_set (&read_idx, r);
222 } /* end namespace */
224 #endif /* __ringbuffer_h__ */