Allow sound sample rate/fragment size to change dynamically.

- Move sound debugging output to logging facilities
- This should fix issue #348, but more testing is required

Bumped version # for beta release.
This commit is contained in:
Stephen Anthony 2018-08-28 11:01:52 -02:30
parent 13b9bbe5a4
commit e5fb010631
7 changed files with 67 additions and 63 deletions

View File

@ -84,11 +84,6 @@ class SoundNull : public Sound
*/
uInt32 getSampleRate() const override { return 31400; }
/**
Reset the sound device.
*/
void reset() override { }
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside

View File

@ -54,32 +54,9 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
return;
}
// The sound system is opened only once per program run, to eliminate
// issues with opening and closing it multiple times
// 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 = myAudioSettings.sampleRate();
desired.format = AUDIO_F32SYS;
desired.channels = 2;
desired.samples = static_cast<Uint16>(myAudioSettings.fragmentSize());
desired.callback = callback;
desired.userdata = static_cast<void*>(this);
myDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &myHardwareSpec,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if(myDevice == 0)
{
ostringstream buf;
buf << "WARNING: Couldn't open SDL audio device! " << endl
<< " " << SDL_GetError() << endl;
myOSystem.logMessage(buf.str(), 0);
SDL_zero(myHardwareSpec);
if(!openDevice())
return;
}
myIsInitializedFlag = true;
mute(true);
@ -95,6 +72,35 @@ SoundSDL2::~SoundSDL2()
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::openDevice()
{
SDL_AudioSpec desired;
desired.freq = myAudioSettings.sampleRate();
desired.format = AUDIO_F32SYS;
desired.channels = 2;
desired.samples = static_cast<Uint16>(myAudioSettings.fragmentSize());
desired.callback = callback;
desired.userdata = static_cast<void*>(this);
if(myIsInitializedFlag)
SDL_CloseAudioDevice(myDevice);
myDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &myHardwareSpec,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if(myDevice == 0)
{
ostringstream buf;
buf << "WARNING: Couldn't open SDL audio device! " << endl
<< " " << SDL_GetError() << endl;
myOSystem.logMessage(buf.str(), 0);
return myIsInitializedFlag = false;
}
return myIsInitializedFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setEnabled(bool state)
{
@ -109,6 +115,12 @@ void SoundSDL2::setEnabled(bool state)
void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
EmulationTiming* emulationTiming)
{
// Do we need to re-open the sound device?
// Only do this when absolutely necessary
if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) ||
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples))
openDevice();
myEmulationTiming = emulationTiming;
myOSystem.logMessage("SoundSDL2::open started ...", 2);
@ -128,18 +140,32 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
// Adjust volume to that defined in settings
setVolume(myAudioSettings.volume());
initResampler();
// Show some info
ostringstream buf;
buf << "Sound enabled:" << endl
<< " Volume: " << myVolume << endl
<< " Frag size: " << uInt32(myHardwareSpec.samples) << endl
<< " Frequency: " << uInt32(myHardwareSpec.freq) << endl
<< " Channels: " << uInt32(myHardwareSpec.channels)
<< endl;
<< " Channels: " << uInt32(myHardwareSpec.channels) << endl
<< " Resampling: ";
switch (myAudioSettings.resamplingQuality()) {
case AudioSettings::ResamplingQuality::nearestNeightbour:
buf << "quality 1, nearest neighbor" << endl;
break;
case AudioSettings::ResamplingQuality::lanczos_2:
buf << "quality 2, nearest Lanczos (a = 2)" << endl;
break;
case AudioSettings::ResamplingQuality::lanczos_3:
buf << "quality 3, nearest Lanczos (a = 3)" << endl;
break;
default:
buf << "unknown resampler" << endl;
break;
}
myOSystem.logMessage(buf.str(), 1);
initResampler();
// And start the SDL sound subsystem ...
mute(false);
@ -158,7 +184,6 @@ void SoundSDL2::close()
myCurrentFragment = nullptr;
myOSystem.logMessage("SoundSDL2::close", 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -170,11 +195,6 @@ void SoundSDL2::mute(bool state)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::reset()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setVolume(uInt32 percent)
{
@ -262,16 +282,13 @@ void SoundSDL2::initResampler()
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;
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 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;

View File

@ -77,11 +77,6 @@ class SoundSDL2 : public Sound
*/
void mute(bool state) override;
/**
Reset the sound device.
*/
void reset() override;
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside
@ -115,6 +110,12 @@ class SoundSDL2 : public Sound
void processFragment(float* stream, uInt32 length);
private:
/**
The actual sound device is opened only when absolutely necessary.
Typically this will only happen once per program run, but it can also
happen dynamically when changing sample rate and/or fragment size.
*/
bool openDevice();
void initResampler();

View File

@ -27,7 +27,7 @@
#include "StateManager.hxx"
#define STATE_HEADER "05099000state"
#define STATE_HEADER "05099100state"
// #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -18,7 +18,7 @@
#ifndef VERSION_HXX
#define VERSION_HXX
#define STELLA_VERSION "6.0_pre1"
#define STELLA_BUILD "4434"
#define STELLA_VERSION "6.0_beta1"
#define STELLA_BUILD "4514"
#endif

View File

@ -589,16 +589,12 @@ void Console::initializeAudio()
myOSystem.sound().close();
myEmulationTiming
.updatePlaybackRate(myOSystem.sound().getSampleRate())
.updatePlaybackPeriod(myOSystem.sound().getFragmentSize())
.updatePlaybackRate(myAudioSettings.sampleRate())
.updatePlaybackPeriod(myAudioSettings.fragmentSize())
.updateAudioQueueExtraFragments(myAudioSettings.bufferSize())
.updateAudioQueueHeadroom(myAudioSettings.headroom())
.updateSpeedFactor(myOSystem.settings().getFloat("speed"));
(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

@ -34,7 +34,7 @@ class Sound
{
public:
/**
Create a new sound object. The init method must be invoked before
Create a new sound object. The open method must be invoked before
using the object.
*/
Sound(OSystem& osystem) : myOSystem(osystem) { }
@ -77,11 +77,6 @@ class Sound
*/
virtual uInt32 getSampleRate() const = 0;
/**
Reset the sound device.
*/
virtual void reset() = 0;
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside