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",
"__mutex_base": "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"
#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<FrameBufferSDL2>(osystem);
}
static unique_ptr<Sound> createAudio(OSystem& osystem)
static unique_ptr<Sound> createAudio(OSystem& osystem, AudioSettings& audioSettings)
{
#ifdef SOUND_SUPPORT
return make_unique<SoundSDL2>(osystem);
return make_unique<SoundSDL2>(osystem, audioSettings);
#else
return make_unique<SoundNull>(osystem);
#endif

View File

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

View File

@ -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<void*>(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> 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> 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<SimpleResampler>(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<LanczosResampler>(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<LanczosResampler>(formatFrom, formatTo, nextFragmentCallback, 3);
break;
default:
throw runtime_error("invalid resampling quality");
break;
}
}

View File

@ -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<Resampler> 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);

View File

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

View File

@ -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<Cartridge>& 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<Cartridge>& 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);

View File

@ -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<Cartridge>& 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];

View File

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

View File

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

View File

@ -90,6 +90,7 @@ OSystem::OSystem()
myBuildInfo = info.str();
mySettings = MediaFactory::createSettings(*this);
myAudioSettings = AudioSettings(mySettings.get());
myRandom = make_unique<Random>(*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<Console> OSystem::openConsole(const FilesystemNode& romfile, string&
// Finally, create the cart with the correct properties
if(cart)
console = make_unique<Console>(*this, cart, props);
console = make_unique<Console>(*this, cart, props, myAudioSettings);
}
return console;

View File

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

View File

@ -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<int>(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<int>(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 <number> The size of sound fragments (must be a power of two)\n"
<< " -freq <number> Set sound sample output frequency (44100|48000|96000)\n"
<< " -resampling.quality <number> Resampling quality (1 -3), default: 2\n"
<< " -volume <number> 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 <number> Output sample rate (44100|48000|96000)\n"
<< " -audio.fragment_size <number> Fragment size (128|256|512|1024|2048|4096)\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
#endif
<< " -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
*/
virtual void setVolume(Int32 percent) = 0;
virtual void setVolume(uInt32 percent) = 0;
/**
Adjusts the volume of the sound device based on the given direction.

View File

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