2022-02-22 18:30:48 +00:00
|
|
|
package audio
|
|
|
|
|
2022-07-19 12:02:41 +00:00
|
|
|
import (
|
2022-07-22 09:39:58 +00:00
|
|
|
"errors"
|
2022-10-03 09:24:03 +00:00
|
|
|
"golang.org/x/exp/slices"
|
2022-07-22 22:12:37 +00:00
|
|
|
"runtime"
|
2022-07-22 09:39:58 +00:00
|
|
|
"unsafe"
|
2022-07-19 12:02:41 +00:00
|
|
|
)
|
2022-07-19 08:36:22 +00:00
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
type SourceFormat int
|
2022-07-19 08:36:22 +00:00
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
const (
|
|
|
|
SourceFloat32 = SourceFormat(iota)
|
|
|
|
SourceInt16
|
|
|
|
SourceInt32
|
|
|
|
)
|
2022-07-20 17:02:31 +00:00
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
type AllowedSourceTypes interface {
|
|
|
|
float32 | int16 | int32
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
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
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
func Split[T AllowedSourceTypes](s TypedSource[T], n int) (sources []Source) {
|
2022-07-19 08:36:22 +00:00
|
|
|
if s.Locked() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sources = make([]Source, n)
|
|
|
|
for i := range sources {
|
2022-07-22 09:39:58 +00:00
|
|
|
sources[i] = s.New()
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
|
|
|
for _, source := range sources {
|
|
|
|
source.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for block := range s.GetBlocks() {
|
|
|
|
for _, source := range sources {
|
2022-07-22 09:39:58 +00:00
|
|
|
source.(TypedSource[T]).IngestNative(block, s.GetBitDepth())
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-22 18:58:26 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
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()
|
2022-07-19 08:36:22 +00:00
|
|
|
} else {
|
2022-07-22 09:39:58 +00:00
|
|
|
return s.ToFloat32().GetBlocks()
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
// 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)
|
2022-07-19 14:46:43 +00:00
|
|
|
return nil
|
2022-07-22 09:39:58 +00:00
|
|
|
case []int16:
|
|
|
|
s.IngestInt16(buf.([]int16), bitDepth)
|
2022-07-19 08:36:22 +00:00
|
|
|
return nil
|
2022-07-22 09:39:58 +00:00
|
|
|
case []float32:
|
|
|
|
s.IngestFloat32(buf.([]float32))
|
2022-07-19 08:36:22 +00:00
|
|
|
return nil
|
2022-07-22 09:39:58 +00:00
|
|
|
case []byte:
|
|
|
|
bufferSlice := buf.([]byte)
|
|
|
|
nsamples := len(bufferSlice) / (bitDepth / 8)
|
|
|
|
switch bitDepth {
|
|
|
|
case 32:
|
2023-04-09 11:10:30 +00:00
|
|
|
s.IngestInt32(slices.Clone(unsafe.Slice((*int32)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples)), bitDepth)
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(bufferSlice)
|
2022-07-22 09:39:58 +00:00
|
|
|
return nil
|
|
|
|
case 24:
|
2023-04-09 11:10:30 +00:00
|
|
|
s.IngestInt24(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples*3), bitDepth)
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(bufferSlice)
|
2022-07-22 09:39:58 +00:00
|
|
|
return nil
|
|
|
|
case 16:
|
2023-04-09 11:10:30 +00:00
|
|
|
s.IngestInt16(slices.Clone(unsafe.Slice((*int16)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples)), bitDepth)
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(bufferSlice)
|
2022-07-22 09:39:58 +00:00
|
|
|
return nil
|
|
|
|
case 8:
|
2023-04-09 11:10:30 +00:00
|
|
|
s.IngestInt8(unsafe.Slice((*int8)(unsafe.Pointer(unsafe.SliceData(bufferSlice))), nsamples), bitDepth)
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(bufferSlice)
|
2022-07-22 09:39:58 +00:00
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return errors.New("not supported bit depth")
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
return errors.New("not supported format")
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
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])
|
2022-07-19 08:36:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
return nil
|
2022-07-20 17:02:31 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 08:36:22 +00:00
|
|
|
type Source interface {
|
|
|
|
GetBitDepth() int
|
|
|
|
GetSampleRate() int
|
|
|
|
GetChannels() int
|
|
|
|
GetFormat() SourceFormat
|
2022-07-22 09:39:58 +00:00
|
|
|
ToFloat32() TypedSource[float32]
|
|
|
|
ToInt16() TypedSource[int16]
|
|
|
|
ToInt32(bitDepth int) TypedSource[int32]
|
2022-07-19 08:36:22 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|