Optimized pattern / song advance code. Prepared release 1.1.

This commit is contained in:
Chris Hodges 2022-12-28 15:24:03 +01:00
parent b177277d40
commit 39d5e5e91e
6 changed files with 93 additions and 112 deletions

View File

@ -1,7 +1,7 @@
# Raspberry Casket
A fast and small open source Pretracker replayer
## Raspberry Casket Player V1.0 (26-Dec-2022)
## Raspberry Casket Player V1.1 (28-Dec-2022)
Provided by Chris 'platon42' Hodges <chrisly@platon42.de>
@ -83,12 +83,15 @@ The original code compressed with *Blueberry's* Shrinkler goes from
18052 bytes down to 9023 bytes.
Raspberry Casket, depending on the features compiled in, is about
6374 bytes and goes down to ~4410 bytes (in isolation).
6216 bytes and goes down to ~4348 bytes (in isolation).
So this means that the optimization is not just "on the outside".
### Timing
Sample generation is a bit faster (I guess around 10-15%), but most of the time is spent on muls operations, so this is the limiting factor.
Raspberry Casket is about twice as fast as the old replayer for playback.
Unfortunately, the replayer is still pretty slow and has high
jitter compared to other standard music replayers.
@ -103,24 +106,30 @@ solve this problem.
### Known issues
- Behaviour for undefined volume slides with both up- and down nibble specified is different (e.g. A9A, hi Rapture!). Don't do that.
- Don't use loops with odd lengths and offsets.
- Don't use loops with odd lengths and offsets (even if Pretracker allows this when dragging the loop points).
- Don't stop the music with F00 and use a note delay (EDx) in the same line.
- Don't try to play music with no waves, instruments or patterns.
- Shinobi seemed to have used an early beta version of Pretracker where it was possible to specify a Subloop Wait of 0. That's illegal.
- Pattern breaks with target row >= $7f will be ignored.
- Shinobi seemed to have used an early beta version of Pretracker where it was possible to specify a Subloop Wait of 0. That's illegal and unsupported.
- Pattern break (Dxx) + Song pos (Bxx) on the same line does not work in original Pretracker & Player: New Dxx position is ignored.
There is code to enable it in the player, so you could in theory make backwards running tracks like in Protracker.
But this doesn't make sense as long as the tracker itself does not support it.
## Changelog
### V1.1 (unreleased)
- Optimized base displacement by reordering variables
- Further optimized ADSR code
- Optimized wave loop code
- Bake in this strange vibrato speed multiplication to precalculated vibrato value (where possible)
- Various small optimizations
- Store instrument number * 4 on loading to avoid using two adds every frame
### V1.1 (28-Dec-22)
- Optimized base displacement by reordering variables.
- Further optimized ADSR code.
- Optimized wave loop code.
- Bake in this strange vibrato speed multiplication to precalculated vibrato value (where possible).
- Various small optimizations.
- Store instrument number * 4 on loading to avoid using two adds every frame.
- Optimized speed/shuffle code. Idea of using xor turned out to make things too complicated for pattern breaks/jumps.
- Rearranged code for more short branches.
- Optimized track delay code further.
- Drop-in replacement code size: 6304 bytes.
- Optimized pattern / song advance code.
- Maximum jitter now about one rasterline less, average about 0.5 rasterlines less (measurements, your mileage may vary).
- Drop-in replacement code size: 6228 bytes.
### V1.0 (26-Dec-22)

Binary file not shown.

View File

@ -1,5 +1,5 @@
This file "raspberry_casket.bin" can replace the original "player.bin"
provided that you used the 16 KB + 16 KB allocation for player
variables as in the original player.
variables as in the original player (you need only about 2 + 12 KB).
It has been assembled from src/drop_in_replacement.asm (version V1.0).
It has been assembled from src/drop_in_replacement.asm (version V1.1).

Binary file not shown.

View File

@ -3,7 +3,7 @@ It features Pink's song Plastic Dove (from the Coda Intro).
Even though it features the full replayer code and tune,
the replayer including my demo framework and the song only
takes a mere 6560 bytes (shrinkled), which is even smaller
takes a mere 6448 bytes (shrinkled), which is even smaller
than the new streaming replayer used in Pink's new streaming
player demonstrated (closed source) in
@ -11,7 +11,7 @@ https://www.pouet.net/prod.php?which=91551
which is 6860 bytes.
The Raspberry Casket replayer takes an average of 14
The Raspberry Casket replayer takes an average of 13.5
rasterlines while Pink's new streaming player takes
only about 10 rasterlines.

View File

@ -1,6 +1,6 @@
;--------------------------------------------------------------------
; Raspberry Casket Player V1.1beta (28-Dec-2022)
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Raspberry Casket Player V1.1 (28-Dec-2022)
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
; Provided by Chris 'platon42' Hodges <chrisly@platon42.de>
;
@ -87,15 +87,22 @@
; 18052 bytes down to 9023 bytes.
;
; Raspberry Casket, depending on the features compiled in, is about
; 6374 bytes and goes down to ~4410 bytes (in isolation).
; 6216 bytes and goes down to ~4348 bytes (in isolation).
;
; So this means that the optimization is not just "on the outside".
;
; Timing
; ~~~~~~
; Sample generation is a bit faster (I guess around 10-15%), but most
; of the time is spent on muls operations, so this is the limiting
; factor.
;
; Raspberry Casket is about twice as fast as the old replayer for
; playback.
;
; Unfortunately, the replayer is still pretty slow and has high
; jitter compared to other standard music replayers.
; This means it may take up to 33 raster lines (14-19 on average)
; This means it may take up to 32 raster lines (13-18 on average)
; which is significant more than a standard Protracker replayer
; (the original one could take about 60 raster lines worst case and
; about 34 on average!).
@ -639,7 +646,7 @@ pre_FuncTable
dc.l pre_PlayerInit-pre_FuncTable
dc.l pre_PlayerTick-pre_FuncTable
ENDC
;dc.b '$VER: Raspberry Casket 1.0',0
;dc.b '$VER: Raspberry Casket 1.1',0
;even
IFNE PRETRACKER_COPPER_OUTPUT
@ -2906,29 +2913,66 @@ pre_PlayerTick:
; end of pattern loop
; ----------------------------------------
; pattern advancing FIXME try to figure out all the cases
; Pattern advancing and pattern break and jump handling. Song looping and song-end detection.
.pattern_advancing
subq.b #1,pv_pat_line_ticks_b(a4)
bne .no_pattern_advance
; clear note delay info
moveq.l #0,d1
moveq.l #0,d0
REPT NUM_CHANNELS
move.b d1,pv_channeldata+pcd_note_delay_b+REPTN*pcd_SIZEOF(a4)
move.b d0,pv_channeldata+pcd_note_delay_b+REPTN*pcd_SIZEOF(a4)
ENDR
move.b sv_num_steps_b(a6),d1
subq.b #1,d1
move.b pv_next_pat_row_b(a4),d0
blt.s .no_pattern_break
cmp.b d0,d1
bgt.s .has_legal_break_pos
move.b d1,d0 ; limit to last step
.has_legal_break_pos
move.b d0,pv_pat_curr_row_b(a4)
move.b sv_num_steps_b(a6),d1 ; number of steps in pattern
move.w sv_curr_pat_pos_w(a6),d3 ; go to next pattern pos
addq.w #1,d3
move.b pv_pat_curr_row_b(a4),d0
addq.b #1,d0 ; normal step increment
move.w sv_curr_pat_pos_w(a6),d3 ; current song position
move.b pv_next_pat_row_b(a4),d2 ; $ff means no pattern break
bmi.s .no_pattern_break
st pv_next_pat_row_b(a4) ; processed break, set to $ff
move.b d2,d0
IFNE 0 ; PRETRACKER_BUGFIX_CODE ; currently disabled to keep old behaviour
moveq.l #0,d2 ; clear mask
ENDC
cmp.b d1,d0
blo.s .has_legal_break_pos
move.b d1,d0 ; limit to last step
subq.b #1,d0
bra.s .has_legal_break_pos
.no_pattern_break
cmp.b d1,d0
blo.s .pattern_end_not_reached
moveq.l #0,d0
IFNE PRETRACKER_PARANOIA_MODE
move.b pv_loop_pattern_b(a4),d0 ; keep same pattern rolling?
bne.s .pattern_end_not_reached
ENDC
.has_legal_break_pos
addq.w #1,d3 ; pattern break will increment song pos -- if there is no new pattern position
.pattern_end_not_reached
move.b pv_next_pat_pos_b(a4),d4
bmi.s .no_new_position ; has a new pattern position
st pv_next_pat_pos_b(a4)
IFNE PRETRACKER_SONG_END_DETECTION
cmp.b d4,d3
bgt.s .no_backjump
st pv_songend_detected_b(a4) ; detect jumping back
.no_backjump
ENDC
move.b d4,d3 ; load new position
IFNE 0 ; PRETRACKER_BUGFIX_CODE ; currently disabled to keep old behaviour
not.b d2
and.b d2,d0 ; if we had NO pattern break, we will clear d0
ELSE
moveq.l #0,d0
ENDC
.no_new_position
cmp.w sv_pat_pos_len_w(a6),d3
blt.s .no_restart_song
@ -2937,87 +2981,15 @@ pre_PlayerTick:
st pv_songend_detected_b(a4)
ENDC
.no_restart_song
move.w d3,sv_curr_pat_pos_w(a6)
st pv_next_pat_row_b(a4) ; processed break, set to $ff
move.b pv_next_pat_pos_b(a4),d3
bge.s .has_new_position ; has a new pattern position together with a break
move.b d0,d2 ; backup pv_pat_curr_row_b
cmp.b d0,d1
ble.s .end_of_pattern_reached
bra.s .done_pat_advance
.no_pattern_break
move.b pv_next_pat_pos_b(a4),d3
bge.s .has_new_position
move.b pv_pat_curr_row_b(a4),d0
move.b d0,d2
cmp.b d1,d2
blt.s .advancetonextpos
.end_of_pattern_reached
clr.b pv_pat_curr_row_b(a4)
IFNE PRETRACKER_PARANOIA_MODE
move.b pv_loop_pattern_b(a4),d0 ; keep same pattern rolling?
bne .done_pat_advance
ENDC
bra.s .advance_song_pos
.advancetonextpos
addq.b #1,d2
move.b sv_num_steps_b(a6),d1
addq.b #1,d0
cmp.b d2,d1
bgt.s .dont_go_to_top_row
moveq.l #0,d0
.dont_go_to_top_row
move.b d0,pv_pat_curr_row_b(a4)
bra.s .done_pat_advance
.has_new_position
clr.b pv_pat_curr_row_b(a4)
IFNE PRETRACKER_PARANOIA_MODE
move.b pv_loop_pattern_b(a4),d0
bne .no_restart_song_after_jump
ENDC
cmp.w sv_pat_pos_len_w(a6),d3
blt.s .set_song_pos2
move.w sv_pat_restart_pos_w(a6),d3
IFNE PRETRACKER_SONG_END_DETECTION
st pv_songend_detected_b(a4)
ENDC
.set_song_pos2
move.w d3,sv_curr_pat_pos_w(a6)
; enters with d0 = 0
.no_restart_song_after_jump
st pv_next_pat_pos_b(a4) ; processed jump, set to $ff
subq.b #1,d2
bhi.s .done_pat_advance
clr.b pv_pat_curr_row_b(a4)
tst.b d0
bne.s .done_pat_advance
.advance_song_pos
move.w sv_curr_pat_pos_w(a6),d0
addq.w #1,d0
cmp.w sv_pat_pos_len_w(a6),d0
blt.s .set_song_pos
move.w sv_pat_restart_pos_w(a6),d0
IFNE PRETRACKER_SONG_END_DETECTION
st pv_songend_detected_b(a4)
ENDC
.set_song_pos
move.w d0,sv_curr_pat_pos_w(a6)
.done_pat_advance
move.b pv_pat_speed_even_b(a4),d0
btst #0,pv_pat_curr_row_b(a4)
beq.s .set_speed_even
move.b pv_pat_speed_odd_b(a4),d0
move.b pv_pat_speed_even_b(a4),d1
lsr.b #1,d0
bcc.s .set_speed_even
move.b pv_pat_speed_odd_b(a4),d1
.set_speed_even
move.b d0,pv_pat_line_ticks_b(a4)
move.b d1,pv_pat_line_ticks_b(a4)
.no_pattern_advance
; ----------------------------------------