Major audio settings overhaul.

This commit is contained in:
Christian Speckner 2018-06-23 00:58:28 +02:00
parent d127865dee
commit ef5261689a
17 changed files with 505 additions and 66 deletions

View File

@ -53,6 +53,11 @@
"typeinfo": "cpp", "typeinfo": "cpp",
"__mutex_base": "cpp", "__mutex_base": "cpp",
"mutex": "cpp", "mutex": "cpp",
"condition_variable": "cpp" "condition_variable": "cpp",
"*.ins": "cpp",
"cstring": "cpp",
"iostream": "cpp",
"cstdint": "cpp",
"ostream": "cpp"
} }
} }

View File

@ -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<int>(AudioSettings::Preset::custom) &&
numericPreset <= static_cast<int>(AudioSettings::Preset::veryHighQualityVeryLowLag)
) ? static_cast<AudioSettings::Preset>(numericPreset) : AudioSettings::DEFAULT_PRESET;
}
AudioSettings::ResamplingQuality normalizeResamplingQuality(int numericResamplingQuality)
{
return (
numericResamplingQuality >= static_cast<int>(AudioSettings::ResamplingQuality::nearestNeightbour) &&
numericResamplingQuality <= static_cast<int>(AudioSettings::ResamplingQuality::lanczos_3)
) ? static_cast<AudioSettings::ResamplingQuality>(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<int>(preset) != settingPreset) settings.setValue(SETTING_PRESET, static_cast<int>(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<int>(resamplingQuality) != settingResamplingQuality)
settings.setValue(SETTING_RESAMPLING_QUALITY, static_cast<int>(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<int>(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<int>(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)));
}

View File

@ -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

View File

@ -50,6 +50,8 @@
#include "SoundNull.hxx" #include "SoundNull.hxx"
#endif #endif
class AudioSettings;
/** /**
This class deals with the different framebuffer/sound/event This class deals with the different framebuffer/sound/event
implementations for the various ports of Stella, and always returns a implementations for the various ports of Stella, and always returns a
@ -108,10 +110,10 @@ class MediaFactory
return make_unique<FrameBufferSDL2>(osystem); return make_unique<FrameBufferSDL2>(osystem);
} }
static unique_ptr<Sound> createAudio(OSystem& osystem) static unique_ptr<Sound> createAudio(OSystem& osystem, AudioSettings& audioSettings)
{ {
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
return make_unique<SoundSDL2>(osystem); return make_unique<SoundSDL2>(osystem, audioSettings);
#else #else
return make_unique<SoundNull>(osystem); return make_unique<SoundNull>(osystem);
#endif #endif

View File

@ -86,7 +86,7 @@ class SoundNull : public Sound
@param percent The new volume percentage level for the sound device @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. Adjusts the volume of the sound device based on the given direction.

View File

@ -30,16 +30,18 @@
#include "SoundSDL2.hxx" #include "SoundSDL2.hxx"
#include "AudioQueue.hxx" #include "AudioQueue.hxx"
#include "EmulationTiming.hxx" #include "EmulationTiming.hxx"
#include "AudioSettings.hxx"
#include "audio/SimpleResampler.hxx" #include "audio/SimpleResampler.hxx"
#include "audio/LanczosResampler.hxx" #include "audio/LanczosResampler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL2::SoundSDL2(OSystem& osystem) SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
: Sound(osystem), : Sound(osystem),
myIsInitializedFlag(false), myIsInitializedFlag(false),
myVolume(100), myVolume(100),
myVolumeFactor(0xffff), myVolumeFactor(0xffff),
myCurrentFragment(nullptr) myCurrentFragment(nullptr),
myAudioSettings(audioSettings)
{ {
myOSystem.logMessage("SoundSDL2::SoundSDL2 started ...", 2); 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, // This fixes a bug most prevalent with ATI video cards in Windows,
// whereby sound stopped working after the first video change // whereby sound stopped working after the first video change
SDL_AudioSpec desired; SDL_AudioSpec desired;
desired.freq = myOSystem.settings().getInt("freq"); desired.freq = myAudioSettings.sampleRate();
desired.format = AUDIO_F32SYS; desired.format = AUDIO_F32SYS;
desired.channels = 2; desired.channels = 2;
desired.samples = myOSystem.settings().getInt("fragsize"); desired.samples = myAudioSettings.fragmentSize();
desired.callback = callback; desired.callback = callback;
desired.userdata = static_cast<void*>(this); desired.userdata = static_cast<void*>(this);
@ -95,7 +97,7 @@ SoundSDL2::~SoundSDL2()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setEnabled(bool state) void SoundSDL2::setEnabled(bool state)
{ {
myOSystem.settings().setValue("sound", state); myAudioSettings.setEnabled(state);
myOSystem.logMessage(state ? "SoundSDL2::setEnabled(true)" : myOSystem.logMessage(state ? "SoundSDL2::setEnabled(true)" :
"SoundSDL2::setEnabled(false)", 2); "SoundSDL2::setEnabled(false)", 2);
@ -110,7 +112,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
myOSystem.logMessage("SoundSDL2::open started ...", 2); myOSystem.logMessage("SoundSDL2::open started ...", 2);
mute(true); mute(true);
if(!myOSystem.settings().getBool("sound")) if(!myAudioSettings.enabled())
{ {
myOSystem.logMessage("Sound disabled\n", 1); myOSystem.logMessage("Sound disabled\n", 1);
return; return;
@ -121,7 +123,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
myCurrentFragment = nullptr; myCurrentFragment = nullptr;
// Adjust volume to that defined in settings // Adjust volume to that defined in settings
setVolume(myOSystem.settings().getInt("volume")); setVolume(myAudioSettings.volume());
// Show some info // Show some info
ostringstream buf; 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; myVolume = percent;
SDL_LockAudio(); SDL_LockAudio();
@ -213,7 +215,7 @@ void SoundSDL2::adjustVolume(Int8 direction)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL2::getFragmentSize() const uInt32 SoundSDL2::getFragmentSize() const
{ {
return myHardwareSpec.size; return myHardwareSpec.samples;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -253,24 +255,26 @@ void SoundSDL2::initResampler()
Resampler::Format formatTo = Resampler::Format formatTo =
Resampler::Format(myHardwareSpec.freq, myHardwareSpec.samples, myHardwareSpec.channels > 1); Resampler::Format(myHardwareSpec.freq, myHardwareSpec.samples, myHardwareSpec.channels > 1);
int quality = myOSystem.settings().getInt("resampling.quality");
switch (quality) { switch (myAudioSettings.resamplingQuality()) {
case 1: case AudioSettings::ResamplingQuality::nearestNeightbour:
myResampler = make_unique<SimpleResampler>(formatFrom, formatTo, nextFragmentCallback); myResampler = make_unique<SimpleResampler>(formatFrom, formatTo, nextFragmentCallback);
(cerr << "resampling quality 1: using nearest neighbor resampling\n").flush(); (cerr << "resampling quality 1: using nearest neighbor resampling\n").flush();
break; break;
default: case AudioSettings::ResamplingQuality::lanczos_2:
case 2:
(cerr << "resampling quality 2: using nearest Lanczos resampling, a = 2\n").flush(); (cerr << "resampling quality 2: using nearest Lanczos resampling, a = 2\n").flush();
myResampler = make_unique<LanczosResampler>(formatFrom, formatTo, nextFragmentCallback, 2); myResampler = make_unique<LanczosResampler>(formatFrom, formatTo, nextFragmentCallback, 2);
break; break;
case 3: case AudioSettings::ResamplingQuality::lanczos_3:
(cerr << "resampling quality 3: using nearest Lanczos resampling, a = 3\n").flush(); (cerr << "resampling quality 3: using nearest Lanczos resampling, a = 3\n").flush();
myResampler = make_unique<LanczosResampler>(formatFrom, formatTo, nextFragmentCallback, 3); myResampler = make_unique<LanczosResampler>(formatFrom, formatTo, nextFragmentCallback, 3);
break; break;
default:
throw runtime_error("invalid resampling quality");
break;
} }
} }

View File

@ -23,6 +23,7 @@
class OSystem; class OSystem;
class AudioQueue; class AudioQueue;
class EmulationTiming; class EmulationTiming;
class AudioSettings;
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
@ -42,7 +43,7 @@ class SoundSDL2 : public Sound
Create a new sound object. The init method must be invoked before Create a new sound object. The init method must be invoked before
using the object. using the object.
*/ */
SoundSDL2(OSystem& osystem); SoundSDL2(OSystem& osystem, AudioSettings& audioSettings);
/** /**
Destructor Destructor
@ -88,7 +89,7 @@ class SoundSDL2 : public Sound
@param percent The new volume percentage level for the sound device @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. Adjusts the volume of the sound device based on the given direction.
@ -137,6 +138,8 @@ class SoundSDL2 : public Sound
unique_ptr<Resampler> myResampler; unique_ptr<Resampler> myResampler;
AudioSettings& myAudioSettings;
private: private:
// Callback function invoked by the SDL Audio library when it needs data // Callback function invoked by the SDL Audio library when it needs data
static void callback(void* udata, uInt8* stream, int len); static void callback(void* udata, uInt8* stream, int len);

View File

@ -16,7 +16,8 @@ MODULE_OBJS := \
src/common/SoundSDL2.o \ src/common/SoundSDL2.o \
src/common/StateManager.o \ src/common/StateManager.o \
src/common/ZipHandler.o \ src/common/ZipHandler.o \
src/common/AudioQueue.o src/common/AudioQueue.o \
src/common/AudioSettings.o
MODULE_DIRS += \ MODULE_DIRS += \
src/common src/common

View File

@ -56,6 +56,7 @@
#include "TIAConstants.hxx" #include "TIAConstants.hxx"
#include "FrameLayout.hxx" #include "FrameLayout.hxx"
#include "AudioQueue.hxx" #include "AudioQueue.hxx"
#include "AudioSettings.hxx"
#include "frame-manager/FrameManager.hxx" #include "frame-manager/FrameManager.hxx"
#include "frame-manager/FrameLayoutDetector.hxx" #include "frame-manager/FrameLayoutDetector.hxx"
#include "frame-manager/YStartDetector.hxx" #include "frame-manager/YStartDetector.hxx"
@ -76,7 +77,7 @@ namespace {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart, Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
const Properties& props) const Properties& props, AudioSettings& audioSettings)
: myOSystem(osystem), : myOSystem(osystem),
myEvent(osystem.eventHandler().event()), myEvent(osystem.eventHandler().event()),
myProperties(props), myProperties(props),
@ -85,7 +86,8 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
myCurrentFormat(0), // Unknown format @ start, myCurrentFormat(0), // Unknown format @ start,
myAutodetectedYstart(0), myAutodetectedYstart(0),
myUserPaletteDefined(false), myUserPaletteDefined(false),
myConsoleTiming(ConsoleTiming::ntsc) myConsoleTiming(ConsoleTiming::ntsc),
myAudioSettings(audioSettings)
{ {
// Load user-defined palette for this ROM // Load user-defined palette for this ROM
loadUserPalette(); loadUserPalette();
@ -553,8 +555,15 @@ void Console::initializeAudio()
{ {
myOSystem.sound().close(); myOSystem.sound().close();
myEmulationTiming.updatePlaybackPeriod(myOSystem.sound().getSampleRate()); myEmulationTiming
myEmulationTiming.updatePlaybackPeriod(myOSystem.sound().getFragmentSize()); .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(); createAudioQueue();
myTIA->setAudioQueue(myAudioQueue); myTIA->setAudioQueue(myAudioQueue);

View File

@ -28,6 +28,7 @@ class Cartridge;
class CompuMate; class CompuMate;
class Debugger; class Debugger;
class AudioQueue; class AudioQueue;
class AudioSettings;
#include "bspf.hxx" #include "bspf.hxx"
#include "Control.hxx" #include "Control.hxx"
@ -80,7 +81,7 @@ class Console : public Serializable
@param props The properties for the cartridge @param props The properties for the cartridge
*/ */
Console(OSystem& osystem, unique_ptr<Cartridge>& cart, Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
const Properties& props); const Properties& props, AudioSettings& audioSettings);
/** /**
Destructor Destructor
@ -423,9 +424,12 @@ class Console : public Serializable
ConsoleTiming myConsoleTiming; ConsoleTiming myConsoleTiming;
// Emulation timing provider. This ties together the timing of the core emulation loop // 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; EmulationTiming myEmulationTiming;
// The audio settings
AudioSettings& myAudioSettings;
// Table of RGB values for NTSC, PAL and SECAM // Table of RGB values for NTSC, PAL and SECAM
static uInt32 ourNTSCPalette[256]; static uInt32 ourNTSCPalette[256];
static uInt32 ourPALPalette[256]; static uInt32 ourPALPalette[256];

View File

@ -19,8 +19,6 @@
namespace { namespace {
constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1; 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) uInt32 discreteDivCeil(uInt32 n, uInt32 d)
{ {
@ -32,25 +30,44 @@ namespace {
EmulationTiming::EmulationTiming(FrameLayout frameLayout) : EmulationTiming::EmulationTiming(FrameLayout frameLayout) :
myFrameLayout(frameLayout), myFrameLayout(frameLayout),
myPlaybackRate(44100), myPlaybackRate(44100),
myPlaybackPeriod(512) myPlaybackPeriod(512),
myAudioQueueExtraFragments(1),
myAudioQueueHeadroom(2)
{} {}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EmulationTiming::updateFrameLayout(FrameLayout frameLayout) EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
{ {
myFrameLayout = frameLayout; myFrameLayout = frameLayout;
return *this;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EmulationTiming::updatePlaybackRate(uInt32 playbackRate) EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
{ {
myPlaybackRate = playbackRate; myPlaybackRate = playbackRate;
return *this;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod) EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
{ {
myPlaybackPeriod = 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()); 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 uInt32 EmulationTiming::prebufferFragmentCount() const
{ {
return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + PREBUFFER_EXTRA_FRAGMENT_COUNT; return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + myAudioQueueHeadroom;
} }

View File

@ -26,11 +26,15 @@ class EmulationTiming {
EmulationTiming(FrameLayout frameLayout = FrameLayout::ntsc); 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; uInt32 maxCyclesPerTimeslice() const;
@ -60,6 +64,9 @@ class EmulationTiming {
uInt32 myPlaybackPeriod; uInt32 myPlaybackPeriod;
uInt32 myAudioQueueExtraFragments;
uInt32 myAudioQueueHeadroom;
private: private:
EmulationTiming(const EmulationTiming&) = delete; EmulationTiming(const EmulationTiming&) = delete;

View File

@ -90,6 +90,7 @@ OSystem::OSystem()
myBuildInfo = info.str(); myBuildInfo = info.str();
mySettings = MediaFactory::createSettings(*this); mySettings = MediaFactory::createSettings(*this);
myAudioSettings = AudioSettings(mySettings.get());
myRandom = make_unique<Random>(*this); myRandom = make_unique<Random>(*this);
} }
@ -321,9 +322,9 @@ FBInitStatus OSystem::createFrameBuffer()
void OSystem::createSound() void OSystem::createSound()
{ {
if(!mySound) if(!mySound)
mySound = MediaFactory::createAudio(*this); mySound = MediaFactory::createAudio(*this, myAudioSettings);
#ifndef SOUND_SUPPORT #ifndef SOUND_SUPPORT
mySettings->setValue("sound", false); myAudioSettings.setEnabled(false);
#endif #endif
} }
@ -549,7 +550,7 @@ unique_ptr<Console> OSystem::openConsole(const FilesystemNode& romfile, string&
// Finally, create the cart with the correct properties // Finally, create the cart with the correct properties
if(cart) if(cart)
console = make_unique<Console>(*this, cart, props); console = make_unique<Console>(*this, cart, props, myAudioSettings);
} }
return console; return console;

View File

@ -44,6 +44,7 @@ class EmulationWorker;
#include "FrameBufferConstants.hxx" #include "FrameBufferConstants.hxx"
#include "EventHandlerConstants.hxx" #include "EventHandlerConstants.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "AudioSettings.hxx"
/** /**
This class provides an interface for accessing operating system specific This class provides an interface for accessing operating system specific
@ -481,6 +482,9 @@ class OSystem
// Indicates whether to stop the main loop // Indicates whether to stop the main loop
bool myQuitLoop; bool myQuitLoop;
// Audio settings
AudioSettings myAudioSettings;
private: private:
string myBaseDir; string myBaseDir;
string myStateDir; string myStateDir;

View File

@ -21,6 +21,7 @@
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Version.hxx" #include "Version.hxx"
#include "AudioSettings.hxx"
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
#include "DebuggerDialog.hxx" #include "DebuggerDialog.hxx"
@ -69,12 +70,14 @@ Settings::Settings(OSystem& osystem)
setInternal("tv.bleed", "0.0"); setInternal("tv.bleed", "0.0");
// Sound options // Sound options
setInternal("sound", "true"); setInternal(AudioSettings::SETTING_ENABLED, AudioSettings::DEFAULT_ENABLED);
setInternal("aud.mode", "balanced"); setInternal(AudioSettings::SETTING_PRESET, static_cast<int>(AudioSettings::DEFAULT_PRESET));
setInternal("fragsize", "512"); setInternal(AudioSettings::SETTING_SAMPLE_RATE, AudioSettings::DEFAULT_SAMPLE_RATE);
setInternal("freq", "44100"); setInternal(AudioSettings::SETTING_FRAGMENT_SIZE, AudioSettings::DEFAULT_FRAGMENT_SIZE);
setInternal("volume", "100"); setInternal(AudioSettings::SETTING_BUFFER_SIZE, AudioSettings::DEFAULT_BUFFER_SIZE);
setInternal("resampling.quality", "2"); setInternal(AudioSettings::SETTING_HEADROOM, AudioSettings::DEFAULT_HEADROOM);
setInternal(AudioSettings::SETTING_RESAMPLING_QUALITY, static_cast<int>(AudioSettings::DEFAULT_RESAMPLING_QUALITY));
setInternal(AudioSettings::SETTING_VOLUME, AudioSettings::DEFAULT_VOLUME);
// Input event options // Input event options
setInternal("keymap", ""); setInternal("keymap", "");
@ -361,13 +364,7 @@ void Settings::validate()
if(i < 0 || i > 6) setInternal("plr.tm.horizon", 5);*/ if(i < 0 || i > 6) setInternal("plr.tm.horizon", 5);*/
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
i = getInt("volume"); AudioSettings::normalize(*this);
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);
#endif #endif
i = getInt("joydeadzone"); i = getInt("joydeadzone");
@ -447,11 +444,14 @@ void Settings::usage() const
<< " -uimessages <1|0> Show onscreen UI messages for different events\n" << " -uimessages <1|0> Show onscreen UI messages for different events\n"
<< endl << endl
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
<< " -sound <1|0> Enable sound generation\n" << " -audio.enabled <1|0> Enable audio\n"
<< " -fragsize <number> The size of sound fragments (must be a power of two)\n" << " -audio.preset <1-5> Audio preset (or 1 for custom)\n"
<< " -freq <number> Set sound sample output frequency (44100|48000|96000)\n" << " -audio.sample_rate <number> Output sample rate (44100|48000|96000)\n"
<< " -resampling.quality <number> Resampling quality (1 -3), default: 2\n" << " -audio.fragment_size <number> Fragment size (128|256|512|1024|2048|4096)\n"
<< " -volume <number> Set the volume (0 - 100)\n" << " -audio.buffer_size <number> Max. number of additional half-frames to buffer (0 -- 20)\n"
<< " -audio.headroom <number> Additional half-frames to prebuffer (0 -- 20)\n"
<< " -audio.resampling_quality <1-3> Resampling quality\n"
<< " -audio.volume <number> Vokume (0 -- 100)\n"
<< endl << endl
#endif #endif
<< " -tia.zoom <zoom> Use the specified zoom level (windowed mode) for TIA image\n" << " -tia.zoom <zoom> Use the specified zoom level (windowed mode) for TIA image\n"

View File

@ -89,7 +89,7 @@ class Sound
@param percent The new volume percentage level for the sound device @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. Adjusts the volume of the sound device based on the given direction.

View File

@ -20,6 +20,4 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsWINDOWS::SettingsWINDOWS(OSystem& osystem) SettingsWINDOWS::SettingsWINDOWS(OSystem& osystem)
: Settings(osystem) : Settings(osystem)
{ {}
setInternal("fragsize", "1024");
}