From ef5261689a9cd87bc583e5f79eef8de3d0c509ae Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Sat, 23 Jun 2018 00:58:28 +0200 Subject: [PATCH] Major audio settings overhaul. --- .vscode/settings.json | 7 +- src/common/AudioSettings.cxx | 262 ++++++++++++++++++++++++++++++++ src/common/AudioSettings.hxx | 122 +++++++++++++++ src/common/MediaFactory.hxx | 6 +- src/common/SoundNull.hxx | 2 +- src/common/SoundSDL2.cxx | 38 ++--- src/common/SoundSDL2.hxx | 7 +- src/common/module.mk | 3 +- src/emucore/Console.cxx | 17 ++- src/emucore/Console.hxx | 8 +- src/emucore/EmulationTiming.cxx | 33 +++- src/emucore/EmulationTiming.hxx | 13 +- src/emucore/OSystem.cxx | 7 +- src/emucore/OSystem.hxx | 4 + src/emucore/Settings.cxx | 36 ++--- src/emucore/Sound.hxx | 2 +- src/windows/SettingsWINDOWS.cxx | 4 +- 17 files changed, 505 insertions(+), 66 deletions(-) create mode 100644 src/common/AudioSettings.cxx create mode 100644 src/common/AudioSettings.hxx diff --git a/.vscode/settings.json b/.vscode/settings.json index effb7653b..d271851f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,6 +53,11 @@ "typeinfo": "cpp", "__mutex_base": "cpp", "mutex": "cpp", - "condition_variable": "cpp" + "condition_variable": "cpp", + "*.ins": "cpp", + "cstring": "cpp", + "iostream": "cpp", + "cstdint": "cpp", + "ostream": "cpp" } } diff --git a/src/common/AudioSettings.cxx b/src/common/AudioSettings.cxx new file mode 100644 index 000000000..bdc2e2ead --- /dev/null +++ b/src/common/AudioSettings.cxx @@ -0,0 +1,262 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "AudioSettings.hxx" +#include "Settings.hxx" + +namespace { + uInt32 convertInt(int x, int defaultValue) + { + return x <= defaultValue ? defaultValue : x; + } + + AudioSettings::Preset normalizedPreset(int numericPreset) + { + return ( + numericPreset >= static_cast(AudioSettings::Preset::custom) && + numericPreset <= static_cast(AudioSettings::Preset::veryHighQualityVeryLowLag) + ) ? static_cast(numericPreset) : AudioSettings::DEFAULT_PRESET; + } + + AudioSettings::ResamplingQuality normalizeResamplingQuality(int numericResamplingQuality) + { + return ( + numericResamplingQuality >= static_cast(AudioSettings::ResamplingQuality::nearestNeightbour) && + numericResamplingQuality <= static_cast(AudioSettings::ResamplingQuality::lanczos_3) + ) ? static_cast(numericResamplingQuality) : AudioSettings::DEFAULT_RESAMPLING_QUALITY; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +AudioSettings::AudioSettings(Settings* settings) + : mySettings(settings) +{ + setPreset(normalizedPreset(mySettings->getInt(SETTING_PRESET))); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::normalize(Settings& settings) +{ + int settingPreset = settings.getInt(SETTING_PRESET); + Preset preset = normalizedPreset(settingPreset); + if (static_cast(preset) != settingPreset) settings.setValue(SETTING_PRESET, static_cast(DEFAULT_PRESET)); + + switch (settings.getInt(SETTING_SAMPLE_RATE)) { + case 44100: + case 48000: + case 96000: + break; + + default: + settings.setValue(SETTING_SAMPLE_RATE, DEFAULT_SAMPLE_RATE); + break; + } + + switch (settings.getInt(SETTING_FRAGMENT_SIZE)) { + case 128: + case 256: + case 512: + case 1024: + case 2048: + case 4096: + break; + + default: + settings.setValue(SETTING_FRAGMENT_SIZE, DEFAULT_FRAGMENT_SIZE); + break; + } + + int settingBufferSize = settings.getInt(SETTING_BUFFER_SIZE); + if (settingBufferSize < 0 || settingBufferSize > 20) settings.setValue(SETTING_BUFFER_SIZE, DEFAULT_BUFFER_SIZE); + + int settingHeadroom = settings.getInt(SETTING_HEADROOM); + if (settingHeadroom < 0 || settingHeadroom > 20) settings.setValue(SETTING_HEADROOM, DEFAULT_HEADROOM); + + int settingResamplingQuality = settings.getInt(SETTING_RESAMPLING_QUALITY); + ResamplingQuality resamplingQuality = normalizeResamplingQuality(settingResamplingQuality); + if (static_cast(resamplingQuality) != settingResamplingQuality) + settings.setValue(SETTING_RESAMPLING_QUALITY, static_cast(DEFAULT_RESAMPLING_QUALITY)); + + int settingVolume = settings.getInt(SETTING_VOLUME); + if (settingVolume < 0 || settingVolume > 100) settings.setValue(SETTING_VOLUME, DEFAULT_VOLUME); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +AudioSettings::Preset AudioSettings::preset() +{ + updatePresetFromSettings(); + return myPreset; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 AudioSettings::sampleRate() +{ + updatePresetFromSettings(); + return customSettings() ? convertInt(mySettings->getInt(SETTING_SAMPLE_RATE), DEFAULT_SAMPLE_RATE) : myPresetSampleRate; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 AudioSettings::fragmentSize() +{ + updatePresetFromSettings(); + return customSettings() ? convertInt(mySettings->getInt(SETTING_FRAGMENT_SIZE), DEFAULT_FRAGMENT_SIZE) : myPresetFragmentSize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 AudioSettings::bufferSize() +{ + updatePresetFromSettings(); + // 0 is a valid value -> keep it + return customSettings() ? convertInt(mySettings->getInt(SETTING_BUFFER_SIZE), 0) : myPresetBufferSize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 AudioSettings::headroom() +{ + updatePresetFromSettings(); + // 0 is a valid value -> keep it + return customSettings() ? convertInt(mySettings->getInt(SETTING_HEADROOM), 0) : myPresetHeadroom; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +AudioSettings::ResamplingQuality AudioSettings::resamplingQuality() +{ + updatePresetFromSettings(); + return customSettings() ? normalizeResamplingQuality(mySettings->getInt(SETTING_RESAMPLING_QUALITY)) : myPresetResamplingQuality; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 AudioSettings::volume() const +{ + // 0 is a valid value -> keep it + return convertInt(mySettings->getInt(SETTING_VOLUME), 0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool AudioSettings::enabled() const +{ + return mySettings->getBool(SETTING_ENABLED); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setPreset(AudioSettings::Preset preset) +{ + if (preset == myPreset) return; + myPreset = preset; + + switch (myPreset) { + case Preset::custom: + break; + + case Preset::lowQualityMediumLag: + myPresetSampleRate = 44100; + myPresetFragmentSize = 1024; + myPresetBufferSize = 4; + myPresetHeadroom = 3; + myPresetResamplingQuality = ResamplingQuality::nearestNeightbour; + break; + + case Preset::highQualityMediumLag: + myPresetSampleRate = 44100; + myPresetFragmentSize = 1024; + myPresetBufferSize = 4; + myPresetHeadroom = 3; + myPresetResamplingQuality = ResamplingQuality::lanczos_2; + break; + + case Preset::highQualityLowLag: + myPresetSampleRate = 48000; + myPresetFragmentSize = 512; + myPresetBufferSize = 2; + myPresetHeadroom = 1; + myPresetResamplingQuality = ResamplingQuality::lanczos_2; + break; + + case Preset::veryHighQualityVeryLowLag: + myPresetSampleRate = 96000; + myPresetFragmentSize = 128; + myPresetBufferSize = 0; + myPresetHeadroom = 0; + myPresetResamplingQuality = ResamplingQuality::lanczos_3; + break; + + default: + throw runtime_error("invalid preset"); + } + + mySettings->setValue(SETTING_PRESET, static_cast(myPreset)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setSampleRate(uInt32 sampleRate) +{ + mySettings->setValue(SETTING_SAMPLE_RATE, sampleRate); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setFragmentSize(uInt32 fragmentSize) +{ + mySettings->setValue(SETTING_FRAGMENT_SIZE, fragmentSize); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setBufferSize(uInt32 bufferSize) +{ + mySettings->setValue(SETTING_BUFFER_SIZE, bufferSize); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setHeadroom(uInt32 headroom) +{ + mySettings->setValue(SETTING_HEADROOM, headroom); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setResamplingQuality(AudioSettings::ResamplingQuality resamplingQuality) +{ + mySettings->setValue(SETTING_RESAMPLING_QUALITY, static_cast(resamplingQuality)); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setVolume(uInt32 volume) +{ + mySettings->setValue(SETTING_VOLUME, volume); + normalize(*mySettings); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::setEnabled(bool isEnabled) +{ + mySettings->setValue(SETTING_ENABLED, isEnabled); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool AudioSettings::customSettings() const +{ + return myPreset == Preset::custom; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AudioSettings::updatePresetFromSettings() +{ + setPreset(normalizedPreset(mySettings->getInt(SETTING_PRESET))); +} diff --git a/src/common/AudioSettings.hxx b/src/common/AudioSettings.hxx new file mode 100644 index 000000000..3435a344d --- /dev/null +++ b/src/common/AudioSettings.hxx @@ -0,0 +1,122 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef AUDIO_PARAMTERS_HXX +#define AUDIO_PARAMTERS_HXX + +#include "bspf.hxx" + +class Settings; + +class AudioSettings +{ + public: + + enum class Preset { + custom = 1, + lowQualityMediumLag = 2, + highQualityMediumLag = 3, + highQualityLowLag = 4, + veryHighQualityVeryLowLag = 5 + }; + + enum class ResamplingQuality { + nearestNeightbour = 1, + lanczos_2 = 2, + lanczos_3 = 3 + }; + + static constexpr const char* SETTING_PRESET = "audio.preset"; + static constexpr const char* SETTING_SAMPLE_RATE = "audio.sample_rate"; + static constexpr const char* SETTING_FRAGMENT_SIZE = "audio.fragment_size"; + static constexpr const char* SETTING_BUFFER_SIZE = "audio.buffer_size"; + static constexpr const char* SETTING_HEADROOM = "audio.headroom"; + static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality"; + static constexpr const char* SETTING_VOLUME = "audio.volume"; + static constexpr const char* SETTING_ENABLED = "audio.enabled"; + + static constexpr Preset DEFAULT_PRESET = Preset::highQualityMediumLag; + static constexpr uInt32 DEFAULT_SAMPLE_RATE = 44100; + static constexpr uInt32 DEFAULT_FRAGMENT_SIZE = 512; + static constexpr uInt32 DEFAULT_BUFFER_SIZE = 3; + static constexpr uInt32 DEFAULT_HEADROOM = 2; + static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2; + static constexpr uInt32 DEFAULT_VOLUME = 80; + static constexpr bool DEFAULT_ENABLED = true; + + public: + + AudioSettings() = default; + + AudioSettings(Settings* mySettings); + + static void initialize(Settings& settings); + + static void normalize(Settings& settings); + + Preset preset(); + + uInt32 sampleRate(); + + uInt32 fragmentSize(); + + uInt32 bufferSize(); + + uInt32 headroom(); + + ResamplingQuality resamplingQuality(); + + uInt32 volume() const; + + bool enabled() const; + + void setPreset(Preset preset); + + void setSampleRate(uInt32 sampleRate); + + void setFragmentSize(uInt32 fragmentSize); + + void setBufferSize(uInt32 bufferSize); + + void setHeadroom(uInt32 headroom); + + void setResamplingQuality(ResamplingQuality resamplingQuality); + + void setVolume(uInt32 volume); + + void setEnabled(bool isEnabled); + + private: + + bool customSettings() const; + + void updatePresetFromSettings(); + + private: + + Settings* mySettings; + + Preset myPreset; + + uInt32 myPresetSampleRate; + uInt32 myPresetFragmentSize; + uInt32 myPresetBufferSize; + uInt32 myPresetHeadroom; + ResamplingQuality myPresetResamplingQuality; +}; + +#endif // AUDIO_PARAMTERS_HXX diff --git a/src/common/MediaFactory.hxx b/src/common/MediaFactory.hxx index 1338b1ccd..708413967 100644 --- a/src/common/MediaFactory.hxx +++ b/src/common/MediaFactory.hxx @@ -50,6 +50,8 @@ #include "SoundNull.hxx" #endif +class AudioSettings; + /** This class deals with the different framebuffer/sound/event implementations for the various ports of Stella, and always returns a @@ -108,10 +110,10 @@ class MediaFactory return make_unique(osystem); } - static unique_ptr createAudio(OSystem& osystem) + static unique_ptr createAudio(OSystem& osystem, AudioSettings& audioSettings) { #ifdef SOUND_SUPPORT - return make_unique(osystem); + return make_unique(osystem, audioSettings); #else return make_unique(osystem); #endif diff --git a/src/common/SoundNull.hxx b/src/common/SoundNull.hxx index 21560c589..41bfbaef4 100644 --- a/src/common/SoundNull.hxx +++ b/src/common/SoundNull.hxx @@ -86,7 +86,7 @@ class SoundNull : public Sound @param percent The new volume percentage level for the sound device */ - void setVolume(Int32 percent) override { } + void setVolume(uInt32 percent) override { } /** Adjusts the volume of the sound device based on the given direction. diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx index a7f87caf9..fd27d432e 100644 --- a/src/common/SoundSDL2.cxx +++ b/src/common/SoundSDL2.cxx @@ -30,16 +30,18 @@ #include "SoundSDL2.hxx" #include "AudioQueue.hxx" #include "EmulationTiming.hxx" +#include "AudioSettings.hxx" #include "audio/SimpleResampler.hxx" #include "audio/LanczosResampler.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SoundSDL2::SoundSDL2(OSystem& osystem) +SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings) : Sound(osystem), myIsInitializedFlag(false), myVolume(100), myVolumeFactor(0xffff), - myCurrentFragment(nullptr) + myCurrentFragment(nullptr), + myAudioSettings(audioSettings) { myOSystem.logMessage("SoundSDL2::SoundSDL2 started ...", 2); @@ -48,10 +50,10 @@ SoundSDL2::SoundSDL2(OSystem& osystem) // This fixes a bug most prevalent with ATI video cards in Windows, // whereby sound stopped working after the first video change SDL_AudioSpec desired; - desired.freq = myOSystem.settings().getInt("freq"); + desired.freq = myAudioSettings.sampleRate(); desired.format = AUDIO_F32SYS; desired.channels = 2; - desired.samples = myOSystem.settings().getInt("fragsize"); + desired.samples = myAudioSettings.fragmentSize(); desired.callback = callback; desired.userdata = static_cast(this); @@ -95,7 +97,7 @@ SoundSDL2::~SoundSDL2() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void SoundSDL2::setEnabled(bool state) { - myOSystem.settings().setValue("sound", state); + myAudioSettings.setEnabled(state); myOSystem.logMessage(state ? "SoundSDL2::setEnabled(true)" : "SoundSDL2::setEnabled(false)", 2); @@ -110,7 +112,7 @@ void SoundSDL2::open(shared_ptr audioQueue, myOSystem.logMessage("SoundSDL2::open started ...", 2); mute(true); - if(!myOSystem.settings().getBool("sound")) + if(!myAudioSettings.enabled()) { myOSystem.logMessage("Sound disabled\n", 1); return; @@ -121,7 +123,7 @@ void SoundSDL2::open(shared_ptr audioQueue, myCurrentFragment = nullptr; // Adjust volume to that defined in settings - setVolume(myOSystem.settings().getInt("volume")); + setVolume(myAudioSettings.volume()); // Show some info ostringstream buf; @@ -171,11 +173,11 @@ void SoundSDL2::reset() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void SoundSDL2::setVolume(Int32 percent) +void SoundSDL2::setVolume(uInt32 percent) { - if(myIsInitializedFlag && (percent >= 0) && (percent <= 100)) + if(myIsInitializedFlag && (percent <= 100)) { - myOSystem.settings().setValue("volume", percent); + myAudioSettings.setVolume(percent); myVolume = percent; SDL_LockAudio(); @@ -213,7 +215,7 @@ void SoundSDL2::adjustVolume(Int8 direction) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 SoundSDL2::getFragmentSize() const { - return myHardwareSpec.size; + return myHardwareSpec.samples; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -253,24 +255,26 @@ void SoundSDL2::initResampler() Resampler::Format formatTo = Resampler::Format(myHardwareSpec.freq, myHardwareSpec.samples, myHardwareSpec.channels > 1); - int quality = myOSystem.settings().getInt("resampling.quality"); - switch (quality) { - case 1: + switch (myAudioSettings.resamplingQuality()) { + case AudioSettings::ResamplingQuality::nearestNeightbour: myResampler = make_unique(formatFrom, formatTo, nextFragmentCallback); (cerr << "resampling quality 1: using nearest neighbor resampling\n").flush(); break; - default: - case 2: + case AudioSettings::ResamplingQuality::lanczos_2: (cerr << "resampling quality 2: using nearest Lanczos resampling, a = 2\n").flush(); myResampler = make_unique(formatFrom, formatTo, nextFragmentCallback, 2); break; - case 3: + case AudioSettings::ResamplingQuality::lanczos_3: (cerr << "resampling quality 3: using nearest Lanczos resampling, a = 3\n").flush(); myResampler = make_unique(formatFrom, formatTo, nextFragmentCallback, 3); break; + + default: + throw runtime_error("invalid resampling quality"); + break; } } diff --git a/src/common/SoundSDL2.hxx b/src/common/SoundSDL2.hxx index 38156f79a..5ffdd63ef 100644 --- a/src/common/SoundSDL2.hxx +++ b/src/common/SoundSDL2.hxx @@ -23,6 +23,7 @@ class OSystem; class AudioQueue; class EmulationTiming; +class AudioSettings; #include "SDL_lib.hxx" @@ -42,7 +43,7 @@ class SoundSDL2 : public Sound Create a new sound object. The init method must be invoked before using the object. */ - SoundSDL2(OSystem& osystem); + SoundSDL2(OSystem& osystem, AudioSettings& audioSettings); /** Destructor @@ -88,7 +89,7 @@ class SoundSDL2 : public Sound @param percent The new volume percentage level for the sound device */ - void setVolume(Int32 percent) override; + void setVolume(uInt32 percent) override; /** Adjusts the volume of the sound device based on the given direction. @@ -137,6 +138,8 @@ class SoundSDL2 : public Sound unique_ptr myResampler; + AudioSettings& myAudioSettings; + private: // Callback function invoked by the SDL Audio library when it needs data static void callback(void* udata, uInt8* stream, int len); diff --git a/src/common/module.mk b/src/common/module.mk index e482cb9b9..4be025053 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -16,7 +16,8 @@ MODULE_OBJS := \ src/common/SoundSDL2.o \ src/common/StateManager.o \ src/common/ZipHandler.o \ - src/common/AudioQueue.o + src/common/AudioQueue.o \ + src/common/AudioSettings.o MODULE_DIRS += \ src/common diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 376548e02..0739588c7 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -56,6 +56,7 @@ #include "TIAConstants.hxx" #include "FrameLayout.hxx" #include "AudioQueue.hxx" +#include "AudioSettings.hxx" #include "frame-manager/FrameManager.hxx" #include "frame-manager/FrameLayoutDetector.hxx" #include "frame-manager/YStartDetector.hxx" @@ -76,7 +77,7 @@ namespace { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Console::Console(OSystem& osystem, unique_ptr& cart, - const Properties& props) + const Properties& props, AudioSettings& audioSettings) : myOSystem(osystem), myEvent(osystem.eventHandler().event()), myProperties(props), @@ -85,7 +86,8 @@ Console::Console(OSystem& osystem, unique_ptr& cart, myCurrentFormat(0), // Unknown format @ start, myAutodetectedYstart(0), myUserPaletteDefined(false), - myConsoleTiming(ConsoleTiming::ntsc) + myConsoleTiming(ConsoleTiming::ntsc), + myAudioSettings(audioSettings) { // Load user-defined palette for this ROM loadUserPalette(); @@ -553,8 +555,15 @@ void Console::initializeAudio() { myOSystem.sound().close(); - myEmulationTiming.updatePlaybackPeriod(myOSystem.sound().getSampleRate()); - myEmulationTiming.updatePlaybackPeriod(myOSystem.sound().getFragmentSize()); + myEmulationTiming + .updatePlaybackRate(myOSystem.sound().getSampleRate()) + .updatePlaybackPeriod(myOSystem.sound().getFragmentSize()) + .updateAudioQueueExtraFragments(myAudioSettings.bufferSize()) + .updateAudioQueueHeadroom(myAudioSettings.headroom()); + + (cout << "sample rate: " << myOSystem.sound().getSampleRate() << std::endl).flush(); + (cout << "fragment size: " << myOSystem.sound().getFragmentSize() << std::endl).flush(); + (cout << "prebuffer fragment count: " << myEmulationTiming.prebufferFragmentCount() << std::endl).flush(); createAudioQueue(); myTIA->setAudioQueue(myAudioQueue); diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index b10740467..0acb6b7e7 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -28,6 +28,7 @@ class Cartridge; class CompuMate; class Debugger; class AudioQueue; +class AudioSettings; #include "bspf.hxx" #include "Control.hxx" @@ -80,7 +81,7 @@ class Console : public Serializable @param props The properties for the cartridge */ Console(OSystem& osystem, unique_ptr& cart, - const Properties& props); + const Properties& props, AudioSettings& audioSettings); /** Destructor @@ -423,9 +424,12 @@ class Console : public Serializable ConsoleTiming myConsoleTiming; // Emulation timing provider. This ties together the timing of the core emulation loop - // and the audio synthesis parameters + // and the parameters that govern audio synthesis EmulationTiming myEmulationTiming; + // The audio settings + AudioSettings& myAudioSettings; + // Table of RGB values for NTSC, PAL and SECAM static uInt32 ourNTSCPalette[256]; static uInt32 ourPALPalette[256]; diff --git a/src/emucore/EmulationTiming.cxx b/src/emucore/EmulationTiming.cxx index 7f850d148..655be9cb7 100644 --- a/src/emucore/EmulationTiming.cxx +++ b/src/emucore/EmulationTiming.cxx @@ -19,8 +19,6 @@ namespace { constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1; - constexpr uInt32 QUEUE_CAPACITY_EXTRA_FRAGMENTS = 1; - constexpr uInt32 PREBUFFER_EXTRA_FRAGMENT_COUNT = 2; uInt32 discreteDivCeil(uInt32 n, uInt32 d) { @@ -32,25 +30,44 @@ namespace { EmulationTiming::EmulationTiming(FrameLayout frameLayout) : myFrameLayout(frameLayout), myPlaybackRate(44100), - myPlaybackPeriod(512) + myPlaybackPeriod(512), + myAudioQueueExtraFragments(1), + myAudioQueueHeadroom(2) {} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EmulationTiming::updateFrameLayout(FrameLayout frameLayout) +EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout) { myFrameLayout = frameLayout; + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EmulationTiming::updatePlaybackRate(uInt32 playbackRate) +EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate) { myPlaybackRate = playbackRate; + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod) +EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod) { myPlaybackPeriod = playbackPeriod; + return *this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments) +{ + myAudioQueueExtraFragments = audioQueueExtraFragments; + return *this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +EmulationTiming& EmulationTiming::updateAudioQueueHeadroom(uInt32 audioQueueHeadroom) +{ + myAudioQueueHeadroom = audioQueueHeadroom; + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -124,11 +141,11 @@ uInt32 EmulationTiming::audioQueueCapacity() const { uInt32 minCapacity = discreteDivCeil(maxCyclesPerTimeslice() * audioSampleRate(), audioFragmentSize() * cyclesPerSecond()); - return std::max(prebufferFragmentCount(), minCapacity) + QUEUE_CAPACITY_EXTRA_FRAGMENTS; + return std::max(prebufferFragmentCount(), minCapacity) + myAudioQueueExtraFragments; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 EmulationTiming::prebufferFragmentCount() const { - return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + PREBUFFER_EXTRA_FRAGMENT_COUNT; + return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + myAudioQueueHeadroom; } diff --git a/src/emucore/EmulationTiming.hxx b/src/emucore/EmulationTiming.hxx index 163df9088..8c6f68239 100644 --- a/src/emucore/EmulationTiming.hxx +++ b/src/emucore/EmulationTiming.hxx @@ -26,11 +26,15 @@ class EmulationTiming { EmulationTiming(FrameLayout frameLayout = FrameLayout::ntsc); - void updateFrameLayout(FrameLayout frameLayout); + EmulationTiming& updateFrameLayout(FrameLayout frameLayout); - void updatePlaybackRate(uInt32 playbackRate); + EmulationTiming& updatePlaybackRate(uInt32 playbackRate); - void updatePlaybackPeriod(uInt32 period); + EmulationTiming& updatePlaybackPeriod(uInt32 period); + + EmulationTiming& updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments); + + EmulationTiming& updateAudioQueueHeadroom(uInt32 audioQueueHeadroom); uInt32 maxCyclesPerTimeslice() const; @@ -60,6 +64,9 @@ class EmulationTiming { uInt32 myPlaybackPeriod; + uInt32 myAudioQueueExtraFragments; + uInt32 myAudioQueueHeadroom; + private: EmulationTiming(const EmulationTiming&) = delete; diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 87bb00905..5b05259a4 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -90,6 +90,7 @@ OSystem::OSystem() myBuildInfo = info.str(); mySettings = MediaFactory::createSettings(*this); + myAudioSettings = AudioSettings(mySettings.get()); myRandom = make_unique(*this); } @@ -321,9 +322,9 @@ FBInitStatus OSystem::createFrameBuffer() void OSystem::createSound() { if(!mySound) - mySound = MediaFactory::createAudio(*this); + mySound = MediaFactory::createAudio(*this, myAudioSettings); #ifndef SOUND_SUPPORT - mySettings->setValue("sound", false); + myAudioSettings.setEnabled(false); #endif } @@ -549,7 +550,7 @@ unique_ptr OSystem::openConsole(const FilesystemNode& romfile, string& // Finally, create the cart with the correct properties if(cart) - console = make_unique(*this, cart, props); + console = make_unique(*this, cart, props, myAudioSettings); } return console; diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index 85d23b9d6..981f3c729 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -44,6 +44,7 @@ class EmulationWorker; #include "FrameBufferConstants.hxx" #include "EventHandlerConstants.hxx" #include "bspf.hxx" +#include "AudioSettings.hxx" /** This class provides an interface for accessing operating system specific @@ -481,6 +482,9 @@ class OSystem // Indicates whether to stop the main loop bool myQuitLoop; + // Audio settings + AudioSettings myAudioSettings; + private: string myBaseDir; string myStateDir; diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 81433eef2..8c48494b6 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -21,6 +21,7 @@ #include "OSystem.hxx" #include "Version.hxx" +#include "AudioSettings.hxx" #ifdef DEBUGGER_SUPPORT #include "DebuggerDialog.hxx" @@ -69,12 +70,14 @@ Settings::Settings(OSystem& osystem) setInternal("tv.bleed", "0.0"); // Sound options - setInternal("sound", "true"); - setInternal("aud.mode", "balanced"); - setInternal("fragsize", "512"); - setInternal("freq", "44100"); - setInternal("volume", "100"); - setInternal("resampling.quality", "2"); + setInternal(AudioSettings::SETTING_ENABLED, AudioSettings::DEFAULT_ENABLED); + setInternal(AudioSettings::SETTING_PRESET, static_cast(AudioSettings::DEFAULT_PRESET)); + setInternal(AudioSettings::SETTING_SAMPLE_RATE, AudioSettings::DEFAULT_SAMPLE_RATE); + setInternal(AudioSettings::SETTING_FRAGMENT_SIZE, AudioSettings::DEFAULT_FRAGMENT_SIZE); + setInternal(AudioSettings::SETTING_BUFFER_SIZE, AudioSettings::DEFAULT_BUFFER_SIZE); + setInternal(AudioSettings::SETTING_HEADROOM, AudioSettings::DEFAULT_HEADROOM); + setInternal(AudioSettings::SETTING_RESAMPLING_QUALITY, static_cast(AudioSettings::DEFAULT_RESAMPLING_QUALITY)); + setInternal(AudioSettings::SETTING_VOLUME, AudioSettings::DEFAULT_VOLUME); // Input event options setInternal("keymap", ""); @@ -361,13 +364,7 @@ void Settings::validate() if(i < 0 || i > 6) setInternal("plr.tm.horizon", 5);*/ #ifdef SOUND_SUPPORT - i = getInt("volume"); - if(i < 0 || i > 100) setInternal("volume", "100"); - i = getInt("freq"); - if(!(i == 44100 || i == 48000 || i == 96000)) - setInternal("freq", "44100"); - i = getInt("resampling.quality"); - if (i < 1 || i > 3) setInternal("resampling.quality", 2); + AudioSettings::normalize(*this); #endif i = getInt("joydeadzone"); @@ -447,11 +444,14 @@ void Settings::usage() const << " -uimessages <1|0> Show onscreen UI messages for different events\n" << endl #ifdef SOUND_SUPPORT - << " -sound <1|0> Enable sound generation\n" - << " -fragsize The size of sound fragments (must be a power of two)\n" - << " -freq Set sound sample output frequency (44100|48000|96000)\n" - << " -resampling.quality Resampling quality (1 -3), default: 2\n" - << " -volume Set the volume (0 - 100)\n" + << " -audio.enabled <1|0> Enable audio\n" + << " -audio.preset <1-5> Audio preset (or 1 for custom)\n" + << " -audio.sample_rate Output sample rate (44100|48000|96000)\n" + << " -audio.fragment_size Fragment size (128|256|512|1024|2048|4096)\n" + << " -audio.buffer_size Max. number of additional half-frames to buffer (0 -- 20)\n" + << " -audio.headroom Additional half-frames to prebuffer (0 -- 20)\n" + << " -audio.resampling_quality <1-3> Resampling quality\n" + << " -audio.volume Vokume (0 -- 100)\n" << endl #endif << " -tia.zoom Use the specified zoom level (windowed mode) for TIA image\n" diff --git a/src/emucore/Sound.hxx b/src/emucore/Sound.hxx index 763926460..44e7b5db0 100644 --- a/src/emucore/Sound.hxx +++ b/src/emucore/Sound.hxx @@ -89,7 +89,7 @@ class Sound @param percent The new volume percentage level for the sound device */ - virtual void setVolume(Int32 percent) = 0; + virtual void setVolume(uInt32 percent) = 0; /** Adjusts the volume of the sound device based on the given direction. diff --git a/src/windows/SettingsWINDOWS.cxx b/src/windows/SettingsWINDOWS.cxx index 7820b0937..432509b3f 100644 --- a/src/windows/SettingsWINDOWS.cxx +++ b/src/windows/SettingsWINDOWS.cxx @@ -20,6 +20,4 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SettingsWINDOWS::SettingsWINDOWS(OSystem& osystem) : Settings(osystem) -{ - setInternal("fragsize", "1024"); -} +{}