Cross platform audio, single audio thread, full audio emulation accuracy, full implementation of the entire SB PCM, SB DSP, SB Hardware mixer, SB reset delay, OPL port read/write delays, SB variants, OPL3, OPL2, dual OPL2, Adlib Gold Music and surround effects, and rewired PC Speaker. Full software mixer UI on top.#1635
Merged
maximilien-noal merged 279 commits intomasterfrom Mar 6, 2026
Merged
Cross platform audio, single audio thread, full audio emulation accuracy, full implementation of the entire SB PCM, SB DSP, SB Hardware mixer, SB reset delay, OPL port read/write delays, SB variants, OPL3, OPL2, dual OPL2, Adlib Gold Music and surround effects, and rewired PC Speaker. Full software mixer UI on top.#1635maximilien-noal merged 279 commits intomasterfrom
maximilien-noal merged 279 commits intomasterfrom
Conversation
Contributor
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
… tests (#1675) * Initial plan * add dsp port parity tests Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * expand dsp command tests Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * add dsp timeconstant samplerate and test register tests Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * cache sb state in dsp tests Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * add asm dsp integration tests Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * remove reflection and use nasm dsp binary Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…and stabilize test execution across platforms (#1676) * Initial plan * add sound blaster mixer tests and asm status output Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Update mixer asm stereo expectation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Disable xunit parallelization to avoid Windows OOM Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Guard headless render timer against disposed renderer Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Remove swallowed exceptions in headless render timer Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…1677) * Initial plan * Add DacChannel property and PCM test infrastructure - Add public DacChannel property to SoundBlaster class for test access - Add audio capture infrastructure to SbPcmAsmIntegrationTests - Add 5 new basic functional tests that don't require golden WAV files - Tests validate test infrastructure, file writing, and basic execution - Note: Audio capture limited when using DummyAudio engine Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Address code review: remove unused System.Linq import - Remove unused System.Linq using directive - Keep DacChannel public property (follows existing pattern from Opl3Fm) - All tests still passing (5 new tests, 1026 total passing) Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Fix resource disposal in PCM tests to prevent leaks - Wrap Spice86DependencyInjection in using statements for proper disposal - Add disposal delay to allow timer cleanup before test completion - Prevents resource leaks from undisposed emulator instances - Note: Renderer NullReferenceException is pre-existing issue in test framework Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add NullReferenceException guards in Renderer.Render - Wrap state access in try-catch to handle disposal race conditions - Prevents crash when HeadlessGui timer fires during cleanup - Remove Task.Delay calls that increased race condition window - All tests pass without crashes Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * refactor: remove poor error handling Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> Co-authored-by: Maximilien Noal <noal.maximilien@gmail.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…ing hot path mirror) (#1683) * Initial plan * Phase 2: Replace ConcurrentDictionary with Dictionary + lock in Mixer Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add audio performance tests and validation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add comprehensive audio performance optimization documentation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Address code review feedback: improve comments and buffer capacity checks Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Revert to ConcurrentDictionary to fix critical performance regression Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Update documentation to reflect Dictionary + lock revert and lessons learned Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Remove markdown documentation per user request Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Optimize OPL AudioCallback to use AddSamples_sfloat for bulk frame addition Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
* Initial plan * Optimize OPL audio hot paths following DOSBox Staging architecture Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Address code review feedback: remove extra blank lines and reduce lock contention * Remove custom generator support and apply DOSBox Staging hot path optimizations Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Remove dead RenderTo method for cleaner DOSBox Staging parity Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…nd embedded test data (#1687) * Initial plan * Add SB PCM 11025Hz test: WAV file and ASM program Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add C# tests for 11025Hz PCM and fix 8-bit WAV reading support Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add completion documentation for SB PCM test implementation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Complete sb_dma_8bit_single.asm with 11025 Hz PCM playback, remove docs and Python scripts Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Fix 8-bit PCM to float conversion to use correct scaling factor (127.5f) Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Update documentation to reflect actual 8-bit PCM conversion values Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Remove redundant IRQ ack, add 5-second WAV as embedded C# byte array Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Clarify ASM comments and improve WavFileFormat documentation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…h optimized performance (#1689) * Initial plan * WIP: Start exact C# port of Speex resampler - TDD approach with initial test infrastructure Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Complete faithful C# port of Speex resampler - all 18 tests passing Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Integrate pure C# Speex into MixerChannel, remove P/Invoke bindings, update docs Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Refactor Speex integration: make non-nullable, remove try/catch, use const quality, initialize in constructor Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Complete Speex API port: Add all missing methods with TDD - 32/32 tests passing Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Fix code review issues: ArrayPool for hot paths, bounds checking, readonly fields, IDisposable, precision fixes Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Replace all ternary operators with if/else statements for better readability Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Fix code review feedback: clarify GCD comment, remove unnecessary null-conditional, use ternary operators Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
Initialize _sampleRateHz to 22050 to prevent input sample rate exceptions with the Speex resampler. Note: this constructor initialization may not be the ideal solution for Speex resampler integration. No other logic was modified.
…1737) * Initial plan * Fix Speex resampler initialization to mirror DOSBox Staging exactly Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Add ClearResampler method, fix default resample method, update documentation Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Document architectural difference - recommend testing current fixes first Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…1741) * Initial plan * fix: keep framesRemaining in scope for handler call Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
* Initial plan * Phase 1: Move resampling to AddSamples methods - mirrors DOSBox architecture - Added Envelope class for click prevention (mirrors DOSBox Envelope) - Completely refactored all AddSamples methods (m8, m16, s16, mfloat, sfloat, AudioFrames) - Created type-specific ConvertSamplesAndMaybeZohUpsample methods for each sample format - Resampling now happens IN AddSamples methods (not in Mix) - Added Lerp helper function for linear interpolation - Added ApplySpeexResampling for Speex resampling in AddSamples - Added ApplyInPlaceProcessing for filters and crossfeed in AddSamples - Removed orphaned ApplyLerpUpsampling and SpeexResampleBuffer methods - Architecture now matches DOSBox exactly: Convert→Resample→Process pattern Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> * Document critical architectural finding in AUDIO_PORT_PLAN.md - Documented that resampling architecture is now fixed in MixerChannel - Identified CRITICAL issue: SoundBlaster.cs bypasses AddSamples entirely - SoundBlaster PCM audio is NOT resampled (bypasses the resampling we just fixed) - Need to implement output_queue pattern from DOSBox for SoundBlaster - OPL is fixed (calls AddSamples_sfloat), SoundBlaster PCM still broken Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
Signed-off-by: Maximilien Noal <noal.maximilien@gmail.com>
kevinferrare
approved these changes
Mar 3, 2026
Refactored audio frame enqueueing logic to use generic methods for mono/stereo and sample type handling, eliminating redundant code. Added helper functions and delegates for sample conversion, improved silence handling, and optimized silent frame generation with an empty frame cache. Updated DAC mode to enqueue frames directly. Improved code clarity and maintainability.
Replace magic numbers with strongly-typed enums for port offsets, DSP commands, and mixer registers in SoundBlaster. Add XML docs for each enum value. Improves code clarity and maintainability with no change to emulation logic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of Changes
Fixes all PCM and music rendering issues by using the latest dosbox staging architecture for the software mixer, which runs on its own thread, and update every related class along with it (PC Speaker, Sound Blaster, OPL, ...)
It uses less threads. The audio logic is inverted: the software mixer pulls audio data from devices.
For comparison here is the old audio thread logic from the master branch:
It uses more threads, and is less efficient. It is also crash prone. EIther the emulated program crashes, or the emulator itself crashes.
Rationale behind Changes
Vastly improved audio quality, performance, architecture, hardware emulation, and hardware compatibility.
Fixes crash issues related to sound threads.
Re-architectures the entire audio logic, for better performance and emulation accuracy.
Makes Dune, Lost Eden, Krondor, and so many other games sound as they should, for the PC Speaker, SB PCM, OPL3, OPL2, Dual OPL2, and Adlib Gold OPL.
Fixes #1513 #1514 #1464
Also fixes Where in Space is Carmen Sandiego from locking up, and so many game compatibility issues.
Status
The code is now 100% ported and 100% correct.
Audio is performant and fully cross platform. Works on Windows, Linux, MacOS, x86_64, and ARM64.
No native dependencies anymore. Pure C#. The audio backend code is ported from SDL 2 source code into C#.
See: https://github.com/OpenRakis/Spice86.Audio/tree/main/src/Spice86.Audio for details
All games I could test work fine.
The code is ready for review
Suggested Testing Steps
Test General MIDI, MT-32, PC Speaker, Sound Blaster PCM, Sound Blaster OPL, Sound Blaster variants (Sb16, Sb1, Sb2, SbPro1, SbPro2), sbmixer on/off and, Adlib Gold music (with surround module!) on whatever platform you want (Linux, Mac, Windows, x86_64, ARM64)
Except for MT-32 on MacOS ARM64 (which still lacks a native dependency), all devices on all platforms should be accurate, sound at it should sound, be fast, and overall just work.
(in Release build, that is! -- A 'problem' inherited from master branch is that sometimes Debug build can slow down on large video updates - this won't affect the Nuget package since it's compiled in Release mode with all compiler optimizations)