pokecrystal-board/audio/engine.asm

2853 lines
47 KiB
NASM
Raw Normal View History

; The entire sound engine. Uses section "audio" in WRAM.
; Interfaces are in bank 0.
; Notable functions:
; FadeMusic
2013-10-08 10:05:52 -07:00
; PlayStereoSFX
_InitSound::
; restart sound operation
; clear all relevant hardware registers & wram
push hl
push de
push bc
push af
call MusicOff
2013-02-19 23:46:40 -08:00
ld hl, rNR50 ; channel control registers
xor a
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR50 ; volume/vin
ld [hli], a ; rNR51 ; sfx channels
ld a, $80 ; all channels on
ld [hli], a ; rNR52 ; music channels
2013-02-19 23:46:40 -08:00
ld hl, rNR10 ; sound channel registers
2017-12-09 10:28:23 -08:00
ld e, NUM_MUSIC_CHANS
.clearsound
; sound channel 1 2 3 4
xor a
ld [hli], a ; rNR10, rNR20, rNR30, rNR40 ; sweep = 0
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
ld a, $8
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
xor a
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
ld a, $80
2013-02-19 23:46:40 -08:00
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
2017-12-09 10:28:23 -08:00
ld a, MAX_VOLUME
ld [wVolume], a
call MusicOn
pop af
pop bc
pop de
pop hl
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicFadeRestart:
; restart but keep the music id to fade in to
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID + 1]
push af
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID]
push af
call _InitSound
pop af
2018-01-23 14:39:09 -08:00
ld [wMusicFadeID], a
pop af
2018-01-23 14:39:09 -08:00
ld [wMusicFadeID + 1], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicOn:
2015-12-06 19:36:09 -08:00
ld a, 1
2018-01-23 14:39:09 -08:00
ld [wMusicPlaying], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicOff:
xor a
2018-01-23 14:39:09 -08:00
ld [wMusicPlaying], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
_UpdateSound::
; called once per frame
; no use updating audio if it's not playing
2018-01-23 14:39:09 -08:00
ld a, [wMusicPlaying]
and a
ret z
; start at ch1
xor a
2018-01-23 14:39:09 -08:00
ld [wCurChannel], a ; just
ld [wSoundOutput], a ; off
ld bc, wChannel1
.loop
; is the channel active?
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
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]
2015-12-06 19:36:09 -08:00
cp $2 ; 1 or 0?
jr c, .noteover
dec [hl]
2016-05-10 11:33:24 -07:00
jr .continue_sound_update
2015-12-07 15:33:04 -08:00
.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
2016-05-10 11:33:24 -07:00
.continue_sound_update
call ApplyPitchSlide
; duty cycle
ld hl, CHANNEL_DUTY_CYCLE
add hl, bc
ld a, [hli]
2015-12-07 08:28:58 -08:00
ld [wCurTrackDuty], a
; volume envelope
ld a, [hli]
ld [wCurTrackVolumeEnvelope], a
; frequency
ld a, [hli]
2015-12-07 08:28:58 -08:00
ld [wCurTrackFrequency], a
ld a, [hl]
2015-12-07 08:28:58 -08:00
ld [wCurTrackFrequency + 1], a
2016-05-10 13:38:40 -07:00
; vibrato, noise
2016-05-10 11:33:24 -07:00
call HandleTrackVibrato ; handle vibrato and other things
call HandleNoise
; turn off music when playing sfx?
2018-01-23 14:39:09 -08:00
ld a, [wSFXPriority]
and a
jr z, .next
; are we in a sfx channel right now?
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
cp NUM_MUSIC_CHANS
jr nc, .next
; are any sfx channels active?
; if so, mute
ld hl, wChannel5Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .restnote
ld hl, wChannel6Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .restnote
ld hl, wChannel7Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .restnote
ld hl, wChannel8Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr z, .next
.restnote
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2015-12-07 15:33:04 -08:00
set NOTE_REST, [hl] ; Rest
.next
; are we in a sfx channel right now?
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
cp NUM_MUSIC_CHANS
2016-05-10 11:33:24 -07:00
jr nc, .sfx_channel
ld hl, CHANNEL_STRUCT_LENGTH * NUM_MUSIC_CHANS + CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
2016-05-10 11:33:24 -07:00
jr nz, .sound_channel_on
.sfx_channel
call UpdateChannels
ld hl, CHANNEL_TRACKS
add hl, bc
2018-01-23 14:39:09 -08:00
ld a, [wSoundOutput]
or [hl]
2018-01-23 14:39:09 -08:00
ld [wSoundOutput], a
2016-05-10 11:33:24 -07:00
.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
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
inc a
2018-01-23 14:39:09 -08:00
ld [wCurChannel], a
cp NUM_CHANNELS ; are we done?
jp nz, .loop ; do it all again
2014-04-21 21:02:40 -07:00
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
2018-01-23 14:39:09 -08:00
ld a, [wSoundOutput]
ldh [rNR51], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
UpdateChannels:
ld hl, .ChannelFnPtrs
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2015-12-06 19:36:09 -08:00
and $7
add a
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
jp hl
.ChannelFnPtrs:
dw .Channel1
dw .Channel2
dw .Channel3
dw .Channel4
; sfx ch ptrs are identical to music chs
; ..except 5
dw .Channel5
dw .Channel6
dw .Channel7
dw .Channel8
.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
2015-12-07 15:33:04 -08:00
bit NOTE_REST, [hl] ; rest
2020-06-29 16:35:47 -07:00
jr nz, .ch1_rest
bit NOTE_NOISE_SAMPLING, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch1_noise_sampling
bit NOTE_FREQ_OVERRIDE, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch1_frequency_override
bit NOTE_VIBRATO_OVERRIDE, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch1_vibrato_override
jr .ch1_check_duty_override
2015-12-07 15:33:04 -08:00
2020-06-29 16:35:47 -07:00
.ch1_frequency_override
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR13], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
ldh [rNR14], a
2020-06-29 16:35:47 -07:00
.ch1_check_duty_override
bit NOTE_DUTY_OVERRIDE, [hl]
ret z
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackDuty]
ld d, a
ldh a, [rNR11]
2015-12-06 19:36:09 -08:00
and $3f ; sound length
or d
ldh [rNR11], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch1_vibrato_override
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackDuty]
ld d, a
ldh a, [rNR11]
2015-12-06 19:36:09 -08:00
and $3f ; sound length
or d
ldh [rNR11], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR13], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch1_rest
ldh a, [rNR52]
2015-12-06 19:36:09 -08:00
and %10001110 ; ch1 off
ldh [rNR52], a
2013-02-19 23:46:40 -08:00
ld hl, rNR10
call ClearChannel
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch1_noise_sampling
2015-12-07 08:28:58 -08:00
ld hl, wCurTrackDuty
ld a, $3f ; sound length
or [hl]
ldh [rNR11], a
ld a, [wCurTrackVolumeEnvelope]
ldh [rNR12], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR13], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
2015-12-06 19:36:09 -08:00
or $80
ldh [rNR14], a
ret
.Channel2:
.Channel6:
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2015-12-07 15:33:04 -08:00
bit NOTE_REST, [hl] ; rest
2020-06-29 16:35:47 -07:00
jr nz, .ch2_rest
bit NOTE_NOISE_SAMPLING, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch2_noise_sampling
bit NOTE_VIBRATO_OVERRIDE, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch2_vibrato_override
bit NOTE_DUTY_OVERRIDE, [hl]
ret z
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackDuty]
ld d, a
ldh a, [rNR21]
2015-12-06 19:36:09 -08:00
and $3f ; sound length
or d
ldh [rNR21], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch2_frequency_override ; unreferenced
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR23], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
ldh [rNR24], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch2_vibrato_override
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackDuty]
ld d, a
ldh a, [rNR21]
2015-12-06 19:36:09 -08:00
and $3f ; sound length
or d
ldh [rNR21], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR23], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch2_rest
ldh a, [rNR52]
2015-12-06 19:36:09 -08:00
and %10001101 ; ch2 off
ldh [rNR52], a
ld hl, rNR20
call ClearChannel
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch2_noise_sampling
2015-12-07 08:28:58 -08:00
ld hl, wCurTrackDuty
ld a, $3f ; sound length
or [hl]
ldh [rNR21], a
ld a, [wCurTrackVolumeEnvelope]
ldh [rNR22], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR23], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
2015-12-06 19:36:09 -08:00
or $80 ; initial (restart)
ldh [rNR24], a
ret
.Channel3:
.Channel7:
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2020-06-29 16:35:47 -07:00
bit NOTE_REST, [hl]
jr nz, .ch3_rest
bit NOTE_NOISE_SAMPLING, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch3_noise_sampling
bit NOTE_VIBRATO_OVERRIDE, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch3_vibrato_override
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch3_frequency_override ; unreferenced
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR33], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
ldh [rNR34], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch3_vibrato_override
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR33], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch3_rest
ldh a, [rNR52]
2015-12-06 19:36:09 -08:00
and %10001011 ; ch3 off
ldh [rNR52], a
2013-02-19 23:46:40 -08:00
ld hl, rNR30
call ClearChannel
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch3_noise_sampling
2018-01-11 09:00:01 -08:00
ld a, $3f ; sound length
ldh [rNR31], a
xor a
ldh [rNR30], a
2020-06-29 16:35:47 -07:00
call .load_wave_pattern
ld a, $80
ldh [rNR30], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR33], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency + 1]
2015-12-06 19:36:09 -08:00
or $80
ldh [rNR34], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.load_wave_pattern
push hl
ld a, [wCurTrackVolumeEnvelope]
2015-12-06 19:36:09 -08:00
and $f ; only 0-9 are valid
ld l, a
2015-12-06 19:36:09 -08:00
ld h, 0
; hl << 4
2015-12-06 19:36:09 -08:00
; each wavepattern is $f bytes long
; so seeking is done in $10s
2015-07-20 19:18:18 -07:00
rept 4
add hl, hl
2015-07-20 19:18:18 -07:00
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]
2015-12-06 19:36:09 -08:00
and $f0
sla a
ldh [rNR32], a
ret
.Channel4:
.Channel8:
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2020-06-29 16:35:47 -07:00
bit NOTE_REST, [hl]
jr nz, .ch4_rest
bit NOTE_NOISE_SAMPLING, [hl]
2020-06-29 16:35:47 -07:00
jr nz, .ch4_noise_sampling
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch4_frequency_override ; unreferenced
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR43], a
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch4_rest
ldh a, [rNR52]
2015-12-06 19:36:09 -08:00
and %10000111 ; ch4 off
ldh [rNR52], a
ld hl, rNR40
call ClearChannel
ret
2015-12-07 08:28:58 -08:00
2020-06-29 16:35:47 -07:00
.ch4_noise_sampling
ld a, $3f ; sound length
ldh [rNR41], a
ld a, [wCurTrackVolumeEnvelope]
ldh [rNR42], a
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ldh [rNR43], a
ld a, $80
ldh [rNR44], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
_CheckSFX:
; return carry if any sfx channels are active
ld hl, wChannel5Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .sfxon
ld hl, wChannel6Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .sfxon
ld hl, wChannel7Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .sfxon
ld hl, wChannel8Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .sfxon
and a
ret
2015-12-07 08:28:58 -08:00
.sfxon
scf
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
PlayDanger:
ld a, [wLowHealthAlarm]
bit DANGER_ON_F, a
ret z
2018-06-04 05:34:35 -07:00
; Don't do anything if SFX is being played
and $ff ^ (1 << DANGER_ON_F)
ld d, a
call _CheckSFX
2018-06-04 05:34:35 -07:00
jr c, .increment
; Play the high tone
and a
2018-06-04 05:34:35 -07:00
jr z, .begin
; Play the low tone
cp 16
jr z, .halfway
jr .increment
.halfway
ld hl, DangerSoundLow
jr .applychannel
.begin
2018-06-05 01:13:09 -07:00
ld hl, DangerSoundHigh
2018-06-04 05:34:35 -07:00
.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
2018-06-04 05:34:35 -07:00
.increment
ld a, d
inc a
2018-06-04 05:34:35 -07:00
cp 30 ; Ending frame
jr c, .noreset
xor a
2018-06-04 05:34:35 -07:00
.noreset
; Make sure the danger sound is kept on
or 1 << DANGER_ON_F
ld [wLowHealthAlarm], a
2018-06-04 05:34:35 -07:00
; Enable channel 1 if it's off
2018-01-23 14:39:09 -08:00
ld a, [wSoundOutput]
2015-12-06 19:36:09 -08:00
and $11
ret nz
2018-01-23 14:39:09 -08:00
ld a, [wSoundOutput]
2015-12-06 19:36:09 -08:00
or $11
2018-01-23 14:39:09 -08:00
ld [wSoundOutput], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
DangerSoundHigh:
db $80 ; duty 50%
2015-12-06 19:36:09 -08:00
db $e2 ; volume 14, envelope decrease sweep 2
db $50 ; frequency: $750
db $87 ; restart sound
2018-06-24 07:09:41 -07:00
DangerSoundLow:
db $80 ; duty 50%
2015-12-06 19:36:09 -08:00
db $e2 ; volume 14, envelope decrease sweep 2
db $ee ; frequency: $6ee
db $86 ; restart sound
2018-06-24 07:09:41 -07:00
FadeMusic:
; fade music if applicable
; usage:
2018-01-23 14:39:09 -08:00
; write to wMusicFade
; song fades out at the given rate
2018-01-23 14:39:09 -08:00
; load song id in wMusicFadeID
; fade new song in
; notes:
; max # frames per volume level is $3f
; fading?
2018-01-23 14:39:09 -08:00
ld a, [wMusicFade]
and a
ret z
; has the count ended?
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeCount]
and a
jr z, .update
; count down
dec a
2018-01-23 14:39:09 -08:00
ld [wMusicFadeCount], a
ret
2015-12-07 08:28:58 -08:00
.update
2018-01-23 14:39:09 -08:00
ld a, [wMusicFade]
ld d, a
; get new count
2015-12-06 19:36:09 -08:00
and $3f
2018-01-23 14:39:09 -08:00
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
2015-11-05 11:06:03 -08:00
.novolume
; make sure volume is off
xor a
ld [wVolume], a
; did we just get on a bike?
2018-01-23 14:39:09 -08:00
ld a, [wPlayerState]
cp PLAYER_BIKE
jr z, .bicycle
push bc
; restart sound
call MusicFadeRestart
; get new song id
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID]
and a
jr z, .quit ; this assumes there are fewer than 256 songs!
ld e, a
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID + 1]
ld d, a
; load new song
2013-10-08 10:26:05 -07:00
call _PlayMusic
.quit
; cleanup
pop bc
; stop fading
xor a
2018-01-23 14:39:09 -08:00
ld [wMusicFade], a
ret
2015-12-07 08:28:58 -08:00
.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
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID]
ld e, a
2018-01-23 14:39:09 -08:00
ld a, [wMusicFadeID + 1]
ld d, a
; load new song
2013-10-08 10:26:05 -07:00
call _PlayMusic
pop bc
; fade in
2018-01-23 14:39:09 -08:00
ld hl, wMusicFade
set MUSIC_FADE_IN_F, [hl]
ret
2015-11-05 11:06:03 -08:00
.fadein
; are we done?
cp MAX_VOLUME & $f
jr nc, .maxvolume
; inc volume
inc a
jr .updatevolume
2015-12-07 15:33:04 -08:00
.maxvolume
; we're done
xor a
2018-01-23 14:39:09 -08:00
ld [wMusicFade], a
ret
2015-12-07 08:28:58 -08:00
.updatevolume
; hi = lo
ld d, a
swap a
or d
ld [wVolume], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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]
2016-05-10 11:33:24 -07:00
ld hl, wCurNoteDuration
sub [hl]
jr nc, .ok
2015-12-06 19:36:09 -08:00
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]
2015-12-08 13:06:13 -08:00
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
2015-12-08 13:06:13 -08:00
jr .resume
2015-12-07 15:33:04 -08:00
2015-12-08 13:06:13 -08:00
.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
2015-12-08 13:06:13 -08:00
.resume
2016-05-10 11:33:24 -07:00
; de = x * [wCurNoteDuration] + y
; x + 1 -> d
; y -> a
push bc
2016-05-10 11:33:24 -07:00
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
2015-12-07 15:33:04 -08:00
.quit
2016-05-10 11:33:24 -07:00
ld a, e ; remainder
add [hl]
2016-05-10 11:33:24 -07:00
ld d, b ; quotient
pop bc
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT
add hl, bc
2016-05-10 11:33:24 -07:00
ld [hl], d ; quotient
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT_FRACTION
add hl, bc
2016-05-10 11:33:24 -07:00
ld [hl], a ; remainder
ld hl, CHANNEL_FIELD25
add hl, bc
xor a
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
HandleTrackVibrato:
2016-05-10 13:38:40 -07:00
; 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
2015-12-06 19:36:09 -08:00
and $c0
2015-12-07 08:28:58 -08:00
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]
2015-12-07 08:28:58 -08:00
ld hl, wCurTrackFrequency
ld a, [hli]
ld h, [hl]
ld l, a
add hl, de
ld e, l
ld d, h
2015-12-07 08:28:58 -08:00
ld hl, wCurTrackFrequency
ld [hl], e
inc hl
ld [hl], d
.vibrato
; is vibrato on?
ld hl, CHANNEL_FLAGS2
add hl, bc
2015-12-07 15:33:04 -08:00
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]
2015-12-06 19:36:09 -08:00
and $f ; count
jr z, .toggle
.subexit
dec [hl]
jr .quit
2015-12-07 15:33:04 -08:00
.toggle
; refresh count
ld a, [hl]
swap [hl]
or [hl]
ld [hl], a
; ????
2015-12-07 08:28:58 -08:00
ld a, [wCurTrackFrequency]
ld e, a
; toggle vibrato up/down
ld hl, CHANNEL_FLAGS3
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_VIBRATO_DIR, [hl] ; vibrato up/down
jr z, .down
; up
; vibrato down
2015-12-07 15:33:04 -08:00
res SOUND_VIBRATO_DIR, [hl]
; get the delay
ld a, d
2015-12-06 19:36:09 -08:00
and $f ; lo
;
ld d, a
ld a, e
sub d
jr nc, .no_carry
2015-12-06 19:36:09 -08:00
ld a, 0
jr .no_carry
2015-12-07 15:33:04 -08:00
.down
; vibrato up
2015-12-07 15:33:04 -08:00
set SOUND_VIBRATO_DIR, [hl]
; get the delay
ld a, d
2015-12-06 19:36:09 -08:00
and $f0 ; hi
swap a ; move it to lo
;
add e
jr nc, .no_carry
ld a, $ff
.no_carry
2015-12-07 08:28:58 -08:00
ld [wCurTrackFrequency], a
;
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
set NOTE_VIBRATO_OVERRIDE, [hl]
.quit
ret
2015-12-07 08:28:58 -08:00
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]
2016-05-10 11:33:24 -07:00
jr z, .decreasing
; frequency += [Channel*PitchSlideAmount]
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT
add hl, bc
ld l, [hl]
2015-12-06 19:36:09 -08:00
ld h, 0
add hl, de
ld d, h
ld e, l
; [Channel*Field25] += [Channel*PitchSlideAmountFraction]
2016-05-10 11:33:24 -07:00
; 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
2015-12-06 19:36:09 -08:00
ld a, 0
adc e
ld e, a
2015-12-06 19:36:09 -08:00
ld a, 0
adc d
ld d, a
; Compare the dw at [Channel*PitchSlideTarget] to de.
2016-05-10 11:33:24 -07:00
; 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
2015-12-07 15:33:04 -08:00
2016-05-10 11:33:24 -07:00
.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
2016-05-10 11:33:24 -07:00
; if rollover: Frequency -= 1
ld hl, CHANNEL_PITCH_SLIDE_AMOUNT_FRACTION
add hl, bc
ld a, [hl]
add a
ld [hl], a
ld a, e
sbc 0
ld e, a
ld a, d
sbc 0
2016-05-10 11:33:24 -07:00
ld d, a
; Compare the dw at [Channel*PitchSlideTarget] to de.
2016-05-10 11:33:24 -07:00
; 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
2015-12-07 08:28:58 -08:00
.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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
HandleNoise:
; is noise sampling on?
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_NOISE, [hl] ; noise sampling
ret z
; are we in a sfx channel?
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-04-05 08:44:02 -07:00
bit NOISE_CHAN_F, a
jr nz, .next
; is ch8 on? (noise)
ld hl, wChannel8Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl] ; on?
jr z, .next
; is ch8 playing noise?
2015-12-07 15:33:04 -08:00
bit SOUND_NOISE, [hl]
ret nz ; quit if so
;
.next
2015-12-07 08:28:58 -08:00
ld a, [wNoiseSampleDelay]
and a
jr z, ReadNoiseSample
dec a
2015-12-07 08:28:58 -08:00
ld [wNoiseSampleDelay], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
ReadNoiseSample:
; sample struct:
; [wx] [yy] [zz]
; w: ? either 2 or 3
2013-11-05 12:04:26 -08:00
; x: duration
; zz: volume envelope
2013-11-05 12:04:26 -08:00
; yy: frequency
2018-01-23 14:39:09 -08:00
; de = [wNoiseSampleAddress]
ld hl, wNoiseSampleAddress
ld e, [hl]
inc hl
ld d, [hl]
2013-11-05 12:04:26 -08:00
; is it empty?
ld a, e
or d
jr z, .quit
2013-11-05 12:04:26 -08:00
ld a, [de]
inc de
2013-11-05 12:04:26 -08:00
cp sound_ret_cmd
jr z, .quit
2013-11-05 12:04:26 -08:00
and $f
inc a
2015-12-07 08:28:58 -08:00
ld [wNoiseSampleDelay], a
ld a, [de]
inc de
ld [wCurTrackVolumeEnvelope], a
ld a, [de]
inc de
2015-12-07 08:28:58 -08:00
ld [wCurTrackFrequency], a
xor a
2015-12-07 08:28:58 -08:00
ld [wCurTrackFrequency + 1], a
2013-11-05 12:04:26 -08:00
2018-01-23 14:39:09 -08:00
ld hl, wNoiseSampleAddress
ld [hl], e
inc hl
ld [hl], d
2013-11-05 12:04:26 -08:00
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
set NOTE_NOISE_SAMPLING, [hl]
ret
2015-12-07 08:28:58 -08:00
.quit
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
; then it's a command
.readcommand
call ParseMusicCommand
jr ParseMusic ; start over
.readnote
2018-01-23 14:39:09 -08:00
; wCurMusicByte contains current note
; special notes
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_SFX, [hl]
2016-05-10 11:33:24 -07:00
jp nz, ParseSFXOrRest
2015-12-07 15:33:04 -08:00
bit SOUND_REST, [hl] ; rest
2016-05-10 11:33:24 -07:00
jp nz, ParseSFXOrRest
2015-12-07 15:33:04 -08:00
bit SOUND_NOISE, [hl] ; noise sample
jp nz, GetNoiseSample
; normal note
; set note duration (bottom nybble)
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
2015-12-06 19:36:09 -08:00
and $f
call SetNoteDuration
; get note pitch (top nybble)
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
swap a
2015-12-06 19:36:09 -08:00
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
2015-12-06 19:36:09 -08:00
.rest
; note = rest
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2015-12-07 15:33:04 -08:00
set NOTE_REST, [hl] ; Rest
ret
2015-12-07 08:28:58 -08:00
.sound_ret
; $ff is reached in music data
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_SUBROUTINE, [hl] ; in a subroutine?
jr nz, .readcommand ; execute
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2017-12-09 10:28:23 -08:00
cp CHAN5
2015-12-06 19:36:09 -08:00
jr nc, .chan_5to8
; ????
ld hl, CHANNEL_STRUCT_LENGTH * NUM_MUSIC_CHANS + CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr nz, .ok
2015-12-06 19:36:09 -08:00
.chan_5to8
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_REST, [hl]
call nz, RestoreVolume
; end music
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2017-12-09 10:28:23 -08:00
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
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl]
; note = rest
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
2015-12-07 15:33:04 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
RestoreVolume:
; ch5 only
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2017-12-09 10:28:23 -08:00
cp CHAN5
ret nz
xor a
ld hl, wChannel6PitchOffset
ld [hli], a
ld [hl], a
ld hl, wChannel8PitchOffset
ld [hli], a
ld [hl], a
2018-01-23 14:39:09 -08:00
ld a, [wLastVolume]
ld [wVolume], a
xor a
2018-01-23 14:39:09 -08:00
ld [wLastVolume], a
ld [wSFXPriority], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
ParseSFXOrRest:
; turn noise sampling on
ld hl, CHANNEL_NOTE_FLAGS
add hl, bc
set NOTE_NOISE_SAMPLING, [hl] ; noise sample
; update note duration
2018-01-23 14:39:09 -08:00
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)
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
2018-01-11 09:00:01 -08:00
cp CHAN4
ret z
; update hi frequency from next param
call GetMusicByte
ld hl, CHANNEL_FREQUENCY + 1
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
GetNoiseSample:
2018-01-23 14:39:09 -08:00
; load ptr to sample header in wNoiseSampleAddress
; are we on the last channel?
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
and NUM_MUSIC_CHANS - 1
2018-01-11 09:00:01 -08:00
cp CHAN4
; ret if not
ret nz
; update note duration
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
2015-12-06 19:36:09 -08:00
and $f
call SetNoteDuration
; check current channel
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-04-05 08:44:02 -07:00
bit NOISE_CHAN_F, a
jr nz, .sfx
ld hl, wChannel8Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl] ; is ch8 on? (noise)
ret nz
2018-01-23 14:39:09 -08:00
ld a, [wMusicNoiseSampleSet]
jr .next
2015-12-07 15:33:04 -08:00
.sfx
2018-01-23 14:39:09 -08:00
ld a, [wSFXNoiseSampleSet]
.next
; load noise sample set id into de
ld e, a
2015-12-06 19:36:09 -08:00
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
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
swap a
; non-rest note?
2015-12-06 19:36:09 -08:00
and $f
ret z
; use 'pitch' to seek noise sample set
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
add hl, de
add hl, de
2018-01-23 14:39:09 -08:00
; load sample pointer into wNoiseSampleAddress
ld a, [hli]
2018-01-23 14:39:09 -08:00
ld [wNoiseSampleAddress], a
ld a, [hl]
2018-01-23 14:39:09 -08:00
ld [wNoiseSampleAddress + 1], a
; clear ????
xor a
2015-12-07 08:28:58 -08:00
ld [wNoiseSampleDelay], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
ParseMusicCommand:
; reload command
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
; get command #
sub FIRST_MUSIC_CMD
ld e, a
2015-12-06 19:36:09 -08:00
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
2015-12-07 15:33:04 -08:00
2018-06-24 07:09:41 -07:00
MusicCommands:
; entries correspond to audio constants (see macros/scripts/audio.asm)
2015-12-06 19:36:09 -08:00
dw Music_Octave8 ; octave 8
dw Music_Octave7 ; octave 7
dw Music_Octave6 ; octave 6
dw Music_Octave5 ; octave 5
dw Music_Octave4 ; octave 4
dw Music_Octave3 ; octave 3
dw Music_Octave2 ; octave 2
dw Music_Octave1 ; octave 1
dw Music_NoteType ; note length + volume envelope
dw Music_Transpose ; transpose
2015-12-06 19:36:09 -08:00
dw Music_Tempo ; tempo
dw Music_DutyCycle ; duty cycle
dw Music_VolumeEnvelope ; volume envelope
dw Music_PitchSweep ; update pitch sweep
dw Music_DutyCyclePattern ; duty cycle pattern
2016-05-10 13:38:40 -07:00
dw Music_ToggleSFX ; sound on/off
dw Music_PitchSlide ; pitch slide
2015-12-06 19:36:09 -08:00
dw Music_Vibrato ; vibrato
2015-12-08 13:06:13 -08:00
dw MusicE2 ; unused
2015-12-06 19:36:09 -08:00
dw Music_ToggleNoise ; music noise sampling
dw Music_ForceStereoPanning ; force stereo panning
2015-12-06 19:36:09 -08:00
dw Music_Volume ; volume
dw Music_PitchOffset ; pitch offset
2015-12-08 13:06:13 -08:00
dw MusicE7 ; unused
dw MusicE8 ; unused
dw Music_TempoRelative ; tempo adjust
2015-12-06 19:36:09 -08:00
dw Music_RestartChannel ; restart current channel from header
dw Music_NewSong ; new song
dw Music_SFXPriorityOn ; sfx priority on
dw Music_SFXPriorityOff ; sfx priority off
2015-12-08 13:06:13 -08:00
dw MusicEE ; unused
2015-12-06 19:36:09 -08:00
dw Music_StereoPanning ; stereo panning
dw Music_SFXToggleNoise ; sfx noise sampling
dw MusicF1 ; nothing
2013-11-05 12:17:24 -08:00
dw MusicF2 ; nothing
dw MusicF3 ; nothing
dw MusicF4 ; nothing
dw MusicF5 ; nothing
dw MusicF6 ; nothing
dw MusicF7 ; nothing
dw MusicF8 ; nothing
2015-12-08 13:06:13 -08:00
dw MusicF9 ; unused
dw Music_SetCondition ; set condition
2016-05-10 13:38:40 -07:00
dw Music_JumpIf ; jumpif
dw Music_Jump ; jump
dw Music_Loop ; loop
dw Music_Call ; call
dw Music_Ret ; return
2018-06-24 07:09:41 -07:00
MusicF1:
MusicF2:
MusicF3:
MusicF4:
MusicF5:
MusicF6:
MusicF7:
MusicF8:
ret
2015-12-07 08:28:58 -08:00
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
2015-12-07 15:33:04 -08:00
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
2015-12-07 08:28:58 -08:00
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
2015-12-07 15:33:04 -08:00
set SOUND_SUBROUTINE, [hl]
ret
2015-12-07 08:28:58 -08:00
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
2015-12-07 08:28:58 -08:00
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
2015-12-07 15:33:04 -08:00
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
2015-12-07 15:33:04 -08:00
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
2015-12-07 15:33:04 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-07 08:28:58 -08:00
.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
2015-12-07 08:28:58 -08:00
2018-11-20 12:46:00 -08:00
MusicEE:
; conditional jump
; checks a byte in ram corresponding to the current channel
; doesn't seem to be set by any commands
; params: 2
; ll hh ; pointer
; if ????, jump
; get channel
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
2018-01-23 14:39:09 -08:00
; 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
2015-12-07 08:28:58 -08:00
.jump
; reset jump flag
2015-12-06 19:36:09 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicF9:
; sets some flag
; seems to be unused
; params: 0
2019-04-19 08:35:27 -07:00
ld a, TRUE
ld [wUnusedMusicF9Flag], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicE2:
; seems to have been dummied out
; params: 1
call GetMusicByte
ld hl, CHANNEL_FIELD2C
add hl, bc
ld [hl], a
ld hl, CHANNEL_FLAGS2
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_UNKN_0B, [hl]
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-07 15:33:04 -08:00
set SOUND_VIBRATO, [hl]
; start at lower frequency (extent is positive)
ld hl, CHANNEL_FLAGS3
add hl, bc
2015-12-07 15:33:04 -08:00
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
2015-12-06 19:36:09 -08:00
and $f0
swap a
srl a ; halve
ld e, a
2018-05-20 14:55:27 -07:00
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
2015-12-06 19:36:09 -08:00
and $f
ld d, a
swap a
or d
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
Music_PitchSlide:
; set the target for pitch slide
; params: 2
; note duration
; target note
call GetMusicByte
2016-05-10 11:33:24 -07:00
ld [wCurNoteDuration], a
2015-12-06 19:36:09 -08:00
call GetMusicByte
; pitch in e
ld d, a
2015-12-06 19:36:09 -08:00
and $f
ld e, a
2015-12-06 19:36:09 -08:00
; octave in d
ld a, d
swap a
2015-12-06 19:36:09 -08:00
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
2015-12-07 08:28:58 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicE7:
2015-12-08 13:06:13 -08:00
; unused
; params: 1
ld hl, CHANNEL_FLAGS2
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_UNKN_0E, [hl]
call GetMusicByte
ld hl, CHANNEL_FIELD29
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
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
2016-05-10 13:38:40 -07:00
; sound duty sequence
call GetMusicByte
rrca
rrca
ld hl, CHANNEL_DUTY_CYCLE_PATTERN
add hl, bc
ld [hl], a
; update duty cycle
2015-12-06 19:36:09 -08:00
and $c0 ; only uses top 2 bits
ld hl, CHANNEL_DUTY_CYCLE
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MusicE8:
2015-12-08 13:06:13 -08:00
; unused
; params: 1
ld hl, CHANNEL_FLAGS2
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_UNKN_0D, [hl]
call GetMusicByte
ld hl, CHANNEL_FIELD2A
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_ToggleSFX:
; toggle something
; params: none
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_SFX, [hl]
jr z, .on
2015-12-07 15:33:04 -08:00
res SOUND_SFX, [hl]
ret
2015-12-07 08:28:58 -08:00
.on
2015-12-07 15:33:04 -08:00
set SOUND_SFX, [hl]
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-07 15:33:04 -08:00
bit SOUND_NOISE, [hl]
jr z, .on
; turn noise sampling off
2015-12-07 15:33:04 -08:00
res SOUND_NOISE, [hl]
ret
2015-12-07 08:28:58 -08:00
.on
; turn noise sampling on
2015-12-07 15:33:04 -08:00
set SOUND_NOISE, [hl]
call GetMusicByte
2018-01-23 14:39:09 -08:00
ld [wMusicNoiseSampleSet], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_SFXToggleNoise:
; toggle sfx noise sampling
; params:
; on: 1
; off: 0
; check if noise sampling is on
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
bit SOUND_NOISE, [hl]
jr z, .on
; turn noise sampling off
2015-12-07 15:33:04 -08:00
res SOUND_NOISE, [hl]
ret
2015-12-07 08:28:58 -08:00
.on
; turn noise sampling on
2015-12-07 15:33:04 -08:00
set SOUND_NOISE, [hl]
call GetMusicByte
2018-01-23 14:39:09 -08:00
ld [wSFXNoiseSampleSet], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
2018-01-11 09:00:01 -08:00
cp CHAN4
ret z
; volume envelope
call Music_VolumeEnvelope
ret
2015-12-07 08:28:58 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_DutyCycle:
; duty cycle
; params: 1
call GetMusicByte
rrca
rrca
2015-12-06 19:36:09 -08:00
and $c0
ld hl, CHANNEL_DUTY_CYCLE
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
Music_VolumeEnvelope:
; volume envelope
; params: 1
; hi: volume
; lo: fade
call GetMusicByte
ld hl, CHANNEL_VOLUME_ENVELOPE
add hl, bc
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_Tempo:
; global tempo
; params: 2
; de: tempo
call GetMusicByte
ld d, a
call GetMusicByte
ld e, a
call SetGlobalTempo
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
2013-11-05 12:17:24 -08:00
and 7
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_StereoPanning:
; stereo panning
; params: 1
; stereo on?
2018-01-23 14:39:09 -08:00
ld a, [wOptions]
2017-12-09 10:28:23 -08:00
bit STEREO, a
jr nz, Music_ForceStereoPanning
; skip param
call GetMusicByte
ret
2015-12-07 08:28:58 -08:00
Music_ForceStereoPanning:
; force panning
; params: 1
call SetLRTracks
call GetMusicByte
ld hl, CHANNEL_TRACKS
add hl, bc
and [hl]
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_Volume:
; set volume
; params: 1
; see Volume
; read param even if it's not used
call GetMusicByte
; is the song fading?
2018-01-23 14:39:09 -08:00
ld a, [wMusicFade]
and a
ret nz
; reload param
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
; set volume
ld [wVolume], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_TempoRelative:
; set global tempo to current channel tempo +/- param
; params: 1 signed
call GetMusicByte
ld e, a
; check sign
2015-12-06 19:36:09 -08:00
cp $80
jr nc, .negative
;positive
2015-12-06 19:36:09 -08:00
ld d, 0
jr .ok
2015-12-07 15:33:04 -08:00
.negative
2015-12-06 19:36:09 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_SFXPriorityOn:
; turn sfx priority on
; params: none
2015-12-06 19:36:09 -08:00
ld a, 1
2018-01-23 14:39:09 -08:00
ld [wSFXPriority], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_SFXPriorityOff:
; turn sfx priority off
; params: none
xor a
2018-01-23 14:39:09 -08:00
ld [wSFXPriority], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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]
2018-01-23 14:39:09 -08:00
ld [wMusicID], a
ld a, [hl]
2018-01-23 14:39:09 -08:00
ld [wMusicID + 1], a
; update music bank
ld hl, CHANNEL_MUSIC_BANK
add hl, bc
ld a, [hl]
2018-01-23 14:39:09 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
Music_NewSong:
; new song
; params: 2
; de: song id
call GetMusicByte
ld e, a
call GetMusicByte
ld d, a
push bc
2013-10-08 10:26:05 -07:00
call _PlayMusic
pop bc
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
; load address into de
ld hl, CHANNEL_MUSIC_ADDRESS
add hl, bc
ld a, [hli]
ld e, a
ld d, [hl]
; load bank into a
ld hl, CHANNEL_MUSIC_BANK
add hl, bc
ld a, [hl]
; get byte
2018-01-23 14:39:09 -08:00
call _LoadMusicByte ; load data into wCurMusicByte
inc de ; advance to next byte for next time this is called
; update channeldata address
ld hl, CHANNEL_MUSIC_ADDRESS
add hl, bc
ld a, e
ld [hli], a
ld [hl], d
; cleanup
pop de
pop hl
; store channeldata in a
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-06 19:36:09 -08:00
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]
2015-12-06 19:36:09 -08:00
and $f ; lo nybble
ld l, a ; ok
2015-12-06 19:36:09 -08:00
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
2015-12-06 19:36:09 -08:00
; shift right by [7 - octave] bits
.loop
; [7 - octave] loops
2015-12-06 19:36:09 -08:00
cp $7
jr nc, .ok
; sra de
sra d
rr e
inc a
jr .loop
2015-12-07 15:33:04 -08:00
.ok
ld a, d
2015-12-06 19:36:09 -08:00
and $7 ; top 3 bits for frequency (11 total)
ld d, a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
SetNoteDuration:
; input: a = note duration in 16ths
; store delay units in de
inc a
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
; store NoteLength in a
ld hl, CHANNEL_NOTE_LENGTH
add hl, bc
ld a, [hl]
; multiply NoteLength by delay units
ld l, 0 ; just multiply
2015-12-08 13:06:13 -08:00
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))
2015-12-08 13:06:13 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
.Multiply:
; multiplies a and de
; adds the result to l
; stores the result in hl
2015-12-06 19:36:09 -08:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
SetGlobalTempo:
push bc ; save current channel
; are we dealing with music or sfx?
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2015-12-07 08:28:58 -08:00
cp CHAN5
jr nc, .sfxchannels
ld bc, wChannel1
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel2
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel3
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel4
2015-12-08 13:06:13 -08:00
call Tempo
jr .end
2015-12-07 15:33:04 -08:00
.sfxchannels
ld bc, wChannel5
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel6
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel7
2015-12-08 13:06:13 -08:00
call Tempo
ld bc, wChannel8
2015-12-08 13:06:13 -08:00
call Tempo
.end
pop bc ; restore current channel
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
StartChannel:
call SetLRTracks
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_CHANNEL_ON, [hl] ; turn channel on
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
; get this channel's lr tracks
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
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
_PlayMusic::
; load music
call MusicOff
2018-01-23 14:39:09 -08:00
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]
2018-01-23 14:39:09 -08:00
ld [wMusicBank], a
ld e, [hl]
inc hl
ld d, [hl] ; music header address
2013-10-08 10:34:32 -07:00
call LoadMusicByte ; store first byte of music header in a
rlca
rlca
2018-01-16 14:27:50 -08:00
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
2019-04-19 08:35:27 -07:00
ld [wUnusedMusicF9Flag], a
2018-01-23 14:39:09 -08:00
ld [wChannel1JumpCondition], a
ld [wChannel2JumpCondition], a
ld [wChannel3JumpCondition], a
ld [wChannel4JumpCondition], a
ld [wNoiseSampleAddress], a
ld [wNoiseSampleAddress + 1], a
2015-12-07 08:28:58 -08:00
ld [wNoiseSampleDelay], a
2018-01-23 14:39:09 -08:00
ld [wMusicNoiseSampleSet], a
call MusicOn
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
_PlayCry::
2013-02-23 13:47:39 -08:00
; Play cry de using parameters:
2018-01-23 14:39:09 -08:00
; wCryPitch
; wCryLength
call MusicOff
2013-02-23 13:47:39 -08:00
; Overload the music id with the cry id
2018-01-23 14:39:09 -08:00
ld hl, wMusicID
ld [hl], e
inc hl
ld [hl], d
2013-02-23 13:47:39 -08:00
; 3-byte pointers (bank, address)
ld hl, Cries
add hl, de
add hl, de
add hl, de
ld a, [hli]
2018-01-23 14:39:09 -08:00
ld [wMusicBank], a
ld e, [hl]
inc hl
ld d, [hl]
2013-02-23 13:47:39 -08:00
; Read the cry's sound header
2013-10-08 10:34:32 -07:00
call LoadMusicByte
2013-02-23 13:47:39 -08:00
; Top 2 bits contain the number of channels
rlca
rlca
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
2013-02-23 13:47:39 -08:00
; For each channel:
inc a
.loop
push af
call LoadChannel
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_REST, [hl]
ld hl, CHANNEL_FLAGS2
add hl, bc
set SOUND_PITCH_OFFSET, [hl]
ld hl, CHANNEL_PITCH_OFFSET
add hl, bc
2018-01-23 14:39:09 -08:00
ld a, [wCryPitch]
ld [hli], a
2018-01-23 14:39:09 -08:00
ld a, [wCryPitch + 1]
ld [hl], a
2013-02-23 13:47:39 -08:00
; No tempo for channel 4
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
2018-01-11 09:00:01 -08:00
cp CHAN4
jr nc, .start
2013-02-23 13:47:39 -08:00
; Tempo is effectively length
ld hl, CHANNEL_TEMPO
add hl, bc
2018-01-23 14:39:09 -08:00
ld a, [wCryLength]
ld [hli], a
ld a, [wCryLength + 1]
ld [hl], a
.start
call StartChannel
2015-12-23 14:10:50 -08:00
ld a, [wStereoPanningMask]
and a
jr z, .next
2013-02-23 13:47:39 -08:00
; Stereo only: Play cry from the monster's side.
; This only applies in-battle.
2018-01-23 14:39:09 -08:00
ld a, [wOptions]
2017-12-09 10:28:23 -08:00
bit STEREO, a
jr z, .next
2018-01-23 14:39:09 -08:00
; [Tracks] &= [wCryTracks]
ld hl, CHANNEL_TRACKS
add hl, bc
ld a, [hl]
2018-01-23 14:39:09 -08:00
ld hl, wCryTracks
2015-12-06 19:36:09 -08:00
and [hl]
ld hl, CHANNEL_TRACKS
add hl, bc
ld [hl], a
.next
pop af
dec a
jr nz, .loop
2013-02-23 13:47:39 -08:00
; Cries play at max volume, so we save the current volume for later.
2018-01-23 14:39:09 -08:00
ld a, [wLastVolume]
and a
jr nz, .end
ld a, [wVolume]
2018-01-23 14:39:09 -08:00
ld [wLastVolume], a
2017-12-09 10:28:23 -08:00
ld a, MAX_VOLUME
ld [wVolume], a
.end
2013-02-23 13:47:39 -08:00
ld a, 1 ; stop playing music
2018-01-23 14:39:09 -08:00
ld [wSFXPriority], a
call MusicOn
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
_PlaySFX::
; clear channels if they aren't already
call MusicOff
ld hl, wChannel5Flags1
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl] ; ch5 on?
jr z, .ch6
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl] ; turn it off
xor a
ldh [rNR11], a ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
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
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr z, .ch7
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl] ; turn it off
xor a
ldh [rNR21], a ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
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
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr z, .ch8
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl] ; turn it off
xor a
ldh [rNR30], a ; sound mode #3 off
ldh [rNR31], a ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
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
2015-12-07 15:33:04 -08:00
bit SOUND_CHANNEL_ON, [hl]
jr z, .chscleared
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl] ; turn it off
xor a
ldh [rNR41], a ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
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
2018-01-23 14:39:09 -08:00
ld [wNoiseSampleAddress], a
ld [wNoiseSampleAddress + 1], a
.chscleared
; start reading sfx header for # chs
2018-01-23 14:39:09 -08:00
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]
2018-01-23 14:39:09 -08:00
ld [wMusicBank], a
; get address
ld e, [hl]
inc hl
ld d, [hl]
; get # channels
2013-10-08 10:34:32 -07:00
call LoadMusicByte
rlca ; top 2
rlca ; bits
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
inc a ; # channels -> # loops
.startchannels
push af
call LoadChannel ; bc = current channel
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_SFX, [hl]
call StartChannel
pop af
dec a
jr nz, .startchannels
call MusicOn
xor a
2018-01-23 14:39:09 -08:00
ld [wSFXPriority], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
PlayStereoSFX::
; play sfx de
call MusicOff
; standard procedure if stereo's off
2018-01-23 14:39:09 -08:00
ld a, [wOptions]
2017-12-09 10:28:23 -08:00
bit STEREO, a
2013-10-08 10:13:35 -07:00
jp z, _PlaySFX
; else, let's go ahead with this
2018-01-23 14:39:09 -08:00
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]
2018-01-23 14:39:09 -08:00
ld [wMusicBank], a
; address
ld e, [hl]
inc hl
ld d, [hl]
; bit 2-3
2013-10-08 10:34:32 -07:00
call LoadMusicByte
rlca
rlca
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
inc a
.loop
push af
call LoadChannel
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_SFX, [hl]
push de
; get tracks for this channel
2018-01-23 14:39:09 -08:00
ld a, [wCurChannel]
2018-01-16 14:27:50 -08:00
maskbits NUM_MUSIC_CHANS
ld e, a
2015-12-06 19:36:09 -08:00
ld d, 0
call GetLRTracks
add hl, de
ld a, [hl]
2015-12-23 14:10:50 -08:00
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
2018-01-23 14:39:09 -08:00
ld a, [wCryTracks]
cp 2 ; ch 1-2
2015-12-06 19:36:09 -08:00
jr c, .skip
; ch3-4
2015-12-23 14:10:50 -08:00
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
2015-12-07 15:33:04 -08:00
set SOUND_UNKN_0F, [hl]
2015-12-06 19:36:09 -08:00
.skip
pop de
; turn channel on
ld hl, CHANNEL_FLAGS1
add hl, bc
2015-12-07 15:33:04 -08:00
set SOUND_CHANNEL_ON, [hl] ; on
; done?
pop af
dec a
jr nz, .loop
; we're done
call MusicOn
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
LoadChannel:
; prep channel for use
; input:
; de:
; get pointer to current channel
2013-10-08 10:34:32 -07:00
call LoadMusicByte
inc de
2015-12-06 19:36:09 -08:00
and $7 ; bit 0-2 (current channel)
2018-01-23 14:39:09 -08:00
ld [wCurChannel], a
ld c, a
2015-12-06 19:36:09 -08:00
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
2015-12-07 15:33:04 -08:00
res SOUND_CHANNEL_ON, [hl] ; channel off
call ChannelInit
; load music pointer
ld hl, CHANNEL_MUSIC_ADDRESS
add hl, bc
2013-10-08 10:34:32 -07:00
call LoadMusicByte
ld [hli], a
inc de
2013-10-08 10:34:32 -07:00
call LoadMusicByte
ld [hl], a
inc de
; load music id
ld hl, CHANNEL_MUSIC_ID
add hl, bc
2018-01-23 14:39:09 -08:00
ld a, [wMusicID]
ld [hli], a
2018-01-23 14:39:09 -08:00
ld a, [wMusicID + 1]
ld [hl], a
; load music bank
ld hl, CHANNEL_MUSIC_BANK
add hl, bc
2018-01-23 14:39:09 -08:00
ld a, [wMusicBank]
ld [hl], a
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2015-12-06 19:36:09 -08:00
; set note length to default ($1) (fast)
ld hl, CHANNEL_NOTE_LENGTH
add hl, bc
ld [hl], a
pop de
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
LoadMusicByte::
; input:
; de = current music address
; output:
2018-01-23 14:39:09 -08:00
; a = wCurMusicByte
ld a, [wMusicBank]
2013-10-08 10:34:32 -07:00
call _LoadMusicByte
2018-01-23 14:39:09 -08:00
ld a, [wCurMusicByte]
ret
2015-12-07 08:28:58 -08:00
INCLUDE "audio/notes.asm"
INCLUDE "audio/wave_samples.asm"
INCLUDE "audio/drumkits.asm"
2018-06-24 07:09:41 -07:00
GetLRTracks:
; gets the default sound l/r channels
; stores mono/stereo table in hl
2018-01-23 14:39:09 -08:00
ld a, [wOptions]
2017-12-09 10:28:23 -08:00
bit STEREO, a
; made redundant, could have had a purpose in gold
jr nz, .stereo
ld hl, MonoTracks
ret
2015-12-07 08:28:58 -08:00
.stereo
ld hl, StereoTracks
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
MonoTracks:
; bit corresponds to track #
; hi: left channel
; lo: right channel
db $11, $22, $44, $88
2018-06-24 07:09:41 -07:00
StereoTracks:
; made redundant
; seems to be modified on a per-song basis
db $11, $22, $44, $88
2018-06-24 07:09:41 -07:00
ChannelPointers:
; music channels
dw wChannel1
dw wChannel2
dw wChannel3
dw wChannel4
; sfx channels
dw wChannel5
dw wChannel6
dw wChannel7
dw wChannel8
2018-06-24 07:09:41 -07:00
ClearChannels::
; runs ClearChannel for all 4 channels
; doesn't seem to be used, but functionally identical to InitSound
2013-02-19 23:46:40 -08:00
ld hl, rNR50
xor a
ld [hli], a
ld [hli], a
ld a, $80
ld [hli], a
2013-02-19 23:46:40 -08:00
ld hl, rNR10
2017-12-09 10:28:23 -08:00
ld e, NUM_MUSIC_CHANS
.loop
call ClearChannel
dec e
jr nz, .loop
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
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
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
2015-12-06 19:36:09 -08:00
ld a, $8
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
xor a
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
ld a, $80
2013-02-19 23:46:40 -08:00
ld [hli], a ; rNR14, rNR24, rNR34, rNR44 ; restart sound (freq hi = 0)
ret
2015-12-07 08:28:58 -08:00
2018-06-24 07:09:41 -07:00
PlayTrainerEncounterMusic::
; input: e = trainer type
; turn fade off
xor a
2018-01-23 14:39:09 -08:00
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