Prepare buffer for seeking
Keep track of safe reservation: Data has been read (or was skipped) previously can be read again up to the allocated "reservation" (which is never overwritten).
This commit is contained in:
parent
b3fda6236a
commit
6975b5ca54
|
@ -34,7 +34,7 @@ class /*LIBPBD_API*/ PlaybackBuffer
|
||||||
public:
|
public:
|
||||||
PlaybackBuffer (int32_t sz, guint res = 8191)
|
PlaybackBuffer (int32_t sz, guint res = 8191)
|
||||||
: reservation (res)
|
: reservation (res)
|
||||||
, _writepos_lock ()
|
, _reservation_lock ()
|
||||||
{
|
{
|
||||||
sz += reservation;
|
sz += reservation;
|
||||||
|
|
||||||
|
@ -46,24 +46,28 @@ public:
|
||||||
buf = new T[size];
|
buf = new T[size];
|
||||||
|
|
||||||
read_idx = 0;
|
read_idx = 0;
|
||||||
reset (0);
|
reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~PlaybackBuffer () {
|
virtual ~PlaybackBuffer () {
|
||||||
delete [] buf;
|
delete [] buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* non-linear write needs to reset() the buffer and set the
|
/* init (mlock) */
|
||||||
* position that write() will commence at */
|
T *buffer () { return buf; }
|
||||||
void reset (int64_t start = 0) {
|
/* init (mlock) */
|
||||||
|
guint bufsize () const { return size; }
|
||||||
|
|
||||||
|
/* write-thread */
|
||||||
|
void reset () {
|
||||||
/* writer, when seeking, may block */
|
/* writer, when seeking, may block */
|
||||||
Glib::Threads::Mutex::Lock lm (_reset_lock);
|
Glib::Threads::Mutex::Lock lm (_reset_lock);
|
||||||
SpinLock sl (_writepos_lock);
|
SpinLock sl (_reservation_lock);
|
||||||
write_pos = start;
|
|
||||||
|
|
||||||
g_atomic_int_set (&write_idx, g_atomic_int_get (&read_idx));
|
g_atomic_int_set (&write_idx, g_atomic_int_get (&read_idx));
|
||||||
|
g_atomic_int_set (&reserved, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* write-thread */
|
||||||
guint write_space () const {
|
guint write_space () const {
|
||||||
guint w, r;
|
guint w, r;
|
||||||
|
|
||||||
|
@ -92,6 +96,7 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* read-thread */
|
||||||
guint read_space () const {
|
guint read_space () const {
|
||||||
guint w, r;
|
guint w, r;
|
||||||
|
|
||||||
|
@ -105,37 +110,77 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* read-thead */
|
||||||
guint read (T *dest, guint cnt, bool commit = true);
|
guint read (T *dest, guint cnt, bool commit = true);
|
||||||
|
|
||||||
|
/* write-thead */
|
||||||
guint write (T const * src, guint cnt);
|
guint write (T const * src, guint cnt);
|
||||||
|
/* write-thead */
|
||||||
guint write_zero (guint cnt);
|
guint write_zero (guint cnt);
|
||||||
|
|
||||||
T *buffer () { return buf; }
|
/* read-thead */
|
||||||
guint bufsize () const { return size; }
|
void read_flush ()
|
||||||
|
{
|
||||||
guint get_write_idx () const { return g_atomic_int_get (&write_idx); }
|
SpinLock sl (_reservation_lock);
|
||||||
guint get_read_idx () const { return g_atomic_int_get (&read_idx); }
|
g_atomic_int_set (&read_idx, g_atomic_int_get (&write_idx));
|
||||||
|
g_atomic_int_set (&reserved, 0);
|
||||||
void read_flush () { g_atomic_int_set (&read_idx, g_atomic_int_get (&write_idx)); }
|
|
||||||
|
|
||||||
void increment_read_ptr (guint cnt) {
|
|
||||||
cnt = std::min (cnt, read_space ());
|
|
||||||
g_atomic_int_set (&read_idx, (g_atomic_int_get (&read_idx) + cnt) & size_mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
/* read-thead */
|
||||||
|
guint decrement_read_ptr (guint cnt)
|
||||||
|
{
|
||||||
|
SpinLock sl (_reservation_lock);
|
||||||
|
guint r = g_atomic_int_get (&read_idx);
|
||||||
|
guint res = g_atomic_int_get (&reserved);
|
||||||
|
|
||||||
|
cnt = std::min (cnt, res);
|
||||||
|
|
||||||
|
r = (r + size - cnt) & size_mask;
|
||||||
|
res -= cnt;
|
||||||
|
|
||||||
|
g_atomic_int_set (&read_idx, r);
|
||||||
|
g_atomic_int_set (&reserved, res);
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read-thead */
|
||||||
|
guint increment_read_ptr (guint cnt)
|
||||||
|
{
|
||||||
|
cnt = std::min (cnt, read_space ());
|
||||||
|
|
||||||
|
SpinLock sl (_reservation_lock);
|
||||||
|
g_atomic_int_set (&read_idx, (g_atomic_int_get (&read_idx) + cnt) & size_mask);
|
||||||
|
g_atomic_int_set (&reserved, std::min (reservation, g_atomic_int_get (&reserved) + cnt));
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read-thead */
|
||||||
|
bool can_seek (int cnt) {
|
||||||
|
if (cnt > 0) {
|
||||||
|
return read_space() >= cnt;
|
||||||
|
}
|
||||||
|
else if (cnt < 0) {
|
||||||
|
return g_atomic_int_get (&reserved) >= cnt;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
T *buf;
|
T *buf;
|
||||||
guint reservation;
|
guint reservation;
|
||||||
guint size;
|
guint size;
|
||||||
guint size_mask;
|
guint size_mask;
|
||||||
|
|
||||||
int64_t write_pos; // samplepos_t
|
mutable gint write_idx;
|
||||||
|
|
||||||
mutable gint write_idx; // corresponds to (write_pos)
|
|
||||||
mutable gint read_idx;
|
mutable gint read_idx;
|
||||||
|
mutable gint reserved;
|
||||||
|
|
||||||
private:
|
/* spinlock will be used to update write_idx and reserved in sync */
|
||||||
/* spinlock will be used to update write_pos and write_idx in sync */
|
spinlock_t _reservation_lock;
|
||||||
mutable spinlock_t _writepos_lock;
|
|
||||||
/* reset_lock is used to prevent concurrent reading and reset (seek, transport reversal etc). */
|
/* reset_lock is used to prevent concurrent reading and reset (seek, transport reversal etc). */
|
||||||
Glib::Threads::Mutex _reset_lock;
|
Glib::Threads::Mutex _reset_lock;
|
||||||
};
|
};
|
||||||
|
@ -170,11 +215,7 @@ PlaybackBuffer<T>::write (T const *src, guint cnt)
|
||||||
w = n2;
|
w = n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
g_atomic_int_set (&write_idx, w);
|
||||||
SpinLock sl (_writepos_lock);
|
|
||||||
write_pos += to_write;
|
|
||||||
g_atomic_int_set (&write_idx, w);
|
|
||||||
}
|
|
||||||
return to_write;
|
return to_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,11 +249,7 @@ PlaybackBuffer<T>::write_zero (guint cnt)
|
||||||
w = n2;
|
w = n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
g_atomic_int_set (&write_idx, w);
|
||||||
SpinLock sl (_writepos_lock);
|
|
||||||
write_pos += to_write;
|
|
||||||
g_atomic_int_set (&write_idx, w);
|
|
||||||
}
|
|
||||||
return to_write;
|
return to_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,10 +288,11 @@ PlaybackBuffer<T>::read (T *dest, guint cnt, bool commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commit) {
|
if (commit) {
|
||||||
/* set read-pointer to position of last read's end */
|
SpinLock sl (_reservation_lock);
|
||||||
g_atomic_int_set (&read_idx, r);
|
g_atomic_int_set (&read_idx, r);
|
||||||
|
g_atomic_int_set (&reserved, std::min (reservation, g_atomic_int_get (&reserved) + to_read));
|
||||||
}
|
}
|
||||||
return cnt;
|
return to_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace */
|
} /* end namespace */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user