Punkline
Dr. Frankenstack
- Joined
- May 15, 2015
- Messages
- 423
This code introduces new menu items to the sound options menu for the purpose of configuring voice SFX volume settings.
These options can be used to adjust character voice volume, as well as a new RNG-based silence mechanic that applies to all character voices. The RNG may optionally be excluded from smash and or special attacks.
All settings are saved to the memory card.
To install the DOL Mod, use the latest version of Melee Code Manager.
Voice SFX Options - 1.1:
Uses padding in trophies 264, 265, and some of 266 on memory card.
Update 1.1: -- added a small default options data variable that can be used to set defaults when no memory card is used
From the volume section of the sound options menu, 5 new menu items have been added by crudely modifying the selection index logic and the text description pointer in the main menu GObj.
The VOICES label can be selected to play a hit sound effect alongside a voice sound effect. A pokemon voice is played in place of a character voice because they are available in the universal sound bank -- but this code does not affect pokemon voices.
The sound/music balance measurement graphic has been used to create a makeshift slider for voice parameters; using a ~ to represent silence chance, and a | to represent volume amount:
They may each be selected using and , and adjusted using and . The description will show you the value of each slider as you adjust it. You can also press A to test the voice sound. Setting the Silence chance to 100% or the Volume amount to 0% will allow for voices to be completely silenced.
The green and red symbols next to the voice test label can be selected to toggle whether the voice silence chance will be applied to SMASH and SPECIAL attacks, respectively. When selected, the symbols will turn blue:
---
Stormghetti -- you should be able to use this to randomize all character voices without stopping regular sound effects or voices that come from smash/special attacks. This should affect both hardcoded voices and subaction event voices used in attacks, as requested. Let me know if there's anything else you want to be added to this.
These options can be used to adjust character voice volume, as well as a new RNG-based silence mechanic that applies to all character voices. The RNG may optionally be excluded from smash and or special attacks.
All settings are saved to the memory card.
To install the DOL Mod, use the latest version of Melee Code Manager.
Voice SFX Options - 1.1:
Uses padding in trophies 264, 265, and some of 266 on memory card.
Update 1.1: -- added a small default options data variable that can be used to set defaults when no memory card is used
Code:
-==-
Voice SFX Options - 1.1
From the Sound Options menu:
- Control the volume of voices as a subset of general SFX
- Set a % chance for RNG voice silence
- Optionally exclude smash/special voices from RNG silence chance
Changes will be saved to the memory card.
You can change initial defaults from <Voice_SFX_Default_Options>.
[Punkline]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
<Voice_SFX_Default_Options> ALL
0000 0000 # <- modify this to change non-initialized defaults
# 2000 0000 = bool - allow for chance of silent smash attacks
# 1000 0000 = bool - allow for chance of silent special attacks
# 0800 0000 = bool - enable silence chance
# 0700 0000 = 3-bit - silence chance, in increments of 1/8
# 0000 2000 = bool - enable volume decrease
# 0000 1F00 = 5-bit - volume, in increments of 1/32
NTSC 1.02 --- 0x802492D4 ---- 9421FF00 -> 9421FE00
------------- 0x80249A10 ---- 38210100 -> 38210200
------------- 0x8024934C ---- 7C800379 -> Branch
7C800379 812D8840 4082020C 80CDBD80 7C000026 90C10130 897E0002 90010124 2C8B0001 7060001F 7C007120 2D0B0007 895E0003 4EC10B82 819E0004 4D082A02 A0091EE8 70080800 81091EE4 40A20018 60000800
lis r8, <<Voice_SFX_Default_Options>>@h
ori r8, r8, <<Voice_SFX_Default_Options>>@l
81080000 B0091EE8 40B6018C 70600020 4F1DE382 4EFFF382 4F182A02 4F57C382 4F5A2A02 4C42B102 4182019C 40B70084 89491CC4 40BF0034 41840158 40A60014 39600008 550A477E 4F5AD042 4800004C 4088000C 4CA52982 48000138 396BFFFF 39400001 48000034 40A40014 39600006 4F5AD042 39400001 48000020 40A90010 39600000 4F5AD042 48000010 41860108 396B0001 550A477E 2C8B0001 2D0B0007 4D082A02 40AA00F0 550AC6FE 480000E8 408500E4 40AA000C 4FBDE842 4F9CE042 40B8009C 38A02000 418A0008 3CA00800 40BD0008 394AFFFF 40BC0018 41880010 7D002839 7D082B78 41820008 394A0001 2C0A0000 40A00018 39400000 4F5AD182 4188000C 7CA528F8 7D082838 40A80018 2C0A0002 40A1000C 39400002 4F5AD182 48000074 40AA001C 2C0A001F 40A1000C 3940001F 4F5AD182 514844EE 48000058 40A90054 2C0A0007 40A1000C 39400007 4F5AD182 5148C14E 4800003C 40AA0008 4F39C842 40A90008 4F39C842 40A80024 4F484382 2C0A0001 40A00008 6D082000 40A10008 6D081000 4082000C 4F221382 4F5AD182 7C000026 9101012C 997E0002 90010128 91091EE4 80010124 995E0003 40850014
b 0x802499f8
88091CC4 981E0003 48000008 7C0FF120 00000000
------------- 0x802499F8 ---- 80010104 -> b <voice_sound_options>
<voice_sound_options> NTSC 1.02
80610130 800DBD80 7C030000 40820250
lis r0, <<voice_sound_options>>@h
ori r31, r0, <<voice_sound_options>>@l
83CDC188 80010128 83DE001C 7C0FF120 83DE002C 819E0004 897E0002 8121012C 895E0003 83AC005C 38DF0264 7C1D3000 4C42B102 41A20210 90CC005C 5523C6FE 55259FFE 5524477E 55202FFE 7C632A14 7C840214 54631838 54842834 7D0329D6 7CE401D6 1C6800EF 1C8700EF 5465CFFE 5480CFFE 5463C23E 5484C23E 7C632A14 7C840214 38A0014C 7C632850 3884005D B07F028F B09F029F 7D280120 807F0428 809F0438 40A20008 807F0440 40A30008 809F0440 907F026A 909F027A 807F0430 809F0448 80BF0440 907F02AA 909F028A 90BF029A 41A50010 7C06E800 40820114 48000164 40A80078 2D8A0001 3BBF02D5 40AE0010 801F0434 901F02AA 480000F4 387F034F 418D0020 3BBF032C 809F042C 93BF0310 38BF026A 3BBF02FB 40A20034 48000014 38BF027A 809F043C 3BBF0314 40A30020 809F0444 387F03DC 90850000 907F034B 387F03EA 7CE43B78 48000048 90850000 907F034B 48000098 40AA0020 801F044C 387F039C 901F028A 38800100 7C882050 3BBF037A 4800001C 408900C8 801F0444 387F03EA 901F029A 7CE43B78 3BBF03CA 1CA40064 54A00DEE 7CA50214 54A4C23E 38000064 38A01010 7C862378 38E0000A 7C060000 2D851010 4C007202 41A00018 38A02000 7D2603D6 7D0901D6 7CA54A14 7CC83050 B4A30002 7C003BD6 2C000001 4181FFD0 3CA02000 4182FFC8 93BF02D1 41990014 409A004C 386000AE 388060FE 4800003C 386000AF
bl 0x80380580
547EE73E 5463073E 38630061 3880007F
bl <psfx>
3960007F 39800000
bl <voice_sfx_argument_mutator>
389F0450 7FC4F0AE 387E2711 7D645B78
bl <psfx>
80010204
b 0x802499fc
100E0080 00800C26 904D0700 A3FF5A20 FB0E00A0 00A00CD5 17290701 04FF5821 060E0200 01030CDD AA120700 5DFF6A20 F20E00F0 01000C19 9AB10700 5DFF6A20 F10E0060 00600CAA AAAA0700 D4FF5D10 1618201F 20182012 200C200E 201C0700 B600000E 00800080 0CAAAAAA 08000000 000700B6 0003201D 20282036 20371A20 39203220 2C202620 281A2036 20322038 20312027 20E70007 00B60003 0C26904D 201C2030 20242036 202B1A08 00000000 0700B600 030CD517 29201C20 33202820 26202C20 24202F1A 20242037 20372024 2026202E 0CAAAAAA 1A203920 32202C20 26202820 361A0800 00000020 2C202A20 31203220 3520281A 2036202C 202F2028 20312026 20281A20 26202B20 24203120 26202820 E7000700 B6000320 1F203220 2C202620 2820361A 2033202F 2024203C 1A202420 371A0CDD AA122000 20002000 21031A0C AAAAAA20 3220291A 20362032 20382031 20271A20 39203220 2F203820 30202820 E7000700 B6000320 1F203220 2C202620 2820361A 202B2024 20392028 1A20241A 0C199AB1 20002000 20002103 1A0CAAAA AA202620 2B202420 31202620 281A2032 20291A20 25202820 2C203120 2A1A2036 202C202F 20282031 203720E7 00000000 0C26904D 0C22E369 0CAAAAAA 0CFFFFFF 0CD51729 0CFF5E5E 0C199AB1 0C37D4F1 0CDDAA12 0CFFD25E 000D1014 18191E23 2A2C0000 00000000
------------- 0x80088204 ---- 88032225 -> Branch
89832073 7F8BE378
bl <voice_sfx_argument_mutator>
7D7C5B78 7FE3FB78 88032225 00000000
<voice_sfx_argument_mutator> NTSC 1.02
7C0802A6 9421FF00 90010104 BF610010 3B610080 7C7BC5AA 83ED8840 7D9C6378 83FF1EE4 57FEC6FE 57E39FFE 57FD477E 57E42FFE 7FDE1A14 7FBD2214 38A00020 7FDE2850 57DE1838 7C1E59D6 541E0DEE 7C1E0214 541EC23E 7FBD21D6 38600008
bl 0x80380580
38630001 7FE80120 41820018 2C1C0009 41A00028 2C1C000B 41A10020 38600008 41830018 2C1C0011 41A00010 2C1C0014 41A10008 38600008 7C03E800 41A10008 3BC00000 7FCBF378 7C7BC4AA BB610010 80010104 38210100 7C0803A6 4E800020
<psfx> NTSC 1.02
38A40800 38E43800 38C00000 54A5E636 54E7AF3E
b 0x8038cff4
#
Note to developers: this code monster is actually like 20 little experiments wrapped in a trench coat. The ASM is a gilded mess, and there’s a good chance I’ll end up rewriting it from scratch if I ever decide to modify it in the future:
Code:
-==-
!
ASM - Voice SFX Options - 1.1
From the Sound Options menu:
- Control the volume of voices as a subset of general SFX
- Set a % chance for RNG voice silence
- Optionally exclude smash/special voices from RNG silence chance
Changes will be saved to the memory card.
You can change initial defaults from <Voice_SFX_Default_Options>.
[Punkline]
Revision ---- DOL Offset ---- Hex to Replace ---------- ASM Code -
<Voice_SFX_Default_Options> ALL
0000 0000 # <- modify this to change non-initialized defaults
# 2000 0000 = bool - allow for chance of silent smash attacks
# 1000 0000 = bool - allow for chance of silent special attacks
# 0800 0000 = bool - enable silence chance
# 0700 0000 = 3-bit - silence chance, in increments of 1/8
# 0000 2000 = bool - enable volume decrease
# 0000 1F00 = 5-bit - volume, in increments of 1/32
NTSC 1.02 --- 0x802492D4 ---- 9421FF00 -> 9421FE00
------------- 0x80249A10 ---- 38210100 -> 38210200
------------- 0x8024934C ---- 7C800379 -> Branch
# during sound options menu update
# -- we use a spaghetti logic nightmare to extend the selection index of sound options menu
# -- becomes a spaghetti logic nightmare sandwich through a 2nd injection, and use of the stack
# registers:
rMenu = 30; rText = 12; rRow = 11; rSlider = 10
rMem = 9; rData = 8; rValue = 7; rInput = 3
rSig = 6;
# bools:
bUp = 31; bDown = 30; bLeft = 29; bRight = 28
# includes dpad and dstick inputs
# accelerates when held
bConfirm = 27
# includes start and A
bPlaySound = 26; bTestVoice = 25; bNewSlider = 24; bNewRow = 23; bInput = 22
lt = 0; gt = 1; eq = 2;
# other bools
bChannel = lt+4 # row 0 < 1
bMusicBalance = eq+4 # row 1 == 1
bCustom = gt+4 # row 2+ > 1
# for comparison rRow, 1 (cr1)
bVoiceBools = lt+8 # row 6 < 7
bVoiceVolume = eq+8 # row 7 == 7
bVoiceSilence = gt+8 # row 8 > 7
# for comparison rRow, 7 (cr2)
# stack offsets:
xDefaultR3 = 0x120; xDefaultCR = 0x124; xSavedCR = 0x128; xData = 0x12C; xSig = 0x130
# menu data offsets:
xRow = 0x2; xSlider = 0x3; xText = 0x4
# r13 offsets:
xMem = -0x77C0
xVIWaitForRetraceCounter = -0x4280
# from rMem:
xMemMusicBalance = 0x1cc4
# we use this to reset menu selection parameter
xMemTrophy264 = 0x1cd4 + (264<<1)
# we use trophy memory blocks 264 and 265 -- create a total of 12 safe bits:
# 264 265
# 2000 0000 = bool - allow for chance of silent smash attacks
# 1000 0000 = bool - allow for chance of silent special attacks
# 0800 0000 = bool - enable silence chance
# 0700 0000 = 3-bit - silence chance, in increments of 1/8
# 0000 2000 = bool - enable volume decrease
# 0000 1F00 = 5-bit - volume, in increments of 1/32
_code_start:
or. r0, r4, r0
lwz rMem, xMem(r13)
# original context
# r3 and cr0 are crucial to _default
# so we save it in the stack in case of defaulting:
_enable_menu_exit:
bne- _return_exit
# if exiting because holding B button, then skip code and use default
# else, proceed with setup
lwz rSig, xVIWaitForRetraceCounter(r13) # basically a draw frame counter
mfcr r0
stw rSig, xSig(sp) # serves as a signature for second injection to recognize
lbz rRow, xRow(rMenu)
stw r0, xDefaultCR(sp)
# default context has been stored
cmpwi cr1, rRow, 1
andi. r0, rInput, 0x1F
mtcrf 0x07, r0
cmpwi cr2, rRow, 7
lbz rSlider, xSlider(rMenu)
crmove bInput, gt
lwz rText, xText(rMenu)
crand bVoiceBools, bVoiceBools, bCustom
# saved original context for case of default
# cr setup makes comparisons pre-emptively
# by default, there are only rows 0 and 1 for sound options (channel, music balance)
# we will be inserting 3 additional rows between 0 and 1
# -- to do this without modifying the default indices, we need to use some logic
# 0 = Channel Options
# 1 = Music Balance Options
# starting at custom row 6 seems to keep the bottom selection graphics active:
# 6 = voice silence options
# 7 = voice volume
# 8 = voice silence chance
# list order = [0, 6, 7, 8, 1]
# we want 0 -> 6 ... 8 -> 1 ... 1 -> 0
# 1 <- 0 ... 0 <- 6 ... 8 <- 1
lhz r0, xMemTrophy264+4(rMem)
andi. rData, r0, 0x0800
# load trophy 266 -- is initialization bit TRUE?
lwz rData, xMemTrophy264(rMem)
bne+ _check_input
ori r0, r0, 0x0800
lis r8, <<Voice_SFX_Default_Options>>@h
ori r8, r8, <<Voice_SFX_Default_Options>>@l
lwz rData, 0(r8)
sth r0, xMemTrophy264+4(rMem)
# if initialization is false, load default options, and flag as true
# -- else default to memorized rData
# rData holds our memory card value, taken from trophy slots 264 and 265
# (we can modify 12 bits of this value safely, and store the data in memory card)
# (modifications will not disrupt trophy collection behavior)
_check_input:
bf+ bInput, _update
# if no inputs, then skip the spaghetti
andi. r0, rInput, 0x20
cror bNewSlider, bLeft, bRight
cror bNewRow, bUp, bDown
crand bNewSlider, bNewSlider, bCustom
cror bPlaySound, bNewRow, bNewSlider
crand bPlaySound, bPlaySound, bCustom
# preparing for nested switch case
crandc eq, eq, bInput
beq- _return_default
# if pressing B button while not pressing other buttons
# -- then return to handle menu exit
_check_bNewRow:
bf+ bNewRow, _check_bNewSlider
# if not choosing new row, check if choosing new slider option
lbz rSlider, xMemMusicBalance(rMem)
# default to vanilla slider value
_check_bUp:
bf+ bUp, _check_bDown
# if pressing up...
_case_bUp_bChannel:
bt- bChannel, _update_no_sound
# 1 <- 0
# default cycle back to bottom
_case_bUp_bMusicBalance:
bf+ bMusicBalance, _case_bUp_bVoiceBools
# 8 <- 1
li rRow, 8
rlwinm rSlider, rData, 8, 0x7
crnot bPlaySound, bPlaySound
b _selected_row
# enter custom index
_case_bUp_bVoiceBools:
bf- bVoiceBools, 0xC
crclr bCustom
b _update_no_sound
# 0 <- 6
# exiting custom index
_case_bUp_else:
# 6 <-7 7 <- 8
addi rRow, rRow, -1
li rSlider, 1
b _selected_row
# else subtract from custom row index
_check_bDown:
_case_bDown_bChannel:
bf+ bChannel, _case_bDown_bVoiceSilence
# 0 -> 6
li rRow, 6
crnot bPlaySound, bPlaySound
li rSlider, 1
b _selected_row
# enter custom index
_case_bDown_bVoiceSilence:
bf+ bVoiceSilence, _case_bDown_bMusicBalance
# 8 -> 1
li rRow, 0
crnot bPlaySound, bPlaySound
b _selected_row
# exit custom index
_case_bDown_bMusicBalance:
bt- bMusicBalance, _update
# 1 -> 0
# default cycle back to top
_case_bDown_else:
# 6 -> 7 7 -> 8
addi rRow, rRow, 1
rlwinm rSlider, rData, 8, 0x7
# else add to custom row index
_selected_row:
cmpwi cr1, rRow, 1
cmpwi cr2, rRow, 7
crand bVoiceBools, bVoiceBools, bCustom
# re-compare, to measure new values
bf+ bVoiceVolume, _update
rlwinm rSlider, rData, 24, 0x1F
b _update
# final comparison for row 7 slider reset
_check_bNewSlider:
bf- bCustom, _update
bf+ bVoiceVolume, 0xC
crnot bLeft, bLeft
crnot bRight, bRight
# "volume" value is actually a measurment of volume decrease
# -- so we invert left and right logic for adjusting the value
# (the volume value is kept like this so that default 0 == vanilla volume)
bf+ bNewSlider, _check_bConfirm
li r5, 0x2000
bt- bVoiceVolume, 8
lis r5, 0x0800
# r5 = temporary mask for working with toggle bools
bf+ bLeft, 0x8
addi rSlider, rSlider, -1
bf+ bRight, _check_slider_min
bt- bVoiceBools, _incr_slider
and. r0, rData, r5
or rData, rData, r5
beq- _check_slider_min
_incr_slider:
addi rSlider, rSlider, 1
# apply incr/decr according to button(s) pressed
# if incrementing value, make sure bool is toggled before committing
# -- if bool is false, toggle it on instead of incrementing
_check_slider_min:
cmpwi rSlider, 0
bge+ _bVoiceBools_max
li rSlider, 0
crclr bPlaySound
bt- bVoiceBools, _bVoiceBools_max
not r5, r5
and rData, rData, r5
# check and cap
_bVoiceBools_max:
bf+ bVoiceBools, _bVoiceVolume_max
cmpwi rSlider, 2
ble+ 0xC
li rSlider, 2
crclr bPlaySound
b _update
_bVoiceVolume_max:
bf+ bVoiceVolume, _bVoiceSilence_max
cmpwi rSlider, 0x1F
ble+ 0xC
li rSlider, 0x1F
crclr bPlaySound
rlwimi rData, rSlider, 8, 0x1F00
b _update
_bVoiceSilence_max:
bf+ bVoiceSilence, _update
cmpwi rSlider, 0x7
ble+ 0xC
li rSlider, 0x7
crclr bPlaySound
rlwimi rData, rSlider, 24, 0x07000000
b _update
_check_bConfirm:
bf+ bVoiceVolume, 8
crnot bTestVoice, bTestVoice
bf+ bVoiceSilence, 8
crnot bTestVoice, bTestVoice
# allow for bTestVoice on bConfirb during bVoiceSilence or bVoiceVolume
bf+ bVoiceBools, _update_no_sound
# we only use the confirm button on row 6, to select bool options and test voice
# -- we only need to handle the sound logic for toggling bools
crmove bPlaySound, bVoiceBools
cmpwi cr0, rSlider, 1
bge+ 8
xoris rData, rData, 0x2000 # toggle smash bool
ble+ 8
xoris rData, rData, 0x1000 # toggle special bool
bne- _update
# if not selecting test voice option, then play sound
# else, set bTestVoice
crmove bTestVoice, eq
_update_no_sound:
crclr bPlaySound
# any return to this label will not play sound
# else, update will store the resulting bPlaySound as a variable in the stack frame
_update:
mfcr r0
stw rData, xData(sp)
stb rRow, xRow(rMenu)
stw r0, xSavedCR(sp)
stw rData, xMemTrophy264(rMem)
lwz r0, xDefaultCR(sp)
stb rSlider, xSlider(rMenu)
# save state of context for next injection, and carry on with vanilla routines
# -- unless in a custom row, in which case we skip the vanilla routines
bf- bCustom, _return_default
_return_skip:
b 0x802499f8
# skip to host epilog
_return_exit:
lbz r0, xMemMusicBalance(rMem)
stb r0, xSlider(rMenu)
b _return_default+4
_return_default:
mtcr r0
.long 0
# return to default, with default cr and r3 registers
------------- 0x802499F8 ---- 80010104 -> b <voice_sound_options>
<voice_sound_options> NTSC 1.02
# this "function" actually contains an injection and a data table
# it uses the same stack frame as the previous injection, so we can recover
# all of the stack variables we saved -- effectively picking up where we left off.
# @802499f8 -- on sound options update return
# -- we use this injection to have the final say in what the text describes
# -- included is a data region
# during sound options menu update
# registers:
rSelf = 31; rMenu = 30; rStr = 29
rText = 12; rRow = 11; rSlider = 10
rData = 9; rVolume = 8; rSilence = 7
rRoot = 6;
# bools:
bUp = 31; bDown = 30; bLeft = 29; bRight = 28
# includes dpad and dstick inputs
# accelerates when held
bConfirm = 27
# includes start and A
bPlaySound = 26; bTestVoice = 25; bNewSlider = 24; bNewRow = 23;
bInput = 22; bBoolState = 21;
lt = 0; gt = 1; eq = 2;
bSmashSilence = 2; bSpecSilence = 3
# other bools
bChannel = lt+4 # row 0 < 1
bMusicBalance = eq+4 # row 1 == 1
bCustom = gt+4 # row 2+ > 1
# for comparison rRow, 1 (cr1)
bVoiceBools = lt+8 # row 6 < 7
bVoiceVolume = eq+8 # row 7 == 7
bVoiceSilence = gt+8 # row 8 > 7
# for comparison rRow, 7 (cr2)
# stack offsets:
xDefaultR3 = 0x120; xDefaultCR = 0x124; xSavedCR = 0x128; xData = 0x12C; xSig = 0x130
# menu data offsets:
xRow = 0x2; xSlider = 0x3; xText = 0x4
# r13 offsets:
xVIWaitForRetraceCounter = -0x4280
# text object offsets:
xThisString = 0x5C
# 2000 0000 = bool - allow for chance of silent smash attacks
# 1000 0000 = bool - allow for chance of silent special attacks
# 0800 0000 = bool - enable silence chance
# 0700 0000 = 3-bit - silence chance, in increments of 1/8
# 0000 2000 = bool - enable volume decrease
# 0000 1F00 = 5-bit - volume, in increments of 1/32
_code_region:
lwz r3, xSig(sp)
lwz r0, xVIWaitForRetraceCounter(r13)
cmpw r3, r0
bne- _return
# skip if first injection has not executed this frame
lis r0, <<voice_sound_options>>@h
ori rSelf, r0, <<voice_sound_options>>@l
lwz rMenu, -0x3e78(r13)
lwz r0, xSavedCR(sp)
lwz rMenu, 0x1C(rMenu)
mtcr r0
lwz rMenu, 0x2C(rMenu)
# cr now is in same state we left it in other injection
# rMenu has been reloaded, in case r30 has been clobbered
lwz rText, xText(rMenu)
lbz rRow, xRow(rMenu)
lwz rData, xData(sp)
lbz rSlider, xSlider(rMenu)
# setting up registers
lwz rStr, xThisString(rText)
addi rRoot, rSelf, _root_string@l
cmpw rStr, rRoot
crandc eq, eq, bInput
beq+ _return
# if string is already pointing to _root_string, AND bInput is false
# then skip further updates to the string
# else, we begin updating
stw rRoot, xThisString(rText)
# update string pointer to use _root_string
# - this displays the added menu elements
# - a continuation pointer at the end allows a menu message to be appended by procedure
rlwinm r3, rData, 24, 0x1F # 0-based coef extracted from 5-bit int
rlwinm r5, rData, 19, 1 # volume bool
rlwinm r4, rData, 8, 0x07 # 0-based coef extracted from 3-bit int
rlwinm r0, rData, 5, 1 # silence bool
add r3, r3, r5
add r4, r4, r0 # change to 1-based quantization
slwi r3, r3, 3
slwi r4, r4, 5
mullw rVolume, r3, r5
mullw rSilence, r4, r0
# use multiplication of 0 or 1 to apply toggle as bottom of range
# rVolume and rSilence now hold a 9-bit coef representing a 0%...100% range
mulli r3, rVolume, XSliderRange
mulli r4, rSilence, XSliderRange # apply coef to range value
rlwinm r5, r3, 25, 1
rlwinm r0, r4, 25, 1 # extract sub-integer bit from result, and align to zero
srwi r3, r3, 8
srwi r4, r4, 8 # align integer range to zero
add r3, r3, r5
add r4, r4, r0 # add extracted sub-integer bit to account for rounding
li r5, XSliderBase + XSliderRange
sub r3, r5, r3 # r3 = max - (lerp value)
addi r4, r4, XSliderBase # r4 = min + (lerp value)
# Mantissa math experiment
# (rounded interpolation without division or logic branches, and small ints instead of floats)
sth r3, _volume_position+1(rSelf)
sth r4, _silence_position+1(rSelf)
# update displayed positions according to memory card data and resulting interpolation math
# -- volume slider value measures a DECREASE in voice volume, so it interpolates in reverse
# -- silence slider value measures an INCREASE in voice silence chance
mtcrf 0x80, rData
lwz r3, _cSmash(rSelf)
lwz r4, _cSpecial(rSelf)
bf+ bSmashSilence, 0x8 # memorycard bool 0x20000000
lwz r3, _cSilence(rSelf)
bf+ bSpecSilence, 0x8 # memorycard bool 0x10000000
lwz r4, _cSilence(rSelf)
stw r3, _smash_silence_color(rSelf)
stw r4, _special_silence_color(rSelf)
# update bool option colors
lwz r3, _cLabel(rSelf)
lwz r4, _cVolume(rSelf)
lwz r5, _cSilence(rSelf)
stw r3, _label_color(rSelf)
stw r4, _volume_color(rSelf)
stw r5, _silence_color(rSelf)
# update other colors
_handle_vanilla:
bt+ bCustom, _handle_bVoiceBools
cmpw rRoot, rStr
bne- _store_str_continuation
b _return
# if vanilla row, only update string if rStr != r12
_handle_bVoiceBools:
bf+ bVoiceBools, _handle_bVoiceVolume
cmpwi cr3, rSlider, 1
addi rStr, rSelf, _label_string@l
_bVoiceBools_testvoice:
bne+ cr3, _bVoiceBools_smash
# if testvoice...
lwz r0, _cLabelSelect(rSelf)
stw r0, _label_color(rSelf)
b _store_str_continuation
_bVoiceBools_smash:
addi r3, rSelf, _bool_IGNORE@l
bgt- cr3, _bVoiceBools_special
# if smash bool...
addi rStr, rSelf, _bool_ATTACK_string@l
lwz r4, _cSmashSelect(rSelf)
stw rStr, _bool_smash_selection+1(rSelf)
addi r5, rSelf, _smash_silence_color@l
addi rStr, rSelf, _bool_SMASH_string@l
bf+ bSmashSilence, _store_voicebool_string
b _toggle_silence_bool
# if TRUE, then toggle silence ON
# else toggle silence OFF
_bVoiceBools_special:
# if special bool...
addi r5, rSelf, _special_silence_color@l
lwz r4, _cSpecialSelect(rSelf)
addi rStr, rSelf, _bool_SPECIAL_string@l
bf+ bSpecSilence, _store_voicebool_string
# if TRUE, then toggle silence ON
# else toggle silence OFF
_toggle_silence_bool:
# when toggling bool ON...
lwz r4, _cSilenceSelect(rSelf)
addi r3, rSelf, _silence_chance_HAVE_string@l
stw r4, 0(r5)
stw r3, _bool_ATTACK_selection+1(rSelf)
addi r3, rSelf, _silence_percentage-2@l
mr r4, rSilence
b _hex_to_percent
# handle hex update if showing silence bool
_store_voicebool_string:
# update voicebool string variables
stw r4, 0(r5)
stw r3, _bool_ATTACK_selection+1(rSelf)
b _store_str_continuation
_handle_bVoiceVolume:
bf+ bVoiceVolume, _handle_bVoiceSilence
lwz r0, _cVolumeSelect(rSelf)
addi r3, rSelf, _volume_percentage-2@l
stw r0, _volume_color(rSelf)
li r4, 0x100
sub r4, r4, rVolume
addi rStr, rSelf, _volume_string@l
b _hex_to_percent
_handle_bVoiceSilence:
bf- bVoiceSilence, _return
lwz r0, _cSilenceSelect(rSelf)
addi r3, rSelf, _silence_percentage-2@l
stw r0, _silence_color(rSelf)
mr r4, rSilence
addi rStr, rSelf, _silence_chance_string@l
_hex_to_percent:
mulli r5, r4, 100
rlwinm r0, r5, 1, 0x100
add r5, r5, r0
srwi r4, r5, 8
# r3 = address of string to write dec to
# r4 = decimal number between 0 and 100
# 0x200n displays a digit, where n is a decimal number
# 0x1010 is used to redundantly set format, creating padding
li r0, 100
li r5, 0x1010
mr r6, r4
li r7, 10
# r0 = this digit threshold
# r3 = address of string to write dec to
# r5 = padding value
# r6 =
_for_each_digit:
cmpw r6, r0
cmpwi cr3, r5, 0x1010
crand lt, lt, eq+12
blt+ _store_digit
# if decimal > remaining number
# and padding value is still intact
li r5, 0x2000
divw r9, r6, r0
mullw r8, r9, r0
add r5, r5, r9
sub r6, r6, r8
# exploit integer math to create modulo operation
# overwrite padding value with a digit for store
_store_digit:
sthu r5, 0x2(r3)
divw r0, r0, r7
cmpwi r0, 1
bgt+ _for_each_digit
lis r5, 0x2000
beq+ _for_each_digit
# we don't want to pad out last digit
_store_str_continuation:
stw rStr, _root_selection+1(rSelf)
# update string pointer
_check_sound:
bt- bTestVoice, _testVoice_playSound
bf- bPlaySound, _return
li r3, 0xAE
li r4, 0x60FE
b _playSound
# if playing a regular menu sound, skip to _playSound with menu SFX ID
# else, we handle testing the voice with RNG
_testVoice_playSound:
li r3, 0xAF
bl 0x80380580 #HSD_Randi
rlwinm r30, r3, 28, 0xF
rlwinm r3, r3, 0, 0xF
# r3 = random number between 0...15 -- 16 hit SFX 0x61...0x71
# r30 = random number between 0...9 -- 10 pokemon voices 0x2711 + offset
addi r3, r3, 0x61
li r4, 0x7F
bl <psfx>
# play random hit SFX
li r11, 0x7F
li r12, 0
bl <voice_sfx_argument_mutator>
addi r4, rSelf, _pokemon_voices@l
lbzx r30, r4, r30
addi r3, r30, 0x2711
mr r4, r11
# use r12 to mutate volume of voice according to selected options
# new arguments will override the call used to make menu sounds
_playSound:
bl <psfx>
_return:
lwz r0, 0x204 (sp)
b 0x802499fc
_data_region:
# settings:
XSliderBase = 0x5D
XSliderRange = 0xEF
YSlider = -0x96
# Macros help construct a string of menu text, with some optional commands
# 00 = end of string
.macro terminate
.byte 0x00
.endm
# 03 = new line -- carriage return
.macro newline
.byte 0x03
.endm
# 07 = set XY translation
.macro position, x, y
.byte 0x07
.hword \x&0xFFFF, \y&0xFFFF
.endm
# 08 = point to continuation string
.macro pointnext, addr
.byte 0x08
.long \addr
.endm
# 0A = tracking
.macro tracking, width, height
.byte 0x0A
.hword \width&0xFFFF
.hword \height&0xFFFF
.endm
# 0C = temporary color (can reset back to default)
.macro color, rgb
.byte 0x0C
.byte \rgb>>16&255, \rgb>>8&255, \rgb&255
.endm
# 0E = scale
.macro scale, x, y
.byte 0x0E
.hword \x&0xFFFF, \y&0xFFFF
.endm
# 10 = center alignment (?)
.macro centeralign
.byte 0x10
.endm
# 14 = right alignment
.macro rightalign
.byte 0x14
.endm
# 16 = enable kerning (tracking + kerning)
.macro kern
.byte 0x16
.endm
# 17 = disable kerning (tacking only; monospace)
.macro mono
.byte 0x17
.endm
# 18 = constrain to bounding box (?)
.macro constrain
.byte 0x18
.endm
# 1A = 'space' character
.macro newword
.byte 0x1A
.endm
_root_string:
centeralign
scale 0x80, 0x80
_smash_silence_color:
color cSmash
position 0xA3, -0xA6
.hword 0x20FB # '+' smash silence indicator character
scale 0xA0, 0xA0
_special_silence_color:
color cSpecial
position 0x104, -0xA8
.hword 0x2106 # '*' special silence indicator character
scale 0x200, 0x103
_volume_color:
color cVolume
_volume_position:
position XSliderBase, YSlider
.hword 0x20F2 # '|' volume indicator
scale 0xF0, 0x100
_silence_color:
color cSilence
_silence_position:
position XSliderBase, YSlider
.hword 0x20F1 # '~' silence chance indicator
scale 0x60, 0x60
_label_color:
color cLabel
position 0xD4, -0xA3
centeralign
kern
constrain
.hword 0x201F, 0x2018, 0x2012, 0x200C, 0x200E, 0x201C
# "VOICES" label start
position 0xB6, 0
scale 0x80, 0x80
color cLabel
_root_selection:
pointnext 0
_label_string:
position 0xB6, 3
.hword 0x201d, 0x2028, 0x2036, 0x2037, 0x1a20, 0x3920, 0x3220, 0x2c20, 0x2620, 0x281a, 0x2036, 0x2032, 0x2038, 0x2031, 0x2027, 0x20e7
terminate
# "Test voice sound."
_bool_SMASH_string:
position 0xB6, 3
color cSmash
.hword 0x201c, 0x2030, 0x2024, 0x2036, 0x202b
newword
# "Smash "
_bool_smash_selection:
pointnext 0
_bool_SPECIAL_string:
position 0xB6, 3
color cSpecial
.hword 0x201c, 0x2033, 0x2028, 0x2026, 0x202c, 0x2024, 0x202f
newword
# "Special "
_bool_ATTACK_string:
.hword 0x2024, 0x2037, 0x2037, 0x2024, 0x2026, 0x202e
# "attack"
color cLabel
.hword 0x1a20, 0x3920, 0x3220, 0x2c20, 0x2620, 0x2820, 0x361a
# " voices "
_bool_ATTACK_selection:
pointnext 0
_bool_IGNORE:
.hword 0x202c, 0x202a, 0x2031, 0x2032, 0x2035, 0x2028, 0x1a20, 0x3620, 0x2c20, 0x2f20, 0x2820, 0x3120, 0x2620, 0x281a, 0x2026, 0x202b, 0x2024, 0x2031, 0x2026, 0x2028, 0x20e7
terminate
# "ignore silence chance."
_volume_string:
position 0xB6, 3
.hword 0x201f, 0x2032, 0x202c, 0x2026, 0x2028, 0x2036, 0x1a20, 0x3320, 0x2f20, 0x2420, 0x3c1a, 0x2024, 0x2037
newword
# "Voices play at "
color cVolume
_volume_percentage:
.hword 0x2000, 0x2000, 0x2000, 0x2103
newword
# "nnn% "
color cLabel
.hword 0x2032, 0x2029, 0x1a20, 0x3620, 0x3220, 0x3820, 0x3120, 0x271a, 0x2039, 0x2032, 0x202f, 0x2038, 0x2030, 0x2028, 0x20e7
# "of sound volume."
terminate
_silence_chance_string:
position 0xB6, 3
.hword 0x201f, 0x2032, 0x202c, 0x2026, 0x2028, 0x2036
newword
# "Voices "
_silence_chance_HAVE_string:
.hword 0x202b, 0x2024, 0x2039, 0x2028, 0x1a20, 0x241a
# "have a "
color cSilence
_silence_percentage:
.hword 0x2000, 0x2000, 0x2000, 0x2103
# "nnn% "
newword
color cLabel
.hword 0x2026, 0x202b, 0x2024, 0x2031, 0x2026, 0x2028, 0x1a20, 0x3220, 0x291a, 0x2025, 0x2028, 0x202c, 0x2031, 0x202a, 0x1a20, 0x3620, 0x2c20, 0x2f20, 0x2820, 0x3120, 0x3720
.byte 0xe7
# "chance of being silent."
terminate
.align 2
_color_index:
# color index symbols use c- prefix
_cSmash:
cSmash = 0x26904d; color cSmash # 0
_cSmashSelect:
cSmashSelect = 0x22e369; color cSmashSelect # 1
_cLabel:
cLabel = 0xaaaaaa; color cLabel # 2
_cLabelSelect:
cLabelSelect = 0xffffff; color cLabelSelect # 3
_cSpecial:
cSpecial = 0xd51729; color cSpecial # 4
_cSpecialSelect:
cSpecialSelect = 0xff5e5e; color cSpecialSelect # 5
_cSilence:
cSilence = 0x199ab1; color cSilence # 6
_cSilenceSelect:
cSilenceSelect = 0x37d4f1; color cSilenceSelect # 7
_cVolume:
cVolume = 0xddaa12; color cVolume # 8
_cVolumeSelect:
cVolumeSelect = 0xffd25e; color cVolumeSelect # 9
_pokemon_voices:
.byte 0x0, 0xD, 0x10, 0x14, 0x18, 0x19, 0x1E, 0x23, 0x2a, 0x2C
.align 2
_default_config:
.long 0x00000000
------------- 0x80088204 ---- 88032225 -> Branch
# on voice SFX prefunction
# seems to divide SFX ID index into category for defining voices
# by intercepting, we can control only voice SFX
# offsets:
xMoveID = 0x2073
# registers:
rVolume = 28
lbz r12, 0x2073(r3)
mr r11, rVolume
bl <voice_sfx_argument_mutator>
mr rVolume, r11
# override volume with mutated volume
_return:
mr r3, r31
lbz r0, 0x2225 (r3)
.long 0
<voice_sfx_argument_mutator> NTSC 1.02
# mutate the arguments for a SFX call that includes a volume input
# -- it will be up to the caller to apply the mutated volume returned in r11
# arguments:
# r11 = input volume
# r12 = move ID
# returns:
# r3...r10 are preserved as given
# r11 = output volume (using memory card variables and RNG)
# r13 offsets:
xMem = -0x77C0
xMemTrophy264 = 0x1cd4 + (264<<1)
# we use trophy memory blocks 264 and 265
mflr r0
stwu sp, -0x100(sp)
stw r0, 0x104(sp)
stmw r27, 0x10(sp)
addi r27, sp, 0x80
stswi r3, r27, 0x18
# store r3...r10 at 0x80(sp)
lwz r31, xMem(r13)
mr r28, r12
lwz r31, xMemTrophy264(r31)
# r28 = saved move ID
# r31 = memory card data
# 0800 0000 = bool - enable silence chance
# 0700 0000 = 3-bit - silence chance, in increments of 1/8
# 0000 2000 = bool - enable volume decrease
# 0000 1F00 = 5-bit - volume, in increments of 1/32
rlwinm r30, r31, 24, 0x1F
rlwinm r3, r31, 19, 1
rlwinm r29, r31, 8, 0x07
rlwinm r4, r31, 5, 1
add r30, r30, r3
add r29, r29, r4
li r5, 0x20
sub r30, r5, r30
slwi r30, r30, 3
mullw r0, r30, r11
rlwinm r30, r0, 1, 0x100
add r0, r30, r0
srwi r30, r0, 8
mullw r29, r29, r4
# r30 = volume for voice SFX
# r29 = chance of silence
# r28 = given move ID (9...B = smash, 11...14 = special)
li r3, 8
bl 0x80380580 # HSD_randi
addi r3, r3, 1
# r3 = random number between 1...8
mtcrf 0x80, r31
# load memory card bools into cr0
# 2000 0000 = bool - allow for chance of silent smash attacks
# 1000 0000 = bool - allow for chance of silent special attacks
_check_smash:
bt- 2, _check_special
cmpwi r28, 0x09
blt+ _apply_chance
cmpwi r28, 0x0B
bgt+ _apply_chance
li r3, 8
# if smash silence is not allowed, and move ID is in smash range
# then set chance of silence to 0
_check_special:
bt- 3, _apply_chance
cmpwi r28, 0x11
blt+ _apply_chance
cmpwi r28, 0x14
bgt+ _apply_chance
li r3, 8
# if special silence is not allowed, and move ID is in special range
# then set chance of silence to 0
_apply_chance:
cmpw r3, r29
bgt+ 8
li r30, 0
# if random number between 1...8 (or 0 for no chance)
# is greater than than 1-based memory card Silence Chance value (0...7 + 1)
# -- then skip muting of applied volume
_apply_volume:
mr r11, r30
# return volume in r11, with other given arguments r3...r10
# -- it will be up to the caller to apply the volume returned in r11 by moving registers
_return:
lswi r3, r27, 0x18
lmw r27, 0x10(sp)
lwz r0, 0x104(sp)
addi sp, sp, 0x100
mtlr r0
blr
<psfx> NTSC 1.02
# r3 = SFX ID to play
# r4 00FF = 8-bit volume argument
# r4 0F00 = 4-bit stereo channel argument (0-biased)
# r4 7000 = 3-bit reverb channel argument (0-biased)
addi r5, r4, 0x800
addi r7, r4, 0x3800
li r6, 0
rlwinm r5, r5, 28, 0xF0
rlwinm r7, r7, 21, 0x0F
b 0x8038cff4
From the volume section of the sound options menu, 5 new menu items have been added by crudely modifying the selection index logic and the text description pointer in the main menu GObj.
The VOICES label can be selected to play a hit sound effect alongside a voice sound effect. A pokemon voice is played in place of a character voice because they are available in the universal sound bank -- but this code does not affect pokemon voices.
The sound/music balance measurement graphic has been used to create a makeshift slider for voice parameters; using a ~ to represent silence chance, and a | to represent volume amount:
They may each be selected using and , and adjusted using and . The description will show you the value of each slider as you adjust it. You can also press A to test the voice sound. Setting the Silence chance to 100% or the Volume amount to 0% will allow for voices to be completely silenced.
The green and red symbols next to the voice test label can be selected to toggle whether the voice silence chance will be applied to SMASH and SPECIAL attacks, respectively. When selected, the symbols will turn blue:
---
Stormghetti -- you should be able to use this to randomize all character voices without stopping regular sound effects or voices that come from smash/special attacks. This should affect both hardcoded voices and subaction event voices used in attacks, as requested. Let me know if there's anything else you want to be added to this.
Last edited: