First pass at separating Sound mute/pause functionality.

This will need lots of testing, since it touches many parts of the code.
This commit is contained in:
Stephen Anthony 2022-09-24 21:33:39 -02:30
parent 518f8eb5bd
commit 129ec5886a
9 changed files with 135 additions and 121 deletions

View File

@ -66,29 +66,39 @@ class SoundNull : public Sound
void close() override { }
/**
Set the mute state of the sound object. While muted no sound is played.
Sets the sound mute state; sound processing continues. When turned
off, sound volume is 0; when turned on, sound volume returns to
previously set level.
@param state Mutes sound if true, unmute if false
@return The previous (old) mute state
*/
bool mute(bool state) override { return true; }
void mute(bool state) override { }
/**
Toggles the sound mute state. While muted no sound is played.
@return The previous (old) mute state
Toggles the sound mute state; sound processing continues.
Switches between mute(true) and mute(false).
*/
bool toggleMute() override { return true; }
void toggleMute() override { }
/**
Set the pause state of the sound object. While paused, sound is
neither played nor processed (ie, the sound subsystem is temporarily
disabled).
@param state Pause sound if true, unpause if false
@return The previous (old) pause state
*/
bool pause(bool state) override { return false; }
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. Values outside
this range indicate that the volume shouldn't be changed at all.
volume is given as a range from 0 to 100 (0 indicates mute). Values
outside this range indicate that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
@param volume The new volume level for the sound device
*/
void setVolume(uInt32 percent) override { }
void setVolume(uInt32 volume) override { }
/**
Adjusts the volume of the sound device based on the given direction.
@ -102,7 +112,7 @@ class SoundNull : public Sound
@param device The number of the device to select (0 = default).
*/
void setDevice(uInt32 device) override { };
void setDevice(uInt32 device) override { }
/**
This method is called to provide information about the sound device.

View File

@ -63,10 +63,6 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
if(!openDevice())
return;
SoundSDL2::mute(true);
myMuteState = !audioSettings.enabled();
myWavVolumeFactor = myMuteState ? 0 : myVolumeFactor;
Logger::debug("SoundSDL2::SoundSDL2 initialized");
}
@ -127,7 +123,8 @@ bool SoundSDL2::openDevice()
if(myIsInitializedFlag)
SDL_CloseAudioDevice(myDevice);
myDeviceId = BSPF::clamp(myAudioSettings.device(), 0U, static_cast<uInt32>(myDevices.size() - 1));
myDeviceId = BSPF::clamp(myAudioSettings.device(), 0U,
static_cast<uInt32>(myDevices.size() - 1));
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
myDevice = SDL_OpenAudioDevice(device, 0, &desired, &myHardwareSpec,
@ -149,16 +146,9 @@ bool SoundSDL2::openDevice()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setEnabled(bool enable)
{
myAudioSettings.setEnabled(enable);
if(myAudioQueue)
myAudioQueue->ignoreOverflows(!enable);
// Set new mute state and resulting WAV data volume
myMuteState = !enable;
myWavVolumeFactor = myMuteState ? 0 : myVolumeFactor;
Logger::debug(enable ? "SoundSDL2::setEnabled(true)" :
"SoundSDL2::setEnabled(false)");
cerr << "setEnabled: " << enable << endl;
mute(!enable);
pause(!enable);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -178,8 +168,8 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
myWavSpeed = 262 * 60 * 2. / myEmulationTiming->audioSampleRate();
Logger::debug("SoundSDL2::open started ...");
mute(true);
myAudioSettings.setEnabled(true);
audioQueue->ignoreOverflows(!myAudioSettings.enabled());
if(!myAudioSettings.enabled())
{
@ -202,7 +192,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
Logger::info(myAboutString);
// And start the SDL sound subsystem ...
mute(myMuteState);
pause(false);
Logger::debug("SoundSDL2::open finished");
}
@ -213,9 +203,6 @@ void SoundSDL2::close()
if(!myIsInitializedFlag)
return;
// Mute and remember current mute state for 'open()'
myMuteState = mute(true);
if(myAudioQueue)
myAudioQueue->closeSink(myCurrentFragment);
myAudioQueue.reset();
@ -223,8 +210,36 @@ void SoundSDL2::close()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::mute(bool state)
void SoundSDL2::mute(bool state)
{
myAudioSettings.setEnabled(!state);
if(state)
{
SDL_LockAudioDevice(myDevice);
myVolumeFactor = 0;
SDL_UnlockAudioDevice(myDevice);
}
else
setVolume(myAudioSettings.volume());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::toggleMute()
{
const bool state = !myAudioSettings.enabled();
mute(!state);
string message = "Sound ";
message += state ? "unmuted" : "muted";
myOSystem.frameBuffer().showTextMessage(message);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::pause(bool state)
{
ASSERT_MAIN_THREAD;
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
if(myIsInitializedFlag)
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
@ -235,39 +250,14 @@ bool SoundSDL2::mute(bool state)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::toggleMute()
void SoundSDL2::setVolume(uInt32 volume)
{
const bool enabled = !myAudioSettings.enabled();
setEnabled(enabled);
myOSystem.console().initializeAudio();
// Adjust TIA sound to new mute state
myMuteState = !enabled;
mute(myMuteState);
// Make sure the current WAV file continues playing if it got stopped by 'mute()'
if(myWavDevice)
SDL_PauseAudioDevice(myWavDevice, 0);
string message = "Sound ";
message += enabled ? "unmuted" : "muted";
myOSystem.frameBuffer().showTextMessage(message);
return enabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setVolume(uInt32 percent)
{
if(myIsInitializedFlag && (percent <= 100))
if(myIsInitializedFlag && (volume <= 100))
{
myAudioSettings.setVolume(percent);
myVolume = percent;
myAudioSettings.setVolume(volume);
SDL_LockAudioDevice(myDevice);
myVolumeFactor = static_cast<float>(percent) / 100.F;
myWavVolumeFactor = myAudioSettings.enabled() ? myVolumeFactor : 0;
myVolumeFactor = myAudioSettings.enabled() ? static_cast<float>(volume) / 100.F : 0;
SDL_UnlockAudioDevice(myDevice);
}
}
@ -275,7 +265,7 @@ void SoundSDL2::setVolume(uInt32 percent)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::adjustVolume(int direction)
{
Int32 percent = myVolume;
Int32 percent = myAudioSettings.volume();
percent = BSPF::clamp(percent + direction * 2, 0, 100);
// Enable audio if it is currently disabled
@ -285,8 +275,6 @@ void SoundSDL2::adjustVolume(int direction)
{
setEnabled(true);
myOSystem.console().initializeAudio();
myMuteState = false;
mute(false);
}
setVolume(percent);
@ -301,7 +289,7 @@ string SoundSDL2::about() const
{
ostringstream buf;
buf << "Sound enabled:" << endl
<< " Volume: " << myVolume << "%" << endl
<< " Volume: " << myAudioSettings.volume() << "%" << endl
<< " Device: " << myDevices.at(myDeviceId).first << endl
<< " Channels: " << static_cast<uInt32>(myHardwareSpec.channels)
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl
@ -520,7 +508,7 @@ void SoundSDL2::wavCallback(void* udata, uInt8* stream, int len)
SDL_ConvertAudio(&cvt);
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, cvt.buf, myWavSpec.format, cvt.len_cvt,
SDL_MIX_MAXVOLUME * myWavVolumeFactor);
SDL_MIX_MAXVOLUME * myVolumeFactor);
}
else
{
@ -529,7 +517,7 @@ void SoundSDL2::wavCallback(void* udata, uInt8* stream, int len)
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, myWavPos, myWavSpec.format, len,
SDL_MIX_MAXVOLUME * myWavVolumeFactor);
SDL_MIX_MAXVOLUME * myVolumeFactor);
}
myWavPos += len;
myWavLen -= len;
@ -537,7 +525,7 @@ void SoundSDL2::wavCallback(void* udata, uInt8* stream, int len)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float SoundSDL2::myWavVolumeFactor = 0xffff;
float SoundSDL2::myVolumeFactor = 0.F;
SDL_AudioSpec SoundSDL2::myWavSpec; // audio output format
uInt8* SoundSDL2::myWavPos = nullptr; // pointer to the audio buffer to be played
uInt32 SoundSDL2::myWavLen = 0; // remaining length of the sample we have to play

View File

@ -62,7 +62,8 @@ class SoundSDL2 : public Sound
Initializes the sound device. This must be called before any
calls are made to derived methods.
*/
void open(shared_ptr<AudioQueue> audioQueue, EmulationTiming* emulationTiming) override;
void open(shared_ptr<AudioQueue> audioQueue,
EmulationTiming* emulationTiming) override;
/**
Should be called to close the sound device. Once called the sound
@ -71,29 +72,39 @@ class SoundSDL2 : public Sound
void close() override;
/**
Set the mute state of the sound object. While muted no sound is played.
Sets the sound mute state; sound processing continues. When turned
off, sound volume is 0; when turned on, sound volume returns to
previously set level.
@param state Mutes sound if true, unmute if false
@return The previous (old) mute state
*/
bool mute(bool state) override;
void mute(bool state) override;
/**
Toggles the sound mute state. While muted no sound is played.
@return The previous (old) mute state
Toggles the sound mute state; sound processing continues.
Switches between mute(true) and mute(false).
*/
bool toggleMute() override;
void toggleMute() override;
/**
Set the pause state of the sound object. While paused, sound is
neither played nor processed (ie, the sound subsystem is temporarily
disabled).
@param state Pause sound if true, unpause if false
@return The previous (old) pause state
*/
bool pause(bool state) 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
this range indicate that the volume shouldn't be changed at all.
volume is given as a range from 0 to 100 (0 indicates mute). Values
outside this range indicate that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
@param volume The new volume level for the sound device
*/
void setVolume(uInt32 percent) override;
void setVolume(uInt32 volume) override;
/**
Adjusts the volume of the sound device based on the given direction.
@ -163,12 +174,6 @@ class SoundSDL2 : public Sound
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag{false};
// Current volume as a percentage (0 - 100)
uInt32 myVolume{100};
float myVolumeFactor{0xffff};
// Current mute state, used to control WAV file sound
bool myMuteState{false};
// Audio specification structure
SDL_AudioSpec myHardwareSpec;
@ -188,15 +193,15 @@ class SoundSDL2 : public Sound
AudioSettings& myAudioSettings;
// WAV file sound variables
string myWavFilename{EmptyString};
string myWavFilename;
uInt32 myWavLength{0};
SDL_AudioDeviceID myWavDevice{0};
uInt8* myWavBuffer{nullptr};
static float myVolumeFactor; // Current volume level (0 - 100)
static double myWavSpeed;
static unique_ptr<uInt8[]> myWavCvtBuffer;
static uInt32 myWavCvtBufferSize;
static float myWavVolumeFactor;
static SDL_AudioSpec myWavSpec; // audio output format
static uInt8* myWavPos; // pointer to the audio buffer to be played
static uInt32 myWavLen; // remaining length of the sample we have to play

View File

@ -171,8 +171,8 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
const string& md5 = myProperties.get(PropType::Cart_MD5);
setControllers(md5);
// Mute audio and clear framebuffer while autodetection runs
myOSystem.sound().mute(true);
// Pause audio and clear framebuffer while autodetection runs
myOSystem.sound().pause(true);
myOSystem.frameBuffer().clear();
if(myDisplayFormat == "AUTO" || myOSystem.settings().getBool("rominfo"))

View File

@ -2567,7 +2567,7 @@ void EventHandler::enterMenuMode(EventHandlerState state)
#ifdef GUI_SUPPORT
setState(state);
myOverlay->reStack();
myOSystem.sound().mute(true);
myOSystem.sound().pause(true);
#endif
}
@ -2577,7 +2577,7 @@ void EventHandler::leaveMenuMode()
#ifdef GUI_SUPPORT
myOverlay->removeDialog(); // remove the base dialog from dialog stack
setState(EventHandlerState::EMULATION);
myOSystem.sound().mute(false);
myOSystem.sound().pause(false);
#endif
}
@ -2673,12 +2673,12 @@ void EventHandler::setState(EventHandlerState state)
{
case EventHandlerState::EMULATION:
case EventHandlerState::PLAYBACK:
myOSystem.sound().mute(false);
myOSystem.sound().pause(false);
enableTextEvents(false);
break;
case EventHandlerState::PAUSE:
myOSystem.sound().mute(true);
myOSystem.sound().pause(true);
enableTextEvents(false);
break;

View File

@ -521,8 +521,8 @@ void FrameBuffer::update(UpdateMode mode)
//frames = intervalFrames + std::sqrt(std::max(stateFrames - intervalFrames, 0));
frames = std::round(std::sqrt(stateFrames));
// Mute sound if saved states were removed or states are too far apart
myOSystem.sound().mute(stateFrames > intervalFrames ||
// Pause sound if saved states were removed or states are too far apart
myOSystem.sound().pause(stateFrames > intervalFrames ||
frames > static_cast<Int32>(myOSystem.audioSettings().bufferSize() / 2 + 1));
}
redraw |= success;
@ -534,7 +534,7 @@ void FrameBuffer::update(UpdateMode mode)
if(!success)
{
frames = 0;
myOSystem.sound().mute(true);
myOSystem.sound().pause(true);
myOSystem.eventHandler().enterMenuMode(EventHandlerState::TIMEMACHINE);
}
break; // EventHandlerState::PLAYBACK
@ -1253,8 +1253,8 @@ FBInitStatus FrameBuffer::applyVideoMode()
// Changing the video mode can take some time, during which the last
// sound played may get 'stuck'
// So we mute the sound until the operation completes
const bool oldMuteState = myOSystem.sound().mute(true);
// So we pause the sound until the operation completes
const bool oldPauseState = myOSystem.sound().pause(true);
FBInitStatus status = FBInitStatus::FailNotSupported;
if(myBackend->setVideoMode(mode,
@ -1287,7 +1287,7 @@ FBInitStatus FrameBuffer::applyVideoMode()
Logger::error("ERROR: Couldn't initialize video subsystem");
// Restore sound settings
myOSystem.sound().mute(oldMuteState);
myOSystem.sound().pause(oldPauseState);
return status;
}

View File

@ -62,29 +62,39 @@ class Sound
virtual void close() = 0;
/**
Set the mute state of the sound object. While muted no sound is played.
Sets the sound mute state; sound processing continues. When turned
off, sound volume is 0; when turned on, sound volume returns to
previously set level.
@param state Mutes sound if true, unmute if false
@return The previous (old) mute state
*/
virtual bool mute(bool state) = 0;
virtual void mute(bool state) = 0;
/**
Toggles the sound mute state. While muted no sound is played.
@return The previous (old) mute state
Toggles the sound mute state; sound processing continues.
Switches between mute(true) and mute(false).
*/
virtual bool toggleMute() = 0;
virtual void toggleMute() = 0;
/**
Set the pause state of the sound object. While paused, sound is
neither played nor processed (ie, the sound subsystem is temporarily
disabled).
@param state Pause sound if true, unpause if false
@return The previous (old) pause state
*/
virtual bool pause(bool state) = 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
this range indicate that the volume shouldn't be changed at all.
volume is given as a range from 0 to 100 (0 indicates mute). Values
outside this range indicate that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
@param volume The new volume level for the sound device
*/
virtual void setVolume(uInt32 percent) = 0;
virtual void setVolume(uInt32 volume) = 0;
/**
Adjusts the volume of the sound device based on the given direction.
@ -137,7 +147,6 @@ class Sound
*/
virtual void queryHardware(VariantList& devices) = 0;
protected:
// The OSystem for this sound object
OSystem& myOSystem;

View File

@ -487,7 +487,7 @@ void VideoAudioDialog::addAudioTab()
VarList::push_back(items, "Ultra quality, minimal lag", static_cast<int>(AudioSettings::Preset::ultraQualityMinimalLag));
VarList::push_back(items, "Custom", static_cast<int>(AudioSettings::Preset::custom));
myModePopup = new PopUpWidget(myTab, _font, xpos, ypos,
_font.getStringWidth("Ultry quality, minimal lag"), lineHeight,
_font.getStringWidth("Ultra quality, minimal lag"), lineHeight,
items, "Mode", lwidth, kModeChanged);
wid.push_back(myModePopup);
ypos += lineHeight + VGAP;
@ -1274,7 +1274,7 @@ void VideoAudioDialog::colorPalette()
if(instance().hasConsole())
{
const int order[2][NUM_CHROMA] =
static constexpr int order[2][NUM_CHROMA] =
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{0, 1, 2, 4, 6, 8, 10, 12, 13, 11, 9, 7, 5, 3, 14, 15}
@ -1312,7 +1312,8 @@ void VideoAudioDialog::updateEnabledState()
myStereoSoundCheckbox->setEnabled(active);
myModePopup->setEnabled(active);
// enable only for Pitfall II cart
myDpcPitch->setEnabled(active && instance().hasConsole() && instance().console().cartridge().name() == "CartridgeDPC");
myDpcPitch->setEnabled(active && instance().hasConsole() &&
instance().console().cartridge().name() == "CartridgeDPC");
myFragsizePopup->setEnabled(active && userMode);
myFreqPopup->setEnabled(active && userMode);

View File

@ -147,8 +147,9 @@ class SoundLIBRETRO : public Sound
void queryHardware(VariantList& devices) override { }
void setVolume(uInt32 percent) override { }
void adjustVolume(int direction = +1) override { }
bool mute(bool state) override { return !myIsInitializedFlag; }
bool toggleMute() override { return !myIsInitializedFlag; }
void mute(bool state) override { }
void toggleMute() override { }
bool pause(bool state) override { return !myIsInitializedFlag; }
string about() const override { return ""; }
private: