mirror of
https://gitlab.com/xCrystal/pokecrystal-board.git
synced 2024-11-16 11:27:33 -08:00
fbcc8d1b0e
The comment states that ClearChannels doesn't seem to to be used. This is not the case, as it is used by ExchangeMysteryGiftData and ExchangeNameCardData.
2853 lines
47 KiB
NASM
2853 lines
47 KiB
NASM
; The entire sound engine. Uses section "audio" in WRAM.
|
|
|
|
; Interfaces are in bank 0.
|
|
|
|
; Notable functions:
|
|
; FadeMusic
|
|
; PlayStereoSFX
|
|
|
|
_InitSound::
|
|
; restart sound operation
|
|
; clear all relevant hardware registers & wram
|
|
push hl
|
|
push de
|
|
push bc
|
|
push af
|
|
call MusicOff
|
|
ld hl, rNR50 ; channel control registers
|
|
xor a
|
|
ld [hli], a ; rNR50 ; volume/vin
|
|
ld [hli], a ; rNR51 ; sfx channels
|
|
ld a, $80 ; all channels on
|
|
ld [hli], a ; rNR52 ; music channels
|
|
|
|
ld hl, rNR10 ; sound channel registers
|
|
ld e, NUM_MUSIC_CHANS
|
|
.clearsound
|
|
; sound channel 1 2 3 4
|
|
xor a
|
|
ld [hli], a ; rNR10, rNR20, rNR30, rNR40 ; sweep = 0
|
|
|
|
ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
|
|
ld a, $8
|
|
ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
|
|
xor a
|
|
ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
|
|
ld a, $80
|
|
ld [hli], a ; rNR14, rNR24, rNR34, rNR44 ; restart sound (freq hi = 0)
|
|
dec e
|
|
jr nz, .clearsound
|
|
|
|
ld hl, wAudio
|
|
ld de, wAudioEnd - wAudio
|
|
.clearaudio
|
|
xor a
|
|
ld [hli], a
|
|
dec de
|
|
ld a, e
|
|
or d
|
|
jr nz, .clearaudio
|
|
|
|
ld a, MAX_VOLUME
|
|
ld [wVolume], a
|
|
call MusicOn
|
|
pop af
|
|
pop bc
|
|
pop de
|
|
pop hl
|
|
ret
|
|
|
|
MusicFadeRestart:
|
|
; restart but keep the music id to fade in to
|
|
ld a, [wMusicFadeID + 1]
|
|
push af
|
|
ld a, [wMusicFadeID]
|
|
push af
|
|
call _InitSound
|
|
pop af
|
|
ld [wMusicFadeID], a
|
|
pop af
|
|
ld [wMusicFadeID + 1], a
|
|
ret
|
|
|
|
MusicOn:
|
|
ld a, 1
|
|
ld [wMusicPlaying], a
|
|
ret
|
|
|
|
MusicOff:
|
|
xor a
|
|
ld [wMusicPlaying], a
|
|
ret
|
|
|
|
_UpdateSound::
|
|
; called once per frame
|
|
; no use updating audio if it's not playing
|
|
ld a, [wMusicPlaying]
|
|
and a
|
|
ret z
|
|
; start at ch1
|
|
xor a
|
|
ld [wCurChannel], a ; just
|
|
ld [wSoundOutput], a ; off
|
|
ld bc, wChannel1
|
|
.loop
|
|
; is the channel active?
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jp z, .nextchannel
|
|
; check time left in the current note
|
|
ld hl, CHANNEL_NOTE_DURATION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
cp 2 ; 1 or 0?
|
|
jr c, .noteover
|
|
dec [hl]
|
|
jr .continue_sound_update
|
|
|
|
.noteover
|
|
; reset vibrato delay
|
|
ld hl, CHANNEL_VIBRATO_DELAY
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld hl, CHANNEL_VIBRATO_DELAY_COUNT
|
|
add hl, bc
|
|
ld [hl], a
|
|
; turn vibrato off for now
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
res SOUND_PITCH_SLIDE, [hl]
|
|
; get next note
|
|
call ParseMusic
|
|
.continue_sound_update
|
|
call ApplyPitchSlide
|
|
; duty cycle
|
|
ld hl, CHANNEL_DUTY_CYCLE
|
|
add hl, bc
|
|
ld a, [hli]
|
|
ld [wCurTrackDuty], a
|
|
; volume envelope
|
|
ld a, [hli]
|
|
ld [wCurTrackVolumeEnvelope], a
|
|
; frequency
|
|
ld a, [hli]
|
|
ld [wCurTrackFrequency], a
|
|
ld a, [hl]
|
|
ld [wCurTrackFrequency + 1], a
|
|
; vibrato, noise
|
|
call HandleTrackVibrato ; handle vibrato and other things
|
|
call HandleNoise
|
|
; turn off music when playing sfx?
|
|
ld a, [wSFXPriority]
|
|
and a
|
|
jr z, .next
|
|
; are we in a sfx channel right now?
|
|
ld a, [wCurChannel]
|
|
cp NUM_MUSIC_CHANS
|
|
jr nc, .next
|
|
; are any sfx channels active?
|
|
; if so, mute
|
|
ld hl, wChannel5Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .restnote
|
|
ld hl, wChannel6Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .restnote
|
|
ld hl, wChannel7Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .restnote
|
|
ld hl, wChannel8Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr z, .next
|
|
.restnote
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_REST, [hl] ; Rest
|
|
.next
|
|
; are we in a sfx channel right now?
|
|
ld a, [wCurChannel]
|
|
cp NUM_MUSIC_CHANS
|
|
jr nc, .sfx_channel
|
|
ld hl, CHANNEL_STRUCT_LENGTH * NUM_MUSIC_CHANS + CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .sound_channel_on
|
|
.sfx_channel
|
|
call UpdateChannels
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
ld a, [wSoundOutput]
|
|
or [hl]
|
|
ld [wSoundOutput], a
|
|
.sound_channel_on
|
|
; clear note flags
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
xor a
|
|
ld [hl], a
|
|
.nextchannel
|
|
; next channel
|
|
ld hl, CHANNEL_STRUCT_LENGTH
|
|
add hl, bc
|
|
ld c, l
|
|
ld b, h
|
|
ld a, [wCurChannel]
|
|
inc a
|
|
ld [wCurChannel], a
|
|
cp NUM_CHANNELS ; are we done?
|
|
jp nz, .loop ; do it all again
|
|
|
|
call PlayDanger
|
|
; fade music in/out
|
|
call FadeMusic
|
|
; write volume to hardware register
|
|
ld a, [wVolume]
|
|
ldh [rNR50], a
|
|
; write SO on/off to hardware register
|
|
ld a, [wSoundOutput]
|
|
ldh [rNR51], a
|
|
ret
|
|
|
|
UpdateChannels:
|
|
ld hl, .ChannelFunctions
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_CHANNELS
|
|
add a
|
|
ld e, a
|
|
ld d, 0
|
|
add hl, de
|
|
ld a, [hli]
|
|
ld h, [hl]
|
|
ld l, a
|
|
jp hl
|
|
|
|
.ChannelFunctions:
|
|
table_width 2, UpdateChannels.ChannelFunctions
|
|
; music channels
|
|
dw .Channel1
|
|
dw .Channel2
|
|
dw .Channel3
|
|
dw .Channel4
|
|
assert_table_length NUM_MUSIC_CHANS
|
|
; sfx channels
|
|
; identical to music channels, except .Channel5 is not disabled by the low-HP danger sound
|
|
; (instead, PlayDanger does not play the danger sound if sfx is playing)
|
|
dw .Channel5
|
|
dw .Channel6
|
|
dw .Channel7
|
|
dw .Channel8
|
|
assert_table_length NUM_CHANNELS
|
|
|
|
.Channel1:
|
|
ld a, [wLowHealthAlarm]
|
|
bit DANGER_ON_F, a
|
|
ret nz
|
|
.Channel5:
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
bit NOTE_PITCH_SWEEP, [hl]
|
|
jr z, .noPitchSweep
|
|
;
|
|
ld a, [wPitchSweep]
|
|
ldh [rNR10], a
|
|
.noPitchSweep
|
|
bit NOTE_REST, [hl] ; rest
|
|
jr nz, .ch1_rest
|
|
bit NOTE_NOISE_SAMPLING, [hl]
|
|
jr nz, .ch1_noise_sampling
|
|
bit NOTE_FREQ_OVERRIDE, [hl]
|
|
jr nz, .ch1_frequency_override
|
|
bit NOTE_VIBRATO_OVERRIDE, [hl]
|
|
jr nz, .ch1_vibrato_override
|
|
jr .ch1_check_duty_override
|
|
|
|
.ch1_frequency_override
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR13], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
ldh [rNR14], a
|
|
.ch1_check_duty_override
|
|
bit NOTE_DUTY_OVERRIDE, [hl]
|
|
ret z
|
|
ld a, [wCurTrackDuty]
|
|
ld d, a
|
|
ldh a, [rNR11]
|
|
and $3f ; sound length
|
|
or d
|
|
ldh [rNR11], a
|
|
ret
|
|
|
|
.ch1_vibrato_override
|
|
ld a, [wCurTrackDuty]
|
|
ld d, a
|
|
ldh a, [rNR11]
|
|
and $3f ; sound length
|
|
or d
|
|
ldh [rNR11], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR13], a
|
|
ret
|
|
|
|
.ch1_rest
|
|
ldh a, [rNR52]
|
|
and %10001110 ; ch1 off
|
|
ldh [rNR52], a
|
|
ld hl, rNR10
|
|
call ClearChannel
|
|
ret
|
|
|
|
.ch1_noise_sampling
|
|
ld hl, wCurTrackDuty
|
|
ld a, $3f ; sound length
|
|
or [hl]
|
|
ldh [rNR11], a
|
|
ld a, [wCurTrackVolumeEnvelope]
|
|
ldh [rNR12], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR13], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
or $80
|
|
ldh [rNR14], a
|
|
ret
|
|
|
|
.Channel2:
|
|
.Channel6:
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
bit NOTE_REST, [hl] ; rest
|
|
jr nz, .ch2_rest
|
|
bit NOTE_NOISE_SAMPLING, [hl]
|
|
jr nz, .ch2_noise_sampling
|
|
bit NOTE_VIBRATO_OVERRIDE, [hl]
|
|
jr nz, .ch2_vibrato_override
|
|
bit NOTE_DUTY_OVERRIDE, [hl]
|
|
ret z
|
|
ld a, [wCurTrackDuty]
|
|
ld d, a
|
|
ldh a, [rNR21]
|
|
and $3f ; sound length
|
|
or d
|
|
ldh [rNR21], a
|
|
ret
|
|
|
|
.ch2_frequency_override ; unreferenced
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR23], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
ldh [rNR24], a
|
|
ret
|
|
|
|
.ch2_vibrato_override
|
|
ld a, [wCurTrackDuty]
|
|
ld d, a
|
|
ldh a, [rNR21]
|
|
and $3f ; sound length
|
|
or d
|
|
ldh [rNR21], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR23], a
|
|
ret
|
|
|
|
.ch2_rest
|
|
ldh a, [rNR52]
|
|
and %10001101 ; ch2 off
|
|
ldh [rNR52], a
|
|
ld hl, rNR20
|
|
call ClearChannel
|
|
ret
|
|
|
|
.ch2_noise_sampling
|
|
ld hl, wCurTrackDuty
|
|
ld a, $3f ; sound length
|
|
or [hl]
|
|
ldh [rNR21], a
|
|
ld a, [wCurTrackVolumeEnvelope]
|
|
ldh [rNR22], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR23], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
or $80 ; initial (restart)
|
|
ldh [rNR24], a
|
|
ret
|
|
|
|
.Channel3:
|
|
.Channel7:
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
bit NOTE_REST, [hl]
|
|
jr nz, .ch3_rest
|
|
bit NOTE_NOISE_SAMPLING, [hl]
|
|
jr nz, .ch3_noise_sampling
|
|
bit NOTE_VIBRATO_OVERRIDE, [hl]
|
|
jr nz, .ch3_vibrato_override
|
|
ret
|
|
|
|
.ch3_frequency_override ; unreferenced
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR33], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
ldh [rNR34], a
|
|
ret
|
|
|
|
.ch3_vibrato_override
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR33], a
|
|
ret
|
|
|
|
.ch3_rest
|
|
ldh a, [rNR52]
|
|
and %10001011 ; ch3 off
|
|
ldh [rNR52], a
|
|
ld hl, rNR30
|
|
call ClearChannel
|
|
ret
|
|
|
|
.ch3_noise_sampling
|
|
ld a, $3f ; sound length
|
|
ldh [rNR31], a
|
|
xor a
|
|
ldh [rNR30], a
|
|
call .load_wave_pattern
|
|
ld a, $80
|
|
ldh [rNR30], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR33], a
|
|
ld a, [wCurTrackFrequency + 1]
|
|
or $80
|
|
ldh [rNR34], a
|
|
ret
|
|
|
|
.load_wave_pattern
|
|
push hl
|
|
ld a, [wCurTrackVolumeEnvelope]
|
|
and $f ; only 0-9 are valid
|
|
ld l, a
|
|
ld h, 0
|
|
; hl << 4
|
|
; each wavepattern is $f bytes long
|
|
; so seeking is done in $10s
|
|
rept 4
|
|
add hl, hl
|
|
endr
|
|
ld de, WaveSamples
|
|
add hl, de
|
|
; load wavepattern into rWave_0-rWave_f
|
|
ld a, [hli]
|
|
ldh [rWave_0], a
|
|
ld a, [hli]
|
|
ldh [rWave_1], a
|
|
ld a, [hli]
|
|
ldh [rWave_2], a
|
|
ld a, [hli]
|
|
ldh [rWave_3], a
|
|
ld a, [hli]
|
|
ldh [rWave_4], a
|
|
ld a, [hli]
|
|
ldh [rWave_5], a
|
|
ld a, [hli]
|
|
ldh [rWave_6], a
|
|
ld a, [hli]
|
|
ldh [rWave_7], a
|
|
ld a, [hli]
|
|
ldh [rWave_8], a
|
|
ld a, [hli]
|
|
ldh [rWave_9], a
|
|
ld a, [hli]
|
|
ldh [rWave_a], a
|
|
ld a, [hli]
|
|
ldh [rWave_b], a
|
|
ld a, [hli]
|
|
ldh [rWave_c], a
|
|
ld a, [hli]
|
|
ldh [rWave_d], a
|
|
ld a, [hli]
|
|
ldh [rWave_e], a
|
|
ld a, [hli]
|
|
ldh [rWave_f], a
|
|
pop hl
|
|
ld a, [wCurTrackVolumeEnvelope]
|
|
and $f0
|
|
sla a
|
|
ldh [rNR32], a
|
|
ret
|
|
|
|
.Channel4:
|
|
.Channel8:
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
bit NOTE_REST, [hl]
|
|
jr nz, .ch4_rest
|
|
bit NOTE_NOISE_SAMPLING, [hl]
|
|
jr nz, .ch4_noise_sampling
|
|
ret
|
|
|
|
.ch4_frequency_override ; unreferenced
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR43], a
|
|
ret
|
|
|
|
.ch4_rest
|
|
ldh a, [rNR52]
|
|
and %10000111 ; ch4 off
|
|
ldh [rNR52], a
|
|
ld hl, rNR40
|
|
call ClearChannel
|
|
ret
|
|
|
|
.ch4_noise_sampling
|
|
ld a, $3f ; sound length
|
|
ldh [rNR41], a
|
|
ld a, [wCurTrackVolumeEnvelope]
|
|
ldh [rNR42], a
|
|
ld a, [wCurTrackFrequency]
|
|
ldh [rNR43], a
|
|
ld a, $80
|
|
ldh [rNR44], a
|
|
ret
|
|
|
|
_CheckSFX:
|
|
; return carry if any sfx channels are active
|
|
ld hl, wChannel5Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .sfxon
|
|
ld hl, wChannel6Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .sfxon
|
|
ld hl, wChannel7Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .sfxon
|
|
ld hl, wChannel8Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .sfxon
|
|
and a
|
|
ret
|
|
|
|
.sfxon
|
|
scf
|
|
ret
|
|
|
|
PlayDanger:
|
|
ld a, [wLowHealthAlarm]
|
|
bit DANGER_ON_F, a
|
|
ret z
|
|
|
|
; Don't do anything if SFX is being played
|
|
and ~(1 << DANGER_ON_F)
|
|
ld d, a
|
|
call _CheckSFX
|
|
jr c, .increment
|
|
|
|
; Play the high tone
|
|
and a
|
|
jr z, .begin
|
|
|
|
; Play the low tone
|
|
cp 16
|
|
jr z, .halfway
|
|
|
|
jr .increment
|
|
|
|
.halfway
|
|
ld hl, DangerSoundLow
|
|
jr .applychannel
|
|
|
|
.begin
|
|
ld hl, DangerSoundHigh
|
|
|
|
.applychannel
|
|
xor a
|
|
ldh [rNR10], a
|
|
ld a, [hli]
|
|
ldh [rNR11], a
|
|
ld a, [hli]
|
|
ldh [rNR12], a
|
|
ld a, [hli]
|
|
ldh [rNR13], a
|
|
ld a, [hli]
|
|
ldh [rNR14], a
|
|
|
|
.increment
|
|
ld a, d
|
|
inc a
|
|
cp 30 ; Ending frame
|
|
jr c, .noreset
|
|
xor a
|
|
.noreset
|
|
; Make sure the danger sound is kept on
|
|
or 1 << DANGER_ON_F
|
|
ld [wLowHealthAlarm], a
|
|
|
|
; Enable channel 1 if it's off
|
|
ld a, [wSoundOutput]
|
|
and $11
|
|
ret nz
|
|
ld a, [wSoundOutput]
|
|
or $11
|
|
ld [wSoundOutput], a
|
|
ret
|
|
|
|
DangerSoundHigh:
|
|
db $80 ; duty 50%
|
|
db $e2 ; volume 14, envelope decrease sweep 2
|
|
db $50 ; frequency: $750
|
|
db $87 ; restart sound
|
|
|
|
DangerSoundLow:
|
|
db $80 ; duty 50%
|
|
db $e2 ; volume 14, envelope decrease sweep 2
|
|
db $ee ; frequency: $6ee
|
|
db $86 ; restart sound
|
|
|
|
FadeMusic:
|
|
; fade music if applicable
|
|
; usage:
|
|
; write to wMusicFade
|
|
; song fades out at the given rate
|
|
; load song id in wMusicFadeID
|
|
; fade new song in
|
|
; notes:
|
|
; max # frames per volume level is $3f
|
|
|
|
; fading?
|
|
ld a, [wMusicFade]
|
|
and a
|
|
ret z
|
|
; has the count ended?
|
|
ld a, [wMusicFadeCount]
|
|
and a
|
|
jr z, .update
|
|
; count down
|
|
dec a
|
|
ld [wMusicFadeCount], a
|
|
ret
|
|
|
|
.update
|
|
ld a, [wMusicFade]
|
|
ld d, a
|
|
; get new count
|
|
and $3f
|
|
ld [wMusicFadeCount], a
|
|
; get SO1 volume
|
|
ld a, [wVolume]
|
|
and VOLUME_SO1_LEVEL
|
|
; which way are we fading?
|
|
bit MUSIC_FADE_IN_F, d
|
|
jr nz, .fadein
|
|
; fading out
|
|
and a
|
|
jr z, .novolume
|
|
dec a
|
|
jr .updatevolume
|
|
|
|
.novolume
|
|
; make sure volume is off
|
|
xor a
|
|
ld [wVolume], a
|
|
; did we just get on a bike?
|
|
ld a, [wPlayerState]
|
|
cp PLAYER_BIKE
|
|
jr z, .bicycle
|
|
push bc
|
|
; restart sound
|
|
call MusicFadeRestart
|
|
; get new song id
|
|
ld a, [wMusicFadeID]
|
|
and a
|
|
jr z, .quit ; this assumes there are fewer than 256 songs!
|
|
ld e, a
|
|
ld a, [wMusicFadeID + 1]
|
|
ld d, a
|
|
; load new song
|
|
call _PlayMusic
|
|
.quit
|
|
; cleanup
|
|
pop bc
|
|
; stop fading
|
|
xor a
|
|
ld [wMusicFade], a
|
|
ret
|
|
|
|
.bicycle
|
|
push bc
|
|
; restart sound
|
|
call MusicFadeRestart
|
|
; this turns the volume up
|
|
; turn it back down
|
|
xor a
|
|
ld [wVolume], a
|
|
; get new song id
|
|
ld a, [wMusicFadeID]
|
|
ld e, a
|
|
ld a, [wMusicFadeID + 1]
|
|
ld d, a
|
|
; load new song
|
|
call _PlayMusic
|
|
pop bc
|
|
; fade in
|
|
ld hl, wMusicFade
|
|
set MUSIC_FADE_IN_F, [hl]
|
|
ret
|
|
|
|
.fadein
|
|
; are we done?
|
|
cp MAX_VOLUME & $f
|
|
jr nc, .maxvolume
|
|
; inc volume
|
|
inc a
|
|
jr .updatevolume
|
|
|
|
.maxvolume
|
|
; we're done
|
|
xor a
|
|
ld [wMusicFade], a
|
|
ret
|
|
|
|
.updatevolume
|
|
; hi = lo
|
|
ld d, a
|
|
swap a
|
|
or d
|
|
ld [wVolume], a
|
|
ret
|
|
|
|
LoadNote:
|
|
; wait for pitch slide to finish
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
bit SOUND_PITCH_SLIDE, [hl]
|
|
ret z
|
|
; get note duration
|
|
ld hl, CHANNEL_NOTE_DURATION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld hl, wCurNoteDuration
|
|
sub [hl]
|
|
jr nc, .ok
|
|
ld a, 1
|
|
.ok
|
|
ld [hl], a
|
|
; get frequency
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; get direction of pitch slide
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld a, e
|
|
sub [hl]
|
|
ld e, a
|
|
ld a, d
|
|
sbc 0
|
|
ld d, a
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
sub [hl]
|
|
jr nc, .greater_than
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
set SOUND_PITCH_SLIDE_DIR, [hl]
|
|
; get frequency
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; ????
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld a, [hl]
|
|
sub e
|
|
ld e, a
|
|
ld a, d
|
|
sbc 0
|
|
ld d, a
|
|
; ????
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
ld a, [hl]
|
|
sub d
|
|
ld d, a
|
|
jr .resume
|
|
|
|
.greater_than
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
res SOUND_PITCH_SLIDE_DIR, [hl]
|
|
; get frequency
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; get distance from pitch slide target
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld a, e
|
|
sub [hl]
|
|
ld e, a
|
|
ld a, d
|
|
sbc 0
|
|
ld d, a
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
sub [hl]
|
|
ld d, a
|
|
.resume
|
|
; de = x * [wCurNoteDuration] + y
|
|
; x + 1 -> d
|
|
; y -> a
|
|
push bc
|
|
ld hl, wCurNoteDuration
|
|
ld b, 0 ; quotient
|
|
.loop
|
|
inc b
|
|
ld a, e
|
|
sub [hl]
|
|
ld e, a
|
|
jr nc, .loop
|
|
ld a, d
|
|
and a
|
|
jr z, .quit
|
|
dec d
|
|
jr .loop
|
|
|
|
.quit
|
|
ld a, e ; remainder
|
|
add [hl]
|
|
ld d, b ; quotient
|
|
pop bc
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT
|
|
add hl, bc
|
|
ld [hl], d ; quotient
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT_FRACTION
|
|
add hl, bc
|
|
ld [hl], a ; remainder
|
|
ld hl, CHANNEL_FIELD25
|
|
add hl, bc
|
|
xor a
|
|
ld [hl], a
|
|
ret
|
|
|
|
HandleTrackVibrato:
|
|
; handle duty, cry pitch, and vibrato
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
bit SOUND_DUTY_LOOP, [hl] ; duty cycle looping
|
|
jr z, .next
|
|
ld hl, CHANNEL_DUTY_CYCLE_PATTERN
|
|
add hl, bc
|
|
ld a, [hl]
|
|
rlca
|
|
rlca
|
|
ld [hl], a
|
|
and $c0
|
|
ld [wCurTrackDuty], a
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_DUTY_OVERRIDE, [hl]
|
|
.next
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
bit SOUND_PITCH_OFFSET, [hl]
|
|
jr z, .vibrato
|
|
ld hl, CHANNEL_PITCH_OFFSET
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
ld hl, wCurTrackFrequency
|
|
ld a, [hli]
|
|
ld h, [hl]
|
|
ld l, a
|
|
add hl, de
|
|
ld e, l
|
|
ld d, h
|
|
ld hl, wCurTrackFrequency
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
.vibrato
|
|
; is vibrato on?
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
bit SOUND_VIBRATO, [hl] ; vibrato
|
|
jr z, .quit
|
|
; is vibrato active for this note yet?
|
|
; is the delay over?
|
|
ld hl, CHANNEL_VIBRATO_DELAY_COUNT
|
|
add hl, bc
|
|
ld a, [hl]
|
|
and a
|
|
jr nz, .subexit
|
|
; is the extent nonzero?
|
|
ld hl, CHANNEL_VIBRATO_EXTENT
|
|
add hl, bc
|
|
ld a, [hl]
|
|
and a
|
|
jr z, .quit
|
|
; save it for later
|
|
ld d, a
|
|
; is it time to toggle vibrato up/down?
|
|
ld hl, CHANNEL_VIBRATO_RATE
|
|
add hl, bc
|
|
ld a, [hl]
|
|
and $f ; count
|
|
jr z, .toggle
|
|
.subexit
|
|
dec [hl]
|
|
jr .quit
|
|
|
|
.toggle
|
|
; refresh count
|
|
ld a, [hl]
|
|
swap [hl]
|
|
or [hl]
|
|
ld [hl], a
|
|
; ????
|
|
ld a, [wCurTrackFrequency]
|
|
ld e, a
|
|
; toggle vibrato up/down
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
bit SOUND_VIBRATO_DIR, [hl] ; vibrato up/down
|
|
jr z, .down
|
|
; up
|
|
; vibrato down
|
|
res SOUND_VIBRATO_DIR, [hl]
|
|
; get the delay
|
|
ld a, d
|
|
and $f ; lo
|
|
;
|
|
ld d, a
|
|
ld a, e
|
|
sub d
|
|
jr nc, .no_carry
|
|
ld a, 0
|
|
jr .no_carry
|
|
|
|
.down
|
|
; vibrato up
|
|
set SOUND_VIBRATO_DIR, [hl]
|
|
; get the delay
|
|
ld a, d
|
|
and $f0 ; hi
|
|
swap a ; move it to lo
|
|
;
|
|
add e
|
|
jr nc, .no_carry
|
|
ld a, $ff
|
|
.no_carry
|
|
ld [wCurTrackFrequency], a
|
|
;
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_VIBRATO_OVERRIDE, [hl]
|
|
.quit
|
|
ret
|
|
|
|
ApplyPitchSlide:
|
|
; quit if pitch slide inactive
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
bit SOUND_PITCH_SLIDE, [hl]
|
|
ret z
|
|
; de = Frequency
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; check whether pitch slide is going up or down
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
bit SOUND_PITCH_SLIDE_DIR, [hl]
|
|
jr z, .decreasing
|
|
; frequency += [Channel*PitchSlideAmount]
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT
|
|
add hl, bc
|
|
ld l, [hl]
|
|
ld h, 0
|
|
add hl, de
|
|
ld d, h
|
|
ld e, l
|
|
; [Channel*Field25] += [Channel*PitchSlideAmountFraction]
|
|
; if rollover: Frequency += 1
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT_FRACTION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld hl, CHANNEL_FIELD25
|
|
add hl, bc
|
|
add [hl]
|
|
ld [hl], a
|
|
; could have done "jr nc, .no_rollover / inc de / .no_rollover"
|
|
ld a, 0
|
|
adc e
|
|
ld e, a
|
|
ld a, 0
|
|
adc d
|
|
ld d, a
|
|
; Compare the dw at [Channel*PitchSlideTarget] to de.
|
|
; If frequency is greater, we're finished.
|
|
; Otherwise, load the frequency and set two flags.
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
ld a, [hl]
|
|
cp d
|
|
jp c, .finished_pitch_slide
|
|
jr nz, .continue_pitch_slide
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld a, [hl]
|
|
cp e
|
|
jp c, .finished_pitch_slide
|
|
jr .continue_pitch_slide
|
|
|
|
.decreasing
|
|
; frequency -= [Channel*PitchSlideAmount]
|
|
ld a, e
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT
|
|
add hl, bc
|
|
ld e, [hl]
|
|
sub e
|
|
ld e, a
|
|
ld a, d
|
|
sbc 0
|
|
ld d, a
|
|
; [Channel*Field25] *= 2
|
|
; if rollover: Frequency -= 1
|
|
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT_FRACTION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
add a
|
|
ld [hl], a
|
|
; could have done "jr nc, .no_rollover / dec de / .no_rollover"
|
|
ld a, e
|
|
sbc 0
|
|
ld e, a
|
|
ld a, d
|
|
sbc 0
|
|
ld d, a
|
|
; Compare the dw at [Channel*PitchSlideTarget] to de.
|
|
; If frequency is lower, we're finished.
|
|
; Otherwise, load the frequency and set two flags.
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
ld a, d
|
|
cp [hl]
|
|
jr c, .finished_pitch_slide
|
|
jr nz, .continue_pitch_slide
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld a, e
|
|
cp [hl]
|
|
jr nc, .continue_pitch_slide
|
|
.finished_pitch_slide
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
res SOUND_PITCH_SLIDE, [hl]
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
res SOUND_PITCH_SLIDE_DIR, [hl]
|
|
ret
|
|
|
|
.continue_pitch_slide
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_FREQ_OVERRIDE, [hl]
|
|
set NOTE_DUTY_OVERRIDE, [hl]
|
|
ret
|
|
|
|
HandleNoise:
|
|
; is noise sampling on?
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_NOISE, [hl] ; noise sampling
|
|
ret z
|
|
; are we in a sfx channel?
|
|
ld a, [wCurChannel]
|
|
bit NOISE_CHAN_F, a
|
|
jr nz, .next
|
|
; is ch8 on? (noise)
|
|
ld hl, wChannel8Flags1
|
|
bit SOUND_CHANNEL_ON, [hl] ; on?
|
|
jr z, .next
|
|
; is ch8 playing noise?
|
|
bit SOUND_NOISE, [hl]
|
|
ret nz ; quit if so
|
|
;
|
|
.next
|
|
ld a, [wNoiseSampleDelay]
|
|
and a
|
|
jr z, ReadNoiseSample
|
|
dec a
|
|
ld [wNoiseSampleDelay], a
|
|
ret
|
|
|
|
ReadNoiseSample:
|
|
; sample struct:
|
|
; [wx] [yy] [zz]
|
|
; w: ? either 2 or 3
|
|
; x: duration
|
|
; zz: volume envelope
|
|
; yy: frequency
|
|
|
|
; de = [wNoiseSampleAddress]
|
|
ld hl, wNoiseSampleAddress
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
|
|
; is it empty?
|
|
ld a, e
|
|
or d
|
|
jr z, .quit
|
|
|
|
ld a, [de]
|
|
inc de
|
|
|
|
cp sound_ret_cmd
|
|
jr z, .quit
|
|
|
|
and $f
|
|
inc a
|
|
ld [wNoiseSampleDelay], a
|
|
ld a, [de]
|
|
inc de
|
|
ld [wCurTrackVolumeEnvelope], a
|
|
ld a, [de]
|
|
inc de
|
|
ld [wCurTrackFrequency], a
|
|
xor a
|
|
ld [wCurTrackFrequency + 1], a
|
|
|
|
ld hl, wNoiseSampleAddress
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_NOISE_SAMPLING, [hl]
|
|
ret
|
|
|
|
.quit
|
|
ret
|
|
|
|
ParseMusic:
|
|
; parses until a note is read or the song is ended
|
|
call GetMusicByte ; store next byte in a
|
|
cp sound_ret_cmd
|
|
jr z, .sound_ret
|
|
cp FIRST_MUSIC_CMD
|
|
jr c, .readnote
|
|
.readcommand
|
|
call ParseMusicCommand
|
|
jr ParseMusic ; start over
|
|
|
|
.readnote
|
|
; wCurMusicByte contains current note
|
|
; special notes
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_SFX, [hl]
|
|
jp nz, ParseSFXOrCry
|
|
bit SOUND_CRY, [hl]
|
|
jp nz, ParseSFXOrCry
|
|
bit SOUND_NOISE, [hl]
|
|
jp nz, GetNoiseSample
|
|
; normal note
|
|
; set note duration (bottom nybble)
|
|
ld a, [wCurMusicByte]
|
|
and $f
|
|
call SetNoteDuration
|
|
; get note pitch (top nybble)
|
|
ld a, [wCurMusicByte]
|
|
swap a
|
|
and $f
|
|
jr z, .rest ; pitch 0 -> rest
|
|
; update pitch
|
|
ld hl, CHANNEL_PITCH
|
|
add hl, bc
|
|
ld [hl], a
|
|
; store pitch in e
|
|
ld e, a
|
|
; store octave in d
|
|
ld hl, CHANNEL_OCTAVE
|
|
add hl, bc
|
|
ld d, [hl]
|
|
; update frequency
|
|
call GetFrequency
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
; ????
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_NOISE_SAMPLING, [hl]
|
|
jp LoadNote
|
|
|
|
.rest
|
|
; note = rest
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_REST, [hl] ; Rest
|
|
ret
|
|
|
|
.sound_ret
|
|
; $ff is reached in music data
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_SUBROUTINE, [hl] ; in a subroutine?
|
|
jr nz, .readcommand ; execute
|
|
ld a, [wCurChannel]
|
|
cp CHAN5
|
|
jr nc, .chan_5to8
|
|
; ????
|
|
ld hl, CHANNEL_STRUCT_LENGTH * NUM_MUSIC_CHANS + CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr nz, .ok
|
|
.chan_5to8
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_CRY, [hl]
|
|
call nz, RestoreVolume
|
|
; end music
|
|
ld a, [wCurChannel]
|
|
cp CHAN5
|
|
jr nz, .ok
|
|
; ????
|
|
xor a
|
|
ldh [rNR10], a ; sweep = 0
|
|
.ok
|
|
; stop playing
|
|
; turn channel off
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
res SOUND_CHANNEL_ON, [hl]
|
|
; note = rest
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_REST, [hl]
|
|
; clear music id & bank
|
|
ld hl, CHANNEL_MUSIC_ID
|
|
add hl, bc
|
|
xor a
|
|
ld [hli], a ; id hi
|
|
ld [hli], a ; id lo
|
|
ld [hli], a ; bank
|
|
ret
|
|
|
|
RestoreVolume:
|
|
; ch5 only
|
|
ld a, [wCurChannel]
|
|
cp CHAN5
|
|
ret nz
|
|
xor a
|
|
ld hl, wChannel6PitchOffset
|
|
ld [hli], a
|
|
ld [hl], a
|
|
ld hl, wChannel8PitchOffset
|
|
ld [hli], a
|
|
ld [hl], a
|
|
ld a, [wLastVolume]
|
|
ld [wVolume], a
|
|
xor a
|
|
ld [wLastVolume], a
|
|
ld [wSFXPriority], a
|
|
ret
|
|
|
|
ParseSFXOrCry:
|
|
; turn noise sampling on
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_NOISE_SAMPLING, [hl] ; noise sample
|
|
; update note duration
|
|
ld a, [wCurMusicByte]
|
|
call SetNoteDuration ; top nybble doesnt matter?
|
|
; update volume envelope from next param
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_VOLUME_ENVELOPE
|
|
add hl, bc
|
|
ld [hl], a
|
|
; update lo frequency from next param
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FREQUENCY
|
|
add hl, bc
|
|
ld [hl], a
|
|
; are we on the last channel? (noise sampling)
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
cp CHAN4
|
|
ret z
|
|
; update hi frequency from next param
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FREQUENCY + 1
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
GetNoiseSample:
|
|
; load ptr to sample header in wNoiseSampleAddress
|
|
; are we on the last channel?
|
|
ld a, [wCurChannel]
|
|
and NUM_MUSIC_CHANS - 1
|
|
cp CHAN4
|
|
; ret if not
|
|
ret nz
|
|
; update note duration
|
|
ld a, [wCurMusicByte]
|
|
and $f
|
|
call SetNoteDuration
|
|
; check current channel
|
|
ld a, [wCurChannel]
|
|
bit NOISE_CHAN_F, a
|
|
jr nz, .sfx
|
|
ld hl, wChannel8Flags1
|
|
bit SOUND_CHANNEL_ON, [hl] ; is ch8 on? (noise)
|
|
ret nz
|
|
ld a, [wMusicNoiseSampleSet]
|
|
jr .next
|
|
|
|
.sfx
|
|
ld a, [wSFXNoiseSampleSet]
|
|
.next
|
|
; load noise sample set id into de
|
|
ld e, a
|
|
ld d, 0
|
|
; load ptr to noise sample set in hl
|
|
ld hl, Drumkits
|
|
add hl, de
|
|
add hl, de
|
|
ld a, [hli]
|
|
ld h, [hl]
|
|
ld l, a
|
|
; get pitch
|
|
ld a, [wCurMusicByte]
|
|
swap a
|
|
; non-rest note?
|
|
and $f
|
|
ret z
|
|
; use 'pitch' to seek noise sample set
|
|
ld e, a
|
|
ld d, 0
|
|
add hl, de
|
|
add hl, de
|
|
; load sample pointer into wNoiseSampleAddress
|
|
ld a, [hli]
|
|
ld [wNoiseSampleAddress], a
|
|
ld a, [hl]
|
|
ld [wNoiseSampleAddress + 1], a
|
|
; clear ????
|
|
xor a
|
|
ld [wNoiseSampleDelay], a
|
|
ret
|
|
|
|
ParseMusicCommand:
|
|
; reload command
|
|
ld a, [wCurMusicByte]
|
|
; get command #
|
|
sub FIRST_MUSIC_CMD
|
|
ld e, a
|
|
ld d, 0
|
|
; seek command pointer
|
|
ld hl, MusicCommands
|
|
add hl, de
|
|
add hl, de
|
|
; jump to the new pointer
|
|
ld a, [hli]
|
|
ld h, [hl]
|
|
ld l, a
|
|
jp hl
|
|
|
|
MusicCommands:
|
|
; entries correspond to audio constants (see macros/scripts/audio.asm)
|
|
table_width 2, MusicCommands
|
|
dw Music_Octave8
|
|
dw Music_Octave7
|
|
dw Music_Octave6
|
|
dw Music_Octave5
|
|
dw Music_Octave4
|
|
dw Music_Octave3
|
|
dw Music_Octave2
|
|
dw Music_Octave1
|
|
dw Music_NoteType ; note length + volume envelope
|
|
dw Music_Transpose
|
|
dw Music_Tempo
|
|
dw Music_DutyCycle
|
|
dw Music_VolumeEnvelope
|
|
dw Music_PitchSweep
|
|
dw Music_DutyCyclePattern
|
|
dw Music_ToggleSFX
|
|
dw Music_PitchSlide
|
|
dw Music_Vibrato
|
|
dw MusicE2 ; unused
|
|
dw Music_ToggleNoise
|
|
dw Music_ForceStereoPanning
|
|
dw Music_Volume
|
|
dw Music_PitchOffset
|
|
dw MusicE7 ; unused
|
|
dw MusicE8 ; unused
|
|
dw Music_TempoRelative
|
|
dw Music_RestartChannel
|
|
dw Music_NewSong
|
|
dw Music_SFXPriorityOn
|
|
dw Music_SFXPriorityOff
|
|
dw MusicEE ; unused
|
|
dw Music_StereoPanning
|
|
dw Music_SFXToggleNoise
|
|
dw MusicF1 ; nothing
|
|
dw MusicF2 ; nothing
|
|
dw MusicF3 ; nothing
|
|
dw MusicF4 ; nothing
|
|
dw MusicF5 ; nothing
|
|
dw MusicF6 ; nothing
|
|
dw MusicF7 ; nothing
|
|
dw MusicF8 ; nothing
|
|
dw MusicF9 ; unused
|
|
dw Music_SetCondition
|
|
dw Music_JumpIf
|
|
dw Music_Jump
|
|
dw Music_Loop
|
|
dw Music_Call
|
|
dw Music_Ret
|
|
assert_table_length $100 - FIRST_MUSIC_CMD
|
|
|
|
MusicF1:
|
|
MusicF2:
|
|
MusicF3:
|
|
MusicF4:
|
|
MusicF5:
|
|
MusicF6:
|
|
MusicF7:
|
|
MusicF8:
|
|
ret
|
|
|
|
Music_Ret:
|
|
; called when $ff is encountered w/ subroutine flag set
|
|
; end music stream
|
|
; return to caller of the subroutine
|
|
; reset subroutine flag
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
res SOUND_SUBROUTINE, [hl]
|
|
; copy LastMusicAddress to MusicAddress
|
|
ld hl, CHANNEL_LAST_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ret
|
|
|
|
Music_Call:
|
|
; call music stream (subroutine)
|
|
; parameters: ll hh ; pointer to subroutine
|
|
; get pointer from next 2 bytes
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
push de
|
|
; copy MusicAddress to LastMusicAddress
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
ld hl, CHANNEL_LAST_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
; load pointer into MusicAddress
|
|
pop de
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
; set subroutine flag
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_SUBROUTINE, [hl]
|
|
ret
|
|
|
|
Music_Jump:
|
|
; jump
|
|
; parameters: ll hh ; pointer
|
|
; get pointer from next 2 bytes
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ret
|
|
|
|
Music_Loop:
|
|
; loops xx - 1 times
|
|
; 00: infinite
|
|
; params: 3
|
|
; xx ll hh
|
|
; xx : loop count
|
|
; ll hh : pointer
|
|
|
|
; get loop count
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_LOOPING, [hl] ; has the loop been initiated?
|
|
jr nz, .checkloop
|
|
and a ; loop counter 0 = infinite
|
|
jr z, .loop
|
|
; initiate loop
|
|
dec a
|
|
set SOUND_LOOPING, [hl] ; set loop flag
|
|
ld hl, CHANNEL_LOOP_COUNT
|
|
add hl, bc
|
|
ld [hl], a ; store loop counter
|
|
.checkloop
|
|
ld hl, CHANNEL_LOOP_COUNT
|
|
add hl, bc
|
|
ld a, [hl]
|
|
and a ; are we done?
|
|
jr z, .endloop
|
|
dec [hl]
|
|
.loop
|
|
; get pointer
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
; load new pointer into MusicAddress
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ret
|
|
|
|
.endloop
|
|
; reset loop flag
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
res SOUND_LOOPING, [hl]
|
|
; skip to next command
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
inc de ; skip
|
|
inc de ; pointer
|
|
ld [hl], d
|
|
dec hl
|
|
ld [hl], e
|
|
ret
|
|
|
|
Music_SetCondition:
|
|
; set condition for a jump
|
|
; used with FB
|
|
; params: 1
|
|
; xx ; condition
|
|
|
|
; set condition
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_CONDITION
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_JumpIf:
|
|
; conditional jump
|
|
; used with FA
|
|
; params: 3
|
|
; xx: condition
|
|
; ll hh: pointer
|
|
|
|
; check condition
|
|
; a = condition
|
|
call GetMusicByte
|
|
; if existing condition matches, jump to new address
|
|
ld hl, CHANNEL_CONDITION
|
|
add hl, bc
|
|
cp [hl]
|
|
jr z, .jump
|
|
; skip to next command
|
|
; get address
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; skip pointer
|
|
inc de
|
|
inc de
|
|
; update address
|
|
ld [hl], d
|
|
dec hl
|
|
ld [hl], e
|
|
ret
|
|
|
|
.jump
|
|
; jump to the new address
|
|
; get pointer
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
; update pointer in MusicAddress
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ret
|
|
|
|
MusicEE:
|
|
; unused
|
|
; conditional jump
|
|
; checks a byte in ram corresponding to the current channel
|
|
; params: 2
|
|
; ll hh ; pointer
|
|
|
|
; if ????, jump
|
|
; get channel
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
ld e, a
|
|
ld d, 0
|
|
; hl = wChannel1JumpCondition + channel id
|
|
ld hl, wChannel1JumpCondition
|
|
add hl, de
|
|
; if set, jump
|
|
ld a, [hl]
|
|
and a
|
|
jr nz, .jump
|
|
; skip to next command
|
|
; get address
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; skip pointer
|
|
inc de
|
|
inc de
|
|
; update address
|
|
ld [hl], d
|
|
dec hl
|
|
ld [hl], e
|
|
ret
|
|
|
|
.jump
|
|
; reset jump flag
|
|
ld [hl], 0
|
|
; de = pointer
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
; update address
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ret
|
|
|
|
MusicF9:
|
|
; unused
|
|
; sets some flag
|
|
; params: 0
|
|
ld a, TRUE
|
|
ld [wUnusedMusicF9Flag], a
|
|
ret
|
|
|
|
MusicE2:
|
|
; unused
|
|
; params: 1
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FIELD2C
|
|
add hl, bc
|
|
ld [hl], a
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_UNKN_0B, [hl]
|
|
ret
|
|
|
|
Music_Vibrato:
|
|
; vibrato
|
|
; params: 2
|
|
; 1: [xx]
|
|
; delay in frames
|
|
; 2: [yz]
|
|
; y: extent
|
|
; z: rate (# frames per cycle)
|
|
|
|
; set vibrato flag?
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_VIBRATO, [hl]
|
|
; start at lower frequency (extent is positive)
|
|
ld hl, CHANNEL_FLAGS3
|
|
add hl, bc
|
|
res SOUND_VIBRATO_DIR, [hl]
|
|
; get delay
|
|
call GetMusicByte
|
|
; update delay
|
|
ld hl, CHANNEL_VIBRATO_DELAY
|
|
add hl, bc
|
|
ld [hl], a
|
|
; update delay count
|
|
ld hl, CHANNEL_VIBRATO_DELAY_COUNT
|
|
add hl, bc
|
|
ld [hl], a
|
|
; update extent
|
|
; this is split into halves only to get added back together at the last second
|
|
; get extent/rate
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_VIBRATO_EXTENT
|
|
add hl, bc
|
|
ld d, a
|
|
; get top nybble
|
|
and $f0
|
|
swap a
|
|
srl a ; halve
|
|
ld e, a
|
|
adc 0 ; round up
|
|
swap a
|
|
or e
|
|
ld [hl], a
|
|
; update rate
|
|
ld hl, CHANNEL_VIBRATO_RATE
|
|
add hl, bc
|
|
; get bottom nybble
|
|
ld a, d
|
|
and $f
|
|
ld d, a
|
|
swap a
|
|
or d
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_PitchSlide:
|
|
; set the target for pitch slide
|
|
; params: 2
|
|
; note duration
|
|
; target note
|
|
call GetMusicByte
|
|
ld [wCurNoteDuration], a
|
|
|
|
call GetMusicByte
|
|
; pitch in e
|
|
ld d, a
|
|
and $f
|
|
ld e, a
|
|
|
|
; octave in d
|
|
ld a, d
|
|
swap a
|
|
and $f
|
|
ld d, a
|
|
call GetFrequency
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET
|
|
add hl, bc
|
|
ld [hl], e
|
|
ld hl, CHANNEL_PITCH_SLIDE_TARGET + 1
|
|
add hl, bc
|
|
ld [hl], d
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_PITCH_SLIDE, [hl]
|
|
ret
|
|
|
|
Music_PitchOffset:
|
|
; tone
|
|
; params: 1 (dw)
|
|
; offset to add to each note frequency
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_PITCH_OFFSET, [hl]
|
|
ld hl, CHANNEL_PITCH_OFFSET + 1
|
|
add hl, bc
|
|
call GetMusicByte
|
|
ld [hld], a
|
|
call GetMusicByte
|
|
ld [hl], a
|
|
ret
|
|
|
|
MusicE7:
|
|
; unused
|
|
; params: 1
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_UNKN_0E, [hl]
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FIELD29
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_DutyCyclePattern:
|
|
; sequence of 4 duty cycles to be looped
|
|
; params: 1 (4 2-bit duty cycle arguments)
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_DUTY_LOOP, [hl] ; duty cycle looping
|
|
; sound duty sequence
|
|
call GetMusicByte
|
|
rrca
|
|
rrca
|
|
ld hl, CHANNEL_DUTY_CYCLE_PATTERN
|
|
add hl, bc
|
|
ld [hl], a
|
|
; update duty cycle
|
|
and $c0 ; only uses top 2 bits
|
|
ld hl, CHANNEL_DUTY_CYCLE
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
MusicE8:
|
|
; unused
|
|
; params: 1
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_UNKN_0D, [hl]
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_FIELD2A
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_ToggleSFX:
|
|
; toggle something
|
|
; params: none
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_SFX, [hl]
|
|
jr z, .on
|
|
res SOUND_SFX, [hl]
|
|
ret
|
|
|
|
.on
|
|
set SOUND_SFX, [hl]
|
|
ret
|
|
|
|
Music_ToggleNoise:
|
|
; toggle music noise sampling
|
|
; can't be used as a straight toggle since the param is not read from on->off
|
|
; params:
|
|
; noise on: 1
|
|
; noise off: 0
|
|
; check if noise sampling is on
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_NOISE, [hl]
|
|
jr z, .on
|
|
; turn noise sampling off
|
|
res SOUND_NOISE, [hl]
|
|
ret
|
|
|
|
.on
|
|
; turn noise sampling on
|
|
set SOUND_NOISE, [hl]
|
|
call GetMusicByte
|
|
ld [wMusicNoiseSampleSet], a
|
|
ret
|
|
|
|
Music_SFXToggleNoise:
|
|
; toggle sfx noise sampling
|
|
; params:
|
|
; on: 1
|
|
; off: 0
|
|
; check if noise sampling is on
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
bit SOUND_NOISE, [hl]
|
|
jr z, .on
|
|
; turn noise sampling off
|
|
res SOUND_NOISE, [hl]
|
|
ret
|
|
|
|
.on
|
|
; turn noise sampling on
|
|
set SOUND_NOISE, [hl]
|
|
call GetMusicByte
|
|
ld [wSFXNoiseSampleSet], a
|
|
ret
|
|
|
|
Music_NoteType:
|
|
; note length
|
|
; # frames per 16th note
|
|
; volume envelope: see Music_VolumeEnvelope
|
|
; params: 2
|
|
; note length
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_NOTE_LENGTH
|
|
add hl, bc
|
|
ld [hl], a
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
cp CHAN4
|
|
ret z
|
|
; volume envelope
|
|
call Music_VolumeEnvelope
|
|
ret
|
|
|
|
Music_PitchSweep:
|
|
; update pitch sweep
|
|
; params: 1
|
|
call GetMusicByte
|
|
ld [wPitchSweep], a
|
|
ld hl, CHANNEL_NOTE_FLAGS
|
|
add hl, bc
|
|
set NOTE_PITCH_SWEEP, [hl]
|
|
ret
|
|
|
|
Music_DutyCycle:
|
|
; duty cycle
|
|
; params: 1
|
|
call GetMusicByte
|
|
rrca
|
|
rrca
|
|
and $c0
|
|
ld hl, CHANNEL_DUTY_CYCLE
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_VolumeEnvelope:
|
|
; volume envelope
|
|
; params: 1
|
|
; hi: volume
|
|
; lo: fade
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_VOLUME_ENVELOPE
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_Tempo:
|
|
; global tempo
|
|
; params: 2
|
|
; de: tempo
|
|
call GetMusicByte
|
|
ld d, a
|
|
call GetMusicByte
|
|
ld e, a
|
|
call SetGlobalTempo
|
|
ret
|
|
|
|
Music_Octave8:
|
|
Music_Octave7:
|
|
Music_Octave6:
|
|
Music_Octave5:
|
|
Music_Octave4:
|
|
Music_Octave3:
|
|
Music_Octave2:
|
|
Music_Octave1:
|
|
; set octave based on lo nybble of the command
|
|
ld hl, CHANNEL_OCTAVE
|
|
add hl, bc
|
|
ld a, [wCurMusicByte]
|
|
and 7
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_Transpose:
|
|
; set starting octave
|
|
; this forces all notes up by the starting octave
|
|
; params: 1
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_TRANSPOSITION
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_StereoPanning:
|
|
; stereo panning
|
|
; params: 1
|
|
; stereo on?
|
|
ld a, [wOptions]
|
|
bit STEREO, a
|
|
jr nz, Music_ForceStereoPanning
|
|
; skip param
|
|
call GetMusicByte
|
|
ret
|
|
|
|
Music_ForceStereoPanning:
|
|
; force panning
|
|
; params: 1
|
|
call SetLRTracks
|
|
call GetMusicByte
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
and [hl]
|
|
ld [hl], a
|
|
ret
|
|
|
|
Music_Volume:
|
|
; set volume
|
|
; params: 1
|
|
; see Volume
|
|
; read param even if it's not used
|
|
call GetMusicByte
|
|
; is the song fading?
|
|
ld a, [wMusicFade]
|
|
and a
|
|
ret nz
|
|
; reload param
|
|
ld a, [wCurMusicByte]
|
|
; set volume
|
|
ld [wVolume], a
|
|
ret
|
|
|
|
Music_TempoRelative:
|
|
; set global tempo to current channel tempo +/- param
|
|
; params: 1 signed
|
|
call GetMusicByte
|
|
ld e, a
|
|
; check sign
|
|
cp $80
|
|
jr nc, .negative
|
|
;positive
|
|
ld d, 0
|
|
jr .ok
|
|
|
|
.negative
|
|
ld d, -1
|
|
.ok
|
|
ld hl, CHANNEL_TEMPO
|
|
add hl, bc
|
|
ld a, [hli]
|
|
ld h, [hl]
|
|
ld l, a
|
|
add hl, de
|
|
ld e, l
|
|
ld d, h
|
|
call SetGlobalTempo
|
|
ret
|
|
|
|
Music_SFXPriorityOn:
|
|
; turn sfx priority on
|
|
; params: none
|
|
ld a, 1
|
|
ld [wSFXPriority], a
|
|
ret
|
|
|
|
Music_SFXPriorityOff:
|
|
; turn sfx priority off
|
|
; params: none
|
|
xor a
|
|
ld [wSFXPriority], a
|
|
ret
|
|
|
|
Music_RestartChannel:
|
|
; restart current channel from channel header (same bank)
|
|
; params: 2 (5)
|
|
; ll hh: pointer to new channel header
|
|
; header format: 0x yy zz
|
|
; x: channel # (0-3)
|
|
; zzyy: pointer to new music data
|
|
|
|
; update music id
|
|
ld hl, CHANNEL_MUSIC_ID
|
|
add hl, bc
|
|
ld a, [hli]
|
|
ld [wMusicID], a
|
|
ld a, [hl]
|
|
ld [wMusicID + 1], a
|
|
; update music bank
|
|
ld hl, CHANNEL_MUSIC_BANK
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld [wMusicBank], a
|
|
; get pointer to new channel header
|
|
call GetMusicByte
|
|
ld l, a
|
|
call GetMusicByte
|
|
ld h, a
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
push bc ; save current channel
|
|
call LoadChannel
|
|
call StartChannel
|
|
pop bc ; restore current channel
|
|
ret
|
|
|
|
Music_NewSong:
|
|
; new song
|
|
; params: 2
|
|
; de: song id
|
|
call GetMusicByte
|
|
ld e, a
|
|
call GetMusicByte
|
|
ld d, a
|
|
push bc
|
|
call _PlayMusic
|
|
pop bc
|
|
ret
|
|
|
|
GetMusicByte:
|
|
; returns byte from current address in a
|
|
; advances to next byte in music data
|
|
; input: bc = start of current channel
|
|
push hl
|
|
push de
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld a, [hli]
|
|
ld e, a
|
|
ld d, [hl]
|
|
ld hl, CHANNEL_MUSIC_BANK
|
|
add hl, bc
|
|
ld a, [hl]
|
|
call _LoadMusicByte ; load data into [wCurMusicByte]
|
|
inc de ; advance to next byte for next time this is called
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
ld a, e
|
|
ld [hli], a
|
|
ld [hl], d
|
|
pop de
|
|
pop hl
|
|
ld a, [wCurMusicByte]
|
|
ret
|
|
|
|
GetFrequency:
|
|
; generate frequency
|
|
; input:
|
|
; d: octave
|
|
; e: pitch
|
|
; output:
|
|
; de: frequency
|
|
|
|
; get octave
|
|
; get starting octave
|
|
ld hl, CHANNEL_TRANSPOSITION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
swap a ; hi nybble
|
|
and $f
|
|
; add current octave
|
|
add d
|
|
push af ; we'll use this later
|
|
; get starting octave
|
|
ld hl, CHANNEL_TRANSPOSITION
|
|
add hl, bc
|
|
ld a, [hl]
|
|
and $f ; lo nybble
|
|
ld l, a ; ok
|
|
ld d, 0
|
|
ld h, d
|
|
add hl, de ; add current pitch
|
|
add hl, hl ; skip 2 bytes for each
|
|
ld de, FrequencyTable
|
|
add hl, de
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; get our octave
|
|
pop af
|
|
; shift right by [7 - octave] bits
|
|
.loop
|
|
; [7 - octave] loops
|
|
cp $7
|
|
jr nc, .ok
|
|
; sra de
|
|
sra d
|
|
rr e
|
|
inc a
|
|
jr .loop
|
|
|
|
.ok
|
|
ld a, d
|
|
and $7 ; top 3 bits for frequency (11 total)
|
|
ld d, a
|
|
ret
|
|
|
|
SetNoteDuration:
|
|
; input: a = note duration in 16ths
|
|
; store delay units in de
|
|
inc a
|
|
ld e, a
|
|
ld d, 0
|
|
ld hl, CHANNEL_NOTE_LENGTH
|
|
add hl, bc
|
|
ld a, [hl]
|
|
; multiply NoteLength by delay units
|
|
ld l, 0 ; just multiply
|
|
call .Multiply
|
|
ld a, l ; low
|
|
; store Tempo in de
|
|
ld hl, CHANNEL_TEMPO
|
|
add hl, bc
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; add ???? to the next result
|
|
ld hl, CHANNEL_FIELD16
|
|
add hl, bc
|
|
ld l, [hl]
|
|
; multiply Tempo by last result (NoteLength * LOW(delay))
|
|
call .Multiply
|
|
; copy result to de
|
|
ld e, l
|
|
ld d, h
|
|
; store result in ????
|
|
ld hl, CHANNEL_FIELD16
|
|
add hl, bc
|
|
ld [hl], e
|
|
; store result in NoteDuration
|
|
ld hl, CHANNEL_NOTE_DURATION
|
|
add hl, bc
|
|
ld [hl], d
|
|
ret
|
|
|
|
.Multiply:
|
|
; multiplies a and de
|
|
; adds the result to l
|
|
; stores the result in hl
|
|
ld h, 0
|
|
.loop
|
|
; halve a
|
|
srl a
|
|
; is there a remainder?
|
|
jr nc, .skip
|
|
; add it to the result
|
|
add hl, de
|
|
.skip
|
|
; add de, de
|
|
sla e
|
|
rl d
|
|
; are we done?
|
|
and a
|
|
jr nz, .loop
|
|
ret
|
|
|
|
SetGlobalTempo:
|
|
push bc ; save current channel
|
|
; are we dealing with music or sfx?
|
|
ld a, [wCurChannel]
|
|
cp CHAN5
|
|
jr nc, .sfxchannels
|
|
ld bc, wChannel1
|
|
call Tempo
|
|
ld bc, wChannel2
|
|
call Tempo
|
|
ld bc, wChannel3
|
|
call Tempo
|
|
ld bc, wChannel4
|
|
call Tempo
|
|
jr .end
|
|
|
|
.sfxchannels
|
|
ld bc, wChannel5
|
|
call Tempo
|
|
ld bc, wChannel6
|
|
call Tempo
|
|
ld bc, wChannel7
|
|
call Tempo
|
|
ld bc, wChannel8
|
|
call Tempo
|
|
.end
|
|
pop bc ; restore current channel
|
|
ret
|
|
|
|
Tempo:
|
|
; input:
|
|
; de: note length
|
|
; update Tempo
|
|
ld hl, CHANNEL_TEMPO
|
|
add hl, bc
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
; clear ????
|
|
xor a
|
|
ld hl, CHANNEL_FIELD16
|
|
add hl, bc
|
|
ld [hl], a
|
|
ret
|
|
|
|
StartChannel:
|
|
call SetLRTracks
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_CHANNEL_ON, [hl] ; turn channel on
|
|
ret
|
|
|
|
SetLRTracks:
|
|
; set tracks for a the current channel to default
|
|
; seems to be redundant since this is overwritten by stereo data later
|
|
push de
|
|
; store current channel in de
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
ld e, a
|
|
ld d, 0
|
|
call GetLRTracks
|
|
add hl, de ; de = channel 0-3
|
|
ld a, [hl]
|
|
; load lr tracks into Tracks
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
ld [hl], a
|
|
pop de
|
|
ret
|
|
|
|
_PlayMusic::
|
|
; load music
|
|
call MusicOff
|
|
ld hl, wMusicID
|
|
ld [hl], e ; song number
|
|
inc hl
|
|
ld [hl], d ; (always 0)
|
|
ld hl, Music
|
|
add hl, de ; three
|
|
add hl, de ; byte
|
|
add hl, de ; pointer
|
|
ld a, [hli]
|
|
ld [wMusicBank], a
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl] ; music header address
|
|
call LoadMusicByte ; store first byte of music header in a
|
|
rlca
|
|
rlca
|
|
maskbits NUM_MUSIC_CHANS
|
|
inc a
|
|
.loop
|
|
; start playing channels
|
|
push af
|
|
call LoadChannel
|
|
call StartChannel
|
|
pop af
|
|
dec a
|
|
jr nz, .loop
|
|
xor a
|
|
ld [wUnusedMusicF9Flag], a
|
|
ld [wChannel1JumpCondition], a
|
|
ld [wChannel2JumpCondition], a
|
|
ld [wChannel3JumpCondition], a
|
|
ld [wChannel4JumpCondition], a
|
|
ld [wNoiseSampleAddress], a
|
|
ld [wNoiseSampleAddress + 1], a
|
|
ld [wNoiseSampleDelay], a
|
|
ld [wMusicNoiseSampleSet], a
|
|
call MusicOn
|
|
ret
|
|
|
|
_PlayCry::
|
|
; Play cry de using parameters:
|
|
; wCryPitch
|
|
; wCryLength
|
|
|
|
call MusicOff
|
|
|
|
; Overload the music id with the cry id
|
|
ld hl, wMusicID
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
|
|
; 3-byte pointers (bank, address)
|
|
ld hl, Cries
|
|
add hl, de
|
|
add hl, de
|
|
add hl, de
|
|
|
|
ld a, [hli]
|
|
ld [wMusicBank], a
|
|
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
|
|
; Read the cry's sound header
|
|
call LoadMusicByte
|
|
; Top 2 bits contain the number of channels
|
|
rlca
|
|
rlca
|
|
maskbits NUM_MUSIC_CHANS
|
|
|
|
; For each channel:
|
|
inc a
|
|
.loop
|
|
push af
|
|
call LoadChannel ; bc = current channel
|
|
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_CRY, [hl]
|
|
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_PITCH_OFFSET, [hl]
|
|
|
|
ld hl, CHANNEL_PITCH_OFFSET
|
|
add hl, bc
|
|
ld a, [wCryPitch]
|
|
ld [hli], a
|
|
ld a, [wCryPitch + 1]
|
|
ld [hl], a
|
|
|
|
; No tempo for channel 4
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
cp CHAN4
|
|
jr nc, .start
|
|
|
|
; Tempo is effectively length
|
|
ld hl, CHANNEL_TEMPO
|
|
add hl, bc
|
|
ld a, [wCryLength]
|
|
ld [hli], a
|
|
ld a, [wCryLength + 1]
|
|
ld [hl], a
|
|
.start
|
|
call StartChannel
|
|
ld a, [wStereoPanningMask]
|
|
and a
|
|
jr z, .next
|
|
|
|
; Stereo only: Play cry from the monster's side.
|
|
; This only applies in-battle.
|
|
|
|
ld a, [wOptions]
|
|
bit STEREO, a
|
|
jr z, .next
|
|
|
|
; [CHANNEL_TRACKS] &= [wCryTracks]
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
ld a, [hl]
|
|
ld hl, wCryTracks
|
|
and [hl]
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
ld [hl], a
|
|
|
|
.next
|
|
pop af
|
|
dec a
|
|
jr nz, .loop
|
|
|
|
; Cries play at max volume, so we save the current volume for later.
|
|
ld a, [wLastVolume]
|
|
and a
|
|
jr nz, .end
|
|
|
|
ld a, [wVolume]
|
|
ld [wLastVolume], a
|
|
ld a, MAX_VOLUME
|
|
ld [wVolume], a
|
|
|
|
.end
|
|
ld a, 1 ; stop playing music
|
|
ld [wSFXPriority], a
|
|
call MusicOn
|
|
ret
|
|
|
|
_PlaySFX::
|
|
; clear channels if they aren't already
|
|
call MusicOff
|
|
ld hl, wChannel5Flags1
|
|
bit SOUND_CHANNEL_ON, [hl] ; ch5 on?
|
|
jr z, .ch6
|
|
res SOUND_CHANNEL_ON, [hl] ; turn it off
|
|
xor a
|
|
ldh [rNR11], a ; length/wavepattern = 0
|
|
ld a, $8
|
|
ldh [rNR12], a ; envelope = 0
|
|
xor a
|
|
ldh [rNR13], a ; frequency lo = 0
|
|
ld a, $80
|
|
ldh [rNR14], a ; restart sound (freq hi = 0)
|
|
xor a
|
|
ld [wPitchSweep], a ; pitch sweep off
|
|
ldh [rNR10], a ; pitch sweep off
|
|
.ch6
|
|
ld hl, wChannel6Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr z, .ch7
|
|
res SOUND_CHANNEL_ON, [hl] ; turn it off
|
|
xor a
|
|
ldh [rNR21], a ; length/wavepattern = 0
|
|
ld a, $8
|
|
ldh [rNR22], a ; envelope = 0
|
|
xor a
|
|
ldh [rNR23], a ; frequency lo = 0
|
|
ld a, $80
|
|
ldh [rNR24], a ; restart sound (freq hi = 0)
|
|
.ch7
|
|
ld hl, wChannel7Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr z, .ch8
|
|
res SOUND_CHANNEL_ON, [hl] ; turn it off
|
|
xor a
|
|
ldh [rNR30], a ; sound mode #3 off
|
|
ldh [rNR31], a ; length/wavepattern = 0
|
|
ld a, $8
|
|
ldh [rNR32], a ; envelope = 0
|
|
xor a
|
|
ldh [rNR33], a ; frequency lo = 0
|
|
ld a, $80
|
|
ldh [rNR34], a ; restart sound (freq hi = 0)
|
|
.ch8
|
|
ld hl, wChannel8Flags1
|
|
bit SOUND_CHANNEL_ON, [hl]
|
|
jr z, .chscleared
|
|
res SOUND_CHANNEL_ON, [hl] ; turn it off
|
|
xor a
|
|
ldh [rNR41], a ; length/wavepattern = 0
|
|
ld a, $8
|
|
ldh [rNR42], a ; envelope = 0
|
|
xor a
|
|
ldh [rNR43], a ; frequency lo = 0
|
|
ld a, $80
|
|
ldh [rNR44], a ; restart sound (freq hi = 0)
|
|
xor a
|
|
ld [wNoiseSampleAddress], a
|
|
ld [wNoiseSampleAddress + 1], a
|
|
.chscleared
|
|
; start reading sfx header for # chs
|
|
ld hl, wMusicID
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
ld hl, SFX
|
|
add hl, de ; three
|
|
add hl, de ; byte
|
|
add hl, de ; pointers
|
|
; get bank
|
|
ld a, [hli]
|
|
ld [wMusicBank], a
|
|
; get address
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
; get # channels
|
|
call LoadMusicByte
|
|
rlca ; top 2
|
|
rlca ; bits
|
|
maskbits NUM_MUSIC_CHANS
|
|
inc a ; # channels -> # loops
|
|
.startchannels
|
|
push af
|
|
call LoadChannel ; bc = current channel
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_SFX, [hl]
|
|
call StartChannel
|
|
pop af
|
|
dec a
|
|
jr nz, .startchannels
|
|
call MusicOn
|
|
xor a
|
|
ld [wSFXPriority], a
|
|
ret
|
|
|
|
PlayStereoSFX::
|
|
; play sfx de
|
|
|
|
call MusicOff
|
|
|
|
; standard procedure if stereo's off
|
|
ld a, [wOptions]
|
|
bit STEREO, a
|
|
jp z, _PlaySFX
|
|
|
|
; else, let's go ahead with this
|
|
ld hl, wMusicID
|
|
ld [hl], e
|
|
inc hl
|
|
ld [hl], d
|
|
|
|
; get sfx ptr
|
|
ld hl, SFX
|
|
add hl, de
|
|
add hl, de
|
|
add hl, de
|
|
|
|
; bank
|
|
ld a, [hli]
|
|
ld [wMusicBank], a
|
|
; address
|
|
ld e, [hl]
|
|
inc hl
|
|
ld d, [hl]
|
|
|
|
; bit 2-3
|
|
call LoadMusicByte
|
|
rlca
|
|
rlca
|
|
maskbits NUM_MUSIC_CHANS
|
|
inc a
|
|
|
|
.loop
|
|
push af
|
|
call LoadChannel
|
|
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_SFX, [hl]
|
|
|
|
push de
|
|
; get tracks for this channel
|
|
ld a, [wCurChannel]
|
|
maskbits NUM_MUSIC_CHANS
|
|
ld e, a
|
|
ld d, 0
|
|
call GetLRTracks
|
|
add hl, de
|
|
ld a, [hl]
|
|
ld hl, wStereoPanningMask
|
|
and [hl]
|
|
|
|
ld hl, CHANNEL_TRACKS
|
|
add hl, bc
|
|
ld [hl], a
|
|
|
|
ld hl, CHANNEL_FIELD30
|
|
add hl, bc
|
|
ld [hl], a
|
|
|
|
ld a, [wCryTracks]
|
|
cp 2 ; ch 1-2
|
|
jr c, .skip
|
|
|
|
; ch3-4
|
|
ld a, [wSFXDuration]
|
|
|
|
ld hl, CHANNEL_FIELD2E
|
|
add hl, bc
|
|
ld [hl], a
|
|
|
|
ld hl, CHANNEL_FIELD2F
|
|
add hl, bc
|
|
ld [hl], a
|
|
|
|
ld hl, CHANNEL_FLAGS2
|
|
add hl, bc
|
|
set SOUND_UNKN_0F, [hl]
|
|
|
|
.skip
|
|
pop de
|
|
|
|
; turn channel on
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
set SOUND_CHANNEL_ON, [hl] ; on
|
|
|
|
; done?
|
|
pop af
|
|
dec a
|
|
jr nz, .loop
|
|
|
|
; we're done
|
|
call MusicOn
|
|
ret
|
|
|
|
LoadChannel:
|
|
; input: de = audio pointer
|
|
; sets bc to current channel pointer
|
|
call LoadMusicByte
|
|
inc de
|
|
maskbits NUM_CHANNELS
|
|
ld [wCurChannel], a
|
|
ld c, a
|
|
ld b, 0
|
|
ld hl, ChannelPointers
|
|
add hl, bc
|
|
add hl, bc
|
|
ld c, [hl]
|
|
inc hl
|
|
ld b, [hl] ; bc = channel pointer
|
|
ld hl, CHANNEL_FLAGS1
|
|
add hl, bc
|
|
res SOUND_CHANNEL_ON, [hl] ; channel off
|
|
call ChannelInit
|
|
; load music pointer
|
|
ld hl, CHANNEL_MUSIC_ADDRESS
|
|
add hl, bc
|
|
call LoadMusicByte
|
|
ld [hli], a
|
|
inc de
|
|
call LoadMusicByte
|
|
ld [hl], a
|
|
inc de
|
|
; load music id
|
|
ld hl, CHANNEL_MUSIC_ID
|
|
add hl, bc
|
|
ld a, [wMusicID]
|
|
ld [hli], a
|
|
ld a, [wMusicID + 1]
|
|
ld [hl], a
|
|
; load music bank
|
|
ld hl, CHANNEL_MUSIC_BANK
|
|
add hl, bc
|
|
ld a, [wMusicBank]
|
|
ld [hl], a
|
|
ret
|
|
|
|
ChannelInit:
|
|
; make sure channel is cleared
|
|
; set default tempo and note length in case nothing is loaded
|
|
; input:
|
|
; bc = channel struct pointer
|
|
push de
|
|
xor a
|
|
; get channel struct location and length
|
|
ld hl, CHANNEL_MUSIC_ID ; start
|
|
add hl, bc
|
|
ld e, CHANNEL_STRUCT_LENGTH ; channel struct length
|
|
; clear channel
|
|
.loop
|
|
ld [hli], a
|
|
dec e
|
|
jr nz, .loop
|
|
; set tempo to default ($100)
|
|
ld hl, CHANNEL_TEMPO
|
|
add hl, bc
|
|
xor a
|
|
ld [hli], a
|
|
inc a
|
|
ld [hl], a
|
|
; set note length to default ($1) (fast)
|
|
ld hl, CHANNEL_NOTE_LENGTH
|
|
add hl, bc
|
|
ld [hl], a
|
|
pop de
|
|
ret
|
|
|
|
LoadMusicByte::
|
|
; input:
|
|
; de = current music address
|
|
; output:
|
|
; a = wCurMusicByte
|
|
ld a, [wMusicBank]
|
|
call _LoadMusicByte
|
|
ld a, [wCurMusicByte]
|
|
ret
|
|
|
|
INCLUDE "audio/notes.asm"
|
|
|
|
INCLUDE "audio/wave_samples.asm"
|
|
|
|
INCLUDE "audio/drumkits.asm"
|
|
|
|
GetLRTracks:
|
|
; gets the default sound l/r channels
|
|
; stores mono/stereo table in hl
|
|
ld a, [wOptions]
|
|
bit STEREO, a
|
|
; made redundant, could have had a purpose in gold
|
|
jr nz, .stereo
|
|
ld hl, MonoTracks
|
|
ret
|
|
|
|
.stereo
|
|
ld hl, StereoTracks
|
|
ret
|
|
|
|
MonoTracks:
|
|
; bit corresponds to track #
|
|
; hi: left channel
|
|
; lo: right channel
|
|
db $11, $22, $44, $88
|
|
|
|
StereoTracks:
|
|
; made redundant
|
|
; seems to be modified on a per-song basis
|
|
db $11, $22, $44, $88
|
|
|
|
ChannelPointers:
|
|
table_width 2, ChannelPointers
|
|
; music channels
|
|
dw wChannel1
|
|
dw wChannel2
|
|
dw wChannel3
|
|
dw wChannel4
|
|
assert_table_length NUM_MUSIC_CHANS
|
|
; sfx channels
|
|
dw wChannel5
|
|
dw wChannel6
|
|
dw wChannel7
|
|
dw wChannel8
|
|
assert_table_length NUM_CHANNELS
|
|
|
|
ClearChannels::
|
|
; runs ClearChannel for all 4 channels
|
|
ld hl, rNR50
|
|
xor a
|
|
ld [hli], a
|
|
ld [hli], a
|
|
ld a, $80
|
|
ld [hli], a
|
|
ld hl, rNR10
|
|
ld e, NUM_MUSIC_CHANS
|
|
.loop
|
|
call ClearChannel
|
|
dec e
|
|
jr nz, .loop
|
|
ret
|
|
|
|
ClearChannel:
|
|
; input: hl = beginning hw sound register (rNR10, rNR20, rNR30, rNR40)
|
|
; output: 00 00 80 00 80
|
|
|
|
; sound channel 1 2 3 4
|
|
xor a
|
|
ld [hli], a ; rNR10, rNR20, rNR30, rNR40 ; sweep = 0
|
|
|
|
ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
|
|
ld a, $8
|
|
ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
|
|
xor a
|
|
ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
|
|
ld a, $80
|
|
ld [hli], a ; rNR14, rNR24, rNR34, rNR44 ; restart sound (freq hi = 0)
|
|
ret
|
|
|
|
PlayTrainerEncounterMusic::
|
|
; input: e = trainer type
|
|
; turn fade off
|
|
xor a
|
|
ld [wMusicFade], a
|
|
; play nothing for one frame
|
|
push de
|
|
ld de, MUSIC_NONE
|
|
call PlayMusic
|
|
call DelayFrame
|
|
; play new song
|
|
call MaxVolume
|
|
pop de
|
|
ld d, $00
|
|
ld hl, TrainerEncounterMusic
|
|
add hl, de
|
|
ld e, [hl]
|
|
call PlayMusic
|
|
ret
|