summaryrefslogtreecommitdiff
path: root/audio/engine.asm
diff options
context:
space:
mode:
Diffstat (limited to 'audio/engine.asm')
-rw-r--r--audio/engine.asm2345
1 files changed, 2327 insertions, 18 deletions
diff --git a/audio/engine.asm b/audio/engine.asm
index 157734c..f722621 100644
--- a/audio/engine.asm
+++ b/audio/engine.asm
@@ -1,6 +1,6 @@
INCLUDE "constants.asm"
-SECTION "audio/engine.asm@Audio", ROMX
+SECTION "audio/engine.asm", ROMX
_DisableAudio::
push hl
@@ -38,7 +38,7 @@ _DisableAudio::
or d
jr nz, .clear
- ld a, $77
+ ld a, MAX_VOLUME
ld [wVolume], a
pop af
@@ -94,7 +94,7 @@ UpdateChannel:
.noteover
call DisablePitchWheel
- call Functione884f
+ call ParseMusic
.continue_sound_update
call Functione80b6
@@ -170,15 +170,15 @@ Functione80b6:
ld [wCurTrackFrequency], a
ld a, [hl]
ld [wCurTrackFrequency + 1], a
- ld a, $3f
- ld [wc195], a
+ ld a, $3F
+ ld [wSoundLength], a
call Functione85d8
- call Functione87f9
- call Functione8839
+ call ReadNoiseSample
+ call HaltMusicWhileSFXPlaying
call IsChannelSFXOn
jr nc, .end
- call Functione80fa
+ call UpdateChannels
ld hl, CHANNEL_TRACKS
add hl, bc
ld a, [wSoundOutput]
@@ -192,7 +192,7 @@ Functione80b6:
ld [hl], a
ret
-Functione80fa:
+UpdateChannels:
ld hl, .jumptable
ld a, [wCurChannel]
maskbits NUM_CHANNELS
@@ -206,9 +206,280 @@ Functione80fa:
jp hl
.jumptable
- ; TODO
+; Music channels
+ dw .Channel1
+ dw .Channel2
+ dw .Channel3
+ dw .Channel4
+; SFX channels
+ dw .Channel5
+ dw .Channel6
+ dw .Channel7
+ dw .Channel8
+
+.Channel1:
+ ld a, [wLowHealthAlarm]
+ bit 7, a
+ ret nz
+
+.Channel5:
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ bit NOTE_PITCH_SWEEP, [hl]
+ jr z, .ch1_no_sweep
+
+ ld a, [wPitchSweep]
+ ldh [rNR10], a
+
+.ch1_no_sweep
+ bit NOTE_REST, [hl]
+ jr nz, .ch1_rest
+ bit NOTE_NOISE_SAMPLING, [hl]
+ jr nz, .ch1_noise_sampling
+ bit NOTE_FREQ_OVERRIDE, [hl]
+ call nz, .ch1_freq_override
+ bit NOTE_INTENSITY_OVERRIDE, [hl]
+ call nz, .ch1_intensity_override
+ bit NOTE_DUTY_OVERRIDE, [hl]
+ call nz, .ch1_duty_override
+ ret
+
+.ch1_rest
+ ld a, %1000 ; stop envelope
+ ldh [rNR12], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80 ; restart ch1
+ ldh [rNR14], a
+ ret
+
+.ch1_noise_sampling
+ ld hl, wCurTrackDuty
+ ld a, [wSoundLength]
+ or [hl]
+ ldh [rNR11], a
+ ld a, [wCurTrackIntensity]
+ ldh [rNR12], a
+ ld a, [wCurTrackFrequency]
+ ldh [rNR13], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80
+ ldh [rNR14], a
+ ret
+
+.ch1_duty_override
+ ld a, [wCurTrackDuty]
+ ldh a, [rNR11]
+ and $3f
+ or d
+ ldh [rNR11], a
+ ret
+
+.ch1_intensity_override
+ ld a, [wCurTrackIntensity]
+ ldh [rNR12], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80
+ ldh [rNR14], a
+ ret
+
+.ch1_freq_override
+ ld a, [wCurTrackFrequency]
+ ldh [rNR13], a
+ ld a, [wCurTrackFrequency + 1]
+ ldh [rNR14], a
+ ret
+
+.Channel2:
+.Channel6:
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ bit NOTE_REST, [hl]
+ jr nz, .ch2_rest
+ bit NOTE_NOISE_SAMPLING, [hl]
+ jr nz, .ch2_noise_sampling
+ bit NOTE_FREQ_OVERRIDE, [hl]
+ call nz, .ch2_freq_override
+ bit NOTE_INTENSITY_OVERRIDE, [hl]
+ call nz, .ch2_intensity_override
+ bit NOTE_DUTY_OVERRIDE, [hl]
+ call nz, .ch2_duty_override
+ ret
+
+.ch2_rest
+ ld a, $08
+ ldh [rNR22], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80
+ ldh [rNR24], a
+ ret
+
+.ch2_noise_sampling
+ ld hl, wCurTrackDuty
+ ld a, [wSoundLength]
+ or [hl]
+ ldh [rNR21], a
+ ld a, [wCurTrackIntensity]
+ ldh [rNR22], a
+ ld a, [wCurTrackFrequency]
+ ldh [rNR23], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80
+ ldh [rNR24], a
+ ret
+
+.ch2_duty_override
+ ld a, [wCurTrackDuty]
+ ld d, a
+ ldh a, [rNR21]
+ and $3f
+ or d
+ ldh [rNR21], a
+ ret
+
+.ch2_intensity_override
+ ld a, [wCurTrackIntensity]
+ ldh [rNR22], a
+ ld a, [wCurTrackFrequency + 1]
+ or $80
+ ldh [rNR24], a
+ ret
+
+.ch2_freq_override
+ ld a, [wCurTrackFrequency]
+ ldh [rNR23], a
+ ld a, [wCurTrackFrequency + 1]
+ 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_FREQ_OVERRIDE, [hl]
+ call nz, .ch3_freq_override
+ bit NOTE_INTENSITY_OVERRIDE, [hl]
+ call nz, .ch3_intensity_override
+ ret
+
+.ch3_rest
+ xor a
+ ldh [rNR30], a
+ ret
+
+.ch3_noise_sampling
+ ld a, [wSoundLength]
+ 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
+
+.ch3_freq_override
+ ld a, [wCurTrackFrequency]
+ ldh [rNR33], a
+ ld a, [wCurTrackFrequency + 1]
+ ldh [rNR34], a
+ ret
+
+.ch3_intensity_override
+ 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, [wCurTrackIntensity]
+; only patterns 0 - 9 are valid
+ and $f
+ ld l, a
+ ld h, 0
+; hl << 4 (hl * $10), because each pattern is $f bytes
+rept 4
+ add hl, hl
+endr
+ ld de, WaveSamples
+ add hl, de
+ ld de, rWave_0
+ push bc
+ ld b, $10
+.load_pattern
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .load_pattern
+
+ pop bc
+ pop hl
+ ld a, [wCurTrackIntensity]
+ 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
+ bit NOTE_FREQ_OVERRIDE, [hl]
+ call nz, .ch4_freq_override
+ bit NOTE_INTENSITY_OVERRIDE, [hl]
+ call nz, .ch4_intensity_override
+ ret
+
+.ch4_rest
+ ld a, $08
+ ldh [rNR42], a
+ ld a, $80
+ ldh [rNR44], a
+ ret
+
+.ch4_noise_sampling
+ ld a, [wSoundLength]
+ ldh [rNR41], a
+ ld a, [wCurTrackIntensity]
+ ldh [rNR42], a
+ ld a, [wCurTrackFrequency]
+ ldh [rNR43], a
+ ld a, $80
+ ldh [rNR44], a
+ ret
+
+.ch4_freq_override
+ ld a, [wCurTrackFrequency]
+ ldh [rNR43], a
+ ret
+
+.ch4_intensity_override
+ ld a, [wCurTrackIntensity]
+ ldh [rNR42], a
+ ld a, $80
+ ldh [rNR44], a
+ ret
-SECTION "audio/engine.asm@IsChannelSFXOn", ROMX
IsChannelSFXOn:
; If it's not a valid channel, return
ld a, [wCurChannel]
@@ -249,12 +520,11 @@ IsAnySFXOn:
scf
ret
-SECTION "audio/engine.asm@Functione82f0", ROMX
Functione82f0:
call IncrementTempo
call PlayDanger
call FadeMusic
- call Functione841d
+ call DoSweepingFade
ld a, [wVolume]
ld [rNR50], a
ld a, [wSoundOutput]
@@ -502,7 +772,1676 @@ FadeMusic:
ld [wVolume], a
ret
-SECTION "audio/engine.asm@Audio engine, part 2", ROMX
+DoSweepingFade::
+; performs a sweeping fade effect starting from the
+; left channel, then the right, then repeats
+
+ ld a, [wSweepingFadeIndex]
+ and a
+ ret z
+
+; first nybble of wSweepingFadeIndex is the subroutine index
+; second is the fade length
+
+ swap a
+ and 7
+ ld e, a
+ ld d, 0
+ ld hl, .jumptable
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+.jumptable
+ dw .DoFade1
+ dw .DoFade2
+ dw .DoFade3
+ dw .DoFade4
+ dw .DoFade5
+ dw .DoFade6
+ dw .DoFade1
+ dw .DoFade1
+
+.DoFade1:
+ ld a, 1
+ call .SetIndex
+ xor a
+ ld [wSweepingFadeCounter], a
+ ld [wVolume], a
+
+.DoFade2:
+ call .LeftChannel
+ call .DecrementCounter
+ ret nc
+ call .IncreaseVolume
+ ret nc
+ ld a, 2
+ call .SetIndex
+
+.DoFade3:
+ call .LeftChannel
+ call .DecrementCounter
+ ret nc
+ call .DecreaseVolume
+ ret nc
+ ld a, 3
+ call .SetIndex
+
+.DoFade4:
+ call .RightChannel
+ call .DecrementCounter
+ ret nc
+ call .IncreaseVolume
+ ret nc
+ ld a, 4
+ call .SetIndex
+
+.DoFade5:
+ call .RightChannel
+ call .DecrementCounter
+ ret nc
+ call .DecreaseVolume
+ ret nc
+ ld a, 0
+ call .SetIndex
+ ret
+
+.DoFade6:
+ xor a
+ ld [wSweepingFadeIndex], a
+ ld a, MAX_VOLUME
+ ld [wVolume], a
+ ret
+
+.SetIndex:
+ swap a
+ ld d, a
+ ld a, [wSweepingFadeIndex]
+ and $f
+ or d
+ ld [wSweepingFadeIndex], a
+ ret
+
+.DecrementCounter:
+ ld a, [wSweepingFadeCounter]
+ and a
+ jr z, .reset_counter
+ dec a
+ ld [wSweepingFadeCounter], a
+ and a
+ ret
+ .reset_counter
+ ld a, [wSweepingFadeIndex]
+ and $f
+ ld [wSweepingFadeCounter], a
+ scf
+ ret
+
+.IncreaseVolume:
+ ld a, [wVolume]
+ and 7
+ cp 7
+ jr nc, .max_volume
+ inc a
+ ld d, a
+ swap a
+ or d
+ ld [wVolume], a
+ and a
+ ret
+.max_volume
+ scf
+ ret
+
+.DecreaseVolume:
+ ld a, [wVolume]
+ and $f
+ jr z, .min_volume
+ dec a
+ ld d, a
+ swap a
+ or d
+ ld [wVolume], a
+ and a
+ ret
+.min_volume
+ scf
+ ret
+
+.LeftChannel:
+ call IsAnySFXOn
+ ret c
+ ld a, [wSoundOutput]
+ and $f0
+ ld [wSoundOutput], a
+ ret
+
+.RightChannel:
+ call IsAnySFXOn
+ ret c
+ ld a, [wSoundOutput]
+ and $0f
+ ld [wSoundOutput], a
+ ret
+
+LoadNote:
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ bit SOUND_PITCH_WHEEL, [hl]
+ call nz, .get_note
+ bit SOUND_VIBRATO, [hl]
+ call nz, .vibrato
+ bit SOUND_UNKN_0E, [hl]
+ call nz, .flag_0e
+ bit SOUND_UNKN_0D, [hl]
+ call nz, .flag_0d
+ bit SOUND_UNKN_0B, [hl]
+ call nz, .flag_0b
+ ret
+
+.get_note
+ push hl
+ 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
+ ld hl, CHANNEL_FREQUENCY
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET
+ add hl, bc
+ ld a, e
+ sub [hl]
+ ld e, a
+ ld a, d
+ sbc 0
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ sub [hl]
+ jr nc, .greater_than
+
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ set SOUND_PITCH_WHEEL_DIR, [hl]
+ ld hl, CHANNEL_FREQUENCY
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET
+ add hl, bc
+ ld a, [hl]
+ sub e
+ ld e, a
+ ld a, d
+ sbc 0
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_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_WHEEL_DIR, [hl]
+ ld hl, CHANNEL_FREQUENCY
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET
+ add hl, bc
+ ld a, e
+ sub [hl]
+ ld e, a
+ ld a, d
+ sbc 0
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ sub [hl]
+ ld d, a
+
+.resume
+ push bc
+ ld hl, wCurNoteDuration
+ ld b, 0
+
+.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
+ add [hl]
+ ld d, b
+ pop bc
+ ld hl, CHANNEL_PITCH_WHEEL_AMOUNT
+ add hl, bc
+ ld [hl], d
+ ld hl, CHANNEL_PITCH_WHEEL_AMOUNT_FRACTION
+ add hl, bc
+ ld [hl], a
+ ld hl, CHANNEL_FIELD25
+ add hl, bc
+ xor a
+ ld [hl], a
+ pop hl
+ ret
+
+.vibrato
+ push hl
+ ld hl, CHANNEL_VIBRATO_DELAY
+ add hl, bc
+ ld a, [hl]
+ ld hl, CHANNEL_VIBRATO_DELAY_COUNT
+ add hl, bc
+ ld [hl], a
+ pop hl
+ ret
+
+.flag_0e
+ push hl
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ res NOTE_INTENSITY_OVERRIDE, [hl]
+ pop hl
+ ret
+
+.flag_0d
+ push hl
+ ld hl, CHANNEL_FIELD2A + 1
+ add hl, bc
+ xor a
+ ld [hl], a
+ pop hl
+ ret
+
+.flag_0b
+ push hl
+ ld hl, CHANNEL_FIELD2C
+ add hl, bc
+ ld a, [hl]
+ ld hl, CHANNEL_FIELD25 + 1
+ add hl, bc
+ ld [hl], a
+ pop hl
+ ret
+
+Functione85d8::
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ bit SOUND_DUTY, [hl]
+ call nz, HandleDuty
+ bit SOUND_UNKN_0E, [hl]
+ call nz, Handle_0e
+ bit SOUND_CRY_PITCH, [hl]
+ call nz, Handle_crypitch
+ bit SOUND_PITCH_WHEEL, [hl]
+ call nz, ApplyPitchSlide
+ bit SOUND_VIBRATO, [hl]
+ call nz, HandleVibrato
+ bit SOUND_UNKN_0D, [hl]
+ call nz, Handle_0d
+ bit SOUND_UNKN_0B, [hl]
+ call nz, Handle_0b
+ bit SOUND_UNKN_0F, [hl]
+ call nz, HandleNoise
+ ret
+
+HandleDuty:
+ push hl
+ ld hl, CHANNEL_SFX_DUTY_LOOP
+ 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 0, [hl]
+ pop hl
+ ret
+
+ApplyPitchSlide:
+ push hl
+ ld hl, CHANNEL_FREQUENCY
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ bit SOUND_PITCH_WHEEL_DIR, [hl]
+ jr z, .decreasing
+
+ ld hl, CHANNEL_PITCH_WHEEL_AMOUNT
+ add hl, bc
+ ld l, [hl]
+ ld h, 0
+ add hl, de
+ ld d, h
+ ld e, l
+ ld hl, CHANNEL_PITCH_WHEEL_AMOUNT_FRACTION
+ add hl, bc
+ ld a, [hl]
+ ld hl, CHANNEL_FIELD25
+ add hl, bc
+ add [hl]
+ ld [hl], a
+ ld a, 0
+ adc e
+ ld e, a
+ ld a, 0
+ adc d
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ ld a, [hl]
+ cp d
+ jp c, .finished_pitch_slide
+ jr nz, .continue_pitch_slide
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET
+ add hl, bc
+ ld a, [hl]
+ cp e
+ jp c, .finished_pitch_slide
+ jr .continue_pitch_slide
+
+.decreasing
+ ld a, e
+ ld hl, CHANNEL_PITCH_WHEEL_AMOUNT
+ add hl, bc
+ ld e, [hl]
+ sub e
+ ld e, a
+ ld a, d
+ sbc 0
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_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
+ ld d, a
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ ld a, d
+ cp [hl]
+ jr c, .finished_pitch_slide
+ jr nz, .continue_pitch_slide
+ ld hl, CHANNEL_PITCH_WHEEL_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 1, [hl]
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ res 1, [hl]
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+
+.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 1, [hl]
+ pop hl
+ ret
+
+
+HandleVibrato:
+ push hl
+ ld hl, CHANNEL_VIBRATO_DELAY_COUNT
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr nz, .subexit
+
+ ld hl, CHANNEL_VIBRATO_RATE
+ add hl, bc
+ ld a, [hl]
+ and $0f
+ jr z, .toggle
+
+.subexit
+ dec [hl]
+ jr .quit
+
+.toggle
+ ld a, [hl]
+ swap [hl]
+ or [hl]
+ ld [hl], a
+ ld hl, CHANNEL_VIBRATO_EXTENT
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .quit
+
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ bit SOUND_VIBRATO_DIR, [hl]
+ jr z, .down
+
+ res SOUND_VIBRATO_DIR, [hl]
+ and $0f
+ ld d, a
+ ld a, [wCurTrackFrequency]
+ sub d
+ jr nc, .no_carry
+
+ xor a
+ jr .no_carry
+
+.down
+ set SOUND_VIBRATO_DIR, [hl]
+ and $f0
+ swap a
+ ld d, a
+ ld a, [wCurTrackFrequency]
+ add d
+ jr nc, .no_carry
+
+ ld a, $ff
+
+.no_carry
+ ld [wCurTrackFrequency], a
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_FREQ_OVERRIDE, [hl]
+
+.quit
+ pop hl
+ ret
+
+
+Handle_0b:
+ push hl
+ ld hl, CHANNEL_FIELD25 + 1
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .set_rest
+
+ dec [hl]
+ jr .done
+
+.set_rest
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_REST, [hl]
+
+.done
+ pop hl
+ ret
+
+
+Handle_crypitch:
+ push hl
+ ld hl, CHANNEL_CRY_PITCH
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, wCurTrackFrequency
+ push hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ add hl, de
+ ld e, l
+ ld d, h
+ pop hl
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ pop hl
+ ret
+
+
+Handle_0e:
+ push hl
+ ld hl, CHANNEL_FLAGS3
+ add hl, bc
+ bit SOUND_UNKN_12, [hl]
+ jr nz, .skip
+
+ set SOUND_UNKN_12, [hl]
+ jr .done
+
+.skip
+ res SOUND_UNKN_12, [hl]
+ ld hl, CHANNEL_PITCH
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .done
+
+ ld hl, CHANNEL_FIELD29
+ add hl, bc
+ add [hl]
+ ld e, a
+ ld hl, CHANNEL_OCTAVE
+ add hl, bc
+ ld d, [hl]
+ call GetFrequency
+ ld hl, wCurTrackFrequency
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+.done
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set SOUND_PITCH_WHEEL_DIR, [hl]
+ pop hl
+ ret
+
+
+Handle_0d:
+ push hl
+ ld hl, CHANNEL_FIELD2A
+ add hl, bc
+ ld e, [hl]
+ ld d, 0
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ cp CHAN3
+ jr nz, .not_ch3
+
+ ld hl, WaveOverrides
+ call GetFromTable
+ jr c, .rest_done
+
+ ld d, a
+ ld a, [wCurTrackIntensity]
+ and $c0
+ or d
+ jr .intensity_done
+
+.not_ch3
+ ld hl, IntensityOverrides
+ call GetFromTable
+ jr nc, .intensity_done
+
+.rest_done
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_REST, [hl]
+ pop hl
+ ret
+
+
+.intensity_done
+ ld [wCurTrackIntensity], a
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_INTENSITY_OVERRIDE, [hl]
+ pop hl
+ ret
+
+
+GetFromTable:
+ add hl, de
+ add hl, de
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_FIELD2A + 1
+ add hl, bc
+ push hl
+ ld l, [hl]
+ ld h, 0
+ add hl, de
+ ld a, [hl]
+ pop hl
+ cp $ff
+ jr z, .carry
+
+ cp $fe
+ jr nz, .done_nocarry
+
+ xor a
+ ld [hl], a
+ ld a, [de]
+
+.done_nocarry
+ inc [hl]
+ and a
+ ret
+.carry
+ scf
+ ret
+
+
+HandleNoise:
+ ld hl, CHANNEL_FIELD2E
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .skip
+
+ dec [hl]
+ ld hl, CHANNEL_FIELD30
+ add hl, bc
+ ld a, [hl]
+ ld hl, CHANNEL_TRACKS
+ add hl, bc
+ ld [hl], a
+ ret
+
+.skip
+ ld hl, CHANNEL_FIELD2F
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .skip2
+
+ dec [hl]
+ ld hl, CHANNEL_FIELD30
+ add hl, bc
+ ld a, [hl]
+ swap a
+ or [hl]
+ ld hl, CHANNEL_TRACKS
+ add hl, bc
+ ld [hl], a
+ ret
+
+.skip2
+ ld hl, CHANNEL_FIELD30
+ add hl, bc
+ ld a, [hl]
+ swap a
+ ld hl, CHANNEL_TRACKS
+ add hl, bc
+ ld [hl], a
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ res SOUND_UNKN_0F, [hl]
+ ret
+
+
+ReadNoiseSample::
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_NOISE, [hl]
+ ret z
+
+ ld a, [wNoiseSampleDelay]
+ and a
+ jr z, .get_new_sample
+
+ dec a
+ ld [wNoiseSampleDelay], a
+ ret
+
+.get_new_sample
+ ld hl, wNoiseSampleAddress
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld a, [de]
+ inc de
+ cp $ff
+ jr z, .done
+
+ and $0f
+ inc a
+ ld [wNoiseSampleDelay], a
+ ld a, [de]
+ inc de
+ ld [wCurTrackIntensity], 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
+
+.done
+ ret
+
+
+HaltMusicWhileSFXPlaying::
+ ld a, [wSFXPriority]
+ and a
+ ret z
+
+ ld a, [wCurChannel]
+ cp CHAN5
+ ret nc
+
+ call IsAnySFXOn
+ ret nc
+
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_REST, [hl]
+ ret
+
+
+ParseMusic::
+ call GetMusicByte
+ cp sound_ret_cmd
+ jr z, .end_music
+
+ cp FIRST_MUSIC_CMD
+ jr nc, .parse_commands
+
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_SFX, [hl]
+ jr nz, .parse_sfx_or_rest
+
+ bit SOUND_REST, [hl]
+ jr nz, .parse_sfx_or_rest
+
+ bit SOUND_NOISE, [hl]
+ jr nz, .parse_noise
+
+ call _ParseMusic
+ ret
+
+.parse_sfx_or_rest
+ call ParseSFXOrRest
+ ret
+
+.parse_noise
+ call GetNoiseSample
+ ret
+
+.end_music
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_SUBROUTINE, [hl]
+ jr nz, .parse_commands
+
+ call IsChannelSFXOn
+ jr nc, .ok
+
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_REST, [hl]
+ call nz, RestoreVolume
+ ld a, [wCurChannel]
+ cp CHAN5
+ jr nz, .ok
+
+ xor a
+ ldh [rNR10], a
+
+.ok
+ call StopChannel
+ ret
+
+.parse_commands
+ call ParseMusicCommand
+ jr ParseMusic
+
+RestoreVolume:
+ ld a, [wCurChannel]
+ cp CHAN5
+ ret nz
+
+ xor a
+ ld hl, wChannel6CryPitch
+ ld [hli], a
+ ld [hl], a
+ ld hl, wChannel8CryPitch
+ ld [hli], a
+ ld [hl], a
+ ld a, [wLastVolume]
+ ld [wVolume], a
+ xor a
+ ld [wLastVolume], a
+ ld [wSFXPriority], a
+ ret
+
+
+_ParseMusic:
+ ld a, [wCurMusicByte]
+ and $f
+ call SetNoteDuration
+ ld a, [wCurMusicByte]
+ swap a
+ and $f
+ jr z, .rest
+
+ ld hl, CHANNEL_PITCH
+ add hl, bc
+ ld [hl], a
+ ld e, a
+ ld hl, CHANNEL_OCTAVE
+ add hl, bc
+ ld d, [hl]
+ 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]
+ call LoadNote
+ ret
+
+
+.rest
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_REST, [hl]
+ ret
+
+
+ParseSFXOrRest:
+; turn noise sampling on
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set NOTE_NOISE_SAMPLING, [hl]
+ ld a, [wCurMusicByte]
+ call SetNoteDuration
+
+; update volume envelope from next param
+ call GetMusicByte
+ ld hl, CHANNEL_INTENSITY
+ add hl, bc
+ ld [hl], a
+
+; update lo frequence from next param
+ call GetMusicByte
+ ld hl, CHANNEL_FREQUENCY
+ add hl, bc
+ ld [hl], a
+
+; on noise channel?
+ 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:
+ ld a, [wCurChannel]
+ cp CHAN4
+ ret nz
+
+ ld a, [wCurMusicByte]
+ and $f
+ call SetNoteDuration
+
+ ld a, [wNoiseSampleSet]
+ ld e, a
+ ld d, 0
+ ld hl, Drumkits
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+ ld a, [wCurMusicByte]
+ swap a
+ and $f
+ ret z
+
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld [wNoiseSampleAddress], a
+ ld a, [hl]
+ ld [wNoiseSampleAddress + 1], a
+ xor a
+ ld [wNoiseSampleDelay], a
+ ret
+
+
+ParseMusicCommand:
+ ld a, [wCurMusicByte]
+ sub FIRST_MUSIC_CMD
+ ld e, a
+ ld d, 0
+ ld hl, MusicCommands
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+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
+ 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
+ dw Music_ToggleNoise
+ dw Music_ForceStereoPanning
+ dw Music_Volume
+ dw Music_PitchOffset
+ dw MusicE7
+ dw MusicE8
+ dw Music_TempoRelative
+ dw Music_RestartChannel
+ dw Music_NewSong
+ dw Music_SFXPriorityOn
+ dw Music_SFXPriorityOff
+ dw MusicEE
+ dw Music_StereoPanning
+ dw MusicF0
+ dw MusicF1
+ dw MusicF2
+ dw MusicF3
+ dw MusicF4
+ dw MusicF5
+ dw MusicF6
+ dw MusicF7
+ dw MusicF8
+ dw MusicF9
+ dw Music_SetCondition
+ dw Music_JumpIf
+ dw Music_Jump
+ dw Music_Loop
+ dw Music_Call
+ dw Music_Ret
+
+MusicF0:
+MusicF1:
+MusicF2:
+MusicF3:
+MusicF4:
+MusicF5:
+MusicF6:
+MusicF7:
+MusicF8:
+ ret
+
+Music_Ret:
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ res SOUND_SUBROUTINE, [hl]
+ 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 GetMusicByte
+ ld e, a
+ call GetMusicByte
+ ld d, a
+ push de
+ 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
+ pop de
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ set SOUND_SUBROUTINE, [hl]
+ ret
+
+Music_Jump:
+ 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:
+ call GetMusicByte
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_LOOPING, [hl]
+ jr nz, .checkloop
+
+ and a
+ jr z, .loop
+
+ dec a
+ set SOUND_LOOPING, [hl]
+ ld hl, CHANNEL_LOOP_COUNT
+ add hl, bc
+ ld [hl], a
+
+.checkloop
+ ld hl, CHANNEL_LOOP_COUNT
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .endloop
+ dec [hl]
+
+.loop
+ 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
+
+.endloop
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ res SOUND_LOOPING, [hl]
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc de
+ inc de
+ ld [hl], d
+ dec hl
+ ld [hl], e
+ ret
+
+Music_SetCondition:
+ call GetMusicByte
+ ld hl, CHANNEL_CONDITION
+ add hl, bc
+ ld [hl], a
+ ret
+
+Music_JumpIf:
+ call GetMusicByte
+ ld hl, CHANNEL_CONDITION
+ add hl, bc
+ cp [hl]
+ jr z, .jump
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc de
+ inc de
+ ld [hl], d
+ dec hl
+ ld [hl], e
+ ret
+
+.jump
+ 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
+
+MusicEE:
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ ld e, a
+ ld d, 0
+ ld hl, wChannel1JumpCondition
+ add hl, de
+ ld a, [hl]
+ and a
+ jr nz, .jump
+
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc de
+ inc de
+ ld [hl], d
+ dec hl
+ ld [hl], e
+ ret
+
+.jump
+ ld [hl], 0
+ 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
+
+MusicF9:
+ ld a, 1
+ ld [wc1b3], a
+ ret
+
+MusicE2:
+ 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:
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_VIBRATO, [hl]
+ res SOUND_VIBRATO, [hl]
+ call GetMusicByte
+ ld hl, CHANNEL_VIBRATO_DELAY
+ add hl, bc
+ ld [hl], a
+ call GetMusicByte
+ ld hl, CHANNEL_VIBRATO_EXTENT
+ add hl, bc
+ ld d, a
+ and $f0
+ swap a
+ srl a
+ ld e, a
+ adc 0
+ swap a
+ or e
+ ld [hl], a
+ ld hl, CHANNEL_VIBRATO_RATE
+ add hl, bc
+ ld a, d
+ and $0f
+ ld d, a
+ swap a
+ or d
+ ld [hl], a
+ ret
+
+Music_PitchSlide:
+ call GetMusicByte
+ ld [wCurNoteDuration], a
+ call GetMusicByte
+ ld d, a
+ and $f
+ ld e, a
+ ld a, d
+ swap a
+ and $f
+ ld d, a
+ call GetFrequency
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET
+ add hl, bc
+ ld [hl], e
+ ld hl, CHANNEL_PITCH_WHEEL_TARGET + 1
+ add hl, bc
+ ld [hl], d
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_PITCH_WHEEL, [hl]
+ ret
+
+Music_PitchOffset:
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_CRY_PITCH, [hl]
+ ld hl, CHANNEL_CRY_PITCH + 1
+ add hl, bc
+ call GetMusicByte
+ ld [hld], a
+ call GetMusicByte
+ ld [hl], a
+ ret
+
+MusicE7:
+ 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:
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_DUTY, [hl]
+ call GetMusicByte
+ rrca
+ rrca
+ ld hl, CHANNEL_SFX_DUTY_LOOP
+ add hl, bc
+ ld [hl], a
+ and $c0
+ ld hl, CHANNEL_DUTY_CYCLE
+ add hl, bc
+ ld [hl], a
+ ret
+
+MusicE8:
+ 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:
+ 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:
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ bit SOUND_NOISE, [hl]
+ jr z, .on
+ res SOUND_NOISE, [hl]
+ ret
+.on
+ set SOUND_NOISE, [hl]
+ call GetMusicByte
+ ld [wNoiseSampleSet], a
+ ret
+
+Music_NoteType:
+ call GetMusicByte
+ ld hl, CHANNEL_NOTE_LENGTH
+ add hl, bc
+ ld [hl], a
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ cp CHAN4
+ ret z
+ call Music_VolumeEnvelope
+ ret
+
+Music_PitchSweep:
+ call GetMusicByte
+ ld [wPitchSweep], a
+ ld hl, CHANNEL_NOTE_FLAGS
+ add hl, bc
+ set 3, [hl]
+ ret
+
+Music_DutyCycle:
+ call GetMusicByte
+ rrca
+ rrca
+ and $c0
+ ld hl, CHANNEL_DUTY_CYCLE
+ add hl, bc
+ ld [hl], a
+ ret
+
+
+Music_VolumeEnvelope:
+ call GetMusicByte
+ ld hl, CHANNEL_INTENSITY
+ add hl, bc
+ ld [hl], a
+ ret
+
+Music_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:
+ ld hl, CHANNEL_OCTAVE
+ add hl, bc
+ ld a, [wCurMusicByte]
+ and 7
+ ld [hl], a
+ ret
+
+Music_Transpose:
+ call GetMusicByte
+ ld hl, CHANNEL_PITCH_OFFSET
+ add hl, bc
+ ld [hl], a
+ ret
+
+Music_StereoPanning:
+ ld a, [wce5f]
+ bit 5, a
+ ret z
+
+Music_ForceStereoPanning:
+ call SetLRTracks
+ call GetMusicByte
+ ld hl, CHANNEL_TRACKS
+ add hl, bc
+ and [hl]
+ ld [hl], a
+ ret
+
+Music_Volume:
+ call GetMusicByte
+ ld a, [wMusicFade]
+ and a
+ ret nz
+ ld a, [wCurMusicByte]
+ ld [wVolume], a
+ ret
+
+Music_TempoRelative:
+ 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:
+ ld a, 1
+ ld [wSFXPriority], a
+ ret
+
+Music_SFXPriorityOff:
+ xor a
+ ld [wSFXPriority], a
+ ret
+
+Music_RestartChannel:
+ ld hl, CHANNEL_MUSIC_ID
+ add hl, bc
+ ld a, [hli]
+ ld [wMusicID], a
+ ld a, [hl]
+ ld [wMusicID + 1], a
+ ld hl, CHANNEL_MUSIC_BANK
+ add hl, bc
+ ld a, [hl]
+ ld [wMusicBank], a
+ call GetMusicByte
+ ld l, a
+ call GetMusicByte
+ ld h, a
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ push bc
+ call LoadChannel
+ call StartChannel
+ pop bc
+ ret
+
+Music_NewSong:
+ call GetMusicByte
+ ld e, a
+ call GetMusicByte
+ ld d, a
+ push bc
+ call _PlayMusic
+ pop bc
+ ret
+
+GetMusicByte:
+ push hl
+ push de
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_MUSIC_BANK
+ add hl, bc
+ ld a, [hl]
+ call _LoadMusicByte
+ ld [wCurMusicByte], a
+ inc de
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ pop de
+ pop hl
+ ret
+
+
+GetFrequency:
+; generate frequency
+; input:
+; d: octave
+; e: pitch
+; output:
+; de: frequency
+
+; get octave
+ ld hl, CHANNEL_PITCH_OFFSET
+ add hl, bc
+ ld a, [hl]
+ swap a
+ and $f
+ add d
+
+ push af
+
+ ld hl, CHANNEL_PITCH_OFFSET
+ add hl, bc
+ ld a, [hl]
+ and $f
+ ld l, a
+ ld d, 0
+ ld h, d
+ add hl, de
+ add hl, hl
+
+ ld de, FrequencyTable
+ add hl, de
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+
+ pop af
+
+.loop
+; [7 - octave] loops
+ cp 7
+ jr nc, .ok
+
+ sra d
+ rr e
+ inc a
+ jr .loop
+
+.ok
+ ld a, d
+ and $07
+ ld d, a
+ ret
+
+
+SetNoteDuration:
+; input: a = note duration in 16ths
+
+ inc a
+ ld e, a
+ ld d, 0
+ ld hl, CHANNEL_NOTE_LENGTH
+ add hl, bc
+ ld a, [hl]
+ ld l, 0
+ call .Multiply
+ ld a, l
+ ld hl, CHANNEL_TEMPO
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld hl, CHANNEL_FIELD16
+ add hl, bc
+ ld l, [hl]
+ call .Multiply
+ ld e, l
+ ld d, h
+ ld hl, CHANNEL_FIELD16
+ add hl, bc
+ ld [hl], e
+ 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
+ jr nc, .skip
+
+; add the remainder to the result
+ add hl, de
+
+.skip
+; de * 2
+ sla e
+ rl d
+
+; done multiplying?
+ and a
+ jr nz, .loop
+ ret
SetGlobalTempo:
push bc
@@ -573,7 +2512,7 @@ SetLRTracks:
maskbits NUM_MUSIC_CHANS
ld e, a
ld d, 0
- ld hl, $52B3 ; FIXME
+ ld hl, LRTracks
add hl, de
ld a, [hl]
ld hl, CHANNEL_TRACKS
@@ -588,29 +2527,399 @@ _PlayMusic::
ld [hl], e
inc hl
ld [hl], d
- ld hl, Music
+ ld hl, SongHeaderPointers
+ add hl, de
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld [wMusicBank], a
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ call LoadMusicByte
+ rlca
+ rlca
+ maskbits NUM_MUSIC_CHANS
+ inc a
+.loop
+ push af
+ call LoadChannel
+ call StartChannel
+ pop af
+ dec a
+ jr nz, .loop
+ xor a
+ ld [wc1b3], a
+ ld [wChannel1JumpCondition], a
+ ld [wChannel2JumpCondition], a
+ ld [wChannel3JumpCondition], a
+ ld [wChannel4JumpCondition], a
+ ret
+
+
+_PlayCryHeader::
+ ld hl, wMusicID
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+ ld hl, CryHeaderPointers
+ add hl, de
+ add hl, de
+ add hl, de
+
+ ld a, [hli]
+ ld [wMusicBank], a
+
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+
+ call LoadMusicByte
+ rlca
+ rlca
+ maskbits NUM_MUSIC_CHANS
+ inc a
+
+.loop
+ push af
+ call LoadChannel
+
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ set SOUND_REST, [hl]
+
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_CRY_PITCH, [hl]
+
+ ld hl, CHANNEL_CRY_PITCH
+ add hl, bc
+ ld a, [wCryPitch]
+ ld [hli], a
+ ld a, [wCryPitch + 1]
+ ld [hl], a
+
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ cp 3
+ jr nc, .start
+
+; no tempo for ch4
+ 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
+
+ ld a, [wce5f]
+ bit 5, a
+ jr z, .next
+
+ 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
+
+ ld a, [wLastVolume]
+ and a
+ jr nz, .end
+
+ ld a, [wVolume]
+ ld [wLastVolume], a
+ ld a, MAX_VOLUME
+ ld [wVolume], a
+
+.end
+; stop playing music
+ ld a, 1
+ ld [wSFXPriority], a
+ ret
+
+
+_PlaySFX::
+ ld hl, wMusicID
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ ld hl, SFXPointers
+; three byte pointers
+ add hl, de
+ add hl, de
+ add hl, de
+; get bank
+ ld a, [hli]
+ ld [wMusicBank], a
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ call LoadMusicByte
+ rlca
+ rlca
+ maskbits NUM_MUSIC_CHANS
+ inc a
+
+.start_channels
+ push af
+ call LoadChannel
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ set SOUND_SFX, [hl]
+ call StartChannel
+ pop af
+ dec a
+ jr nz, .start_channels
+ ret
+
+ ld a, [wce5f]
+ bit 5, a ; Stereo flag?
+ jr z, _PlaySFX
+
+ ld hl, wMusicID
+ ld [hl], e
+ inc hl
+ ld [hl], d
+
+ ld hl, SFXPointers
add hl, de
add hl, de
add hl, de
+
ld a, [hli]
ld [wMusicBank], a
ld e, [hl]
inc hl
ld d, [hl]
+
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
+
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ ld e, a
+ ld d, 0
+ ld hl, LRTracks
+ 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
+ jr c, .skip
+
+ 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
+
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ set SOUND_CHANNEL_ON, [hl]
+
+ pop af
+ dec a
+ jr nz, .loop
+
+ ret
+
+ ld hl, wMusicID
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ ld hl, SFXPointers
+ add hl, de
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld [wMusicBank], a
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ call LoadMusicByte
+ rlca
+ rlca
+ maskbits NUM_MUSIC_CHANS
+ inc a
+
+.cry_channels
+ push af
+ call LoadChannel
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ set SOUND_SFX, [hl]
+ ld hl, CHANNEL_FLAGS2
+ add hl, bc
+ set SOUND_CRY_PITCH, [hl]
+ ld hl, CHANNEL_CRY_PITCH
+ add hl, bc
+ ld a, [wCryPitch]
+ ld [hli], a
+ ld a, [wCryPitch + 1]
+ ld [hl], a
+ ld a, [wCurChannel]
+ maskbits NUM_MUSIC_CHANS
+ cp 3
+ jr nc, .cry_ok
+
+ ld hl, CHANNEL_TEMPO
+ add hl, bc
+ ld a, [wCryLength]
+ ld [hli], a
+ ld a, [wCryLength + 1]
+ ld [hl], a
+
+.cry_ok
call StartChannel
pop af
dec a
+ jr nz, .cry_channels
+
+ ret
+
+
+LoadChannel::
+ call LoadMusicByte
+ inc de
+ and 7
+ 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]
+ ld hl, CHANNEL_FLAGS1
+ add hl, bc
+ res SOUND_CHANNEL_ON, [hl]
+ call ChannelInit
+ ld hl, CHANNEL_MUSIC_ADDRESS
+ add hl, bc
+ call LoadMusicByte
+ ld [hli], a
+ inc de
+ call LoadMusicByte
+ ld [hl], a
+ inc de
+ ld hl, CHANNEL_MUSIC_ID
+ add hl, bc
+ ld a, [wMusicID]
+ ld [hli], a
+ ld a, [wMusicID + 1]
+ ld [hl], a
+ ld hl, CHANNEL_MUSIC_BANK
+ add hl, bc
+ ld a, [wMusicBank]
+ ld [hl], a
+ ret
+
+
+ChannelInit:
+ push de
+ xor a
+ ld hl, CHANNEL_MUSIC_ID
+ add hl, bc
+ ld e, CHANNEL_STRUCT_LENGTH
+
+.loop
+ ld [hli], a
+ dec e
jr nz, .loop
+
+ ld hl, CHANNEL_TEMPO
+ add hl, bc
xor a
- ; TODO
+ ld [hli], a
+ inc a
+ ld [hl], a
+ ld hl, CHANNEL_NOTE_LENGTH
+ add hl, bc
+ ld [hl], a
+ pop de
+ ret
+
+
+LoadMusicByte::
+ ld a, [wMusicBank]
+ call _LoadMusicByte
+ ret
+
+INCLUDE "audio/notes.inc"
+
+INCLUDE "audio/wave_samples.inc"
+
+INCLUDE "audio/wave_overrides.inc"
+
+INCLUDE "audio/intensity_overrides.inc"
+
+INCLUDE "audio/drumkits.inc"
+
+LRTracks:
+; bit corresponds to track #
+; hi: left channel
+; lo: right channel
+ db $11, $22, $44, $88
+
+ChannelPointers:
+; music channels
+ dw wChannel1
+ dw wChannel2
+ dw wChannel3
+ dw wChannel4
+; sfx channels
+ dw wChannel5
+ dw wChannel6
+ dw wChannel7
+ dw wChannel8
-SECTION "audio/engine.asm@Song Header Pointers", ROMX
INCLUDE "audio/song_header_pointers.inc"