diff --git a/CMakeLists.txt b/CMakeLists.txt index 01020c378..d92804ee8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,6 +304,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h + src/core/libraries/audio/sdl_in.h + src/core/libraries/audio/sdl_in.cpp src/core/libraries/voice/voice.cpp src/core/libraries/voice/voice.h src/core/libraries/audio/audioout.cpp diff --git a/src/common/config.cpp b/src/common/config.cpp index a1b12ee5d..a17129ebb 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -55,6 +55,7 @@ static bool useSpecialPad = false; static int specialPadClass = 1; static bool isMotionControlsEnabled = true; static bool useUnifiedInputConfig = true; +static std::string micDevice = "Default Device"; // These two entries aren't stored in the config static bool overrideControllerColor = false; @@ -201,6 +202,10 @@ int getCursorHideTimeout() { return cursorHideTimeout; } +std::string getMicDevice() { + return micDevice; +} + double getTrophyNotificationDuration() { return trophyNotificationDuration; } @@ -453,6 +458,10 @@ void setCursorHideTimeout(int newcursorHideTimeout) { cursorHideTimeout = newcursorHideTimeout; } +void setMicDevice(std::string device) { + micDevice = device; +} + void setTrophyNotificationDuration(double newTrophyNotificationDuration) { trophyNotificationDuration = newTrophyNotificationDuration; } @@ -652,6 +661,7 @@ void load(const std::filesystem::path& path) { toml::find_or(input, "isMotionControlsEnabled", isMotionControlsEnabled); useUnifiedInputConfig = toml::find_or(input, "useUnifiedInputConfig", useUnifiedInputConfig); + micDevice = toml::find_or(input, "micDevice", micDevice); entry_count += input.size(); } @@ -836,6 +846,7 @@ void save(const std::filesystem::path& path) { data["Input"]["specialPadClass"] = specialPadClass; data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled; data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig; + data["Input"]["micDevice"] = micDevice; data["GPU"]["screenWidth"] = windowWidth; data["GPU"]["screenHeight"] = windowHeight; data["GPU"]["internalScreenWidth"] = internalScreenWidth; @@ -938,6 +949,7 @@ void setDefaultValues() { controllerCustomColorRGB[0] = 0; controllerCustomColorRGB[1] = 0; controllerCustomColorRGB[2] = 255; + micDevice = "Default Device"; // GPU windowWidth = 1280; diff --git a/src/common/config.h b/src/common/config.h index 4ace4d316..937ea93fa 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -85,7 +85,9 @@ void setLogFilter(const std::string& type); double getTrophyNotificationDuration(); void setTrophyNotificationDuration(double newTrophyNotificationDuration); int getCursorHideTimeout(); +std::string getMicDevice(); void setCursorHideTimeout(int newcursorHideTimeout); +void setMicDevice(std::string device); void setSeparateLogFilesEnabled(bool enabled); bool getSeparateLogFilesEnabled(); u32 GetLanguage(); diff --git a/src/core/libraries/audio/audioin.cpp b/src/core/libraries/audio/audioin.cpp index c227d8f15..c62f05c9b 100644 --- a/src/core/libraries/audio/audioin.cpp +++ b/src/core/libraries/audio/audioin.cpp @@ -3,17 +3,21 @@ #include "common/logging/log.h" #include "core/libraries/audio/audioin.h" +#include "core/libraries/audio/sdl_in.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" namespace Libraries::AudioIn { +static std::unique_ptr audio = std::make_unique(); + int PS4_SYSV_ABI sceAudioInChangeAppModuleState() { LOG_ERROR(Lib_AudioIn, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioInClose() { +int PS4_SYSV_ABI sceAudioInClose(s32 handle) { + audio->AudioInClose(handle); LOG_ERROR(Lib_AudioIn, "(STUBBED) called"); return ORBIS_OK; } @@ -93,9 +97,15 @@ int PS4_SYSV_ABI sceAudioInGetSilentState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioInHqOpen() { - LOG_ERROR(Lib_AudioIn, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAudioInHqOpen(Libraries::UserService::OrbisUserServiceUserId userId, u32 type, + u32 index, u32 len, u32 freq, u32 param) { + LOG_ERROR(Lib_AudioIn, "called"); + int result = audio->AudioInOpen(type, len, freq, param); + if (result == -1) { + LOG_ERROR(Lib_AudioOut, "Audio ports are full"); + return 0x80260005; + } + return result; } int PS4_SYSV_ABI sceAudioInHqOpenEx() { @@ -108,9 +118,8 @@ int PS4_SYSV_ABI sceAudioInInit() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioInInput() { - LOG_ERROR(Lib_AudioIn, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceAudioInInput(s32 handle, void* dest) { + return audio->AudioInInput(handle, dest); } int PS4_SYSV_ABI sceAudioInInputs() { @@ -123,9 +132,15 @@ int PS4_SYSV_ABI sceAudioInIsSharedDevice() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioInOpen() { - LOG_ERROR(Lib_AudioIn, "(DUMMY) called"); - return 0x80260005; // ports are full return +int PS4_SYSV_ABI sceAudioInOpen(Libraries::UserService::OrbisUserServiceUserId userId, u32 type, + u32 index, u32 len, u32 freq, u32 param) { + LOG_ERROR(Lib_AudioIn, "called"); + int result = audio->AudioInOpen(type, len, freq, param); + if (result == -1) { + LOG_ERROR(Lib_AudioOut, "Audio ports are full"); + return 0x80260005; + } + return result; } int PS4_SYSV_ABI sceAudioInOpenEx() { diff --git a/src/core/libraries/audio/audioin.h b/src/core/libraries/audio/audioin.h index cbcee6d03..f528c730e 100644 --- a/src/core/libraries/audio/audioin.h +++ b/src/core/libraries/audio/audioin.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -11,8 +12,12 @@ class SymbolsResolver; namespace Libraries::AudioIn { +enum class OrbisAudioInParamFormat : u32 { S16Mono = 0, S16Stereo = 2 }; + +enum class OrbisAudioInType : u32 { VoiceChat = 0, General = 1, VoiceRecognition = 5 }; + int PS4_SYSV_ABI sceAudioInChangeAppModuleState(); -int PS4_SYSV_ABI sceAudioInClose(); +int PS4_SYSV_ABI sceAudioInClose(s32 handle); int PS4_SYSV_ABI sceAudioInCountPorts(); int PS4_SYSV_ABI sceAudioInDeviceHqOpen(); int PS4_SYSV_ABI sceAudioInDeviceIdHqOpen(); @@ -28,13 +33,15 @@ int PS4_SYSV_ABI sceAudioInGetGain(); int PS4_SYSV_ABI sceAudioInGetHandleStatusInfo(); int PS4_SYSV_ABI sceAudioInGetRerouteCount(); int PS4_SYSV_ABI sceAudioInGetSilentState(); -int PS4_SYSV_ABI sceAudioInHqOpen(); +int PS4_SYSV_ABI sceAudioInHqOpen(Libraries::UserService::OrbisUserServiceUserId userId, u32 type, + u32 index, u32 len, u32 freq, u32 param); int PS4_SYSV_ABI sceAudioInHqOpenEx(); int PS4_SYSV_ABI sceAudioInInit(); -int PS4_SYSV_ABI sceAudioInInput(); +int PS4_SYSV_ABI sceAudioInInput(s32 handle, void* dest); int PS4_SYSV_ABI sceAudioInInputs(); int PS4_SYSV_ABI sceAudioInIsSharedDevice(); -int PS4_SYSV_ABI sceAudioInOpen(); +int PS4_SYSV_ABI sceAudioInOpen(Libraries::UserService::OrbisUserServiceUserId userId, u32 type, + u32 index, u32 len, u32 freq, u32 param); int PS4_SYSV_ABI sceAudioInOpenEx(); int PS4_SYSV_ABI sceAudioInSetAllMute(); int PS4_SYSV_ABI sceAudioInSetCompressorPreGain(); diff --git a/src/core/libraries/audio/sdl_in.cpp b/src/core/libraries/audio/sdl_in.cpp new file mode 100644 index 000000000..0ea34ecfa --- /dev/null +++ b/src/core/libraries/audio/sdl_in.cpp @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "sdl_in.h" + +int SDLAudioIn::AudioInit() { + return SDL_InitSubSystem(SDL_INIT_AUDIO); +} + +int SDLAudioIn::AudioInOpen(int type, uint32_t samples_num, uint32_t freq, uint32_t format) { + std::scoped_lock lock{m_mutex}; + + for (int id = 0; id < static_cast(portsIn.size()); ++id) { + auto& port = portsIn[id]; + if (!port.isOpen) { + port.isOpen = true; + port.type = type; + port.samples_num = samples_num; + port.freq = freq; + port.format = format; + + SDL_AudioFormat sampleFormat; + switch (format) { + case Libraries::AudioIn::ORBIS_AUDIO_IN_PARAM_FORMAT_S16_MONO: + sampleFormat = SDL_AUDIO_S16; + port.channels_num = 1; + port.sample_size = 2; + break; + case Libraries::AudioIn::ORBIS_AUDIO_IN_PARAM_FORMAT_S16_STEREO: + sampleFormat = SDL_AUDIO_S16; + port.channels_num = 2; + port.sample_size = 2; + break; + default: + port.isOpen = false; + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + } + + SDL_AudioSpec fmt; + SDL_zero(fmt); + fmt.format = sampleFormat; + fmt.channels = port.channels_num; + fmt.freq = port.freq; + + std::string micDevStr = Config::getMicDevice(); + uint32_t devId; + + if (micDevStr == "None") { + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + } else if (micDevStr == "Default Device") { + devId = SDL_AUDIO_DEVICE_DEFAULT_RECORDING; + } else { + try { + devId = static_cast(std::stoul(micDevStr)); + } catch (const std::exception& e) { + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + } + } + + port.stream = SDL_OpenAudioDeviceStream(devId, &fmt, nullptr, nullptr); + + if (!port.stream) { + port.isOpen = false; + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + } + + if (SDL_ResumeAudioStreamDevice(port.stream) == false) { + SDL_DestroyAudioStream(port.stream); + port = {}; + return ORBIS_AUDIO_IN_ERROR_STREAM_FAIL; + } + + return id + 1; + } + } + + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; +} + +int SDLAudioIn::AudioInInput(int handle, void* out_buffer) { + std::scoped_lock lock{m_mutex}; + + if (handle < 1 || handle > static_cast(portsIn.size()) || !out_buffer) + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + + auto& port = portsIn[handle - 1]; + if (!port.isOpen) + return ORBIS_AUDIO_IN_ERROR_INVALID_PORT; + + const int bytesToRead = port.samples_num * port.sample_size * port.channels_num; + + if (out_buffer == nullptr) { + int attempts = 0; + while (SDL_GetAudioStreamAvailable(port.stream) > 0) { + SDL_Delay(1); + if (++attempts > 1000) { + return ORBIS_AUDIO_IN_ERROR_TIMEOUT; + } + } + return 0; // done + } + + int attempts = 0; + while (SDL_GetAudioStreamAvailable(port.stream) < bytesToRead) { + SDL_Delay(1); + if (++attempts > 1000) { + return ORBIS_AUDIO_IN_ERROR_TIMEOUT; + } + } + + const int bytesRead = SDL_GetAudioStreamData(port.stream, out_buffer, bytesToRead); + if (bytesRead < 0) { + // SDL_GetAudioStreamData failed + SDL_Log("AudioInInput error: %s", SDL_GetError()); + return ORBIS_AUDIO_IN_ERROR_STREAM_FAIL; + } + const int framesRead = bytesRead / (port.sample_size * port.channels_num); + return framesRead; +} + +void SDLAudioIn::AudioInClose(int handle) { + std::scoped_lock lock{m_mutex}; + if (handle < 1 || handle > (int)portsIn.size()) + return; + + auto& port = portsIn[handle - 1]; + if (!port.isOpen) + return; + + SDL_DestroyAudioStream(port.stream); + port = {}; +} \ No newline at end of file diff --git a/src/core/libraries/audio/sdl_in.h b/src/core/libraries/audio/sdl_in.h new file mode 100644 index 000000000..e1b2a4682 --- /dev/null +++ b/src/core/libraries/audio/sdl_in.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +namespace Libraries::AudioIn { +enum OrbisAudioInParam { + ORBIS_AUDIO_IN_PARAM_FORMAT_S16_MONO = 0, + ORBIS_AUDIO_IN_PARAM_FORMAT_S16_STEREO = 2 +}; +} + +#define ORBIS_AUDIO_IN_ERROR_INVALID_PORT -1 +#define ORBIS_AUDIO_IN_ERROR_TIMEOUT -2 +#define ORBIS_AUDIO_IN_ERROR_STREAM_FAIL -3 + +class SDLAudioIn { +public: + int AudioInit(); + int AudioInOpen(int type, uint32_t samples_num, uint32_t freq, uint32_t format); + int AudioInInput(int handle, void* out_buffer); + void AudioInClose(int handle); + +private: + struct AudioInPort { + bool isOpen = false; + int type = 0; + uint32_t samples_num = 0; + uint32_t freq = 0; + int channels_num = 0; + int sample_size = 0; + uint32_t format = 0; + SDL_AudioStream* stream = nullptr; + }; + + std::array portsIn; + std::mutex m_mutex; +}; diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index f7cbed931..9a6fb9927 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -186,7 +186,7 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled() { int PS4_SYSV_ABI sceHttpGetAllResponseHeaders() { LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; + return ORBIS_FAIL; } int PS4_SYSV_ABI sceHttpGetAuthEnabled() { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index b08bb5aee..3ff308d72 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "common/config.h" @@ -28,6 +29,7 @@ #include "settings_dialog.h" #include "ui_settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" + QStringList languageNames = {"Arabic", "Czech", "Danish", @@ -66,6 +68,7 @@ QMap channelMap; QMap logTypeMap; QMap screenModeMap; QMap chooseHomeTabMap; +QMap micMap; int backgroundImageOpacitySlider_backup; int bgm_volume_backup; @@ -94,6 +97,7 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, {tr("Graphics"), "Graphics"}, {tr("User"), "User"}, {tr("Input"), "Input"}, {tr("Paths"), "Paths"}, {tr("Debug"), "Debug"}}; + micMap = {{tr("None"), "None"}, {tr("Default Device"), "Default Device"}}; if (m_physical_devices.empty()) { // Populate cache of physical devices. @@ -125,6 +129,25 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, ui->hideCursorComboBox->addItem(tr("Idle")); ui->hideCursorComboBox->addItem(tr("Always")); + ui->micComboBox->addItem(micMap.key("None"), "None"); + ui->micComboBox->addItem(micMap.key("Default Device"), "Default Device"); + SDL_InitSubSystem(SDL_INIT_AUDIO); + int count = 0; + SDL_AudioDeviceID* devices = SDL_GetAudioRecordingDevices(&count); + if (devices) { + for (int i = 0; i < count; ++i) { + SDL_AudioDeviceID devId = devices[i]; + const char* name = SDL_GetAudioDeviceName(devId); + if (name) { + QString qname = QString::fromUtf8(name); + ui->micComboBox->addItem(qname, QString::number(devId)); + } + } + SDL_free(devices); + } else { + qDebug() << "Erro SDL_GetAudioRecordingDevices:" << SDL_GetError(); + } + InitializeEmulatorLanguages(); LoadValuesFromConfig(); @@ -453,6 +476,14 @@ void SettingsDialog::LoadValuesFromConfig() { ui->hideCursorComboBox->setCurrentIndex(toml::find_or(data, "Input", "cursorState", 1)); OnCursorStateChanged(toml::find_or(data, "Input", "cursorState", 1)); ui->idleTimeoutSpinBox->setValue(toml::find_or(data, "Input", "cursorHideTimeout", 5)); + + QString micValue = QString::fromStdString(Config::getMicDevice()); + int micIndex = ui->micComboBox->findData(micValue); + if (micIndex != -1) { + ui->micComboBox->setCurrentIndex(micIndex); + } else { + ui->micComboBox->setCurrentIndex(0); + } // First options is auto selection -1, so gpuId on the GUI will always have to subtract 1 // when setting and add 1 when getting to select the correct gpu in Qt ui->graphicsAdapterBox->setCurrentIndex(toml::find_or(data, "Vulkan", "gpuId", -1) + 1); @@ -771,6 +802,7 @@ void SettingsDialog::UpdateSettings() { m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked()); Config::setAllowHDR(ui->enableHDRCheckBox->isChecked()); Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString()); + Config::setMicDevice(ui->micComboBox->currentData().toString().toStdString()); Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); Config::setUserName(ui->userNameLineEdit->text().toStdString()); Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 9ebb1cbc1..772bc74dd 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -86,7 +86,161 @@ 6 - + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Emulator + + + false + + + false + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + 10 + + + + + Show Splash + + + + + + + Enable Discord Rich Presence + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + System + + + + 70 + + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + Qt::Orientation::Vertical @@ -168,6 +322,32 @@ 80 + + + + + 0 + 0 + + + + + 11 + false + + + + Check for Updates at Startup + + + + + + + Always Show Changelog + + + @@ -255,32 +435,6 @@ - - - - - 0 - 0 - - - - - 11 - false - - - - Check for Updates at Startup - - - - - - - Always Show Changelog - - - @@ -1521,7 +1675,7 @@ - 7 + 6 0 @@ -1684,6 +1838,12 @@ Controller + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + false + 0 @@ -1696,27 +1856,14 @@ - - Enable Motion Controls - - - - - - - true - - + 0 0 - - - 0 - 0 - + + Enable Motion Controls @@ -1733,20 +1880,38 @@ 0 - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 20 - 20 - - - + + + + + Microphone + + + + + 14 + 33 + 431 + 31 + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 40 + + + + + diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index c2e35f4ee..99a72589f 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -1582,6 +1582,18 @@ Hide Cursor Idle Timeout Hide Cursor Idle Timeout + + Microphone + Microphone + + + None + None + + + Default Device + Default Device + s s