Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectively suppress initial Sound Blaster DMA transfer #411

Closed
wants to merge 14 commits into from

Conversation

kcgen
Copy link
Member

@kcgen kcgen commented May 30, 2020

Many games use a small DMA transfer as part of their Sound Blaster detection and initialization routine. Because the content in this initial DMA transfer is often garbage and/or discontinuous, this leads to a "pop" while the game is starting up (this information is courtesy ripsaw8080, thank you!).

This PR suppresses this initial DMA transfer after a DSP_Reset cycle if:

  • the transfer is a DWORD or less, or
  • the SB's onboard speaker-output is disabled

This PR is a quality of life improvement because these initial pops can be jarring and lead to hearing damage at high volumes, especially for those using high-SnR earbud headphone. It's also a bug-fix because this "audio" is not game content, and instead is the result of poor documentation and hardware design by Creative Labs. See the before-and-after comparison images in the follow-up comment for just a small sampling of games affected by this.

Notes: the suppressed transfer is processed but without being played, so all aspects of the card state appear normal. If the suppression criteria isn't met then the behavior matches that prior to the PR. At no point is the audio content itself or channel volume altered.

After testing hundreds of titles, this corrects a significant number of games without adversely affecting those where the criteria is not met. I should warn that even with this, not all games games are fixed: some apply a constant DC-offset as part of their audio track (which pops on playback), others can pop even without using the Sound Blaster, and others pop without using DMA transfers.

PR review: Suggest reviewing commit-by-commit; I tried hard to layer these in a nice sequence that builds up to the functional change.

@kcgen kcgen requested a review from dreamer May 30, 2020 23:28
@kcgen kcgen self-assigned this May 30, 2020
@kcgen kcgen added bug Something isn't working cleanup Non-functional changes that simplify, improve maintainability, or squash warnings labels May 30, 2020
@kcgen kcgen force-pushed the kc/suppress-sb-dma-init-1 branch from 5dc605d to d6165c3 Compare May 30, 2020 23:48
@kcgen
Copy link
Member Author

kcgen commented May 31, 2020

Game Init Sequence Before & After Audio Files
AD&D Dark Sun I Shattered Lands DSP-Reset
Supp-4b
comparison AD&D Dark Sun I Shattered Lands.zip
AD&D Legend III Eye of the Beholder III Assault of Myth Drannor DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Spkr-off
DSP-Reset
Spkr-on
Spkr-off
DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
AD D Legend III Eye of the Beholder III Assault of Myth Drannor-comparison AD&D Legend III Eye of the Beholder III Assault of Myth Drannor.zip
AD&D Ravenloft II Stone Prophet DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
AD D Ravenloft II Stone Prophet-comparison AD&D Ravenloft II Stone Prophet.zip
AD&D Ravenloft I Strahd's Possession DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
AD D Ravenloft I Strahd's Possession-comparison AD&D Ravenloft I Strahd's Possession.zip
Battle Chess 4000 DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Battle Chess 4000-comparison Battle Chess 4000.zip
Battle Isle 2 Titan's Legacy DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Spkr-off
DSP-Reset
Battle Isle 2 Titan's Legacy-comparison Battle Isle 2 Titan's Legacy.zip
Dune II The Building of a Dynasty DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Dune II The Building of a Dynasty-comparison Dune II The Building of a Dynasty.zip
Dungeon Master II The Legend of Skullkeep DSP-Reset
Supp-3b
Supp-3b
DSP-Reset
DSP-Reset
Spkr-on
Dungeon Master II The Legend of Skullkeep-comparison Dungeon Master II The Legend of Skullkeep.zip
Highway Hunter DSP-Reset
DSP-Reset
Supp-1b
Spkr-on
Supp-1b
Highway Hunter-comparison Highway Hunter.zip
Hoyle's Classic Card Games DSP-Reset
Supp-1b
Spkr-on
Hoyle's Classic Card Games-comparison Hoyle's Classic Card Games.zip
Indiana Jones and the Fate of Atlantis DSP-Reset
DSP-Reset
Supp-1b
Spkr-on
Indiana Jones and the Fate of Atlantis-comparison Indiana Jones and the Fate of Atlantis.zip
IndyCar Series DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
IndyCar Series-comparison IndyCar Series.zip
King's Quest V Absence Makes the Heart Go Yonder! DSP-Reset
Supp-1b
Spkr-on
King's Quest V Absence Makes the Heart Go Yonder!-comparison King's Quest V Absence Makes the Heart Go Yonder!.zip
Lands of Lore The Throne of Chaos DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Lands of Lore The Throne of Chaos-comparison Lands of Lore The Throne of Chaos.zip
Leisure Suit Larry I (1991) in the Land of the Lounge Lizards DSP-Reset
Supp-1b
Spkr-on
Leisure Suit Larry I (1991) in the Land of the Lounge Lizards-comparison Leisure Suit Larry I (1991) in the Land of the Lounge Lizards.zip
Leisure Suit Larry V Passionate Patti Does a Little Undercover Work DSP-Reset
Supp-1b
Spkr-on
Leisure Suit Larry V Passionate Patti Does a Little Undercover Work-comparison Leisure Suit Larry V Passionate Patti Does a Little Undercover Work.zip
Maniac Mansion II Day of the Tentacle DSP-Reset
DSP-Reset
Supp-1b
Spkr-on
Maniac Mansion II Day of the Tentacle-comparison Maniac Mansion II Day of the Tentacle.zip
Mario Teaches Typing DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Mario Teaches Typing-comparison Mario Teaches Typing.zip
Master of Magic DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Master of Magic-comparison Master of Magic.zip
Master of Orion 1 DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Master of Orion 1-comparison Master of Orion 1.zip
Monkey Island II LeChuck's Revenge DSP-Reset
Supp-1b
Spkr-on
DSP-Reset
DSP-Reset
Supp-1b
Spkr-on
Spkr-off
Monkey Island II LeChuck's Revenge-comparison Monkey Island II LeChuck's Revenge.zip
Pacific Strike DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Pacific Strike-comparison Pacific Strike.zip
Panzer General DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Panzer General-comparison Panzer General.zip
Police Quest I (1992) In Pursuit of the Death Angel DSP-Reset
Supp-1b
Spkr-on
Police Quest I (1992) In Pursuit of the Death Angel-comparison Police Quest I (1992) In Pursuit of the Death Angel.zip
Police Quest III The Kindred DSP-Reset
Supp-1b
Spkr-on
Police Quest III The Kindred-comparison Police Quest III The Kindred.zip
Prophecy of the Shadow DSP-Reset
Supp-1b
DSP-Reset
Supp-1b
Spkr-on
Prophecy of the Shadow-comparison Prophecy of the Shadow.zip
Quarantine SBPro2: DSP reset requested
SBPro2: Speaker-output has been toggled on
SBPro2: Suppressed playback of 3-byte initial DMA transfer
SBPro2: DSP reset requested
SBPro2: Speaker-output has been toggled on
Quarantine-comparison Quarantine.zip
Quest For Glory I (1992) So You Want To Be A Hero? DSP-Reset
Supp-1b
Spkr-on
Quest For Glory I (1992) So You Want To Be A Hero?-comparison Quest For Glory I (1992) So You Want To Be A Hero?.zip
Quest For Glory III Wages of War DSP-Reset
Supp-1b
Spkr-on
Quest For Glory III Wages of War-comparison Quest For Glory III Wages of War.zip
Risky Woods DSP-Reset
Supp-1b
DSP-Reset
Supp-1b
DSP-Reset
Supp-1b
Supp-1b
Spkr-on
Risky Woods-comparison Risky Woods.zip
Rules of Engagement 1 DSP-Reset
DSP-Reset
Supp-1b
Spkr-on
Rules of Engagement 1-comparison Rules of Engagement 1.zip
Rules of Engagement 2 DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Rules of Engagement 2-comparison Rules of Engagement 2.zip
Sid Meier's Colonization DSP-Reset
SB16: Suppressed playback of 5-byte initial DMA transfer
Spkr-on
Sid Meier's Colonization-comparison Sid Meier's Colonization.zip
SimCity 2000 DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
SimCity 2000-comparison SimCity 2000.zip
SimCity Classic DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
SimCity Classic-comparison SimCity Classic.zip
Space Hulk DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Space Hulk-comparison Space Hulk.zip
Space Quest I (1991) The Sarien Encounter DSP-Reset
Supp-1b
Spkr-on
Space Quest I (1991) The Sarien Encounter-comparison Space Quest I (1991) The Sarien Encounter.zip
Space Quest IV The Time Rippers DSP-Reset
Supp-1b
Spkr-on
Space Quest IV The Time Rippers-comparison Space Quest IV The Time Rippers.zip
Space Quest V The Next Mutation DSP-Reset
Supp-1b
Spkr-on
Space Quest V The Next Mutation-comparison Space Quest V The Next Mutation.zip
Star Trek (1993) Judgment Rites DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
Star Trek (1993) Judgment Rites-comparison Star Trek (1993) Judgment Rites.zip
The Chaos Engine DSP-Reset
Supp-3b
Spkr-on
The Chaos Engine-comparison The Chaos Engine.zip
The Legend of Kyrandia I DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Supp-4b
Spkr-on
The Legend of Kyrandia I-comparison The Legend of Kyrandia I.zip
The Legend of Kyrandia II Hand of Fate DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
The Legend of Kyrandia II Hand of Fate-comparison The Legend of Kyrandia II Hand of Fate.zip
Ultima Underworld II Labyrinth of Worlds DSP-Reset
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Ultima Underworld II Labyrinth of Worlds-comparison Ultima Underworld II Labyrinth of Worlds.zip
Ultima Underworld I The Stygian Abyss DSP-Reset
Supp-1b
DSP-Reset
Supp-1b
Spkr-on
Ultima Underworld I The Stygian Abyss-comparison Ultima Underworld I The Stygian Abyss.zip
Wing Commander Privateer Righteous Fire DSP-Reset
Supp-4b
Supp-4b
DSP-Reset
Supp-4b
Spkr-on
Wing Commander Privateer & Righteous Fire-comparison Wing Commander Privateer Righteous Fire.zip

@kcgen kcgen force-pushed the kc/suppress-sb-dma-init-1 branch 2 times, most recently from b7b873d to b2a471b Compare June 6, 2020 14:33
@kcgen kcgen force-pushed the kc/suppress-sb-dma-init-1 branch from b2a471b to 1e7b22f Compare June 12, 2020 15:36
Copy link
Member

@dreamer dreamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On first sight, looks good code-wise :) I left few comments (naming and such), will follow up with some testing for the next round of review.

src/hardware/sblaster.cpp Outdated Show resolved Hide resolved
src/hardware/sblaster.cpp Outdated Show resolved Hide resolved
@dreamer
Copy link
Member

dreamer commented Jun 13, 2020

BTW, thank you for detailed commit messages - they really help :)

@kcgen kcgen force-pushed the kc/suppress-sb-dma-init-1 branch 2 times, most recently from a89af72 to 2f5e50b Compare June 14, 2020 19:43
kcgen added 14 commits June 14, 2020 20:02
CheckDMAEnd actually finishes the DMA transfer and actually doesn't
return a boolean (or success/fail) as its name describes.
This commit makes it fall in line with the other DMA_* functions.
The function doesn't generate DMA sounds. It processes audio samples
as provided by the emulated software.  The suggested name also falls
in line with the existing DMA_* functions.
The name implies it does something to terminate the DMA event.
We see it's actually a no-op wrapper that just processes DMA samples.
We eliminate this redundant wrapping so the calling code more
accurately describes what's actually happening.
We rename this slightly to reflect the fact that this function is
actually processing samples.  We also prefer the name "Suppress" over
"Silent", as the former is an verb where as the latter in an adjective
describing a state-of-being.
This opens up the ability to differentiate how samples are processed:
if they're actually Played or Suppressed.
This function will suppress the given DMA transfer if its size is
less than or equal to a DOS DWORD or of any size if the SB's
onboard speaker-output is set to disabled (in which case, the game
doesn't want it to be heard).

Once the initial suppression takes place, this function flips the
DMA_Process_Samples function pointer over to DMA_Play_Samples, so
subsequent DMA transfers are no suppressed.

Note: this commit simply adds this function without making use of
it.
This intialization function is added to DSP_Reset(), which is
called both during the object's construction as well as requested
on-the-fly by the emulated software, typically during the
detection phase; sometimes many times as the driver performs
various tests to detemine if the card exists and is useable.

This intialization function also makes use of the new DMA
initialization suppression function to ensure "try-and-see
transfers" are not heard.
Previous when an SB16 was being emulated, the mixer's channel was
always left enabled (per its initial state), even when the
game requested that it be disabled.

This commit allows the (dosbox) mixer's audio channel state to
reflect the requested speaker-output state, which saves cycles and
simplifies the logic.

This also respects the game's actual behavior: if the game
developer asked the SB's onboard speaker-output to be disabled (or
enabled), then we trust that's infact what they wanted.
Some of these would have conflicted in the commit history,
so these have come at the end.

- Improve naming of DMA transfer functions and avoid
  conflict with general dma.h functions.
- Improve order of speaker setup routine to flush
  old content before turning on the audio channel.
- Fix tab'ing in initialization function.
@kcgen kcgen force-pushed the kc/suppress-sb-dma-init-1 branch from 2f5e50b to ba69d97 Compare June 15, 2020 03:02
@kcgen kcgen requested a review from dreamer June 15, 2020 03:10
Copy link
Member

@dreamer dreamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified last commit to replace Neither_Snake_Case_Nor_Camel_Case naming with CamelCase (to be consistent with naming of other functions in this file) and noticed some other whitespace errors.

That would've been my last comment, otherwise it's good - I will merge it in momentarily :)

@dreamer
Copy link
Member

dreamer commented Jun 15, 2020

Merged in: 426358e

@kcgen Thanks! :)

@dreamer dreamer closed this Jun 15, 2020
@kcgen
Copy link
Member Author

kcgen commented Jun 15, 2020

Excellent, thanks for the last fixup @dreamer!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working cleanup Non-functional changes that simplify, improve maintainability, or squash warnings
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants