mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 14:19:46 -06:00
Merge pull request #12769 from sepalani/wii-speak
IOS/USB: Emulate Wii Speak using cubeb
This commit is contained in:
@ -247,6 +247,8 @@ add_executable(dolphin-emu
|
||||
DiscordHandler.h
|
||||
DiscordJoinRequestDialog.cpp
|
||||
DiscordJoinRequestDialog.h
|
||||
EmulatedUSB/WiiSpeakWindow.cpp
|
||||
EmulatedUSB/WiiSpeakWindow.h
|
||||
FIFO/FIFOAnalyzer.cpp
|
||||
FIFO/FIFOAnalyzer.h
|
||||
FIFO/FIFOPlayerWindow.cpp
|
||||
|
@ -157,6 +157,7 @@
|
||||
<ClCompile Include="Debugger\WatchWidget.cpp" />
|
||||
<ClCompile Include="DiscordHandler.cpp" />
|
||||
<ClCompile Include="DiscordJoinRequestDialog.cpp" />
|
||||
<ClCompile Include="EmulatedUSB\WiiSpeakWindow.cpp" />
|
||||
<ClCompile Include="FIFO\FIFOAnalyzer.cpp" />
|
||||
<ClCompile Include="FIFO\FIFOPlayerWindow.cpp" />
|
||||
<ClCompile Include="GameList\GameList.cpp" />
|
||||
@ -375,6 +376,7 @@
|
||||
<QtMoc Include="Debugger\WatchWidget.h" />
|
||||
<QtMoc Include="DiscordHandler.h" />
|
||||
<QtMoc Include="DiscordJoinRequestDialog.h" />
|
||||
<QtMoc Include="EmulatedUSB\WiiSpeakWindow.h" />
|
||||
<QtMoc Include="FIFO\FIFOAnalyzer.h" />
|
||||
<QtMoc Include="FIFO\FIFOPlayerWindow.h" />
|
||||
<QtMoc Include="GameList\GameList.h" />
|
||||
|
144
Source/Core/DolphinQt/EmulatedUSB/WiiSpeakWindow.cpp
Normal file
144
Source/Core/DolphinQt/EmulatedUSB/WiiSpeakWindow.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/EmulatedUSB/WiiSpeakWindow.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#ifdef HAVE_CUBEB
|
||||
#include "AudioCommon/CubebUtils.h"
|
||||
#endif
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
WiiSpeakWindow::WiiSpeakWindow(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
// i18n: Window for managing the Wii Speak microphone
|
||||
setWindowTitle(tr("Wii Speak Manager"));
|
||||
setObjectName(QStringLiteral("wii_speak_manager"));
|
||||
setMinimumSize(QSize(700, 200));
|
||||
|
||||
CreateMainWindow();
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&WiiSpeakWindow::OnEmulationStateChanged);
|
||||
|
||||
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
}
|
||||
|
||||
WiiSpeakWindow::~WiiSpeakWindow() = default;
|
||||
|
||||
void WiiSpeakWindow::CreateMainWindow()
|
||||
{
|
||||
auto* main_layout = new QVBoxLayout();
|
||||
auto* 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);
|
||||
|
||||
auto* checkbox_group = new QGroupBox();
|
||||
auto* checkbox_layout = new QHBoxLayout();
|
||||
checkbox_layout->setAlignment(Qt::AlignHCenter);
|
||||
m_checkbox_enabled = new QCheckBox(tr("Emulate Wii Speak"), this);
|
||||
m_checkbox_enabled->setChecked(Config::Get(Config::MAIN_EMULATE_WII_SPEAK));
|
||||
connect(m_checkbox_enabled, &QCheckBox::toggled, this, &WiiSpeakWindow::EmulateWiiSpeak);
|
||||
checkbox_layout->addWidget(m_checkbox_enabled);
|
||||
checkbox_group->setLayout(checkbox_layout);
|
||||
main_layout->addWidget(checkbox_group);
|
||||
|
||||
auto* config_group = new QGroupBox(tr("Microphone Configuration"));
|
||||
auto* config_layout = new QHBoxLayout();
|
||||
|
||||
auto checkbox_mic_muted = new QCheckBox(tr("Mute"), this);
|
||||
checkbox_mic_muted->setChecked(Settings::Instance().IsWiiSpeakMuted());
|
||||
connect(&Settings::Instance(), &Settings::WiiSpeakMuteChanged, checkbox_mic_muted,
|
||||
&QCheckBox::setChecked);
|
||||
connect(checkbox_mic_muted, &QCheckBox::toggled, &Settings::Instance(),
|
||||
&Settings::SetWiiSpeakMuted);
|
||||
checkbox_mic_muted->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
config_layout->addWidget(checkbox_mic_muted);
|
||||
|
||||
auto* volume_layout = new QGridLayout();
|
||||
static constexpr int FILTER_MIN = -50;
|
||||
static constexpr int FILTER_MAX = 50;
|
||||
const int volume_modifier =
|
||||
std::clamp<int>(Config::Get(Config::MAIN_WII_SPEAK_VOLUME_MODIFIER), FILTER_MIN, FILTER_MAX);
|
||||
auto filter_slider = new QSlider(Qt::Horizontal, this);
|
||||
auto slider_label = new QLabel(tr("Volume modifier (value: %1dB)").arg(volume_modifier));
|
||||
connect(filter_slider, &QSlider::valueChanged, this, [slider_label](int value) {
|
||||
Config::SetBaseOrCurrent(Config::MAIN_WII_SPEAK_VOLUME_MODIFIER, 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_combobox_microphones = new QComboBox();
|
||||
#ifndef HAVE_CUBEB
|
||||
m_combobox_microphones->addItem(QLatin1String("(%1)").arg(tr("Audio backend unsupported")),
|
||||
QString{});
|
||||
#else
|
||||
m_combobox_microphones->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_combobox_microphones->addItem(QString::fromStdString(device_name), user_data);
|
||||
}
|
||||
#endif
|
||||
connect(m_combobox_microphones, &QComboBox::currentIndexChanged, this,
|
||||
&WiiSpeakWindow::OnInputDeviceChange);
|
||||
|
||||
auto current_device_id = QString::fromStdString(Config::Get(Config::MAIN_WII_SPEAK_MICROPHONE));
|
||||
m_combobox_microphones->setCurrentIndex(m_combobox_microphones->findData(current_device_id));
|
||||
config_layout->addWidget(m_combobox_microphones);
|
||||
|
||||
config_group->setLayout(config_layout);
|
||||
main_layout->addWidget(config_group);
|
||||
|
||||
setLayout(main_layout);
|
||||
}
|
||||
|
||||
void WiiSpeakWindow::EmulateWiiSpeak(bool emulate)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_EMULATE_WII_SPEAK, emulate);
|
||||
}
|
||||
|
||||
void WiiSpeakWindow::OnEmulationStateChanged(Core::State state)
|
||||
{
|
||||
const bool running = state != Core::State::Uninitialized;
|
||||
|
||||
m_checkbox_enabled->setEnabled(!running);
|
||||
m_combobox_microphones->setEnabled(!running);
|
||||
}
|
||||
|
||||
void WiiSpeakWindow::OnInputDeviceChange()
|
||||
{
|
||||
auto user_data = m_combobox_microphones->currentData();
|
||||
if (!user_data.isValid())
|
||||
return;
|
||||
|
||||
const std::string device_id = user_data.toString().toStdString();
|
||||
Config::SetBaseOrCurrent(Config::MAIN_WII_SPEAK_MICROPHONE, device_id);
|
||||
}
|
28
Source/Core/DolphinQt/EmulatedUSB/WiiSpeakWindow.h
Normal file
28
Source/Core/DolphinQt/EmulatedUSB/WiiSpeakWindow.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "Core/Core.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
|
||||
class WiiSpeakWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WiiSpeakWindow(QWidget* parent = nullptr);
|
||||
~WiiSpeakWindow() override;
|
||||
|
||||
private:
|
||||
void CreateMainWindow();
|
||||
void OnEmulationStateChanged(Core::State state);
|
||||
void EmulateWiiSpeak(bool emulate);
|
||||
void OnInputDeviceChange();
|
||||
|
||||
QCheckBox* m_checkbox_enabled;
|
||||
QComboBox* m_combobox_microphones;
|
||||
};
|
@ -296,6 +296,15 @@ void HotkeyScheduler::Run()
|
||||
Settings::Instance().SetUSBKeyboardConnected(
|
||||
!Settings::Instance().IsUSBKeyboardConnected());
|
||||
}
|
||||
|
||||
if (IsHotkey(HK_TOGGLE_WII_SPEAK_MUTE))
|
||||
{
|
||||
const bool muted = !Settings::Instance().IsWiiSpeakMuted();
|
||||
Settings::Instance().SetWiiSpeakMuted(muted);
|
||||
// i18n: Wii Speak (un)muted notification message
|
||||
const QString msg = tr("Wii Speak %1").arg(muted ? tr("muted") : tr("unmuted"));
|
||||
OSD::AddMessage(msg.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
if (IsHotkey(HK_PREV_WIIMOTE_PROFILE_1))
|
||||
|
@ -95,6 +95,7 @@
|
||||
#include "DolphinQt/Debugger/ThreadWidget.h"
|
||||
#include "DolphinQt/Debugger/WatchWidget.h"
|
||||
#include "DolphinQt/DiscordHandler.h"
|
||||
#include "DolphinQt/EmulatedUSB/WiiSpeakWindow.h"
|
||||
#include "DolphinQt/FIFO/FIFOPlayerWindow.h"
|
||||
#include "DolphinQt/GCMemcardManager.h"
|
||||
#include "DolphinQt/GameList/GameList.h"
|
||||
@ -581,6 +582,7 @@ void MainWindow::ConnectMenuBar()
|
||||
connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer);
|
||||
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::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote);
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
@ -1440,6 +1442,18 @@ void MainWindow::ShowInfinityBase()
|
||||
m_infinity_window->activateWindow();
|
||||
}
|
||||
|
||||
void MainWindow::ShowWiiSpeakWindow()
|
||||
{
|
||||
if (!m_wii_speak_window)
|
||||
{
|
||||
m_wii_speak_window = new WiiSpeakWindow();
|
||||
}
|
||||
|
||||
m_wii_speak_window->show();
|
||||
m_wii_speak_window->raise();
|
||||
m_wii_speak_window->activateWindow();
|
||||
}
|
||||
|
||||
void MainWindow::StateLoad()
|
||||
{
|
||||
QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ?
|
||||
|
@ -56,6 +56,7 @@ class ThreadWidget;
|
||||
class ToolBar;
|
||||
class WatchWidget;
|
||||
class WiiTASInputWindow;
|
||||
class WiiSpeakWindow;
|
||||
struct WindowSystemInfo;
|
||||
|
||||
namespace Core
|
||||
@ -177,6 +178,7 @@ private:
|
||||
void ShowFIFOPlayer();
|
||||
void ShowSkylanderPortal();
|
||||
void ShowInfinityBase();
|
||||
void ShowWiiSpeakWindow();
|
||||
void ShowMemcardManager();
|
||||
void ShowResourcePackManager();
|
||||
void ShowCheatsManager();
|
||||
@ -250,6 +252,7 @@ private:
|
||||
FIFOPlayerWindow* m_fifo_window = nullptr;
|
||||
SkylanderPortalWindow* m_skylander_window = nullptr;
|
||||
InfinityBaseWindow* m_infinity_window = nullptr;
|
||||
WiiSpeakWindow* m_wii_speak_window = nullptr;
|
||||
MappingWindow* m_hotkey_window = nullptr;
|
||||
FreeLookWindow* m_freelook_window = nullptr;
|
||||
|
||||
|
@ -281,6 +281,7 @@ void MenuBar::AddToolsMenu()
|
||||
auto* usb_device_menu = new QMenu(tr("Emulated USB Devices"), tools_menu);
|
||||
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);
|
||||
tools_menu->addMenu(usb_device_menu);
|
||||
|
||||
tools_menu->addSeparator();
|
||||
|
@ -94,6 +94,7 @@ signals:
|
||||
void ShowResourcePackManager();
|
||||
void ShowSkylanderPortal();
|
||||
void ShowInfinityBase();
|
||||
void ShowWiiSpeakWindow();
|
||||
void ConnectWiiRemote(int id);
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
|
@ -795,6 +795,20 @@ void Settings::SetUSBKeyboardConnected(bool connected)
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::IsWiiSpeakMuted() const
|
||||
{
|
||||
return Config::Get(Config::MAIN_WII_SPEAK_MUTED);
|
||||
}
|
||||
|
||||
void Settings::SetWiiSpeakMuted(bool muted)
|
||||
{
|
||||
if (IsWiiSpeakMuted() == muted)
|
||||
return;
|
||||
|
||||
Config::SetBaseOrCurrent(Config::MAIN_WII_SPEAK_MUTED, muted);
|
||||
emit WiiSpeakMuteChanged(muted);
|
||||
}
|
||||
|
||||
void Settings::SetIsContinuouslyFrameStepping(bool is_stepping)
|
||||
{
|
||||
m_continuously_frame_stepping = is_stepping;
|
||||
|
@ -119,6 +119,9 @@ public:
|
||||
bool IsUSBKeyboardConnected() const;
|
||||
void SetUSBKeyboardConnected(bool connected);
|
||||
|
||||
bool IsWiiSpeakMuted() const;
|
||||
void SetWiiSpeakMuted(bool muted);
|
||||
|
||||
void SetIsContinuouslyFrameStepping(bool is_stepping);
|
||||
bool GetIsContinuouslyFrameStepping() const;
|
||||
|
||||
@ -222,6 +225,7 @@ signals:
|
||||
void DevicesChanged();
|
||||
void SDCardInsertionChanged(bool inserted);
|
||||
void USBKeyboardConnectionChanged(bool connected);
|
||||
void WiiSpeakMuteChanged(bool muted);
|
||||
void EnableGfxModsChanged(bool enabled);
|
||||
|
||||
private:
|
||||
|
Reference in New Issue
Block a user