Kirika/audio/filter/filter.go
DataHoarder 09f3cf3b56
All checks were successful
continuous-integration/drone/push Build is passing
Use generics to implement TypedSource[float32|int16|int32]
2022-07-22 12:07:01 +02:00

250 lines
6.1 KiB
Go

package filter
import (
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
"log"
"time"
)
type Filter interface {
Process(source audio.Source) audio.Source
}
func NewFilterChain(source audio.Source, filters ...Filter) audio.Source {
for _, filter := range filters {
source = filter.Process(source)
}
return source
}
type SourceFormatFilter struct {
Format audio.SourceFormat
}
func (f SourceFormatFilter) Process(source audio.Source) audio.Source {
switch f.Format {
case audio.SourceFloat32:
return source.ToFloat32()
case audio.SourceInt16:
return source.ToInt16()
case audio.SourceInt32:
if int16Source, ok := source.(audio.TypedSource[int16]); ok {
return source.ToInt32(int16Source.GetBitDepth())
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
return int32Source
} else {
return source.ToInt32(32)
}
}
//TODO: panic?
return source
}
type BufferFilter struct {
blockBufferSize int
}
func NewBufferFilter(blockBufferSize int) BufferFilter {
return BufferFilter{
blockBufferSize: blockBufferSize,
}
}
func (f BufferFilter) Process(source audio.Source) audio.Source {
if floatSource, ok := source.(audio.TypedSource[float32]); ok {
outBlocks := make(chan []float32, f.blockBufferSize)
go func() {
defer close(outBlocks)
for block := range floatSource.GetBlocks() {
outBlocks <- block
}
}()
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)
for block := range int16Source.GetBlocks() {
outBlocks <- block
}
}()
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)
for block := range int32Source.GetBlocks() {
outBlocks <- block
}
}()
newSource := audio.NewSource[int32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
close(newSource.SwapBlocks(outBlocks))
return newSource
}
return source
}
type RealTimeFilter struct {
blocksPerSecond int
}
func NewRealTimeFilter(blocksPerSecond int) RealTimeFilter {
return RealTimeFilter{
blocksPerSecond: blocksPerSecond,
}
}
func (f RealTimeFilter) Process(source audio.Source) audio.Source {
if source.GetSampleRate()%f.blocksPerSecond != 0 {
log.Panicf("%d %% %d != 0", source.GetSampleRate(), f.blocksPerSecond)
}
blockSize := (source.GetSampleRate() / f.blocksPerSecond) * source.GetChannels()
throttler := time.Tick(time.Second / time.Duration(f.blocksPerSecond))
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
for block := range floatSource.GetBlocks() {
buf = append(buf, block...)
for len(buf) >= blockSize {
outSource.IngestFloat32(buf[0:blockSize])
buf = buf[blockSize:]
<-throttler
}
}
outSource.IngestFloat32(buf)
}()
return outSource
} else if int16Source, ok := source.(audio.TypedSource[int16]); ok {
bitDepth := source.GetBitDepth()
if bitDepth > 16 {
bitDepth = 16
}
outSource := audio.NewSource[int16](bitDepth, source.GetSampleRate(), source.GetChannels())
go func() {
defer outSource.Close()
var buf []int16
for block := range int16Source.GetBlocks() {
buf = append(buf, block...)
for len(buf) >= blockSize {
outSource.IngestInt16(buf[0:blockSize], bitDepth)
buf = buf[blockSize:]
<-throttler
}
}
outSource.IngestInt16(buf, source.GetBitDepth())
}()
return outSource
} 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
for block := range int32Source.GetBlocks() {
buf = append(buf, block...)
for len(buf) >= blockSize {
outSource.IngestInt32(buf[0:blockSize], source.GetBitDepth())
buf = buf[blockSize:]
<-throttler
}
}
outSource.IngestInt32(buf, source.GetBitDepth())
}()
return outSource
}
return nil
}
type VolumeFilter struct {
adjustment float32
}
func NewVolumeFilter(adjustment float32) VolumeFilter {
return VolumeFilter{
adjustment: adjustment,
}
}
func (f VolumeFilter) Process(source audio.Source) audio.Source {
if f.adjustment == 1. {
return source
}
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), source.GetChannels())
go func() {
defer outSource.Close()
for block := range source.ToFloat32().GetBlocks() {
out := make([]float32, len(block))
for i := range block {
out[i] = block[i] * f.adjustment
}
outSource.IngestFloat32(out)
}
}()
return outSource
}
type StereoFilter struct {
}
func (f StereoFilter) Process(source audio.Source) audio.Source {
if source.GetChannels() == 2 { //no change
return source
}
//TODO: make this for int16 and int32
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 2)
go func() {
defer outSource.Close()
for block := range source.ToFloat32().GetBlocks() {
outSource.IngestFloat32(vector.MultipleChannelsToStereo(block, source.GetChannels()))
}
}()
return outSource
}
type MonoFilter struct {
}
func (f MonoFilter) Process(source audio.Source) audio.Source {
if source.GetChannels() == 1 { //no change
return source
}
//TODO: make this for int16 and int32
outSource := audio.NewSource[float32](source.GetBitDepth(), source.GetSampleRate(), 1)
go func() {
defer outSource.Close()
for block := range source.ToFloat32().GetBlocks() {
outSource.IngestFloat32(vector.MultipleChannelsToMono(block, source.GetChannels()))
}
}()
return outSource
}