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 |
|
||||
|:----------:|:----------------------------------------------------------------------------------------:|:-------:|:---------------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **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). |
|
||||
| **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). |
|
||||
| **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
|
||||
|
||||
|
@ -116,7 +116,7 @@ Some tests cannot be completed in this mode.
|
|||
| 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). |
|
||||
| **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). |
|
||||
| **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). |
|
||||
|
|
|
@ -29,9 +29,9 @@ func (f SourceFormatFilter) Process(source audio.Source) audio.Source {
|
|||
case audio.SourceInt16:
|
||||
return source.ToInt16()
|
||||
case audio.SourceInt32:
|
||||
if int16Source, ok := source.(*audio.Int16Source); ok {
|
||||
if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||
return source.ToInt32(int16Source.GetBitDepth())
|
||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
||||
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||
return int32Source
|
||||
} else {
|
||||
return source.ToInt32(32)
|
||||
|
@ -53,7 +53,7 @@ func NewBufferFilter(blockBufferSize int) BufferFilter {
|
|||
}
|
||||
|
||||
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)
|
||||
go func() {
|
||||
defer close(outBlocks)
|
||||
|
@ -62,12 +62,10 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
|||
}
|
||||
}()
|
||||
|
||||
return &audio.Float32Source{
|
||||
Channels: source.GetChannels(),
|
||||
SampleRate: source.GetSampleRate(),
|
||||
Blocks: outBlocks,
|
||||
}
|
||||
} else if int16Source, ok := source.(*audio.Int16Source); ok {
|
||||
newSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
close(newSource.SwapBlocks(outBlocks))
|
||||
return newSource
|
||||
} else if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||
outBlocks := make(chan []int16, f.blockBufferSize)
|
||||
go func() {
|
||||
defer close(outBlocks)
|
||||
|
@ -76,13 +74,10 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
|||
}
|
||||
}()
|
||||
|
||||
return &audio.Int16Source{
|
||||
BitDepth: source.GetBitDepth(),
|
||||
Channels: source.GetChannels(),
|
||||
SampleRate: source.GetSampleRate(),
|
||||
Blocks: outBlocks,
|
||||
}
|
||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
||||
newSource := audio.NewSource[int16](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
close(newSource.SwapBlocks(outBlocks))
|
||||
return newSource
|
||||
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||
outBlocks := make(chan []int32, f.blockBufferSize)
|
||||
go func() {
|
||||
defer close(outBlocks)
|
||||
|
@ -91,12 +86,9 @@ func (f BufferFilter) Process(source audio.Source) audio.Source {
|
|||
}
|
||||
}()
|
||||
|
||||
return &audio.Int32Source{
|
||||
BitDepth: source.GetBitDepth(),
|
||||
Channels: source.GetChannels(),
|
||||
SampleRate: source.GetSampleRate(),
|
||||
Blocks: outBlocks,
|
||||
}
|
||||
newSource := audio.NewSource[int32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
close(newSource.SwapBlocks(outBlocks))
|
||||
return newSource
|
||||
}
|
||||
|
||||
return source
|
||||
|
@ -119,8 +111,8 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
|||
blockSize := (source.GetSampleRate() / f.blocksPerSecond) * source.GetChannels()
|
||||
throttler := time.Tick(time.Second / time.Duration(f.blocksPerSecond))
|
||||
|
||||
if floatSource, ok := source.(*audio.Float32Source); ok {
|
||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
||||
if floatSource, ok := source.(audio.TypedSource[float32]); ok {
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
var buf []float32
|
||||
|
@ -137,12 +129,12 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
|||
}()
|
||||
|
||||
return outSource
|
||||
} else if int16Source, ok := source.(*audio.Int16Source); ok {
|
||||
} else if int16Source, ok := source.(audio.TypedSource[int16]); ok {
|
||||
bitDepth := source.GetBitDepth()
|
||||
if bitDepth > 16 {
|
||||
bitDepth = 16
|
||||
}
|
||||
outSource := audio.NewInt16Source(bitDepth, source.GetSampleRate(), source.GetChannels())
|
||||
outSource := audio.NewSource[int16](bitDepth, source.GetSampleRate(), source.GetChannels())
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
var buf []int16
|
||||
|
@ -159,8 +151,8 @@ func (f RealTimeFilter) Process(source audio.Source) audio.Source {
|
|||
}()
|
||||
|
||||
return outSource
|
||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
||||
outSource := audio.NewInt32Source(source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||
outSource := audio.NewSource[int32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
var buf []int32
|
||||
|
@ -197,7 +189,7 @@ func (f VolumeFilter) Process(source audio.Source) audio.Source {
|
|||
return source
|
||||
}
|
||||
|
||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
|
||||
|
@ -223,7 +215,7 @@ func (f StereoFilter) Process(source audio.Source) audio.Source {
|
|||
}
|
||||
|
||||
//TODO: make this for int16 and int32
|
||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), 2)
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 2)
|
||||
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
|
@ -244,7 +236,7 @@ func (f MonoFilter) Process(source audio.Source) audio.Source {
|
|||
}
|
||||
|
||||
//TODO: make this for int16 and int32
|
||||
outSource := audio.NewFloat32Source(source.GetSampleRate(), 1)
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 1)
|
||||
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
|
|
|
@ -20,7 +20,7 @@ func (f ResampleFilter) Process(source audio.Source) audio.Source {
|
|||
return source
|
||||
}
|
||||
|
||||
outSource := audio.NewFloat32Source(f.sampleRate, source.GetChannels())
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), f.sampleRate, source.GetChannels())
|
||||
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
|
|
|
@ -20,7 +20,7 @@ func (f ResampleFilter) Process(source audio.Source) audio.Source {
|
|||
return source
|
||||
}
|
||||
|
||||
outSource := audio.NewFloat32Source(f.sampleRate, source.GetChannels())
|
||||
outSource := audio.NewSource[float32](source.GetBitDepth(), f.sampleRate, source.GetChannels())
|
||||
|
||||
go func() {
|
||||
defer outSource.Close()
|
||||
|
|
|
@ -135,7 +135,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
source := audio.NewInt16Source(16, decoder.SampleRate(), decoder.NumChannels())
|
||||
source := audio.NewSource[int16](16, decoder.SampleRate(), decoder.NumChannels())
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
@ -181,7 +181,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
source := audio.NewInt16Source(16, decoder.SampleRate(), decoder.NumChannels())
|
||||
source := audio.NewSource[int16](16, decoder.SampleRate(), decoder.NumChannels())
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -43,7 +43,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
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() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -37,7 +37,7 @@ func NewAnalyzerChannel(source audio.Source, err error) (audio.Source, AnalyzerC
|
|||
go func() {
|
||||
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() {
|
||||
channel <- &AnalyzerPacket{
|
||||
Samples: block,
|
||||
|
|
|
@ -31,7 +31,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
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() {
|
||||
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() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -35,7 +35,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
source := audio.NewInt16Source(16, decoder.SampleRate(), 2)
|
||||
source := audio.NewSource[int16](16, decoder.SampleRate(), 2)
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -33,7 +33,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
source := audio.NewInt16Source(16, decoder.SampleRate(), 2)
|
||||
source := audio.NewSource[int16](16, decoder.SampleRate(), 2)
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -46,7 +46,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
}
|
||||
|
||||
channelCount := info.ChannelCount
|
||||
source := audio.NewInt16Source(16, FixedSampleRate, channelCount)
|
||||
source := audio.NewSource[int16](16, FixedSampleRate, channelCount)
|
||||
|
||||
go func() {
|
||||
defer stream.Close()
|
||||
|
|
|
@ -80,20 +80,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var source audio.Source
|
||||
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))
|
||||
|
||||
}
|
||||
source := audio.NewSource[int32](int(info.Bps), int(info.Sps), int(info.Nch))
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
@ -107,26 +94,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
break
|
||||
}
|
||||
|
||||
bufferSlice := buffer[:writeLen]
|
||||
|
||||
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)
|
||||
}
|
||||
source.IngestInt32(vector.BytesToInt32(buffer[:writeLen], int(info.Bps)), int(info.Bps))
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
source := audio.NewFloat32Source(reader.Channels(), reader.SampleRate())
|
||||
source := audio.NewSource[float32](16, reader.Channels(), reader.SampleRate())
|
||||
|
||||
go func() {
|
||||
defer source.Close()
|
||||
|
|
|
@ -22,9 +22,7 @@ type QueueEntry struct {
|
|||
|
||||
type Queue struct {
|
||||
queue []*QueueEntry
|
||||
float32Output *audio.Float32Source
|
||||
int16Output *audio.Int16Source
|
||||
int32Output *audio.Int32Source
|
||||
output audio.Source
|
||||
interrupt chan bool
|
||||
interruptDepth int64
|
||||
closed bool
|
||||
|
@ -44,252 +42,124 @@ func NewQueue(format audio.SourceFormat, bitDepth, sampleRate, channels int) *Qu
|
|||
|
||||
switch format {
|
||||
case audio.SourceFloat32:
|
||||
q.float32Output = audio.NewFloat32Source(sampleRate, channels)
|
||||
q.output = audio.NewSource[float32](bitDepth, sampleRate, channels)
|
||||
queueLoopStart[float32](q)
|
||||
case audio.SourceInt16:
|
||||
q.int16Output = audio.NewInt16Source(bitDepth, sampleRate, channels)
|
||||
q.output = audio.NewSource[int16](bitDepth, sampleRate, channels)
|
||||
queueLoopStart[int16](q)
|
||||
case audio.SourceInt32:
|
||||
q.int32Output = audio.NewInt32Source(bitDepth, sampleRate, channels)
|
||||
q.output = audio.NewSource[int32](bitDepth, sampleRate, channels)
|
||||
queueLoopStart[int32](q)
|
||||
default:
|
||||
log.Panicf("not found source format %d", int(format))
|
||||
}
|
||||
|
||||
q.start()
|
||||
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)
|
||||
output = audio.NewSource[T](input.GetBitDepth(), input.GetSampleRate(), input.GetChannels())
|
||||
|
||||
switch q.GetSource().GetFormat() {
|
||||
case audio.SourceFloat32:
|
||||
output = audio.NewFloat32Source(input.GetSampleRate(), input.GetChannels())
|
||||
bitDepth := input.GetBitDepth()
|
||||
|
||||
sourceChannel := input.ToFloat32().GetBlocks()
|
||||
go func() {
|
||||
defer output.Close()
|
||||
L1:
|
||||
for {
|
||||
select {
|
||||
case <-cancel:
|
||||
sourceChannel := input.GetBlocks()
|
||||
go func() {
|
||||
defer output.Close()
|
||||
L1:
|
||||
for {
|
||||
select {
|
||||
case <-cancel:
|
||||
break L1
|
||||
case block, more := <-sourceChannel:
|
||||
if !more {
|
||||
//no more blocks!
|
||||
break L1
|
||||
case block, more := <-sourceChannel:
|
||||
if !more {
|
||||
//no more blocks!
|
||||
break L1
|
||||
} else {
|
||||
output.IngestFloat32(block)
|
||||
}
|
||||
} else {
|
||||
output.IngestNative(block, bitDepth)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
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()
|
||||
|
||||
input.Unlock()
|
||||
|
||||
//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()))
|
||||
}
|
||||
//sink remaining
|
||||
go audio.NewNullSink().Process(input)
|
||||
}()
|
||||
|
||||
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)
|
||||
go func() {
|
||||
defer q.wg.Done()
|
||||
var current *QueueEntry
|
||||
|
||||
if q.float32Output != nil {
|
||||
L1:
|
||||
for {
|
||||
q.lock.RLock()
|
||||
if q.closed {
|
||||
q.float32Output.Close()
|
||||
break L1
|
||||
}
|
||||
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]
|
||||
var currentBlocks chan []T
|
||||
L1:
|
||||
for {
|
||||
q.lock.RLock()
|
||||
if q.closed {
|
||||
q.output.Close()
|
||||
break L1
|
||||
}
|
||||
if len(q.queue) == 0 { //no more entries, wait for interrupt
|
||||
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:
|
||||
for {
|
||||
select {
|
||||
case <-q.interrupt:
|
||||
atomic.AddInt64(&q.interruptDepth, -1)
|
||||
//force recheck
|
||||
F1:
|
||||
for {
|
||||
select {
|
||||
case <-q.interrupt:
|
||||
atomic.AddInt64(&q.interruptDepth, -1)
|
||||
//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
|
||||
case block, more := <-current.Source.(*audio.Float32Source).Blocks:
|
||||
if !more {
|
||||
//no more blocks! skip
|
||||
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)
|
||||
} else {
|
||||
if current.StartCallback != nil && current.ReadSamples == 0 && len(block) > 0 {
|
||||
current.StartCallback(q, current)
|
||||
}
|
||||
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 {
|
||||
if q.GetChannels() == 1 {
|
||||
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 {
|
||||
if q.float32Output != nil {
|
||||
return q.float32Output
|
||||
} else if q.int16Output != nil {
|
||||
return q.int16Output
|
||||
} else if q.int32Output != nil {
|
||||
return q.int32Output
|
||||
}
|
||||
return nil
|
||||
return q.output
|
||||
}
|
||||
|
||||
func (q *Queue) GetSampleRate() int {
|
||||
|
|
|
@ -21,7 +21,7 @@ func NewNormalizationFilter(delayInSeconds int) NormalizationFilter {
|
|||
}
|
||||
|
||||
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() {
|
||||
defer outSource.Close()
|
||||
|
||||
|
|
|
@ -20,16 +20,24 @@ func GetTrackReplayGain(source audio.Source) (gain, peak float64, err error) {
|
|||
}
|
||||
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() {
|
||||
if err = state.AddShort(block); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
||||
for block := range int32Source.ToInt32(32).GetBlocks() {
|
||||
if err = state.AddInt(block); err != nil {
|
||||
return
|
||||
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
||||
if source.GetBitDepth() == 16 {
|
||||
for block := range source.ToInt16().GetBlocks() {
|
||||
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 {
|
||||
|
@ -85,16 +93,24 @@ func GetAlbumReplayGain(sources []audio.Source) (albumGain, albumPeak float64, t
|
|||
defer wg.Done()
|
||||
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() {
|
||||
if err = state.AddShort(block); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if int32Source, ok := source.(*audio.Int32Source); ok {
|
||||
for block := range int32Source.ToInt32(32).GetBlocks() {
|
||||
if err = state.AddInt(block); err != nil {
|
||||
return
|
||||
} else if _, ok := source.(audio.TypedSource[int32]); ok {
|
||||
if source.GetBitDepth() == 16 {
|
||||
for block := range source.ToInt16().GetBlocks() {
|
||||
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 {
|
||||
|
|
|
@ -18,16 +18,16 @@ func NewNullSink() *NullSink {
|
|||
}
|
||||
|
||||
func (n *NullSink) Process(source Source) {
|
||||
if float32Source, ok := source.(*Float32Source); ok {
|
||||
for range float32Source.GetBlocks() {
|
||||
if f32Source, ok := source.(TypedSource[float32]); ok {
|
||||
for range f32Source.GetBlocks() {
|
||||
|
||||
}
|
||||
} else if int16Source, ok := source.(*Int16Source); ok {
|
||||
for range int16Source.GetBlocks() {
|
||||
} else if i16Source, ok := source.(TypedSource[int16]); ok {
|
||||
for range i16Source.GetBlocks() {
|
||||
|
||||
}
|
||||
} else if int32Source, ok := source.(*Int32Source); ok {
|
||||
for range int32Source.GetBlocks() {
|
||||
} else if i32Source, ok := source.(TypedSource[int32]); ok {
|
||||
for range i32Source.GetBlocks() {
|
||||
|
||||
}
|
||||
} else {
|
||||
|
@ -58,16 +58,28 @@ func (n *ForwardSink) GetSamplesRead() int64 {
|
|||
return atomic.LoadInt64(&n.SamplesRead)
|
||||
}
|
||||
|
||||
func (n *ForwardSink) Process(source Source) {
|
||||
processor := NewFloat32Source(source.GetSampleRate(), source.GetChannels())
|
||||
func forwardSinkProcess[T AllowedSourceTypes](n *ForwardSink, source TypedSource[T]) {
|
||||
processor := NewSource[T](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
|
||||
go func() {
|
||||
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(&n.SamplesRead, int64(len(block)/source.GetChannels()))
|
||||
processor.IngestFloat32(block)
|
||||
processor.IngestNative(block, source.GetBitDepth())
|
||||
}
|
||||
}()
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
const (
|
||||
|
@ -568,21 +14,124 @@ const (
|
|||
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 {
|
||||
GetBitDepth() int
|
||||
GetSampleRate() int
|
||||
GetChannels() int
|
||||
GetFormat() SourceFormat
|
||||
ToFloat32() *Float32Source
|
||||
ToInt16() *Int16Source
|
||||
ToInt32(bitDepth int) *Int32Source
|
||||
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)
|
||||
GetBlocksInterface() interface{}
|
||||
|
||||
//Split a Source into multiple ones. After calling this function, the source is locked.
|
||||
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