Files
aego/audio/mix/queue.go
T

196 lines
3.0 KiB
Go

package mix
import "sync/atomic"
type CommandKind uint8
const (
CmdPlay CommandKind = iota + 1
CmdStop
CmdSetGain
CmdSetPan
CmdSetPitch
CmdPause
CmdResume
CmdBusGain
CmdBusMute
CmdBusSolo
CmdPauseAll
CmdResumeAll
CmdBusLayer
CmdKill
)
type Command struct {
Kind CommandKind
Voice VoiceID
Bus int32
Layer uint8
Memory *Memory
Stream *Stream
Gain float32
Pan float32
Pitch float32
Fade float32
Priority int32
Flag bool
}
type EventKind uint8
const (
EvFinished EventKind = iota + 1
EvStolen
EvRejected
EvStarved
)
type Event struct {
Kind EventKind
Voice VoiceID
}
type spsc[T any] struct {
buf []T
mask uint64
head atomic.Uint64
_ [56]byte
tail atomic.Uint64
}
func newSPSC[T any](size int) *spsc[T] {
n := 1
for n < size {
n <<= 1
}
return &spsc[T]{buf: make([]T, n), mask: uint64(n - 1)}
}
func (q *spsc[T]) push(v T) bool {
t := q.tail.Load()
if t-q.head.Load() == uint64(len(q.buf)) {
return false
}
q.buf[t&q.mask] = v
q.tail.Store(t + 1)
return true
}
func (q *spsc[T]) pop() (T, bool) {
var zero T
h := q.head.Load()
if h == q.tail.Load() {
return zero, false
}
v := q.buf[h&q.mask]
q.buf[h&q.mask] = zero
q.head.Store(h + 1)
return v, true
}
func (q *spsc[T]) len() int {
return int(q.tail.Load() - q.head.Load())
}
type CommandQueue struct {
q *spsc[Command]
dropped atomic.Uint64
}
func (c *CommandQueue) Push(cmd Command) bool {
if c.q.push(cmd) {
return true
}
c.dropped.Add(1)
return false
}
func (c *CommandQueue) Dropped() uint64 {
return c.dropped.Load()
}
func (c *CommandQueue) Len() int {
return c.q.len()
}
type EventQueue struct {
q *spsc[Event]
dropped atomic.Uint64
}
func (e *EventQueue) Pop() (Event, bool) {
return e.q.pop()
}
func (e *EventQueue) Dropped() uint64 {
return e.dropped.Load()
}
func (e *EventQueue) push(ev Event) {
if !e.q.push(ev) {
e.dropped.Add(1)
}
}
type Ring struct {
buf []float32
mask uint64
ch int
head atomic.Uint64
_ [56]byte
tail atomic.Uint64
}
func NewRing(frames, channels int) *Ring {
if channels != 2 {
channels = 1
}
n := 2
for n < frames*channels {
n <<= 1
}
return &Ring{buf: make([]float32, n), mask: uint64(n - 1), ch: channels}
}
func (r *Ring) Channels() int {
return r.ch
}
func (r *Ring) Cap() int {
return len(r.buf) / r.ch
}
func (r *Ring) Len() int {
return int(r.tail.Load()-r.head.Load()) / r.ch
}
func (r *Ring) Free() int {
return r.Cap() - r.Len()
}
func (r *Ring) Write(p []float32) int {
t := r.tail.Load()
n := min(len(p), len(r.buf)-int(t-r.head.Load()))
n -= n % r.ch
if n <= 0 {
return 0
}
k := copy(r.buf[int(t&r.mask):], p[:n])
copy(r.buf, p[k:n])
r.tail.Store(t + uint64(n))
return n / r.ch
}
func (r *Ring) Read(p []float32) int {
h := r.head.Load()
n := min(len(p), int(r.tail.Load()-h))
n -= n % r.ch
if n <= 0 {
return 0
}
k := copy(p[:n], r.buf[int(h&r.mask):])
copy(p[k:n], r.buf)
r.head.Store(h + uint64(n))
return n / r.ch
}