182 lines
4.2 KiB
Go
182 lines
4.2 KiB
Go
package audio
|
|
|
|
/*
|
|
#cgo CFLAGS: -I"${SRCDIR}/../cgo" -march=native -Ofast -std=c99
|
|
#include "audio.h"
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"github.com/dh1tw/gosamplerate"
|
|
"log"
|
|
)
|
|
|
|
type Stream struct {
|
|
source chan []float32
|
|
channels int
|
|
sampleRate float64
|
|
samplesProcessed int
|
|
buffer []float32
|
|
blockSize int
|
|
}
|
|
|
|
func NewStream(source chan []float32, channels int, sampleRate float64, blockSize int) *Stream {
|
|
return &Stream{
|
|
source: source,
|
|
channels: channels,
|
|
sampleRate: sampleRate,
|
|
blockSize: blockSize,
|
|
}
|
|
}
|
|
|
|
func (s *Stream) GetChannels() int {
|
|
return s.channels
|
|
}
|
|
|
|
func (s *Stream) GetSampleRate() float64 {
|
|
return s.sampleRate
|
|
}
|
|
|
|
func (s *Stream) GetSamplesProcessed() int {
|
|
return s.samplesProcessed
|
|
}
|
|
|
|
func (s *Stream) GetAsChannel() chan float32 {
|
|
newChannel := make(chan float32)
|
|
go func() {
|
|
defer close(newChannel)
|
|
for {
|
|
v, more := s.Get()
|
|
if !more {
|
|
return
|
|
}
|
|
newChannel <- v
|
|
s.samplesProcessed++
|
|
}
|
|
}()
|
|
return newChannel
|
|
}
|
|
|
|
func (s *Stream) GetAsBlockChannel() chan []float32 {
|
|
newChannel := make(chan []float32)
|
|
|
|
go func() {
|
|
defer close(newChannel)
|
|
for buf := range s.source {
|
|
s.buffer = append(s.buffer, buf...)
|
|
for len(s.buffer) >= s.blockSize*s.channels {
|
|
newChannel <- s.buffer[0 : s.blockSize*s.channels]
|
|
s.samplesProcessed += s.blockSize * s.channels
|
|
s.buffer = s.buffer[s.blockSize*s.channels:]
|
|
}
|
|
}
|
|
|
|
if len(s.buffer) > 0 {
|
|
newChannel <- s.buffer
|
|
s.samplesProcessed += len(s.buffer)
|
|
}
|
|
s.buffer = nil
|
|
}()
|
|
return newChannel
|
|
}
|
|
|
|
func (s *Stream) Get() (float32, bool) {
|
|
var more bool
|
|
if len(s.buffer) == 0 {
|
|
s.buffer, more = <-s.source
|
|
if !more {
|
|
return 0, false
|
|
}
|
|
}
|
|
f := s.buffer[0]
|
|
s.buffer = s.buffer[1:]
|
|
s.samplesProcessed++
|
|
return f, true
|
|
}
|
|
|
|
func (s *Stream) AdvanceSeconds(seconds float64) bool {
|
|
stopAt := s.secondsIndex(seconds)
|
|
for i := 0; i < stopAt; i++ {
|
|
_, more := s.Get()
|
|
if !more {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *Stream) secondsIndex(seconds float64) int {
|
|
return int(seconds * s.sampleRate)
|
|
}
|
|
|
|
const (
|
|
RESAMPLER_QUALITY_BANDLIMITED_BEST = gosamplerate.SRC_SINC_BEST_QUALITY
|
|
RESAMPLER_QUALITY_BANDLIMITED_MEDIUM = gosamplerate.SRC_SINC_MEDIUM_QUALITY
|
|
RESAMPLER_QUALITY_BANDLIMITED_FASTEST = gosamplerate.SRC_SINC_FASTEST
|
|
RESAMPLER_QUALITY_HOLD = gosamplerate.SRC_ZERO_ORDER_HOLD
|
|
RESAMPLER_QUALITY_LINEAR = gosamplerate.SRC_LINEAR
|
|
)
|
|
|
|
func (s *Stream) DoResample(channels int, sampleRate float64, quality int) (*Stream, error) {
|
|
if channels != 1 && channels != 2 && s.channels != channels {
|
|
return nil, fmt.Errorf("cannot convert from %d channels to %d", s.channels, channels)
|
|
}
|
|
|
|
if channels == s.channels && sampleRate == s.sampleRate {
|
|
return s, nil
|
|
}
|
|
|
|
samplerateConverter, err := gosamplerate.New(quality, channels, s.blockSize*s.channels)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newChannel := make(chan []float32)
|
|
|
|
go func() {
|
|
defer gosamplerate.Delete(samplerateConverter)
|
|
defer close(newChannel)
|
|
ratio := sampleRate / s.sampleRate
|
|
|
|
for buf := range s.GetAsBlockChannel() {
|
|
if channels != s.channels && channels == 1 {
|
|
if channels == 1 {
|
|
//bring any number of channels to mono, equally weighted, reusing buffer backwards
|
|
C.audio_multiple_channels_to_mono((*C.float)(&buf[0]), C.size_t(len(buf)), C.int(s.channels))
|
|
buf = buf[0:(len(buf) / s.channels)]
|
|
} else if channels == 2 {
|
|
//bring any number of channels to stereo, using downmix formulas when necessary
|
|
out := make([]float32, (len(buf)/s.channels)*2)
|
|
C.audio_multiple_channels_to_stereo((*C.float)(&buf[0]), C.size_t(len(buf)), (*C.float)(&out[0]), C.int(s.channels))
|
|
buf = out
|
|
}
|
|
}
|
|
|
|
for len(buf) > 0 {
|
|
n := len(buf)
|
|
if n > s.blockSize*channels {
|
|
n = s.blockSize * channels
|
|
}
|
|
b, err := samplerateConverter.Process(buf[0:n], ratio, false)
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
if len(b) > 0 {
|
|
newChannel <- b
|
|
}
|
|
buf = buf[n:]
|
|
}
|
|
}
|
|
|
|
b, err := samplerateConverter.Process([]float32{}, ratio, true)
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
if len(b) > 0 {
|
|
newChannel <- b
|
|
}
|
|
}()
|
|
|
|
return NewStream(newChannel, channels, sampleRate, s.blockSize), nil
|
|
}
|