diff --git a/CHANGELOG.md b/CHANGELOG.md index c44ba78..309ab7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - In the WebAssembly core, $WRK was messed after stereo oscillators, making modulations not work +### Changed +- The crush resolution is now in bits instead of linear range; this is a + breaking change and changes the meaning of the resolution values. But + now there are more usable values in the resolution. + ## v0.1.0 ### Added - An instrument (set of opcodes & accompanying values) can have any number of voices. diff --git a/patch.go b/patch.go index 23901bf..09e037e 100644 --- a/patch.go +++ b/patch.go @@ -233,6 +233,11 @@ func (p Patch) ParamHintString(instrIndex, unitIndex int, param string) string { return "aux3 right" } } + case "crush": + switch param { + case "resolution": + return fmt.Sprintf("%v bits", 24*float32(value)/128) + } } return "" } diff --git a/tests/expected_output/test_crush.raw b/tests/expected_output/test_crush.raw index a832cac..964de24 100644 Binary files a/tests/expected_output/test_crush.raw and b/tests/expected_output/test_crush.raw differ diff --git a/tests/expected_output/test_crush_stereo.raw b/tests/expected_output/test_crush_stereo.raw index 6cddc5e..4bd50e6 100644 Binary files a/tests/expected_output/test_crush_stereo.raw and b/tests/expected_output/test_crush_stereo.raw differ diff --git a/tests/test_crush.yml b/tests/test_crush.yml index 109d242..8fdbd84 100644 --- a/tests/test_crush.yml +++ b/tests/test_crush.yml @@ -25,6 +25,6 @@ patch: - type: mulp parameters: {stereo: 0} - type: crush - parameters: {resolution: 64, stereo: 0} + parameters: {resolution: 32, stereo: 0} - type: out parameters: {gain: 128, stereo: 1} diff --git a/tests/test_crush_stereo.yml b/tests/test_crush_stereo.yml index 142577e..8e2e67c 100644 --- a/tests/test_crush_stereo.yml +++ b/tests/test_crush_stereo.yml @@ -23,6 +23,6 @@ patch: - type: mulp parameters: {stereo: 0} - type: crush - parameters: {resolution: 32, stereo: 1} + parameters: {resolution: 8, stereo: 1} - type: out parameters: {gain: 128, stereo: 1} diff --git a/tests/wasm_test_renderer.es6 b/tests/wasm_test_renderer.es6 index 3df4e59..b74e10c 100644 --- a/tests/wasm_test_renderer.es6 +++ b/tests/wasm_test_renderer.es6 @@ -63,7 +63,7 @@ if (process.argv.length <= 3) { return 1 } - let margin = 1e-2 * (instance.exports.t.value ? 32767 : 1); + let margin = 2e-2 * (instance.exports.t.value ? 32767 : 1); var firstError = true, firstErrorPos, errorCount = 0 // we still have occasional sample wrong here or there. We only consider this a true error diff --git a/vm/compiler/templates/amd64-386/effects.asm b/vm/compiler/templates/amd64-386/effects.asm index 0275ccc..6ab474e 100644 --- a/vm/compiler/templates/amd64-386/effects.asm +++ b/vm/compiler/templates/amd64-386/effects.asm @@ -34,16 +34,19 @@ su_op_hold_holding: ;------------------------------------------------------------------------------- ; CRUSH opcode: quantize the signal to finite number of levels ;------------------------------------------------------------------------------- -; Mono: x -> e*int(x/e) +; Mono: x -> e*int(x/e) where e=2**(-24*resolution) ; Stereo: l r -> e*int(l/e) e*int(r/e) ;------------------------------------------------------------------------------- {{.Func "su_op_crush" "Opcode"}} {{- if .Stereo "crush"}} {{.Call "su_effects_stereohelper"}} {{- end}} - fdiv dword [{{.Input "crush" "resolution"}}] + xor eax, eax + {{.Call "su_nonlinear_map"}} + fxch st0, st1 + fdiv st0, st1 frndint - fmul dword [{{.Input "crush" "resolution"}}] + fmulp st1, st0 ret {{end}} diff --git a/vm/compiler/templates/wasm/effects.wat b/vm/compiler/templates/wasm/effects.wat index e419ce5..af96370 100644 --- a/vm/compiler/templates/wasm/effects.wat +++ b/vm/compiler/templates/wasm/effects.wat @@ -54,14 +54,14 @@ ;; Mono: x -> e*int(x/e) ;; Stereo: l r -> e*int(l/e) e*int(r/e) ;;------------------------------------------------------------------------------- -(func $su_op_crush (param $stereo i32) +(func $su_op_crush (param $stereo i32) (local $e f32) {{- if .Stereo "crush"}} (call $stereoHelper (local.get $stereo) (i32.const {{div (.GetOp "crush") 2}})) {{- end}} call $pop - (f32.div (call $input (i32.const {{.InputNumber "crush" "resolution"}}))) + (f32.div (local.tee $e (call $nonLinearMap (i32.const {{.InputNumber "crush" "resolution"}})))) f32.nearest - (f32.mul (call $input (i32.const {{.InputNumber "crush" "resolution"}}))) + (f32.mul (local.get $e)) call $push ) {{end}} diff --git a/vm/interpreter.go b/vm/interpreter.go index 8ebbd27..ad398cb 100644 --- a/vm/interpreter.go +++ b/vm/interpreter.go @@ -611,7 +611,8 @@ func clip(value float32) float32 { } func crush(value, amount float32) float32 { - return float32(math.Round(float64(value/amount)) * float64(amount)) + n := nonLinearMap(amount) + return float32(math.Round(float64(value/n)) * float64(n)) } func waveshape(value, amount float32) float32 {