Add more music that changes at night - pret/pokecrystal GitHub Wiki

In DPPt and SuMo/USUM, the music for most areas of the overworld changes during the night, helping with the immersion in the game. In the original GSC, the only music that changes during the night is the Johto Wild Battle theme, to a slower and "softer" theme (not even HGSS ported this music over). In this tutorial, we'll extrapolate the changes applied to that music to every overworld theme in the game, and implement a system that changes the music automatically during the night. We'll also add a new night version of the Kanto Wild Battle theme.

Before starting, please make sure to fix this design flaw or else you won't be able to properly follow the tutorial.

Contents

  1. Understanding how the Johto Wild Battle Night version is programmed
  2. Adding a new Night version
  3. Adding the code that switches the music at night
  4. Adding the rest of the music
  5. Adding softer drumkits
  6. Adding a custom wave
  7. Adding the Kanto Wild Battle Night theme
  8. Extra: Support for evening

1. Understanding how the Johto Wild Battle Night version is programmed

You can skip this section, since knowing about how the original night song is programmed isn't necessary to the rest of the tutorial. But if you're curious about how the original night music is programmed or the music engine in general, this is a good way to get started.

If you open the johtowildbattlenight.asm file, which contains this particular music, you'll immediately notice it's only 29 lines long, way too short to contain the entire song. This is because, unlike the Night versions in the later gens, this one reuses almost the entire code of the regular Day version. The file pretty much only contains the header defining the different channels (in this case, the Noise channel isn't used) and almost immediately calls the main loop of the Day version for each channel (as defined by the last command in each channel, sound_loop). In short, the differences between the Day and Night version isn't the notes themselves, but the way they are played. Here are the main differences between the two versions:

  • Tempo: The tempo of the song, as defined by the tempo command, is higher in the Night version (107 as opposed to 104). Note that the tempo command works in reverse to BPM: the higher it is, the slower the song plays.

  • Duty Cycle: The duty cycle of Channel 2 (which contains the melody) is always $3 (corresponds to a pulse of 75%) in the Day version, but is changed to $2 (corresponds to a pulse of 50%) for the main loop of the Night version. This gives a softer feel to the music.

  • Channel 3 Waveform: Unlike Channels 1 and 2, the third channel is programmable and can produce different waveforms from the restrictive "pulse" from the other two channels. The Gen 2 games use 10 different waveforms for their music, can be found in the wave_samples.asm file; some of these are close to a sine wave, making them very "soft", while others have more complex shapes and are much "sharper". The Night version of the Johto Wild Battle switches the wave sample $4 (which is relatively sharp) for wave sample $1, which is much softer.

Apart from these changes, we're also going to make a small change to the Noise channel for the music which uses it: define new, lower noise notes to make the music even softer.

2. Adding a new Night version

As an example, we'll add the Night version of New Bark Town into the game. Here's a link to the file containing the original song, plus the night version at the bottom, with the transformations we have covered earlier. Note that this file uses the depreciated music commands, but the file should still work fine in more recent versions of pokecrystal.

Adding a new night version music to the game works exactly the same way as adding any other new song; there's already a tutorial for that, and the process here is mostly the same. Add the music pointer Music_NewBarkTownNight to the music_pointers.asm file and the new music constant MUSIC_NEW_BARK_TOWN_NIGHT.

There's a slight caveat though: Instead of adding the Night version in a separate file like explained in sections 2 and 4 of the other tutorial, you need to replace the entire original song with this new file. There are two reasons for this: first, since the Night version reuses much of the same code as the Day version, both separate files would need to be in the same section; otherwise, the game wouldn't compile (if you noticed, both versions of the Johto Wild Battle are in the same section in the audio.asm file, although they are in separate files); second, the code of the original song was altered, essentially creating more subroutines that can be called as needed by the new Night version and thus reducing the need for redundant code. But don't worry, the original song will play in exactly the same way as before.

However, if you try to compile the game, make will give you this error: error: layout.link(156): Sections would extend past the end of ROMX. The reason for this is because the music sections are very well compacted, which makes it difficult to add even a couple of new bytes corresponding to the new songs without overfilling a bank. A way to solve this is moving the altered song to a brand new section:

Edit audio.asm:

 SECTION "Songs 1", ROMX

 ...
 INCLUDE "audio/music/championbattle.asm"
 INCLUDE "audio/music/ssaqua.asm"
-INCLUDE "audio/music/newbarktown.asm"
 INCLUDE "audio/music/goldenrodcity.asm"
 ...

+SECTION "Altered Songs", ROMX
+INCLUDE "audio/music/newbarktown.asm"

Now it should work, and the new night version should be in the game! You can use it like any other song in the game, for example adding it to a specific map in New Bark Town. But the objective is to play this version only during the night, and in all maps that play the New Bark Town song.

3. Adding the code that switches the music at night

The way we are going to make the game switch the music at night is by "hijacking" the code that loads the music into the wram. But first, we need a way to tell the game what are the "pairs" of songs we want to switch between. We are going to do that through a table.

First, create the file home/audionighttable.asm:

+NightMusicTable:
+MACRO night_music
+	db \1, \2
+ENDM
+
+	night_music MUSIC_NEW_BARK_TOWN,    MUSIC_NEW_BARK_TOWN_NIGHT
+	db -1 ; end

Then include it in home.asm:

 INCLUDE "home/sprite_anims.asm"
 INCLUDE "home/audio.asm"
+INCLUDE "home/audionighttable.asm"
 INCLUDE "home/mobile.asm"

This file contains a table with two constants on each row: the first column has the Day version of a song, and the second column contains the Night version of the same song.

On its own, this table doesn't do anything. We need the code to actually change the music when it's night. For that, we'll create a new function.

Edit home/map.asm, adding this code at the end (thanks to ISSOtm for helping with this code):

+ChangeMusicIfNight::
+	ld a, [wTimeOfDay]
+  	cp NITE_F
+	ret nz
+	ld hl, NightMusicTable
+.loop
+	ld a, [hli]
+	cp -1
+	ret z
+	cp c
+	ld a, [hli]
+	jr nz, .loop
+	ld c, a
+	ret

What this function does is first see if the time of day is night (cp NITE_F); if not, we return to where we were before. If the time of day is night, the table we created earlier is loaded and the constants in the first column are sequentially compared to the constant in c until we reach the end of the table. If there's a match, c is updated with the constant on the same row and the second column of the table.

Now, we just need to do a small edit on the same home/map.asm file to actually call this function:

 GetMapMusic::
 	push hl
 	push bc
 	ld de, MAP_MUSIC
 	call GetMapField
 	ld a, c
 	cp MUSIC_MAHOGANY_MART
 	jr z, .mahoganymart
 	bit RADIO_TOWER_MUSIC_F, c
 	jr nz, .radiotower
 	farcall Function8b342
+       call ChangeMusicIfNight
 	ld e, c
 	ld d, 0
 .done
 	pop bc
 	pop hl
 	ret

And that's it! The Night version of New Bark Town should now play during the night whenever that particular song is loaded!

However, there's still a couple of small oversights that need to be fixed.

The first one you might not even notice during gameplay: when the game reloads the overworld music after a battle or when the game is restarted, for example, it does it by loading the constant directly from WRAM. This means that, if the time of day changes mid-battle, the map music won't actually be changed until the map itself is changed. This is very easy to fix.

Edit home/audio.asm:

 RestartMapMusic::
 	push hl
 	push de
 	push bc
 	push af
 	ld de, MUSIC_NONE
 	call PlayMusic
 	call DelayFrame
 	ld a, [wMapMusic]
+       ld c, a
+       call ChangeMusicIfNight
+       ld e, c
-       ld e, a
 	ld d, 0
 	call PlayMusic
 	pop af
 	pop bc
 	pop de
 	pop hl
 	ret

Here we're just loading the current map music from WRAM to c and checking if it needs to be changed using the same function as before.

The second problem is a bit more tricky to fix. There are two special cases of maps that have their music change throughout the game: the Radio Tower in Goldenrod City and the small shop in Mahogany Town, both of which have their music change to the Team Rocket themes during the respective story sequences, and back to the normal town music when they are defeated. This is done through a series of checks in the GetMapMusic function, which we've already changed a while earlier. Because of the way this code is done, the music won't actually switch at night with the current code. To fix this issue, we need to change the logic of these checks.

Edit home/map.asm:

 GetMapMusic::
 	push hl
 	push bc
 	ld de, MAP_MUSIC
 	call GetMapField
 	ld a, c
 	cp MUSIC_MAHOGANY_MART
 	jr z, .mahoganymart
 	bit RADIO_TOWER_MUSIC_F, c
 	jr nz, .radiotower
 	farcall Function8b342
+.done
 	call ChangeMusicIfNight
 	ld e, c
 	ld d, 0
-.done
 	pop bc
 	pop hl
 	ret
 
 .radiotower
 	ld a, [wStatusFlags2]
 	bit STATUSFLAGS2_ROCKETS_IN_RADIO_TOWER_F, a
 	jr z, .clearedradiotower
-	ld de, MUSIC_ROCKET_OVERTURE
+	ld c, MUSIC_ROCKET_OVERTURE
 	jr .done

 .clearedradiotower
 	; the rest of the byte
-       ld de, MUSIC_GOLDENROD_CITY
+	ld c, MUSIC_GOLDENROD_CITY
 	jr .done

 .mahoganymart
 	ld a, [wStatusFlags2]
 	bit STATUSFLAGS2_ROCKETS_IN_MAHOGANY_F, a
 	jr z, .clearedmahogany
-       ld de, MUSIC_ROCKET_HIDEOUT
+	ld c, MUSIC_ROCKET_HIDEOUT
 	jr .done

.clearedmahogany
-       ld de, MUSIC_CHERRYGROVE_CITY
+	ld c, MUSIC_CHERRYGROVE_CITY
	jr .done

What we are doing here is essentially changing the register where these checks place the music constant in, be it the Team Rocket version or the normal version. In the original code, they put the constant in de, which is what is needed for the next functions, but the new code to switch the music at night changes the constant at c, which is where GetMapField puts it. Luckily, there's already code that changes the register of the music constant (the ld e, c / ld d, 0 lines), so we simply need to anticipate where those checks jump to in the base code (the .done) by three lines and thus reuse that bit of code.

4. Adding the rest of the music

The rest of the asm files for all overworld music of Gen 2 that TriteHexagon arranged can be found on this ASM repository.

And the updated night conversion table below:

 NightMusicTable:
 MACRO night_music
 	db \1, \2
 	;\1 - original song
 	;\2 - night song
 ENDM
 
  	night_music MUSIC_NEW_BARK_TOWN,    MUSIC_NEW_BARK_TOWN_NIGHT
+	night_music MUSIC_ROUTE_29,         MUSIC_ROUTE_29_NIGHT
+	night_music MUSIC_CHERRYGROVE_CITY, MUSIC_CHERRYGROVE_CITY_NIGHT
+	night_music MUSIC_ROUTE_30,         MUSIC_ROUTE_30_NIGHT
+	night_music MUSIC_VIOLET_CITY,      MUSIC_VIOLET_CITY_NIGHT 
+	night_music MUSIC_AZALEA_TOWN,      MUSIC_AZALEA_TOWN_NIGHT
+	night_music MUSIC_GOLDENROD_CITY,   MUSIC_GOLDENROD_CITY_NIGHT
+	night_music MUSIC_ECRUTEAK_CITY,    MUSIC_ECRUTEAK_CITY_NIGHT
+	night_music MUSIC_ROUTE_37,         MUSIC_ROUTE_37_NIGHT
+	night_music MUSIC_LAKE_OF_RAGE,     MUSIC_LAKE_OF_RAGE_NIGHT
+	night_music MUSIC_ROUTE_26,         MUSIC_ROUTE_26_NIGHT
+	night_music MUSIC_VIRIDIAN_CITY,    MUSIC_VIRIDIAN_CITY_NIGHT
+	night_music MUSIC_ROUTE_3,          MUSIC_ROUTE_3_NIGHT
+	night_music MUSIC_ROUTE_12,         MUSIC_ROUTE_12_NIGHT
+	night_music MUSIC_CELADON_CITY,     MUSIC_CELADON_CITY_NIGHT
+	night_music MUSIC_VERMILION_CITY,   MUSIC_VERMILION_CITY_NIGHT
+	night_music MUSIC_PALLET_TOWN,      MUSIC_PALLET_TOWN_NIGHT
+	night_music MUSIC_ROUTE_1,          MUSIC_ROUTE_1_NIGHT
+	night_music MUSIC_LAVENDER_TOWN,    MUSIC_LAVENDER_TOWN_NIGHT
 	db -1 ; end

Of course, you can use this same code to add any new pairs of Day/Night music by adding a new line.

If you try to add all the songs in at the same time, the game will compile and work fine; however, a few songs might sound a bit weird. This is because we still have to define the lower noise notes and the respective drumkits. Likewise, the Viridian City music uses a custom wave; if you don't include this, the song will sound weird as well (more on this later).

5. Adding softer drumkits

First, we need to define these new drumkits. Edit audio/drumkits.asm:

 Drumkits:
	dw Drumkit0
	dw Drumkit1
	dw Drumkit2
	dw Drumkit3
	dw Drumkit4
	dw Drumkit5
+       dw Drumkit0S
+	dw Drumkit1S 
+	dw Drumkit_Empty
+	dw Drumkit3S
+	dw Drumkit4S
+	dw Drumkit5S
 ...
+Drumkit0S:
+	dw Drum00   
+	dw Snare2
+	dw Snare3   
+	dw Snare4 
+	dw Snare4_Soft 
+	dw Drum05  
+	dw Triangle1_Soft
+	dw Triangle2_Soft
+	dw HiHat1_Soft
+	dw Snare5_Soft
+	dw Snare6_Soft
+	dw Snare7_Soft
+	dw HiHat1
+Drumkit1S:
+	dw Drum00
+	dw HiHat1_Soft
+	dw Snare5_Soft
+	dw Snare6_Soft
+	dw Snare7_Soft
+	dw HiHat2_Soft
+	dw HiHat3_Soft
+	dw Snare8_Soft
+	dw Triangle3_Soft
+	dw Triangle4_Soft
+	dw Snare9_Soft
+	dw Snare10_Soft
+	dw Snare11_Soft
+Drumkit_Empty:
+Drumkit3S:
+	dw Drum21
+	dw Snare12_Soft
+	dw Snare13_Soft
+	dw Snare14_Soft
+	dw Kick1_Soft
+	dw Triangle5_Soft
+	dw Drum20
+	dw Drum27_Soft
+	dw Drum28_Soft
+	dw Drum29_Soft
+	dw Drum21
+	dw Kick2_Soft
+	dw Crash2_Soft
+Drumkit4S:
+	dw Drum00
+	dw Drum20
+	dw Snare13_Soft
+	dw Snare14_Soft
+	dw Kick1_Soft
+	dw Drum33_Soft
+	dw Triangle5_Soft
+	dw Drum35_Soft
+	dw Drum31_Soft
+	dw Drum32_Soft
+	dw Drum36_Soft
+	dw Kick2_Soft
+	dw Crash1
+Drumkit5S:
+	dw Drum00
+	dw Snare9_Soft
+	dw Snare10_Soft
+	dw Snare11_Soft
+	dw Drum27_Soft
+	dw Drum28_Soft 
+	dw Drum29_Soft  
+	dw Drum05      
+	dw Triangle1_Soft 
+	dw Crash1
+	dw Snare14_Soft 
+	dw Snare13_Soft
+	dw Kick2_Soft 

And add the new "softer" noise notes at the end of the same file:

+; Softer Noise Notes
+Snare12_Soft:
+	noise_note 32, 8, 1, 51
+	sound_ret
+
+Snare13_Soft:
+	noise_note 32, 4, 1, 50
+	sound_ret
+
Snare14_Soft:
+	noise_note 32, 7, 1, 49
+	sound_ret
+
+Kick1_Soft:
+	noise_note 32, 7, 8, 107
+	noise_note 32, 6, 1, 0
+	sound_ret
+
+Triangle5_Soft:
+	noise_note 48, 8, 1, 24
+	sound_ret
+
+Drum27_Soft:
+	noise_note 39, 8, 2, 16
+	sound_ret
+
+Drum28_Soft:
+	noise_note 51, 8, 1, 0
+	noise_note 51, 1, 1, 0
+	sound_ret
+
+Drum29_Soft:
+	noise_note 51, 8, 1, 17
+	noise_note 51, 1, 1, 0
+	sound_ret
+
+Drum31_Soft:
+	noise_note 51, 4, 1, 33
+	noise_note 51, 1, 1, 17
+	sound_ret
+
+Drum32_Soft:
+	noise_note 51, 4, 1, 80
+	noise_note 51, 1, 1, 17
+	sound_ret
+
+Drum33_Soft:
+	noise_note 32, 9, 1, 49
+	sound_ret
+
+Drum35_Soft:
+	noise_note 51, 7, 1, 0
+	noise_note 51, 1, 1, 0
+	sound_ret
+
+Drum36_Soft:
+	noise_note 51, 7, 1, 33
+	noise_note 51, 1, 1, 17
+	sound_ret
+
+Snare4_Soft:
+	noise_note 32, 7, 1, 51
+	sound_ret
+
+Snare5_Soft:
+	noise_note 32, 7, 2, 35
+	sound_ret
+
+Snare6_Soft:
+	noise_note 32, 7, 2, 37
+	sound_ret
+
+Snare7_Soft:
+	noise_note 32, 7, 2, 38
+	sound_ret
+
+Snare8_Soft:
+	noise_note 32, 9, 2, 80
+	sound_ret
+
+Snare9_Soft:
+	noise_note 32, 8, 1, 34
+	sound_ret
+
+Snare10_Soft:
+	noise_note 32, 6, 1, 34
+	sound_ret
+
+Snare11_Soft:
+	noise_note 32, 5, 1, 34
+	sound_ret
+
+Kick2_Soft:
+	noise_note 32, 9, 8, 107
+	noise_note 32, 6, 1, 0
+	sound_ret
+
+Crash2_Soft:
+	noise_note 32, 7, 4, 18
+	sound_ret
+
+Triangle1_Soft:
+	noise_note 32, 6, 1, 42
+	sound_ret
+
+Triangle2_Soft:
+	noise_note 33, 3, 1, 43
+	noise_note 32, 5, 1, 42
+	sound_ret
+
+Triangle3_Soft:
+	noise_note 32, 9, 1, 24
+	noise_note 32, 2, 1, 51
+	sound_ret
+
+Triangle4_Soft:
+	noise_note 34, 8, 1, 40
+	noise_note 32, 6, 1, 24
+	sound_ret
+
+HiHat1_Soft:
+	noise_note 32, 7, 1, 16
+	sound_ret
+
+HiHat2_Soft:
+	noise_note 32, 9, 1, 16
+	sound_ret
+
+HiHat3_Soft:
+	noise_note 32, 9, 2, 17
+	sound_ret

You may notice the Drumkit_Empty on the list of drumkits. This is used to keep the offset between the normal and the softer versions of the drumkit 6 spaces apart (so drumkit $6 is the softer version of drumkit $0, drumkit $7 is the softer version of drumkit $1, and so on); no song in the vanilla game uses drumkit $2, so we don't need to define this lower version. This isn't really necessary but this helps in case you need to add in a new drumkit of your own.

6. Adding a custom wave

For the Viridian City nighttime version to sound correct, we'll need to add in a new wave. This is very easy to do. Edit audio/wave_samples.asm:

 WaveSamples:
 	; These are streams of 32 nybbles (4-bit values) used as wave patterns.
 	; Plot them as a line chart to see the wave's shape.
 	dn  0,  2,  4,  6,  8, 10, 12, 14, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 10,  9,  8,  7,  6,  5,  4,  4,  3,  3,  2,  2,  1,  1
 	dn  0,  2,  4,  6,  8, 10, 12, 14, 14, 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  2,  1,  1
 	dn  1,  3,  6,  9, 11, 13, 14, 14, 14, 14, 15, 15, 15, 15, 14, 13, 13, 14, 15, 15, 15, 15, 14, 14, 14, 14, 13, 11,  9,  6,  3,  1
 	dn  0,  2,  4,  6,  8, 10, 12, 13, 14, 15, 15, 14, 13, 14, 15, 15, 14, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0
 	dn  0,  1,  2,  3,  4,  5,  6,  7,  8, 10, 12, 13, 14, 14, 15,  7,  7, 15, 14, 14, 13, 12, 10,  8,  7,  6,  5,  4,  3,  2,  1,  0
 	dn  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  3,  3,  2,  2,  1,  1, 15, 15, 14, 14, 12, 12, 10, 10,  8,  8, 10, 10, 12, 12, 14, 14
 	dn  0,  2,  4,  6,  8, 10, 12, 14, 12, 11, 10,  9,  8,  7,  6,  5, 15, 15, 15, 14, 14, 13, 13, 12,  4,  4,  3,  3,  2,  2,  1,  1
 	dn 12,  0, 10,  9,  8,  7, 15,  5, 15, 15, 15, 14, 14, 13, 13, 12,  4,  4,  3,  3,  2,  2, 15,  1,  0,  2,  4,  6,  8, 10, 12, 14
 	dn  4,  4,  3,  3,  2,  2,  1, 15,  0,  0,  4,  6,  8, 10, 12, 14, 15,  8, 15, 14, 14, 13, 13, 12, 12, 11, 10,  9,  8,  7,  6,  5
 	dn  1,  1,  0,  0,  0,  0,  0,  8,  0,  0,  1,  3,  5,  7,  9, 10, 11,  4, 11, 10, 10,  9,  9,  8,  8,  7,  6,  5,  4,  3,  2,  1
+       dn  2,  3,  4,  4,  5,  6,  6,  7,  8,  9,  11, 11, 12, 12, 13, 7,  7, 13, 12, 12, 11, 11,  9,  8,  7,  6,  6,  5,  4,  4,  3,  2

This new wave is a "softer" version of wave $4. The music is already pulling the wave from index A, so unless you have any extra custom waves, there shouldn't be an issue. The definition of this specific wave can be found on the file itself.

7. Adding the Kanto Wild Battle Night theme

Adding the Kanto Wild Battle theme works just like any other Night song. Replace the original song with the respective file and add a new Music_KantoWildBattleNight pointer and MUSIC_KANTO_WILD_BATTLE_NIGHT constant.

However, to actually make it load in-game, we need to add new code to switch the music at night, just like with the Johto Wild Battle Night theme.

Edit engine/battle/start_battle.asm:

 .kantowild
 	ld de, MUSIC_KANTO_WILD_BATTLE
+       ld a, [wTimeOfDay]
+       cp NITE_F
+       jr nz, .done
+       ld de, MUSIC_KANTO_WILD_BATTLE_NIGHT
 	jr .done

And with this final edit, we have all our night versions in and a system to switch any overworld music at night.

TriteHexagon's note: you might have noticed there's no file for Route 2, the remixed version of the theme for Viridian Forest in the original RBY. This is because I figured most people would prefer restoring the Viridian Forest map and use that song there, which would turn it into a dungeon and therefore would be no need to switch the song at night. I'll leave the conversion to a night version of this song as an exercise to the reader if you really need it ;)

8. Extra: Support for evening

If you followed Rangi's tutorial for adding evening as the fourth time of day, you might want the night versions to play in the new evening period. This is very easy to do.

Edit home/map.asm:

 ChangeMusicIfNight::
 	ld a, [wTimeOfDay]
  	cp NITE_F
-	ret nz
+       ret c
 ...

And also edit engine/battle/start_battle.asm:

 .kantowild
 	ld de, MUSIC_KANTO_WILD_BATTLE
 	ld a, [wTimeOfDay]
 	cp NITE_F
-       jr nz, .done
+       jr c, .done ; not NITE_F or EVE_F
 	ld de, MUSIC_KANTO_WILD_BATTLE_NIGHT
 	jr .done

Here we are simply changing the checks for when to use the night music to include every time of day over NITE_F, which includes EVE_F.

And this is the end of the tutorial. Enjoy your new night tunes while playing your game!