fix(asm/wasm): oscillator phase was causing rounding errors once large enough

gopher had fixed this, but we foolishly removed it. reintroducing fix, although this could be optional only for those who really care. ultimate size optimizers could still want to get rid of it.
This commit is contained in:
Veikko Sariola 2020-12-30 21:19:27 +02:00
parent 7974f0ff82
commit c02c5c3c3d
47 changed files with 43 additions and 25 deletions

View File

@ -172,20 +172,31 @@ su_op_oscillat_normalize_note:
fmul dword [{{.Float 0.000092696138 | .Use}}] ; // st0 is now frequency fmul dword [{{.Float 0.000092696138 | .Use}}] ; // st0 is now frequency
su_op_oscillat_normalized: su_op_oscillat_normalized:
fadd dword [{{.WRK}}] fadd dword [{{.WRK}}]
fst dword [{{.WRK}}]
fadd dword [{{.Input "oscillator" "phase"}}]
{{- if .SupportsParamValue "oscillator" "type" .Sample}} {{- if .SupportsParamValue "oscillator" "type" .Sample}}
test al, byte 0x80 test al, byte 0x80
jz short su_op_oscillat_not_sample jz short su_op_oscillat_not_sample
fst dword [{{.WRK}}] ; for samples, we store the phase without mod(p,1)
{{- if or (.SupportsParamValueOtherThan "oscillator" "phase" 0) (.SupportsModulation "oscillator" "phase")}}
fadd dword [{{.Input "oscillator" "phase"}}]
{{- end}}
{{.Call "su_oscillat_sample"}} {{.Call "su_oscillat_sample"}}
jmp su_op_oscillat_shaping ; skip the rest to avoid color phase normalization and colorloading jmp su_op_oscillat_shaping ; skip the rest to avoid color phase normalization and colorloading
su_op_oscillat_not_sample: su_op_oscillat_not_sample:
{{- end}} {{- end}}
fld1 fld1 ; we need to take mod(p,1) so the frequency does not drift as the float
fadd st1, st0 fadd st1, st0 ; make no mistake: without this, there is audible drifts in oscillator pitch
fxch ; as the actual period changes once the phase becomes too big
fprem ; we actually computed mod(p+1,1) instead of mod(p,1) as the fprem takes mod
fstp st1 ; towards zero
fst dword [{{.WRK}}] ; store back the updated phase
{{- if or (.SupportsParamValueOtherThan "oscillator" "phase" 0) (.SupportsModulation "oscillator" "phase")}}
fadd dword [{{.Input "oscillator" "phase"}}]
fld1 ; this is a bit stupid, but we need to take mod(x,1) again after phase modulations
fadd st1, st0 ; as the actual oscillator functions expect x in [0,1]
fxch fxch
fprem fprem
fstp st1 fstp st1
{{- end}}
fld dword [{{.Input "oscillator" "color"}}] ; // c p fld dword [{{.Input "oscillator" "color"}}] ; // c p
; every oscillator test included if needed ; every oscillator test included if needed
{{- if .SupportsParamValue "oscillator" "type" .Sine}} {{- if .SupportsParamValue "oscillator" "type" .Sine}}

View File

@ -121,27 +121,34 @@
{{- end}} {{- end}}
(f32.store ;; update phase (f32.store ;; update phase
(global.get $WRK) (global.get $WRK)
;; Transpose calculation starts (local.tee $phase
(f32.div (f32.sub
(call $inputSigned (i32.const {{.InputNumber "oscillator" "transpose"}})) (local.tee $phase
(f32.const 0.015625) ;; Transpose calculation starts
) ;; scale back to 0 - 128 (f32.div
(f32.add (local.get $detune)) ;; add detune. detune is -1 to 1 so can detune a full note up or down at max (call $inputSigned (i32.const {{.InputNumber "oscillator" "transpose"}}))
(f32.add (select (f32.const 0.015625)
(f32.const 0) ) ;; scale back to 0 - 128
(f32.convert_i32_u (i32.load (global.get $voice))) (f32.add (local.get $detune)) ;; add detune. detune is -1 to 1 so can detune a full note up or down at max
(i32.and (local.get $flags) (i32.const 0x8)) (f32.add (select
)) ;; if lfo is not enabled, add the note number to it (f32.const 0)
(f32.mul (f32.const 0.0833333)) ;; /12, in full octaves (f32.convert_i32_u (i32.load (global.get $voice)))
(call $pow2) (i32.and (local.get $flags) (i32.const 0x8))
(f32.mul (select )) ;; if lfo is not enabled, add the note number to it
(f32.const 0.000038) ;; pretty random scaling constant to get LFOs into reasonable range. Historical reasons, goes all the way back to 4klang (f32.mul (f32.const 0.0833333)) ;; /12, in full octaves
(f32.const 0.000092696138) ;; scaling constant to get middle-C to where it should be (call $pow2)
(i32.and (local.get $flags) (i32.const 0x8)) (f32.mul (select
)) (f32.const 0.000038) ;; pretty random scaling constant to get LFOs into reasonable range. Historical reasons, goes all the way back to 4klang
(f32.add (f32.load (global.get $WRK))) ;; add the current phase of the oscillator (f32.const 0.000092696138) ;; scaling constant to get middle-C to where it should be
(i32.and (local.get $flags) (i32.const 0x8))
))
(f32.add (f32.load (global.get $WRK))) ;; add the current phase of the oscillator
)
(f32.floor (local.get $phase))
)
)
) )
(f32.add (f32.load (global.get $WRK)) (call $input (i32.const {{.InputNumber "oscillator" "phase"}}))) (f32.add (local.get $phase) (call $input (i32.const {{.InputNumber "oscillator" "phase"}})))
(local.set $phase (f32.sub (local.tee $phase) (f32.floor (local.get $phase)))) ;; phase = phase mod 1.0 (local.set $phase (f32.sub (local.tee $phase) (f32.floor (local.get $phase)))) ;; phase = phase mod 1.0
(local.set $color (call $input (i32.const {{.InputNumber "oscillator" "color"}}))) (local.set $color (call $input (i32.const {{.InputNumber "oscillator" "color"}})))
{{- if .SupportsParamValue "oscillator" "type" .Sine}} {{- if .SupportsParamValue "oscillator" "type" .Sine}}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -85,7 +85,7 @@ if (process.argv.length <= 3) {
} }
errorCount++ errorCount++
} }
if (errorCount > 100) { if (errorCount > 200) {
console.error("got different buffer than expected. First error at: "+(firstErrorPos/2|0)+(firstErrorPos%1," right"," left")); console.error("got different buffer than expected. First error at: "+(firstErrorPos/2|0)+(firstErrorPos%1," right"," left"));
return 1; return 1;
} }