Use c-gaborator callback system
This commit is contained in:
parent
e6cb68032d
commit
c3226aff5e
|
@ -4,9 +4,11 @@ Simple Gaborator cgo implementation.
|
|||
|
||||
Requires [c-gaborator](https://git.gammaspectra.live/S.O.N.G/c-gaborator) installed.
|
||||
```shell
|
||||
git clone https://git.gammaspectra.live/S.O.N.G/c-gaborator
|
||||
git clone --depth 1 https://git.gammaspectra.live/S.O.N.G/c-gaborator
|
||||
cd c-gaborator && make build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-march=native" -DCMAKE_C_FLAGS_RELEASE="-march=native" \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr"
|
||||
make
|
||||
sudo make install
|
||||
```
|
160
goborator.go
160
goborator.go
|
@ -1,132 +1,45 @@
|
|||
package goborator
|
||||
|
||||
// #cgo pkg-config: cgaborator
|
||||
// #include <cgaborator.h>
|
||||
/*
|
||||
#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 unsafe.Pointer
|
||||
latency int64
|
||||
sampleRate float64
|
||||
audioBlockSize int
|
||||
bandcenterCache []float32
|
||||
firstBandCache int
|
||||
coefficientOutputChannel chan []float32
|
||||
frequencyBinTimeStepSize int
|
||||
bandsPerOctave int
|
||||
coefficients [][]float32
|
||||
coefficientIndexOffset int
|
||||
mostRecentCoefficentIndex int
|
||||
pointer C.uintptr_t
|
||||
audioBlockSize int
|
||||
coefficientOutputChannel chan []float32
|
||||
bandsPerOctave int
|
||||
bandNumberCache int
|
||||
}
|
||||
|
||||
func NewGaborator(blockSize int, sampleRate float64, bandsPerOctave int, minimumFrequency, maximumFrequency, referenceFrequency float64, stepSize int) *Gaborator {
|
||||
g := &Gaborator{
|
||||
pointer: unsafe.Pointer(C.gaborator_initialize(C.double(sampleRate), C.int(bandsPerOctave), C.double(minimumFrequency), C.double(referenceFrequency), C.double(maximumFrequency))),
|
||||
sampleRate: sampleRate,
|
||||
audioBlockSize: blockSize,
|
||||
frequencyBinTimeStepSize: stepSize,
|
||||
bandsPerOctave: bandsPerOctave,
|
||||
mostRecentCoefficentIndex: 0,
|
||||
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,
|
||||
}
|
||||
|
||||
g.latency = int64(C.gaborator_get_anal_support(g.pointer))
|
||||
g.bandcenterCache = g.getBandcenters()
|
||||
|
||||
g.firstBandCache = g.firstBand()
|
||||
|
||||
coefficientSize := (g.latency + 2*int64(blockSize)) / int64(stepSize)
|
||||
g.coefficients = make([][]float32, coefficientSize)
|
||||
for i := range g.coefficients {
|
||||
g.coefficients[i] = make([]float32, g.GetNumberOfBands())
|
||||
}
|
||||
g.coefficientIndexOffset = 0
|
||||
|
||||
return g
|
||||
ob.bandNumberCache = ob.GetNumberOfBands()
|
||||
return ob
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetNumberOfBands() int {
|
||||
numberOfBands := 0
|
||||
for _, e := range g.bandcenterCache {
|
||||
if e > 0 {
|
||||
numberOfBands++
|
||||
}
|
||||
}
|
||||
|
||||
return numberOfBands
|
||||
}
|
||||
|
||||
func (g *Gaborator) firstBand() int {
|
||||
|
||||
for i, e := range g.bandcenterCache {
|
||||
if e > 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (g *Gaborator) bandCenters(bandIndex int) float32 {
|
||||
return g.bandcenterCache[bandIndex+g.firstBandCache]
|
||||
}
|
||||
|
||||
func (g *Gaborator) getBandcenters() []float32 {
|
||||
result := make([]float32, int(C.gaborator_bandcenters_array_length(g.pointer)))
|
||||
C.gaborator_bandcenters(g.pointer, (*C.float)(&result[0]))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func float32Max(a, b float32) float32 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
return int(C.gaborator_number_of_bands(g.pointer))
|
||||
}
|
||||
|
||||
func (g *Gaborator) gaborTransform(audioData []float32) {
|
||||
|
||||
analysisResult := g.analyze(audioData)
|
||||
|
||||
//The analysis result consists of a float array with three values:
|
||||
// a frequency band index [i] (always an integer)
|
||||
// an audio sample index [i+1] (expressed in audio samples)
|
||||
// a magnitude value [i+2] (the magnitude value)
|
||||
for i := 0; i < len(analysisResult); i += 3 {
|
||||
band := int(analysisResult[i])
|
||||
audioSample := int(analysisResult[i+1])
|
||||
coefficient := float32(analysisResult[i+2])
|
||||
|
||||
coefficientIndex := audioSample/g.frequencyBinTimeStepSize - g.coefficientIndexOffset
|
||||
bandIndex := band - g.firstBandCache
|
||||
|
||||
circularIndex := coefficientIndex % len(g.coefficients)
|
||||
|
||||
// The first results have a negative audio sample index
|
||||
// ignore these
|
||||
if coefficientIndex > 0 && bandIndex < len(g.coefficients[circularIndex]) {
|
||||
|
||||
// If a new index is reached, save the old (fixed) coefficents in the history
|
||||
// Fill the array with zeros to get the max
|
||||
if coefficientIndex > g.mostRecentCoefficentIndex && coefficientIndex > len(g.coefficients) {
|
||||
// keep the new maximum
|
||||
g.mostRecentCoefficentIndex = coefficientIndex
|
||||
// "copy" the oldest data to the history
|
||||
// the slice can be reused thanks to the oldest being filled with zeros just after
|
||||
g.coefficientOutputChannel <- g.coefficients[circularIndex]
|
||||
// fill the oldest with zeros
|
||||
g.coefficients[circularIndex] = make([]float32, len(g.coefficients[circularIndex]))
|
||||
}
|
||||
// due to reduction in precision (from audio sample accuracy to steps) multiple
|
||||
// magnitudes could be placed in the same stepIndex, bandIndex pair.
|
||||
// We take the maximum magnitudes value.
|
||||
g.coefficients[circularIndex][bandIndex] = float32Max(g.coefficients[circularIndex][bandIndex], coefficient)
|
||||
}
|
||||
}
|
||||
g.analyze(audioData)
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetChannel() chan []float32 {
|
||||
|
@ -191,9 +104,9 @@ func (g *Gaborator) Process(block []float32) error {
|
|||
return nil
|
||||
}
|
||||
func (g *Gaborator) ProcessingFinished() {
|
||||
if g.pointer != nil {
|
||||
if g.pointer != 0 {
|
||||
C.gaborator_release(g.pointer)
|
||||
g.pointer = nil
|
||||
g.pointer = 0
|
||||
}
|
||||
if g.coefficientOutputChannel != nil {
|
||||
close(g.coefficientOutputChannel)
|
||||
|
@ -201,32 +114,21 @@ func (g *Gaborator) ProcessingFinished() {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetStepSize() int {
|
||||
return g.frequencyBinTimeStepSize
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetBlockSize() int {
|
||||
return g.audioBlockSize
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetSampleRate() float64 {
|
||||
return g.sampleRate
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetBandwidth() float64 {
|
||||
return 1200. / float64(g.bandsPerOctave)
|
||||
}
|
||||
|
||||
func (g *Gaborator) GetLatency() int64 {
|
||||
return g.latency
|
||||
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))
|
||||
}
|
||||
|
||||
func (g *Gaborator) analyze(block []float32) []C.float {
|
||||
//log.Printf("analyze block len %d", len(block))
|
||||
C.gaborator_analyze(g.pointer, (*C.float)(&block[0]), C.int(len(block)))
|
||||
cSize := uintptr(C.gaborator_get_array_length(g.pointer))
|
||||
|
||||
//log.Print(cSize)
|
||||
ptr := (*C.float)(C.gaborator_get_array(g.pointer))
|
||||
return unsafe.Slice(ptr, cSize)
|
||||
//export cgoCallback
|
||||
func cgoCallback(ptr C.uintptr_t, data *C.float, size C.int) {
|
||||
cgo.Handle(ptr).Value().(chan []float32) <- unsafe.Slice((*float32)(data), int(size))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGoborator(t *testing.T) {
|
||||
|
@ -34,9 +35,13 @@ func TestGoborator(t *testing.T) {
|
|||
}()
|
||||
|
||||
var i = 0
|
||||
for c := range ob.GaborTransform(channel) {
|
||||
fmt.Printf("%d: %+F\n", i, c)
|
||||
start := time.Now()
|
||||
//for c := range ob.GaborTransform(channel) {
|
||||
for _ = range ob.GaborTransform(channel) {
|
||||
//fmt.Printf("%d: %+F\n", i, c)
|
||||
i++
|
||||
}
|
||||
|
||||
fmt.Printf("%d, %dms", i, time.Now().Sub(start).Milliseconds())
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue