INCLUDE "constants.asm" SECTION "audio/engine.asm", ROMX _DisableAudio:: push hl push de push bc push af ld hl, rNR50 xor a ld [hli], a ld [hli], a ld a, $80 ld [hli], a ld hl, rNR10 ld e, 4 .init_channel xor a ld [hli], a ld [hli], a ld a, 8 ld [hli], a xor a ld [hli], a ld a, $80 ld [hli], a dec e jr nz, .init_channel ld hl, wMusic ld de, wMusicInitEnd - wMusic .clear xor a ld [hli], a dec de ld a, e or d jr nz, .clear ld a, MAX_VOLUME ld [wVolume], a pop af pop bc pop de pop hl ret _UpdateSound:: ; Called once per frame xor a ld [wCurChannel], a ld [wSoundOutput], a ld bc, wChannel1 .loop ld hl, CHANNEL_FLAGS1 add hl, bc bit SOUND_CHANNEL_ON, [hl] jr z, .nextchannel call UpdateChannel .nextchannel ; Next channel ld hl, CHANNEL_STRUCT_LENGTH add hl, bc ld c, l ld b, h ld a, [wCurChannel] inc a ld [wCurChannel], a cp NUM_CHANNELS jr nz, .loop call Functione82f0 ret UpdateChannel: ; Get the note's duration ld hl, CHANNEL_NOTE_DURATION add hl, bc ld a, [hl] ; Check if this is the last frame cp 1 jr z, .noteover and a jr z, .noteover dec [hl] jr .continue_sound_update .noteover call DisablePitchWheel call ParseMusic .continue_sound_update call Functione80b6 ret DisablePitchWheel: ld hl, CHANNEL_FLAGS2 add hl, bc res SOUND_PITCH_WHEEL, [hl] ret Unreferenced_Functione8081: ld a, [wMapMusic] bit 0, a jr nz, .disable_music bit 1, a jr nz, .disable_sfx .fail and a ret .disable_music call _DisableAudio jr .done .disable_sfx call IsChannelSFXOn jr c, .fail ; Clear the registers of the channel call GetChannelRegisters ld e, rNR20 - rNR10 xor a .clear_loop ld [hli], a dec e jr nz, .clear_loop .done scf ret GetChannelRegisters: ld a, [wCurChannel] ld e, a ld d, $00 ld hl, .registers add hl, de ld l, [hl] ld h, $ff ; HIGH(rNR10, rNR20, rNR30, rNR40) ret .registers db LOW(rNR10), LOW(rNR20), LOW(rNR30), LOW(rNR40) Functione80b6: ld hl, CHANNEL_DUTY_CYCLE add hl, bc ld a, [hl] ld [wCurTrackDuty], a ld hl, CHANNEL_INTENSITY add hl, bc ld a, [hl] ld [wCurTrackIntensity], a ld hl, CHANNEL_FREQUENCY add hl, bc ld a, [hli] ld [wCurTrackFrequency], a ld a, [hl] ld [wCurTrackFrequency + 1], a ld a, $3F ld [wSoundLength], a call Functione85d8 call ReadNoiseSample call HaltMusicWhileSFXPlaying call IsChannelSFXOn jr nc, .end call UpdateChannels ld hl, CHANNEL_TRACKS add hl, bc ld a, [wSoundOutput] or [hl] ld [wSoundOutput], a .end ld hl, CHANNEL_NOTE_FLAGS add hl, bc xor a ld [hl], a ret UpdateChannels: ld hl, .jumptable ld a, [wCurChannel] maskbits NUM_CHANNELS add a ld e, a ld d, 0 add hl, de ld a, [hli] ld h, [hl] ld l, a jp hl .jumptable ; 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 IsChannelSFXOn: ; If it's not a valid channel, return ld a, [wCurChannel] cp NUM_MUSIC_CHANS jr nc, .off ; Check if the corresponding SFX channel is on ld hl, CHANNEL_STRUCT_LENGTH * NUM_MUSIC_CHANS + CHANNEL_FLAGS1 add hl, bc bit SOUND_CHANNEL_ON, [hl] jr z, .off and a ret .off scf ret IsAnySFXOn: ld hl, wChannel5Flags1 bit SOUND_CHANNEL_ON, [hl] jr nz, .on ld hl, wChannel6Flags1 bit SOUND_CHANNEL_ON, [hl] jr nz, .on ld hl, wChannel7Flags1 bit SOUND_CHANNEL_ON, [hl] jr nz, .on ld hl, wChannel8Flags1 bit SOUND_CHANNEL_ON, [hl] jr nz, .on and a ret .on scf ret Functione82f0: call IncrementTempo call PlayDanger call FadeMusic call DoSweepingFade ld a, [wVolume] ld [rNR50], a ld a, [wSoundOutput] ld [rNR51], a ret PlayDanger: ld a, [wLowHealthAlarm] bit DANGER_ON_F, a ret z ; Don't do anything if SFX is being played and $ff ^ (1 << DANGER_ON_F) ld d, a call IsAnySFXOn jr c, .increment ; Play the high tone and a jr z, .begin ; Play the low tone cp 16 jr z, .halfway jr .increment .halfway ld hl, DangerSoundLow jr .applychannel .begin ld hl, DangerSoundHigh .applychannel xor a ld [rNR10], a ld a, [hli] ld [rNR11], a ld a, [hli] ld [rNR12], a ld a, [hli] ld [rNR13], a ld a, [hli] ld [rNR14], a .increment ld a, d inc a cp 30 ; Ending frame jr c, .noreset xor a .noreset ; Make sure the danger sound is kept on or 1 << DANGER_ON_F ld [wLowHealthAlarm], a ; Make sure channel 1 is on ld a, [wSoundOutput] or $11 ld [wSoundOutput], a ret DangerSoundHigh: db $80 ; duty 50% db $e2 ; volume 14, envelope decrease sweep 2 db $50 ; frequency: $750 db $87 ; restart sound DangerSoundLow: db $80 ; duty 50% db $e2 ; volume 14, envelope decrease sweep 2 db $ee ; frequency: $6ee db $86 ; restart sound IncrementTempo: call IsAnyChannelOn ret c ld a, [wIncrementTempo] ld e, a ld a, [wIncrementTempo + 1] ld d, a or e ret z ld hl, wChannel1Tempo call .addtempo ld hl, wChannel2Tempo call .addtempo ld hl, wChannel3Tempo call .addtempo ld hl, wChannel4Tempo call .addtempo xor a ld [wIncrementTempo], a ld [wIncrementTempo + 1], a ret .addtempo ; (int16)[hl] += de push de 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 de ret IsAnyChannelOn: ; Check if any music channel is on and isn't on the last frame ld hl, wChannel1Flags1 bit SOUND_CHANNEL_ON, [hl] jr z, .check_channel2 ld hl, wChannel1NoteDuration ld a, [hl] cp 1 jr nz, .on .check_channel2 ld hl, wChannel2Flags1 bit SOUND_CHANNEL_ON, [hl] jr z, .check_channel3 ld hl, wChannel2NoteDuration ld a, [hl] cp 1 jr nz, .on .check_channel3 ld hl, wChannel3Flags1 bit SOUND_CHANNEL_ON, [hl] jr z, .check_channel4 ld hl, wChannel3NoteDuration ld a, [hl] cp 1 jr nz, .on .check_channel4 ld hl, wChannel4Flags1 bit SOUND_CHANNEL_ON, [hl] jr z, .off ld hl, wChannel4NoteDuration ld a, [hl] cp 1 jr nz, .on .off and a ret .on scf ret FadeMusic: ; Fade music if applicable ; usage: ; write to wMusicFade ; song fades out at the given rate ; load song id in wMusicFadeID ; fade new song in ; notes: ; max # frames per volume level is $7f ; Exit early if not fading ld a, [wMusicFade] and a ret z ; If it doesn't have a count, assign it ld a, [wMusicFadeCount] and a jr z, .update ; Count down dec a ld [wMusicFadeCount], a ret .update ld a, [wMusicFade] ld d, a ; Get new count and $7f 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 ; Decrement volume and a jr z, .novolume dec a jr .updatevolume .novolume ; Fade in new song ld a, [wMusicFadeID] ld e, a ld a, [wMusicFadeID + 1] ld d, a push bc call _PlayMusic pop bc ld hl, wMusicFade set 7, [hl] ret .fadein ; Increment volume cp MAX_VOLUME & $f jr nc, .maxvolume inc a jr .updatevolume .maxvolume ; Clear wMusicFade xor a ld [wMusicFade], a ret .updatevolume ; Set the current volume for both speakers ld d, a swap a or d ld [wVolume], a ret 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 ld a, [wCurChannel] cp CHAN5 jr nc, .sfxchannels ld bc, wChannel1 call Tempo ld bc, wChannel2 call Tempo ld bc, wChannel3 call Tempo ld bc, wChannel4 call Tempo jr .end .sfxchannels ld bc, wChannel5 call Tempo ld bc, wChannel6 call Tempo ld bc, wChannel7 call Tempo ld bc, wChannel8 call Tempo .end pop bc ret Tempo: ld hl, CHANNEL_TEMPO add hl, bc ld [hl], e inc hl ld [hl], d xor a ld hl, CHANNEL_FIELD16 add hl, bc ld [hl], a ret StartChannel: call SetLRTracks ld hl, CHANNEL_FLAGS1 add hl, bc set SOUND_CHANNEL_ON, [hl] ret StopChannel: ld hl, CHANNEL_FLAGS1 add hl, bc res SOUND_CHANNEL_ON, [hl] ld hl, CHANNEL_MUSIC_ID add hl, bc xor a ld [hli], a ld [hli], a ld [hli], a ret SetLRTracks: 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, CHANNEL_TRACKS add hl, bc ld [hl], a pop de ret _PlayMusic:: ld hl, wMusicID ld [hl], e inc hl ld [hl], d 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, Cries 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, SFX ; 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, SFX 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, SFX 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 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 INCLUDE "audio/song_header_pointers.inc"