• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Voice SFX Options

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

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 :GCU:and :GCD:, and adjusted using :GCL:and :GCR:. 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 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:

Stormghetti

Smash Journeyman
Joined
Aug 23, 2015
Messages
400
Location
Europe
Slippi.gg
STRM#798
NNID
Stormghetti
This is insane! I can't even imagine how long this took you, but it was well worth it. I didn't think it would turn out like this, but making it customizable ingame is a huge plus (I would like to configure the options of the code to my liking as well, just so they always load with X values without a Mem Card save). That being said, I wanted the code to apply to all attacks, not Smash attacks or specials, but if it's somehow possible to add regular attacks as a third option then that works.

The functionality of the code is great.
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
Stormghetti Stormghetti - Oh the RNG silence effect is applied to ALL character voices, including regular attacks. It's just smash and special attacks that can be filtered out of the RNG effect.

I've updated the code to include a data function that can be used to set the default settings for first use of a memory card or after erasing your trophy data. It should also work when you don't have a memory card.

For example, the value 0C00 0000 will set the defaults to use a 63% silence chance for all non-smash, non-special action voices, at 100% volume.
You can modify it like so:

 
Last edited:

Stormghetti

Smash Journeyman
Joined
Aug 23, 2015
Messages
400
Location
Europe
Slippi.gg
STRM#798
NNID
Stormghetti
Stormghetti Stormghetti - Oh the RNG silence effect is applied to ALL character voices, including regular attacks. It's just smash and special attacks that can be filtered out of the RNG effect.

I've updated the code to include a data function that can be used to set the default settings for first use of a memory card or after erasing your trophy data. It should also work when you don't have a memory card.

For example, the value 0C00 0000 will set the defaults to use a 63% silence chance for all non-smash, non-special action voices, at 100% volume.
You can modify it like so:

Ohh, my bad. I suppose I didn't pay enough attention when I read the explanation... Not sure how that got past me, but thanks for updating the code! I'll go play around with it.

Edit: one of Luigi's audio files seems to be affected during Side-B (even though I have special attacks set to be unaffected), but that's not a voice clip, so I'm assuming this applies to a few other stuff. It seems like voice clips in specials and Smash attacks always have a chance of being silenced no matter what I do, I don't know if that's just me or something you messed up in the code. I'm not sure what the code looks for when it tries to silence voice clips, but it doesn't work with moves that have voice clips added to them. I'm assuming it's because I had to change the pointers of the moves in order to add the voice clips.

When I requested a code for this kind of thing, I really only wanted it on aerials, tilts and Jabs, nothing else (especially not jumps because I like how customizable your jump SFX code is).
 
Last edited:

JoshuaMK

Smash Apprentice
Joined
May 19, 2019
Messages
75
This is absolutely amazing!!! Too bad it is incompatible with your Sound Test IDs code :p
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
This is absolutely amazing!!! Too bad it is incompatible with your Sound Test IDs code :p
Thanks. That code should actually work fine with this one, are you running into any problems?

Ohh, my bad. I suppose I didn't pay enough attention when I read the explanation... Not sure how that got past me, but thanks for updating the code! I'll go play around with it.

Edit: one of Luigi's audio files seems to be affected during Side-B (even though I have special attacks set to be unaffected), but that's not a voice clip, so I'm assuming this applies to a few other stuff. It seems like voice clips in specials and Smash attacks always have a chance of being silenced no matter what I do, I don't know if that's just me or something you messed up in the code. I'm not sure what the code looks for when it tries to silence voice clips, but it doesn't work with moves that have voice clips added to them. I'm assuming it's because I had to change the pointers of the moves in order to add the voice clips.

When I requested a code for this kind of thing, I really only wanted it on aerials, tilts and Jabs, nothing else (especially not jumps because I like how customizable your jump SFX code is).
Oh interesting, I'll check that out with Luigi. I was able to make this code because it turns out that the game uses a special function to handle voice clips over regular sounds. Basically, the code creates a mutation of the volume argument for that voice function -- so anything that goes through it gets checked.

Thankfully though this function also includes a player GObj argument, so I'm able to use that to filter the logic based on move IDs, or action states. I'll look into that later tonight. Thanks for the feedback!
 
Last edited:

JoshuaMK

Smash Apprentice
Joined
May 19, 2019
Messages
75
Interesting, now it is working... It used to say that it had conflicting code with your Sound Test IDs code. Okay then! Lol
 
Top Bottom