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 }