Skip to content

Play AltSound packs instead of the ROM's sound - #49

Merged
freezy merged 5 commits into
masterfrom
feat/altsound
Oct 25, 2026
Merged

freezy merged 5 commits into
masterfrom
feat/altsound

Conversation

@freezy

@freezy freezy commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Plays AltSound packs (recorded music, callouts and effects triggered by the ROM's sound commands) instead of the ROM's own sound, as Visual PinMAME does.

Player settings

The PinMAME plugin settings get an AltSound section:

  • Use AltSound: global, with a per-table override.
  • AltSound folder: the folder holding one subfolder per ROM, such as VPinMAME's altsound folder. The plugin also accepts a ROM's own folder, or a folder that contains altsound. Left empty, it looks next to VPinMAME's ROM folder and in VPE's PinMAME folder.
  • When AltSound is on:
    • AltSound volume: the pack's own level, up to 200%. The PinMAME ROM volume doesn't change it.
    • Follow the game's volume: lets the volume set in the game's menu scale the pack too, as in Visual PinMAME. Off by default, since that often makes packs much quieter (a WPC DMD game's volume command can scale a pack down to about 9%). Turning it on applies the game's current volume right away.
    • ROM sound under AltSound: keeps some of the ROM's own sound for packs that don't replace all of it. The default is 0, like Visual PinMAME.

The status lines report whether a pack plays, and why not when it doesn't.

How it works

  • native/AltSound builds VpeAltSound, which compiles libaltsound (BSD-3-Clause, pinned at ce11780) together with a small C interface. libaltsound's own API takes std::string, which C# can't call. Every entry point runs under one lock, commands outside a running instance are dropped, and no C++ exception crosses the boundary.
  • Fixes go on top of the pinned sources until upstream has them: patches/fixes.patch joins the sample paths of folder-based packs (Fix sample paths of folder-based packs vpinball/libaltsound#22), makes AltSoundInit fail when no samples could be loaded, and lets a stream silenced by a zero game volume come back instead of turning into NaN. compat/dirent replaces the Windows directory listing, which changed the process's current directory and couldn't nest (Windows: give each opendir() its own state and keep the current directory vpinball/libaltsound#23).
  • PinMameAltSound finds the pack and starts libaltsound at 44.1 kHz, the packs' rate: at the output rate, libaltsound's own resampling (miniaudio's engine, which runs without its low-pass filter) put images of the treble into the audible band about 20 dB under the music, heard as hiss. The plugin converts the mix to the output rate with a 64-tap windowed-sinc resampler (spurs below -80 dB) and applies the pack's volume there. It pushes the mix into a central-mixer stream on the ROM bus, or into a ring the engine's audio filter plays when the mixer is off (it waits for a little audio first, since libaltsound delivers in bursts). Commands arrive through pinmame-dotnet's new OnSoundCommand event, the hardware generation is set when the game starts, and the pack pauses with the game.
  • CI builds the library for win-x64, win-x86, osx (universal) and linux-x64 into the plugin folders.

Testing

  • Windows x64 build. A probe of the library's C interface covered a missing folder, a pack that can't load, double init, commands before and after init, pause, and shutdown stopping the callbacks.
  • A Player build with a WPC DMD table and a folder-based pack, over ASIO: the pack plays through the central mixer, its log shows the commands decoded and samples played, and a capture of the device output shows the pack's audio with the ROM muted. With AltSound off, the ROM's own sound comes back.
  • The resampler, run on the library's 44.1 kHz output: a three-tone test puts noise and spurs at -83 dB (-21 dB before), and the music's treble is unchanged. In the Player, VPE's rendered ASIO output stays gapless with no samples over full scale, and the pack plays at about the ROM's level.
  • Game-volume tracking through the C interface: a volume of 0 followed by turning following off gives finite, full-level audio, and turning it back on applies the game's volume at once.
  • Not tested: the macOS and Linux builds (CI builds them), playback without the central mixer, and toggling AltSound while a game runs.

Order

Uses the OnSoundCommand event from vpinball/pinmame-dotnet#48, released as PinMame 1.1.2; PinMameVersion is bumped to it here.

The Player's PinMAME settings get an AltSound section: a toggle, the folder with the packs, and when it's on, the pack's volume and how much of the ROM's own sound stays under it (none by default, like Visual PinMAME). The ROM's sound commands drive libaltsound, built into a new VpeAltSound library from native/AltSound with a C interface, two fixes on top of the pinned sources (folder-based packs, failed loads) and a Windows directory listing that leaves the current directory alone. The mix plays through the central mixer's ROM bus, or through Unity without it, and pauses with the game.

Needs the OnSoundCommand event of the next pinmame-dotnet release.
@greptile-apps

greptile-apps Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with the previous volume-tracking issue addressed and no new actionable findings.

Summary

Adds AltSound packs as an optional replacement for ROM audio.

  • PinMAME tables can play AltSound packs when the ROM sends sound commands.
  • Players can choose an AltSound pack and set how it mixes with ROM audio.
  • The AltSound native library is built and packaged for desktop players.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  ROM[ROM sound commands] --> Native[VpeAltSound and libaltsound]
  Native --> Convert[Resample and apply pack volume]
  Convert --> Mixer[Central mixer]
  Convert --> Unity[Unity audio fallback]
Loading

Reviews (5) · Last reviewed commit: "audio: remember a game volume command to..." · Reviewed by Greptile

…ltSound library

The pack now stops when the ROM ends or fails to start, so looping music doesn't outlive it; each start is counted, so the end of a stopped earlier run can't stop the pack of the retry. An AltSound library that can't be loaded reports AltSound unavailable instead of failing the ROM's start.
1.1.2 adds the OnSoundCommand event that AltSound needs.
…he ROM

libaltsound now mixes at the packs' 44.1 kHz: at the output rate, its own resampling (miniaudio's engine, low-pass off) put images of the treble into the audible band about 20 dB under the music, which sounded like hiss and grit. The plugin converts the mix with a 64-tap windowed-sinc resampler instead (spurs below -80 dB) and applies the pack's volume there, up to 200% and no longer scaled by the ROM volume.

The game's own volume setting no longer scales the pack unless 'Follow the game's volume' is on; the wrapper keeps tracking it, so the option applies at once. The pinned libaltsound gets one more fix: a stream silenced by a zero game volume came back as NaN. Unity playback without the mixer waits for a little audio before it starts, since the mix arrives in bursts.
Comment thread native/AltSound/VpeAltSound.cpp Resolved
…oesn't follow it

While the pack doesn't follow the game's volume it played at exactly 1, so a command to full volume looked like no command and turning following on later restored an older, lower volume. It now plays at the float just under 1, which no volume command produces.
@freezy
freezy merged commit 904ae4f into master Oct 25, 2026
17 checks passed
@freezy
freezy deleted the feat/altsound branch October 25, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant