USB: Add emulated Logitech USB Microphone

Co-authored by: supermilkdude67 <[email protected]>
This commit is contained in:
Biendeo
2025-11-25 10:40:30 +11:00
parent 9c28f19e56
commit 07324ca067
21 changed files with 1185 additions and 39 deletions
+2
View File
@@ -262,6 +262,8 @@ add_executable(dolphin-emu
DiscordJoinRequestDialog.h
EmulatedUSB/WiiSpeakWindow.cpp
EmulatedUSB/WiiSpeakWindow.h
EmulatedUSB/LogitechMicWindow.cpp
EmulatedUSB/LogitechMicWindow.h
FIFO/FIFOAnalyzer.cpp
FIFO/FIFOAnalyzer.h
FIFO/FIFOPlayerWindow.cpp
+2
View File
@@ -160,6 +160,7 @@
<ClCompile Include="Debugger\WatchWidget.cpp" />
<ClCompile Include="DiscordHandler.cpp" />
<ClCompile Include="DiscordJoinRequestDialog.cpp" />
<ClCompile Include="EmulatedUSB\LogitechMicWindow.cpp" />
<ClCompile Include="EmulatedUSB\WiiSpeakWindow.cpp" />
<ClCompile Include="FIFO\FIFOAnalyzer.cpp" />
<ClCompile Include="FIFO\FIFOPlayerWindow.cpp" />
@@ -387,6 +388,7 @@
<QtMoc Include="Debugger\WatchWidget.h" />
<QtMoc Include="DiscordHandler.h" />
<QtMoc Include="DiscordJoinRequestDialog.h" />
<QtMoc Include="EmulatedUSB\LogitechMicWindow.h" />
<QtMoc Include="EmulatedUSB\WiiSpeakWindow.h" />
<QtMoc Include="FIFO\FIFOAnalyzer.h" />
<QtMoc Include="FIFO\FIFOPlayerWindow.h" />
@@ -0,0 +1,162 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/EmulatedUSB/LogitechMicWindow.h"
#include <algorithm>
#include <QCheckBox>
#include <QComboBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QString>
#ifdef HAVE_CUBEB
#include "AudioCommon/CubebUtils.h"
#endif
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
LogitechMicWindow::LogitechMicWindow(QWidget* parent) : QWidget(parent)
{
setWindowTitle(tr("Logitech USB Microphone Manager"));
setWindowIcon(Resources::GetAppIcon());
setObjectName(QStringLiteral("logitech_mic_manager"));
setMinimumSize(QSize(700, 200));
CreateMainWindow();
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&LogitechMicWindow::OnEmulationStateChanged);
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
}
void LogitechMicWindow::CreateMainWindow()
{
auto* const main_layout = new QVBoxLayout;
auto* const label = new QLabel;
label->setText(QStringLiteral("<center><i>%1</i></center>")
.arg(tr("Some settings cannot be changed when emulation is running.")));
main_layout->addWidget(label);
CreateCheckboxGroup(main_layout);
CreateMicrophoneConfigurationGroup(main_layout);
setLayout(main_layout);
}
void LogitechMicWindow::CreateCheckboxGroup(QVBoxLayout* main_layout)
{
auto* checkbox_group = new QGroupBox();
auto* checkbox_layout = new QHBoxLayout();
checkbox_layout->setAlignment(Qt::AlignHCenter);
for (std::size_t index = 0; index != Config::EMULATED_LOGITECH_MIC_COUNT; ++index)
{
m_mic_enabled_checkboxes[index] = new ConfigBool(
tr("Emulate Logitech USB Mic %1").arg(index + 1), Config::MAIN_EMULATE_LOGITECH_MIC[index]);
checkbox_layout->addWidget(m_mic_enabled_checkboxes[index]);
}
checkbox_group->setLayout(checkbox_layout);
main_layout->addWidget(checkbox_group);
}
void LogitechMicWindow::CreateMicrophoneConfigurationGroup(QVBoxLayout* main_layout)
{
auto* main_config_group = new QGroupBox(tr("Microphone Configuration"));
auto* main_config_layout = new QVBoxLayout();
for (std::size_t index = 0; index != Config::EMULATED_LOGITECH_MIC_COUNT; ++index)
{
// i18n: %1 is a number from 1 to 4.
auto* config_group = new QGroupBox(tr("Microphone %1 Configuration").arg(index + 1));
auto* const config_layout = new QHBoxLayout();
auto* const mic_muted = new ConfigBool(tr("Mute"), Config::MAIN_LOGITECH_MIC_MUTED[index]);
mic_muted->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
config_layout->addWidget(mic_muted);
static constexpr int FILTER_MIN = -50;
static constexpr int FILTER_MAX = 50;
auto* volume_layout = new QGridLayout();
const int volume_modifier = std::clamp<int>(
Config::Get(Config::MAIN_LOGITECH_MIC_VOLUME_MODIFIER[index]), FILTER_MIN, FILTER_MAX);
auto* const filter_slider = new QSlider(Qt::Horizontal, this);
auto* const slider_label = new QLabel(tr("Volume modifier (value: %1dB)").arg(volume_modifier));
connect(filter_slider, &QSlider::valueChanged, this, [slider_label, index](int value) {
Config::SetBaseOrCurrent(Config::MAIN_LOGITECH_MIC_VOLUME_MODIFIER[index], value);
slider_label->setText(tr("Volume modifier (value: %1dB)").arg(value));
});
filter_slider->setMinimum(FILTER_MIN);
filter_slider->setMaximum(FILTER_MAX);
filter_slider->setValue(volume_modifier);
filter_slider->setTickPosition(QSlider::TicksBothSides);
filter_slider->setTickInterval(10);
filter_slider->setSingleStep(1);
volume_layout->addWidget(new QLabel(QStringLiteral("%1dB").arg(FILTER_MIN)), 0, 0,
Qt::AlignLeft);
volume_layout->addWidget(slider_label, 0, 1, Qt::AlignCenter);
volume_layout->addWidget(new QLabel(QStringLiteral("%1dB").arg(FILTER_MAX)), 0, 2,
Qt::AlignRight);
volume_layout->addWidget(filter_slider, 1, 0, 1, 3);
config_layout->addLayout(volume_layout);
config_layout->setStretch(1, 3);
m_mic_device_comboboxes[index] = new QComboBox();
#ifndef HAVE_CUBEB
m_combobox_microphone[index]->addItem(
QLatin1String("(%1)").arg(tr("Audio backend unsupported")), QString{});
#else
m_mic_device_comboboxes[index]->addItem(
QLatin1String("(%1)").arg(tr("Autodetect preferred microphone")), QString{});
for (auto& [device_id, device_name] : CubebUtils::ListInputDevices())
{
const auto user_data = QString::fromStdString(device_id);
m_mic_device_comboboxes[index]->addItem(QString::fromStdString(device_name), user_data);
}
#endif
auto current_device_id =
QString::fromStdString(Config::Get(Config::MAIN_LOGITECH_MIC_MICROPHONE[index]));
m_mic_device_comboboxes[index]->setCurrentIndex(
m_mic_device_comboboxes[index]->findData(current_device_id));
connect(m_mic_device_comboboxes[index], &QComboBox::currentIndexChanged, this,
[index, this]() { OnInputDeviceChange(index); });
config_layout->addWidget(m_mic_device_comboboxes[index]);
config_group->setLayout(config_layout);
main_config_layout->addWidget(config_group);
}
main_config_group->setLayout(main_config_layout);
main_layout->addWidget(main_config_group);
}
void LogitechMicWindow::OnEmulationStateChanged(Core::State state)
{
const bool running = state != Core::State::Uninitialized;
for (std::size_t index = 0; index != Config::EMULATED_LOGITECH_MIC_COUNT; ++index)
{
m_mic_enabled_checkboxes[index]->setEnabled(!running);
}
}
void LogitechMicWindow::OnInputDeviceChange(std::size_t index)
{
auto user_data = m_mic_device_comboboxes[index]->currentData();
if (user_data.isValid())
{
const std::string device_id = user_data.toString().toStdString();
Config::SetBaseOrCurrent(Config::MAIN_LOGITECH_MIC_MICROPHONE[index], device_id);
}
}
@@ -0,0 +1,31 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QVBoxLayout>
#include <QWidget>
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
class ConfigBool;
class QComboBox;
class LogitechMicWindow final : public QWidget
{
Q_OBJECT
public:
explicit LogitechMicWindow(QWidget* parent = nullptr);
private:
void CreateMainWindow();
void CreateCheckboxGroup(QVBoxLayout* main_layout);
void CreateMicrophoneConfigurationGroup(QVBoxLayout* main_layout);
void OnEmulationStateChanged(Core::State state);
void OnInputDeviceChange(std::size_t index);
std::array<ConfigBool*, Config::EMULATED_LOGITECH_MIC_COUNT> m_mic_enabled_checkboxes;
std::array<QComboBox*, 4> m_mic_device_comboboxes;
};
+14
View File
@@ -93,6 +93,7 @@
#include "DolphinQt/Debugger/ThreadWidget.h"
#include "DolphinQt/Debugger/WatchWidget.h"
#include "DolphinQt/DiscordHandler.h"
#include "DolphinQt/EmulatedUSB/LogitechMicWindow.h"
#include "DolphinQt/EmulatedUSB/WiiSpeakWindow.h"
#include "DolphinQt/FIFO/FIFOPlayerWindow.h"
#include "DolphinQt/GCMemcardManager.h"
@@ -571,6 +572,7 @@ void MainWindow::ConnectMenuBar()
connect(m_menu_bar, &MenuBar::ShowSkylanderPortal, this, &MainWindow::ShowSkylanderPortal);
connect(m_menu_bar, &MenuBar::ShowInfinityBase, this, &MainWindow::ShowInfinityBase);
connect(m_menu_bar, &MenuBar::ShowWiiSpeakWindow, this, &MainWindow::ShowWiiSpeakWindow);
connect(m_menu_bar, &MenuBar::ShowLogitechMicWindow, this, &MainWindow::ShowLogitechMicWindow);
connect(m_menu_bar, &MenuBar::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote);
#ifdef USE_RETRO_ACHIEVEMENTS
@@ -1424,6 +1426,18 @@ void MainWindow::ShowWiiSpeakWindow()
m_wii_speak_window->activateWindow();
}
void MainWindow::ShowLogitechMicWindow()
{
if (!m_logitech_mic_window)
{
m_logitech_mic_window = new LogitechMicWindow();
}
m_logitech_mic_window->show();
m_logitech_mic_window->raise();
m_logitech_mic_window->activateWindow();
}
void MainWindow::StateLoad()
{
QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ?
+3
View File
@@ -55,6 +55,7 @@ class ToolBar;
class WatchWidget;
class WiiTASInputWindow;
class WiiSpeakWindow;
class LogitechMicWindow;
struct WindowSystemInfo;
namespace Core
@@ -177,6 +178,7 @@ private:
void ShowSkylanderPortal();
void ShowInfinityBase();
void ShowWiiSpeakWindow();
void ShowLogitechMicWindow();
void ShowMemcardManager();
void ShowResourcePackManager();
void ShowCheatsManager();
@@ -252,6 +254,7 @@ private:
SkylanderPortalWindow* m_skylander_window = nullptr;
InfinityBaseWindow* m_infinity_window = nullptr;
WiiSpeakWindow* m_wii_speak_window = nullptr;
LogitechMicWindow* m_logitech_mic_window = nullptr;
MappingWindow* m_hotkey_window = nullptr;
FreeLookWindow* m_freelook_window = nullptr;
+1
View File
@@ -281,6 +281,7 @@ void MenuBar::AddToolsMenu()
usb_device_menu->addAction(tr("&Skylanders Portal"), this, &MenuBar::ShowSkylanderPortal);
usb_device_menu->addAction(tr("&Infinity Base"), this, &MenuBar::ShowInfinityBase);
usb_device_menu->addAction(tr("&Wii Speak"), this, &MenuBar::ShowWiiSpeakWindow);
usb_device_menu->addAction(tr("&Logitech USB Microphone"), this, &MenuBar::ShowLogitechMicWindow);
tools_menu->addMenu(usb_device_menu);
tools_menu->addSeparator();
+1
View File
@@ -98,6 +98,7 @@ signals:
void ShowSkylanderPortal();
void ShowInfinityBase();
void ShowWiiSpeakWindow();
void ShowLogitechMicWindow();
void ConnectWiiRemote(int id);
#ifdef USE_RETRO_ACHIEVEMENTS