Update to go 1.21, use callbacks instead of channel output

This commit is contained in:
DataHoarder 2023-10-15 16:24:06 +02:00
parent 6476c8414b
commit 0de09fa45e
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
5 changed files with 72 additions and 90 deletions

View file

@ -5,7 +5,7 @@ name: build
steps: steps:
- name: build - name: build
image: golang:1.18-bullseye image: golang:1.21-bullseye
commands: commands:
- DEBIAN_FRONTEND=noninteractive apt update - DEBIAN_FRONTEND=noninteractive apt update
- DEBIAN_FRONTEND=noninteractive apt install -y git build-essential cmake make - DEBIAN_FRONTEND=noninteractive apt install -y git build-essential cmake make

View file

@ -1,4 +1,4 @@
goborator is Copyright (C) 2022 WeebDataHoarder. goborator is Copyright (C) 2023 WeebDataHoarder.
License to distribute and modify the code is hereby granted under the License to distribute and modify the code is hereby granted under the
terms of the GNU Affero General Public License, version 3 (henceforth, terms of the GNU Affero General Public License, version 3 (henceforth,

2
go.mod
View file

@ -1,5 +1,5 @@
module git.gammaspectra.live/S.O.N.G/goborator module git.gammaspectra.live/S.O.N.G/goborator
go 1.18 go 1.21
require golang.org/x/exp v0.0.0-20221212164502-fae10dda9338 require golang.org/x/exp v0.0.0-20221212164502-fae10dda9338

View file

@ -7,13 +7,14 @@ package goborator
import "C" import "C"
import ( import (
"fmt" "fmt"
"golang.org/x/exp/slices"
"log"
"runtime" "runtime"
"slices"
"sync"
"unsafe" "unsafe"
) )
type Gaborator struct { type Gaborator struct {
closeOnce sync.Once
pointer C.uintptr_t pointer C.uintptr_t
audioBlockSize int audioBlockSize int
coefficientOutputChannel chan []float32 coefficientOutputChannel chan []float32
@ -37,97 +38,79 @@ func (g *Gaborator) GetNumberOfBands() int {
return int(C.gaborator_number_of_bands(g.pointer)) return int(C.gaborator_number_of_bands(g.pointer))
} }
func (g *Gaborator) gaborTransform(audioData []float32) { func (g *Gaborator) gaborTransform(audioData []float32, outputCallback func(output []float32)) {
g.analyze(audioData) g.analyze(audioData, outputCallback)
} }
func (g *Gaborator) GetChannel() chan []float32 { func (g *Gaborator) GaborBlockTransform(source chan []float32, outputCallback func(output []float32)) error {
defer g.ProcessingFinished()
if g.coefficientOutputChannel == nil { for {
g.coefficientOutputChannel = make(chan []float32, 1024) block, more := <-source
if !more {
break
}
err := g.Process(block, outputCallback)
if err != nil {
return err
}
} }
return g.coefficientOutputChannel //finish
err := g.Process(nil, outputCallback)
if err != nil {
return err
}
return nil
} }
func (g *Gaborator) GaborBlockTransform(source chan []float32) (channel chan []float32) { func (g *Gaborator) GaborTransform(source chan float32, outputCallback func(output []float32)) error {
defer g.ProcessingFinished()
audioData := make([]float32, 0, g.audioBlockSize)
channel = g.GetChannel() for {
go func() { f, more := <-source
defer g.ProcessingFinished() if !more {
break
for {
block, more := <-source
if !more {
break
}
err := g.Process(block)
if err != nil {
log.Panic(err)
}
} }
audioData = append(audioData, f)
//finish for len(audioData) >= g.audioBlockSize {
err := g.Process(nil) g.gaborTransform(audioData[0:g.audioBlockSize], outputCallback)
audioData = audioData[:0]
}
}
//finish
if len(audioData) > 0 {
err := g.Process(audioData, outputCallback)
if err != nil { if err != nil {
log.Panic(err) return err
} }
}() }
err := g.Process(nil, outputCallback)
if err != nil {
return err
}
return channel return nil
} }
func (g *Gaborator) GaborTransform(source chan float32) (channel chan []float32) { func (g *Gaborator) Process(block []float32, outputCallback func(output []float32)) error {
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]
}
}
//finish
if len(audioData) > 0 {
err := g.Process(audioData)
if err != nil {
log.Panic(err)
}
}
err := g.Process(nil)
if err != nil {
log.Panic(err)
}
}()
return channel
}
func (g *Gaborator) Process(block []float32) error {
if len(block) > g.audioBlockSize { if len(block) > g.audioBlockSize {
return fmt.Errorf("invalid block size %d > %d", len(block), g.audioBlockSize) return fmt.Errorf("invalid block size %d > %d", len(block), g.audioBlockSize)
} }
g.gaborTransform(block) g.gaborTransform(block, outputCallback)
return nil return nil
} }
func (g *Gaborator) ProcessingFinished() { func (g *Gaborator) ProcessingFinished() {
if g.pointer != 0 { g.closeOnce.Do(func() {
C.gaborator_release(g.pointer) if g.pointer != 0 {
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 { func (g *Gaborator) GetBlockSize() int {
@ -142,27 +125,23 @@ func (g *Gaborator) GetBandwidth() float64 {
return 1200. / float64(g.bandsPerOctave) return 1200. / float64(g.bandsPerOctave)
} }
func (g *Gaborator) analyze(block []float32) { func (g *Gaborator) analyze(input []float32, outputCallback func(output []float32)) {
var returnSize C.size_t var returnSize C.size_t
var sliceSize C.size_t var sliceSize C.size_t
var returnData *C.float var returnData *C.float
if len(block) == 0 { if len(input) == 0 {
returnData = C.gaborator_transform(g.pointer, (*C.float)(nil), C.int64_t(0), &returnSize, &sliceSize) returnData = C.gaborator_transform(g.pointer, (*C.float)(nil), C.int64_t(0), &returnSize, &sliceSize)
} else { } else {
defer runtime.KeepAlive(block) defer runtime.KeepAlive(input)
returnData = C.gaborator_transform(g.pointer, (*C.float)(&block[0]), C.int64_t(len(block)), &returnSize, &sliceSize) returnData = C.gaborator_transform(g.pointer, (*C.float)(&input[0]), C.int64_t(len(input)), &returnSize, &sliceSize)
} }
if returnData != nil && returnSize > 0 { if returnData != nil && returnSize > 0 {
g.outputResult(unsafe.Slice((*float32)(returnData), uint64(returnSize)), int(sliceSize)) block := unsafe.Slice((*float32)(returnData), uint64(returnSize))
}
} for i := 0; i < len(block); i += int(sliceSize) {
outputCallback(slices.Clone(block[i : i+int(sliceSize)]))
func (g *Gaborator) outputResult(block []float32, sliceSize int) { }
buf := slices.Clone(block)
for i := 0; i < len(block); i += sliceSize {
g.coefficientOutputChannel <- buf[i : i+sliceSize]
} }
} }

View file

@ -2,8 +2,8 @@ package goborator
import ( import (
"fmt" "fmt"
"golang.org/x/exp/slices"
"os" "os"
"slices"
"testing" "testing"
"time" "time"
"unsafe" "unsafe"
@ -40,8 +40,11 @@ func TestGoborator(t *testing.T) {
start := time.Now() start := time.Now()
var data [][]float32 var data [][]float32
for c := range ob.GaborBlockTransform(channel) { err = ob.GaborBlockTransform(channel, func(output []float32) {
data = append(data, c) data = append(data, output)
})
if err != nil {
t.Fatal(err)
} }
end := time.Now().Sub(start).Milliseconds() end := time.Now().Sub(start).Milliseconds()