Use generics to implement TypedSource[float32|int16|int32]
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
DataHoarder 2022-07-22 11:39:58 +02:00
parent 3d70bcc15c
commit 09f3cf3b56
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
22 changed files with 778 additions and 881 deletions

View file

@ -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). |

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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,

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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))
}
}()

View file

@ -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()

View file

@ -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 {

View file

@ -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()

View file

@ -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 {

View file

@ -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())
}
}

View file

@ -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
View 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
View 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
View 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)
}