package goborator /* #cgo pkg-config: cgaborator #include */ 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)])) } } }