Skip to content

A game's sound effects play at volume 0 under Wine, because it reads its volume from the mixer ​

Symptoms ​

The game opens its wave outputs and writes sound to them (WINEDEBUG=+winmm shows waveOutWrite), but nothing is heard, or its volume setting reads 0 and cannot be raised. The trace shows the game asking the mixer for a source line by component type (mixerGetLineInfoA with flags 0x80000003, dwComponentType: 0x1008 for WAVEOUT or 0x1005 for COMPACTDISC).

Cause ​

Some games of 1997 keep their effect and music volumes in the WinMM mixer's source lines, as the sound card's driver offered them. Wine's mixer has a destination line for the speakers and answers the WAVEOUT query with it, and that line's volume control reads 0. It has no COMPACTDISC line (measured, Wine 11.0 on macOS: the game's first read of the wave line's volume gives 0, before it sets anything). POD's Maxi Sound 64 builds take that 0 as the effect volume, write it back, and give their wave streams volume 0 (observed, PODMAX64.EXE FUN_004b9710, FUN_004b9ad4, FUN_004bbe0c).

Evidence ​

Logging the game's mixer calls from a stand-in DLL that hooks them in the import table: mixerGetLineControlsA finds Wine's volume control (ID 2) on the line, and mixerGetControlDetailsA reads its value as 0. WINEDEBUG=+winmm shows the queries but not the values.

What to do ​

Answer the game's two lines from a DLL the game loads, as the card's driver did. Hook mixerGetLineInfoA, mixerGetLineControlsA, mixerGetControlDetailsA and mixerSetControlDetails in the game's import table (shim::iat). For a WAVEOUT or COMPACTDISC query, give a source line with one volume control (0 to 0xffff, starting at full), keep the value the game sets and read it back. Pass every other query to Wine. wrappers/maxi94 does this for POD (maxi94/src/mixer.rs, mixer_dll.rs). A game without a card library of its own needs the hooks in another stand-in it loads, such as ddraw.dll.

Tried ​

  • Answering only when Wine's call fails: Wine answers the WAVEOUT query, so the game kept Wine's line and its volume of 0.
  • Reading the component type after Wine's failed mixerGetLineInfoA: Wine had written its own fields back into the structure, so every query looked like the first. Read the query before calling Wine.

Code: Apache 2.0. Content: CC0 1.0. No game material: bring your own copy.