I've been looking into ways to add sound players/replayers to my MAME cheats. I found something that's used fairly consistently in a bunch of games, so I thought I'd write something up about it.
Anyways, this consists of 2 (possibly 3, but usually 2) parts to it. Normally, they keep track of a certain amount of previous sounds that are played. You will be using them to make something that'll either play specific sounds, or replay the logged sounds. They're not too hard to find as long as you have a rough idea of what to look for, but it can be it bit tricky to find what you need sometimes.
Spoiler: Part 1: The "player" (click to see content)
Anytime sound/music plays, the value is logged in the 2nd part and the value to this part will increase. It's easier to understand if you think of it like a cursor going down a list. How much the value increases, how long the list is, and what type of values it'll use isn't always consistent. You can try searching at integer 2 bytes for them, but I find it's a little easier to do it at 1 byte and sort out how many bytes it actually uses afterwards. I look for 2 addresses very close to each other that have the exact same value for both addresses, and that will only change when a song/sound is played. One of the two lets usually you change the value, the other almost always tries to sync back up with the other one, and will replay logged sounds/music up to the current point on the list. To test if you found it, you'll want to set it to a lower value than the current one. Otherwise, it'll attempt to resync by constantly increasing the value to try and loop back around.
To make this do what we want, you'll want to jot down the lowest and 2nd lowest values it can go to (it'll make the 2nd part a little easier). The lowest value goes to the address that wants the value to stay in sync with the other. You'll be poking this one to get the 1st (possibly 2nd) sound in the list to play. The 2nd lowest value will go to the address that allows you to change the value to it. If you're using Artmoney/Cheat Engine, this value stays frozen.
I mentioned sorting out what bytes it can use. They can be: 2 1 byte values, 1 value at 2 bytes, and 2 values at 2 bytes.
Spoiler: Part 1-A: The "player" (click to see content)
This is a rare occurrence, but sometimes messing with the values to the "player" addresses won't cause the sound to play. They're still of use, but you'll have to poke around the vicinity of those addresses to find something that makes the sound play.
Spoiler: Part 1-B: The "replayer", instead (click to see content)
For some games, the sound/music values might be set up in a way that makes trying to mod the sound/music values not worth the hassle. In this case, you won't be needing the 2nd part, but you can still use the 1st part you found to instead replay the logged sounds. Just make note of the lowest and highest values and how much they increase by. As long as you know those, you can control what sound in the list gets replayed. Just make sure no new sounds play.
Spoiler: Part 2: Modifier of a logged sound value (click to see content)
This part is found in a group of addresses around the vicinity of the "player" addresses. The values might be 1 byte (just the sound), 2 bytes (usually just sounds), or 4 bytes (2 bytes for sounds, possibly 2 bytes for things like volume and pitch. At least , that was the case for PGM games). If you want to try finding this first, try repeatedly playing the same sound, searching at integer 2 bytes, repeatedly playing another sound, filtering, occasionally filtering for value has not changed (I wouldn't suggest doing this during attract mode. For some games, it'll log sounds/music, despite you not being able to hear them). If you found the "player" addresses first, and want to find these, try repeatedly playing a sound a bunch of times, open up the memory editor, and check somewhere above or below for a bunch of addresses that have the same value in them. You're gonna want the 1st, possibly 2nd addresses in this bunch, because of how we set up the 1st part.
Something to try:
If you're using MAME to find these addresses (and know a bit about the debugger/memory editor), here's something to try out: The values of the "player" addresses sometimes can be referenced to find the sound modifier addresses more easily. Here's an example (this should also give you a better idea of what it all kinda looks like):
F015 and F016 are the "player" addresses, and F100 through F1FC are the logged sounds. They're currently at F16A in the list.
Spoiler: Examples of how it'd look as MAME cheats: (click to see content)
Player (1 byte):
<cheat desc="Play Sound">
<parameter min="0" max="255" step="1"/>
<script state="change">
<action>maincpu.pb@E1C54=param</action>
<action>maincpu.pb@E1C94=54</action>
<action>maincpu.pb@E1C96=55</action>
</script>
</cheat>
Simple enough looking. You set the value and press enter to to poke E1C94 with 54, E1C96 with 55, and E1C54 with a custom value to get it to try and play a sound. But what if the game has more than 255 sounds? Well....
Player (2 bytes):
<cheat desc="Play Sounds +256">
<parameter>
<item value="0x00">00</item>
<item value="0xFC">FC</item>
</parameter>
<script state="change">
<action>maincpu.pb@108406=param</action>
<action>maincpu.pw@108402=0408</action>
<action>maincpu.pw@108404=0406</action>
</script>
</cheat>
<cheat desc="Play Sounds +1">
<parameter min="0" max="255" step="1"/>
<script state="change">
<action>maincpu.pb@108407=param</action>
<action>maincpu.pw@108402=0408</action>
<action>maincpu.pw@108404=0406</action>
</script>
</cheat>
A little trickier to use, because you now have 65535 values to go through, and most of them aren't used (which means messing around with the values, or looking through the logged sounds to try and figure it out). I tend to break it up into 2 1 byte cheats for ease of use. This one is set up to go through 0000-00FF/FC00-FCFF.
Replayer:
<cheat desc="Replay Sound">
<parameter min="39342" max="39850" step="4"/>
<script state="change">
<action>maincpu.pw@809D1E=(param)</action>
<action>maincpu.pw@809D22=(param-4)</action>
</script>
</cheat>
Normally, you'll set it up something like this. The game I made this for had the values go from 99AE to 9BAA and the values increased by 4. I set up the parameter of the 2nd address so that it sets the value to 4 less than the 1st.
Spoiler: Other stuff: (click to see content)
- I've encountered this in Star Gladiator/Plasma Sword: There's something that mods the sounds/music, but it only let me play whatever was in the sound test in the test menu (which wasn't much). This issue might be in other 3D capcom games.
- For PGM games: Sometimes there's more than one set of player/sound mod addresses for some of these. In Spectral Vs Generation, the 1st one I found didn't work right because I found it in the test menu, but the 2nd one worked fine as long as I didn't use it in the test menu.
- PGM fun fact: you can find sounds from Darkstalkers in all the PGM games that I've made this cheat for. I haven't looked further in them than Martial Masters, and maybe I should because it used sounds from KOF 98.
- For Neo Geo games: I've encountered this in 1 game (had to go out of my way to encounter it though), but I'm going to mention it just in case: There's more than 1 set of player/sound mod addresses. One's in maincpu and one's in audiocpu. The one in maincpu's easier/less confusing.
- For midway games, or whatever has "dcs" listed for a memory (sound) region: Making sound player cheats is a pain for this. The sound values will probably be 2 bytes. If you made a cheat like this:
<cheat desc="Play Sound">
<parameter min="0" max="65535" step="1"/>
<script state="change">
<action>dcs.dw@3910=param</action>
<action>dcs.dw@4B1=3911</action>
<action>dcs.dw@4B2=3910</action>
</script>
</cheat>
You'll be fine. The problem arises if you wanted break that up into 2 1 byte cheats (unless WANTED to go through 65535 values), because only 1 of those cheats will work (this issue AFAIK, is exclusive to these games). Here's what I came up with to solve that:
<cheat desc="Play Sound +256">
<parameter min="0" max="65535" step="256"/>
<script state="change">
<action>dcs.dw@3910=param</action>
<action>dcs.dw@4B1=3911</action>
<action>dcs.dw@4B2=3910</action>
</script>
</cheat>
<cheat desc="Play Sound +1">
<parameter min="0" max="255" step="1"/>
<script state="change">
<action>dcs.db@3910=param</action>
<action>dcs.dw@4B1=3911</action>
<action>dcs.dw@4B2=3910</action>
</script>
</cheat>
With this, you'd be able to write to both bytes, but using +256 will always set the value to +1 to 00 (it just means you have to be careful with how you set them).
Hope that helps someone.
Edit: Fixed Play Sound +1 for the dcs cheat (+1's supposed to write to 1 byte, or it conflicts with +256).