140 lines
2.4 KiB
Go
140 lines
2.4 KiB
Go
|
|
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
|
||
|
|
}
|
||
|
|
}
|