Kirika/audio/source.go
DataHoarder 514a88aec1
Some checks failed
continuous-integration/drone/push Build is failing
Update to Go 1.20
2023-04-09 13:10:30 +02:00

167 lines
4.2 KiB
Go

package audio
import (
"errors"
"golang.org/x/exp/slices"
"runtime"
"unsafe"
)
type SourceFormat int
const (
SourceFloat32 = SourceFormat(iota)
SourceInt16
SourceInt32
)
type AllowedSourceTypes interface {
float32 | int16 | int32
}
type TypedSource[T AllowedSourceTypes] interface {
Source
New() TypedSource[T]
IngestNative(buf []T, bitDepth int)
GetBlocks() chan []T
// SwapBlocks swaps current Blocks with a different one, and returns old one
SwapBlocks(blocks chan []T) chan []T
}
func Split[T AllowedSourceTypes](s TypedSource[T], n int) (sources []Source) {
if s.Locked() {
return
}
sources = make([]Source, n)
for i := range sources {
sources[i] = s.New()
}
go func() {
defer func() {
for _, source := range sources {
source.Close()
}
}()
for block := range s.GetBlocks() {
for _, source := range sources {
source.(TypedSource[T]).IngestNative(block, s.GetBitDepth())
}
}
}()
return
}
func NewTypedInterface(s Source, bitDepth, sampleRate, channels int) Source {
if _, ok := s.(TypedSource[float32]); ok {
return newFloat32Source(bitDepth, sampleRate, channels)
} else if _, ok = s.(TypedSource[int16]); ok {
return newInt16Source(bitDepth, sampleRate, channels)
} else if _, ok = s.(TypedSource[int32]); ok {
return newInt32Source(bitDepth, sampleRate, channels)
} else {
return newFloat32Source(bitDepth, sampleRate, channels)
}
}
func NewInterface(s Source) Source {
if f32Source, ok := s.(TypedSource[float32]); ok {
return f32Source.New()
} else if i16Source, ok := s.(TypedSource[int16]); ok {
return i16Source.New()
} else if i32Source, ok := s.(TypedSource[int32]); ok {
return i32Source.New()
} else {
return s.ToFloat32().New()
}
}
func GetBlocksInterface(s Source) interface{} {
if f32Source, ok := s.(TypedSource[float32]); ok {
return f32Source.GetBlocks()
} else if i16Source, ok := s.(TypedSource[int16]); ok {
return i16Source.GetBlocks()
} else if i32Source, ok := s.(TypedSource[int32]); ok {
return i32Source.GetBlocks()
} else {
return s.ToFloat32().GetBlocks()
}
}
// Ingest TODO: make this using Generics when type switch is supported
func Ingest(s Source, buf interface{}, bitDepth int) error {
switch buf.(type) {
case []int32:
s.IngestInt32(buf.([]int32), bitDepth)
return nil
case []int16:
s.IngestInt16(buf.([]int16), bitDepth)
return nil
case []float32:
s.IngestFloat32(buf.([]float32))
return nil
case []byte:
bufferSlice := buf.([]byte)
nsamples := len(bufferSlice) / (bitDepth / 8)
switch bitDepth {
case 32:
s.IngestInt32(slices.Clone(unsafe.Slice((*int32)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples)), bitDepth)
runtime.KeepAlive(bufferSlice)
return nil
case 24:
s.IngestInt24(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples*3), bitDepth)
runtime.KeepAlive(bufferSlice)
return nil
case 16:
s.IngestInt16(slices.Clone(unsafe.Slice((*int16)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples)), bitDepth)
runtime.KeepAlive(bufferSlice)
return nil
case 8:
s.IngestInt8(unsafe.Slice((*int8)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples), bitDepth)
runtime.KeepAlive(bufferSlice)
return nil
default:
return errors.New("not supported bit depth")
}
}
return errors.New("not supported format")
}
func NewSource[T AllowedSourceTypes](bitDepth, sampleRate, channels int) TypedSource[T] {
switch interface{}(T(0)).(type) {
case float32:
return newFloat32Source(bitDepth, sampleRate, channels).(TypedSource[T])
case int16:
return newInt16Source(bitDepth, sampleRate, channels).(TypedSource[T])
case int32:
return newInt32Source(bitDepth, sampleRate, channels).(TypedSource[T])
}
return nil
}
type Source interface {
GetBitDepth() int
GetSampleRate() int
GetChannels() int
GetFormat() SourceFormat
ToFloat32() TypedSource[float32]
ToInt16() TypedSource[int16]
ToInt32(bitDepth int) TypedSource[int32]
IngestFloat32(buf []float32)
IngestInt8(buf []int8, bitDepth int)
IngestInt16(buf []int16, bitDepth int)
IngestInt24(buf []byte, bitDepth int)
IngestInt32(buf []int32, bitDepth int)
//Split a Source into multiple ones. After calling this function, the source is locked.
Split(n int) []Source
Close()
Unlock()
Locked() bool
}