mirror of
https://github.com/vsariola/sointu.git
synced 2026-02-15 12:43:13 -05:00
drafting
This commit is contained in:
parent
06a1fb6b52
commit
655d736149
@ -18,13 +18,12 @@ type (
|
||||
|
||||
SpecAnSettings struct {
|
||||
ChnMode SpecChnMode
|
||||
Smooth SpecSmoothing
|
||||
Smooth int
|
||||
Resolution int
|
||||
}
|
||||
|
||||
SpecChnMode int
|
||||
SpecSmoothing int
|
||||
Spectrum [2][]float32
|
||||
SpecChnMode int
|
||||
Spectrum [2][]float32
|
||||
|
||||
specTemp struct {
|
||||
power [2][]float32
|
||||
@ -42,38 +41,24 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
SpecResolutionMin = 7
|
||||
SpecResolutionMax = 16
|
||||
SpecResolutionMin = -3
|
||||
SpecResolutionMax = 3
|
||||
)
|
||||
|
||||
const (
|
||||
SpecChnModeOff SpecChnMode = iota // no spectrum analysis is done to save CPU resources
|
||||
SpecChnModeCombine // calculate a single combined spectrum for both channels
|
||||
SpecSpeedMin = -3
|
||||
SpecSpeedMax = 3
|
||||
)
|
||||
|
||||
const (
|
||||
SpecChnModeSum SpecChnMode = iota // calculate a single combined spectrum for both channels
|
||||
SpecChnModeSeparate // calculate separate spectrums for left and right channels
|
||||
NumSpecChnModes
|
||||
)
|
||||
|
||||
const (
|
||||
SpecSmoothingMedium SpecSmoothing = iota
|
||||
SpecSmoothingFast
|
||||
SpecSmoothingSlow
|
||||
|
||||
NumSpecSmoothing
|
||||
)
|
||||
|
||||
var spectrumSmoothingMap map[SpecSmoothing]float32 = map[SpecSmoothing]float32{
|
||||
SpecSmoothingSlow: 0.1,
|
||||
SpecSmoothingMedium: 0.2,
|
||||
SpecSmoothingFast: 0.4,
|
||||
}
|
||||
|
||||
func NewSpecAnalyzer(broker *Broker) *SpecAnalyzer {
|
||||
ret := &SpecAnalyzer{broker: broker}
|
||||
ret.init(SpecAnSettings{
|
||||
ChnMode: SpecChnModeCombine,
|
||||
Smooth: SpecSmoothingMedium,
|
||||
Resolution: 10,
|
||||
})
|
||||
ret.init(SpecAnSettings{})
|
||||
return ret
|
||||
}
|
||||
|
||||
@ -87,23 +72,28 @@ func (m *Model) BiquadCoeffs() (coeffs BiquadCoeffs, ok bool) {
|
||||
case "filter":
|
||||
p := m.d.Song.Patch[i].Units[u].Parameters
|
||||
f := float32(p["frequency"]) / 128
|
||||
res := float32(p["resonance"]) / 128
|
||||
f2 := f * f
|
||||
g := f2 / (2 - f2)
|
||||
a1 := 2 * (g*g - 1)
|
||||
a2 := (1 - g*(g-res))
|
||||
f *= f
|
||||
r := float32(p["resonance"]) / 128
|
||||
// in state-space, the filter has the form:
|
||||
// s(n+1) = A*s(n)+B*u, where A = [1 f;-f 1-f*r-f*f] and B = [0;f]
|
||||
// y(n) = C*s(n)+D*u, where
|
||||
// C = [low band]
|
||||
//
|
||||
// The transfer function is then H(z) = C*(zI-A)^-1*B + D
|
||||
// z*I-A = [z-1 -f; f z+f*r+f*f-1]
|
||||
// Invert it:
|
||||
// (z*I-A)^-1 = 1/det * [z+f*r+f*f-1 f; -f z-1], where det = (z-1)*(z+f*r+f*f-1)+f^2 = z^2 + z * (f*r+f*f−2) + 1-f*r
|
||||
// (z*I-A)^-1*B = 1/det * [-f*f; f*z-f]
|
||||
var a0 float32 = 1
|
||||
var a1 float32 = r*f + f*f - 2
|
||||
var a2 float32 = 1 - r*f
|
||||
var b0, b1, b2 float32
|
||||
if p["low"] == 1 {
|
||||
b0 += g * g
|
||||
b1 += 2 * g * g
|
||||
b2 += g * g
|
||||
if p["lowpass"] == 1 {
|
||||
b2 = -f * f
|
||||
}
|
||||
b0 += float32(p["high"])
|
||||
b1 += -2 * float32(p["high"])
|
||||
b2 += float32(p["high"])
|
||||
b0 += g * float32(p["band"])
|
||||
b2 += -g * float32(p["band"])
|
||||
return BiquadCoeffs{a0: 1, a1: a1, a2: a2, b0: b0, b1: b1, b2: b2}, true
|
||||
b2 -= f * float32(p["bandpass"])
|
||||
b1 += f * float32(p["bandpass"])
|
||||
return BiquadCoeffs{a0: a0, a1: a1, a2: a2, b0: b0, b1: b1, b2: b2}, true
|
||||
case "belleq":
|
||||
f := float32(m.d.Song.Patch[i].Units[u].Parameters["frequency"]) / 128
|
||||
band := float32(m.d.Song.Patch[i].Units[u].Parameters["bandwidth"]) / 128
|
||||
@ -149,14 +139,12 @@ func (s *SpecAnalyzer) handleMsg(msg MsgToSpecAn) {
|
||||
}
|
||||
switch m := msg.Data.(type) {
|
||||
case *sointu.AudioBuffer:
|
||||
if s.settings.ChnMode != SpecChnModeOff {
|
||||
buf := *m
|
||||
l := len(s.temp.window)
|
||||
// 50% overlap with the windows
|
||||
s.chunker.Process(buf, l, l>>1, func(chunk sointu.AudioBuffer) {
|
||||
TrySend(s.broker.ToModel, MsgToModel{Data: s.update(chunk)})
|
||||
})
|
||||
}
|
||||
buf := *m
|
||||
l := len(s.temp.window)
|
||||
// 50% overlap with the windows
|
||||
s.chunker.Process(buf, l, l>>1, func(chunk sointu.AudioBuffer) {
|
||||
TrySend(s.broker.ToModel, MsgToModel{Data: s.update(chunk)})
|
||||
})
|
||||
s.broker.PutAudioBuffer(m)
|
||||
default:
|
||||
// unknown message type; ignore
|
||||
@ -164,7 +152,7 @@ func (s *SpecAnalyzer) handleMsg(msg MsgToSpecAn) {
|
||||
}
|
||||
|
||||
func (a *SpecAnalyzer) init(s SpecAnSettings) {
|
||||
s.Resolution = min(max(s.Resolution, SpecResolutionMin), SpecResolutionMax)
|
||||
s.Resolution = min(max(s.Resolution, SpecResolutionMin), SpecResolutionMax) + 10
|
||||
a.settings = s
|
||||
n := 1 << s.Resolution
|
||||
a.temp = specTemp{
|
||||
@ -205,7 +193,7 @@ func (s *SpecAnalyzer) update(buf sointu.AudioBuffer) *Spectrum {
|
||||
s.process(buf, 1)
|
||||
ret[0] = append(ret[0], s.temp.power[0]...)
|
||||
ret[1] = append(ret[1], s.temp.power[1]...)
|
||||
case SpecChnModeCombine:
|
||||
case SpecChnModeSum:
|
||||
s.process(buf, 0)
|
||||
s.process(buf, 1)
|
||||
ret[0] = append(ret[0], s.temp.power[0]...)
|
||||
@ -260,7 +248,7 @@ func (sd *SpecAnalyzer) process(buf sointu.AudioBuffer, channel int) {
|
||||
vek32.MulNumber_Inplace(t2[:m-1], 2)
|
||||
// calculate difference to current spectrum and add back, multiplied by smoothing factor
|
||||
vek32.Sub_Inplace(t2, sd.temp.power[channel])
|
||||
alpha := spectrumSmoothingMap[sd.settings.Smooth]
|
||||
alpha := float32(math.Pow(2, float64(sd.settings.Smooth-SpecSpeedMax)))
|
||||
vek32.MulNumber_Inplace(t2, alpha)
|
||||
vek32.Add_Inplace(sd.temp.power[channel], t2)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user