feat(sointu): make patterns local to track

The global pattern table is constructed only during compilation. At this point, we can do also all sorts of optimizations / changes e.g. remove unnecessary releases and reuse patterns if there's a pattern already that could be used.
This commit is contained in:
vsariola
2021-01-03 01:06:59 +02:00
parent 06c006086b
commit 5dd81430b7
100 changed files with 395 additions and 150 deletions

View File

@ -61,7 +61,7 @@ su_render_sampleloop: ; loop through every sample in the row
jl su_render_sampleloop
{{.Pop .AX}} ; Stack: pushad ptr
inc eax
cmp eax, {{.Song.TotalRows}}
cmp eax, {{.EncodedSong.TotalRows}}
jl su_render_rowloop
; rewind the stack the entropy of multiple pop {{.AX}} is probably lower than add
{{- range slice .Stacklocs $prologsize}}
@ -91,7 +91,7 @@ su_render_sampleloop: ; loop through every sample in the row
{{- if ne .VoiceTrackBitmask 0}}
; The more complicated implementation: one track can trigger multiple voices
xor edx, edx
mov ebx, {{.Song.PatternRows}} ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256...
mov ebx, {{.EncodedSong.PatternLength}} ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256...
div ebx ; eax = current pattern, edx = current row in pattern
{{.Prepare "su_tracks"}}
lea {{.SI}}, [{{.Use "su_tracks"}}+{{.AX}}] ; esi points to the pattern data for current track
@ -100,7 +100,7 @@ su_render_sampleloop: ; loop through every sample in the row
mov {{.BP}}, {{.PTRWORD}} su_synth_obj ; ebp points to the current_voiceno array
su_update_voices_trackloop:
movzx eax, byte [{{.SI}}] ; eax = current pattern
imul eax, {{.Song.PatternRows}} ; eax = offset to current pattern data
imul eax, {{.EncodedSong.PatternLength}} ; eax = offset to current pattern data
{{- .Prepare "su_patterns" .AX | indent 4}}
movzx eax,byte [{{.Use "su_patterns" .AX}},{{.DX}}] ; eax = note
push {{.DX}} ; Stack: ptrnrow
@ -138,7 +138,7 @@ su_update_voices_skipreset:
su_update_voices_nexttrack:
pop {{.BX}} ; ebx=first voice of next instrument, Stack: ptrnrow
pop {{.DX}} ; edx=patrnrow
add {{.SI}}, {{.Song.SequenceLength}}
add {{.SI}}, {{.EncodedSong.SequenceLength}}
inc {{.BP}}
{{- $addrname := len .Song.Tracks | printf "su_synth_obj + %v"}}
{{- .Prepare $addrname | indent 8}}
@ -149,7 +149,7 @@ su_update_voices_nexttrack:
; The simple implementation: each track triggers always the same voice
xor edx, edx
xor ebx, ebx
mov bl, {{.Song.PatternRows}} ; rows per pattern
mov bl, {{.EncodedSong.PatternLength}} ; rows per pattern
div ebx ; eax = current pattern, edx = current row in pattern
{{- .Prepare "su_tracks" | indent 4}}
lea {{.SI}}, [{{.Use "su_tracks"}}+{{.AX}}]; esi points to the pattern data for current track
@ -157,7 +157,7 @@ su_update_voices_nexttrack:
mov bl, {{len .Song.Tracks}} ; MAX_TRACKS is always <= 32 so this is ok
su_update_voices_trackloop:
movzx eax, byte [{{.SI}}] ; eax = current pattern
imul eax, {{.Song.PatternRows}} ; multiply by rows per pattern, eax = offset to current pattern data
imul eax, {{.EncodedSong.PatternLength}} ; multiply by rows per pattern, eax = offset to current pattern data
{{- .Prepare "su_patterns" .AX | indent 8}}
movzx eax, byte [{{.Use "su_patterns" .AX}} + {{.DX}}] ; ecx = note
cmp al, {{.Song.Hold}} ; anything but hold causes action
@ -173,7 +173,7 @@ su_update_voices_retrigger:
su_update_voices_nexttrack:
add {{.DI}}, su_voice.size
su_update_voices_skipadd:
add {{.SI}}, {{.Song.SequenceLength}}
add {{.SI}}, {{.EncodedSong.SequenceLength}}
dec ebx
jnz short su_update_voices_trackloop
ret
@ -185,7 +185,7 @@ su_update_voices_skipadd:
; Patterns
;-------------------------------------------------------------------------------
{{.Data "su_patterns"}}
{{- range .Song.Patterns}}
{{- range .EncodedSong.Patterns}}
db {{. | toStrings | join ","}}
{{- end}}
@ -193,8 +193,8 @@ su_update_voices_skipadd:
; Tracks
;-------------------------------------------------------------------------------
{{.Data "su_tracks"}}
{{- range .Song.Tracks}}
db {{.Sequence | toStrings | join ","}}
{{- range .EncodedSong.Sequences}}
db {{. | toStrings | join ","}}
{{- end}}
{{- if gt (.SampleOffsets | len) 0}}

View File

@ -7,7 +7,7 @@
*/}}
{{- .SetLabel "su_patterns"}}
{{- $m := .}}
{{- range .Song.Patterns}}
{{- range .EncodedSong.Patterns}}
{{- range .}}
{{- $.DataB .}}
{{- end}}
@ -20,8 +20,8 @@
*/}}
{{- .SetLabel "su_tracks"}}
{{- $m := .}}
{{- range .Song.Tracks}}
{{- range .Sequence}}
{{- range .EncodedSong.Sequences}}
{{- range .}}
{{- $.DataB .}}
{{- end}}
{{- end}}
@ -111,7 +111,7 @@
;; 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 8388608)) ;; TODO: do not hard code, layout memory somehow intelligently
(global $outputLength (export "l") i32 (i32.const {{if .Song.Output16Bit}}{{mul .Song.TotalRows .Song.SamplesPerRow 4}}{{else}}{{mul .Song.TotalRows .Song.SamplesPerRow 8}}{{end}}))
(global $outputLength (export "l") i32 (i32.const {{if .Song.Output16Bit}}{{mul .EncodedSong.TotalRows .Song.SamplesPerRow 4}}{{else}}{{mul .EncodedSong.TotalRows .Song.SamplesPerRow 8}}{{end}}))
(global $output16bit (export "t") i32 (i32.const {{if .Song.Output16Bit}}1{{else}}0{{end}}))
@ -183,17 +183,17 @@
(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 {{.Song.PatternRows}})))
(br_if $row_loop (i32.lt_s (global.get $row) (i32.const {{.EncodedSong.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 {{.Song.SequenceLength}})))
(br_if $pattern_loop (i32.lt_s (global.get $pattern) (i32.const {{.EncodedSong.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 .Song.Tracks}}))
(local.set $tracksRemaining (i32.const {{len .EncodedSong.Sequences}}))
(local.set $si (global.get $pattern))
(local.set $nextTrackStartsAt (i32.const 0))
loop $track_loop
@ -212,7 +212,7 @@
br_if $voiceLoop
end
(i32.load8_u offset={{index .Labels "su_tracks"}} (local.get $si))
(i32.mul (i32.const {{.Song.PatternRows}}))
(i32.mul (i32.const {{.EncodedSong.PatternLength}}))
(i32.add (global.get $row))
(i32.load8_u offset={{index .Labels "su_patterns"}})
(local.tee $note)
@ -246,7 +246,7 @@
(i32.store8 offset=768 (local.get $tracksRemaining) (local.get $voiceNo))
))
))
(local.set $si (i32.add (local.get $si) (i32.const {{.Song.SequenceLength}})))
(local.set $si (i32.add (local.get $si) (i32.const {{.EncodedSong.SequenceLength}})))
(br_if $track_loop (local.tee $tracksRemaining (i32.sub (local.get $tracksRemaining) (i32.const 1))))
end
)
@ -254,12 +254,12 @@
{{- 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.set $tracksRemaining (i32.const {{len .Song.Tracks}}))
(local.set $tracksRemaining (i32.const {{len .EncodedSong.Sequences}}))
(local.set $si (global.get $pattern))
(local.set $di (i32.const 4160))
loop $track_loop
(i32.load8_u offset={{index .Labels "su_tracks"}} (local.get $si))
(i32.mul (i32.const {{.Song.PatternRows}}))
(i32.mul (i32.const {{.EncodedSong.PatternLength}}))
(i32.add (global.get $row))
(i32.load8_u offset={{index .Labels "su_patterns"}})
(local.tee $note)
@ -271,7 +271,7 @@
))
))
(local.set $di (i32.add (local.get $di) (i32.const 4096)))
(local.set $si (i32.add (local.get $si) (i32.const {{.Song.SequenceLength}})))
(local.set $si (i32.add (local.get $si) (i32.const {{.EncodedSong.SequenceLength}})))
(br_if $track_loop (local.tee $tracksRemaining (i32.sub (local.get $tracksRemaining) (i32.const 1))))
end
)