summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorSepalani <sepalani@hotmail.fr>2024-05-11 02:11:45 +0400
committerSepalani <sepalani@hotmail.fr>2025-05-07 20:33:22 +0400
commitf5dd80bb5ba3a354cdae01715e0af4d61976442e (patch)
treecdbcbe5e784c69eb540183bbfd1bb5886e597a4f /Source
parent6a36930c7449a606aedf111f99af9030c73e1ff3 (diff)
IOS/USB: Try to fix the Wii Speak Channel record/playback feature
1. Fix Wii Speak SAMPLER_MUTE register: The register should be 12 (i.e. 0x0c) instead of 0xc0. 2. Fix Wii Speak buffer memcpy size parameter: It seems to fix random echoes and reduce noises when nobody is speaking. 3. Change the isochronous transfer timing: It is based on empirical testing.
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/IOS/USB/Emulated/Microphone.cpp21
-rw-r--r--Source/Core/Core/IOS/USB/Emulated/Microphone.h4
-rw-r--r--Source/Core/Core/IOS/USB/Emulated/WiiSpeak.cpp43
-rw-r--r--Source/Core/Core/IOS/USB/Emulated/WiiSpeak.h2
4 files changed, 43 insertions, 27 deletions
diff --git a/Source/Core/Core/IOS/USB/Emulated/Microphone.cpp b/Source/Core/Core/IOS/USB/Emulated/Microphone.cpp
index 49bd7cfcaa..e061012669 100644
--- a/Source/Core/Core/IOS/USB/Emulated/Microphone.cpp
+++ b/Source/Core/Core/IOS/USB/Emulated/Microphone.cpp
@@ -193,6 +193,7 @@ long Microphone::DataCallback(const s16* input_buffer, long nframes)
m_samples_avail += nframes;
if (m_samples_avail > STREAM_SIZE)
{
+ WARN_LOG_FMT(IOS_USB, "Wii Speak ring buffer is full, data will be lost!");
m_samples_avail = STREAM_SIZE;
}
@@ -202,18 +203,30 @@ long Microphone::DataCallback(const s16* input_buffer, long nframes)
void Microphone::ReadIntoBuffer(u8* dst, u32 size)
{
+ static constexpr u32 SINGLE_READ_SIZE = BUFF_SIZE_SAMPLES * sizeof(SampleType);
+
+ // Avoid buffer overflow during memcpy
+ static_assert((STREAM_SIZE % BUFF_SIZE_SAMPLES) == 0,
+ "The STREAM_SIZE isn't a multiple of BUFF_SIZE_SAMPLES");
+
std::lock_guard lock(m_ring_lock);
- if (m_samples_avail >= BUFF_SIZE_SAMPLES)
+ for (u8* end = dst + size; dst < end; dst += SINGLE_READ_SIZE, size -= SINGLE_READ_SIZE)
{
- u8* last_buffer = reinterpret_cast<u8*>(&m_stream_buffer[m_stream_rpos]);
- std::memcpy(dst, static_cast<u8*>(last_buffer), size);
+ if (size < SINGLE_READ_SIZE || m_samples_avail < BUFF_SIZE_SAMPLES)
+ break;
- m_samples_avail -= BUFF_SIZE_SAMPLES;
+ SampleType* last_buffer = &m_stream_buffer[m_stream_rpos];
+ std::memcpy(dst, last_buffer, SINGLE_READ_SIZE);
+ m_samples_avail -= BUFF_SIZE_SAMPLES;
m_stream_rpos += BUFF_SIZE_SAMPLES;
m_stream_rpos %= STREAM_SIZE;
}
+ if (size != 0)
+ {
+ std::memset(dst, 0, size);
+ }
}
bool Microphone::HasData() const
diff --git a/Source/Core/Core/IOS/USB/Emulated/Microphone.h b/Source/Core/Core/IOS/USB/Emulated/Microphone.h
index 3d5ae29e1a..58264eafd1 100644
--- a/Source/Core/Core/IOS/USB/Emulated/Microphone.h
+++ b/Source/Core/Core/IOS/USB/Emulated/Microphone.h
@@ -42,11 +42,11 @@ private:
void StreamStop();
static constexpr u32 SAMPLING_RATE = 8000;
- static constexpr u32 BUFFER_SIZE = SAMPLING_RATE / 2;
+ using SampleType = s16;
static constexpr u32 BUFF_SIZE_SAMPLES = 16;
static constexpr u32 STREAM_SIZE = BUFF_SIZE_SAMPLES * 500;
- std::array<s16, STREAM_SIZE> m_stream_buffer{};
+ std::array<SampleType, STREAM_SIZE> m_stream_buffer{};
u32 m_stream_wpos = 0;
u32 m_stream_rpos = 0;
u32 m_samples_avail = 0;
diff --git a/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.cpp b/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.cpp
index 05a574ac36..0e5ce62a2c 100644
--- a/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.cpp
+++ b/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.cpp
@@ -190,16 +190,35 @@ int WiiSpeak::SubmitTransfer(std::unique_ptr<IsoMessage> cmd)
break;
}
- // Anything more causes the visual cue to not appear.
- // Anything less is more choppy audio.
+ // Transferring too slow causes the visual cue to not appear,
+ // while transferring too fast results in more choppy audio.
DEBUG_LOG_FMT(IOS_USB,
"Wii Speak isochronous transfer: length={:04x} endpoint={:02x} num_packets={:02x}",
cmd->length, cmd->endpoint, cmd->num_packets);
// According to the Wii Speak specs on wiibrew, it's "USB 2.0 Full-speed Device Module",
// so the length of a single frame should be 1 ms.
- // TODO: Find a proper way to compute the transfer timing.
- const u32 transfer_timing = 2500; // 2.5 ms
+ //
+ // Monster Hunter 3 and the Wii Speak Channel use cmd->length=0x100, allowing 256/2 samples
+ // (i.e. 128 samples in 16-bit mono) per frame transfer. The Microphone class is using cubeb
+ // configured with a sample rate of 8000.
+ //
+ // Based on these numbers, here are some theoretical speeds:
+ // - fastest transfer speed would be 8000 samples in 63 ms (i.e. 8000 * 1/128 = 62.5)
+ // * using a timing of 1 ms per frame of 128 samples
+ // * here cubeb sample rate is the bottleneck
+ // - slowest transfer speed would be 8000 samples in 1000 ms (i.e. 128 * 1000/8000 = 16)
+ // * using a timing of 16 ms per frame of 128 samples
+ // * matching cubeb sampling rate
+ //
+ // A decent timing would be 16ms. However, it seems that the Wii Speak Channel record feature is
+ // broken if the timing is less than 32ms and that the record playback is broken when around 34ms
+ // and above. Monster Hunter 3 doesn't seem affected by this timing issue.
+ //
+ // TODO: Investigate and ensure that's the proper way to fix it.
+ // Maybe it's related to the Wii DSP and its stereo/mono mode?
+ // If so, what would be the impact of using DSP LLE/HLE in mono/stereo?
+ const u32 transfer_timing = 32000;
cmd->ScheduleTransferCompletion(IPC_SUCCESS, transfer_timing);
return IPC_SUCCESS;
}
@@ -215,22 +234,6 @@ void WiiSpeak::SetRegister(const std::unique_ptr<CtrlMessage>& cmd)
DEBUG_LOG_FMT(IOS_USB, "Wii Speak register set (reg={:02x}, arg1={:04x}, arg2={:04x})", reg, arg1,
arg2);
- // TODO
- //
- // - On Wii Speak Channel start
- // W[IOS_USB]: Wii Speak unsupported register set (reg=0c, arg1=0000, arg2=0000)
- // W[IOS_USB]: Wii Speak unsupported register get (reg=0c, arg1=109091e2, arg2=109091e4)
- //
- // - On Wii Speak Channel close
- // W[IOS_USB]: Wii Speak unsupported register set (reg=0c, arg1=0001, arg2=0000)
- // W[IOS_USB]: Wii Speak unsupported register get (reg=0c, arg1=109091e2, arg2=109091e4)
- //
- // - On Monster Hunter 3 (RMHE08) online start
- // N[OSREPORT_HLE]: 80450a20->80418adc| ok to call PMICStartAsync() -> 0
- // W[IOS_USB]: Wii Speak unsupported register set (reg=0c, arg1=0000, arg2=0000)
- // W[IOS_USB]: Wii Speak unsupported register get (reg=0c, arg1=10037f62, arg2=10037f64)
- // N[OSREPORT_HLE]: 80450a94->80418adc| ok to start P-Mic -> 0.
-
switch (reg)
{
case SAMPLER_STATE:
diff --git a/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.h b/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.h
index bfd0a57563..6dc88e6456 100644
--- a/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.h
+++ b/Source/Core/Core/IOS/USB/Emulated/WiiSpeak.h
@@ -49,7 +49,7 @@ private:
enum Registers
{
SAMPLER_STATE = 0,
- SAMPLER_MUTE = 0xc0,
+ SAMPLER_MUTE = 0x0c,
SAMPLER_FREQ = 2,
FREQ_8KHZ = 0,