added option to select audio device (resolves #682)

This commit is contained in:
thrust26 2020-07-27 09:43:31 +02:00
parent 598e039001
commit 75a74e0be5
13 changed files with 125 additions and 9 deletions

View File

@ -20,9 +20,9 @@
* Extended global hotkeys for debug options. * Extended global hotkeys for debug options.
* Added option to playback a game using the Time Machine * Added option to playback a game using the Time Machine.
* Allow taking snapshots from within Time Machine dialog * Allow taking snapshots from within Time Machine dialog.
* Added ability to access most files that Stella uses from within a ZIP * Added ability to access most files that Stella uses from within a ZIP
file. This includes the following: file. This includes the following:
@ -33,7 +33,9 @@
Basically, you are now able to put many files that Stella uses inside Basically, you are now able to put many files that Stella uses inside
one ZIP file, and distribute just that file. one ZIP file, and distribute just that file.
* Replaced "Re-disassemble" with "Disassemble @ current line" in debugger * Added option to select the audio device.
* Replaced "Re-disassemble" with "Disassemble @ current line" in debugger.
* Fix bug when taking fullscreen snapshots; the dimensions were sometimes * Fix bug when taking fullscreen snapshots; the dimensions were sometimes
cut off. cut off.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -2201,6 +2201,11 @@
<td>Set the volume.</td> <td>Set the volume.</td>
</tr> </tr>
<tr>
<td><pre>-audio.device &lt;number&gt;</pre></td>
<td>Set the audio device (0 = default).</td>
</tr>
<tr> <tr>
<td><pre>-audio.preset &lt;1 - 5&gt;</pre></td> <td><pre>-audio.preset &lt;1 - 5&gt;</pre></td>
<td>Set an audio preset. Numbers in sequence represent presets for <td>Set an audio preset. Numbers in sequence represent presets for
@ -3084,7 +3089,8 @@
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">CommandLine</a></th></tr> <tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">CommandLine</a></th></tr>
<tr><td>Enable audio</td><td>Self-explanatory</td><td>-audio.enabled</td></tr> <tr><td>Enable audio</td><td>Self-explanatory</td><td>-audio.enabled</td></tr>
<tr><td>Volume</td><td>Self-explanatory</td><td>-audio.volume</td></tr> <tr><td>Volume</td><td>Self-explanatory</td><td>-audio.volume</td></tr>
<tr><td>Mode</td><td>Select an audio preset or choose 'custom' for manual configuration</td><td>-audio.preset</td></tr> <tr><td>Device</td><td>Use the specified audio device.</td><td>-audio.device</td></tr>
<tr><td>Mode</td><td>Select an audio preset or choose 'Custom' for manual configuration.</td><td>-audio.preset</td></tr>
<tr><td>Fragment size</td><td>The number of samples in a single fragment processed by the audio driver. Smaller values mean less latency, but may lead to dropouts (depending on OS and hardware).</td><td>-audio.fragment_size</td></tr> <tr><td>Fragment size</td><td>The number of samples in a single fragment processed by the audio driver. Smaller values mean less latency, but may lead to dropouts (depending on OS and hardware).</td><td>-audio.fragment_size</td></tr>
<tr><td>Sample rate</td><td> <tr><td>Sample rate</td><td>
Output samples per second. Higher values reduce artifacts from resampling and decrease latency, Output samples per second. Higher values reduce artifacts from resampling and decrease latency,
@ -3097,7 +3103,7 @@
some games (notably Quadrun). some games (notably Quadrun).
</td><td>-audio.resampling_quality</td></tr> </td><td>-audio.resampling_quality</td></tr>
<tr><td>Headroom</td><td>Number of frames to buffer before playback starts. Higher values increase latency, but reduce the potential for dropouts.</td><td>-audio.headroom</td></tr> <tr><td>Headroom</td><td>Number of frames to buffer before playback starts. Higher values increase latency, but reduce the potential for dropouts.</td><td>-audio.headroom</td></tr>
<tr><td>Buffer size</td><td>Maximum size of the audio buffer. Higher values increase maximum latency, but reduce the potential for dropouts</td><td>-audio.buffer_size</td></tr> <tr><td>Buffer size</td><td>Maximum size of the audio buffer. Higher values increase maximum latency, but reduce the potential for dropouts.</td><td>-audio.buffer_size</td></tr>
<tr><td>Stereo for all ROMs</td><td>Enable stereo mode for all ROMs.</td><td>-audio.stereo</td></tr> <tr><td>Stereo for all ROMs</td><td>Enable stereo mode for all ROMs.</td><td>-audio.stereo</td></tr>
<tr><td>Pitfall II music pitch</td><td>Defines the pitch of Pitfall II music (which may vary between carts).</td><td>-audio.dpc_pitch</td></tr> <tr><td>Pitfall II music pitch</td><td>Defines the pitch of Pitfall II music (which may vary between carts).</td><td>-audio.dpc_pitch</td></tr>
</table> </table>

View File

@ -154,6 +154,12 @@ uInt32 AudioSettings::volume() const
return lboundInt(mySettings.getInt(SETTING_VOLUME), 0); return lboundInt(mySettings.getInt(SETTING_VOLUME), 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 AudioSettings::device() const
{
return mySettings.getInt(SETTING_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AudioSettings::enabled() const bool AudioSettings::enabled() const
{ {
@ -285,6 +291,14 @@ void AudioSettings::setVolume(uInt32 volume)
normalize(mySettings); normalize(mySettings);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setDevice(uInt32 device)
{
if(!myIsPersistent) return;
mySettings.setValue(SETTING_DEVICE, device);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setEnabled(bool isEnabled) void AudioSettings::setEnabled(bool isEnabled)
{ {

View File

@ -48,6 +48,7 @@ class AudioSettings
static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality"; static constexpr const char* SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
static constexpr const char* SETTING_STEREO = "audio.stereo"; static constexpr const char* SETTING_STEREO = "audio.stereo";
static constexpr const char* SETTING_VOLUME = "audio.volume"; static constexpr const char* SETTING_VOLUME = "audio.volume";
static constexpr const char* SETTING_DEVICE = "audio.device";
static constexpr const char* SETTING_ENABLED = "audio.enabled"; static constexpr const char* SETTING_ENABLED = "audio.enabled";
static constexpr const char* SETTING_DPC_PITCH = "audio.dpc_pitch"; static constexpr const char* SETTING_DPC_PITCH = "audio.dpc_pitch";
@ -59,6 +60,7 @@ class AudioSettings
static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2; static constexpr ResamplingQuality DEFAULT_RESAMPLING_QUALITY = ResamplingQuality::lanczos_2;
static constexpr bool DEFAULT_STEREO = false; static constexpr bool DEFAULT_STEREO = false;
static constexpr uInt32 DEFAULT_VOLUME = 80; static constexpr uInt32 DEFAULT_VOLUME = 80;
static constexpr uInt32 DEFAULT_DEVICE = 0;
static constexpr bool DEFAULT_ENABLED = true; static constexpr bool DEFAULT_ENABLED = true;
static constexpr uInt32 DEFAULT_DPC_PITCH = 20000; static constexpr uInt32 DEFAULT_DPC_PITCH = 20000;
@ -87,6 +89,8 @@ class AudioSettings
uInt32 volume() const; uInt32 volume() const;
uInt32 device() const;
bool enabled() const; bool enabled() const;
uInt32 dpcPitch() const; uInt32 dpcPitch() const;
@ -109,6 +113,8 @@ class AudioSettings
void setVolume(uInt32 volume); void setVolume(uInt32 volume);
void setDevice(uInt32 device);
void setEnabled(bool isEnabled); void setEnabled(bool isEnabled);
void setPersistent(bool isPersistent); void setPersistent(bool isPersistent);

View File

@ -118,7 +118,7 @@ void FrameBufferSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
Logger::debug(s.str()); Logger::debug(s.str());
s.str(""); s.str("");
lastRes = res.str(); lastRes = res.str();
s << lastRes << ": "; s << " " << lastRes << ": ";
} }
s << mode.refresh_rate << "Hz"; s << mode.refresh_rate << "Hz";
if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate) if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate)

View File

@ -101,6 +101,13 @@ class SoundNull : public Sound
*/ */
void adjustVolume(int direction = 1) override { } void adjustVolume(int direction = 1) override { }
/**
Sets the audio device.
@param device The number of the device to select (0 = default).
*/
void setDevice(uInt32 device) override { };
/** /**
This method is called to provide information about the sound device. This method is called to provide information about the sound device.
*/ */

View File

@ -56,6 +56,9 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
return; return;
} }
queryHardware(myDevices);
SDL_zero(myHardwareSpec); SDL_zero(myHardwareSpec);
if(!openDevice()) if(!openDevice())
return; return;
@ -76,6 +79,29 @@ SoundSDL2::~SoundSDL2()
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::queryHardware(VariantList& devices)
{
ASSERT_MAIN_THREAD;
int numDevices = SDL_GetNumAudioDevices(0);
// log the available audio devices
ostringstream s;
s << "Supported audio devices (" << numDevices << "):";
Logger::debug(s.str());
VarList::push_back(devices, "Default", 0);
for(int i = 0; i < numDevices; ++i) {
ostringstream ss;
ss << " " << i + 1 << ": " << SDL_GetAudioDeviceName(i, 0);
Logger::debug(ss.str());
VarList::push_back(devices, SDL_GetAudioDeviceName(i, 0), i + 1);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::openDevice() bool SoundSDL2::openDevice()
{ {
@ -91,7 +117,11 @@ bool SoundSDL2::openDevice()
if(myIsInitializedFlag) if(myIsInitializedFlag)
SDL_CloseAudioDevice(myDevice); SDL_CloseAudioDevice(myDevice);
myDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &myHardwareSpec,
myDeviceId = BSPF::clamp(myAudioSettings.device(), 0u, uInt32(myDevices.size() - 1));
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
myDevice = SDL_OpenAudioDevice(device, 0, &desired, &myHardwareSpec,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if(myDevice == 0) if(myDevice == 0)
@ -126,7 +156,8 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
// Do we need to re-open the sound device? // Do we need to re-open the sound device?
// Only do this when absolutely necessary // Only do this when absolutely necessary
if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) || if(myAudioSettings.sampleRate() != uInt32(myHardwareSpec.freq) ||
myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples)) myAudioSettings.fragmentSize() != uInt32(myHardwareSpec.samples) ||
myAudioSettings.device() != myDeviceId)
openDevice(); openDevice();
myEmulationTiming = emulationTiming; myEmulationTiming = emulationTiming;
@ -261,6 +292,7 @@ string SoundSDL2::about() const
ostringstream buf; ostringstream buf;
buf << "Sound enabled:" << endl buf << "Sound enabled:" << endl
<< " Volume: " << myVolume << "%" << endl << " Volume: " << myVolume << "%" << endl
<< " Device: " << myDevices.at(myDeviceId).first << endl
<< " Channels: " << uInt32(myHardwareSpec.channels) << " Channels: " << uInt32(myHardwareSpec.channels)
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl << (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << endl
<< " Preset: "; << " Preset: ";

View File

@ -108,6 +108,13 @@ class SoundSDL2 : public Sound
string about() const override; string about() const override;
protected: protected:
/**
This method is called to query the audio devices.
@param devices List of device names
*/
void queryHardware(VariantList& devices);
/** /**
Invoked by the sound callback to process the next sound fragment. Invoked by the sound callback to process the next sound fragment.
The stream is 16-bits (even though the callback is 8-bits), since The stream is 16-bits (even though the callback is 8-bits), since
@ -139,6 +146,8 @@ class SoundSDL2 : public Sound
// Audio specification structure // Audio specification structure
SDL_AudioSpec myHardwareSpec; SDL_AudioSpec myHardwareSpec;
uInt32 myDeviceId{0};
SDL_AudioDeviceID myDevice{0}; SDL_AudioDeviceID myDevice{0};
shared_ptr<AudioQueue> myAudioQueue; shared_ptr<AudioQueue> myAudioQueue;

View File

@ -80,6 +80,7 @@ Settings::Settings()
// Sound options // Sound options
setPermanent(AudioSettings::SETTING_ENABLED, AudioSettings::DEFAULT_ENABLED); setPermanent(AudioSettings::SETTING_ENABLED, AudioSettings::DEFAULT_ENABLED);
setPermanent(AudioSettings::SETTING_VOLUME, AudioSettings::DEFAULT_VOLUME); setPermanent(AudioSettings::SETTING_VOLUME, AudioSettings::DEFAULT_VOLUME);
setPermanent(AudioSettings::SETTING_DEVICE, AudioSettings::DEFAULT_DEVICE);
setPermanent(AudioSettings::SETTING_PRESET, static_cast<int>(AudioSettings::DEFAULT_PRESET)); setPermanent(AudioSettings::SETTING_PRESET, static_cast<int>(AudioSettings::DEFAULT_PRESET));
setPermanent(AudioSettings::SETTING_FRAGMENT_SIZE, AudioSettings::DEFAULT_FRAGMENT_SIZE); setPermanent(AudioSettings::SETTING_FRAGMENT_SIZE, AudioSettings::DEFAULT_FRAGMENT_SIZE);
setPermanent(AudioSettings::SETTING_SAMPLE_RATE, AudioSettings::DEFAULT_SAMPLE_RATE); setPermanent(AudioSettings::SETTING_SAMPLE_RATE, AudioSettings::DEFAULT_SAMPLE_RATE);
@ -426,6 +427,7 @@ void Settings::usage() const
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
<< " -audio.enabled <1|0> Enable audio\n" << " -audio.enabled <1|0> Enable audio\n"
<< " -audio.volume <0-100> Volume\n" << " -audio.volume <0-100> Volume\n"
<< " -audio.device <number> ID of the audio device (0 = default)\n"
<< " -audio.preset <1-5> Audio preset (or 1 for custom)\n" << " -audio.preset <1-5> Audio preset (or 1 for custom)\n"
<< " -audio.sample_rate <number> Output sample rate (44100|48000|96000)\n" << " -audio.sample_rate <number> Output sample rate (44100|48000|96000)\n"
<< " -audio.fragment_size <number> Fragment size (128|256|512|1024|\n" << " -audio.fragment_size <number> Fragment size (128|256|512|1024|\n"

View File

@ -97,10 +97,28 @@ class Sound
*/ */
virtual string about() const = 0; virtual string about() const = 0;
/**
Get the supported devices for the audio hardware.
@return An array of supported devices
*/
const VariantList& supportedDevices() const {return myDevices;}
protected:
/**
This method is called to query the audio devices.
@param devices List of device names
*/
virtual void queryHardware(VariantList& devices) = 0;
protected: protected:
// The OSystem for this sound object // The OSystem for this sound object
OSystem& myOSystem; OSystem& myOSystem;
// Supported device
VariantList myDevices;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
Sound() = delete; Sound() = delete;

View File

@ -68,7 +68,7 @@ VideoAudioDialog::VideoAudioDialog(OSystem& osystem, DialogContainer& parent,
// Set real dimensions // Set real dimensions
setSize(44 * fontWidth + HBORDER * 2 + PopUpWidget::dropDownWidth(font) * 2, setSize(44 * fontWidth + HBORDER * 2 + PopUpWidget::dropDownWidth(font) * 2,
_th + VGAP * 6 + lineHeight + 10 * (lineHeight + VGAP) + buttonHeight + VBORDER * 3, _th + VGAP * 3 + lineHeight + 11 * (lineHeight + VGAP) + buttonHeight + VBORDER * 3,
max_w, max_h); max_w, max_h);
// The tab widget // The tab widget
@ -365,6 +365,14 @@ void VideoAudioDialog::addAudioTab()
wid.push_back(myVolumeSlider); wid.push_back(myVolumeSlider);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
// Device
myDevicePopup = new PopUpWidget(myTab, _font, xpos, ypos,
_w - xpos - lwidth - HBORDER - PopUpWidget::dropDownWidth(_font) - 2, lineHeight,
instance().sound().supportedDevices(),
"Device", lwidth, kDeviceChanged);
wid.push_back(myDevicePopup);
ypos += lineHeight + VGAP;
// Mode // Mode
items.clear(); items.clear();
VarList::push_back(items, "Low quality, medium lag", static_cast<int>(AudioSettings::Preset::lowQualityMediumLag)); VarList::push_back(items, "Low quality, medium lag", static_cast<int>(AudioSettings::Preset::lowQualityMediumLag));
@ -542,6 +550,11 @@ void VideoAudioDialog::loadConfig()
// Volume // Volume
myVolumeSlider->setValue(audioSettings.volume()); myVolumeSlider->setValue(audioSettings.volume());
// Device
uInt32 deviceId = BSPF::clamp(audioSettings.device(), 0u,
uInt32(instance().sound().supportedDevices().size() - 1));
myDevicePopup->setSelected(deviceId);
// Stereo // Stereo
myStereoSoundCheckbox->setState(audioSettings.stereo()); myStereoSoundCheckbox->setState(audioSettings.stereo());
@ -666,6 +679,9 @@ void VideoAudioDialog::saveConfig()
audioSettings.setVolume(myVolumeSlider->getValue()); audioSettings.setVolume(myVolumeSlider->getValue());
instance().sound().setVolume(myVolumeSlider->getValue()); instance().sound().setVolume(myVolumeSlider->getValue());
// Device
audioSettings.setDevice(myDevicePopup->getSelected());
// Stereo // Stereo
audioSettings.setStereo(myStereoSoundCheckbox->getState()); audioSettings.setStereo(myStereoSoundCheckbox->getState());
@ -754,6 +770,7 @@ void VideoAudioDialog::setDefaults()
case 3: // Audio case 3: // Audio
mySoundEnableCheckbox->setState(AudioSettings::DEFAULT_ENABLED); mySoundEnableCheckbox->setState(AudioSettings::DEFAULT_ENABLED);
myVolumeSlider->setValue(AudioSettings::DEFAULT_VOLUME); myVolumeSlider->setValue(AudioSettings::DEFAULT_VOLUME);
myDevicePopup->setSelected(AudioSettings::DEFAULT_DEVICE);
myStereoSoundCheckbox->setState(AudioSettings::DEFAULT_STEREO); myStereoSoundCheckbox->setState(AudioSettings::DEFAULT_STEREO);
myDpcPitch->setValue(AudioSettings::DEFAULT_DPC_PITCH); myDpcPitch->setValue(AudioSettings::DEFAULT_DPC_PITCH);
myModePopup->setSelected(static_cast<int>(AudioSettings::DEFAULT_PRESET)); myModePopup->setSelected(static_cast<int>(AudioSettings::DEFAULT_PRESET));
@ -1061,6 +1078,7 @@ void VideoAudioDialog::updateEnabledState()
bool userMode = preset == AudioSettings::Preset::custom; bool userMode = preset == AudioSettings::Preset::custom;
myVolumeSlider->setEnabled(active); myVolumeSlider->setEnabled(active);
myDevicePopup->setEnabled(active);
myStereoSoundCheckbox->setEnabled(active); myStereoSoundCheckbox->setEnabled(active);
myModePopup->setEnabled(active); myModePopup->setEnabled(active);
// enable only for Pitfall II cart // enable only for Pitfall II cart

View File

@ -115,6 +115,7 @@ class VideoAudioDialog : public Dialog
// Audio // Audio
CheckboxWidget* mySoundEnableCheckbox{nullptr}; CheckboxWidget* mySoundEnableCheckbox{nullptr};
SliderWidget* myVolumeSlider{nullptr}; SliderWidget* myVolumeSlider{nullptr};
PopUpWidget* myDevicePopup{nullptr};
CheckboxWidget* myStereoSoundCheckbox{nullptr}; CheckboxWidget* myStereoSoundCheckbox{nullptr};
PopUpWidget* myModePopup{nullptr}; PopUpWidget* myModePopup{nullptr};
PopUpWidget* myFragsizePopup{nullptr}; PopUpWidget* myFragsizePopup{nullptr};
@ -149,6 +150,7 @@ class VideoAudioDialog : public Dialog
kScanlinesChanged = 'VDsc', kScanlinesChanged = 'VDsc',
kSoundEnableChanged = 'ADse', kSoundEnableChanged = 'ADse',
kDeviceChanged = 'ADdc',
kModeChanged = 'ADmc', kModeChanged = 'ADmc',
kHeadroomChanged = 'ADhc', kHeadroomChanged = 'ADhc',
kBufferSizeChanged = 'ADbc' kBufferSizeChanged = 'ADbc'