[4.5.x] Speed up music

kevin81

Well-known member
Hello everyone! I've written a small snippet you can use to speed up a song while it's playing. You could, for example, use it on a timer, for when the player is running out of time (you know, like in that turtle-kicking plumber game). The tempo gets reset to the original speed (by NESMaker/GGsound design) when the game loads a new song.

Code:
; This constant defines how much the tempo increases.
; A bigger value equals a bigger increase in tempo.
; Change this value to your liking.
MUSIC_SPEED_INCREMENT = #$B0

; Check if the high byte needs decreasing.
; A lower stream_tempo_ value indicates a higher speed,
; so these variables could be considered to be "length"
; rather than "tempo".
    LDA stream_tempo_lo
    SEC
    SBC #MUSIC_SPEED_INCREMENT
    BCS +changeLowByte

; Check if speed can be increased further.
; If not, skip changing the tempo altogether.
    STA temp
    LDA stream_tempo_hi
    CMP #$01
    BEQ +doNotChange

; Decrease the high byte values.
    DEC stream_tempo_hi
    DEC stream_tempo_hi+1
    DEC stream_tempo_hi+2
    DEC stream_tempo_hi+3    

; Decrease DPCM channel low byte only if DPCM is enabled
    ifdef FEATURE_DPCM
        DEC stream_tempo_hi+4
    endif

; Change the low byte values.
    LDA temp
+changeLowByte:
    STA stream_tempo_lo
    STA stream_tempo_lo+1
    STA stream_tempo_lo+2
    STA stream_tempo_lo+3

; Change DPCM channel low byte only if DPCM is enabled
    ifdef FEATURE_DPCM
        STA stream_tempo_lo+4
    endif

; we're done here
+doNotChange:

I have tested this code as an input script and that worked. Also it accounts for DPCM sample usage, although I didn't actually test this with the DPCM channel enabled. Please feel free to let me know what you think! If it works - or doesn't work - as intended, I'd love to hear that too.
 

kevin81

Well-known member
Does it go anyplace special, like a specific script or bank, or can it be used anywhere?

The script only modifies a couple values in RAM, so it should work from any bank at any location. I've only tested it as an input script though, which sits in the static ($1F) bank. As long as you make sure the script gets executed only once (instead for instance at a tile collision, which gets run every frame or so, which will keep increasing the music speed to max value in no-time), you'll be good.
 

dale_coop

Moderator
Staff member
A similar topic:
 

timefor

New member
I integrated this into a simple timer script, but with the repeating nature of a timer loop, any thoughts on a simple way to make sure the tempo increase runs only once? If I use the code as is, the speed increases until the max immediately. This code loops many times right at the "30" second mark.

Code:
;; Do Handle Game Timer Maze Module from NESmakers Forum (DALE COOP, 5kids2feed, ARCTIC BLIZZARD) ;;

;; What this script does is the Game Timer counts down.        ;;
;; When timer reaches zero your game will reset by default. ;;

;;; Game Timer Ticks ;;;

    LDA gameTimerTicks
    CLC
    ADC #$04             ;; this represents the speed myTimer will tick ;;
    STA gameTimerTicks
    BCC +normalwhatever

;; myTimer OFF when Screen Flag below is ACTIVE ;;

    LDA ScreenFlags00
    AND #%01000000 ;; if "hide hud" is set.
    BNE +normalwhatever ;; skip all the code related to myTimer

    SubtractValue #$02, myTimer, #$1, #$00
    UpdateHudElement #$06

+normalwhatever

;;; Game Timer 1's, & 10's place, & 100's place,  ;;;

    LDA gameTimerLo
    ADC #$09        ;; this represents the 1's place in the timer.
    STA gameTimerLo
    LDA gameTimerHi
    ADC #$09        ;; this represents the 10's place in the timer.
    STA gameTimerHi
    LDA gameTimerHigher
    ADC #$09        ;; this represents the 100's place in the timer.
    STA gameTimerHigher


;;; checking the value of the Game Timer ;;;

    LDA myTimer_100
    BNE +notEnded
    
    LDA myTimer_10
    CMP #$03 ;check if 10's place = 3
    BNE +notEnded ;skip if not
        LDA myTimer
        CMP #$00 ;check if 1's place = 0
        BNE +notEnded ;skip if not
            
            ;Play sound if myTime equals 30
            PlaySound #sfx_timeout
            
            ; This constant defines how much the tempo increases.
            ; A bigger value equals a bigger increase in tempo.
            ; Change this value to your liking.
            MUSIC_SPEED_INCREMENT = #$B0

            ; Check if the high byte needs decreasing.
            ; A lower stream_tempo_ value indicates a higher speed,
            ; so these variables could be considered to be "length"
            ; rather than "tempo".
                LDA stream_tempo_lo
                SEC
                SBC #MUSIC_SPEED_INCREMENT
                BCS +changeLowByte

            ; Check if speed can be increased further.
            ; If not, skip changing the tempo altogether.
                STA temp
                LDA stream_tempo_hi
                CMP #$01
                BEQ +doNotChange

            ; Decrease the high byte values.
                DEC stream_tempo_hi
                DEC stream_tempo_hi+1
                DEC stream_tempo_hi+2
                DEC stream_tempo_hi+3   

            ; Decrease DPCM channel low byte only if DPCM is enabled
                ifdef FEATURE_DPCM
                    DEC stream_tempo_hi+4
                endif

            ; Change the low byte values.
                LDA temp
            +changeLowByte:
                STA stream_tempo_lo
                STA stream_tempo_lo+1
                STA stream_tempo_lo+2
                STA stream_tempo_lo+3

            ; Change DPCM channel low byte only if DPCM is enabled
                ifdef FEATURE_DPCM
                    STA stream_tempo_lo+4
                endif

            ; we're done here
            +doNotChange:   
    
    LDA myTimer_10
    BNE +notEnded
    LDA myTimer
    BNE +notEnded

;; when Game Timer reaches 00.

    JMP RESET        ;; TESTING to RESET GAME ;;

+notEnded:
 

kevin81

Well-known member
I integrated this into a simple timer script, but with the repeating nature of a timer loop, any thoughts on a simple way to make sure the tempo increase runs only once? If I use the code as is, the speed increases until the max immediately. This code loops many times right at the "30" second mark.

The easiest way I can think of right now is using a helper variable, and add that as an extra check in the timer script.

The general idea is this:

1) Add a variable to your zero page settings, called isSpedUp, with an initial value of 0

2) In your post screen load script, reset this variable to zero:
Code:
    LDA #0
    STA isSpedUp

3) When the timer hits thirty, check if isSpedUp is zero:
Code:
    LDA isSpedUp
    BEQ +
        JMP +doNotChange
    +

4) If isSpedUp is zero, set it to one:
Code:
    LDA #1
    STA isSpedUp
and execute the speed-up script.

Ideally you would use a screenFlag instead of a variable, since you'll only need a single bit instead of a whole byte. But as long as you've got an extra zero page variable to spare, this method will work as well.
 

kevin81

Well-known member
A similar topic:

Whelp, I guess I overlooked that post before adding this topic... Sorry for the duplicate!
 

dale_coop

Moderator
Staff member
Whelp, I guess I overlooked that post before adding this topic... Sorry for the duplicate!
Do'nt be sorry. Your script is different. It's nice to have different approaches.
I just added the link because it's related (if people are interested/curious to check).
 
Top Bottom