shithub: pokecrystal

ref: dad64b1d9d1a5225f3c68199ec65a574a2a5176f
dir: /audio/engine.asm/

View raw version
; The entire sound engine. Uses section "audio" in WRAM.

; Interfaces are in bank 0.

; Notable functions:
; 	UpdateSound (called during VBlank)
; 	FadeMusic
; 	PlaySFX
; 	PlayCry

SoundRestart: ; e8000
; restart sound operation
; clear all relevant hardware registers & wram
	push hl
	push de
	push bc
	push af
	call MusicOff
	ld hl, rNR50 ; channel control registers
	xor a
	ld [hli], a ; rNR50 ; volume/vin
	ld [hli], a ; rNR51 ; sfx channels
	ld a, $80 ; all channels on
	ld [hli], a ; ff26 ; music channels

	ld hl, rNR10 ; sound channel registers
	ld e, $04 ; number of channels
.clearsound
;   sound channel   1      2      3      4
	xor a
	ld [hli], a ; rNR10, $ff15, rNR30, $ff1f ; sweep = 0

	ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
	ld a, $08
	ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
	xor a
	ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
	ld a, $80
	ld [hli], a ; rNR14, rNR24, rNR34, rNR44 ; restart sound (freq hi = 0)
	dec e
	jr nz, .clearsound

	ld hl, Channel1 ; start of channel data
	ld de, $01bf ; length of area to clear (entire sound wram area)
.clearchannels ; clear $c101-$c2bf
	xor a
	ld [hli], a
	dec de
	ld a, e
	or d
	jr nz, .clearchannels
	ld a, $77 ; max
	ld [Volume], a
	call MusicOn
	pop af
	pop bc
	pop de
	pop hl
	ret
; e803d

MusicFadeRestart: ; e803d
; restart but keep the music id to fade in to
	ld a, [MusicFadeIDHi]
	push af
	ld a, [MusicFadeIDLo]
	push af
	call SoundRestart
	pop af
	ld [MusicFadeIDLo], a
	pop af
	ld [MusicFadeIDHi], a
	ret
; e8051

MusicOn: ; e8051
	ld a, $01
	ld [MusicPlaying], a
	ret
; e8057

MusicOff: ; e8057
	xor a
	ld [MusicPlaying], a
	ret
; e805c

UpdateSound: ; e805c
; called once per frame
	; no use updating audio if it's not playing
	ld a, [MusicPlaying]
	and a
	ret z
	; start at ch1
	xor a
	ld [CurChannel], a ; just
	ld [SoundOutput], a ; off
	ld bc, Channel1
.loop
	; is the channel active?
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 0, [hl]
	jp z, .nextchannel
	; check time left in the current note
	ld hl, Channel1NoteDuration - Channel1
	add hl, bc
	ld a, [hl]
	cp a, $02 ; 1 or 0?
	jr c, .noteover
	dec [hl]
	jr .asm_e8093
.noteover
	; reset vibrato delay
	ld hl, Channel1VibratoDelay - Channel1
	add hl, bc
	ld a, [hl]
	ld hl, Channel1VibratoDelayCount - Channel1
	add hl, bc
	ld [hl], a
	; turn vibrato off for now
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	res 1, [hl]
	; get next note
	call ParseMusic
.asm_e8093
	;
	call Functione84f9
	; duty cycle
	ld hl, Channel1DutyCycle - Channel1
	add hl, bc
	ld a, [hli]
	ld [$c292], a
	; intensity
	ld a, [hli]
	ld [$c293], a
	; frequency
	ld a, [hli]
	ld [$c294], a
	ld a, [hl]
	ld [$c295], a
	;
	call Functione8466 ; handle vibrato and other things
	call HandleNoise
	; turn off music when playing sfx?
	ld a, [SFXPriority]
	and a
	jr z, .next
	; are we in a sfx channel right now?
	ld a, [CurChannel]
	cp a, $04
	jr nc, .next
	; are any sfx channels active?
	; if so, mute
	ld hl, $c1cc ; Channel5Flags
	bit 0, [hl]
	jr nz, .restnote
	ld hl, $c1fe ; Channel6Flags
	bit 0, [hl]
	jr nz, .restnote
	ld hl, $c230 ; Channel7Flags
	bit 0, [hl]
	jr nz, .restnote
	ld hl, $c262 ; Channel8Flags
	bit 0, [hl]
	jr z, .next
.restnote
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 5, [hl] ; Rest
.next
	; are we in a sfx channel right now?
	ld a, [CurChannel]
	cp a, $04 ; sfx
	jr nc, .asm_e80ee
	ld hl, $00cb
	add hl, bc
	bit 0, [hl]
	jr nz, .asm_e80fc
.asm_e80ee
	call UpdateChannels
	ld hl, Channel1Tracks - Channel1
	add hl, bc
	ld a, [SoundOutput]
	or [hl]
	ld [SoundOutput], a
.asm_e80fc
	; clear note flags
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	xor a
	ld [hl], a
.nextchannel
	; next channel
	ld hl, Channel2 - Channel1
	add hl, bc
	ld c, l
	ld b, h
	ld a, [CurChannel]
	inc a
	ld [CurChannel], a
	cp a, $08 ; are we done?
	jp nz, .loop ; do it all again
	; writing to hardware registers?
	call Functione8307
	; fade music in/out
	call FadeMusic
	; write volume to hardware register
	ld a, [Volume]
	ld [rNR50], a
	; write SO on/off to hardware register
	ld a, [SoundOutput]
	ld [rNR51], a
	ret
; e8125

UpdateChannels: ; e8125
	ld hl, .ChannelFnPtrs
	ld a, [CurChannel]
	and a, $07
	add a
	ld e, a
	ld d, $00
	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, [$c2a6]
	bit 7, a
	ret nz
.Channel5
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	bit 3, [hl]
	jr z, .asm_e8159
	;
	ld a, [SoundInput]
	ld [rNR10], a
.asm_e8159
	bit 5, [hl] ; rest
	jr nz, .ch1rest
	bit 4, [hl]
	jr nz, .asm_e81a2
	bit 1, [hl]
	jr nz, .asm_e816b
	bit 6, [hl]
	jr nz, .asm_e8184
	jr .asm_e8175
.asm_e816b
	ld a, [$c294]
	ld [rNR13], a
	ld a, [$c295]
	ld [rNR14], a
.asm_e8175
	bit 0, [hl]
	ret z
	ld a, [$c292]
	ld d, a
	ld a, [rNR11]
	and a, $3f ; sound length
	or d
	ld [rNR11], a
	ret
.asm_e8184
	ld a, [$c292]
	ld d, a
	ld a, [rNR11]
	and a, $3f ; sound length
	or d
	ld [rNR11], a
	ld a, [$c294]
	ld [rNR13], a
	ret
.ch1rest
	ld a, [rNR52]
	and a, %10001110 ; ch1 off
	ld [rNR52], a
	ld hl, rNR10
	call ClearChannel
	ret
.asm_e81a2
	ld hl, $c292
	ld a, $3f ; sound length
	or [hl]
	ld [rNR11], a
	ld a, [$c293]
	ld [rNR12], a
	ld a, [$c294]
	ld [rNR13], a
	ld a, [$c295]
	or a, $80
	ld [rNR14], a
	ret

.Channel2
.Channel6
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	bit 5, [hl] ; rest
	jr nz, .ch2rest
	bit 4, [hl]
	jr nz, .asm_e8204
	bit 6, [hl]
	jr nz, .asm_e81e6
	bit 0, [hl]
	ret z
	ld a, [$c292]
	ld d, a
	ld a, [rNR21]
	and a, $3f ; sound length
	or d
	ld [rNR21], a
	ret
.asm_e81db ; unused
	ld a, [$c294]
	ld [rNR23], a
	ld a, [$c295]
	ld [rNR24], a
	ret
.asm_e81e6
	ld a, [$c292]
	ld d, a
	ld a, [rNR21]
	and a, $3f ; sound length
	or d
	ld [rNR21], a
	ld a, [$c294]
	ld [rNR23], a
	ret
.ch2rest
	ld a, [rNR52]
	and a, %10001101 ; ch2 off
	ld [rNR52], a
	ld hl, $ff15
	call ClearChannel
	ret
.asm_e8204
	ld hl, $c292
	ld a, $3f ; sound length
	or [hl]
	ld [rNR21], a
	ld a, [$c293]
	ld [rNR22], a
	ld a, [$c294]
	ld [rNR23], a
	ld a, [$c295]
	or a, $80 ; initial (restart)
	ld [rNR24], a
	ret

.Channel3
.Channel7
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	bit 5, [hl] ; rest
	jr nz, .ch3rest
	bit 4, [hl]
	jr nz, .asm_e824d
	bit 6, [hl]
	jr nz, .asm_e823a
	ret
.asm_e822f ; unused
	ld a, [$c294]
	ld [rNR33], a
	ld a, [$c295]
	ld [rNR34], a
	ret
.asm_e823a
	ld a, [$c294]
	ld [rNR33], a
	ret
.ch3rest
	ld a, [rNR52]
	and a, %10001011 ; ch3 off
	ld [rNR52], a
	ld hl, rNR30
	call ClearChannel
	ret
.asm_e824d
	ld a, $3f
	ld [rNR31], a
	xor a
	ld [rNR30], a
	call .asm_e8268
	ld a, $80
	ld [rNR30], a
	ld a, [$c294]
	ld [rNR33], a
	ld a, [$c295]
	or a, $80
	ld [rNR34], a
	ret
.asm_e8268
	push hl
	ld a, [$c293]
	and a, $0f ; only 0-9 are valid
	ld l, a
	ld h, $00
	; hl << 4
	; each wavepattern is $0f bytes long
	; so seeking is done in $10s
	add hl, hl
	add hl, hl
	add hl, hl
	add hl, hl
	ld de, WaveSamples
	add hl, de
	; load wavepattern into $ff30-$ff3f
	ld a, [hli]
	ld [$ff30], a
	ld a, [hli]
	ld [$ff31], a
	ld a, [hli]
	ld [$ff32], a
	ld a, [hli]
	ld [$ff33], a
	ld a, [hli]
	ld [$ff34], a
	ld a, [hli]
	ld [$ff35], a
	ld a, [hli]
	ld [$ff36], a
	ld a, [hli]
	ld [$ff37], a
	ld a, [hli]
	ld [$ff38], a
	ld a, [hli]
	ld [$ff39], a
	ld a, [hli]
	ld [$ff3a], a
	ld a, [hli]
	ld [$ff3b], a
	ld a, [hli]
	ld [$ff3c], a
	ld a, [hli]
	ld [$ff3d], a
	ld a, [hli]
	ld [$ff3e], a
	ld a, [hli]
	ld [$ff3f], a
	pop hl
	ld a, [$c293]
	and a, $f0
	sla a
	ld [rNR32], a
	ret

.Channel4
.Channel8
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	bit 5, [hl] ; rest
	jr nz, .ch4rest
	bit 4, [hl]
	jr nz, .asm_e82d4
	ret
.asm_e82c1 ; unused
	ld a, [$c294]
	ld [rNR43], a
	ret
.ch4rest
	ld a, [rNR52]
	and a, %10000111 ; ch4 off
	ld [rNR52], a
	ld hl, $ff1f
	call ClearChannel
	ret
.asm_e82d4
	ld a, $3f ; sound length
	ld [rNR41], a
	ld a, [$c293]
	ld [rNR42], a
	ld a, [$c294]
	ld [rNR43], a
	ld a, $80
	ld [rNR44], a
	ret
; e82e7

_CheckSFX: ; e82e7
; return carry if any sfx channels are active
	ld hl, $c1cc ; Channel5Flags
	bit 0, [hl]
	jr nz, .sfxon
	ld hl, $c1fe ; Channel6Flags
	bit 0, [hl]
	jr nz, .sfxon
	ld hl, $c230 ; Channel7Flags
	bit 0, [hl]
	jr nz, .sfxon
	ld hl, $c262 ; Channel8Flags
	bit 0, [hl]
	jr nz, .sfxon
	and a
	ret
.sfxon
	scf
	ret
; e8307

Functione8307: ; e8307
; what is $c2a6?
	ld a, [$c2a6]
	bit 7, a
	ret z
	and a, $7f
	ld d, a
	call _CheckSFX
	jr c, .asm_e8335
	and a
	jr z, .asm_e8323
	cp a, $10
	jr z, .asm_e831e
	jr .asm_e8335
.asm_e831e
	ld hl, Tablee8354
	jr .updatehw
.asm_e8323
	ld hl, Tablee8350
.updatehw
	xor a
	ld [rNR10], a ; sweep off
	ld a, [hli]
	ld [rNR11], a ; sound length / duty cycle
	ld a, [hli]
	ld [rNR12], a ; ch1 volume envelope
	ld a, [hli]
	ld [rNR13], a ; ch1 frequency lo
	ld a, [hli]
	ld [rNR14], a ; ch1 frequency hi
.asm_e8335
	ld a, d
	inc a
	cp a, $1e
	jr c, .asm_e833c
	xor a
.asm_e833c
	or a, $80
	ld [$c2a6], a
	; is hw ch1 on?
	ld a, [SoundOutput]
	and a, $11
	ret nz
	; if not, turn it on
	ld a, [SoundOutput]
	or a, $11
	ld [SoundOutput], a
	ret
; e8350

Tablee8350: ; e8350
	db $80 ; duty 50%
	db $e2 ; volume $e, envelope decrease sweep 2
	db $50 ; frequency: $750
	db $87 ; restart sound
; e8354

Tablee8354: ; e8354
	db $80 ; duty 50%
	db $e2 ; volume $e, envelope decrease sweep 2
	db $ee ; frequency: $6ee
	db $86 ; restart sound
; e8358

FadeMusic: ; e8358
; fade music if applicable
; usage:
;	write to MusicFade
;	song fades out at the given rate
;	load song id in MusicFadeID
;	fade new song in
; notes:
;	max # frames per volume level is $3f

	; fading?
	ld a, [MusicFade]
	and a
	ret z
	; has the count ended?
	ld a, [MusicFadeCount]
	and a
	jr z, .update
	; count down
	dec a
	ld [MusicFadeCount], a
	ret
.update
	ld a, [MusicFade]
	ld d, a
	; get new count
	and a, $3f
	ld [MusicFadeCount], a
	; get SO1 volume
	ld a, [Volume]
	and a, $07
	; which way are we fading?
	bit 7, d
	jr nz, .fadein
	; fading out
	and a
	jr z, .novolume
	dec a
	jr .updatevolume
.novolume
	; make sure volume is off
	xor a
	ld [Volume], a
	; did we just get on a bike?
	ld a, [PlayerState]
	cp a, $01 ; bicycle
	jr z, .bicycle
	push bc
	; restart sound
	call MusicFadeRestart
	; get new song id
	ld a, [MusicFadeIDLo]
	and a
	jr z, .quit ; this assumes there are fewer than 256 songs!
	ld e, a
	ld a, [MusicFadeIDHi]
	ld d, a
	; load new song
	call LoadMusic
.quit
	; cleanup
	pop bc
	; stop fading
	xor a
	ld [MusicFade], a
	ret
.bicycle
	push bc
	; restart sound
	call MusicFadeRestart
	; this turns the volume up
	; turn it back down
	xor a
	ld [Volume], a
	; get new song id
	ld a, [MusicFadeIDLo]
	ld e, a
	ld a, [MusicFadeIDHi]
	ld d, a
	; load new song
	call LoadMusic
	pop bc
	; fade in
	ld hl, MusicFade
	set 7, [hl]
	ret
.fadein
	; are we done?
	cp a, $07
	jr nc, .maxvolume
	; inc volume
	inc a
	jr .updatevolume
.maxvolume
	; we're done
	xor a
	ld [MusicFade], a
	ret
.updatevolume
	; hi = lo
	ld d, a
	swap a
	or d
	ld [Volume], a
	ret
; e83d1

LoadNote: ; e83d1
	; check mute??
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	bit 1, [hl]
	ret z
	; get note duration
	ld hl, Channel1NoteDuration - Channel1
	add hl, bc
	ld a, [hl]
	ld hl, $c297 ; ????
	sub [hl]
	jr nc, .ok
	ld a, $01
.ok
	ld [hl], a
	; get frequency
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; ????
	ld hl, $0021
	add hl, bc
	ld a, e
	sub [hl]
	ld e, a
	ld a, d
	sbc a, $00
	ld d, a
	; ????
	ld hl, $0022
	add hl, bc
	sub [hl]
	jr nc, .asm_e8420
	; ????
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	set 1, [hl]
	; get frequency
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; ????
	ld hl, $0021
	add hl, bc
	ld a, [hl]
	sub e
	ld e, a
	ld a, d
	sbc a, $00
	ld d, a
	; ????
	ld hl, $0022
	add hl, bc
	ld a, [hl]
	sub d
	ld d, a
	jr .asm_e843e
.asm_e8420
	; ????
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	res 1, [hl]
	; get frequency
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; ????
	ld hl, $0021
	add hl, bc
	ld a, e
	sub [hl]
	ld e, a
	ld a, d
	sbc a, $00
	ld d, a
	; ????
	ld hl, $0022
	add hl, bc
	sub [hl]
	ld d, a
.asm_e843e
	push bc
	ld hl, $c297
	ld b, $00 ; loop count
.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 ; result
	add [hl]
	ld d, b ; loop count
	; ????
	pop bc
	ld hl, $0023
	add hl, bc
	ld [hl], d
	ld hl, $0024
	add hl, bc
	ld [hl], a
	; clear ????
	ld hl, $0025
	add hl, bc
	xor a
	ld [hl], a
	ret
; e8466

Functione8466: ; e8466
; handle vibrato and other things
; unknowns: $c292, $c294
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	bit 2, [hl]
	jr z, .next
	ld hl, $001c
	add hl, bc
	ld a, [hl]
	rlca
	rlca
	ld [hl], a
	and a, $c0
	ld [$c292], a
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 0, [hl]
.next
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	bit 4, [hl]
	jr z, .vibrato
	ld hl, $0027
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	ld hl, $c294
	ld a, [hli]
	ld h, [hl]
	ld l, a
	add hl, de
	ld e, l
	ld d, h
	ld hl, $c294
	ld [hl], e
	inc hl
	ld [hl], d
.vibrato
	; is vibrato on?
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	bit 0, [hl] ; vibrato
	jr z, .quit
	; is vibrato active for this note yet?
	; is the delay over?
	ld hl, Channel1VibratoDelayCount - Channel1
	add hl, bc
	ld a, [hl]
	and a
	jr nz, .subexit
	; is the extent nonzero?
	ld hl, Channel1VibratoExtent - Channel1
	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, Channel1VibratoRate - Channel1
	add hl, bc
	ld a, [hl]
	and a, $0f ; count
	jr z, .toggle
.subexit
	dec [hl]
	jr .quit
.toggle
	; refresh count
	ld a, [hl]
	swap [hl]
	or [hl]
	ld [hl], a
	; ????
	ld a, [$c294]
	ld e, a
	; toggle vibrato up/down
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	bit 0, [hl] ; vibrato up/down
	jr z, .down
; up
	; vibrato down
	res 0, [hl]
	; get the delay
	ld a, d
	and a, $0f ; lo
	;
	ld d, a
	ld a, e
	sub d
	jr nc, .asm_e84ef
	ld a, $00
	jr .asm_e84ef
.down
	; vibrato up
	set 0, [hl]
	; get the delay
	ld a, d
	and a, $f0 ; hi
	swap a ; move it to lo
	;
	add e
	jr nc, .asm_e84ef
	ld a, $ff
.asm_e84ef
	ld [$c294], a
	;
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 6, [hl]
.quit
	ret
; e84f9

Functione84f9: ; e84f9
	; quit if ????
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	bit 1, [hl]
	ret z
	; de = Frequency
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	;
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	bit 1, [hl]
	jr z, .next
	;
	ld hl, $0023
	add hl, bc
	ld l, [hl]
	ld h, $00
	add hl, de
	ld d, h
	ld e, l
	; get ????
	ld hl, $0024
	add hl, bc
	ld a, [hl]
	; add it to ????
	ld hl, $0025
	add hl, bc
	add [hl]
	ld [hl], a
	ld a, $00
	adc e
	ld e, a
	ld a, $00
	adc d
	ld d, a
	;
	ld hl, $0022
	add hl, bc
	ld a, [hl]
	cp d
	jp c, .quit1
	jr nz, .quit2
	ld hl, $0021
	add hl, bc
	ld a, [hl]
	cp e
	jp c, .quit1
	jr .quit2
.next
	ld a, e
	ld hl, $0023
	add hl, bc
	ld e, [hl]
	sub e
	ld e, a
	ld a, d
	sbc a, $00
	ld d, a
	ld hl, $0024
	add hl, bc
	ld a, [hl]
	add a
	ld [hl], a
	ld a, e
	sbc a, $00
	ld e, a
	ld a, d
	sbc a, $00
	ld d,a
	ld hl, $0022
	add hl, bc
	ld a, d
	cp [hl]
	jr c, .quit1
	jr nz, .quit2
	ld hl, $0021
	add hl, bc
	ld a, e
	cp [hl]
	jr nc, .quit2
.quit1
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	res 1, [hl]
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	res 1, [hl]
	ret
.quit2
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 1, [hl]
	set 0, [hl]
	ret
; e858c

HandleNoise: ; e858c
	; is noise sampling on?
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 4, [hl] ; noise sampling
	ret z
	; are we in a sfx channel?
	ld a, [CurChannel]
	bit 2, a ; sfx
	jr nz, .next
	; is ch8 on? (noise)
	ld hl, $c262 ; Channel8Flags
	bit 0, [hl] ; on?
	jr z, .next
	; is ch8 playing noise?
	bit 4, [hl]
	ret nz ; quit if so
	;
.next
	ld a, [$c2a2]
	and a
	jr z, ReadNoiseSample
	dec a
	ld [$c2a2], a
	ret
; e85af

ReadNoiseSample: ; e85af
; sample struct:
;	[wx] [yy] [zz]
;	w: ? either 2 or 3
;	x: ? 0-7
;	zzyy: pointer to sample data
;		NOTE: these seem to have $4000 added to them later

	; de = NoiseSampleAddress
	ld hl, NoiseSampleAddress
	ld e, [hl]
	inc hl
	ld d, [hl]
	; is it empty?
	ld a, e
	or d
	jr z, .quit
	; get the noise sample
	ld a, [de]
	inc de
	; are we done?
	cp a, $ff
	jr z, .quit
	;
	and a, $0f ; bottom nybble
	inc a
	ld [$c2a2], a
	ld a, [de]
	inc de
	ld [$c293], a
	ld a, [de]
	inc de
	ld [$c294], a
	xor a
	ld [$c295], a
	;
	ld hl, NoiseSampleAddress
	ld [hl], e
	inc hl
	ld [hl], d
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 4, [hl]
	ret
.quit
	ret
; e85e1

ParseMusic: ; e85e1
; parses until a note is read or the song is ended
	call GetMusicByte ; store next byte in a
	cp a, $ff ; is the song over?
	jr z, .readff
	cp a, $d0 ; is it a note?
	jr c, .readnote
	; then it's a command
.readcommand
	call ParseCommand
	jr ParseMusic ; start over

.readnote
; CurMusicByte contains current note
; special notes
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 3, [hl]
	jp nz, Functione8698
	bit 5, [hl]
	jp nz, Functione8698
	bit 4, [hl] ; noise sample
	jp nz, GetNoiseSample
; normal note
	; set note duration (bottom nybble)
	ld a, [CurMusicByte]
	and a, $0f
	call SetNoteDuration
	; get note pitch (top nybble)
	ld a, [CurMusicByte]
	swap a
	and a, $0f
	jr z, .rest ; pitch $0 -> rest
	; update pitch
	ld hl, Channel1Pitch - Channel1
	add hl, bc
	ld [hl], a
	; store pitch in e
	ld e, a
	; store octave in d
	ld hl, Channel1Octave - Channel1
	add hl, bc
	ld d, [hl]
	; update frequency
	call GetFrequency
	ld hl, Channel1Frequency - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	; ????
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 4, [hl]
	jp LoadNote
.rest
; note = rest
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 5, [hl] ; Rest
	ret
;
.readff
; $ff is reached in music data
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 1, [hl] ; in a subroutine?
	jr nz, .readcommand ; execute
	ld a, [CurChannel]
	cp a, $04 ; channels 0-3?
	jr nc, .asm_e8651
	; ????
	ld hl, $00cb
	add hl, bc
	bit 0, [hl]
	jr nz, .ok
.asm_e8651
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 5, [hl]
	call nz, RestoreVolume
	; end music
	ld a, [CurChannel]
	cp a, $04 ; channel 5?
	jr nz, .ok
	; ????
	xor a
	ld [rNR10], a ; sweep = 0
.ok
; stop playing
	; turn channel off
	ld hl, Channel1Flags - Channel1
	add hl, bc
	res 0, [hl]
	; note = rest
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 5, [hl]
	; clear music id & bank
	ld hl, Channel1MusicID - Channel1
	add hl, bc
	xor a
	ld [hli], a ; id hi
	ld [hli], a ; id lo
	ld [hli], a ; bank
	ret
; e8679

RestoreVolume: ; e8679
	; ch5 only
	ld a, [CurChannel]
	cp a, $04
	ret nz
	xor a
	ld hl, $c222
	ld [hli], a
	ld [hl], a
	ld hl, $c286
	ld [hli], a
	ld [hl], a
	ld a, [LastVolume]
	ld [Volume], a
	xor a
	ld [LastVolume], a
	ld [SFXPriority], a
	ret
; e8698

Functione8698: ; e8698
	; turn noise sampling on
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 4, [hl] ; noise sample
	; update note duration
	ld a, [CurMusicByte]
	call SetNoteDuration ; top nybble doesnt matter?
	; update intensity from next param
	call GetMusicByte
	ld hl, Channel1Intensity - Channel1
	add hl, bc
	ld [hl], a
	; update lo frequency from next param
	call GetMusicByte
	ld hl, Channel1FrequencyLo - Channel1
	add hl, bc
	ld [hl], a
	; are we on the last channel? (noise sampling)
	ld a, [CurChannel]
	and a, $03
	cp a, $03
	ret z
	; update hi frequency from next param
	call GetMusicByte
	ld hl, Channel1FrequencyHi - Channel1
	add hl, bc
	ld [hl], a
	ret
; e86c5

GetNoiseSample: ; e86c5
; load ptr to sample header in NoiseSampleAddress
	; are we on the last channel?
	ld a, [CurChannel]
	and a, $03
	cp a, $03
	; ret if not
	ret nz
	; update note duration
	ld a, [CurMusicByte]
	and a, $0f
	call SetNoteDuration
	; check current channel
	ld a, [CurChannel]
	bit 2, a ; are we in a sfx channel?
	jr nz, .sfx
	ld hl, $c262 ; Channel8Flags
	bit 0, [hl] ; is ch8 on? (noise)
	ret nz
	ld a, [MusicNoiseSampleSet]
	jr .next
.sfx
	ld a, [SFXNoiseSampleSet]
.next
	; load noise sample set id into de
	ld e, a
	ld d, $00
	; load ptr to noise sample set in hl
	ld hl, NoiseSampleSetsPointers
	add hl, de
	add hl, de
	ld a, [hli]
	ld h, [hl]
	ld l, a
	; get pitch
	ld a, [CurMusicByte]
	swap a
	; non-rest note?
	and a, $0f
	ret z
	; use 'pitch' to seek noise sample set
	ld e, a
	ld d, $00
	add hl, de
	add hl, de
	; load sample pointer into NoiseSampleAddress
	ld a, [hli]
	ld [NoiseSampleAddressLo], a
	ld a, [hl]
	ld [NoiseSampleAddressHi], a
	; clear ????
	xor a
	ld [$c2a2], a
	ret
; e870f

ParseCommand ; e870f
	; reload command
	ld a, [CurMusicByte]
	; get command #
	sub a, $d0 ; first command
	ld e, a
	ld d, $00
	; 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]
; e8720

MusicCommands: ; e8720
; pointer to each command in order
	; octaves
	dw MusicD0 ; octave 8
	dw MusicD0 ; octave 7
	dw MusicD0 ; octave 6
	dw MusicD0 ; octave 5
	dw MusicD0 ; octave 4
	dw MusicD0 ; octave 3
	dw MusicD0 ; octave 2
	dw MusicD0 ; octave 1
	dw MusicD8 ; note length + intensity
	dw MusicD9 ; set starting octave
	dw MusicDA ; tempo
	dw MusicDB ; duty cycle
	dw MusicDC ; intensity
	dw MusicDD ; update sound status
	dw MusicDE ; ???? + duty cycle
	dw MusicDF ;
	dw MusicE0 ;
	dw MusicE1 ; vibrato
	dw MusicE2 ;
	dw MusicE3 ; music noise sampling
	dw MusicE4 ; force panning
	dw MusicE5 ; volume
	dw MusicE6 ; tune
	dw MusicE7 ;
	dw MusicE8 ;
	dw MusicE9 ; global tempo
	dw MusicEA ; restart current channel from header
	dw MusicEB ; new song
	dw MusicEC ; sfx priority on
	dw MusicED ; sfx priority off
	dw MusicEE ;
	dw MusicEF ; stereo panning
	dw MusicF0 ; sfx noise sampling
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF1 ; nothing
	dw MusicF9 ;
	dw MusicFA ;
	dw MusicFB ;
	dw MusicFC ; jump
	dw MusicFD ; loop
	dw MusicFE ; call
	dw MusicFF ; return
; e8780

MusicF1: ; e8780
	ret
; e8781

MusicFF: ; e8781
; called when $ff is encountered w/ subroutine flag set
; end music stream
; return to caller of the subroutine
	; reset subroutine flag
	ld hl, Channel1Flags - Channel1
	add hl, bc
	res 1, [hl]
	; copy LastMusicAddress to MusicAddress
	ld hl, Channel1LastMusicAddress - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ret
; e8796

MusicFE: ; e8796
; 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, Channel1MusicAddress - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	ld hl, Channel1LastMusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	; load pointer into MusicAddress
	pop de
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	; set subroutine flag
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 1, [hl]
	ret
; e87bc

MusicFC: ; e87bc
; jump
; parameters: ll hh ; pointer
	; get pointer from next 2 bytes
	call GetMusicByte
	ld e, a
	call GetMusicByte
	ld d, a
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ret
; e87cc

MusicFD: ; e87cc
; loops xx - 1 times
; 	00: infinite
; params: 3
;	xx ll hh
;		xx : loop count
;   	ll hh : pointer

	; get loop count
	call GetMusicByte
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 2, [hl] ; has the loop been initiated?
	jr nz, .checkloop
	and a ; loop counter 0 = infinite
	jr z, .loop
	; initiate loop
	dec a
	set 2, [hl] ; set loop flag
	ld hl, Channel1LoopCount - Channel1
	add hl, bc
	ld [hl], a ; store loop counter
.checkloop
	ld hl, Channel1LoopCount - Channel1
	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, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ret

.endloop
	; reset loop flag
	ld hl, Channel1Flags - Channel1
	add hl, bc
	res 2, [hl]
	; skip to next command
	ld hl, Channel1MusicAddress - Channel1
	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
; e880e

MusicFA: ; e880e
; set condition for a jump
; used with FB
; params: 1
;	xx ; condition

	; set condition
	call GetMusicByte
	ld hl, Channel1Condition - Channel1
	add hl, bc
	ld [hl], a
	ret
; e8817

MusicFB: ; e8817
; 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, Channel1Condition - Channel1
	add hl, bc
	cp [hl]
	jr z, .jump
; skip to next command
	; get address
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; skip pointer
	inc de
	inc de
	; update address
	ld [hl], d
	dec hl
	ld [hl], e
	ret
.jump
; jump to the new address
	; get pointer
	call GetMusicByte
	ld e, a
	call GetMusicByte
	ld d, a
	; update pointer in MusicAddress
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ret
; e883e

MusicEE; e883e
; 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
	ld a, [CurChannel]
	and a, $03 ; ch0-3
	ld e, a
	ld d, $00
	; hl = $c2b8 + channel id
	ld hl, $c2b8
	add hl, de
	; if set, jump
	ld a, [hl]
	and a
	jr nz, .jump
; skip to next command
	; get address
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; skip pointer
	inc de
	inc de
	; update address
	ld [hl], d
	dec hl
	ld [hl], e
	ret
.jump
	; reset jump flag
	ld [hl], $00
	; de = pointer
	call GetMusicByte
	ld e, a
	call GetMusicByte
	ld d, a
	; update address
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	ret
; e886d

MusicF9: ; e886d
; sets some flag
; seems to be unused
; params: 0
	ld a, $01
	ld [$c2b5], a
	ret
; e8873

MusicE2: ; e8873
; seems to have been dummied out
; params: 1
	call GetMusicByte
	ld hl, $002c
	add hl, bc
	ld [hl], a
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 3, [hl]
	ret
; e8882

MusicE1: ; e8882
; vibrato
; params: 2
;	1: [xx]
	; delay in frames
;	2: [yz]
	; y: extent
	; z: rate (# frames per cycle)

	; set vibrato flag?
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 0, [hl]
	; start at lower frequency (extent is positive)
	ld hl, Channel1Flags3 - Channel1
	add hl, bc
	res 0, [hl]
	; get delay
	call GetMusicByte
; update delay
	ld hl, Channel1VibratoDelay - Channel1
	add hl, bc
	ld [hl], a
; update delay count
	ld hl, Channel1VibratoDelayCount - Channel1
	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, Channel1VibratoExtent - Channel1
	add hl, bc
	ld d, a
	; get top nybble
	and a, $f0
	swap a
	srl a ; halve
	ld e, a
	adc a, $00 ; round up
	swap a
	or e
	ld [hl], a
; update rate
	ld hl, Channel1VibratoRate - Channel1
	add hl, bc
	; get bottom nybble
	ld a, d
	and a, $0f
	ld d, a
	swap a
	or d
	ld [hl], a
	ret
; e88bd

MusicE0: ; e88bd
; ????
; params: 2
	call GetMusicByte
	ld [$c297], a
	call GetMusicByte
	ld d, a
	and a, $0f
	ld e, a
	ld a, d
	swap a
	and a, $0f
	ld d, a
	call GetFrequency
	ld hl, $0021
	add hl, bc
	ld [hl], e
	ld hl, $0022
	add hl, bc
	ld [hl], d
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 1, [hl]
	ret
; e88e4

MusicE6: ; e88e4
; tone
; params: 2
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 4, [hl]
	ld hl, $0028
	add hl, bc
	call GetMusicByte
	ld [hld], a
	call GetMusicByte
	ld [hl], a
	ret
; e88f7

MusicE7: ; e88f7
; shrug
; params: 1
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 6, [hl]
	call GetMusicByte
	ld hl, $0029
	add hl, bc
	ld [hl], a
	ret
; e8906

MusicDE: ; e8906
; ???? + duty cycle
; params: 1
	;
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 2, [hl] ; duty cycle
	;
	call GetMusicByte
	rrca
	rrca
	ld hl, $001c
	add hl, bc
	ld [hl], a
	; update duty cycle
	and a, $c0 ; only uses top 2 bits
	ld hl, Channel1DutyCycle - Channel1
	add hl, bc
	ld [hl], a
	ret
; e891e

MusicE8: ; e891e
; shrug
; params: 1
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 5, [hl]
	call GetMusicByte
	ld hl, $002a
	add hl, bc
	ld [hl], a
	ret
; e892d

MusicDF: ; e892d
; toggle something
; params: none
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 3, [hl]
	jr z, .on
	res 3, [hl]
	ret
.on
	set 3, [hl]
	ret
; e893b

MusicE3: ; e893b
; 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, Channel1Flags - Channel1
	add hl, bc
	bit 4, [hl]
	jr z, .on
	; turn noise sampling off
	res 4, [hl]
	ret
.on
	; turn noise sampling on
	set 4, [hl]
	call GetMusicByte
	ld [MusicNoiseSampleSet], a
	ret
; e894f

MusicF0: ; e894f
; toggle sfx noise sampling
; params:
;	on: 1
; 	off: 0
	; check if noise sampling is on
	ld hl, Channel1Flags - Channel1
	add hl, bc
	bit 4, [hl]
	jr z, .on
	; turn noise sampling off
	res 4, [hl]
	ret
.on
	; turn noise sampling on
	set 4, [hl]
	call GetMusicByte
	ld [SFXNoiseSampleSet], a
	ret
; e8963

MusicD8: ; e8963
; note length
;	# frames per 16th note
; intensity: see MusicDC
; params: 2
	; note length
	call GetMusicByte
	ld hl, $002d
	add hl, bc
	ld [hl], a
	ld a, [CurChannel]
	and a, $03
	cp a, $03
	ret z
	; intensity
	call MusicDC
	ret
; e8977

MusicDD: ; e8977
; update sound status
; params: 1
	call GetMusicByte
	ld [SoundInput], a
	ld hl, Channel1NoteFlags - Channel1
	add hl, bc
	set 3, [hl]
	ret
; e8984

MusicDB: ; e8984
; duty cycle
; params: 1
	call GetMusicByte
	rrca
	rrca
	and a, $c0
	ld hl, Channel1DutyCycle - Channel1
	add hl, bc
	ld [hl], a
	ret
; e8991

MusicDC: ; e8991
; intensity
; params: 1
;	hi: pressure
;   lo: velocity
	call GetMusicByte
	ld hl, Channel1Intensity - Channel1
	add hl, bc
	ld [hl], a
	ret
; e899a

MusicDA: ; e899a
; global tempo
; params: 2
;	de: tempo
	call GetMusicByte
	ld d, a
	call GetMusicByte
	ld e, a
	call SetGlobalTempo
	ret
; e89a6

MusicD0: ; e89a6
; used by d0-d7
; set octave based on lo nybble of the command
	ld hl, Channel1Octave - Channel1
	add hl, bc
	ld a, [CurMusicByte] ; get current command
	and a, $07
	ld [hl], a
	ret
; e89b1

MusicD9: ; e89b1
; set starting octave
; this forces all notes up by the starting octave
; params: 1
	call GetMusicByte
	ld hl, Channel1StartingOctave - Channel1
	add hl, bc
	ld [hl], a
	ret
; e89ba

MusicEF: ; e89ba
; stereo panning
; params: 1
	; stereo on?
	ld a, [Options]
	bit 5, a ; stereo
	jr nz, MusicE4
	; skip param
	call GetMusicByte
	ret
; e89c5

MusicE4: ; e89c5
; force panning
; params: 1
	call SetLRTracks
	call GetMusicByte
	ld hl, Channel1Tracks - Channel1
	add hl, bc
	and [hl]
	ld [hl], a
	ret
; e89d2

MusicE5: ; e89d2
; set volume
; params: 1
;	see Volume
	; read param even if it's not used
	call GetMusicByte
	; is the song fading?
	ld a, [MusicFade]
	and a
	ret nz
	; reload param
	ld a, [CurMusicByte]
	; set volume
	ld [Volume], a
	ret
; e89e1

MusicE9: ; e89e1
; set global tempo to current channel tempo +- param
; params: 1 signed
	call GetMusicByte
	ld e, a
	; check sign
	cp a, $80
	jr nc, .negative
;positive
	ld d, $00
	jr .ok
.negative
	ld d, $ff
.ok
	ld hl, Channel1Tempo - Channel1
	add hl, bc
	ld a, [hli]
	ld h, [hl]
	ld l, a
	add hl, de
	ld e, l
	ld d, h
	call SetGlobalTempo
	ret
; e89fd

MusicEC: ; e89fd
; turn sfx priority on
; params: none
	ld a, $01
	ld [SFXPriority], a
	ret
; e8a03

MusicED: ; e8a03
; turn sfx priority off
; params: none
	xor a
	ld [SFXPriority], a
	ret
; e8a08

MusicEA: ; e8a08
; 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, Channel1MusicID - Channel1
	add hl, bc
	ld a, [hli]
	ld [MusicIDLo], a
	ld a, [hl]
	ld [MusicIDHi], a
	; update music bank
	ld hl, Channel1MusicBank - Channel1
	add hl, bc
	ld a, [hl]
	ld [MusicBank], 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
; e8a30

MusicEB: ; e8a30
; new song
; params: 2
;	de: song id
	call GetMusicByte
	ld e, a
	call GetMusicByte
	ld d, a
	push bc
	call LoadMusic
	pop bc
	ret
; e8a3e

GetMusicByte: ; e8a3e
; 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, Channel1MusicAddress - Channel1
	add hl, bc
	ld a, [hli]
	ld e, a
	ld d, [hl]
	; load bank into a
	ld hl, Channel1MusicBank - Channel1
	add hl, bc
	ld a, [hl]
	; get byte
	call LoadMusicByte ; load data into CurMusicByte
	inc de ; advance to next byte for next time this is called
	; update channeldata address
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	ld a, e
	ld [hli], a
	ld [hl], d
	; cleanup
	pop de
	pop hl
	; store channeldata in a
	ld a, [CurMusicByte]
	ret
; e8a5d

GetFrequency: ; e8a5d
; generate frequency
; input:
; 	d: octave
;	e: pitch
; output:
; 	de: frequency

; get octave
	; get starting octave
	ld hl, Channel1StartingOctave - Channel1
	add hl, bc
	ld a, [hl]
	swap a ; hi nybble
	and a, $0f
	; add current octave
	add d
	push af ; we'll use this later
	; get starting octave
	ld hl, Channel1StartingOctave - Channel1
	add hl, bc
	ld a, [hl]
	and a, $0f ; lo nybble
	;
	ld l, a ; ok
	ld d, $00
	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
.loop
	; [7 - octave] loops
	cp a, $07
	jr nc, .ok
	; sra de
	sra d
	rr e
	inc a
	jr .loop
.ok
	ld a, d
	and a, $07 ; top 3 bits for frequency (11 total)
	ld d, a
	ret
; e8a8d

SetNoteDuration: ; e8a8d
; input: a = note duration in 16ths
	; store delay units in de
	inc a
	ld e, a
	ld d, $00
	; store NoteLength in a
	ld hl, Channel1NoteLength - Channel1
	add hl, bc
	ld a, [hl]
	; multiply NoteLength by delay units
	ld l, $00 ; just multiply
	call MultiplySimple
	ld a, l ; % $100
	; store Tempo in de
	ld hl, Channel1Tempo - Channel1
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	; add ???? to the next result
	ld hl, $0016
	add hl, bc
	ld l, [hl]
	; multiply Tempo by last result (NoteLength * delay % $100)
	call MultiplySimple
	; copy result to de
	ld e, l
	ld d, h
	; store result in ????
	ld hl, $0016
	add hl, bc
	ld [hl], e
	; store result in NoteDuration
	ld hl, Channel1NoteDuration - Channel1
	add hl, bc
	ld [hl], d
	ret
; e8ab8

MultiplySimple: ; e8ab8
; multiplies a and de
; adds the result to l
; stores the result in hl
	ld h, $00
.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
; e8ac7

SetGlobalTempo: ; e8ac7
	push bc ; save current channel
	; are we dealing with music or sfx?
	ld a, [CurChannel]
	cp a, $04
	jr nc, .sfxchannels
	ld bc, Channel1
	call SetTempo
	ld bc, Channel2
	call SetTempo
	ld bc, Channel3
	call SetTempo
	ld bc, Channel4
	call SetTempo
	jr .end
.sfxchannels
	ld bc, Channel5
	call SetTempo
	ld bc, Channel6
	call SetTempo
	ld bc, Channel7
	call SetTempo
	ld bc, Channel8
	call SetTempo
.end
	pop bc ; restore current channel
	ret
; e8b03

SetTempo: ; e8b03
; input:
; 	de: note length
	; update Tempo
	ld hl, Channel1Tempo - Channel1
	add hl, bc
	ld [hl], e
	inc hl
	ld [hl], d
	; clear ????
	xor a
	ld hl, $0016
	add hl, bc
	ld [hl], a
	ret
; e8b11

StartChannel: ; e8b11
	call SetLRTracks
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 0, [hl] ; turn channel on
	ret
; e8b1b

SetLRTracks: ; e8b1b
; set tracks for a the current channel to default
; seems to be redundant since this is overwritten by stereo data later
	push de
	; store current channel in de
	ld a, [CurChannel]
	and a, $03
	ld e, a
	ld d, $00
	; 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, Channel1Tracks - Channel1
	add hl, bc
	ld [hl], a
	pop de
	ret
; e8b30

LoadMusic: ; e8b30
; load music
	call MusicOff
	ld hl, MusicID
	ld [hl], e ; song number
	inc hl
	ld [hl], d ; MusicIDHi (always $00)
	ld hl, Music
	add hl, de ; three
	add hl, de ; byte
	add hl, de ; pointer
	ld a, [hli]
	ld [MusicBank], a
	ld e, [hl]
	inc hl
	ld d, [hl] ; music header address
	call FarLoadMusicByte ; store first byte of music header in a
	rlca
	rlca
	and a, $03 ; get number of channels
	inc a
.loop
; start playing channels
	push af
	call LoadChannel
	call StartChannel
	pop af
	dec a
	jr nz, .loop
	xor a
	ld [$c2b5], a
	ld [$c2b8], a
	ld [$c2b9], a
	ld [$c2ba], a
	ld [$c2bb], a
	ld [NoiseSampleAddressLo], a
	ld [NoiseSampleAddressHi], a
	ld [$c2a2], a
	ld [MusicNoiseSampleSet], a
	call MusicOn
	ret
; e8b79

PlayCry: ; e8b79
; Play cry de using parameters:
;	CryPitch
;	CryEcho
;	CryLength
	
	call MusicOff
	
; Overload the music id with the cry id
	ld hl, MusicID
	ld [hl], e
	inc hl
	ld [hl], d
	
; 3-byte pointers (bank, address)
	ld hl, Cries
	add hl, de
	add hl, de
	add hl, de
	
	ld a, [hli]
	ld [MusicBank], a
	
	ld e, [hl]
	inc hl
	ld d, [hl]
	
; Read the cry's sound header
	call FarLoadMusicByte
	; Top 2 bits contain the number of channels
	rlca
	rlca
	and a, 3
	
; For each channel:
	inc a
.loop
	push af
	call LoadChannel
	
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 5, [hl]
	
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 4, [hl]
	
	ld hl, Channel1CryPitch - Channel1
	add hl, bc
	ld a, [CryPitch]
	ld [hli], a
	ld a, [CryEcho]
	ld [hl], a
	
; No tempo for channel 4
	ld a, [CurChannel]
	and a, 3
	cp 3
	jr nc, .start
	
; Tempo is effectively length
	ld hl, Channel1Tempo - Channel1
	add hl, bc
	ld a, [CryLength]
	ld [hli], a
	ld a, [CryLength+1]
	ld [hl], a
.start
	call StartChannel
	ld a, [$c2bc]
	and a
	jr z, .next
	
; Stereo only: Play cry from the monster's side.
; This only applies in-battle.
	
	ld a, [Options]
	bit 5, a ; stereo
	jr z, .next
	
; [Tracks] &= [CryTracks]
	ld hl, Channel1Tracks - Channel1
	add hl, bc
	ld a, [hl]
	ld hl, CryTracks
	and a, [hl]
	ld hl, Channel1Tracks - Channel1
	add hl, bc
	ld [hl], a
	
.next
	pop af
	dec a
	jr nz, .loop
	
	
; Cries play at max volume, so we save the current volume for later.
	ld a, [LastVolume]
	and a
	jr nz, .end
	
	ld a, [Volume]
	ld [LastVolume], a
	ld a, $77
	ld [Volume], a
	
.end
	ld a, 1 ; stop playing music
	ld [SFXPriority], a
	call MusicOn
	ret
; e8c04

LoadSFX: ; e8c04
; clear channels if they aren't already
	call MusicOff
	ld hl, $c1cc ; Channel5Flags
	bit 0, [hl] ; ch5 on?
	jr z, .ch6
	res 0, [hl] ; turn it off
	xor a
	ld [rNR11], a ; length/wavepattern = 0
	ld a, $08
	ld [rNR12], a ; envelope = 0
	xor a
	ld [rNR13], a ; frequency lo = 0
	ld a, $80
	ld [rNR14], a ; restart sound (freq hi = 0)
	xor a
	ld [SoundInput], a ; global sound off
	ld [rNR10], a ; sweep = 0
.ch6
	ld hl, $c1fe ; ch6 on?
	bit 0, [hl]
	jr z, .ch7
	res 0, [hl] ; turn it off
	xor a
	ld [rNR21], a ; length/wavepattern = 0
	ld a, $08
	ld [rNR22], a ; envelope = 0
	xor a
	ld [rNR23], a ; frequency lo = 0
	ld a, $80
	ld [rNR24], a ; restart sound (freq hi = 0)
.ch7
	ld hl, $c230 ; ch7 on?
	bit 0, [hl]
	jr z, .ch8
	res 0, [hl] ; turn it off
	xor a
	ld [rNR30], a ; sound mode #3 off
	ld [rNR31], a ; length/wavepattern = 0
	ld a, $08
	ld [rNR32], a ; envelope = 0
	xor a
	ld [rNR33], a ; frequency lo = 0
	ld a, $80
	ld [rNR34], a ; restart sound (freq hi = 0)
.ch8
	ld hl, $c262 ; ch8 on?
	bit 0, [hl]
	jr z, .chscleared
	res 0, [hl] ; turn it off
	xor a
	ld [rNR41], a ; length/wavepattern = 0
	ld a, $08
	ld [rNR42], a ; envelope = 0
	xor a
	ld [rNR43], a ; frequency lo = 0
	ld a, $80
	ld [rNR44], a ; restart sound (freq hi = 0)
	xor a
	ld [NoiseSampleAddressLo], a
	ld [NoiseSampleAddressHi], a
.chscleared
; start reading sfx header for # chs
	ld hl, MusicID
	ld [hl], e
	inc hl
	ld [hl], d
	ld hl, SFX
	add hl, de ; three
	add hl, de ; byte
	add hl, de ; pointers
	; get bank
	ld a, [hli]
	ld [MusicBank], a
	; get address
	ld e, [hl]
	inc hl
	ld d, [hl]
	; get # channels
	call FarLoadMusicByte
	rlca ; top 2
	rlca ; bits
	and a, $03
	inc a ; # channels -> # loops
.startchannels
	push af
	call LoadChannel ; bc = current channel
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 3, [hl]
	call StartChannel
	pop af
	dec a
	jr nz, .startchannels
	call MusicOn
	xor a
	ld [SFXPriority], a
	ret
; e8ca6


PlaySFX: ; e8ca6
; play sfx de

	call MusicOff
	
; standard procedure if stereo's off
	ld a, [Options]
	bit 5, a
	jp z, LoadSFX
	
; else, let's go ahead with this
	ld hl, MusicID
	ld [hl], e
	inc hl
	ld [hl], d
	
; get sfx ptr
	ld hl, SFX
	add hl, de
	add hl, de
	add hl, de
	
; bank
	ld a, [hli]
	ld [MusicBank], a
; address
	ld e, [hl]
	inc hl
	ld d, [hl]
	
; bit 2-3
	call FarLoadMusicByte
	rlca
	rlca
	and 3 ; ch1-4
	inc a
	
.loop
	push af
	call LoadChannel
	
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 3, [hl]
	
	push de
	; get tracks for this channel
	ld a, [CurChannel]
	and 3 ; ch1-4
	ld e, a
	ld d, $0
	call GetLRTracks
	add hl, de
	ld a, [hl]
	ld hl, $c2bc
	and [hl]
	
	ld hl, Channel1Tracks - Channel1
	add hl, bc
	ld [hl], a
	
	ld hl, $0030 ; $c131 - Channel1
	add hl, bc
	ld [hl], a
	
	ld a, [CryTracks]
	cp 2 ; ch 1-2
	jr c, .asm_e8d0c
	
; ch3-4
	ld a, [$c2be]
	
	ld hl, $002e ; $c12f - Channel1
	add hl, bc
	ld [hl], a
	
	ld hl, $002f ; $c130 - Channel1
	add hl, bc
	ld [hl], a
	
	ld hl, Channel1Flags2 - Channel1
	add hl, bc
	set 7, [hl]
	
.asm_e8d0c
	pop de
	
; turn channel on
	ld hl, Channel1Flags - Channel1
	add hl, bc
	set 0, [hl] ; on
	
; done?
	pop af
	dec a
	jr nz, .loop
	
; we're done
	call MusicOn
	ret
; e8d1b


LoadChannel: ; e8d1b
; prep channel for use
; input:
; 	de:
	; get pointer to current channel
	call FarLoadMusicByte
	inc de
	and a, $07 ; bit 0-2 (current channel)
	ld [CurChannel], a
	ld c, a
	ld b, $00
	ld hl, ChannelPointers
	add hl, bc
	add hl, bc
	ld c, [hl]
	inc hl
	ld b, [hl] ; bc = channel pointer
	ld hl, Channel1Flags - Channel1
	add hl, bc
	res 0, [hl] ; channel off
	call ChannelInit
	; load music pointer
	ld hl, Channel1MusicAddress - Channel1
	add hl, bc
	call FarLoadMusicByte
	ld [hli], a
	inc de
	call FarLoadMusicByte
	ld [hl], a
	inc de
	; load music id
	ld hl, Channel1MusicID - Channel1
	add hl, bc
	ld a, [MusicIDLo]
	ld [hli], a
	ld a, [MusicIDHi]
	ld [hl], a
	; load music bank
	ld hl, Channel1MusicBank - Channel1
	add hl, bc
	ld a, [MusicBank]
	ld [hl], a
	ret
; e8d5b

ChannelInit: ; e8d5b
; 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, Channel1MusicID - Channel1 ; start
	add hl, bc
	ld e, Channel2 - Channel1 ; channel struct length
	; clear channel
.loop
	ld [hli], a
	dec e
	jr nz, .loop
	; set tempo to default ($100)
	ld hl, Channel1Tempo - Channel1
	add hl, bc
	xor a
	ld [hli], a
	inc a
	ld [hl], a
	; set note length to default ($01) (fast)
	ld hl, Channel1NoteLength - Channel1
	add hl, bc
	ld [hl], a
	pop de
	ret
; e8d76

FarLoadMusicByte: ; e8d76
; input:
;   de = current music address
; output:
;   a = CurMusicByte
	ld a, [MusicBank]
	call LoadMusicByte
	ld a, [CurMusicByte]
	ret
; e8d80

FrequencyTable: ; e8d80
	dw $0000 ; filler
	dw $f82c
	dw $f89d
	dw $f907
	dw $f96b
	dw $f9ca
	dw $fa23
	dw $fa77
	dw $fac7
	dw $fb12
	dw $fb58
	dw $fb9b
	dw $fbda
	dw $fc16
	dw $fc4e
	dw $fc83
	dw $fcb5
	dw $fce5
	dw $fd11
	dw $fd3b
	dw $fd63
	dw $fd89
	dw $fdac
	dw $fdcd
	dw $fded
; e8db2

WaveSamples: ; e8db2
	; these are streams of 32 4-bit values used as wavepatterns
	; nothing interesting here!
	db $02, $46, $8a, $ce, $ff, $fe, $ed, $dc, $cb, $a9, $87, $65, $44, $33, $22, $11
	db $02, $46, $8a, $ce, $ef, $ff, $fe, $ee, $dd, $cb, $a9, $87, $65, $43, $22, $11
	db $13, $69, $bd, $ee, $ee, $ff, $ff, $ed, $de, $ff, $ff, $ee, $ee, $db, $96, $31
	db $02, $46, $8a, $cd, $ef, $fe, $de, $ff, $ee, $dc, $ba, $98, $76, $54, $32, $10
	db $01, $23, $45, $67, $8a, $cd, $ee, $f7, $7f, $ee, $dc, $a8, $76, $54, $32, $10
	db $00, $11, $22, $33, $44, $33, $22, $11, $ff, $ee, $cc, $aa, $88, $aa, $cc, $ee
	db $02, $46, $8a, $ce, $cb, $a9, $87, $65, $ff, $fe, $ed, $dc, $44, $33, $22, $11
	db $c0, $a9, $87, $f5, $ff, $fe, $ed, $dc, $44, $33, $22, $f1, $02, $46, $8a, $ce
	db $44, $33, $22, $1f, $00, $46, $8a, $ce, $f8, $fe, $ed, $dc, $cb, $a9, $87, $65
	db $11, $00, $00, $08, $00, $13, $57, $9a, $b4, $ba, $a9, $98, $87, $65, $43, $21
; e8e52

NoiseSampleSetsPointers: ; e8e52
	dw NoiseSampleSets0
	dw NoiseSampleSets1
	dw NoiseSampleSets2
	dw NoiseSampleSets3
	dw NoiseSampleSets4
	dw NoiseSampleSets5
; e8e5e

NoiseSampleSets:
NoiseSampleSets0: ; e8e5e
	dw NoiseSampleSet00 ; rest
	dw NoiseSampleSet01 ; c
	dw NoiseSampleSet02 ; c#
	dw NoiseSampleSet03 ; d
	dw NoiseSampleSet04 ; d#
	dw NoiseSampleSet05 ; e
	dw NoiseSampleSet06 ; f
	dw NoiseSampleSet07 ; f#
	dw NoiseSampleSet08 ; g
	dw NoiseSampleSet09 ; g#
	dw NoiseSampleSet10 ; a
	dw NoiseSampleSet11 ; a#
	dw NoiseSampleSet12 ; b
NoiseSampleSets1: ; e8e78
	dw NoiseSampleSet00
	dw NoiseSampleSet08
	dw NoiseSampleSet09
	dw NoiseSampleSet10
	dw NoiseSampleSet11
	dw NoiseSampleSet12
	dw NoiseSampleSet13
	dw NoiseSampleSet14
	dw NoiseSampleSet15
	dw NoiseSampleSet16
	dw NoiseSampleSet17
	dw NoiseSampleSet18
	dw NoiseSampleSet19
NoiseSampleSets2: ; e8e92
	dw NoiseSampleSet00
	dw NoiseSampleSet01
	dw NoiseSampleSet17
	dw NoiseSampleSet18
	dw NoiseSampleSet19
	dw NoiseSampleSet05
	dw NoiseSampleSet06
	dw NoiseSampleSet07
	dw NoiseSampleSet08
	dw NoiseSampleSet09
	dw NoiseSampleSet10
	dw NoiseSampleSet11
	dw NoiseSampleSet12
NoiseSampleSets3: ; e8eac
	dw NoiseSampleSet21
	dw NoiseSampleSet22
	dw NoiseSampleSet23
	dw NoiseSampleSet24
	dw NoiseSampleSet25
	dw NoiseSampleSet26
	dw NoiseSampleSet20
	dw NoiseSampleSet27
	dw NoiseSampleSet28
	dw NoiseSampleSet29
	dw NoiseSampleSet21
	dw NoiseSampleSet37
	dw NoiseSampleSet34
NoiseSampleSets4: ; e8ec6
	dw NoiseSampleSet21
	dw NoiseSampleSet20
	dw NoiseSampleSet23
	dw NoiseSampleSet24
	dw NoiseSampleSet25
	dw NoiseSampleSet33
	dw NoiseSampleSet26
	dw NoiseSampleSet35
	dw NoiseSampleSet31
	dw NoiseSampleSet32
	dw NoiseSampleSet36
	dw NoiseSampleSet37
	dw NoiseSampleSet30
NoiseSampleSets5: ; e8ee0
	dw NoiseSampleSet00
	dw NoiseSampleSet17
	dw NoiseSampleSet18
	dw NoiseSampleSet19
	dw NoiseSampleSet27
	dw NoiseSampleSet28
	dw NoiseSampleSet29
	dw NoiseSampleSet05
	dw NoiseSampleSet06
	dw NoiseSampleSet30
	dw NoiseSampleSet24
	dw NoiseSampleSet23
	dw NoiseSampleSet37
; e8efa

NoiseSampleSet00: ; e8efa
; unused
	db $20, $11, $00
	db $ff ; end
; e8efe

NoiseSampleSet01: ; e8efe
	db $20, $c1, $33
	db $ff ; end
; e8f02

NoiseSampleSet02: ; e8f02
	db $20, $b1, $33
	db $ff ; end
; e8f06

NoiseSampleSet03: ; e8f06
	db $20, $a1, $33
	db $ff ; end
; e8f0a

NoiseSampleSet04: ; e8f0a
	db $20, $81, $33
	db $ff ; end
; e8f0e

NoiseSampleSet05: ; e8f0e
	db $27, $84, $37
	db $26, $84, $36
	db $25, $83, $35
	db $24, $83, $34
	db $23, $82, $33
	db $22, $81, $32
	db $ff ; end
; e8f21

NoiseSampleSet06: ; e8f21
	db $20, $51, $2a
	db $ff ; end
; e8f25

NoiseSampleSet07: ; e8f25
	db $21, $41, $2b
	db $20, $61, $2a
	db $ff ; end
; e8f2c

NoiseSampleSet08: ; e8f2c
	db $20, $81, $10
	db $ff ; end
; e8f30

NoiseSampleSet09: ; e8f30
	db $20, $82, $23
	db $ff ; end
; e8f34

NoiseSampleSet10: ; e8f34
	db $20, $82, $25
	db $ff ; end
; e8f38

NoiseSampleSet11: ; e8f38
	db $20, $82, $26
	db $ff ; end
; e8f3c

NoiseSampleSet12: ; e8f3c
	db $20, $a1, $10
	db $ff ; end
; e8f40

NoiseSampleSet13: ; e8f40
	db $20, $a2, $11
	db $ff ; end
; e8f44

NoiseSampleSet14: ; e8f44
	db $20, $a2, $50
	db $ff ; end
; e8f48

NoiseSampleSet15: ; e8f48
	db $20, $a1, $18
	db $20, $31, $33
	db $ff ; end
; e8f4f

NoiseSampleSet16: ; e8f4f
	db $22, $91, $28
	db $20, $71, $18
	db $ff ; end
; e8f56

NoiseSampleSet17: ; e8f56
	db $20, $91, $22
	db $ff ; end
; e8f5a

NoiseSampleSet18: ; e8f5a
	db $20, $71, $22
	db $ff ; end
; e8f5e

NoiseSampleSet19: ; e8f5e
	db $20, $61, $22
	db $ff ; end
; e8f62

NoiseSampleSet20: ; e8f62
	db $20, $11, $11
	db $ff ; end
; e8f66

NoiseSampleSet21: ; e8f66
	db $ff
; e8f67

NoiseSampleSet22: ; e8f67
	db $20, $91, $33
	db $ff ; end
; e8f6b

NoiseSampleSet23: ; e8f6b
	db $20, $51, $32
	db $ff ; end
; e8f6f

NoiseSampleSet24: ; e8f6f
	db $20, $81, $31
	db $ff ; end
; e8f73

NoiseSampleSet25: ; e8f73
	db $20, $88, $6b
	db $20, $71, $00
	db $ff ; end
; e8f7a

NoiseSampleSet26: ; e8f7a
	db $30, $91, $18
	db $ff ; end
; e8f7e

NoiseSampleSet27: ; e8f7e
	db $27, $92, $10
	db $ff ; end
; e8f82

NoiseSampleSet28: ; e8f82
	db $33, $91, $00
	db $33, $11, $00
	db $ff ; end
; e8f89

NoiseSampleSet29: ; e8f89
	db $33, $91, $11
	db $33, $11, $00
	db $ff ; end
; e8f90

NoiseSampleSet30: ; e8f90
	db $33, $88, $15
	db $20, $65, $12
	db $ff ; end
; e8f97

NoiseSampleSet31: ; e8f97
	db $33, $51, $21
	db $33, $11, $11
	db $ff ; end
; e8f9e

NoiseSampleSet32: ; e8f9e
	db $33, $51, $50
	db $33, $11, $11
	db $ff ; end
; e8fa5

NoiseSampleSet33: ; e8fa5
	db $20, $a1, $31
	db $ff ; end
; e8fa9

NoiseSampleSet34: ; e8fa9
	db $20, $84, $12
	db $ff ; end
; e8fad

NoiseSampleSet35: ; e8fad
	db $33, $81, $00
	db $33, $11, $00
	db $ff ; end
; e8fb4

NoiseSampleSet36: ; e8fb4
	db $33, $81, $21
	db $33, $11, $11
	db $ff ; end
; e8fbb

NoiseSampleSet37: ; e8fbb
	db $20, $a8, $6b
	db $20, $71, $00
	db $ff ; end
; e8fc2

GetLRTracks: ; e8fc2
; gets the default sound l/r channels
; stores mono/stereo table in hl
	ld a, [Options]
	bit 5, a ; stereo
	; made redundant, could have had a purpose in gold
	jr nz, .stereo
	ld hl, MonoTracks
	ret
.stereo
	ld hl, StereoTracks
	ret
; e8fd1

MonoTracks: ; e8fd1
; bit corresponds to track #
; hi: left channel
; lo: right channel
	db $11, $22, $44, $88
; e8fd5

StereoTracks: ; e8fd5
; made redundant
; seems to be modified on a per-song basis
	db $11, $22, $44, $88
; e8fd9

ChannelPointers: ; e8fd9
; music channels
	dw Channel1
	dw Channel2
	dw Channel3
	dw Channel4
; sfx channels
	dw Channel5
	dw Channel6
	dw Channel7
	dw Channel8
; e8fe9

ClearChannels: ; e8fe9
; runs ClearChannel for all 4 channels
; doesn't seem to be used, but functionally identical to SoundRestart
	ld hl, rNR50
	xor a
	ld [hli], a
	ld [hli], a
	ld a, $80
	ld [hli], a
	ld hl, rNR10
	ld e, $04
.loop
	call ClearChannel
	dec e
	jr nz, .loop
	ret
; e8ffe

ClearChannel: ; e8ffe
; input: hl = beginning hw sound register (rNR10, $ff15, rNR30, $ff1f)
; output: 00 00 80 00 80

;   sound channel   1      2      3      4
	xor a
	ld [hli], a ; rNR10, $ff15, rNR30, $ff1f ; sweep = 0

	ld [hli], a ; rNR11, rNR21, rNR31, rNR41 ; length/wavepattern = 0
	ld a, $08
	ld [hli], a ; rNR12, rNR22, rNR32, rNR42 ; envelope = 0
	xor a
	ld [hli], a ; rNR13, rNR23, rNR33, rNR43 ; frequency lo = 0
	ld a, $80
	ld [hli], a ; rNR14, rNR24, rNR34, rNR44 ; restart sound (freq hi = 0)
	ret
; e900a