Merge 99e60d2c82
into 49ebdaaae3
This commit is contained in:
commit
caa006ab20
|
@ -427,12 +427,16 @@ add_library(core
|
|||
IOS/USB/Common.h
|
||||
IOS/USB/Emulated/Infinity.cpp
|
||||
IOS/USB/Emulated/Infinity.h
|
||||
IOS/USB/Emulated/Microphone.cpp
|
||||
IOS/USB/Emulated/Microphone.h
|
||||
IOS/USB/Emulated/Skylanders/Skylander.cpp
|
||||
IOS/USB/Emulated/Skylanders/Skylander.h
|
||||
IOS/USB/Emulated/Skylanders/SkylanderCrypto.cpp
|
||||
IOS/USB/Emulated/Skylanders/SkylanderCrypto.h
|
||||
IOS/USB/Emulated/Skylanders/SkylanderFigure.cpp
|
||||
IOS/USB/Emulated/Skylanders/SkylanderFigure.h
|
||||
IOS/USB/Emulated/WiiSpeak.cpp
|
||||
IOS/USB/Emulated/WiiSpeak.h
|
||||
IOS/USB/Host.cpp
|
||||
IOS/USB/Host.h
|
||||
IOS/USB/OH0/OH0.cpp
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cubeb/cubeb.h>
|
||||
|
||||
#include "AudioCommon/CubebUtils.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/IOS/USB/Emulated/Microphone.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Objbase.h>
|
||||
#endif
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
Microphone::Microphone()
|
||||
{
|
||||
StreamInit();
|
||||
}
|
||||
|
||||
Microphone::~Microphone()
|
||||
{
|
||||
StreamTerminate();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_should_couninit)
|
||||
{
|
||||
Common::Event sync_event;
|
||||
m_work_queue.EmplaceItem([this, &sync_event] {
|
||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||
m_should_couninit = false;
|
||||
CoUninitialize();
|
||||
});
|
||||
sync_event.Wait();
|
||||
}
|
||||
m_coinit_success = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Microphone::StreamInit()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!m_coinit_success)
|
||||
return;
|
||||
Common::Event sync_event;
|
||||
m_work_queue.EmplaceItem([this, &sync_event] {
|
||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||
#endif
|
||||
m_cubeb_ctx = CubebUtils::GetContext();
|
||||
#ifdef _WIN32
|
||||
});
|
||||
sync_event.Wait();
|
||||
#endif
|
||||
|
||||
// TODO: Not here but rather inside the WiiSpeak device if possible?
|
||||
StreamStart();
|
||||
}
|
||||
|
||||
void Microphone::StreamTerminate()
|
||||
{
|
||||
StopStream();
|
||||
|
||||
if (m_cubeb_ctx)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!m_coinit_success)
|
||||
return;
|
||||
Common::Event sync_event;
|
||||
m_work_queue.EmplaceItem([this, &sync_event] {
|
||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||
#endif
|
||||
m_cubeb_ctx.reset();
|
||||
#ifdef _WIN32
|
||||
});
|
||||
sync_event.Wait();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void state_callback(cubeb_stream* stream, void* user_data, cubeb_state state)
|
||||
{
|
||||
}
|
||||
|
||||
void Microphone::StreamStart()
|
||||
{
|
||||
if (!m_cubeb_ctx)
|
||||
return;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!m_coinit_success)
|
||||
return;
|
||||
Common::Event sync_event;
|
||||
m_work_queue.EmplaceItem([this, &sync_event] {
|
||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||
#endif
|
||||
stream_size = buff_size_samples * 500;
|
||||
stream_buffer = new s16[stream_size];
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.format = CUBEB_SAMPLE_S16LE;
|
||||
params.rate = SAMPLING_RATE;
|
||||
params.channels = 1;
|
||||
params.layout = CUBEB_LAYOUT_MONO;
|
||||
|
||||
u32 minimum_latency;
|
||||
if (cubeb_get_min_latency(m_cubeb_ctx.get(), ¶ms, &minimum_latency) != CUBEB_OK)
|
||||
{
|
||||
WARN_LOG_FMT(EXPANSIONINTERFACE, "Error getting minimum latency");
|
||||
}
|
||||
|
||||
if (cubeb_stream_init(m_cubeb_ctx.get(), &m_cubeb_stream,
|
||||
"Dolphin Emulated GameCube Microphone", nullptr, ¶ms, nullptr,
|
||||
nullptr, std::max<u32>(16, minimum_latency), DataCallback, state_callback,
|
||||
this) != CUBEB_OK)
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_USB, "Error initializing cubeb stream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cubeb_stream_start(m_cubeb_stream) != CUBEB_OK)
|
||||
{
|
||||
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error starting cubeb stream");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "started cubeb stream");
|
||||
#ifdef _WIN32
|
||||
});
|
||||
sync_event.Wait();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Microphone::StopStream()
|
||||
{
|
||||
if (m_cubeb_stream)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Common::Event sync_event;
|
||||
m_work_queue.EmplaceItem([this, &sync_event] {
|
||||
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
|
||||
#endif
|
||||
if (cubeb_stream_stop(m_cubeb_stream) != CUBEB_OK)
|
||||
ERROR_LOG_FMT(IOS_USB, "Error stopping cubeb stream");
|
||||
cubeb_stream_destroy(m_cubeb_stream);
|
||||
m_cubeb_stream = nullptr;
|
||||
#ifdef _WIN32
|
||||
});
|
||||
sync_event.Wait();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
long Microphone::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* /*output_buffer*/, long nframes)
|
||||
{
|
||||
auto* mic = static_cast<Microphone*>(user_data);
|
||||
|
||||
std::lock_guard lk(mic->ring_lock);
|
||||
|
||||
const s16* buff_in = static_cast<const s16*>(input_buffer);
|
||||
for (long i = 0; i < nframes; i++)
|
||||
{
|
||||
mic->stream_buffer[mic->stream_wpos] = Common::swap16(buff_in[i]);
|
||||
mic->stream_wpos = (mic->stream_wpos + 1) % mic->stream_size;
|
||||
}
|
||||
|
||||
mic->samples_avail += nframes;
|
||||
if (mic->samples_avail > mic->stream_size)
|
||||
{
|
||||
mic->samples_avail = 0;
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
void Microphone::ReadIntoBuffer(u8* dst, u32 size)
|
||||
{
|
||||
std::lock_guard lk(ring_lock);
|
||||
|
||||
if (samples_avail >= buff_size_samples)
|
||||
{
|
||||
u8* last_buffer = reinterpret_cast<u8*>(&stream_buffer[stream_rpos]);
|
||||
std::memcpy(dst, static_cast<u8*>(last_buffer), size);
|
||||
|
||||
samples_avail -= buff_size_samples;
|
||||
|
||||
stream_rpos += buff_size_samples;
|
||||
stream_rpos %= stream_size;
|
||||
}
|
||||
}
|
||||
|
||||
bool Microphone::HasData() const
|
||||
{
|
||||
return samples_avail > 0;
|
||||
}
|
||||
} // namespace IOS::HLE::USB
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
struct cubeb;
|
||||
struct cubeb_stream;
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class Microphone final
|
||||
{
|
||||
public:
|
||||
Microphone();
|
||||
~Microphone();
|
||||
|
||||
void StreamInit();
|
||||
void StreamTerminate();
|
||||
void ReadIntoBuffer(u8* dst, u32 size);
|
||||
bool HasData() const;
|
||||
|
||||
private:
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long nframes);
|
||||
|
||||
void StreamStart();
|
||||
void StopStream();
|
||||
|
||||
static constexpr u32 SAMPLING_RATE = 8000;
|
||||
static constexpr u32 BUFFER_SIZE = SAMPLING_RATE / 2;
|
||||
|
||||
s16* stream_buffer;
|
||||
std::mutex ring_lock;
|
||||
std::shared_ptr<cubeb> m_cubeb_ctx = nullptr;
|
||||
cubeb_stream* m_cubeb_stream = nullptr;
|
||||
std::vector<u8> m_temp_buffer{};
|
||||
|
||||
int stream_size;
|
||||
int stream_wpos;
|
||||
int stream_rpos;
|
||||
int samples_avail;
|
||||
int buff_size_samples = 16;
|
||||
|
||||
#ifdef _WIN32
|
||||
Common::WorkQueueThread<std::function<void()>> m_work_queue;
|
||||
bool m_coinit_success = false;
|
||||
bool m_should_couninit = false;
|
||||
#endif
|
||||
};
|
||||
} // namespace IOS::HLE::USB
|
|
@ -0,0 +1,314 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/IOS/USB/Emulated/WiiSpeak.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
WiiSpeak::WiiSpeak(IOS::HLE::EmulationKernel& ios, const std::string& device_name) : m_ios(ios)
|
||||
{
|
||||
m_vid = 0x57E;
|
||||
m_pid = 0x0308;
|
||||
m_id = (u64(m_vid) << 32 | u64(m_pid) << 16 | u64(9) << 8 | u64(1));
|
||||
m_device_descriptor =
|
||||
DeviceDescriptor{0x12, 0x1, 0x200, 0, 0, 0, 0x10, 0x57E, 0x0308, 0x0214, 0x1, 0x2, 0x0, 0x1};
|
||||
m_config_descriptor.emplace_back(ConfigDescriptor{0x9, 0x2, 0x0030, 0x1, 0x1, 0x0, 0x80, 0x32});
|
||||
m_interface_descriptor.emplace_back(
|
||||
InterfaceDescriptor{0x9, 0x4, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0xFF, 0x0});
|
||||
m_interface_descriptor.emplace_back(
|
||||
InterfaceDescriptor{0x9, 0x4, 0x0, 0x01, 0x03, 0xFF, 0xFF, 0xFF, 0x0});
|
||||
m_endpoint_descriptor.emplace_back(EndpointDescriptor{0x7, 0x5, 0x81, 0x1, 0x0020, 0x1});
|
||||
m_endpoint_descriptor.emplace_back(EndpointDescriptor{0x7, 0x5, 0x2, 0x2, 0x0020, 0});
|
||||
m_endpoint_descriptor.emplace_back(EndpointDescriptor{0x7, 0x5, 0x3, 0x1, 0x0040, 1});
|
||||
|
||||
m_microphone = std::make_unique<Microphone>();
|
||||
}
|
||||
|
||||
DeviceDescriptor WiiSpeak::GetDeviceDescriptor() const
|
||||
{
|
||||
return m_device_descriptor;
|
||||
}
|
||||
|
||||
std::vector<ConfigDescriptor> WiiSpeak::GetConfigurations() const
|
||||
{
|
||||
return m_config_descriptor;
|
||||
}
|
||||
|
||||
std::vector<InterfaceDescriptor> WiiSpeak::GetInterfaces(u8 config) const
|
||||
{
|
||||
return m_interface_descriptor;
|
||||
}
|
||||
|
||||
std::vector<EndpointDescriptor> WiiSpeak::GetEndpoints(u8 config, u8 interface, u8 alt) const
|
||||
{
|
||||
return m_endpoint_descriptor;
|
||||
}
|
||||
|
||||
bool WiiSpeak::Attach()
|
||||
{
|
||||
if (m_device_attached)
|
||||
return true;
|
||||
|
||||
DEBUG_LOG_FMT(IOS_USB, "[{:04x}:{:04x}] Opening device", m_vid, m_pid);
|
||||
m_device_attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiiSpeak::AttachAndChangeInterface(const u8 interface)
|
||||
{
|
||||
if (!Attach())
|
||||
return false;
|
||||
|
||||
if (interface != m_active_interface)
|
||||
return ChangeInterface(interface) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int WiiSpeak::CancelTransfer(const u8 endpoint)
|
||||
{
|
||||
INFO_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Cancelling transfers (endpoint {:#x})", m_vid, m_pid,
|
||||
m_active_interface, endpoint);
|
||||
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
int WiiSpeak::ChangeInterface(const u8 interface)
|
||||
{
|
||||
DEBUG_LOG_FMT(IOS_USB, "[{:04x}:{:04x} {}] Changing interface to {}", m_vid, m_pid,
|
||||
m_active_interface, interface);
|
||||
m_active_interface = interface;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WiiSpeak::GetNumberOfAltSettings(u8 interface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WiiSpeak::SetAltSetting(u8 alt_setting)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WiiSpeak::SubmitTransfer(std::unique_ptr<CtrlMessage> cmd)
|
||||
{
|
||||
DEBUG_LOG_FMT(IOS_USB,
|
||||
"[{:04x}:{:04x} {}] Control: bRequestType={:02x} bRequest={:02x} wValue={:04x}"
|
||||
" wIndex={:04x} wLength={:04x}",
|
||||
m_vid, m_pid, m_active_interface, cmd->request_type, cmd->request, cmd->value,
|
||||
cmd->index, cmd->length);
|
||||
|
||||
switch (cmd->request_type << 8 | cmd->request)
|
||||
{
|
||||
case USBHDR(DIR_DEVICE2HOST, TYPE_STANDARD, REC_INTERFACE, REQUEST_GET_INTERFACE):
|
||||
{
|
||||
std::array<u8, 1> data{1};
|
||||
cmd->FillBuffer(data.data(), 1);
|
||||
cmd->ScheduleTransferCompletion(1, 100);
|
||||
break;
|
||||
}
|
||||
case USBHDR(DIR_HOST2DEVICE, TYPE_VENDOR, REC_INTERFACE, 0):
|
||||
{
|
||||
init = false;
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
break;
|
||||
}
|
||||
case USBHDR(DIR_DEVICE2HOST, TYPE_VENDOR, REC_INTERFACE, REQUEST_GET_DESCRIPTOR):
|
||||
{
|
||||
if (!init)
|
||||
{
|
||||
std::array<u8, 1> data{0};
|
||||
cmd->FillBuffer(data.data(), 1);
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
init = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::array<u8, 1> data{1};
|
||||
cmd->FillBuffer(data.data(), 1);
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case USBHDR(DIR_HOST2DEVICE, TYPE_VENDOR, REC_INTERFACE, 1):
|
||||
SetRegister(cmd);
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
break;
|
||||
case USBHDR(DIR_DEVICE2HOST, TYPE_VENDOR, REC_INTERFACE, 2):
|
||||
GetRegister(cmd);
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
NOTICE_LOG_FMT(IOS_USB, "Unknown command");
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
}
|
||||
|
||||
return IPC_SUCCESS;
|
||||
};
|
||||
int WiiSpeak::SubmitTransfer(std::unique_ptr<BulkMessage> cmd)
|
||||
{
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
return IPC_SUCCESS;
|
||||
};
|
||||
int WiiSpeak::SubmitTransfer(std::unique_ptr<IntrMessage> cmd)
|
||||
{
|
||||
m_ios.EnqueueIPCReply(cmd->ios_request, IPC_SUCCESS);
|
||||
return IPC_SUCCESS;
|
||||
};
|
||||
|
||||
int WiiSpeak::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
|
||||
{
|
||||
auto& system = m_ios.GetSystem();
|
||||
auto& memory = system.GetMemory();
|
||||
|
||||
u8* packets = memory.GetPointer(cmd->data_address);
|
||||
if (cmd->endpoint == 0x81 && m_microphone->HasData())
|
||||
m_microphone->ReadIntoBuffer(packets, cmd->length);
|
||||
|
||||
// TODO: Figure out proper timings.
|
||||
cmd->ScheduleTransferCompletion(IPC_SUCCESS, 2500);
|
||||
return IPC_SUCCESS;
|
||||
};
|
||||
|
||||
void WiiSpeak::SetRegister(std::unique_ptr<CtrlMessage>& cmd)
|
||||
{
|
||||
auto& system = m_ios.GetSystem();
|
||||
auto& memory = system.GetMemory();
|
||||
const u8 reg = memory.Read_U8(cmd->data_address + 1) & ~1;
|
||||
const u16 arg1 = memory.Read_U16(cmd->data_address + 2);
|
||||
const u16 arg2 = memory.Read_U16(cmd->data_address + 4);
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case SAMPLER_STATE:
|
||||
sampler.sample_on = !!arg1;
|
||||
break;
|
||||
case SAMPLER_FREQ:
|
||||
switch (arg1)
|
||||
{
|
||||
case FREQ_8KHZ:
|
||||
// TODO: I have never seen it not be 8000 kHz
|
||||
sampler.freq = 8000;
|
||||
break;
|
||||
case FREQ_11KHZ:
|
||||
sampler.freq = 11025;
|
||||
break;
|
||||
case FREQ_RESERVED:
|
||||
case FREQ_16KHZ:
|
||||
default:
|
||||
sampler.freq = 16000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAMPLER_GAIN:
|
||||
switch (arg1 & ~0x300)
|
||||
{
|
||||
case GAIN_00dB:
|
||||
sampler.gain = 0;
|
||||
break;
|
||||
case GAIN_15dB:
|
||||
sampler.gain = 15;
|
||||
break;
|
||||
case GAIN_30dB:
|
||||
sampler.gain = 30;
|
||||
break;
|
||||
case GAIN_36dB:
|
||||
default:
|
||||
sampler.gain = 36;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EC_STATE:
|
||||
sampler.ec_reset = !!arg1;
|
||||
break;
|
||||
case SP_STATE:
|
||||
switch (arg1)
|
||||
{
|
||||
case SP_ENABLE:
|
||||
sampler.sp_on = arg2 == 0;
|
||||
break;
|
||||
case SP_SIN:
|
||||
case SP_SOUT:
|
||||
case SP_RIN:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAMPLER_MUTE:
|
||||
sampler.mute = !!arg1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WiiSpeak::GetRegister(std::unique_ptr<CtrlMessage>& cmd)
|
||||
{
|
||||
auto& system = m_ios.GetSystem();
|
||||
auto& memory = system.GetMemory();
|
||||
const u8 reg = memory.Read_U8(cmd->data_address + 1) & ~1;
|
||||
const u32 arg1 = cmd->data_address + 2;
|
||||
const u32 arg2 = cmd->data_address + 4;
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case SAMPLER_STATE:
|
||||
memory.Write_U16(sampler.sample_on ? 1 : 0, arg1);
|
||||
break;
|
||||
case SAMPLER_FREQ:
|
||||
switch (sampler.freq)
|
||||
{
|
||||
case 8000:
|
||||
memory.Write_U16(FREQ_8KHZ, arg1);
|
||||
break;
|
||||
case 11025:
|
||||
memory.Write_U16(FREQ_11KHZ, arg1);
|
||||
break;
|
||||
case 16000:
|
||||
default:
|
||||
memory.Write_U16(FREQ_16KHZ, arg1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAMPLER_GAIN:
|
||||
switch (sampler.gain)
|
||||
{
|
||||
case 0:
|
||||
memory.Write_U16(0x300 | GAIN_00dB, arg1);
|
||||
break;
|
||||
case 15:
|
||||
memory.Write_U16(0x300 | GAIN_15dB, arg1);
|
||||
break;
|
||||
case 30:
|
||||
memory.Write_U16(0x300 | GAIN_30dB, arg1);
|
||||
break;
|
||||
case 36:
|
||||
default:
|
||||
memory.Write_U16(0x300 | GAIN_36dB, arg1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EC_STATE:
|
||||
memory.Write_U16(sampler.ec_reset ? 1 : 0, arg1);
|
||||
break;
|
||||
case SP_STATE:
|
||||
switch (memory.Read_U16(arg1))
|
||||
{
|
||||
case SP_ENABLE:
|
||||
memory.Write_U16(1, arg2);
|
||||
break;
|
||||
case SP_SIN:
|
||||
break;
|
||||
case SP_SOUT:
|
||||
memory.Write_U16(0x39B0, arg2); // 6dB
|
||||
break;
|
||||
case SP_RIN:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SAMPLER_MUTE:
|
||||
memory.Write_U16(sampler.mute ? 1 : 0, arg1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace IOS::HLE::USB
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
#include "Core/IOS/USB/Emulated/Microphone.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class WiiSpeak final : public Device
|
||||
{
|
||||
public:
|
||||
WiiSpeak(EmulationKernel& ios, const std::string& device_name);
|
||||
DeviceDescriptor GetDeviceDescriptor() const override;
|
||||
std::vector<ConfigDescriptor> GetConfigurations() const override;
|
||||
std::vector<InterfaceDescriptor> GetInterfaces(u8 config) const override;
|
||||
std::vector<EndpointDescriptor> GetEndpoints(u8 config, u8 interface, u8 alt) const override;
|
||||
bool Attach() override;
|
||||
bool AttachAndChangeInterface(u8 interface) override;
|
||||
int CancelTransfer(u8 endpoint) override;
|
||||
int ChangeInterface(u8 interface) override;
|
||||
int GetNumberOfAltSettings(u8 interface) override;
|
||||
int SetAltSetting(u8 alt_setting) override;
|
||||
int SubmitTransfer(std::unique_ptr<CtrlMessage> message) override;
|
||||
int SubmitTransfer(std::unique_ptr<BulkMessage> message) override;
|
||||
int SubmitTransfer(std::unique_ptr<IntrMessage> message) override;
|
||||
int SubmitTransfer(std::unique_ptr<IsoMessage> message) override;
|
||||
|
||||
private:
|
||||
struct WSState
|
||||
{
|
||||
bool sample_on;
|
||||
bool mute;
|
||||
int freq;
|
||||
int gain;
|
||||
bool ec_reset;
|
||||
bool sp_on;
|
||||
};
|
||||
|
||||
WSState sampler{};
|
||||
|
||||
enum Registers
|
||||
{
|
||||
SAMPLER_STATE = 0,
|
||||
SAMPLER_MUTE = 0xc0,
|
||||
|
||||
SAMPLER_FREQ = 2,
|
||||
FREQ_8KHZ = 0,
|
||||
FREQ_11KHZ = 1,
|
||||
FREQ_RESERVED = 2,
|
||||
FREQ_16KHZ = 3,
|
||||
|
||||
SAMPLER_GAIN = 4,
|
||||
GAIN_00dB = 0,
|
||||
GAIN_15dB = 1,
|
||||
GAIN_30dB = 2,
|
||||
GAIN_36dB = 3,
|
||||
|
||||
EC_STATE = 0x14,
|
||||
|
||||
SP_STATE = 0x38,
|
||||
SP_ENABLE = 0x1010,
|
||||
SP_SIN = 0x2001,
|
||||
SP_SOUT = 0x2004,
|
||||
SP_RIN = 0x200d
|
||||
};
|
||||
|
||||
void GetRegister(std::unique_ptr<CtrlMessage>& cmd);
|
||||
void SetRegister(std::unique_ptr<CtrlMessage>& cmd);
|
||||
|
||||
EmulationKernel& m_ios;
|
||||
u16 m_vid = 0;
|
||||
u16 m_pid = 0;
|
||||
u8 m_active_interface = 0;
|
||||
bool m_device_attached = false;
|
||||
bool init = false;
|
||||
std::unique_ptr<Microphone> m_microphone;
|
||||
DeviceDescriptor m_device_descriptor{};
|
||||
std::vector<ConfigDescriptor> m_config_descriptor;
|
||||
std::vector<InterfaceDescriptor> m_interface_descriptor;
|
||||
std::vector<EndpointDescriptor> m_endpoint_descriptor;
|
||||
std::mutex m_mutex;
|
||||
Common::Event m_shutdown_event;
|
||||
};
|
||||
} // namespace IOS::HLE::USB
|
|
@ -24,6 +24,7 @@
|
|||
#include "Core/IOS/USB/Common.h"
|
||||
#include "Core/IOS/USB/Emulated/Infinity.h"
|
||||
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
|
||||
#include "Core/IOS/USB/Emulated/WiiSpeak.h"
|
||||
#include "Core/IOS/USB/LibusbDevice.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/System.h"
|
||||
|
@ -195,6 +196,9 @@ void USBHost::AddEmulatedDevices(std::set<u64>& new_devices, DeviceChangeHooks&
|
|||
auto infinity_base = std::make_unique<USB::InfinityUSB>(GetEmulationKernel());
|
||||
CheckAndAddDevice(std::move(infinity_base), new_devices, hooks, always_add_hooks);
|
||||
}
|
||||
|
||||
auto wii_speak = std::make_unique<USB::WiiSpeak>(GetEmulationKernel(), "Wii Speak");
|
||||
CheckAndAddDevice(std::move(wii_speak), new_devices, hooks, always_add_hooks);
|
||||
}
|
||||
|
||||
void USBHost::CheckAndAddDevice(std::unique_ptr<USB::Device> device, std::set<u64>& new_devices,
|
||||
|
|
|
@ -401,9 +401,11 @@
|
|||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Common.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Infinity.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Microphone.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Skylanders\Skylander.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Skylanders\SkylanderCrypto.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\Skylanders\SkylanderFigure.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Emulated\WiiSpeak.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Host.h" />
|
||||
<ClInclude Include="Core\IOS\USB\LibusbDevice.h" />
|
||||
<ClInclude Include="Core\IOS\USB\OH0\OH0.h" />
|
||||
|
@ -1064,9 +1066,11 @@
|
|||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Common.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Infinity.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Microphone.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Skylanders\Skylander.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Skylanders\SkylanderCrypto.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\Skylanders\SkylanderFigure.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Emulated\WiiSpeak.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Host.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\LibusbDevice.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\OH0\OH0.cpp" />
|
||||
|
|
Loading…
Reference in New Issue