148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
package goborator
|
|
|
|
/*
|
|
#cgo pkg-config: cgaborator
|
|
#include <cgaborator.h>
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"slices"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
type Gaborator struct {
|
|
closeOnce sync.Once
|
|
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, outputCallback func(output []float32)) {
|
|
g.analyze(audioData, outputCallback)
|
|
}
|
|
|
|
func (g *Gaborator) GaborBlockTransform(source chan []float32, outputCallback func(output []float32)) error {
|
|
defer g.ProcessingFinished()
|
|
|
|
for {
|
|
block, more := <-source
|
|
if !more {
|
|
break
|
|
}
|
|
err := g.Process(block, outputCallback)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
//finish
|
|
err := g.Process(nil, outputCallback)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *Gaborator) GaborTransform(source chan float32, outputCallback func(output []float32)) error {
|
|
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], outputCallback)
|
|
audioData = audioData[:0]
|
|
}
|
|
}
|
|
//finish
|
|
if len(audioData) > 0 {
|
|
err := g.Process(audioData, outputCallback)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
err := g.Process(nil, outputCallback)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *Gaborator) Process(block []float32, outputCallback func(output []float32)) error {
|
|
|
|
if len(block) > g.audioBlockSize {
|
|
return fmt.Errorf("invalid block size %d > %d", len(block), g.audioBlockSize)
|
|
}
|
|
g.gaborTransform(block, outputCallback)
|
|
return nil
|
|
}
|
|
|
|
func (g *Gaborator) ProcessingFinished() {
|
|
g.closeOnce.Do(func() {
|
|
if g.pointer != 0 {
|
|
C.gaborator_release(g.pointer)
|
|
g.pointer = 0
|
|
}
|
|
})
|
|
}
|
|
|
|
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(input []float32, outputCallback func(output []float32)) {
|
|
var returnSize C.size_t
|
|
var sliceSize C.size_t
|
|
var returnData *C.float
|
|
|
|
if len(input) == 0 {
|
|
returnData = C.gaborator_transform(g.pointer, (*C.float)(nil), C.int64_t(0), &returnSize, &sliceSize)
|
|
} else {
|
|
returnData = C.gaborator_transform(g.pointer, (*C.float)(unsafe.SliceData(input)), C.int64_t(len(input)), &returnSize, &sliceSize)
|
|
runtime.KeepAlive(input)
|
|
}
|
|
|
|
if returnData != nil && returnSize > 0 {
|
|
block := unsafe.Slice((*float32)(returnData), uint64(returnSize))
|
|
|
|
for i := 0; i < len(block); i += int(sliceSize) {
|
|
outputCallback(slices.Clone(block[i : i+int(sliceSize)]))
|
|
}
|
|
}
|
|
}
|