Kirika/audio/filter.go
DataHoarder e2639f51ee
All checks were successful
continuous-integration/drone/push Build is passing
Fix samplerate filter
2022-02-23 18:01:10 +01:00

217 lines
4.7 KiB
Go

package audio
/*
#cgo CFLAGS: -I"${SRCDIR}/../cgo" -march=native -Ofast -std=c99
#include "audio.h"
*/
import "C"
import (
"github.com/dh1tw/gosamplerate"
"log"
"time"
)
type Filter interface {
Process(source Source) Source
}
func NewFilterChain(source Source, filters ...Filter) Source {
for _, filter := range filters {
source = filter.Process(source)
}
return source
}
type BufferFilter struct {
blockBufferSize int
}
func NewBufferFilter(blockBufferSize int) BufferFilter {
return BufferFilter{
blockBufferSize: blockBufferSize,
}
}
func (f BufferFilter) Process(source Source) Source {
outBlocks := make(chan []float32, f.blockBufferSize)
go func() {
defer close(outBlocks)
for block := range source.Blocks {
outBlocks <- block
}
}()
return Source{
Channels: source.Channels,
SampleRate: source.SampleRate,
Blocks: outBlocks,
}
}
type RealTimeFilter struct {
}
func (f RealTimeFilter) Process(source Source) Source {
outBlocks := make(chan []float32)
const blocksPerSecond = 10
if source.SampleRate%blocksPerSecond != 0 {
log.Panicf("%d %% %d != 0", source.SampleRate, blocksPerSecond)
}
blockSize := (source.SampleRate / blocksPerSecond) * source.Channels
throttler := time.Tick(time.Second / blocksPerSecond)
go func() {
defer close(outBlocks)
var buf []float32
for block := range source.Blocks {
buf = append(buf, block...)
for len(buf) >= blockSize {
outBlocks <- buf[0:blockSize]
buf = buf[blockSize:]
<-throttler
}
}
outBlocks <- buf
}()
return Source{
Channels: source.Channels,
SampleRate: source.SampleRate,
Blocks: outBlocks,
}
}
type StereoFilter struct {
}
func (f StereoFilter) Process(source Source) Source {
if source.Channels == 2 { //no change
return source
}
outBlocks := make(chan []float32)
go func() {
defer close(outBlocks)
for block := range source.Blocks {
//bring any number of channels to stereo, using downmix formulas when necessary
buf := make([]float32, (len(block)/source.Channels)*2)
C.audio_multiple_channels_to_stereo((*C.float)(&block[0]), C.size_t(len(block)), (*C.float)(&buf[0]), C.int(source.Channels))
outBlocks <- buf
}
}()
return Source{
Channels: 2,
SampleRate: source.SampleRate,
Blocks: outBlocks,
}
}
type MonoFilter struct {
}
func (f MonoFilter) Process(source Source) Source {
if source.Channels == 1 { //no change
return source
}
outBlocks := make(chan []float32)
go func() {
defer close(outBlocks)
for block := range source.Blocks {
//bring any number of channels to mono, equally weighted, reusing buffer backwards
C.audio_multiple_channels_to_mono((*C.float)(&block[0]), C.size_t(len(block)), C.int(source.Channels))
outBlocks <- block[0:(len(block) / source.Channels)]
}
}()
return Source{
Channels: 1,
SampleRate: source.SampleRate,
Blocks: outBlocks,
}
}
type ResampleFilter struct {
sampleRate int
quality ResampleQuality
blockSize int
}
type ResampleQuality int
const (
BandlimitedBest ResampleQuality = gosamplerate.SRC_SINC_BEST_QUALITY
BandlimitedMedium ResampleQuality = gosamplerate.SRC_SINC_MEDIUM_QUALITY
BandlimitedFastest ResampleQuality = gosamplerate.SRC_SINC_FASTEST
ZeroOrderHold ResampleQuality = gosamplerate.SRC_ZERO_ORDER_HOLD
Linear ResampleQuality = gosamplerate.SRC_LINEAR
)
func NewResampleFilter(sampleRate int, quality ResampleQuality, blockSize int) ResampleFilter {
if blockSize == 0 {
blockSize = 1024 * 64
}
return ResampleFilter{
sampleRate: sampleRate,
quality: quality,
blockSize: blockSize,
}
}
func (f ResampleFilter) Process(source Source) Source {
if source.SampleRate == f.sampleRate { //no change
return source
}
outBlocks := make(chan []float32)
go func() {
defer close(outBlocks)
blockSize := f.blockSize * source.Channels
samplerateConverter, err := gosamplerate.New(int(f.quality), source.Channels, blockSize)
if err != nil {
log.Panic(err)
}
defer gosamplerate.Delete(samplerateConverter)
ratio := float64(f.sampleRate) / float64(source.SampleRate)
for block := range source.Blocks {
for len(block) >= blockSize {
b, err := samplerateConverter.Process(block[0:blockSize], ratio, false)
if err != nil {
log.Panic(err)
}
if len(b) > 0 {
outBlocks <- b
}
block = block[blockSize:]
}
b, err := samplerateConverter.Process(block, ratio, false)
if err != nil {
log.Panic(err)
}
if len(b) > 0 {
outBlocks <- b
}
}
b, err := samplerateConverter.Process([]float32{}, ratio, true)
if err != nil {
log.Panic(err)
}
if len(b) > 0 {
outBlocks <- b
}
}()
return Source{
Channels: source.Channels,
SampleRate: f.sampleRate,
Blocks: outBlocks,
}
}