(module {{- /* ;------------------------------------------------------------------------------ ; Patterns ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_patterns"}} {{- $m := .}} {{- range .Patterns}} {{- range .}} {{- $.DataB .}} {{- end}} {{- end}} {{- /* ;------------------------------------------------------------------------------ ; Tracks ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_tracks"}} {{- $m := .}} {{- range .Sequences}} {{- range .}} {{- $.DataB .}} {{- end}} {{- end}} {{- /* ;------------------------------------------------------------------------------ ; The code for this patch, basically indices to vm jump table ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_patch_code"}} {{- range .Commands}} {{- $.DataB .}} {{- end}} {{- /* ;------------------------------------------------------------------------------- ; The parameters / inputs to each opcode ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_patch_parameters"}} {{- range .Values}} {{- $.DataB .}} {{- end}} {{- /* ;------------------------------------------------------------------------------- ; Delay times ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_delay_times"}} {{- range .DelayTimes}} {{- $.DataW .}} {{- end}} {{- /* ;------------------------------------------------------------------------------- ; The number of transformed parameters each opcode takes ;------------------------------------------------------------------------------- */}} {{- .SetDataLabel "su_vm_transformcounts"}} {{- range .Instructions}} {{- $.TransformCount . | $.ToByte | $.DataB}} {{- end}} {{- /* ;------------------------------------------------------------------------------- ; Allocate memory for stack. ; Stack of 64 float signals is enough for everybody... right? ; Note: as the stack grows _downwards_ the label is _after_ stack ;------------------------------------------------------------------------------- */}} {{- .Align}} {{- .Block 256}} {{- .SetBlockLabel "su_stack"}} {{- /* ;------------------------------------------------------------------------------- ; Allocate memory for transformed values. ;------------------------------------------------------------------------------- */}} {{- .Align}} {{- .SetBlockLabel "su_transformedvalues"}} {{- .Block 32}} {{- /* ;------------------------------------------------------------------------------- ; Uninitialized memory for synth, delaylines & outputbuffer ;------------------------------------------------------------------------------- */}} {{- .Align}} {{- if ne .VoiceTrackBitmask 0}} {{- .SetBlockLabel "su_trackcurrentvoice"}} {{- .Block 32}} {{- end}} {{- .Align}} {{- .SetBlockLabel "su_hold_override"}} {{- .Block 32}} {{- .SetBlockLabel "su_synth"}} {{- .Block 32}} {{- .SetBlockLabel "su_globalports"}} {{- .Block 32}} {{- .SetBlockLabel "su_voices"}} {{- .Block 131072}} {{- .Align}} {{- .SetBlockLabel "su_delaylines"}} {{- .Block (int (mul 262156 .Song.Patch.NumDelayLines))}} {{- .Align}} {{- .SetBlockLabel "su_outputbuffer"}} {{- if .Output16Bit}} {{- .Block (int (mul .PatternLength .SequenceLength .Song.SamplesPerRow 4))}} {{- else}} {{- .Block (int (mul .PatternLength .SequenceLength .Song.SamplesPerRow 8))}} {{- end}} {{- .SetBlockLabel "su_outputend"}} {{- /* ;------------------------------------------------------------------------------- ; Pow function ( initialized below ) ;------------------------------------------------------------------------------- */}} {{- .Align}} {{- .SetBlockLabel "math_pow_1"}} {{- .Block 262}} {{- .SetBlockLabel "math_pow_2"}} {{- .Block 240}} ;;------------------------------------------------------------------------------ ;; Import the difficult math functions from javascript ;; (seriously now, it's 2020) ;;------------------------------------------------------------------------------ (func $log2 (param $0 f32) (result f32) (local $1 i32) (local $2 f64) (local $3 i32) (local $4 i32) (local $5 f64) block $~lib/util/math/log2f_lut|inlined.0 (result f32) local.get $0 i32.reinterpret_f32 local.tee $1 i32.const 8388608 i32.sub i32.const 2130706432 i32.ge_u if f32.const -inf local.get $1 i32.const 1 i32.shl i32.eqz br_if $~lib/util/math/log2f_lut|inlined.0 drop local.get $0 local.get $1 i32.const 2139095040 i32.eq br_if $~lib/util/math/log2f_lut|inlined.0 drop local.get $1 i32.const 31 i32.shr_u local.get $1 i32.const 1 i32.shl i32.const -16777216 i32.ge_u i32.or if local.get $0 local.get $0 f32.sub local.tee $0 local.get $0 f32.div br $~lib/util/math/log2f_lut|inlined.0 end local.get $0 f32.const 8388608 f32.mul i32.reinterpret_f32 i32.const 192937984 i32.sub local.set $1 end local.get $1 i32.const 1060306944 i32.sub local.tee $3 i32.const 19 i32.shr_u i32.const 15 i32.and i32.const 4 i32.shl i32.const {{index .Labels "math_pow_1"}} i32.add local.set $4 local.get $1 local.get $3 i32.const -8388608 i32.and i32.sub f32.reinterpret_i32 f64.promote_f32 local.get $4 f64.load f64.mul f64.const 1 f64.sub local.tee $2 local.get $2 f64.mul local.set $5 local.get $2 f64.const 0.4811247078767291 f64.mul f64.const -0.7213476299867769 f64.add local.get $5 f64.const -0.36051725506874704 f64.mul f64.add local.get $5 f64.mul local.get $2 f64.const 1.4426950186867042 f64.mul local.get $4 f64.load offset=8 local.get $3 i32.const 23 i32.shr_s f64.convert_i32_s f64.add f64.add f64.add f32.demote_f64 end ) (func $~lib/math/NativeMathf.pow (param $0 f32) (param $1 f32) (result f32) (local $2 i32) (local $3 f64) (local $4 i32) (local $5 i64) (local $6 i32) (local $7 i32) (local $8 f64) local.get $1 f32.abs f32.const 2 f32.le if local.get $1 f32.const 2 f32.eq if local.get $0 local.get $0 f32.mul return end local.get $1 f32.const 0.5 f32.eq if local.get $0 f32.sqrt f32.abs f32.const inf local.get $0 f32.const -inf f32.ne select return end local.get $1 f32.const -1 f32.eq if f32.const 1 local.get $0 f32.div return end local.get $1 f32.const 1 f32.eq if local.get $0 return end local.get $1 f32.const 0 f32.eq if f32.const 1 return end end block $~lib/util/math/powf_lut|inlined.0 (result f32) local.get $1 i32.reinterpret_f32 local.tee $7 i32.const 1 i32.shl i32.const 1 i32.sub i32.const -16777217 i32.ge_u local.tee $6 local.get $0 i32.reinterpret_f32 local.tee $2 i32.const 8388608 i32.sub i32.const 2130706432 i32.ge_u i32.or if local.get $6 if f32.const 1 local.get $7 i32.const 1 i32.shl i32.eqz br_if $~lib/util/math/powf_lut|inlined.0 drop f32.const nan:0x400000 local.get $2 i32.const 1065353216 i32.eq br_if $~lib/util/math/powf_lut|inlined.0 drop local.get $0 local.get $1 f32.add local.get $7 i32.const 1 i32.shl i32.const -16777216 i32.gt_u local.get $2 i32.const 1 i32.shl i32.const -16777216 i32.gt_u i32.or br_if $~lib/util/math/powf_lut|inlined.0 drop f32.const nan:0x400000 local.get $2 i32.const 1 i32.shl i32.const 2130706432 i32.eq br_if $~lib/util/math/powf_lut|inlined.0 drop f32.const 0 local.get $7 i32.const 31 i32.shr_u i32.eqz local.get $2 i32.const 1 i32.shl i32.const 2130706432 i32.lt_u i32.eq br_if $~lib/util/math/powf_lut|inlined.0 drop local.get $1 local.get $1 f32.mul br $~lib/util/math/powf_lut|inlined.0 end local.get $2 i32.const 1 i32.shl i32.const 1 i32.sub i32.const -16777217 i32.ge_u if f32.const 1 local.get $0 local.get $0 f32.mul local.tee $0 f32.neg local.get $0 local.get $2 i32.const 31 i32.shr_u if (result i32) block $~lib/util/math/checkintf|inlined.0 (result i32) i32.const 0 local.get $7 i32.const 23 i32.shr_u i32.const 255 i32.and local.tee $2 i32.const 127 i32.lt_u br_if $~lib/util/math/checkintf|inlined.0 drop i32.const 2 local.get $2 i32.const 150 i32.gt_u br_if $~lib/util/math/checkintf|inlined.0 drop i32.const 0 i32.const 1 i32.const 150 local.get $2 i32.sub i32.shl local.tee $2 i32.const 1 i32.sub local.get $7 i32.and br_if $~lib/util/math/checkintf|inlined.0 drop i32.const 1 local.get $2 local.get $7 i32.and br_if $~lib/util/math/checkintf|inlined.0 drop i32.const 2 end i32.const 1 i32.eq else i32.const 0 end select local.tee $0 f32.div local.get $0 local.get $7 i32.const 31 i32.shr_u select br $~lib/util/math/powf_lut|inlined.0 end local.get $2 i32.const 31 i32.shr_u if block $~lib/util/math/checkintf|inlined.1 (result i32) i32.const 0 local.get $7 i32.const 23 i32.shr_u i32.const 255 i32.and local.tee $4 i32.const 127 i32.lt_u br_if $~lib/util/math/checkintf|inlined.1 drop i32.const 2 local.get $4 i32.const 150 i32.gt_u br_if $~lib/util/math/checkintf|inlined.1 drop i32.const 0 i32.const 1 i32.const 150 local.get $4 i32.sub i32.shl local.tee $4 i32.const 1 i32.sub local.get $7 i32.and br_if $~lib/util/math/checkintf|inlined.1 drop i32.const 1 local.get $4 local.get $7 i32.and br_if $~lib/util/math/checkintf|inlined.1 drop i32.const 2 end local.tee $4 i32.eqz if local.get $0 local.get $0 f32.sub local.tee $0 local.get $0 f32.div br $~lib/util/math/powf_lut|inlined.0 end i32.const 65536 i32.const 0 local.get $4 i32.const 1 i32.eq select local.set $4 local.get $2 i32.const 2147483647 i32.and local.set $2 end local.get $2 i32.const 8388608 i32.lt_u if (result i32) local.get $0 f32.const 8388608 f32.mul i32.reinterpret_f32 i32.const 2147483647 i32.and i32.const 192937984 i32.sub else local.get $2 end local.set $2 end local.get $2 local.get $2 i32.const 1060306944 i32.sub local.tee $2 i32.const -8388608 i32.and local.tee $6 i32.sub f32.reinterpret_i32 f64.promote_f32 local.get $2 i32.const 19 i32.shr_u i32.const 15 i32.and i32.const 4 i32.shl i32.const {{index .Labels "math_pow_1"}} i32.add local.tee $2 f64.load f64.mul f64.const 1 f64.sub local.tee $3 local.get $3 f64.mul local.set $8 local.get $1 f64.promote_f32 local.get $3 f64.const 0.288457581109214 f64.mul f64.const -0.36092606229713164 f64.add local.get $8 local.get $8 f64.mul f64.mul local.get $3 f64.const 1.4426950408774342 f64.mul local.get $2 f64.load offset=8 local.get $6 i32.const 23 i32.shr_s f64.convert_i32_s f64.add f64.add local.get $3 f64.const 0.480898481472577 f64.mul f64.const -0.7213474675006291 f64.add local.get $8 f64.mul f64.add f64.add f64.mul local.tee $3 i64.reinterpret_f64 i64.const 47 i64.shr_u i64.const 65535 i64.and i64.const 32959 i64.ge_u if f32.const -1584563250285286751870879e5 f32.const 1584563250285286751870879e5 local.get $4 select f32.const 1584563250285286751870879e5 f32.mul local.get $3 f64.const 127.99999995700433 f64.gt br_if $~lib/util/math/powf_lut|inlined.0 drop f32.const -2.524354896707238e-29 f32.const 2.524354896707238e-29 local.get $4 select f32.const 2.524354896707238e-29 f32.mul local.get $3 f64.const -150 f64.le br_if $~lib/util/math/powf_lut|inlined.0 drop end local.get $3 f64.const 211106232532992 f64.add local.tee $8 i64.reinterpret_f64 local.set $5 local.get $3 local.get $8 f64.const 211106232532992 f64.sub f64.sub local.tee $3 f64.const 0.6931471806916203 f64.mul f64.const 1 f64.add local.get $3 f64.const 0.05550361559341535 f64.mul f64.const 0.2402284522445722 f64.add local.get $3 local.get $3 f64.mul f64.mul f64.add local.get $5 i32.wrap_i64 i32.const 31 i32.and i32.const 3 i32.shl i32.const {{add (index .Labels "math_pow_1") 256}} i32.add i64.load local.get $4 i64.extend_i32_u local.get $5 i64.add i64.const 47 i64.shl i64.add f64.reinterpret_i64 f64.mul f32.demote_f64 end ) (func $pow (param $0 f32) (param $1 f32) (result f32) local.get $0 local.get $1 call $~lib/math/NativeMathf.pow ) (export "pow" (func $pow)) (func $sin (param $0 f32) (result f32) (local $1 f32) local.get $0 f32.const 0.31830987334251404 f32.mul local.tee $0 f32.floor local.set $1 local.get $0 local.get $1 f32.sub local.tee $0 f32.const 1 local.get $0 f32.sub f32.mul local.tee $0 local.get $0 f32.const 3.5999999046325684 f32.mul f32.const 3.0999999046325684 f32.add f32.mul local.tee $0 f32.neg local.get $0 local.get $1 i32.trunc_sat_f32_s i32.const 1 i32.and select ) ;;------------------------------------------------------------------------------ ;; Types. Only useful to define the jump table type, which is ;; (int stereo) void ;;------------------------------------------------------------------------------ (type $opcode_func_signature (func (param i32))) ;;------------------------------------------------------------------------------ ;; The one and only memory ;;------------------------------------------------------------------------------ (memory (export "m") {{.MemoryPages}}) ;;------------------------------------------------------------------------------ ;; Globals. Putting all with same initialization value should compress most ;;------------------------------------------------------------------------------ (global $WRK (mut i32) (i32.const 0)) (global $COM (mut i32) (i32.const 0)) (global $VAL (mut i32) (i32.const 0)) {{- if .SupportsPolyphony}} (global $COM_instr_start (mut i32) (i32.const 0)) (global $VAL_instr_start (mut i32) (i32.const 0)) {{- end}} {{- if .HasOp "delay"}} (global $delayWRK (mut i32) (i32.const 0)) {{- end}} (global $globaltick (export "tick") (mut i32) (i32.const 0)) (global $row (export "row") (mut i32) (i32.const 0)) (global $pattern (export "pattern") (mut i32) (i32.const 0)) (global $sample (export "sample") (mut i32) (i32.const 0)) (global $voice (mut i32) (i32.const 0)) (global $voicesRemain (mut i32) (i32.const 0)) (global $randseed (mut i32) (i32.const 1)) (global $sp (mut i32) (i32.const {{index .Labels "su_stack"}})) (global $outputBufPtr (export "outputBufPtr") (mut i32) (i32.const {{index .Labels "su_outputbuffer"}})) ;; TODO: only export start and length with certain compiler options; in demo use, they can be hard coded ;; in the intro (global $outputStart (export "s") i32 (i32.const {{index .Labels "su_outputbuffer"}})) (global $outputLength (export "l") i32 (i32.const {{if .Output16Bit}}{{mul .PatternLength .SequenceLength .Song.SamplesPerRow 4}}{{else}}{{mul .PatternLength .SequenceLength .Song.SamplesPerRow 8}}{{end}})) (global $output16bit (export "t") i32 (i32.const {{if .Output16Bit}}1{{else}}0{{end}})) ;;------------------------------------------------------------------------------ ;; Functions to emulate FPU stack in software ;;------------------------------------------------------------------------------ (func $peek (result f32) (f32.load (global.get $sp)) ) (func $peek2 (result f32) (f32.load offset=4 (global.get $sp)) ) (func $pop (result f32) (call $peek) (global.set $sp (i32.add (global.get $sp) (i32.const 4))) ) (func $push (param $value f32) (global.set $sp (i32.sub (global.get $sp) (i32.const 4))) (f32.store (global.get $sp) (local.get $value)) ) ;;------------------------------------------------------------------------------ ;; Helper functions ;;------------------------------------------------------------------------------ (func $swap (param f32 f32) (result f32 f32) ;; x,y -> y,x local.get 1 local.get 0 ) (func $scanValueByte (result i32) ;; scans positions $VAL for a byte, incrementing $VAL afterwards (i32.load8_u (global.get $VAL)) ;; in other words: returns byte [$VAL++] (global.set $VAL (i32.add (global.get $VAL) (i32.const 1))) ;; $VAL++ ) {{if .HasOp "sync"}} ;;------------------------------------------------------------------------------ ;; Sync ;;------------------------------------------------------------------------------ {{- .Align}} {{- .SetBlockLabel "su_syncbuf"}} {{- .Block (int (mul 8 .Song.Patch.NumSyncs)) }} (global $syncbufstart (export "sync") i32 (i32.const {{index .Labels "su_syncbuf"}})) (global $sync (mut i32) (i32.const {{index .Labels "su_syncbuf"}})) (func $su_op_sync (param $stereo i32) {{- if .Stereo "sync"}} (if (local.get $stereo) (then (f32.store (global.get $sync) (call $peek2)) (global.set $sync (i32.add (i32.const 4) (global.get $sync))) )) {{- end}} (f32.store (global.get $sync) (call $peek)) (global.set $sync (i32.add (i32.const 4) (global.get $sync))) ) {{end}} ;;------------------------------------------------------------------------------ ;; "Entry point" for the player ;;------------------------------------------------------------------------------ (func $render_128_samples (param) (result i32) (local $rendersamplecount i32) (local $should_update_voices i32) {{- if .Output16Bit }} (local $channel i32) {{- end }} (i32.const 0) (local.set $rendersamplecount) (i32.const 0) (local.set $should_update_voices) (loop $sample_loop (if (i32.eq (global.get $sample) (i32.const 0)) (then (i32.const 1) (local.set $should_update_voices) ) ) (global.set $COM (i32.const {{index .Labels "su_patch_code"}})) (global.set $VAL (i32.const {{index .Labels "su_patch_parameters"}})) {{- if .SupportsPolyphony}} (global.set $COM_instr_start (global.get $COM)) (global.set $VAL_instr_start (global.get $VAL)) {{- end}} (global.set $WRK (i32.const {{index .Labels "su_voices"}})) (global.set $voice (i32.const {{index .Labels "su_voices"}})) (global.set $voicesRemain (i32.const {{.Song.Patch.NumVoices | printf "%v"}})) {{- if .HasOp "delay"}} (global.set $delayWRK (i32.const {{index .Labels "su_delaylines"}})) {{- end}} {{- if .HasOp "sync"}} (global.set $sync (i32.const {{index .Labels "su_syncbuf"}})) {{- end}} (call $su_run_vm) {{- template "output_sound.wat" .}} (global.set $sample (i32.add (global.get $sample) (i32.const 1))) (global.set $globaltick (i32.add (global.get $globaltick) (i32.const 1))) (if (i32.eq (global.get $sample) (i32.const {{.Song.SamplesPerRow}})) (then (global.set $sample (i32.const 0)) (global.set $row (i32.add (global.get $row) (i32.const 1))) ) ) (if (i32.eq (global.get $row) (i32.const {{.PatternLength}})) ( then (global.set $row (i32.const 0)) (global.set $pattern (i32.add (global.get $pattern) (i32.const 1))) ) ) (if (i32.eq (global.get $pattern) (i32.const {{.SequenceLength}})) ( then (global.set $pattern (i32.const 0)) (global.set $globaltick (i32.const 0)) (global.set $outputBufPtr (i32.const {{index .Labels "su_outputbuffer"}})) ) ) (local.set $rendersamplecount (i32.add (local.get $rendersamplecount) (i32.const 1))) (br_if $sample_loop (i32.lt_s (local.get $rendersamplecount) (i32.const 128))) ) (local.get $should_update_voices) ) (export "render_128_samples" (func $render_128_samples)) (export "update_voices" (func $su_update_voices)) (start $render) ;; we run render automagically when the module is instantiated (func $render (param) {{- if .Output16Bit }} (local $channel i32) {{- end }} loop $pattern_loop (global.set $row (i32.const 0)) loop $row_loop (call $su_update_voices) (global.set $sample (i32.const 0)) loop $sample_loop (global.set $COM (i32.const {{index .Labels "su_patch_code"}})) (global.set $VAL (i32.const {{index .Labels "su_patch_parameters"}})) {{- if .SupportsPolyphony}} (global.set $COM_instr_start (global.get $COM)) (global.set $VAL_instr_start (global.get $VAL)) {{- end}} (global.set $WRK (i32.const {{index .Labels "su_voices"}})) (global.set $voice (i32.const {{index .Labels "su_voices"}})) (global.set $voicesRemain (i32.const {{.Song.Patch.NumVoices | printf "%v"}})) {{- if .HasOp "delay"}} (global.set $delayWRK (i32.const {{index .Labels "su_delaylines"}})) {{- end}} {{- if .HasOp "sync"}} (global.set $sync (i32.const {{index .Labels "su_syncbuf"}})) {{- end}} (call $su_run_vm) {{- template "output_sound.wat" .}} (global.set $sample (i32.add (global.get $sample) (i32.const 1))) (global.set $globaltick (i32.add (global.get $globaltick) (i32.const 1))) (br_if $sample_loop (i32.lt_s (global.get $sample) (i32.const {{.Song.SamplesPerRow}}))) end (global.set $row (i32.add (global.get $row) (i32.const 1))) (br_if $row_loop (i32.lt_s (global.get $row) (i32.const {{.PatternLength}}))) end (global.set $pattern (i32.add (global.get $pattern) (i32.const 1))) (br_if $pattern_loop (i32.lt_s (global.get $pattern) (i32.const {{.SequenceLength}}))) end ) {{- if ne .VoiceTrackBitmask 0}} ;; the complex implementation of update_voices: at least one track has more than one voice (func $su_update_voices (local $si i32) (local $di i32) (local $tracksRemaining i32) (local $note i32) (local $firstVoice i32) (local $nextTrackStartsAt i32) (local $numVoices i32) (local $voiceNo i32) (local.set $tracksRemaining (i32.const {{len .Sequences}})) (local.set $si (global.get $pattern)) (local.set $nextTrackStartsAt (i32.const 0)) loop $track_loop (local.set $numVoices (i32.const 0)) (local.set $firstVoice (local.get $nextTrackStartsAt)) loop $voiceLoop (i32.and (i32.shr_u (i32.const {{.VoiceTrackBitmask | printf "%v"}}) (local.get $nextTrackStartsAt) ) (i32.const 1) ) (local.set $nextTrackStartsAt (i32.add (local.get $nextTrackStartsAt) (i32.const 1))) (local.set $numVoices (i32.add (local.get $numVoices) (i32.const 1))) br_if $voiceLoop end (i32.load8_u offset={{index .Labels "su_tracks"}} (local.get $si)) (i32.mul (i32.const {{.PatternLength}})) (i32.add (global.get $row)) (i32.load8_u offset={{index .Labels "su_patterns"}}) (local.tee $note) (if (i32.ne (i32.const {{.Hold}}))(then (i32.store offset={{add (index .Labels "su_voices") 4}} (i32.mul (i32.add (local.tee $voiceNo (i32.load8_u offset={{index .Labels "su_trackcurrentvoice"}} (local.get $tracksRemaining))) (local.get $firstVoice) ) (i32.const 4096) ) (i32.const 1) ) ;; release the note (if (i32.gt_u (local.get $note) (i32.const {{.Hold}}))(then (local.set $di (i32.add (i32.mul (i32.add (local.tee $voiceNo (i32.rem_u (i32.add (local.get $voiceNo) (i32.const 1)) (local.get $numVoices) )) (local.get $firstVoice) ) (i32.const 4096) ) (i32.const {{index .Labels "su_voices"}}) )) (memory.fill (local.get $di) (i32.const 0) (i32.const 4096)) (i32.store (local.get $di) (local.get $note)) (i32.store8 offset={{index .Labels "su_trackcurrentvoice"}} (local.get $tracksRemaining) (local.get $voiceNo)) )) )) (local.set $si (i32.add (local.get $si) (i32.const {{.SequenceLength}}))) (br_if $track_loop (local.tee $tracksRemaining (i32.sub (local.get $tracksRemaining) (i32.const 1)))) end ) {{- else}} ;; the simple implementation of update_voices: each track has exactly one voice (func $su_update_voices (local $si i32) (local $di i32) (local $tracksRemaining i32) (local $note i32) (local $channel i32) (local.set $channel (i32.const 0)) (local.set $tracksRemaining (i32.const {{len .Sequences}})) (local.set $si (global.get $pattern)) (local.set $di (i32.const {{index .Labels "su_voices"}})) loop $track_loop (i32.load8_u offset={{index .Labels "su_tracks"}} (local.get $si)) (i32.mul (i32.const {{.PatternLength}})) (i32.add (global.get $row)) (i32.load8_u offset={{index .Labels "su_patterns"}}) (local.tee $note) (if (i32.ne (i32.const {{.Hold}}))(then (if (i32.eq (i32.const 0) (i32.load8_u offset={{index .Labels "su_hold_override"}} (local.get $channel)) ) (then (i32.store offset=4 (local.get $di) (i32.const 1)) ;; release the note (if (i32.gt_u (local.get $note) (i32.const {{.Hold}}))(then (memory.fill (local.get $di) (i32.const 0) (i32.const 4096)) (i32.store (local.get $di) (local.get $note)) )) )) )) (local.set $di (i32.add (local.get $di) (i32.const 4096))) (local.set $si (i32.add (local.get $si) (i32.const {{.SequenceLength}}))) (local.set $channel (i32.add (local.get $channel) (i32.const 1))) (br_if $track_loop (local.tee $tracksRemaining (i32.sub (local.get $tracksRemaining) (i32.const 1)))) end ) {{- end}} (func $update_single_voice (param $voice_no i32) (param $value i32) (local $di i32) (local.set $di ( i32.add (i32.const {{index .Labels "su_voices"}}) (i32.mul (i32.const 4096) (local.get $voice_no)) )) (i32.store offset=4 (local.get $di) (i32.const 1)) ;; release the note (if (i32.gt_u (local.get $value) (i32.const {{.Hold}}))(then (memory.fill (local.get $di) (i32.const 0) (i32.const 4096)) (i32.store (local.get $di) (local.get $value)) )) (i32.store (i32.add (i32.const {{index .Labels "su_hold_override"}}) (local.get $voice_no)) (local.get $value)) ) (export "update_single_voice" (func $update_single_voice)) {{template "patch.wat" .}} ;; All data is collected into a byte buffer and emitted at once (data (i32.const 0) "{{range .Data}}\{{. | printf "%02x"}}{{end}}") (data (i32.const {{index .Labels "math_pow_1"}}) "\be\f3\f8y\eca\f6?\190\96[\c6\fe\de\bf=\88\afJ\edq\f5?\a4\fc\d42h\0b\db\bf\b0\10\f0\f09\95\f4?{\b7\1f\n\8bA\d7\bf\85\03\b8\b0\95\c9\f3?{\cfm\1a\e9\9d\d3\bf\a5d\88\0c\19\0d\f3?1\b6\f2\f3\9b\1d\d0\bf\a0\8e\0b{\"^\f2?\f0z;\1b\1d|\c9\bf?4\1aJJ\bb\f1?\9f<\af\93\e3\f9\c2\bf\ba\e5\8a\f0X#\f1?\\\8dx\bf\cb`\b9\bf\a7\00\99A?\95\f0?\ce_G\b6\9do\aa\bf\00\00\00\00\00\00\f0?\00\00\00\00\00\00\00\00\acG\9a\fd\8c`\ee?=\f5$\9f\ca8\b3?\a0j\02\1f\b3\a4\ec?\ba\918T\a9v\c4?\e6\fcjW6 \eb?\d2\e4\c4J\0b\84\ce?-\aa\a1c\d1\c2\e9?\1ce\c6\f0E\06\d4?\edAx\03\e6\86\e8?\f8\9f\1b,\9c\8e\d8?bHS\f5\dcg\e7?\cc{\b1N\a4\e0\dc?") (data (i32.const {{index .Labels "math_pow_2"}}) "\f0?t\85\15\d3\b0\d9\ef?\0f\89\f9lX\b5\ef?Q[\12\d0\01\93\ef?{Q}<\b8r\ef?\aa\b9h1\87T\ef?8bunz8\ef?\e1\de\1f\f5\9d\1e\ef?\15\b71\n\fe\06\ef?\cb\a9:7\a7\f1\ee?\"4\12L\a6\de\ee?-\89a`\08\ce\ee?\'*6\d5\da\bf\ee?\82O\9dV+\b4\ee?)TH\dd\07\ab\ee?\85U:\b0~\a4\ee?\cd;\7ff\9e\a0\ee?t_\ec\e8u\9f\ee?\87\01\ebs\14\a1\ee?\13\ceL\99\89\a5\ee?\db\a0*B\e5\ac\ee?\e5\c5\cd\b07\b7\ee?\90\f0\a3\82\91\c4\ee?]%>\b2\03\d5\ee?\ad\d3Z\99\9f\e8\ee?G^\fb\f2v\ff\ee?\9cR\85\dd\9b\19\ef?i\90\ef\dc 7\ef?\87\a4\fb\dc\18X\ef?_\9b{3\97|\ef?\da\90\a4\a2\af\a4\ef?@En[v\d0\ef?") ;;(data (i32.const 8388610) "\52\49\46\46\b2\eb\0c\20\57\41\56\45\66\6d\74\20\12\20\20\20\03\20\02\20\44\ac\20\20\20\62\05\20\08\20\20\20\20\20\66\61\63\74\04\20\20\20\e0\3a\03\20\64\61\74\61\80\eb\0c\20") ) ;; END MODULE