mirror of https://github.com/stella-emu/stella.git
Merge branch 'master' into feature/filesystem
This commit is contained in:
commit
1419f2d538
|
@ -4068,10 +4068,10 @@
|
||||||
|
|
||||||
<p>Stella supports viewing images and ROM properties of the currently
|
<p>Stella supports viewing images and ROM properties of the currently
|
||||||
selected ROM in the ROM launcher. Image support is automatic, as long as your
|
selected ROM in the ROM launcher. Image support is automatic, as long as your
|
||||||
image directory contains snapshots in the appropriate format. The label
|
image directory contains any images in the appropriate format. The label (if
|
||||||
(if existing) and the number of matching snapshots are displayed under the
|
existing) and the number of matching images are displayed under the
|
||||||
current one. The mouse or the ROM Launcher hotkeys can be used to browse
|
current image. The mouse, the ROM Launcher hotkeys or the controller can be
|
||||||
multiple images or a ROM.</p>
|
used to browse multiple images of a ROM.</p>
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
<li>The images can have PNG or JPG format.</li>
|
<li>The images can have PNG or JPG format.</li>
|
||||||
|
|
|
@ -160,6 +160,16 @@
|
||||||
Button 2 or 6 close the menu without any action.
|
Button 2 or 6 close the menu without any action.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Button 1 + Left</td>
|
||||||
|
<td>-</td>
|
||||||
|
<td>Display previous image</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Button 1 + Right</td>
|
||||||
|
<td>-</td>
|
||||||
|
<td>Display next image</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Button 2</td>
|
<td>Button 2</td>
|
||||||
<td>SKILL P2</td>
|
<td>SKILL P2</td>
|
||||||
|
|
|
@ -74,11 +74,6 @@ SoundSDL2::~SoundSDL2()
|
||||||
if(!myIsInitializedFlag)
|
if(!myIsInitializedFlag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(myWavDevice)
|
|
||||||
{
|
|
||||||
SDL_CloseAudioDevice(myWavDevice);
|
|
||||||
SDL_FreeWAV(myWavBuffer);
|
|
||||||
}
|
|
||||||
SDL_CloseAudioDevice(myDevice);
|
SDL_CloseAudioDevice(myDevice);
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +160,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
|
||||||
openDevice();
|
openDevice();
|
||||||
|
|
||||||
myEmulationTiming = emulationTiming;
|
myEmulationTiming = emulationTiming;
|
||||||
myWavSpeed = 262 * 60 * 2. / myEmulationTiming->audioSampleRate();
|
myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
|
||||||
|
|
||||||
Logger::debug("SoundSDL2::open started ...");
|
Logger::debug("SoundSDL2::open started ...");
|
||||||
|
|
||||||
|
@ -214,11 +209,7 @@ void SoundSDL2::mute(bool state)
|
||||||
{
|
{
|
||||||
myAudioSettings.setEnabled(!state);
|
myAudioSettings.setEnabled(!state);
|
||||||
if(state)
|
if(state)
|
||||||
{
|
|
||||||
SDL_LockAudioDevice(myDevice);
|
|
||||||
myVolumeFactor = 0;
|
myVolumeFactor = 0;
|
||||||
SDL_UnlockAudioDevice(myDevice);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
setVolume(myAudioSettings.volume());
|
setVolume(myAudioSettings.volume());
|
||||||
}
|
}
|
||||||
|
@ -242,10 +233,10 @@ bool SoundSDL2::pause(bool state)
|
||||||
|
|
||||||
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
|
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
|
||||||
if(myIsInitializedFlag)
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
|
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
|
||||||
if(myWavDevice)
|
myWavHandler.pause(state);
|
||||||
SDL_PauseAudioDevice(myWavDevice, state ? 1 : 0);
|
}
|
||||||
|
|
||||||
return oldstate;
|
return oldstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +246,7 @@ void SoundSDL2::setVolume(uInt32 volume)
|
||||||
if(myIsInitializedFlag && (volume <= 100))
|
if(myIsInitializedFlag && (volume <= 100))
|
||||||
{
|
{
|
||||||
myAudioSettings.setVolume(volume);
|
myAudioSettings.setVolume(volume);
|
||||||
|
|
||||||
SDL_LockAudioDevice(myDevice);
|
|
||||||
myVolumeFactor = myAudioSettings.enabled() ? static_cast<float>(volume) / 100.F : 0;
|
myVolumeFactor = myAudioSettings.enabled() ? static_cast<float>(volume) / 100.F : 0;
|
||||||
SDL_UnlockAudioDevice(myDevice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,15 +324,6 @@ string SoundSDL2::about() const
|
||||||
return buf.str();
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::processFragment(float* stream, uInt32 length)
|
|
||||||
{
|
|
||||||
myResampler->fillFragment(stream, length);
|
|
||||||
|
|
||||||
for(uInt32 i = 0; i < length; ++i)
|
|
||||||
stream[i] *= myVolumeFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::initResampler()
|
void SoundSDL2::initResampler()
|
||||||
{
|
{
|
||||||
|
@ -395,12 +374,21 @@ void SoundSDL2::initResampler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::callback(void* udata, uInt8* stream, int len)
|
void SoundSDL2::callback(void* object, uInt8* stream, int len)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<SoundSDL2*>(udata);
|
auto* self = static_cast<SoundSDL2*>(object);
|
||||||
|
|
||||||
if(self->myAudioQueue)
|
if(self->myAudioQueue)
|
||||||
self->processFragment(reinterpret_cast<float*>(stream), len >> 2);
|
{
|
||||||
|
// The stream is 32-bit float (even though this callback is 8-bits), since
|
||||||
|
// the resampler and TIA audio subsystem always generate float samples
|
||||||
|
auto* s = reinterpret_cast<float*>(stream);
|
||||||
|
const uInt32 length = len >> 2;
|
||||||
|
self->myResampler->fillFragment(s, length);
|
||||||
|
|
||||||
|
for(uInt32 i = 0; i < length; ++i) // TODO - perhaps move into Resampler
|
||||||
|
s[i] *= SoundSDL2::myVolumeFactor;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
SDL_memset(stream, 0, len);
|
SDL_memset(stream, 0, len);
|
||||||
}
|
}
|
||||||
|
@ -409,128 +397,160 @@ void SoundSDL2::callback(void* udata, uInt8* stream, int len)
|
||||||
bool SoundSDL2::playWav(const string& fileName, const uInt32 position,
|
bool SoundSDL2::playWav(const string& fileName, const uInt32 position,
|
||||||
const uInt32 length)
|
const uInt32 length)
|
||||||
{
|
{
|
||||||
// Load WAV file
|
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
|
||||||
if(fileName != myWavFilename || myWavBuffer == nullptr)
|
|
||||||
{
|
|
||||||
if(myWavBuffer)
|
|
||||||
{
|
|
||||||
SDL_FreeWAV(myWavBuffer);
|
|
||||||
myWavBuffer = nullptr;
|
|
||||||
}
|
|
||||||
if(SDL_LoadWAV(fileName.c_str(), &myWavSpec, &myWavBuffer, &myWavLength) == nullptr)
|
|
||||||
return false;
|
|
||||||
// Set the callback function
|
|
||||||
myWavSpec.callback = wavCallback;
|
|
||||||
myWavSpec.userdata = nullptr;
|
|
||||||
//myWavSpec.samples = 4096; // decrease for smaller samples;
|
|
||||||
}
|
|
||||||
if(position > myWavLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
myWavFilename = fileName;
|
return myWavHandler.play(fileName, device, position, length);
|
||||||
|
|
||||||
myWavLen = length
|
|
||||||
? std::min(length, myWavLength - position)
|
|
||||||
: myWavLength;
|
|
||||||
myWavPos = myWavBuffer + position;
|
|
||||||
|
|
||||||
// Open audio device
|
|
||||||
if(!myWavDevice)
|
|
||||||
{
|
|
||||||
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
|
|
||||||
|
|
||||||
myWavDevice = SDL_OpenAudioDevice(device, 0, &myWavSpec, nullptr, 0);
|
|
||||||
if(!myWavDevice)
|
|
||||||
return false;
|
|
||||||
// Play audio
|
|
||||||
SDL_PauseAudioDevice(myWavDevice, 0);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::stopWav()
|
void SoundSDL2::stopWav()
|
||||||
{
|
{
|
||||||
if(myWavBuffer)
|
myWavHandler.stop();
|
||||||
{
|
|
||||||
// Clean up
|
|
||||||
myWavLen = 0;
|
|
||||||
SDL_CloseAudioDevice(myWavDevice);
|
|
||||||
myWavDevice = 0;
|
|
||||||
SDL_FreeWAV(myWavBuffer);
|
|
||||||
myWavBuffer = nullptr;
|
|
||||||
}
|
|
||||||
if(myWavCvtBuffer)
|
|
||||||
{
|
|
||||||
myWavCvtBuffer.reset();
|
|
||||||
myWavCvtBufferSize = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 SoundSDL2::wavSize() const
|
uInt32 SoundSDL2::wavSize() const
|
||||||
{
|
{
|
||||||
return myWavBuffer ? myWavLen : 0;
|
return myWavHandler.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::wavCallback(void* udata, uInt8* stream, int len)
|
bool SoundSDL2::WavHandlerSDL2::play(
|
||||||
|
const string& fileName, const char* device,
|
||||||
|
const uInt32 position, const uInt32 length
|
||||||
|
)
|
||||||
{
|
{
|
||||||
SDL_memset(stream, myWavSpec.silence, len);
|
// Load WAV file
|
||||||
if(myWavLen)
|
if(fileName != myFilename || myBuffer == nullptr)
|
||||||
{
|
{
|
||||||
if(myWavSpeed != 1.0)
|
if(myBuffer)
|
||||||
{
|
{
|
||||||
const int origLen = len;
|
SDL_FreeWAV(myBuffer);
|
||||||
len = std::round(len / myWavSpeed);
|
myBuffer = nullptr;
|
||||||
const int newFreq =
|
|
||||||
std::round(static_cast<double>(myWavSpec.freq) * origLen / len);
|
|
||||||
|
|
||||||
if(static_cast<uInt32>(len) > myWavLen)
|
|
||||||
len = myWavLen;
|
|
||||||
|
|
||||||
SDL_AudioCVT cvt;
|
|
||||||
SDL_BuildAudioCVT(&cvt, myWavSpec.format, myWavSpec.channels, myWavSpec.freq,
|
|
||||||
myWavSpec.format, myWavSpec.channels, newFreq);
|
|
||||||
SDL_assert(cvt.needed); // Obviously, this one is always needed.
|
|
||||||
cvt.len = len * myWavSpec.channels; // Mono 8 bit sample frames
|
|
||||||
|
|
||||||
if(!myWavCvtBuffer ||
|
|
||||||
myWavCvtBufferSize < static_cast<uInt32>(cvt.len * cvt.len_mult))
|
|
||||||
{
|
|
||||||
myWavCvtBufferSize = cvt.len * cvt.len_mult;
|
|
||||||
myWavCvtBuffer = make_unique<uInt8[]>(myWavCvtBufferSize);
|
|
||||||
}
|
|
||||||
cvt.buf = myWavCvtBuffer.get();
|
|
||||||
|
|
||||||
// Read original data into conversion buffer
|
|
||||||
SDL_memcpy(cvt.buf, myWavPos, cvt.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 * myVolumeFactor);
|
|
||||||
}
|
}
|
||||||
else
|
SDL_zero(mySpec);
|
||||||
{
|
if(SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength) == nullptr)
|
||||||
if(static_cast<uInt32>(len) > myWavLen)
|
return false;
|
||||||
len = myWavLen;
|
|
||||||
|
|
||||||
// Mix volume adjusted WAV data into silent buffer
|
// Set the callback function
|
||||||
SDL_MixAudioFormat(stream, myWavPos, myWavSpec.format, len,
|
mySpec.callback = callback;
|
||||||
SDL_MIX_MAXVOLUME * myVolumeFactor);
|
mySpec.userdata = this;
|
||||||
}
|
}
|
||||||
myWavPos += len;
|
if(position > myLength)
|
||||||
myWavLen -= len;
|
return false;
|
||||||
|
|
||||||
|
myFilename = fileName;
|
||||||
|
|
||||||
|
myRemaining = length
|
||||||
|
? std::min(length, myLength - position)
|
||||||
|
: myLength;
|
||||||
|
myPos = myBuffer + position;
|
||||||
|
|
||||||
|
// Open audio device
|
||||||
|
if(!myDevice)
|
||||||
|
{
|
||||||
|
myDevice = SDL_OpenAudioDevice(device, 0, &mySpec, nullptr, 0);
|
||||||
|
if(!myDevice)
|
||||||
|
return false;
|
||||||
|
// Play audio
|
||||||
|
SDL_PauseAudioDevice(myDevice, 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL2::WavHandlerSDL2::stop()
|
||||||
|
{
|
||||||
|
if(myBuffer)
|
||||||
|
{
|
||||||
|
// Clean up
|
||||||
|
myRemaining = 0;
|
||||||
|
SDL_CloseAudioDevice(myDevice); myDevice = 0;
|
||||||
|
SDL_FreeWAV(myBuffer); myBuffer = nullptr;
|
||||||
|
}
|
||||||
|
if(myCvtBuffer)
|
||||||
|
{
|
||||||
|
myCvtBuffer.reset();
|
||||||
|
myCvtBufferSize = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL2::WavHandlerSDL2::processWav(uInt8* stream, uInt32 len)
|
||||||
|
{
|
||||||
|
SDL_memset(stream, mySpec.silence, len);
|
||||||
|
if(myRemaining)
|
||||||
|
{
|
||||||
|
if(mySpeed != 1.0)
|
||||||
|
{
|
||||||
|
const int origLen = len;
|
||||||
|
len = std::round(len / mySpeed);
|
||||||
|
const int newFreq =
|
||||||
|
std::round(static_cast<double>(mySpec.freq) * origLen / len);
|
||||||
|
|
||||||
|
if(static_cast<uInt32>(len) > myRemaining)
|
||||||
|
len = myRemaining;
|
||||||
|
|
||||||
|
SDL_AudioCVT cvt;
|
||||||
|
SDL_BuildAudioCVT(&cvt, mySpec.format, mySpec.channels, mySpec.freq,
|
||||||
|
mySpec.format, mySpec.channels, newFreq);
|
||||||
|
SDL_assert(cvt.needed); // Obviously, this one is always needed.
|
||||||
|
cvt.len = len * mySpec.channels; // Mono 8 bit sample frames
|
||||||
|
|
||||||
|
if(!myCvtBuffer ||
|
||||||
|
myCvtBufferSize < static_cast<uInt32>(cvt.len * cvt.len_mult))
|
||||||
|
{
|
||||||
|
myCvtBufferSize = cvt.len * cvt.len_mult;
|
||||||
|
myCvtBuffer = make_unique<uInt8[]>(myCvtBufferSize);
|
||||||
|
}
|
||||||
|
cvt.buf = myCvtBuffer.get();
|
||||||
|
|
||||||
|
// Read original data into conversion buffer
|
||||||
|
SDL_memcpy(cvt.buf, myPos, cvt.len);
|
||||||
|
SDL_ConvertAudio(&cvt);
|
||||||
|
// Mix volume adjusted WAV data into silent buffer
|
||||||
|
SDL_MixAudioFormat(stream, cvt.buf, mySpec.format, cvt.len_cvt,
|
||||||
|
SDL_MIX_MAXVOLUME * SoundSDL2::myVolumeFactor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(static_cast<uInt32>(len) > myRemaining)
|
||||||
|
len = myRemaining;
|
||||||
|
|
||||||
|
// Mix volume adjusted WAV data into silent buffer
|
||||||
|
SDL_MixAudioFormat(stream, myPos, mySpec.format, len,
|
||||||
|
SDL_MIX_MAXVOLUME * myVolumeFactor);
|
||||||
|
}
|
||||||
|
myPos += len;
|
||||||
|
myRemaining -= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL2::WavHandlerSDL2::callback(void* object, uInt8* stream, int len)
|
||||||
|
{
|
||||||
|
static_cast<WavHandlerSDL2*>(object)->processWav(
|
||||||
|
stream, static_cast<uInt32>(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SoundSDL2::WavHandlerSDL2::~WavHandlerSDL2()
|
||||||
|
{
|
||||||
|
if(myDevice)
|
||||||
|
{
|
||||||
|
SDL_CloseAudioDevice(myDevice);
|
||||||
|
SDL_FreeWAV(myBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL2::WavHandlerSDL2::pause(bool state) const
|
||||||
|
{
|
||||||
|
if(myDevice)
|
||||||
|
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
float SoundSDL2::myVolumeFactor = 0.F;
|
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
|
|
||||||
double SoundSDL2::myWavSpeed = 1.0;
|
|
||||||
unique_ptr<uInt8[]> SoundSDL2::myWavCvtBuffer;
|
|
||||||
uInt32 SoundSDL2::myWavCvtBufferSize = 0;
|
|
||||||
|
|
||||||
#endif // SOUND_SUPPORT
|
#endif // SOUND_SUPPORT
|
||||||
|
|
|
@ -125,7 +125,7 @@ class SoundSDL2 : public Sound
|
||||||
@param position The position to start playing
|
@param position The position to start playing
|
||||||
@param length The played length
|
@param length The played length
|
||||||
|
|
||||||
@return True, if the WAV file can be played
|
@return True if the WAV file can be played, else false
|
||||||
*/
|
*/
|
||||||
bool playWav(const string& fileName, const uInt32 position = 0,
|
bool playWav(const string& fileName, const uInt32 position = 0,
|
||||||
const uInt32 length = 0) override;
|
const uInt32 length = 0) override;
|
||||||
|
@ -142,7 +142,7 @@ class SoundSDL2 : public Sound
|
||||||
*/
|
*/
|
||||||
uInt32 wavSize() const override;
|
uInt32 wavSize() const override;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
/**
|
/**
|
||||||
This method is called to query the audio devices.
|
This method is called to query the audio devices.
|
||||||
|
|
||||||
|
@ -150,17 +150,6 @@ class SoundSDL2 : public Sound
|
||||||
*/
|
*/
|
||||||
void queryHardware(VariantList& devices) override;
|
void queryHardware(VariantList& devices) override;
|
||||||
|
|
||||||
/**
|
|
||||||
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 TIASnd class always generates signed 16-bit stereo samples.
|
|
||||||
|
|
||||||
@param stream Pointer to the start of the fragment
|
|
||||||
@param length Length of the fragment
|
|
||||||
*/
|
|
||||||
void processFragment(float* stream, uInt32 length);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
/**
|
||||||
The actual sound device is opened only when absolutely necessary.
|
The actual sound device is opened only when absolutely necessary.
|
||||||
Typically this will only happen once per program run, but it can also
|
Typically this will only happen once per program run, but it can also
|
||||||
|
@ -171,47 +160,75 @@ class SoundSDL2 : public Sound
|
||||||
void initResampler();
|
void initResampler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
AudioSettings& myAudioSettings;
|
||||||
|
|
||||||
// Indicates if the sound device was successfully initialized
|
// Indicates if the sound device was successfully initialized
|
||||||
bool myIsInitializedFlag{false};
|
bool myIsInitializedFlag{false};
|
||||||
|
|
||||||
// Audio specification structure
|
// Audio specification structure
|
||||||
SDL_AudioSpec myHardwareSpec;
|
SDL_AudioSpec myHardwareSpec;
|
||||||
|
|
||||||
|
SDL_AudioDeviceID myDevice{0};
|
||||||
uInt32 myDeviceId{0};
|
uInt32 myDeviceId{0};
|
||||||
|
|
||||||
SDL_AudioDeviceID myDevice{0};
|
|
||||||
|
|
||||||
shared_ptr<AudioQueue> myAudioQueue;
|
shared_ptr<AudioQueue> myAudioQueue;
|
||||||
|
unique_ptr<Resampler> myResampler;
|
||||||
|
|
||||||
EmulationTiming* myEmulationTiming{nullptr};
|
EmulationTiming* myEmulationTiming{nullptr};
|
||||||
|
|
||||||
Int16* myCurrentFragment{nullptr};
|
Int16* myCurrentFragment{nullptr};
|
||||||
bool myUnderrun{false};
|
bool myUnderrun{false};
|
||||||
|
|
||||||
unique_ptr<Resampler> myResampler;
|
string myAboutString;
|
||||||
|
|
||||||
AudioSettings& myAudioSettings;
|
/**
|
||||||
|
This class implements WAV file playback using the SDL2 sound API.
|
||||||
|
*/
|
||||||
|
class WavHandlerSDL2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit WavHandlerSDL2() = default;
|
||||||
|
~WavHandlerSDL2();
|
||||||
|
|
||||||
// WAV file sound variables
|
bool play(const string& fileName, const char* device,
|
||||||
string myWavFilename;
|
const uInt32 position, const uInt32 length);
|
||||||
uInt32 myWavLength{0};
|
void stop();
|
||||||
SDL_AudioDeviceID myWavDevice{0};
|
uInt32 size() const { return myBuffer ? myRemaining : 0; }
|
||||||
uInt8* myWavBuffer{nullptr};
|
|
||||||
|
void setSpeed(const double speed) { mySpeed = speed; }
|
||||||
|
void pause(bool state) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
string myFilename;
|
||||||
|
uInt32 myLength{0};
|
||||||
|
SDL_AudioDeviceID myDevice{0};
|
||||||
|
uInt8* myBuffer{nullptr};
|
||||||
|
double mySpeed{1.0};
|
||||||
|
unique_ptr<uInt8[]> myCvtBuffer;
|
||||||
|
uInt32 myCvtBufferSize{0};
|
||||||
|
SDL_AudioSpec mySpec; // audio output format
|
||||||
|
uInt8* myPos{nullptr}; // pointer to the audio buffer to be played
|
||||||
|
uInt32 myRemaining{0}; // remaining length of the sample we have to play
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Callback function invoked by the SDL Audio library when it needs data
|
||||||
|
void processWav(uInt8* stream, uInt32 len);
|
||||||
|
static void callback(void* object, uInt8* stream, int len);
|
||||||
|
|
||||||
|
// Following constructors and assignment operators not supported
|
||||||
|
WavHandlerSDL2(const WavHandlerSDL2&) = delete;
|
||||||
|
WavHandlerSDL2(WavHandlerSDL2&&) = delete;
|
||||||
|
WavHandlerSDL2& operator=(const WavHandlerSDL2&) = delete;
|
||||||
|
WavHandlerSDL2& operator=(WavHandlerSDL2&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
WavHandlerSDL2 myWavHandler;
|
||||||
|
|
||||||
static float myVolumeFactor; // Current volume level (0 - 100)
|
static float myVolumeFactor; // Current volume level (0 - 100)
|
||||||
static double myWavSpeed;
|
|
||||||
static unique_ptr<uInt8[]> myWavCvtBuffer;
|
|
||||||
static uInt32 myWavCvtBufferSize;
|
|
||||||
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
|
|
||||||
|
|
||||||
string myAboutString;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Callback functions invoked by the SDL Audio library when it needs data
|
// Callback functions invoked by the SDL Audio library when it needs data
|
||||||
static void callback(void* udata, uInt8* stream, int len);
|
static void callback(void* object, uInt8* stream, int len);
|
||||||
static void wavCallback(void* udata, uInt8* stream, int len);
|
|
||||||
|
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
SoundSDL2() = delete;
|
SoundSDL2() = delete;
|
||||||
|
|
|
@ -67,7 +67,7 @@ void DialogContainer::updateTime(uInt64 time)
|
||||||
// Joystick has been pressed long
|
// Joystick has been pressed long
|
||||||
if(myCurrentButtonDown.stick != -1 && myButtonLongPressTime < myTime)
|
if(myCurrentButtonDown.stick != -1 && myButtonLongPressTime < myTime)
|
||||||
{
|
{
|
||||||
myButtonLongPress = true;
|
myIgnoreButtonUp = true;
|
||||||
activeDialog->handleJoyDown(myCurrentButtonDown.stick, myCurrentButtonDown.button, true);
|
activeDialog->handleJoyDown(myCurrentButtonDown.stick, myCurrentButtonDown.button, true);
|
||||||
myButtonLongPressTime = myButtonRepeatTime = myTime + _REPEAT_NONE;
|
myButtonLongPressTime = myButtonRepeatTime = myTime + _REPEAT_NONE;
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,12 @@ void DialogContainer::updateTime(uInt64 time)
|
||||||
// Joystick axis still pressed
|
// Joystick axis still pressed
|
||||||
if(myCurrentAxisDown.stick != -1 && myAxisRepeatTime < myTime)
|
if(myCurrentAxisDown.stick != -1 && myAxisRepeatTime < myTime)
|
||||||
{
|
{
|
||||||
activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
|
if(myCurrentButtonDown.stick == myCurrentAxisDown.stick)
|
||||||
myCurrentAxisDown.adir);
|
activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
|
||||||
|
myCurrentAxisDown.adir, myCurrentButtonDown.button);
|
||||||
|
else
|
||||||
|
activeDialog->handleJoyAxis(myCurrentAxisDown.stick, myCurrentAxisDown.axis,
|
||||||
|
myCurrentAxisDown.adir);
|
||||||
myAxisRepeatTime = myTime + _REPEAT_SUSTAIN_DELAY;
|
myAxisRepeatTime = myTime + _REPEAT_SUSTAIN_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,14 +327,18 @@ void DialogContainer::handleJoyBtnEvent(int stick, int button, bool pressed)
|
||||||
// Send the event to the dialog box on the top of the stack
|
// Send the event to the dialog box on the top of the stack
|
||||||
Dialog* activeDialog = myDialogStack.top();
|
Dialog* activeDialog = myDialogStack.top();
|
||||||
|
|
||||||
if(pressed && myButtonRepeatTime < myTime) // prevent pending repeats after enabling repeat again
|
if(pressed)
|
||||||
{
|
{
|
||||||
myCurrentButtonDown.stick = stick;
|
if(myButtonRepeatTime < myTime || // prevent pending repeats after enabling repeat again
|
||||||
myCurrentButtonDown.button = button;
|
myButtonRepeatTime + _REPEAT_INITIAL_DELAY > myTime) // ignore blocking delays
|
||||||
myButtonRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
|
{
|
||||||
myButtonLongPressTime = myTime + _LONG_PRESS_DELAY;
|
myCurrentButtonDown.stick = stick;
|
||||||
|
myCurrentButtonDown.button = button;
|
||||||
|
myButtonRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
|
||||||
|
myButtonLongPressTime = myTime + _LONG_PRESS_DELAY;
|
||||||
|
|
||||||
activeDialog->handleJoyDown(stick, button);
|
activeDialog->handleJoyDown(stick, button);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -340,8 +348,8 @@ void DialogContainer::handleJoyBtnEvent(int stick, int button, bool pressed)
|
||||||
myCurrentButtonDown.stick = myCurrentButtonDown.button = -1;
|
myCurrentButtonDown.stick = myCurrentButtonDown.button = -1;
|
||||||
myButtonRepeatTime = myButtonLongPressTime = 0;
|
myButtonRepeatTime = myButtonLongPressTime = 0;
|
||||||
}
|
}
|
||||||
if (myButtonLongPress)
|
if(myIgnoreButtonUp)
|
||||||
myButtonLongPress = false;
|
myIgnoreButtonUp = false;
|
||||||
else
|
else
|
||||||
activeDialog->handleJoyUp(stick, button);
|
activeDialog->handleJoyUp(stick, button);
|
||||||
}
|
}
|
||||||
|
@ -356,7 +364,8 @@ void DialogContainer::handleJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, i
|
||||||
// Send the event to the dialog box on the top of the stack
|
// Send the event to the dialog box on the top of the stack
|
||||||
Dialog* activeDialog = myDialogStack.top();
|
Dialog* activeDialog = myDialogStack.top();
|
||||||
|
|
||||||
// Prevent long button press in button/axis combinations
|
// Prevent button repeats and long button press in button/axis combinations
|
||||||
|
myButtonRepeatTime = myTime + _REPEAT_NONE;
|
||||||
myButtonLongPressTime = myTime + _REPEAT_NONE;
|
myButtonLongPressTime = myTime + _REPEAT_NONE;
|
||||||
|
|
||||||
// Only stop firing events if it's the current stick
|
// Only stop firing events if it's the current stick
|
||||||
|
@ -374,6 +383,8 @@ void DialogContainer::handleJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, i
|
||||||
myCurrentAxisDown.adir = adir;
|
myCurrentAxisDown.adir = adir;
|
||||||
myAxisRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
|
myAxisRepeatTime = myTime + (activeDialog->repeatEnabled() ? _REPEAT_INITIAL_DELAY : _REPEAT_NONE);
|
||||||
}
|
}
|
||||||
|
if(adir != JoyDir::NONE)
|
||||||
|
myIgnoreButtonUp = true; // prevent button released events
|
||||||
activeDialog->handleJoyAxis(stick, axis, adir, button);
|
activeDialog->handleJoyAxis(stick, axis, adir, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +397,8 @@ void DialogContainer::handleJoyHatEvent(int stick, int hat, JoyHatDir hdir, int
|
||||||
// Send the event to the dialog box on the top of the stack
|
// Send the event to the dialog box on the top of the stack
|
||||||
Dialog* activeDialog = myDialogStack.top();
|
Dialog* activeDialog = myDialogStack.top();
|
||||||
|
|
||||||
// Prevent long button press in button/hat combinations
|
// Prevent button repeats and long button press in button/hat combinations
|
||||||
|
myButtonRepeatTime = myTime + _REPEAT_NONE;
|
||||||
myButtonLongPressTime = myTime + _REPEAT_NONE;
|
myButtonLongPressTime = myTime + _REPEAT_NONE;
|
||||||
|
|
||||||
// Only stop firing events if it's the current stick
|
// Only stop firing events if it's the current stick
|
||||||
|
|
|
@ -214,7 +214,7 @@ class DialogContainer
|
||||||
} myCurrentButtonDown;
|
} myCurrentButtonDown;
|
||||||
uInt64 myButtonRepeatTime{0};
|
uInt64 myButtonRepeatTime{0};
|
||||||
uInt64 myButtonLongPressTime{0};
|
uInt64 myButtonLongPressTime{0};
|
||||||
bool myButtonLongPress{false};
|
bool myIgnoreButtonUp{false};
|
||||||
|
|
||||||
// For continuous 'joy axis down' events
|
// For continuous 'joy axis down' events
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -952,12 +952,13 @@ Event::Type LauncherDialog::getJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Event::UITabPrev:
|
case Event::UITabPrev:
|
||||||
// TODO: check with controller, then update doc (R77 too)
|
|
||||||
myRomImageWidget->changeImage(-1);
|
myRomImageWidget->changeImage(-1);
|
||||||
|
myEventHandled = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Event::UITabNext:
|
case Event::UITabNext:
|
||||||
myRomImageWidget->changeImage(1);
|
myRomImageWidget->changeImage(1);
|
||||||
|
myEventHandled = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1136,11 +1137,13 @@ void LauncherDialog::openContextMenu(int x, int y)
|
||||||
{
|
{
|
||||||
// Dynamically create context menu for ROM list options
|
// Dynamically create context menu for ROM list options
|
||||||
|
|
||||||
|
bool addCancel = false;
|
||||||
if(x < 0 || y < 0)
|
if(x < 0 || y < 0)
|
||||||
{
|
{
|
||||||
// Long pressed button, determine position from currently selected list item
|
// Long pressed button, determine position from currently selected list item
|
||||||
x = myList->getLeft() + myList->getWidth() / 2;
|
x = myList->getLeft() + myList->getWidth() / 2;
|
||||||
y = myList->getTop() + (myList->getSelected() - myList->currentPos() + 1) * _font.getLineHeight();
|
y = myList->getTop() + (myList->getSelected() - myList->currentPos() + 1) * _font.getLineHeight();
|
||||||
|
addCancel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContextItem {
|
struct ContextItem {
|
||||||
|
@ -1219,6 +1222,8 @@ void LauncherDialog::openContextMenu(int x, int y)
|
||||||
// items.push_back(ContextItem("Options" + ELLIPSIS, "Ctrl+O", "options"));
|
// items.push_back(ContextItem("Options" + ELLIPSIS, "Ctrl+O", "options"));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
if(addCancel)
|
||||||
|
items.push_back(ContextItem("Cancel", "")); // closes the context menu and does nothing
|
||||||
|
|
||||||
// Format items for menu
|
// Format items for menu
|
||||||
VariantList varItems;
|
VariantList varItems;
|
||||||
|
|
Loading…
Reference in New Issue