Music

From ZeldaHacking Wiki
Jump to navigation Jump to search

This page will detail how the Oracles game handles music data and reading it. You should already be familiar with the disassembly. Also, see https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware regarding how the Gameboy Color sound hardware works.

Structure

Music and sound effects use the same system. Music is defined as indices 0x00 through 0x4b. Sound effects are indices 0x4c through 0xd4. Indices 0xde and 0xf0 - 0xfc are used for sound controls, like stopping music, sound effects, fade-ins and fade-outs.

Indices 0xd5 - 0xdc and 0xdf - 0xef are undefined and unused by the games.

The Gameboy Color has four channels. Two square channels, one wave channel, and one noise channel. The first square channel is capable of using sweeps, but this is not used by the Oracle games (at least not for music).

The disassembly has several files regarding the music data. First, there are a few files in the audio/{game}/ folder:

soundPointers.s
soundChannelPointers.s
soundChannelData.s

Sound Pointers

This is a list of pointers to soundChannelPointers.s. The order of these pointers are actually what define the index of music. So, for example, if musTitlescreen is swapped with musOverworld, then the title screen music will play in the overworld, and vice versa. In the Disassembly, macros are used for easier presentation of these pointers. The macro helps define the bank of the channel data offset from the audio code (code/audio), and then the actual pointer to soundChannelPointers.s.

Sound Channel Pointers

These are the pointers utilized by the game to define the channels used for the music.

For music, the square channels are defined as $00 and $01. The wave channel is $04 and the noise channel is $06. For sound effects, square channels are $02 and $03. Wave channel is $05, and the noise channel is $07. Reminder that the hardware only has the four channels. The games only separate them like this so that sound effects can play over music without the games losing track of either.

Here is an example of one of the pointers, this one for the overworld:

musOverworld:
	.db $00
	.dw musOverworldChannel0
	.db $01
	.dw musOverworldChannel1
	.db $04
	.dw musOverworldChannel4
	.db $06
	.dw musOverworldChannel6
	.db $ff

This is an example of the sound effects pointers. Note that not all channels have to be defined for one sound effect, and thus keeping it open for other effects. Further, only bits 0-2 are utilized to define the channel index. Bits 3-7, set to $a0 in the example, are not yet fully understood.

sndTimewarpInitiated:
	.db $a2
	.dw sndTimewarpInitiatedChannel2
	.db $a3
	.dw sndTimewarpInitiatedChannel3
	.db $a5
	.dw sndTimewarpInitiatedChannel5
	.db $a7
	.dw sndTimewarpInitiatedChannel7
	.db $ff

Sound Channel Data

This file is a compilation of all of the music data that the game holds. While soundPointers.s and soundChannelPointers.s are housed within the first bank, with the audio code (code/audio.s), this data is so large that it spans multiple banks. However, music data for one song is always in its own, single bank.

Although all the data used to be in this single file, the Disassembly has since been restructured so that this file only has .include instructions, so that all the music can be in its separate, unique .s file. These are within the audio/{game}/mus/ or audio/{game}/sfx/ folders. These files are named the same as the constants in constants/music.s.

Music Data

While much of the information in this section is applicable to sound effects data as well, the author is much more accustomed to music data than sound effect data. Therefore, for clarity, this section will call it "music data" rather than the generic "channel data."

The data are bytes 0x00 through 0xff. The audio code interprets each byte as a sort of scripting language, where the first byte defines the command, and subsequent bytes can define parameters, such as note lengths and volume levels.

The disassembly uses macros as to make the music data easier to read. These are located in include/musicMacros.s.

beat
octave, octaved, octaveu
note
rest, rest2
vol
env
cmdf0
duty
cmdf8
vibrato
cmdfd
goto
cmdff, cmdf4, cmdf5

Pitch Macros

Commands $00 through $d0 are for pitches (frequencies). These have one parameter, defining the length the pitch is played. The pitch frequencies starts at C at Octave 1 and goes up to b at Octave 8. In include/musicMacros.s, these pitches are enumerated so that instead of using $00 in the following macros, you can use c1, etc. Note that these pitches are only defined with sharps. As such, gs4 exists, but ab4 or af4 does not.

Allowable frequencies for the noise channel are as follows:

$22: Crash
$23: Tom-like drum sound
$24: Snare drum, short dash noise
$26: Long dash noise
$27: Longer dash noise
$28: Wave crash
$29: digging, bumping
$2a: Dash noise
$2e: Long dash noise, like a bush break
$2f: Crumble noise
$30: Crash
$32: Tom-like, dash sound
$52: Dash noise

The descriptions for these noises are not scientific.

Note

This macro is utilized by the disassembly for the games' original music data. This is because it is usually used for one frequency-length pair. Lengths can range from $00 through $ff; however, note that a length of $00 underflows so that the true length is $100. This macro is a lesser version of Beat.

note d5 24

Although the games' music data uses hexadecimal values for the lengths, decimal values are allowed and recommended. Thus, a quarter note at 150 BPM will have a value of 24, or $18. Shortening note lengths are intuitive, such that an eighth note at 150 BPM will be 12, or $0c. However, see the Tempo macro below detailing how to avoid using absolute values at all.

Beat

This is Stewmat's custom version of the note macro. The main input for this macro is frequency-length pairs. However, Stewmat has introduced a few other commands that can be used inside this macro that is helpful for music creation, but does not actually affect the number of bytes that is put into the game.

Before this macro is used, the user must define BEAT with a value, as this constant is multiplied by the input lengths to arrive at the final, true length. For example,

.redefine BEAT 1
beat d5 24 c3 6 g4 12

is the same as

.redefine BEAT 6
beat d5 4 c3 1 g4 2

This macro also supports using pitches without a defined octave (i.e., relative octaves). Rather, the octave can first be defined with the Octave macro, then shifted up and down as needed. For example, these two lead to the same output:

beat d5 24 c3 6 g4 12
octave 5
beat d 24 od od c 24 ou g 12

Either are entirely viable and up to the user on how they are input. As shown above, od and ou are the beat macro's version of octaved and octaveu, respectively. See below.

Further, this macro also allows r as an input. This is the same as the rest macro.

Finally, you can make NOTE_END_WAIT greater than $00 so that notes are shortened by that length and a rest will be placed in between.

.redefine NOTE_END_WAIT 2
beat c4 12 c4 12
.redefine NOTE_END_WAIT 0
beat c4 12

is the same as

beat c4 10 r 2 c4 10 r 2 c4 12

This can be necessary if multiple notes have the same pitch, and so there needs to be a break for the rhythm to come out. However, the second parameter of env command can be used instead for shorter, more staccato notes. NOTE_END_WAIT is assumed to be $00, but make sure to redefine it to $00 after its use, as it can affect music data from other songs if it's not reset.

Octave

Octaves start at C and go up to B. Middle C is located in Octave 4.

This macro is utilized in conjunction with the beat macro if relative octaves are being used.

The user can also use octaveu and octaved to shift the octave up and down, respectively.

Rest

This command defines a period that the channel should wait until the next command is read. It only has one parameter, the length.

rest 24

Note that if you need the channel to rest longer than $100 (the highest possible with $00), then it is recommended to set the volume to $0 and play a note with a low frequency. This is because the channels play small blips at the end of a rest, but this is avoided when the volume is already down and it's already "playing" a pitch. gs3 is the pitch used by the games. Therefore, it could look like this:

vol $0
beat gs3 $00 gs3 24

Volume

This command is only actually applicable to the square and noise channels. It does not affect the wave channel. Inputs can range from $0 to $f, with $0 being complete silence and $f being the loudest. The games only ever go up to $8, and $6 is used the most as a good, forte volume. Examples:

vol $0
vol $6

Volume levels can be combined to create an "echo" effect.

env $1 $00
vibrato $81
vol $6
beat c4 40

env $0 $00
vibrato $01
vol $4
beat c4 20

This will cause Middle C to play for 60 periods, but will only be loud for 40. Make sure to unset the wait on the vibrato and the first parameter of the volume envelope, if they're difference than $0.

This command does nothing for the wave channel.

Envelopes

Volume envelopes can affect either how a note begins or ends. This is an example of the envelope macro:

env $1 $05

The first parameter causes a note to start at low volume and increase to the actual volume within a short time. The effect is soft, and is usually used to simulate instruments like Guru Guru's phonograph. The larger the value, the longer it takes for the note to reach full volume. The second parameter causes a note to decrease in volume after a certain time. The larger the value, the longer it takes for the note to decrease. It is usually used to create a staccato without introducing a rest between notes. It's not required for the envelope to have both effects at once, and in fact this is usually not utilized.

See example above in the Volume section to see how an "echo" effect can be achieved.

This does nothing for the wave channel.

Cmdf0

This command is only used for sound effects and is not fully understood. It sets wc039 at times.

Duties

This sets the duty cycle for the channel.

Square Channels

The square channels can be set only at values $00, $01, $02, and $03. In effect, lower values are more harsh while high values are more soft.

Wave Channel

This command sets the wave form for the wave channel. The game has preset wave forms that can be viewed at code/audio.s. However, here is the following most common forms:

$0e: Normal volume
$0f: Used as an echo for $0e
$08: Used as an echo for a square channel (Tarm Ruins)

Cmdf8

This command is only used for sound effects and is not fully understood. It sets wc03f at times.

Vibrato

This command sets the channels vibratos. It is mainly used for the square channels. The upper nybble is the amount of time waited until the vibrato starts. The lower nybble is the intensity of the vibrato. Common values:

$00: No vibrato
$81: short wait, low intensity
$e2: long wait, high intensity
$02: No wait, high intensity

See example above in the Volume section to see how an "echo" effect can be achieved.

Cmdfd

This command shifts the pitch in an absolute way. It sets wc033. It will NOT shift pitches by a half step, but rather by the actual frequency value. It is not used by the games for music.

Jumps

The goto macro, this command, is utilized by the games to loop the entire piece. There are no conditional jumps in the music data. Therefore, unless the audio code is edited, the entire piece must be expanded with all conditional repeats.

Take this paragraph as a suggestion when creating music: When creating labels, note the music name, channel name, and the applicable measure or other pertinent information. For example, the overworld music loops at measure 5 on channel 1. Therefore, the label would be musOverworldChannel1Measure5Loop. However, as the channel data labels were created prior to analyzing it, the disassmebly usually just uses the address. Therefore, currently this label is musiceca67.

Cmdff

This command mutes the channel - the internal index, not the actual hardware channel, and causes the channel to stop reading music data. It usually marks the end of a channel's music data for a piece, or is used right after a goto command, although needless. Additionally, certain commands are copies of this one, such as cmdf4, cmdf5, $f7, and $fa - $fc. The first two have cmd macros as the games use these for some unknown reason.

Tempo

This is ZerotoKoops's macro that allows for relative note lengths. The input is the desired tempo. However, as note lengths must be kept at whole values, the actual output tempo might vary slightly from the input tempo. For example, tempo 150 sets the tempo-related variables to relate to 150 BPM. tempo 140 sets the tempo at around 138.46. The usable variables are:

Whole: W
Half: HF
Quarter: Q
Eighth: E1, E2
Sixteenth: S1, S2, S3, S4
Thirty-second: T1, T2, T3, T4, T5, T6, T7, T8
Quarter Triplet: R1, R2, R3
Eighth Triplet: Y1, Y2, Y3, Y4, Y5, Y6
Sixteenth Triplet: W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12

With tempos above about 170, certain subdivisions of the quarter note will be calculated at $00, and therefore cause the note to be played for 256 periods rather than none due to an underflow error. Tempos above 170 can still be used, just beware of using small subdivisions.

Thus, an example of the notes in used would be the following:

tempo 150
octave 4
beat c W ou c HF
octaved
beat b Q as E1+S3 a S4
beat gs R1 g R2 fs R3
beat f R1+R2 g R3 b Q
beat c W

is the same as

octave 4
beat c 96 ou c 48
octaved
beat b 24 as 18 a 6
beat gs 8 g 8 fs 8
beat f 16 g 8 b 24
beat c 96

Note that for certain tempos and certain subdivisions, one might not equal another. For example, with a quarter note length of 29, thirty-second notes are as follows:

T1: 3
T2: 4
T3: 3
T4: 4
T5: 4
T6: 3
T7: 4
T8: 4

The pattern of these subdivisions are random and already determined by the macro.

Moving and Importing Music Data

Currently, there are a few custom pieces which have been created for the Oracle games' engines. Furthermore, a user might want to import music from either game to the other.

Sounds Pointers

For this example, we will import the Past Overworld music from Oracle of Ages into Seasons. The three main .s files must be edited. We will start with audio/seasons/soundPointers.s. Decide which index will be replaced with musOverworldPast (the base label for the music data). Reminder that music only goes up to index $4b, and therefore another piece must be replaced. There are several blank indices, but if you use those, you might run into storage issues in a later step. For our example, we will replace musCarnival, index $08.

Constants

An optional step would be to change the constant at constants/common/music.s, but this is not required. It is however recommended to close the project in LynnaLab when editing these constants.

Sound Channel Pointers

The next step is to add/replace the the channel pointers at audio/seasons/soundChannelPointers.s. In our example, we would replace the one referencing musCarnival to musOverworldPast.

musCarnival:
	.db $00
	.dw musCarnivalChannel0
	.db $01
	.dw musCarnivalChannel1
	.db $04
	.dw musCarnivalChannel4
	.db $06
	.dw musCarnivalChannel6
	.db $ff

becomes

musOverworldPast:
	.db $00
	.dw musOverworldPastChannel0
	.db $01
	.dw musOverworldPastChannel1
	.db $04
	.dw musOverworldPastChannel4
	.db $06
	.dw musOverworldPastChannel6
	.db $ff

Sound Channel Data

Finally, we will need to add/replace the actual music data into soundChannelData.s. We can place this anywhere as long as it fits within the bank, but we'll replace the .include of carnival to overworldPast. If it doesn't fit, we can delete other music data, but we would need to remove the references as well. Therefore,

.include "audio/seasons/mus/carnival.s"

is replaced by

.include "audio/ages/mus/overworldPast.s"

For consistency, we could move the music data to the appropriate games' folder, but this is not necessary.