From 09f3cf3b563ae510c778913f1d1015c7dda12d6a Mon Sep 17 00:00:00 2001
From: WeebDataHoarder <57538841+WeebDataHoarder@users.noreply.github.com>
Date: Fri, 22 Jul 2022 11:39:58 +0200
Subject: [PATCH] Use generics to implement TypedSource[float32|int16|int32]
---
README.md | 6 +-
audio/filter/filter.go | 54 +-
audio/filter/resample_filter_cgo.go | 2 +-
audio/filter/resample_filter_nocgo.go | 2 +-
audio/format/aac/libfdk-aac.go | 4 +-
audio/format/alac/libalac.go | 2 +-
audio/format/analyzer.go | 2 +-
audio/format/flac/goflac.go | 2 +-
audio/format/flac/libflac.go | 2 +-
audio/format/mp3/mp3.go | 2 +-
audio/format/mp3/mp3_nocgo.go | 2 +-
audio/format/opus/opus.go | 2 +-
audio/format/tta/tta.go | 36 +-
audio/format/vorbis/vorbis.go | 2 +-
audio/queue/queue.go | 303 +++-------
audio/replaygain/filter_normalization.go | 2 +-
audio/replaygain/normalization.go | 36 +-
audio/sink.go | 32 +-
audio/source.go | 669 ++++-------------------
audio/source_float32.go | 132 +++++
audio/source_int16.go | 181 ++++++
audio/source_int32.go | 184 +++++++
22 files changed, 778 insertions(+), 881 deletions(-)
create mode 100644 audio/source_float32.go
create mode 100644 audio/source_int16.go
create mode 100644 audio/source_int32.go
diff --git a/README.md b/README.md
index e053d14..7062e51 100644
--- a/README.md
+++ b/README.md
@@ -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.
Decoding/encoding by [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac).
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.
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.
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).
Decoding/encoding by [FDK-AAC](https://github.com/mstorsjo/fdk-aac) via [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac).
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.
*ADIF only supported on encoding.
**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).
Disabled by default.
*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).
Disabled by default.
*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). |
diff --git a/audio/filter/filter.go b/audio/filter/filter.go
index 1ba2801..290e15d 100644
--- a/audio/filter/filter.go
+++ b/audio/filter/filter.go
@@ -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()
diff --git a/audio/filter/resample_filter_cgo.go b/audio/filter/resample_filter_cgo.go
index 8e57f19..49982b6 100644
--- a/audio/filter/resample_filter_cgo.go
+++ b/audio/filter/resample_filter_cgo.go
@@ -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()
diff --git a/audio/filter/resample_filter_nocgo.go b/audio/filter/resample_filter_nocgo.go
index 6f8c4f8..4f1cea1 100644
--- a/audio/filter/resample_filter_nocgo.go
+++ b/audio/filter/resample_filter_nocgo.go
@@ -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()
diff --git a/audio/format/aac/libfdk-aac.go b/audio/format/aac/libfdk-aac.go
index 07eea6b..99c3a3e 100644
--- a/audio/format/aac/libfdk-aac.go
+++ b/audio/format/aac/libfdk-aac.go
@@ -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()
diff --git a/audio/format/alac/libalac.go b/audio/format/alac/libalac.go
index c30a853..e685c0e 100644
--- a/audio/format/alac/libalac.go
+++ b/audio/format/alac/libalac.go
@@ -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()
diff --git a/audio/format/analyzer.go b/audio/format/analyzer.go
index 4d6248c..b893ef1 100644
--- a/audio/format/analyzer.go
+++ b/audio/format/analyzer.go
@@ -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,
diff --git a/audio/format/flac/goflac.go b/audio/format/flac/goflac.go
index 8aef35c..043d800 100644
--- a/audio/format/flac/goflac.go
+++ b/audio/format/flac/goflac.go
@@ -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()
diff --git a/audio/format/flac/libflac.go b/audio/format/flac/libflac.go
index f65de9a..acfadf6 100644
--- a/audio/format/flac/libflac.go
+++ b/audio/format/flac/libflac.go
@@ -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()
diff --git a/audio/format/mp3/mp3.go b/audio/format/mp3/mp3.go
index 5af8eea..dbf9fb7 100644
--- a/audio/format/mp3/mp3.go
+++ b/audio/format/mp3/mp3.go
@@ -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()
diff --git a/audio/format/mp3/mp3_nocgo.go b/audio/format/mp3/mp3_nocgo.go
index bd3502b..a8b3b7c 100644
--- a/audio/format/mp3/mp3_nocgo.go
+++ b/audio/format/mp3/mp3_nocgo.go
@@ -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()
diff --git a/audio/format/opus/opus.go b/audio/format/opus/opus.go
index 900cd2d..2f9b2b8 100644
--- a/audio/format/opus/opus.go
+++ b/audio/format/opus/opus.go
@@ -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()
diff --git a/audio/format/tta/tta.go b/audio/format/tta/tta.go
index f98812a..ff807b7 100644
--- a/audio/format/tta/tta.go
+++ b/audio/format/tta/tta.go
@@ -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))
}
}()
diff --git a/audio/format/vorbis/vorbis.go b/audio/format/vorbis/vorbis.go
index 9778419..44ccc30 100644
--- a/audio/format/vorbis/vorbis.go
+++ b/audio/format/vorbis/vorbis.go
@@ -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()
diff --git a/audio/queue/queue.go b/audio/queue/queue.go
index ed17aaa..f86d0f6 100644
--- a/audio/queue/queue.go
+++ b/audio/queue/queue.go
@@ -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 {
diff --git a/audio/replaygain/filter_normalization.go b/audio/replaygain/filter_normalization.go
index d746f8c..a7eab73 100644
--- a/audio/replaygain/filter_normalization.go
+++ b/audio/replaygain/filter_normalization.go
@@ -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()
diff --git a/audio/replaygain/normalization.go b/audio/replaygain/normalization.go
index 6fa4cb8..41e6139 100644
--- a/audio/replaygain/normalization.go
+++ b/audio/replaygain/normalization.go
@@ -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 {
diff --git a/audio/sink.go b/audio/sink.go
index 83808e2..12e760e 100644
--- a/audio/sink.go
+++ b/audio/sink.go
@@ -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())
+ }
+}
diff --git a/audio/source.go b/audio/source.go
index 34d8334..1e2dae4 100644
--- a/audio/source.go
+++ b/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
diff --git a/audio/source_float32.go b/audio/source_float32.go
new file mode 100644
index 0000000..482bf1f
--- /dev/null
+++ b/audio/source_float32.go
@@ -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)
+}
diff --git a/audio/source_int16.go b/audio/source_int16.go
new file mode 100644
index 0000000..85f8a0c
--- /dev/null
+++ b/audio/source_int16.go
@@ -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)
+}
diff --git a/audio/source_int32.go b/audio/source_int32.go
new file mode 100644
index 0000000..69f418c
--- /dev/null
+++ b/audio/source_int32.go
@@ -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)
+}