goborator/goborator.go

143 lines
3.3 KiB
Go

package goborator
/*
#cgo pkg-config: cgaborator
#include <cgaborator.h>
void cgoCallback(uintptr_t callback_data, float* data, int size);
typedef void (*gaborator_transform_callback)(uintptr_t callback_data, float* data, int size);
*/
import "C"
import (
"fmt"
"log"
"runtime/cgo"
"unsafe"
)
type Gaborator struct {
pointer C.uintptr_t
audioBlockSize int
coefficientOutputChannel chan []float32
bandsPerOctave int
bandNumberCache int
latency int64
}
func NewGaborator(blockSize int, sampleRate float64, bandsPerOctave int, minimumFrequency, maximumFrequency, referenceFrequency float64, stepSize int) *Gaborator {
ob := &Gaborator{
pointer: C.gaborator_initialize(C.int(blockSize), C.double(sampleRate), C.int(bandsPerOctave), C.double(minimumFrequency), C.double(referenceFrequency), C.double(maximumFrequency), C.int(stepSize)),
audioBlockSize: blockSize,
bandsPerOctave: bandsPerOctave,
}
ob.bandNumberCache = ob.GetNumberOfBands()
ob.latency = int64(C.gaborator_analysis_support(ob.pointer))
return ob
}
func (g *Gaborator) GetNumberOfBands() int {
return int(C.gaborator_number_of_bands(g.pointer))
}
func (g *Gaborator) gaborTransform(audioData []float32) {
g.analyze(audioData)
}
func (g *Gaborator) GetChannel() chan []float32 {
if g.coefficientOutputChannel == nil {
g.coefficientOutputChannel = make(chan []float32, 1024)
}
return g.coefficientOutputChannel
}
func (g *Gaborator) GaborBlockTransform(source chan []float32) (channel chan []float32) {
channel = g.GetChannel()
go func() {
defer g.ProcessingFinished()
for {
block, more := <-source
if !more {
break
}
err := g.Process(block)
if err != nil {
log.Panic(err)
}
}
}()
return channel
}
func (g *Gaborator) GaborTransform(source chan float32) (channel chan []float32) {
channel = g.GetChannel()
go func() {
defer g.ProcessingFinished()
audioData := make([]float32, 0, g.audioBlockSize)
for {
f, more := <-source
if !more {
break
}
audioData = append(audioData, f)
for len(audioData) >= g.audioBlockSize {
g.gaborTransform(audioData[0:g.audioBlockSize])
audioData = audioData[:0]
}
}
}()
return channel
}
func (g *Gaborator) Process(block []float32) error {
if len(block) != g.audioBlockSize {
return fmt.Errorf("invalid block size %d != %d", len(block), g.audioBlockSize)
}
g.gaborTransform(block)
return nil
}
func (g *Gaborator) ProcessingFinished() {
if g.pointer != 0 {
C.gaborator_release(g.pointer)
g.pointer = 0
}
if g.coefficientOutputChannel != nil {
close(g.coefficientOutputChannel)
g.coefficientOutputChannel = nil
}
}
func (g *Gaborator) GetBlockSize() int {
return g.audioBlockSize
}
func (g *Gaborator) GetLatency() int64 {
return g.latency
}
func (g *Gaborator) GetBandwidth() float64 {
return 1200. / float64(g.bandsPerOctave)
}
func (g *Gaborator) analyze(block []float32) {
handle := cgo.NewHandle(g.coefficientOutputChannel)
defer handle.Delete()
C.gaborator_transform(g.pointer, (*C.float)(&block[0]), C.int(len(block)), C.gaborator_transform_callback(C.cgoCallback), C.uintptr_t(handle))
}
//export cgoCallback
func cgoCallback(ptr C.uintptr_t, data *C.float, size C.int) {
buf := make([]float32, size)
copy(buf, unsafe.Slice((*float32)(data), int(size)))
cgo.Handle(ptr).Value().(chan []float32) <- buf
}