mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 10:40:01 +01:00
Merge pull request #21341 from hrydgard/more-regression-fixes
Fix crash on audio device switch in Windows
This commit is contained in:
27 files changed
+156
-61
No files matched your search
@@ -32,5 +32,6 @@ public:
|
||||
virtual int BufferSize() const = 0;
|
||||
virtual int PeriodFrames() const = 0;
|
||||
virtual void DescribeOutputFormat(char *buffer, size_t bufferSize) const { buffer[0] = '-'; buffer[1] = '\0'; }
|
||||
virtual std::string GetCurrentDeviceName() const { return ""; }
|
||||
virtual void FrameUpdate(bool allowAutoChange) {}
|
||||
};
|
||||
+1
-1
@@ -886,7 +886,7 @@ static const ConfigSetting soundSettings[] = {
|
||||
ConfigSetting("GamePreviewVolume", SETTING(g_Config, iGamePreviewVolume), &DefaultGamePreviewVolume, CfgFlag::DEFAULT),
|
||||
|
||||
ConfigSetting("AudioDevice", SETTING(g_Config, sAudioDevice), "", CfgFlag::DEFAULT),
|
||||
ConfigSetting("AutoAudioDevice", SETTING(g_Config, bAutoAudioDevice), true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AutoAudioDevice", SETTING(g_Config, bAutoSwitchAudioDevice), true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AudioMixWithOthers", SETTING(g_Config, bAudioMixWithOthers), true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AudioRespectSilentMode", SETTING(g_Config, bAudioRespectSilentMode), false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("UseOldAtrac", SETTING(g_Config, bUseOldAtrac), false, CfgFlag::DEFAULT),
|
||||
|
||||
+1
-1
@@ -410,7 +410,7 @@ public:
|
||||
|
||||
bool bExtraAudioBuffering; // For bluetooth
|
||||
std::string sAudioDevice;
|
||||
bool bAutoAudioDevice;
|
||||
bool bAutoSwitchAudioDevice;
|
||||
bool bUseOldAtrac;
|
||||
|
||||
// iOS only for now
|
||||
|
||||
@@ -58,7 +58,7 @@ float ControlMapper::GetDeviceAxisThreshold(int device, const InputMapping &mapp
|
||||
if (absCoValue > 0.0f) {
|
||||
// Bias down the threshold if the other axis is active.
|
||||
const float biasedThreshold = g_Config.fAnalogStickThreshold * (1.0f - absCoValue * 0.35f);
|
||||
INFO_LOG(Log::System, "coValue: %f threshold: %f", absCoValue, biasedThreshold);
|
||||
// INFO_LOG(Log::System, "coValue: %f threshold: %f", absCoValue, biasedThreshold);
|
||||
return biasedThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1290,7 +1290,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta
|
||||
break;
|
||||
}
|
||||
// Don't start auto switching for a couple of seconds, because some devices init on start.
|
||||
bool doAutoSwitch = g_Config.bAutoAudioDevice;
|
||||
bool doAutoSwitch = g_Config.bAutoSwitchAudioDevice;
|
||||
if ((time_now_d() - g_audioStartTime) < 3.0) {
|
||||
INFO_LOG(Log::Audio, "Ignoring new audio device: %s (current: %s)", name, g_Config.sAudioDevice.c_str());
|
||||
doAutoSwitch = false;
|
||||
|
||||
@@ -796,7 +796,7 @@ void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
|
||||
WARN_LOG(Log::Audio, "InitOutputDevice failed");
|
||||
}
|
||||
});
|
||||
CheckBox *autoAudio = audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));
|
||||
CheckBox *autoAudio = audioSettings->Add(new CheckBox(&g_Config.bAutoSwitchAudioDevice, a->T("Use new audio devices automatically")));
|
||||
autoAudio->SetEnabledFunc([]()->bool {
|
||||
return g_Config.sAudioDevice.empty();
|
||||
});
|
||||
@@ -808,7 +808,7 @@ void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
|
||||
audioSettings->Add(new ItemHeader(a->T("Audio backend")));
|
||||
|
||||
if (sdlAudio) {
|
||||
audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));
|
||||
audioSettings->Add(new CheckBox(&g_Config.bAutoSwitchAudioDevice, a->T("Use new audio devices automatically")));
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
|
||||
+1
-1
@@ -1017,7 +1017,7 @@ void NativeFrame(GraphicsContext *graphicsContext) {
|
||||
g_iconCache.FrameUpdate();
|
||||
|
||||
if (g_audioBackend) {
|
||||
g_audioBackend->FrameUpdate(g_Config.bAutoAudioDevice);
|
||||
g_audioBackend->FrameUpdate(g_Config.bAutoSwitchAudioDevice);
|
||||
}
|
||||
|
||||
// NOTE: We must begin the frame before update, so we can do texture size queries and stuff in Measure etc.
|
||||
|
||||
+2
-1
@@ -842,9 +842,10 @@ void GamePauseScreen::OnExit(UI::EventParams &e) {
|
||||
std::string confirmExitMessage = GetConfirmExitMessage();
|
||||
|
||||
if (!confirmExitMessage.empty()) {
|
||||
auto mm = GetI18NCategory(I18NCat::MAINMENU);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
std::string_view title = di->T("Are you sure you want to exit?");
|
||||
screenManager()->push(new UI::MessagePopupScreen(title, confirmExitMessage, di->T("Exit"), di->T("Cancel"), [this](bool result) {
|
||||
screenManager()->push(new UI::MessagePopupScreen(title, confirmExitMessage, mm->T("Exit"), di->T("Cancel"), [this](bool result) {
|
||||
if (result) {
|
||||
if (g_Config.bPauseMenuExitsEmulator) {
|
||||
System_ExitApp();
|
||||
|
||||
+104
-19
@@ -67,11 +67,10 @@ WASAPIContext::~WASAPIContext() {
|
||||
}
|
||||
Stop();
|
||||
enumerator_->UnregisterEndpointNotificationCallback(¬ificationClient_);
|
||||
delete[] tempBuf_;
|
||||
}
|
||||
|
||||
WASAPIContext::AudioFormat WASAPIContext::Classify(const WAVEFORMATEX *format) {
|
||||
if (format->wFormatTag == WAVE_FORMAT_PCM && format->wBitsPerSample == 2) {
|
||||
if (format->wFormatTag == WAVE_FORMAT_PCM && format->wBitsPerSample == 16) {
|
||||
return AudioFormat::S16;
|
||||
} else if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
const WAVEFORMATEXTENSIBLE *ex = (const WAVEFORMATEXTENSIBLE *)format;
|
||||
@@ -153,7 +152,10 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
GetDeviceDesc(device.Get(), &desc);
|
||||
INFO_LOG(Log::Audio, "Activating audio device: %s", desc.name.c_str());
|
||||
|
||||
deviceId_ = uniqueId;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(deviceLock_);
|
||||
curDeviceId_ = uniqueId;
|
||||
}
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
// Try IAudioClient3 first if not in "safe" mode. It's probably safe anyway, but still, let's use the legacy client as a safe fallback option.
|
||||
@@ -162,11 +164,12 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
}
|
||||
|
||||
// Get rid of any old tempBuf_.
|
||||
delete[] tempBuf_;
|
||||
tempBuf_ = nullptr;
|
||||
tempBuf_.reset();
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
audioClient3_->GetMixFormat(&format_);
|
||||
curSamplesPerSec_ = format_->nSamplesPerSec;
|
||||
|
||||
// We only use AudioClient3 if we got the format we wanted (stereo float).
|
||||
if (format_->nChannels != 2 || Classify(format_) != AudioFormat::Float) {
|
||||
// Let's fall back to the old path. The docs seem to be wrong, if you try to create an
|
||||
@@ -174,15 +177,15 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
audioClient3_.Reset();
|
||||
// Fall through to AudioClient creation below.
|
||||
} else {
|
||||
audioClient3_->GetSharedModeEnginePeriod(format_, &defaultPeriodFrames, &fundamentalPeriodFrames, &minPeriodFrames, &maxPeriodFrames);
|
||||
audioClient3_->GetSharedModeEnginePeriod(format_, &defaultPeriodFrames_, &fundamentalPeriodFrames_, &minPeriodFrames_, &maxPeriodFrames_);
|
||||
|
||||
INFO_LOG(Log::Audio, "AudioClient3: default: %d fundamental: %d min: %d max: %d\n", (int)defaultPeriodFrames, (int)fundamentalPeriodFrames, (int)minPeriodFrames, (int)maxPeriodFrames);
|
||||
INFO_LOG(Log::Audio, "initializing with %d frame period at %d Hz, meaning %0.1fms\n", (int)minPeriodFrames, (int)format_->nSamplesPerSec, FramesToMs(minPeriodFrames, format_->nSamplesPerSec));
|
||||
INFO_LOG(Log::Audio, "AudioClient3: default: %d fundamental: %d min: %d max: %d\n", (int)defaultPeriodFrames_, (int)fundamentalPeriodFrames_, (int)minPeriodFrames_, (int)maxPeriodFrames_);
|
||||
INFO_LOG(Log::Audio, "initializing with %d frame period at %d Hz, meaning %0.1fms\n", (int)minPeriodFrames_, (int)format_->nSamplesPerSec, FramesToMs(minPeriodFrames_, format_->nSamplesPerSec));
|
||||
|
||||
audioEvent_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
HRESULT result = audioClient3_->InitializeSharedAudioStream(
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
minPeriodFrames,
|
||||
minPeriodFrames_,
|
||||
format_,
|
||||
nullptr
|
||||
);
|
||||
@@ -191,7 +194,7 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
audioClient3_.Reset();
|
||||
return false;
|
||||
}
|
||||
actualPeriodFrames_ = minPeriodFrames;
|
||||
actualPeriodFrames_ = minPeriodFrames_;
|
||||
|
||||
audioClient3_->GetBufferSize(&reportedBufferSize_);
|
||||
audioClient3_->SetEventHandle(audioEvent_);
|
||||
@@ -207,12 +210,18 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
return false;
|
||||
}
|
||||
|
||||
audioClient_->GetMixFormat(&format_);
|
||||
hr = audioClient_->GetMixFormat(&format_);
|
||||
if (FAILED(hr)) {
|
||||
_dbg_assert_(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are too many channels, try asking for a 2-channel output format.
|
||||
DWORD extraStreamFlags = 0;
|
||||
const AudioFormat fmt = Classify(format_);
|
||||
|
||||
curSamplesPerSec_ = format_->nSamplesPerSec;
|
||||
|
||||
bool createBuffer = false;
|
||||
if (fmt == AudioFormat::Float) {
|
||||
if (format_->nChannels != 2) {
|
||||
@@ -244,6 +253,7 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
}
|
||||
} else {
|
||||
// All good, nothing to convert.
|
||||
_dbg_assert_(format_);
|
||||
}
|
||||
} else {
|
||||
// Some other format.
|
||||
@@ -279,7 +289,7 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
audioClient_->GetService(IID_PPV_ARGS(&renderClient_));
|
||||
|
||||
if (createBuffer) {
|
||||
tempBuf_ = new float[reportedBufferSize_ * 2];
|
||||
tempBuf_ = std::make_unique<float[]>(reportedBufferSize_ * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,18 +303,25 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
|
||||
}
|
||||
|
||||
void WASAPIContext::Start() {
|
||||
if (audioThread_.joinable()) {
|
||||
_dbg_assert_(false);
|
||||
ERROR_LOG(Log::Audio, "Audio thread already running!");
|
||||
return;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
audioThread_ = std::thread([this]() { AudioLoop(); });
|
||||
}
|
||||
|
||||
void WASAPIContext::Stop() {
|
||||
running_ = false;
|
||||
if (audioClient_) audioClient_->Stop();
|
||||
if (audioEvent_) SetEvent(audioEvent_);
|
||||
// Stop is actually called on the audioclient in the thread, while exiting.
|
||||
if (audioThread_.joinable()) audioThread_.join();
|
||||
|
||||
renderClient_.Reset();
|
||||
audioClient_.Reset();
|
||||
audioClient3_.Reset();
|
||||
if (audioEvent_) {
|
||||
CloseHandle(audioEvent_);
|
||||
audioEvent_ = nullptr;
|
||||
@@ -313,14 +330,37 @@ void WASAPIContext::Stop() {
|
||||
CoTaskMemFree(format_);
|
||||
format_ = nullptr;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(deviceLock_);
|
||||
curDeviceId_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPIContext::FrameUpdate(bool allowAutoChange) {
|
||||
if (deviceId_.empty() && defaultDeviceChanged_ && allowAutoChange) {
|
||||
defaultDeviceChanged_ = false;
|
||||
Stop();
|
||||
Start();
|
||||
std::string deviceIdToInit;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(deviceLock_);
|
||||
if (!defaultDeviceChanged_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (allowAutoChange) {
|
||||
// Check if there actually was a change, we ignore false positives.
|
||||
{
|
||||
if (newDeviceId_ == curDeviceId_) {
|
||||
// False positive, ignore.
|
||||
defaultDeviceChanged_ = false;
|
||||
return;
|
||||
}
|
||||
deviceIdToInit = newDeviceId_;
|
||||
newDeviceId_.clear();
|
||||
}
|
||||
defaultDeviceChanged_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool reverted;
|
||||
InitOutputDevice(deviceIdToInit, latencyMode_, &reverted);
|
||||
}
|
||||
|
||||
void WASAPIContext::AudioLoop() {
|
||||
@@ -345,6 +385,11 @@ void WASAPIContext::AudioLoop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!format_) {
|
||||
ERROR_LOG(Log::Audio, "Can't start audio - no format");
|
||||
return;
|
||||
}
|
||||
|
||||
const AudioFormat format = Classify(format_);
|
||||
const int nChannels = format_->nChannels;
|
||||
|
||||
@@ -372,7 +417,7 @@ void WASAPIContext::AudioLoop() {
|
||||
}
|
||||
} else {
|
||||
// We decided previously that we need conversion, so mix to our temp buffer...
|
||||
callback_(tempBuf_, framesToWrite, format_->nSamplesPerSec, userdata_);
|
||||
callback_(tempBuf_.get(), framesToWrite, format_->nSamplesPerSec, userdata_);
|
||||
// .. and convert according to format (we support multi-channel float and s16)
|
||||
if (format == AudioFormat::S16 && buffer) {
|
||||
// Need to convert.
|
||||
@@ -421,7 +466,8 @@ void WASAPIContext::AudioLoop() {
|
||||
|
||||
if (audioClient3_) {
|
||||
audioClient3_->Stop();
|
||||
} else {
|
||||
}
|
||||
if (audioClient_) {
|
||||
audioClient_->Stop();
|
||||
}
|
||||
|
||||
@@ -431,6 +477,10 @@ void WASAPIContext::AudioLoop() {
|
||||
}
|
||||
|
||||
void WASAPIContext::DescribeOutputFormat(char *buffer, size_t bufferSize) const {
|
||||
if (!format_) {
|
||||
snprintf(buffer, bufferSize, "No format");
|
||||
return;
|
||||
}
|
||||
const int numChannels = format_->nChannels;
|
||||
const int sampleBits = format_->wBitsPerSample;
|
||||
const int sampleRateHz = format_->nSamplesPerSec;
|
||||
@@ -447,3 +497,38 @@ void WASAPIContext::DescribeOutputFormat(char *buffer, size_t bufferSize) const
|
||||
}
|
||||
snprintf(buffer, bufferSize, "%d Hz %s %d-bit, %d ch%s", sampleRateHz, fmt, sampleBits, numChannels, audioClient3_ ? " (ac3)" : " (ac)");
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WASAPIContext::DeviceNotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device) {
|
||||
if (flow != eRender) {
|
||||
INFO_LOG(Log::Audio, "Default WASAPI audio recording device changed! Currently ignoring.");
|
||||
return S_OK;
|
||||
}
|
||||
INFO_LOG(Log::Audio, "Default device changed to %s! role=%d", ConvertWStringToUTF8(device).c_str(), role);
|
||||
if (role == eConsole) {
|
||||
// PostMessage(hwnd, WM_APP + 1, 0, 0);
|
||||
std::lock_guard<std::mutex> guard(engine_->deviceLock_);
|
||||
engine_->defaultDeviceChanged_ = true;
|
||||
engine_->newDeviceId_ = ConvertWStringToUTF8(device);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WASAPIContext::DeviceNotificationClient::OnDeviceAdded(LPCWSTR device) {
|
||||
INFO_LOG(Log::Audio, "Audio device added! device=%s", ConvertWStringToUTF8(device).c_str());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WASAPIContext::DeviceNotificationClient::OnDeviceRemoved(LPCWSTR device) {
|
||||
INFO_LOG(Log::Audio, "Audio device removed! device=%s", ConvertWStringToUTF8(device).c_str());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WASAPIContext::DeviceNotificationClient::OnDeviceStateChanged(LPCWSTR device, DWORD state) {
|
||||
INFO_LOG(Log::Audio, "Audio device state changed! device=%s state=%08x", ConvertWStringToUTF8(device).c_str(), state);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WASAPIContext::DeviceNotificationClient::OnPropertyValueChanged(LPCWSTR device, const PROPERTYKEY key) {
|
||||
return S_OK;
|
||||
}
|
||||
+25
-17
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Audio/AudioBackend.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmdeviceapi.h>
|
||||
@@ -9,6 +10,7 @@
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <wrl/client.h>
|
||||
|
||||
class WASAPIContext : public AudioBackend {
|
||||
@@ -16,6 +18,7 @@ public:
|
||||
WASAPIContext();
|
||||
~WASAPIContext();
|
||||
|
||||
// This is only called on init.
|
||||
void SetRenderCallback(RenderCallback callback, void *userdata) override {
|
||||
callback_ = callback;
|
||||
userdata_ = userdata;
|
||||
@@ -29,7 +32,7 @@ public:
|
||||
|
||||
int PeriodFrames() const override { return actualPeriodFrames_; } // NOTE: This may have the wrong value (too large) until audio has started playing.
|
||||
int BufferSize() const override { return reportedBufferSize_; }
|
||||
int SampleRate() const override { return format_->nSamplesPerSec; }
|
||||
int SampleRate() const override { return curSamplesPerSec_; }
|
||||
|
||||
// Implements device change notifications
|
||||
class DeviceNotificationClient : public IMMNotificationClient {
|
||||
@@ -46,24 +49,22 @@ public:
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override {
|
||||
if (flow == eRender && role == eConsole) {
|
||||
// PostMessage(hwnd, WM_APP + 1, 0, 0);
|
||||
engine_->defaultDeviceChanged_ = true;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR) override { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR) override { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) override { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device) override;
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device) override;
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device) override;
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR device, DWORD state) override;
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR device, const PROPERTYKEY key) override;
|
||||
private:
|
||||
WASAPIContext *engine_;
|
||||
};
|
||||
|
||||
void DescribeOutputFormat(char *buffer, size_t bufferSize) const override;
|
||||
|
||||
std::string GetCurrentDeviceName() const override {
|
||||
std::lock_guard<std::mutex> guard(deviceLock_);
|
||||
return curDeviceId_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Start();
|
||||
void Stop();
|
||||
@@ -85,7 +86,11 @@ private:
|
||||
WAVEFORMATEX *format_ = nullptr;
|
||||
HANDLE audioEvent_ = nullptr;
|
||||
std::thread audioThread_;
|
||||
UINT32 defaultPeriodFrames = 0, fundamentalPeriodFrames = 0, minPeriodFrames = 0, maxPeriodFrames = 0;
|
||||
int curSamplesPerSec_ = 0;
|
||||
UINT32 defaultPeriodFrames_ = 0;
|
||||
UINT32 fundamentalPeriodFrames_ = 0;
|
||||
UINT32 minPeriodFrames_ = 0;
|
||||
UINT32 maxPeriodFrames_ = 0;
|
||||
std::atomic<bool> running_ = true;
|
||||
UINT32 actualPeriodFrames_ = 0; // may not be the requested.
|
||||
UINT32 reportedBufferSize_ = 0;
|
||||
@@ -94,8 +99,11 @@ private:
|
||||
RenderCallback callback_{};
|
||||
void *userdata_ = nullptr;
|
||||
LatencyMode latencyMode_ = LatencyMode::Aggressive;
|
||||
std::string deviceId_;
|
||||
std::atomic<bool> defaultDeviceChanged_{};
|
||||
|
||||
float *tempBuf_ = nullptr;
|
||||
mutable std::mutex deviceLock_;
|
||||
std::string curDeviceId_;
|
||||
std::string newDeviceId_;
|
||||
bool defaultDeviceChanged_ = false;
|
||||
|
||||
std::unique_ptr<float[]> tempBuf_;
|
||||
};
|
||||
@@ -157,7 +157,7 @@ Mouse = Налады мышы
|
||||
Mouse sensitivity = Адчувальнасць мышы
|
||||
Mouse smoothing = Згладжванне мышшу
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Няма (Адкл.)
|
||||
Off = Выкл.
|
||||
On-screen touch controls = Сэнсарнае кіраванне на экране
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Настройки на мишката
|
||||
Mouse sensitivity = Чувствителност на мишката
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Нищо (изключено)
|
||||
Off = Off
|
||||
On-screen touch controls = Управление с докосване на екрана
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Žádné (zakázáno)
|
||||
Off = Vypnuto
|
||||
On-screen touch controls = Dotykové ovládání na obrazovce
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Maus-Einstellungen
|
||||
Mouse sensitivity = Mausempfindlichkeit
|
||||
Mouse smoothing = Mausglättung
|
||||
Mouse wheel button-release delay = Verzögerung beim Loslassen der Mausradtaste
|
||||
MouseControl Tip = Du kannst jetzt die Maus im Steuerungszuordnungs-Bildschirm zuordnen, indem du auf das Symbol „M“ drückst
|
||||
MouseControl Tip = Du kannst Maus-Eingaben im Steuerungszuordnungs-Bildschirm zuordnen, indem du auf das Symbol „M“ drückst
|
||||
None (Disabled) = Keine (Deaktiviert)
|
||||
Off = Aus
|
||||
On-screen touch controls = Berührungs-Bildschirmtasten
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = Tobolo'na jo layar sentu
|
||||
|
||||
@@ -179,7 +179,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = On-screen touch controls
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Ajustes de ratón
|
||||
Mouse sensitivity = Sensibilidad de ratón
|
||||
Mouse smoothing = Suavidad de ratón
|
||||
Mouse wheel button-release delay = Retraso al soltar botón de la rueda del mouse
|
||||
MouseControl Tip = Ahora puedes configurar el mouse en la pantalla de configuración de mando presionando el ícono 'M'.
|
||||
MouseControl Tip = Puedes configurar el mouse en la pantalla de configuración de mando presionando el ícono 'M'.
|
||||
None (Disabled) = Ninguno (desactivado)
|
||||
Off = No
|
||||
On-screen touch controls = Controles táctiles en pantalla
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Desactivado
|
||||
Off = Off
|
||||
On-screen touch controls = Controis táctiles en pantalla
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = מקשי מגע על המסך
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = ךסמה לע עגמ ישקמ
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Ora ana (Ora Aktif)
|
||||
Off = Mati
|
||||
On-screen touch controls = Tombol Layar-sentuh
|
||||
|
||||
@@ -170,7 +170,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = On-screen touch controls
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Nieko (išjungta)
|
||||
Off = Off
|
||||
On-screen touch controls = Ekrano liečiami kontrolės mygtukai
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Tetapan Tetikus
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = Kawalan sentuh di skrin
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Mouse settings
|
||||
Mouse sensitivity = Mouse sensitivity
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = None (disabled)
|
||||
Off = Off
|
||||
On-screen touch controls = Touch-kontroller på skjermen
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Setări Mouse
|
||||
Mouse sensitivity = Sensitivitate Mouse
|
||||
Mouse smoothing = Mouse smoothing
|
||||
Mouse wheel button-release delay = Mouse wheel button-release delay
|
||||
MouseControl Tip = You can now map mouse in control mapping screen by pressing the 'M' icon.
|
||||
MouseControl Tip = You can map mouse inputs in the control mapping screen by pressing the 'M' icon.
|
||||
None (Disabled) = Fara (dezactivat)
|
||||
Off = Off
|
||||
On-screen touch controls = Controale Touch pe Ecran
|
||||
|
||||
@@ -157,7 +157,7 @@ Mouse = Musinställningar
|
||||
Mouse sensitivity = Muskänslighet
|
||||
Mouse smoothing = Musutjämning
|
||||
Mouse wheel button-release delay = Släppfördröjning för knapp på mushjul
|
||||
MouseControl Tip = Du kan nu mappa musrörelser genom att klicka på "M"-ikonen.
|
||||
MouseControl Tip = Du kan mappa musrörelser genom att klicka på "M"-ikonen.
|
||||
None (Disabled) = Ingen (av)
|
||||
Off = Av
|
||||
On-screen touch controls = Touch-kontroller på skärmen
|
||||
|
||||
Reference in new issue
Block a user