goborator/goborator.go

148 lines
3.5 KiB
Go
Raw Permalink Normal View History

2022-01-23 20:04:09 +00:00
package goborator
2022-01-29 04:59:21 +00:00
/*
#cgo pkg-config: cgaborator
#include <cgaborator.h>
*/
2022-01-23 20:04:09 +00:00
import "C"
import (
"fmt"
2022-11-09 20:54:43 +00:00
"runtime"
"slices"
"sync"
2022-01-23 20:04:09 +00:00
"unsafe"
)
type Gaborator struct {
closeOnce sync.Once
2022-01-29 04:59:21 +00:00
pointer C.uintptr_t
audioBlockSize int
coefficientOutputChannel chan []float32
bandsPerOctave int
bandNumberCache int
2022-01-29 05:02:30 +00:00
latency int64
2022-01-23 20:04:09 +00:00
}
func NewGaborator(blockSize int, sampleRate float64, bandsPerOctave int, minimumFrequency, maximumFrequency, referenceFrequency float64, stepSize int) *Gaborator {
2022-01-29 04:59:21 +00:00
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,
2022-01-23 20:04:09 +00:00
}
2022-01-29 04:59:21 +00:00
ob.bandNumberCache = ob.GetNumberOfBands()
2022-01-29 05:02:30 +00:00
ob.latency = int64(C.gaborator_analysis_support(ob.pointer))
2022-01-29 04:59:21 +00:00
return ob
2022-01-23 20:04:09 +00:00
}
2022-01-28 23:39:12 +00:00
func (g *Gaborator) GetNumberOfBands() int {
2022-01-29 04:59:21 +00:00
return int(C.gaborator_number_of_bands(g.pointer))
2022-01-23 20:04:09 +00:00
}
func (g *Gaborator) gaborTransform(audioData []float32, outputCallback func(output []float32)) {
g.analyze(audioData, outputCallback)
2022-01-23 20:04:09 +00:00
}
2022-01-23 20:32:29 +00:00
func (g *Gaborator) GaborBlockTransform(source chan []float32, outputCallback func(output []float32)) error {
defer g.ProcessingFinished()
2022-01-26 14:08:13 +00:00
for {
block, more := <-source
if !more {
break
2022-01-23 20:04:09 +00:00
}
err := g.Process(block, outputCallback)
2022-01-30 01:42:20 +00:00
if err != nil {
return err
2022-01-30 01:42:20 +00:00
}
}
2022-01-25 12:59:35 +00:00
//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)
2022-01-23 20:04:09 +00:00
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)
2022-01-30 01:42:20 +00:00
if err != nil {
return err
2022-01-30 01:42:20 +00:00
}
}
err := g.Process(nil, outputCallback)
if err != nil {
return err
}
return nil
2022-01-23 20:04:09 +00:00
}
func (g *Gaborator) Process(block []float32, outputCallback func(output []float32)) error {
2022-01-23 20:04:09 +00:00
2022-01-30 01:11:08 +00:00
if len(block) > g.audioBlockSize {
return fmt.Errorf("invalid block size %d > %d", len(block), g.audioBlockSize)
2022-01-23 20:04:09 +00:00
}
g.gaborTransform(block, outputCallback)
2022-01-23 20:04:09 +00:00
return nil
}
2022-01-23 20:04:09 +00:00
func (g *Gaborator) ProcessingFinished() {
g.closeOnce.Do(func() {
if g.pointer != 0 {
C.gaborator_release(g.pointer)
g.pointer = 0
}
})
2022-01-23 20:04:09 +00:00
}
func (g *Gaborator) GetBlockSize() int {
return g.audioBlockSize
}
2022-01-29 05:02:30 +00:00
func (g *Gaborator) GetLatency() int64 {
return g.latency
}
2022-01-23 20:04:09 +00:00
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)
2022-01-30 01:42:20 +00:00
} 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)]))
}
}
2022-01-23 20:04:09 +00:00
}