Merge pull request #21341 from hrydgard/more-regression-fixes

Fix crash on audio device switch in Windows
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-03-04 15:54:32 +01:00
commit ecb5cf036e
27 files changed
+156 -61

No files matched your search

+1
View File
@@ -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
View File
@@ -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
View File
@@ -410,7 +410,7 @@ public:
bool bExtraAudioBuffering; // For bluetooth
std::string sAudioDevice;
bool bAutoAudioDevice;
bool bAutoSwitchAudioDevice;
bool bUseOldAtrac;
// iOS only for now
+1 -1
View File
@@ -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
View File
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -67,11 +67,10 @@ WASAPIContext::~WASAPIContext() {
}
Stop();
enumerator_->UnregisterEndpointNotificationCallback(&notificationClient_);
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
View File
@@ -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_;
};
+1 -1
View File
@@ -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 = Сэнсарнае кіраванне на экране
+1 -1
View File
@@ -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 = Управление с докосване на екрана
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 = מקשי מגע על המסך
+1 -1
View File
@@ -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 = ךסמה לע עגמ ישקמ
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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