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.
This commit is contained in:
Sepalani
2024-05-11 02:11:45 +04:00
parent 6a36930c74
commit f5dd80bb5b
4 changed files with 43 additions and 27 deletions

View File

@ -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;
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

View File

@ -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;

View File

@ -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:

View File

@ -49,7 +49,7 @@ private:
enum Registers
{
SAMPLER_STATE = 0,
SAMPLER_MUTE = 0xc0,
SAMPLER_MUTE = 0x0c,
SAMPLER_FREQ = 2,
FREQ_8KHZ = 0,