Files
pmomusic/pmostream/buffer.go

140 lines
2.4 KiB
Go
Raw Normal View History

package pmostream
import (
"sync"
"time"
log "github.com/sirupsen/logrus"
)
type AudioBuffer struct {
mu sync.Mutex
cond *sync.Cond
chunks [][]byte
size int
available int
format SampleFormat
readPos int
writePos int
closed bool
}
func NewAudioBuffer(size int, format SampleFormat) *AudioBuffer {
log.Infof("New AudioBuffer with size %d", size)
ab := &AudioBuffer{
chunks: make([][]byte, size),
size: size,
format: format,
}
ab.cond = sync.NewCond(&ab.mu)
return ab
}
func (ab *AudioBuffer) Available() int {
ab.mu.Lock()
defer ab.mu.Unlock()
return ab.available
}
// Write ajoute un chunk dans le buffer (écrase le plus ancien si plein)
func (ab *AudioBuffer) Write(chunk []byte) {
ab.mu.Lock()
defer ab.mu.Unlock()
if ab.closed {
return
}
ab.chunks[ab.writePos] = chunk
ab.writePos = (ab.writePos + 1) % ab.size
if ab.available < ab.size {
ab.available++
} else {
// tampon plein, on écrase → avancer readPos
ab.readPos = (ab.readPos + 1) % ab.size
}
ab.cond.Broadcast()
}
func (ab *AudioBuffer) Read() []byte {
ab.mu.Lock()
defer ab.mu.Unlock()
if ab.available == 0 {
return nil
}
chunk := ab.chunks[ab.readPos]
ab.chunks[ab.readPos] = nil
ab.readPos = (ab.readPos + 1) % ab.size
ab.available--
return chunk
}
// WaitForData attend qu'au moins n chunks soient disponibles ou timeout/closed
func (ab *AudioBuffer) WaitForData(n int, timeout time.Duration) bool {
ab.mu.Lock()
defer ab.mu.Unlock()
if n <= 0 {
n = 1
}
if timeout <= 0 {
for ab.available < n && !ab.closed {
ab.cond.Wait()
}
return ab.available >= n && !ab.closed
}
deadline := time.Now().Add(timeout)
for ab.available < n && !ab.closed {
remaining := time.Until(deadline)
if remaining <= 0 {
return false
}
waitCondWithTimeout(ab.cond, remaining)
}
return ab.available >= n && !ab.closed
}
// Wait est un raccourci pour WaitForData(1, 0)
func (ab *AudioBuffer) Wait() {
ab.WaitForData(1, 0)
}
func (ab *AudioBuffer) Close() {
ab.mu.Lock()
defer ab.mu.Unlock()
if !ab.closed {
ab.closed = true
ab.cond.Broadcast()
}
}
// Fonction helper pour attendre une sync.Cond avec timeout
func waitCondWithTimeout(c *sync.Cond, d time.Duration) bool {
timer := time.NewTimer(d)
done := make(chan struct{})
go func() {
c.L.Lock()
defer c.L.Unlock()
c.Wait()
close(done)
}()
select {
case <-done:
if !timer.Stop() {
<-timer.C
}
return true
case <-timer.C:
return false
}
}