Use generics to implement TypedSource[float32|int16|int32]
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
3d70bcc15c
commit
09f3cf3b56
|
@ -16,12 +16,12 @@ Collection of audio utilities for decoding/encoding/processing files and streams
|
||||||
| Codec | Containers | Decoder | Decoder Sample Format | Encoder | Notes |
|
| Codec | Containers | Decoder | Decoder Sample Format | Encoder | Notes |
|
||||||
|:----------:|:----------------------------------------------------------------------------------------:|:-------:|:---------------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:----------:|:----------------------------------------------------------------------------------------:|:-------:|:---------------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| **FLAC** | [FLAC](https://xiph.org/flac/format.html), [Ogg](https://xiph.org/flac/ogg_mapping.html) | ✅ | `int32` | ✅ | Adjustable encoding compression level and block size.<br/>Decoding/encoding by [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac).<br/>If [goflac](https://git.gammaspectra.live/S.O.N.G/goflac) codec is disabled, [mewkiz/flac](https://github.com/mewkiz/flac) decoder will be used. |
|
| **FLAC** | [FLAC](https://xiph.org/flac/format.html), [Ogg](https://xiph.org/flac/ogg_mapping.html) | ✅ | `int32` | ✅ | Adjustable encoding compression level and block size.<br/>Decoding/encoding by [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac).<br/>If [goflac](https://git.gammaspectra.live/S.O.N.G/goflac) codec is disabled, [mewkiz/flac](https://github.com/mewkiz/flac) decoder will be used. |
|
||||||
| **TTA** | [TTA](https://www.tausoft.org/en/true_audio_codec_format/) | ✅ | `int16, int32` | ✅ | Decoding/encoding via [S.O.N.G/go-tta](https://git.gammaspectra.live/S.O.N.G/go-tta). |
|
| **TTA** | [TTA](https://www.tausoft.org/en/true_audio_codec_format/) | ✅ | `int32` | ✅ | Decoding/encoding via [S.O.N.G/go-tta](https://git.gammaspectra.live/S.O.N.G/go-tta). |
|
||||||
| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | `2ch. int16` | ✅ | Adjustable encoding bitrate and mode.<br/>Decoding via [minimp3](https://github.com/kvark128/minimp3), encoding by [LAME](https://lame.sourceforge.io/) via [go-lame](https://github.com/viert/go-lame). |
|
| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | `2ch. int16` | ✅ | Adjustable encoding bitrate and mode.<br/>Decoding via [minimp3](https://github.com/kvark128/minimp3), encoding by [LAME](https://lame.sourceforge.io/) via [go-lame](https://github.com/viert/go-lame). |
|
||||||
| **Opus** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `int16` | ✅ | Adjustable encoding bitrate.<br/>Decoding/encoding by [libopus](https://github.com/xiph/opus) via [go-pus](https://git.gammaspectra.live/S.O.N.G/go-pus). Linked Ogg streams of different channel count are not supported. |
|
| **Opus** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `int16` | ✅ | Adjustable encoding bitrate.<br/>Decoding/encoding by [libopus](https://github.com/xiph/opus) via [go-pus](https://git.gammaspectra.live/S.O.N.G/go-pus). Linked Ogg streams of different channel count are not supported. |
|
||||||
| **Vorbis** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `float32` | ❌ | Decoding by [jfreymuth/vorbis](https://github.com/jfreymuth/vorbis) via [jfreymuth/oggvorbis](https://github.com/jfreymuth/oggvorbis). |
|
| **Vorbis** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `float32` | ❌ | Decoding by [jfreymuth/vorbis](https://github.com/jfreymuth/vorbis) via [jfreymuth/oggvorbis](https://github.com/jfreymuth/oggvorbis). |
|
||||||
| **AAC** | [ADTS](https://wiki.multimedia.cx/index.php/ADTS), ADIF*, MP4** | ✅ | `int16` | ✅ | Adjustable encoding bitrate and mode (LC, HEv1, HEv2).<br/>Decoding/encoding by [FDK-AAC](https://github.com/mstorsjo/fdk-aac) via [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac).<br/>If [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac) codec is disabled, [VisualOn AAC encoder](https://github.com/gen2brain/aac-go) will be used for limited encoding support.<br/>*ADIF only supported on encoding.<br/>**MP4 only supported on encoding, and fragmented MP4 currently. |
|
| **AAC** | [ADTS](https://wiki.multimedia.cx/index.php/ADTS), ADIF*, MP4** | ✅ | `int16` | ✅ | Adjustable encoding bitrate and mode (LC, HEv1, HEv2).<br/>Decoding/encoding by [FDK-AAC](https://github.com/mstorsjo/fdk-aac) via [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac).<br/>If [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac) codec is disabled, [VisualOn AAC encoder](https://github.com/gen2brain/aac-go) will be used for limited encoding support.<br/>*ADIF only supported on encoding.<br/>**MP4 only supported on encoding, and fragmented MP4 currently. |
|
||||||
| **ALAC** | MP4* | ✅ | `int16, int32` | ✅ | Decoding/encoding by [libalac](https://git.gammaspectra.live/S.O.N.G/alac) via [go-alac](https://git.gammaspectra.live/S.O.N.G/go-alac).<br/>Disabled by default.<br/>*MP4 encoding only supported on fragmented MP4 currently. |
|
| **ALAC** | MP4* | ✅ | `int32` | ✅ | Decoding/encoding by [libalac](https://git.gammaspectra.live/S.O.N.G/alac) via [go-alac](https://git.gammaspectra.live/S.O.N.G/go-alac).<br/>Disabled by default.<br/>*MP4 encoding only supported on fragmented MP4 currently. |
|
||||||
|
|
||||||
## Container packetizers supported
|
## Container packetizers supported
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ Some tests cannot be completed in this mode.
|
||||||
| Codec | Containers | Decoder | Decoder Sample Format | Encoder | Notes |
|
| Codec | Containers | Decoder | Decoder Sample Format | Encoder | Notes |
|
||||||
|:----------:|:------------------------------------------------------------:|:-------:|:---------------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------|
|
|:----------:|:------------------------------------------------------------:|:-------:|:---------------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| **FLAC** | [FLAC](https://xiph.org/flac/format.html) | ✅ | `int32` | ❌ | Decoding by [mewkiz/flac](https://github.com/mewkiz/flac). |
|
| **FLAC** | [FLAC](https://xiph.org/flac/format.html) | ✅ | `int32` | ❌ | Decoding by [mewkiz/flac](https://github.com/mewkiz/flac). |
|
||||||
| **TTA** | [TTA](https://www.tausoft.org/en/true_audio_codec_format/) | ✅ | `int16, int32` | ✅ | Decoding/encoding via [S.O.N.G/go-tta](https://git.gammaspectra.live/S.O.N.G/go-tta). |
|
| **TTA** | [TTA](https://www.tausoft.org/en/true_audio_codec_format/) | ✅ | `int32` | ✅ | Decoding/encoding via [S.O.N.G/go-tta](https://git.gammaspectra.live/S.O.N.G/go-tta). |
|
||||||
| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | `2ch. int16` | ❌ | Decoding via [hajimehoshi/go-mp3](https://github.com/hajimehoshi/go-mp3). |
|
| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | `2ch. int16` | ❌ | Decoding via [hajimehoshi/go-mp3](https://github.com/hajimehoshi/go-mp3). |
|
||||||
| **Opus** | - | ❌ | - | ❌ | |
|
| **Opus** | - | ❌ | - | ❌ | |
|
||||||
| **Vorbis** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `float32` | ❌ | Decoding by [jfreymuth/vorbis](https://github.com/jfreymuth/vorbis) via [jfreymuth/oggvorbis](https://github.com/jfreymuth/oggvorbis). |
|
| **Vorbis** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | `float32` | ❌ | Decoding by [jfreymuth/vorbis](https://github.com/jfreymuth/vorbis) via [jfreymuth/oggvorbis](https://github.com/jfreymuth/oggvorbis). |
|
||||||
|
|
|
@ -29,9 +29,9 @@ func (f SourceFormatFilter) Process(source audio.Source) audio.Source {
|
||||||
case audio.SourceInt16:
|
case audio.SourceInt16:
|
||||||
return source.ToInt16()
|
return source.ToInt16()
|
||||||
case audio.SourceInt32:
|
case audio.SourceInt32:
|
||||||
if int16Source, ok := source.(*audio.Int16Source); ok {
|
if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||||
return source.ToInt32(int16Source.GetBitDepth())
|
return source.ToInt32(int16Source.GetBitDepth())
|
||||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||||
return int32Source
|
return int32Source
|
||||||
} else {
|
} else {
|
||||||
return source.ToInt32(32)
|
return source.ToInt32(32)
|
||||||
|
@ -53,7 +53,7 @@ func NewBufferFilter(blockBufferSize int) BufferFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f BufferFilter) Process(source audio.Source) audio.Source {
|
func (f BufferFilter) Process(source audio.Source) audio.Source {
|
||||||
if floatSource, ok := source.(*audio.Float32Source); ok {
|
if floatSource, ok := source.(audio.TypedSource[float32]); ok {
|
||||||
outBlocks := make(chan []float32, f.blockBufferSize)
|
outBlocks := make(chan []float32, f.blockBufferSize)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(outBlocks)
|
defer close(outBlocks)
|
||||||
|
@ -62,12 +62,10 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &audio.Float32Source{
|
newSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
Channels: source.GetChannels(),
|
close(newSource.SwapBlocks(outBlocks))
|
||||||
SampleRate: source.GetSampleRate(),
|
return newSource
|
||||||
Blocks: outBlocks,
|
} else if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||||
}
|
|
||||||
} else if int16Source, ok := source.(*audio.Int16Source); ok {
|
|
||||||
outBlocks := make(chan []int16, f.blockBufferSize)
|
outBlocks := make(chan []int16, f.blockBufferSize)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(outBlocks)
|
defer close(outBlocks)
|
||||||
|
@ -76,13 +74,10 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &audio.Int16Source{
|
newSource := audio.NewSource[int16](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
BitDepth: source.GetBitDepth(),
|
close(newSource.SwapBlocks(outBlocks))
|
||||||
Channels: source.GetChannels(),
|
return newSource
|
||||||
SampleRate: source.GetSampleRate(),
|
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||||
Blocks: outBlocks,
|
|
||||||
}
|
|
||||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
|
||||||
outBlocks := make(chan []int32, f.blockBufferSize)
|
outBlocks := make(chan []int32, f.blockBufferSize)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(outBlocks)
|
defer close(outBlocks)
|
||||||
|
@ -91,12 +86,9 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &audio.Int32Source{
|
newSource := audio.NewSource[int32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
BitDepth: source.GetBitDepth(),
|
close(newSource.SwapBlocks(outBlocks))
|
||||||
Channels: source.GetChannels(),
|
return newSource
|
||||||
SampleRate: source.GetSampleRate(),
|
|
||||||
Blocks: outBlocks,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return source
|
return source
|
||||||
|
@ -119,8 +111,8 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
||||||
blockSize := (source.GetSampleRate() / f.blocksPerSecond) * source.GetChannels()
|
blockSize := (source.GetSampleRate() / f.blocksPerSecond) * source.GetChannels()
|
||||||
throttler := time.Tick(time.Second / time.Duration(f.blocksPerSecond))
|
throttler := time.Tick(time.Second / time.Duration(f.blocksPerSecond))
|
||||||
|
|
||||||
if floatSource, ok := source.(*audio.Float32Source); ok {
|
if floatSource, ok := source.(audio.TypedSource[float32]); ok {
|
||||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
var buf []float32
|
var buf []float32
|
||||||
|
@ -137,12 +129,12 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return outSource
|
return outSource
|
||||||
} else if int16Source, ok := source.(*audio.Int16Source); ok {
|
} else if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||||
bitDepth := source.GetBitDepth()
|
bitDepth := source.GetBitDepth()
|
||||||
if bitDepth > 16 {
|
if bitDepth > 16 {
|
||||||
bitDepth = 16
|
bitDepth = 16
|
||||||
}
|
}
|
||||||
outSource := audio.NewInt16Source(bitDepth, source.GetSampleRate(), source.GetChannels())
|
outSource := audio.NewSource[int16](bitDepth, source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
var buf []int16
|
var buf []int16
|
||||||
|
@ -159,8 +151,8 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return outSource
|
return outSource
|
||||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||||
outSource := audio.NewInt32Source(source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
outSource := audio.NewSource[int32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
var buf []int32
|
var buf []int32
|
||||||
|
@ -197,7 +189,7 @@ func (f VolumeFilter) Process(source audio.Source) audio.Source {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
|
||||||
|
@ -223,7 +215,7 @@ func (f StereoFilter) Process(source audio.Source) audio.Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make this for int16 and int32
|
//TODO: make this for int16 and int32
|
||||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), 2)
|
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 2)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
@ -244,7 +236,7 @@ func (f MonoFilter) Process(source audio.Source) audio.Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make this for int16 and int32
|
//TODO: make this for int16 and int32
|
||||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), 1)
|
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (f ResampleFilter) Process(source audio.Source) audio.Source {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
outSource := audio.NewFloat32Source(f.sampleRate, source.GetChannels())
|
outSource := audio.NewSource[float32](source.GetBitDepth(), f.sampleRate, source.GetChannels())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (f ResampleFilter) Process(source audio.Source) audio.Source {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
outSource := audio.NewFloat32Source(f.sampleRate, source.GetChannels())
|
outSource := audio.NewSource[float32](source.GetBitDepth(), f.sampleRate, source.GetChannels())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
|
|
@ -135,7 +135,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt16Source(16, decoder.SampleRate(), decoder.NumChannels())
|
source := audio.NewSource[int16](16, decoder.SampleRate(), decoder.NumChannels())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
@ -181,7 +181,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt16Source(16, decoder.SampleRate(), decoder.NumChannels())
|
source := audio.NewSource[int16](16, decoder.SampleRate(), decoder.NumChannels())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, errors.New("could not decode")
|
return nil, errors.New("could not decode")
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt32Source(decoder.GetBitDepth(), decoder.GetSampleRate(), decoder.GetChannels())
|
source := audio.NewSource[int32](decoder.GetBitDepth(), decoder.GetSampleRate(), decoder.GetChannels())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -37,7 +37,7 @@ func NewAnalyzerChannel(source audio.Source, err error) (audio.Source, AnalyzerC
|
||||||
go func() {
|
go func() {
|
||||||
defer close(channel)
|
defer close(channel)
|
||||||
|
|
||||||
if _, ok := sources[1].(*audio.Float32Source); ok {
|
if _, ok := sources[1].(audio.TypedSource[float32]); ok {
|
||||||
for block := range sources[1].ToInt32(32).GetBlocks() {
|
for block := range sources[1].ToInt32(32).GetBlocks() {
|
||||||
channel <- &AnalyzerPacket{
|
channel <- &AnalyzerPacket{
|
||||||
Samples: block,
|
Samples: block,
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt32Source(int(decoder.Info.BitsPerSample), int(decoder.Info.SampleRate), int(decoder.Info.NChannels))
|
source := audio.NewSource[int32](int(decoder.Info.BitsPerSample), int(decoder.Info.SampleRate), int(decoder.Info.NChannels))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt32Source(decoder.Depth, decoder.Rate, decoder.Channels)
|
source := audio.NewSource[int32](decoder.Depth, decoder.Rate, decoder.Channels)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt16Source(16, decoder.SampleRate(), 2)
|
source := audio.NewSource[int16](16, decoder.SampleRate(), 2)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewInt16Source(16, decoder.SampleRate(), 2)
|
source := audio.NewSource[int16](16, decoder.SampleRate(), 2)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
channelCount := info.ChannelCount
|
channelCount := info.ChannelCount
|
||||||
source := audio.NewInt16Source(16, FixedSampleRate, channelCount)
|
source := audio.NewSource[int16](16, FixedSampleRate, channelCount)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
|
|
|
@ -80,20 +80,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var source audio.Source
|
source := audio.NewSource[int32](int(info.Bps), int(info.Sps), int(info.Nch))
|
||||||
switch info.Bps {
|
|
||||||
case 8:
|
|
||||||
//TODO
|
|
||||||
source = audio.NewInt16Source(8, int(info.Sps), int(info.Nch))
|
|
||||||
case 16:
|
|
||||||
source = audio.NewInt16Source(16, int(info.Sps), int(info.Nch))
|
|
||||||
case 24:
|
|
||||||
//TODO: Use Int24
|
|
||||||
source = audio.NewInt32Source(24, int(info.Sps), int(info.Nch))
|
|
||||||
case 32:
|
|
||||||
source = audio.NewInt32Source(32, int(info.Sps), int(info.Nch))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
@ -107,26 +94,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferSlice := buffer[:writeLen]
|
source.IngestInt32(vector.BytesToInt32(buffer[:writeLen], int(info.Bps)), int(info.Bps))
|
||||||
|
|
||||||
bitDepth := int(info.Bps)
|
|
||||||
|
|
||||||
nsamples := len(bufferSlice) / (bitDepth / 8)
|
|
||||||
|
|
||||||
switch bitDepth {
|
|
||||||
case 32:
|
|
||||||
out := make([]int32, nsamples)
|
|
||||||
copy(out, unsafe.Slice((*int32)(unsafe.Pointer(&bufferSlice[0])), nsamples))
|
|
||||||
source.IngestInt32(out, bitDepth)
|
|
||||||
case 24:
|
|
||||||
source.IngestInt24(unsafe.Slice((*byte)(unsafe.Pointer(&bufferSlice[0])), nsamples*3), bitDepth)
|
|
||||||
case 16:
|
|
||||||
out := make([]int16, nsamples)
|
|
||||||
copy(out, unsafe.Slice((*int16)(unsafe.Pointer(&bufferSlice[0])), nsamples))
|
|
||||||
source.IngestInt16(out, bitDepth)
|
|
||||||
case 8:
|
|
||||||
source.IngestInt8(unsafe.Slice((*int8)(unsafe.Pointer(&bufferSlice[0])), nsamples), bitDepth)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
source := audio.NewFloat32Source(reader.Channels(), reader.SampleRate())
|
source := audio.NewSource[float32](16, reader.Channels(), reader.SampleRate())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
|
@ -22,9 +22,7 @@ type QueueEntry struct {
|
||||||
|
|
||||||
type Queue struct {
|
type Queue struct {
|
||||||
queue []*QueueEntry
|
queue []*QueueEntry
|
||||||
float32Output *audio.Float32Source
|
output audio.Source
|
||||||
int16Output *audio.Int16Source
|
|
||||||
int32Output *audio.Int32Source
|
|
||||||
interrupt chan bool
|
interrupt chan bool
|
||||||
interruptDepth int64
|
interruptDepth int64
|
||||||
closed bool
|
closed bool
|
||||||
|
@ -44,252 +42,124 @@ func NewQueue(format audio.SourceFormat, bitDepth, sampleRate, channels int) *Qu
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
case audio.SourceFloat32:
|
case audio.SourceFloat32:
|
||||||
q.float32Output = audio.NewFloat32Source(sampleRate, channels)
|
q.output = audio.NewSource[float32](bitDepth, sampleRate, channels)
|
||||||
|
queueLoopStart[float32](q)
|
||||||
case audio.SourceInt16:
|
case audio.SourceInt16:
|
||||||
q.int16Output = audio.NewInt16Source(bitDepth, sampleRate, channels)
|
q.output = audio.NewSource[int16](bitDepth, sampleRate, channels)
|
||||||
|
queueLoopStart[int16](q)
|
||||||
case audio.SourceInt32:
|
case audio.SourceInt32:
|
||||||
q.int32Output = audio.NewInt32Source(bitDepth, sampleRate, channels)
|
q.output = audio.NewSource[int32](bitDepth, sampleRate, channels)
|
||||||
|
queueLoopStart[int32](q)
|
||||||
default:
|
default:
|
||||||
log.Panicf("not found source format %d", int(format))
|
log.Panicf("not found source format %d", int(format))
|
||||||
}
|
}
|
||||||
|
|
||||||
q.start()
|
|
||||||
return q
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) spliceSources(input audio.Source) (output audio.Source, cancel chan bool) {
|
func spliceHelper[T audio.AllowedSourceTypes](input audio.TypedSource[T]) (output audio.TypedSource[T], cancel chan bool) {
|
||||||
cancel = make(chan bool, 1)
|
cancel = make(chan bool, 1)
|
||||||
|
output = audio.NewSource[T](input.GetBitDepth(), input.GetSampleRate(), input.GetChannels())
|
||||||
|
|
||||||
switch q.GetSource().GetFormat() {
|
bitDepth := input.GetBitDepth()
|
||||||
case audio.SourceFloat32:
|
|
||||||
output = audio.NewFloat32Source(input.GetSampleRate(), input.GetChannels())
|
|
||||||
|
|
||||||
sourceChannel := input.ToFloat32().GetBlocks()
|
sourceChannel := input.GetBlocks()
|
||||||
go func() {
|
go func() {
|
||||||
defer output.Close()
|
defer output.Close()
|
||||||
L1:
|
L1:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-cancel:
|
case <-cancel:
|
||||||
|
break L1
|
||||||
|
case block, more := <-sourceChannel:
|
||||||
|
if !more {
|
||||||
|
//no more blocks!
|
||||||
break L1
|
break L1
|
||||||
case block, more := <-sourceChannel:
|
} else {
|
||||||
if !more {
|
output.IngestNative(block, bitDepth)
|
||||||
//no more blocks!
|
|
||||||
break L1
|
|
||||||
} else {
|
|
||||||
output.IngestFloat32(block)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.Unlock()
|
|
||||||
|
|
||||||
//sink remaining
|
|
||||||
go audio.NewNullSink().Process(input)
|
|
||||||
}()
|
|
||||||
case audio.SourceInt16:
|
|
||||||
bitDepth := input.GetBitDepth()
|
|
||||||
if bitDepth > 16 {
|
|
||||||
bitDepth = 16
|
|
||||||
}
|
}
|
||||||
output = audio.NewInt16Source(bitDepth, input.GetSampleRate(), input.GetChannels())
|
|
||||||
|
|
||||||
sourceChannel := input.ToInt16().GetBlocks()
|
input.Unlock()
|
||||||
go func() {
|
|
||||||
defer output.Close()
|
|
||||||
L2:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-cancel:
|
|
||||||
break L2
|
|
||||||
case block, more := <-sourceChannel:
|
|
||||||
if !more {
|
|
||||||
//no more blocks!
|
|
||||||
break L2
|
|
||||||
} else {
|
|
||||||
output.IngestInt16(block, bitDepth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.Unlock()
|
//sink remaining
|
||||||
|
go audio.NewNullSink().Process(input)
|
||||||
//sink remaining
|
}()
|
||||||
go audio.NewNullSink().Process(input)
|
|
||||||
}()
|
|
||||||
case audio.SourceInt32:
|
|
||||||
output = audio.NewInt32Source(input.GetBitDepth(), input.GetSampleRate(), input.GetChannels())
|
|
||||||
|
|
||||||
sourceChannel := input.ToInt32(input.GetBitDepth()).GetBlocks()
|
|
||||||
go func() {
|
|
||||||
defer output.Close()
|
|
||||||
L3:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-cancel:
|
|
||||||
break L3
|
|
||||||
case block, more := <-sourceChannel:
|
|
||||||
if !more {
|
|
||||||
//no more blocks!
|
|
||||||
break L3
|
|
||||||
} else {
|
|
||||||
output.IngestInt32(block, output.GetBitDepth())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.Unlock()
|
|
||||||
|
|
||||||
//sink remaining
|
|
||||||
go audio.NewNullSink().Process(input)
|
|
||||||
}()
|
|
||||||
default:
|
|
||||||
log.Panicf("not found source format %d", int(input.GetFormat()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) start() {
|
func (q *Queue) spliceSources(input audio.Source) (audio.Source, chan bool) {
|
||||||
|
|
||||||
|
switch q.GetSource().GetFormat() {
|
||||||
|
case audio.SourceFloat32:
|
||||||
|
return spliceHelper(input.ToFloat32())
|
||||||
|
case audio.SourceInt16:
|
||||||
|
return spliceHelper(input.ToInt16())
|
||||||
|
case audio.SourceInt32:
|
||||||
|
return spliceHelper(input.ToInt32(input.GetBitDepth()))
|
||||||
|
default:
|
||||||
|
log.Panicf("not found source format %d", int(input.GetFormat()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queueLoopStart[T audio.AllowedSourceTypes](q *Queue) {
|
||||||
q.wg.Add(1)
|
q.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer q.wg.Done()
|
defer q.wg.Done()
|
||||||
var current *QueueEntry
|
var current *QueueEntry
|
||||||
|
var currentBlocks chan []T
|
||||||
if q.float32Output != nil {
|
L1:
|
||||||
L1:
|
for {
|
||||||
for {
|
q.lock.RLock()
|
||||||
q.lock.RLock()
|
if q.closed {
|
||||||
if q.closed {
|
q.output.Close()
|
||||||
q.float32Output.Close()
|
break L1
|
||||||
break L1
|
}
|
||||||
}
|
if len(q.queue) == 0 { //no more entries, wait for interrupt
|
||||||
if len(q.queue) == 0 { //no more entries, wait for interrupt
|
|
||||||
q.lock.RUnlock()
|
|
||||||
<-q.interrupt
|
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current = q.queue[0]
|
|
||||||
q.lock.RUnlock()
|
q.lock.RUnlock()
|
||||||
|
<-q.interrupt
|
||||||
|
atomic.AddInt64(&q.interruptDepth, -1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current == nil || current.Identifier != q.queue[0].Identifier {
|
||||||
|
current = q.queue[0]
|
||||||
|
currentBlocks = current.Source.(audio.TypedSource[T]).GetBlocks()
|
||||||
|
}
|
||||||
|
q.lock.RUnlock()
|
||||||
|
|
||||||
F1:
|
F1:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-q.interrupt:
|
case <-q.interrupt:
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
atomic.AddInt64(&q.interruptDepth, -1)
|
||||||
//force recheck
|
//force recheck
|
||||||
|
break F1
|
||||||
|
case block, more := <-currentBlocks:
|
||||||
|
if !more {
|
||||||
|
//no more blocks! skip
|
||||||
|
if current.EndCallback != nil {
|
||||||
|
current.EndCallback(q, current)
|
||||||
|
}
|
||||||
|
q.Remove(current.Identifier)
|
||||||
break F1
|
break F1
|
||||||
case block, more := <-current.Source.(*audio.Float32Source).Blocks:
|
} else {
|
||||||
if !more {
|
if current.StartCallback != nil && current.ReadSamples == 0 && len(block) > 0 {
|
||||||
//no more blocks! skip
|
current.StartCallback(q, current)
|
||||||
if current.EndCallback != nil {
|
|
||||||
current.EndCallback(q, current)
|
|
||||||
}
|
|
||||||
q.Remove(current.Identifier)
|
|
||||||
break F1
|
|
||||||
} else {
|
|
||||||
if current.StartCallback != nil && current.ReadSamples == 0 && len(block) > 0 {
|
|
||||||
current.StartCallback(q, current)
|
|
||||||
}
|
|
||||||
current.ReadSamples += len(block) / current.Source.GetChannels()
|
|
||||||
q.float32Output.IngestFloat32(block)
|
|
||||||
}
|
}
|
||||||
|
current.ReadSamples += len(block) / current.Source.GetChannels()
|
||||||
|
q.output.(audio.TypedSource[T]).IngestNative(block, current.Source.GetBitDepth())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if q.int16Output != nil {
|
|
||||||
L2:
|
|
||||||
for {
|
|
||||||
q.lock.RLock()
|
|
||||||
if q.closed {
|
|
||||||
q.int16Output.Close()
|
|
||||||
break L2
|
|
||||||
}
|
|
||||||
if len(q.queue) == 0 { //no more entries, wait for interrupt
|
|
||||||
q.lock.RUnlock()
|
|
||||||
<-q.interrupt
|
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current = q.queue[0]
|
|
||||||
q.lock.RUnlock()
|
|
||||||
|
|
||||||
F2:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-q.interrupt:
|
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
|
||||||
//force recheck
|
|
||||||
break F2
|
|
||||||
case block, more := <-current.Source.(*audio.Int16Source).Blocks:
|
|
||||||
if !more {
|
|
||||||
//no more blocks! skip
|
|
||||||
if current.EndCallback != nil {
|
|
||||||
current.EndCallback(q, current)
|
|
||||||
}
|
|
||||||
q.Remove(current.Identifier)
|
|
||||||
break F2
|
|
||||||
} else {
|
|
||||||
if current.StartCallback != nil && current.ReadSamples == 0 && len(block) > 0 {
|
|
||||||
current.StartCallback(q, current)
|
|
||||||
}
|
|
||||||
current.ReadSamples += len(block) / current.Source.GetChannels()
|
|
||||||
q.int16Output.IngestInt16(block, current.Source.GetBitDepth())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if q.int32Output != nil {
|
|
||||||
L3:
|
|
||||||
for {
|
|
||||||
q.lock.RLock()
|
|
||||||
if q.closed {
|
|
||||||
q.int32Output.Close()
|
|
||||||
break L3
|
|
||||||
}
|
|
||||||
if len(q.queue) == 0 { //no more entries, wait for interrupt
|
|
||||||
q.lock.RUnlock()
|
|
||||||
<-q.interrupt
|
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current = q.queue[0]
|
|
||||||
q.lock.RUnlock()
|
|
||||||
|
|
||||||
F3:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-q.interrupt:
|
|
||||||
atomic.AddInt64(&q.interruptDepth, -1)
|
|
||||||
//force recheck
|
|
||||||
break F3
|
|
||||||
case block, more := <-current.Source.(*audio.Int32Source).Blocks:
|
|
||||||
if !more {
|
|
||||||
//no more blocks! skip
|
|
||||||
if current.EndCallback != nil {
|
|
||||||
current.EndCallback(q, current)
|
|
||||||
}
|
|
||||||
q.Remove(current.Identifier)
|
|
||||||
break F3
|
|
||||||
} else {
|
|
||||||
if current.StartCallback != nil && current.ReadSamples == 0 && len(block) > 0 {
|
|
||||||
current.StartCallback(q, current)
|
|
||||||
}
|
|
||||||
current.ReadSamples += len(block) / current.Source.GetChannels()
|
|
||||||
q.int32Output.IngestInt32(block, current.Source.GetBitDepth())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) getFilterChain(source audio.Source) audio.Source {
|
func (q *Queue) getFilterChain(source audio.Source) audio.Source {
|
||||||
if q.GetChannels() == 1 {
|
if q.GetChannels() == 1 {
|
||||||
return filter.NewFilterChain(source, filter.MonoFilter{}, filter.NewResampleFilter(q.GetSampleRate(), filter.QualityFastest, 0), filter.SourceFormatFilter{
|
return filter.NewFilterChain(source, filter.MonoFilter{}, filter.NewResampleFilter(q.GetSampleRate(), filter.QualityFastest, 0), filter.SourceFormatFilter{
|
||||||
|
@ -441,14 +311,7 @@ func (q *Queue) GetQueue() (entries []*QueueEntry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) GetSource() audio.Source {
|
func (q *Queue) GetSource() audio.Source {
|
||||||
if q.float32Output != nil {
|
return q.output
|
||||||
return q.float32Output
|
|
||||||
} else if q.int16Output != nil {
|
|
||||||
return q.int16Output
|
|
||||||
} else if q.int32Output != nil {
|
|
||||||
return q.int32Output
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) GetSampleRate() int {
|
func (q *Queue) GetSampleRate() int {
|
||||||
|
|
|
@ -21,7 +21,7 @@ func NewNormalizationFilter(delayInSeconds int) NormalizationFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f NormalizationFilter) Process(source audio.Source) audio.Source {
|
func (f NormalizationFilter) Process(source audio.Source) audio.Source {
|
||||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer outSource.Close()
|
defer outSource.Close()
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,24 @@ func GetTrackReplayGain(source audio.Source) (gain, peak float64, err error) {
|
||||||
}
|
}
|
||||||
defer state.Close()
|
defer state.Close()
|
||||||
|
|
||||||
if _, ok := source.(*audio.Int16Source); ok || source.GetBitDepth() == 16 {
|
if _, ok := source.(audio.TypedSource[int16]); ok {
|
||||||
for block := range source.ToInt16().GetBlocks() {
|
for block := range source.ToInt16().GetBlocks() {
|
||||||
if err = state.AddShort(block); err != nil {
|
if err = state.AddShort(block); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||||
for block := range int32Source.ToInt32(32).GetBlocks() {
|
if source.GetBitDepth() == 16 {
|
||||||
if err = state.AddInt(block); err != nil {
|
for block := range source.ToInt16().GetBlocks() {
|
||||||
return
|
if err = state.AddShort(block); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for block := range int32Source.ToInt32(32).GetBlocks() {
|
||||||
|
if err = state.AddInt(block); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,16 +93,24 @@ func GetAlbumReplayGain(sources []audio.Source) (albumGain, albumPeak float64, t
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if _, ok := source.(*audio.Int16Source); ok || source.GetBitDepth() == 16 {
|
if _, ok := source.(audio.TypedSource[int16]); ok {
|
||||||
for block := range source.ToInt16().GetBlocks() {
|
for block := range source.ToInt16().GetBlocks() {
|
||||||
if err = state.AddShort(block); err != nil {
|
if err = state.AddShort(block); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
} else if _, ok := source.(audio.TypedSource[int32]); ok {
|
||||||
for block := range int32Source.ToInt32(32).GetBlocks() {
|
if source.GetBitDepth() == 16 {
|
||||||
if err = state.AddInt(block); err != nil {
|
for block := range source.ToInt16().GetBlocks() {
|
||||||
return
|
if err = state.AddShort(block); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for block := range source.ToInt32(32).GetBlocks() {
|
||||||
|
if err = state.AddInt(block); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,16 +18,16 @@ func NewNullSink() *NullSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NullSink) Process(source Source) {
|
func (n *NullSink) Process(source Source) {
|
||||||
if float32Source, ok := source.(*Float32Source); ok {
|
if f32Source, ok := source.(TypedSource[float32]); ok {
|
||||||
for range float32Source.GetBlocks() {
|
for range f32Source.GetBlocks() {
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if int16Source, ok := source.(*Int16Source); ok {
|
} else if i16Source, ok := source.(TypedSource[int16]); ok {
|
||||||
for range int16Source.GetBlocks() {
|
for range i16Source.GetBlocks() {
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if int32Source, ok := source.(*Int32Source); ok {
|
} else if i32Source, ok := source.(TypedSource[int32]); ok {
|
||||||
for range int32Source.GetBlocks() {
|
for range i32Source.GetBlocks() {
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,16 +58,28 @@ func (n *ForwardSink) GetSamplesRead() int64 {
|
||||||
return atomic.LoadInt64(&n.SamplesRead)
|
return atomic.LoadInt64(&n.SamplesRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ForwardSink) Process(source Source) {
|
func forwardSinkProcess[T AllowedSourceTypes](n *ForwardSink, source TypedSource[T]) {
|
||||||
processor := NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
processor := NewSource[T](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||||
go func() {
|
go func() {
|
||||||
defer processor.Close()
|
defer processor.Close()
|
||||||
for block := range source.ToFloat32().GetBlocks() {
|
for block := range source.GetBlocks() {
|
||||||
atomic.AddInt64((*int64)(&n.Duration), int64((time.Second*time.Duration(len(block)/source.GetChannels()))/time.Duration(source.GetSampleRate())))
|
atomic.AddInt64((*int64)(&n.Duration), int64((time.Second*time.Duration(len(block)/source.GetChannels()))/time.Duration(source.GetSampleRate())))
|
||||||
atomic.AddInt64(&n.SamplesRead, int64(len(block)/source.GetChannels()))
|
atomic.AddInt64(&n.SamplesRead, int64(len(block)/source.GetChannels()))
|
||||||
processor.IngestFloat32(block)
|
processor.IngestNative(block, source.GetBitDepth())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
n.Target.Process(processor)
|
n.Target.Process(processor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *ForwardSink) Process(source Source) {
|
||||||
|
if f32Source, ok := source.(TypedSource[float32]); ok {
|
||||||
|
forwardSinkProcess(n, f32Source)
|
||||||
|
} else if i16Source, ok := source.(TypedSource[int16]); ok {
|
||||||
|
forwardSinkProcess(n, i16Source)
|
||||||
|
} else if i32Source, ok := source.(TypedSource[int32]); ok {
|
||||||
|
forwardSinkProcess(n, i32Source)
|
||||||
|
} else {
|
||||||
|
forwardSinkProcess(n, source.ToFloat32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
669
audio/source.go
669
audio/source.go
|
@ -1,564 +1,10 @@
|
||||||
package audio
|
package audio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
|
"errors"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Float32Source struct {
|
|
||||||
SampleRate int
|
|
||||||
Channels int
|
|
||||||
Blocks chan []float32
|
|
||||||
locked bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFloat32Source(sampleRate, channels int) *Float32Source {
|
|
||||||
return &Float32Source{
|
|
||||||
SampleRate: sampleRate,
|
|
||||||
Channels: channels,
|
|
||||||
Blocks: make(chan []float32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) Split(n int) (sources []Source) {
|
|
||||||
if s.Locked() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sources = make([]Source, n)
|
|
||||||
for i := range sources {
|
|
||||||
copiedSource := *s
|
|
||||||
copiedSource.Blocks = make(chan []float32, cap(s.Blocks))
|
|
||||||
sources[i] = &copiedSource
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.IngestFloat32(block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetBlocks() chan []float32 {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.locked = true
|
|
||||||
return s.Blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) Close() {
|
|
||||||
close(s.Blocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetSampleRate() int {
|
|
||||||
return s.SampleRate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetChannels() int {
|
|
||||||
return s.Channels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetBitDepth() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetFormat() SourceFormat {
|
|
||||||
return SourceFloat32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) Locked() bool {
|
|
||||||
return s.locked
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) Unlock() {
|
|
||||||
s.locked = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) ToFloat32() *Float32Source {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) ToInt16() *Int16Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
source := &Int16Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
BitDepth: 16,
|
|
||||||
Blocks: make(chan []int16),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
source.Blocks <- vector.Float32ToInt16(block)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) ToInt32(bitDepth int) *Int32Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
source := &Int32Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
BitDepth: bitDepth,
|
|
||||||
Blocks: make(chan []int32),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
source.Blocks <- vector.Float32ToInt32(block, bitDepth)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) IngestFloat32(buf []float32) {
|
|
||||||
s.Blocks <- buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) IngestInt8(buf []int8, bitDepth int) {
|
|
||||||
s.Blocks <- vector.Int8ToFloat32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) IngestInt16(buf []int16, bitDepth int) {
|
|
||||||
s.Blocks <- vector.Int16ToFloat32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) IngestInt24(buf []byte, bitDepth int) {
|
|
||||||
s.Blocks <- vector.Int24ToFloat32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) IngestInt32(buf []int32, bitDepth int) {
|
|
||||||
s.Blocks <- vector.Int32ToFloat32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Float32Source) GetBlocksInterface() interface{} {
|
|
||||||
return s.GetBlocks()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Int16Source struct {
|
|
||||||
BitDepth int
|
|
||||||
SampleRate int
|
|
||||||
Channels int
|
|
||||||
Blocks chan []int16
|
|
||||||
locked bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInt16Source(bitDepth, sampleRate, channels int) *Int16Source {
|
|
||||||
if bitDepth <= 0 || bitDepth > 16 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Int16Source{
|
|
||||||
BitDepth: bitDepth,
|
|
||||||
SampleRate: sampleRate,
|
|
||||||
Channels: channels,
|
|
||||||
Blocks: make(chan []int16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) Split(n int) (sources []Source) {
|
|
||||||
if s.Locked() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sources = make([]Source, n)
|
|
||||||
for i := range sources {
|
|
||||||
copiedSource := *s
|
|
||||||
copiedSource.Blocks = make(chan []int16, cap(s.Blocks))
|
|
||||||
sources[i] = &copiedSource
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.IngestInt16(block, s.BitDepth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetBlocks() chan []int16 {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.locked = true
|
|
||||||
return s.Blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) Close() {
|
|
||||||
close(s.Blocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetSampleRate() int {
|
|
||||||
return s.SampleRate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetChannels() int {
|
|
||||||
return s.Channels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetBitDepth() int {
|
|
||||||
return s.BitDepth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetFormat() SourceFormat {
|
|
||||||
return SourceInt16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) Locked() bool {
|
|
||||||
return s.locked
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) Unlock() {
|
|
||||||
s.locked = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) ToFloat32() *Float32Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
source := &Float32Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
Blocks: make(chan []float32),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
source.Blocks <- vector.Int16ToFloat32(block, s.BitDepth)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) ToInt16() *Int16Source {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) ToInt32(bitDepth int) *Int32Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
source := &Int32Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
BitDepth: bitDepth,
|
|
||||||
Blocks: make(chan []int32),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
diff := s.BitDepth - bitDepth
|
|
||||||
|
|
||||||
if diff >= 0 {
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
buf := make([]int32, len(block))
|
|
||||||
for i := range block {
|
|
||||||
buf[i] = int32(block[i]) >> diff
|
|
||||||
}
|
|
||||||
source.Blocks <- buf
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
diff = -diff
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
buf := make([]int32, len(block))
|
|
||||||
for i := range block {
|
|
||||||
buf[i] = int32(block[i]) << diff
|
|
||||||
}
|
|
||||||
source.Blocks <- buf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) IngestFloat32(buf []float32) {
|
|
||||||
s.Blocks <- vector.Float32ToInt16(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) IngestInt8(buf []int8, bitDepth int) {
|
|
||||||
block := make([]int16, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = int16(buf[i]) << (s.BitDepth - bitDepth)
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) IngestInt16(buf []int16, bitDepth int) {
|
|
||||||
if bitDepth == s.BitDepth {
|
|
||||||
s.Blocks <- buf
|
|
||||||
} else {
|
|
||||||
block := make([]int16, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = buf[i] << (s.BitDepth - bitDepth)
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) IngestInt24(buf []byte, bitDepth int) {
|
|
||||||
block := make([]int16, len(buf)/3)
|
|
||||||
|
|
||||||
for i := 0; i < len(buf); i += 3 {
|
|
||||||
sample := uint32(buf[i])
|
|
||||||
sample += uint32(buf[i+1]) << 8
|
|
||||||
sample += uint32(buf[i+2]) << 16
|
|
||||||
|
|
||||||
//sign extend
|
|
||||||
block[i/3] = int16(int32(sample<<8) >> (bitDepth - s.BitDepth + 8))
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) IngestInt32(buf []int32, bitDepth int) {
|
|
||||||
block := make([]int16, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = int16(buf[i] >> (bitDepth - s.BitDepth))
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int16Source) GetBlocksInterface() interface{} {
|
|
||||||
return s.GetBlocks()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Int32Source struct {
|
|
||||||
BitDepth int
|
|
||||||
SampleRate int
|
|
||||||
Channels int
|
|
||||||
Blocks chan []int32
|
|
||||||
locked bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInt32Source(bitDepth, sampleRate, channels int) *Int32Source {
|
|
||||||
if bitDepth <= 0 || bitDepth > 32 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Int32Source{
|
|
||||||
BitDepth: bitDepth,
|
|
||||||
SampleRate: sampleRate,
|
|
||||||
Channels: channels,
|
|
||||||
Blocks: make(chan []int32),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) Split(n int) (sources []Source) {
|
|
||||||
if s.Locked() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sources = make([]Source, n)
|
|
||||||
for i := range sources {
|
|
||||||
copiedSource := *s
|
|
||||||
copiedSource.Blocks = make(chan []int32, cap(s.Blocks))
|
|
||||||
sources[i] = &copiedSource
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
for _, source := range sources {
|
|
||||||
source.IngestInt32(block, s.BitDepth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetBlocks() chan []int32 {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.locked = true
|
|
||||||
return s.Blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) Unlock() {
|
|
||||||
s.locked = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) Close() {
|
|
||||||
close(s.Blocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetSampleRate() int {
|
|
||||||
return s.SampleRate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetChannels() int {
|
|
||||||
return s.Channels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetBitDepth() int {
|
|
||||||
return s.BitDepth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetFormat() SourceFormat {
|
|
||||||
return SourceInt32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) Locked() bool {
|
|
||||||
return s.locked
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) ToInt16() *Int16Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
source := &Int16Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
BitDepth: 16,
|
|
||||||
Blocks: make(chan []int16),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
source.IngestInt16(vector.Int32ToInt16(block, s.BitDepth), 16)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) ToInt32(bitDepth int) *Int32Source {
|
|
||||||
if s.BitDepth == bitDepth {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
source := &Int32Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
BitDepth: bitDepth,
|
|
||||||
Blocks: make(chan []int32),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
diff := s.BitDepth - bitDepth
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
if diff >= 0 {
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
buf := make([]int32, len(block))
|
|
||||||
for i := range block {
|
|
||||||
buf[i] = block[i] >> diff
|
|
||||||
}
|
|
||||||
source.IngestInt32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
diff = -diff
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
buf := make([]int32, len(block))
|
|
||||||
for i := range block {
|
|
||||||
buf[i] = block[i] << diff
|
|
||||||
}
|
|
||||||
source.IngestInt32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) ToFloat32() *Float32Source {
|
|
||||||
if s.Locked() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
source := &Float32Source{
|
|
||||||
SampleRate: s.SampleRate,
|
|
||||||
Channels: s.Channels,
|
|
||||||
Blocks: make(chan []float32),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer source.Close()
|
|
||||||
for block := range s.GetBlocks() {
|
|
||||||
source.Blocks <- vector.Int32ToFloat32(block, s.BitDepth)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) IngestFloat32(buf []float32) {
|
|
||||||
s.Blocks <- vector.Float32ToInt32(buf, s.BitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) IngestInt8(buf []int8, bitDepth int) {
|
|
||||||
block := make([]int32, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = int32(buf[i]) << (s.BitDepth - bitDepth)
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) IngestInt16(buf []int16, bitDepth int) {
|
|
||||||
block := make([]int32, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = int32(buf[i]) << (s.BitDepth - bitDepth)
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) IngestInt24(buf []byte, bitDepth int) {
|
|
||||||
s.Blocks <- vector.BytesToInt32(buf, bitDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) IngestInt32(buf []int32, bitDepth int) {
|
|
||||||
if bitDepth == s.BitDepth {
|
|
||||||
s.Blocks <- buf
|
|
||||||
} else {
|
|
||||||
block := make([]int32, len(buf))
|
|
||||||
for i := range buf {
|
|
||||||
block[i] = buf[i] << (s.BitDepth - bitDepth)
|
|
||||||
}
|
|
||||||
s.Blocks <- block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Int32Source) GetBlocksInterface() interface{} {
|
|
||||||
return s.GetBlocks()
|
|
||||||
}
|
|
||||||
|
|
||||||
type SourceFormat int
|
type SourceFormat int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -568,21 +14,124 @@ const (
|
||||||
SourceInt32
|
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 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:
|
||||||
|
out := make([]int32, nsamples)
|
||||||
|
copy(out, unsafe.Slice((*int32)(unsafe.Pointer(&bufferSlice)), nsamples))
|
||||||
|
s.IngestInt32(out, bitDepth)
|
||||||
|
return nil
|
||||||
|
case 24:
|
||||||
|
s.IngestInt24(unsafe.Slice((*byte)(unsafe.Pointer(&bufferSlice)), nsamples*3), bitDepth)
|
||||||
|
return nil
|
||||||
|
case 16:
|
||||||
|
out := make([]int16, nsamples)
|
||||||
|
copy(out, unsafe.Slice((*int16)(unsafe.Pointer(&bufferSlice)), nsamples))
|
||||||
|
s.IngestInt16(out, bitDepth)
|
||||||
|
return nil
|
||||||
|
case 8:
|
||||||
|
s.IngestInt8(unsafe.Slice((*int8)(unsafe.Pointer(&bufferSlice)), nsamples), bitDepth)
|
||||||
|
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 {
|
type Source interface {
|
||||||
GetBitDepth() int
|
GetBitDepth() int
|
||||||
GetSampleRate() int
|
GetSampleRate() int
|
||||||
GetChannels() int
|
GetChannels() int
|
||||||
GetFormat() SourceFormat
|
GetFormat() SourceFormat
|
||||||
ToFloat32() *Float32Source
|
ToFloat32() TypedSource[float32]
|
||||||
ToInt16() *Int16Source
|
ToInt16() TypedSource[int16]
|
||||||
ToInt32(bitDepth int) *Int32Source
|
ToInt32(bitDepth int) TypedSource[int32]
|
||||||
|
|
||||||
IngestFloat32(buf []float32)
|
IngestFloat32(buf []float32)
|
||||||
IngestInt8(buf []int8, bitDepth int)
|
IngestInt8(buf []int8, bitDepth int)
|
||||||
IngestInt16(buf []int16, bitDepth int)
|
IngestInt16(buf []int16, bitDepth int)
|
||||||
IngestInt24(buf []byte, bitDepth int)
|
IngestInt24(buf []byte, bitDepth int)
|
||||||
IngestInt32(buf []int32, bitDepth int)
|
IngestInt32(buf []int32, bitDepth int)
|
||||||
GetBlocksInterface() interface{}
|
|
||||||
|
|
||||||
//Split a Source into multiple ones. After calling this function, the source is locked.
|
//Split a Source into multiple ones. After calling this function, the source is locked.
|
||||||
Split(n int) []Source
|
Split(n int) []Source
|
||||||
|
|
132
audio/source_float32.go
Normal file
132
audio/source_float32.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type float32Source struct {
|
||||||
|
BitDepth int
|
||||||
|
SampleRate int
|
||||||
|
Channels int
|
||||||
|
Blocks chan []float32
|
||||||
|
locked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFloat32Source(bitDepth, sampleRate, channels int) TypedSource[float32] {
|
||||||
|
return &float32Source{
|
||||||
|
BitDepth: bitDepth,
|
||||||
|
SampleRate: sampleRate,
|
||||||
|
Channels: channels,
|
||||||
|
Blocks: make(chan []float32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) Split(n int) []Source {
|
||||||
|
return Split[float32](s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) New() TypedSource[float32] {
|
||||||
|
return newFloat32Source(s.BitDepth, s.SampleRate, s.Channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) GetBlocks() chan []float32 {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.locked = true
|
||||||
|
return s.Blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) Close() {
|
||||||
|
close(s.Blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) GetSampleRate() int {
|
||||||
|
return s.SampleRate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) GetChannels() int {
|
||||||
|
return s.Channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) GetBitDepth() int {
|
||||||
|
return s.BitDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) Locked() bool {
|
||||||
|
return s.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) Unlock() {
|
||||||
|
s.locked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) SwapBlocks(blocks chan []float32) chan []float32 {
|
||||||
|
oldBlocks := s.Blocks
|
||||||
|
s.Blocks = blocks
|
||||||
|
return oldBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) GetFormat() SourceFormat {
|
||||||
|
return SourceFloat32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) ToFloat32() TypedSource[float32] {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) ToInt16() TypedSource[int16] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
source := newInt16Source(16, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
source.IngestInt16(vector.Float32ToInt16(block), 16)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) ToInt32(bitDepth int) TypedSource[int32] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
source := newInt32Source(bitDepth, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
source.IngestInt32(vector.Float32ToInt32(block, bitDepth), bitDepth)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestFloat32(buf []float32) {
|
||||||
|
s.Blocks <- buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestInt8(buf []int8, bitDepth int) {
|
||||||
|
s.Blocks <- vector.Int8ToFloat32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestInt16(buf []int16, bitDepth int) {
|
||||||
|
s.Blocks <- vector.Int16ToFloat32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestInt24(buf []byte, bitDepth int) {
|
||||||
|
s.Blocks <- vector.Int24ToFloat32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestInt32(buf []int32, bitDepth int) {
|
||||||
|
s.Blocks <- vector.Int32ToFloat32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *float32Source) IngestNative(buf []float32, bitDepth int) {
|
||||||
|
s.IngestFloat32(buf)
|
||||||
|
}
|
181
audio/source_int16.go
Normal file
181
audio/source_int16.go
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type int16Source struct {
|
||||||
|
BitDepth int
|
||||||
|
SampleRate int
|
||||||
|
Channels int
|
||||||
|
Blocks chan []int16
|
||||||
|
locked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInt16Source(bitDepth, sampleRate, channels int) TypedSource[int16] {
|
||||||
|
if bitDepth <= 0 || bitDepth > 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &int16Source{
|
||||||
|
BitDepth: bitDepth,
|
||||||
|
SampleRate: sampleRate,
|
||||||
|
Channels: channels,
|
||||||
|
Blocks: make(chan []int16),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) Split(n int) []Source {
|
||||||
|
return Split[int16](s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) New() TypedSource[int16] {
|
||||||
|
return newInt16Source(s.BitDepth, s.SampleRate, s.Channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) GetBlocks() chan []int16 {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.locked = true
|
||||||
|
return s.Blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) Close() {
|
||||||
|
close(s.Blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) GetSampleRate() int {
|
||||||
|
return s.SampleRate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) GetChannels() int {
|
||||||
|
return s.Channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) GetBitDepth() int {
|
||||||
|
return s.BitDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) Locked() bool {
|
||||||
|
return s.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) Unlock() {
|
||||||
|
s.locked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) SwapBlocks(blocks chan []int16) chan []int16 {
|
||||||
|
oldBlocks := s.Blocks
|
||||||
|
s.Blocks = blocks
|
||||||
|
return oldBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) GetFormat() SourceFormat {
|
||||||
|
return SourceInt16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) ToFloat32() TypedSource[float32] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source := newFloat32Source(s.BitDepth, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
source.IngestFloat32(vector.Int16ToFloat32(block, s.BitDepth))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) ToInt16() TypedSource[int16] {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) ToInt32(bitDepth int) TypedSource[int32] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source := newInt32Source(bitDepth, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
diff := s.BitDepth - bitDepth
|
||||||
|
|
||||||
|
if diff >= 0 {
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
buf := make([]int32, len(block))
|
||||||
|
for i := range block {
|
||||||
|
buf[i] = int32(block[i]) >> diff
|
||||||
|
}
|
||||||
|
source.IngestInt32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diff = -diff
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
buf := make([]int32, len(block))
|
||||||
|
for i := range block {
|
||||||
|
buf[i] = int32(block[i]) << diff
|
||||||
|
}
|
||||||
|
source.IngestInt32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestFloat32(buf []float32) {
|
||||||
|
s.Blocks <- vector.Float32ToInt16(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestInt8(buf []int8, bitDepth int) {
|
||||||
|
block := make([]int16, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = int16(buf[i]) << (s.BitDepth - bitDepth)
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestInt16(buf []int16, bitDepth int) {
|
||||||
|
if bitDepth == s.BitDepth {
|
||||||
|
s.Blocks <- buf
|
||||||
|
} else {
|
||||||
|
block := make([]int16, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = buf[i] << (s.BitDepth - bitDepth)
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestInt24(buf []byte, bitDepth int) {
|
||||||
|
block := make([]int16, len(buf)/3)
|
||||||
|
|
||||||
|
for i := 0; i < len(buf); i += 3 {
|
||||||
|
sample := uint32(buf[i])
|
||||||
|
sample += uint32(buf[i+1]) << 8
|
||||||
|
sample += uint32(buf[i+2]) << 16
|
||||||
|
|
||||||
|
//sign extend
|
||||||
|
block[i/3] = int16(int32(sample<<8) >> (bitDepth - s.BitDepth + 8))
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestInt32(buf []int32, bitDepth int) {
|
||||||
|
block := make([]int16, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = int16(buf[i] >> (bitDepth - s.BitDepth))
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int16Source) IngestNative(buf []int16, bitDepth int) {
|
||||||
|
s.IngestInt16(buf, bitDepth)
|
||||||
|
}
|
184
audio/source_int32.go
Normal file
184
audio/source_int32.go
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package audio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type int32Source struct {
|
||||||
|
BitDepth int
|
||||||
|
SampleRate int
|
||||||
|
Channels int
|
||||||
|
Blocks chan []int32
|
||||||
|
locked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInt32Source(bitDepth, sampleRate, channels int) TypedSource[int32] {
|
||||||
|
if bitDepth <= 0 || bitDepth > 32 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &int32Source{
|
||||||
|
BitDepth: bitDepth,
|
||||||
|
SampleRate: sampleRate,
|
||||||
|
Channels: channels,
|
||||||
|
Blocks: make(chan []int32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) Split(n int) []Source {
|
||||||
|
return Split[int32](s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) New() TypedSource[int32] {
|
||||||
|
return newInt32Source(s.BitDepth, s.SampleRate, s.Channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) GetBlocks() chan []int32 {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.locked = true
|
||||||
|
return s.Blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) Close() {
|
||||||
|
close(s.Blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) GetSampleRate() int {
|
||||||
|
return s.SampleRate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) GetChannels() int {
|
||||||
|
return s.Channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) GetBitDepth() int {
|
||||||
|
return s.BitDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) Locked() bool {
|
||||||
|
return s.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) Unlock() {
|
||||||
|
s.locked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) SwapBlocks(blocks chan []int32) chan []int32 {
|
||||||
|
oldBlocks := s.Blocks
|
||||||
|
s.Blocks = blocks
|
||||||
|
return oldBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) GetFormat() SourceFormat {
|
||||||
|
return SourceInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) ToInt16() TypedSource[int16] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source := newInt16Source(16, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
source.IngestInt16(vector.Int32ToInt16(block, s.BitDepth), 16)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) ToInt32(bitDepth int) TypedSource[int32] {
|
||||||
|
if s.BitDepth == bitDepth {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
source := newInt32Source(bitDepth, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
diff := s.BitDepth - bitDepth
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
if diff >= 0 {
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
buf := make([]int32, len(block))
|
||||||
|
for i := range block {
|
||||||
|
buf[i] = block[i] >> diff
|
||||||
|
}
|
||||||
|
source.IngestInt32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diff = -diff
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
buf := make([]int32, len(block))
|
||||||
|
for i := range block {
|
||||||
|
buf[i] = block[i] << diff
|
||||||
|
}
|
||||||
|
source.IngestInt32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) ToFloat32() TypedSource[float32] {
|
||||||
|
if s.Locked() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source := newFloat32Source(s.BitDepth, s.SampleRate, s.Channels)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer source.Close()
|
||||||
|
for block := range s.GetBlocks() {
|
||||||
|
source.IngestFloat32(vector.Int32ToFloat32(block, s.BitDepth))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestFloat32(buf []float32) {
|
||||||
|
s.Blocks <- vector.Float32ToInt32(buf, s.BitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestInt8(buf []int8, bitDepth int) {
|
||||||
|
block := make([]int32, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = int32(buf[i]) << (s.BitDepth - bitDepth)
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestInt16(buf []int16, bitDepth int) {
|
||||||
|
block := make([]int32, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = int32(buf[i]) << (s.BitDepth - bitDepth)
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestInt24(buf []byte, bitDepth int) {
|
||||||
|
s.Blocks <- vector.BytesToInt32(buf, bitDepth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestInt32(buf []int32, bitDepth int) {
|
||||||
|
if bitDepth == s.BitDepth {
|
||||||
|
s.Blocks <- buf
|
||||||
|
} else {
|
||||||
|
block := make([]int32, len(buf))
|
||||||
|
for i := range buf {
|
||||||
|
block[i] = buf[i] << (s.BitDepth - bitDepth)
|
||||||
|
}
|
||||||
|
s.Blocks <- block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *int32Source) IngestNative(buf []int32, bitDepth int) {
|
||||||
|
s.IngestInt32(buf, bitDepth)
|
||||||
|
}
|
Loading…
Reference in a new issue