This commit is contained in:
Jordan Woyak 2025-12-15 17:02:28 -05:00 committed by GitHub
commit 7dee9b40b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 278 additions and 124 deletions

View File

@ -264,6 +264,9 @@ void Mixer::PushWiimoteSpeakerSamples(const s16* samples, std::size_t num_sample
if (!IsOutputSampleRateValid())
return;
if (!m_config_wiimote_enable_speaker)
return;
// Max 20 bytes/speaker report, may be 4-bit ADPCM so multiply by 2
static constexpr std::size_t MAX_SPEAKER_SAMPLES = 20 * 2;
std::array<s16, MAX_SPEAKER_SAMPLES * 2> samples_stereo;
@ -432,6 +435,7 @@ void Mixer::RefreshConfig()
m_config_emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED);
m_config_fill_audio_gaps = Config::Get(Config::MAIN_AUDIO_FILL_GAPS);
m_config_audio_buffer_ms = Config::Get(Config::MAIN_AUDIO_BUFFER_SIZE);
m_config_wiimote_enable_speaker = Config::Get(Config::MAIN_WIIMOTE_ENABLE_SPEAKER);
}
void Mixer::MixerFifo::DoState(PointerWrap& p)

View File

@ -165,6 +165,7 @@ private:
float m_config_emulation_speed;
bool m_config_fill_audio_gaps;
int m_config_audio_buffer_ms;
bool m_config_wiimote_enable_speaker;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
};

View File

@ -363,6 +363,8 @@ void Wiimote::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&
{
m_speaker_mute = rpt.enable;
m_speaker_logic.SetMuted(m_speaker_mute);
if (rpt.ack)
SendAck(OutputReportID::SpeakerMute, ErrorCode::Success);
}
@ -371,27 +373,23 @@ void Wiimote::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature
{
m_status.speaker = rpt.enable;
m_speaker_logic.SetEnabled(m_status.speaker);
if (rpt.ack)
SendAck(OutputReportID::SpeakerEnable, ErrorCode::Success);
}
void Wiimote::HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData& rpt)
{
// TODO: Does speaker_mute stop speaker data processing?
// and what about speaker_enable?
// (important to keep decoder in proper state)
if (!m_speaker_mute)
if (rpt.length > std::size(rpt.data))
{
if (rpt.length > std::size(rpt.data))
{
ERROR_LOG_FMT(WIIMOTE, "Bad speaker data length: {}", rpt.length);
}
else
{
// Speaker data reports result in a write to the speaker hardware at offset 0x00.
m_i2c_bus.BusWrite(SpeakerLogic::I2C_ADDR, SpeakerLogic::SPEAKER_DATA_OFFSET, rpt.length,
std::data(rpt.data));
}
ERROR_LOG_FMT(WIIMOTE, "Bad speaker data length: {}", rpt.length);
}
else
{
// Speaker data reports result in a write to the speaker hardware at offset 0x00.
m_i2c_bus.BusWrite(SpeakerLogic::I2C_ADDR, SpeakerLogic::SPEAKER_DATA_OFFSET, rpt.length,
std::data(rpt.data));
}
// FYI: Speaker data reports normally do not ACK but I have seen them ACK with error codes
@ -553,7 +551,12 @@ void Wiimote::DoState(PointerWrap& p)
m_camera_logic.DoState(p);
if (p.IsReadMode())
{
m_speaker_logic.SetEnabled(m_status.speaker);
m_speaker_logic.SetMuted(m_speaker_mute);
m_camera_logic.SetEnabled(m_status.ir);
}
p.Do(m_is_motion_plus_attached);
p.Do(m_active_extension);

View File

@ -54,66 +54,95 @@ static s16 adpcm_yamaha_expand_nibble(ADPCMState& s, u8 nibble)
return s.predictor;
}
void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
static constexpr auto ExpandS8ToS16(s8 sample)
{
// TODO: should we still process samples for the decoder state?
if (!m_speaker_enabled)
return;
return s16(sample * 0x100);
}
if (reg_data.sample_rate == 0 || length == 0)
void SpeakerLogic::ProcessSpeakerData(std::span<const u8> data)
{
// There seem to be multiple flags that can skip input entirely.
if ((m_register_data.speaker_flags & SPEAKER_STOP_BIT) != 0x00)
return;
if ((m_register_data.audio_input_enable & 0x01) == 0x00)
return;
// Even if volume is zero we process samples to maintain proper decoder state.
// Potentially 40 resulting samples.
std::array<s16, WiimoteCommon::OutputReportSpeakerData::DATA_SIZE * 2> samples;
assert(length * 2 <= static_cast<int>(samples.size()));
assert(data.size() * 2 <= samples.size());
unsigned int sample_rate_dividend, sample_length;
u8 volume_divisor;
std::size_t sample_count = 0;
if (reg_data.format == SpeakerLogic::DATA_FORMAT_PCM)
const u8 effective_audio_format = m_register_data.audio_format & 0xe0;
switch (effective_audio_format)
{
// 8 bit PCM
for (int i = 0; i < length; ++i)
// 4bit Yamaha ADPCM (same as dreamcast)
// Games only use this.
case 0x00:
for (u8 value : data)
{
samples[i] = ((s16)(s8)data[i]) * 0x100;
samples[sample_count++] = adpcm_yamaha_expand_nibble(m_adpcm_state, value >> 4);
samples[sample_count++] = adpcm_yamaha_expand_nibble(m_adpcm_state, value & 0xf);
}
break;
// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
sample_rate_dividend = 12000000;
volume_divisor = 0xff;
sample_length = (unsigned int)length;
}
else if (reg_data.format == SpeakerLogic::DATA_FORMAT_ADPCM)
{
// 4 bit Yamaha ADPCM (same as dreamcast)
for (int i = 0; i < length; ++i)
// s8 PCM
case 0x40:
for (u8 value : data)
{
samples[i * 2] = adpcm_yamaha_expand_nibble(adpcm_state, (data[i] >> 4) & 0xf);
samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(adpcm_state, data[i] & 0xf);
samples[sample_count++] = ExpandS8ToS16(s8(value));
}
break;
// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
sample_rate_dividend = 6000000;
volume_divisor = 0x7F;
sample_length = (unsigned int)length * 2;
// s16le PCM (both of these?)
case 0x60:
case 0xe0:
if ((data.size() % sizeof(s16)) != 0)
{
// I assume the real hardware properly buffers odd-sized writes ?
ERROR_LOG_FMT(IOS_WIIMOTE, "Unhandled odd audio data size");
}
sample_count = data.size() / sizeof(s16);
std::ranges::copy(data, Common::AsWritableU8Span(samples).data());
break;
default:
ERROR_LOG_FMT(IOS_WIIMOTE, "Unknown audio format {:x}", m_register_data.audio_format);
break;
}
else
{
ERROR_LOG_FMT(IOS_WIIMOTE, "Unknown speaker format {:x}", reg_data.format);
if (sample_count == 0)
return;
}
if (reg_data.volume > volume_divisor)
m_register_data.decoder_flags |= DECODER_PROCESSED_DATA_BIT;
// When output isn't enabled, we drop the samples after running data through the decoder.
// I think the real hardware buffers the pre-decoded data and decodes/plays only when this is set.
// And I think the above bit is not actually set until that happens.
// We aren't emulating any of that buffering, though.
if ((m_register_data.audio_playback_enable & 0x01) == 0x00)
return;
if (m_is_muted)
return;
// If the 0x01 bit is set here then no audio is produced.
if ((m_register_data.audio_format & 0x01) != 0x00)
return;
// Despite wiibrew claims, the hardware volume can go all the way to 0xff.
// But since games never set the volume beyond 0x7f we'll use that as the maximum.
constexpr auto volume_divisor = 0x7f;
if (m_register_data.volume > volume_divisor)
{
DEBUG_LOG_FMT(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum!");
volume_divisor = reg_data.volume;
// We could potentially amplify the samples in this situation?
DEBUG_LOG_FMT(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum.");
}
// SetWiimoteSpeakerVolume expects values from 0 to 255.
// Multiply by 256, floor to int, and clamp to 255 for a uniformly mapped conversion.
const double volume = float(reg_data.volume) * 256.f / volume_divisor;
const double volume = float(m_register_data.volume) * 256.f / volume_divisor;
// This used the "Constant Power Pan Law", but it is undesirable
// if the pan is 0, and it implied that the loudness of a wiimote speaker
@ -122,7 +151,7 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
// We should play the samples from the wiimote at the native volume they came with,
// because you can lower their volume from the Wii settings and because they are
// already extremely low quality, so any additional quality loss isn't welcome.
speaker_pan = std::clamp(speaker_pan, -1.f, 1.f);
const auto speaker_pan = std::clamp(float(m_speaker_pan_setting.GetValue()) / 100, -1.f, 1.f);
const u32 l_volume = std::min(u32(std::min(1.f - speaker_pan, 1.f) * volume), 255u);
const u32 r_volume = std::min(u32(std::min(1.f + speaker_pan, 1.f) * volume), 255u);
@ -130,31 +159,51 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
SoundStream* sound_stream = system.GetSoundStream();
sound_stream->GetMixer()->SetWiimoteSpeakerVolume(l_volume, r_volume);
// ADPCM sample rate is thought to be x2.(3000 x2 = 6000).
const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
sound_stream->GetMixer()->PushWiimoteSpeakerSamples(
samples.data(), sample_length, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / (sample_rate * 2));
samples.data(), sample_count, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / GetCurrentSampleRate());
}
u32 SpeakerLogic::GetCurrentSampleRate() const
{
auto sr_divisor = m_register_data.sample_rate_divisor;
if (sr_divisor == 0)
{
// Real hardware seems to interpret 0x000 as 0xfff.
// i.e. ~183 samples per second, based buffer "readiness" timing.
sr_divisor = std::numeric_limits<u16>::max();
}
return SAMPLE_RATE_DIVIDEND / sr_divisor;
}
void SpeakerLogic::Reset()
{
reg_data = {};
m_register_data = {};
// Yamaha ADPCM state initialize
adpcm_state.predictor = 0;
adpcm_state.step = 127;
m_register_data.decoder_flags |= DECODER_READY_FOR_DATA_BIT;
m_adpcm_state = {};
m_is_enabled = false;
m_is_muted = false;
}
void SpeakerLogic::DoState(PointerWrap& p)
{
p.Do(adpcm_state);
p.Do(reg_data);
p.Do(m_adpcm_state);
p.Do(m_register_data);
// FYI: `m_is_enabled` and `m_is_muted` are handled by the Wiimote class.
}
void SpeakerLogic::SetSpeakerEnabled(bool enabled)
void SpeakerLogic::SetEnabled(bool enabled)
{
m_speaker_enabled = enabled;
m_is_enabled = enabled;
}
void SpeakerLogic::SetMuted(bool muted)
{
m_is_muted = muted;
}
int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
@ -162,7 +211,13 @@ int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
if (I2C_ADDR != slave_addr)
return 0;
return RawRead(&reg_data, addr, count, data_out);
// FYI: Real hardware returns a stream of 0xff when reading from addr:0x00.
// Reads at other addresses also return mostly 0xff except:
// addr:0x07 returns some decoder state flags.
// addr:0xff returns 0x00.
// Games never read from this device so this isn't implemented.
return RawRead(&m_register_data, addr, count, data_out);
}
int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
@ -170,17 +225,37 @@ int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
if (I2C_ADDR != slave_addr)
return 0;
// It seems writes while not "enabled" succeed but have no effect.
if (!m_is_enabled)
return count;
if (addr == SPEAKER_DATA_OFFSET)
{
SpeakerData(data_in, count, m_speaker_pan_setting.GetValue() / 100);
ProcessSpeakerData(std::span(data_in, count));
return count;
}
else
// Allow writes to clear some bits.
const auto prev_flags = std::exchange(m_register_data.decoder_flags, 0x00);
RawWrite(&m_register_data, addr, count, data_in);
const auto written_bits = std::exchange(m_register_data.decoder_flags, prev_flags);
constexpr u8 CLEARABLE_BITS = DECODER_DROPPED_DATA_BIT | DECODER_PROCESSED_DATA_BIT;
m_register_data.decoder_flags &= ~(written_bits & CLEARABLE_BITS);
if ((written_bits & DECODER_RESET_BIT) != 0x00)
{
// TODO: Does writing immediately change the decoder config even when active
// or does a write to 0x08 activate the new configuration or something?
return RawWrite(&reg_data, addr, count, data_in);
// The real hardware also sets bits 0x04 and 0x08 here if data was buffered.
m_adpcm_state = {};
DEBUG_LOG_FMT(IOS_WIIMOTE, "ADPCM decoder reset via i2c write.");
}
if ((m_register_data.speaker_flags & SPEAKER_DISABLE_PLAYBACK_BIT) != 0x00)
Common::SetBit<0>(m_register_data.audio_playback_enable, false);
return count;
}
} // namespace WiimoteEmu

View File

@ -3,6 +3,8 @@
#pragma once
#include <span>
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteEmu/I2CBus.h"
@ -12,7 +14,8 @@ namespace WiimoteEmu
{
struct ADPCMState
{
s32 predictor, step;
s32 predictor = 0;
s32 step = 127;
};
class Wiimote;
@ -29,34 +32,130 @@ public:
void Reset();
void DoState(PointerWrap& p);
void SetSpeakerEnabled(bool enabled);
void SetEnabled(bool enabled);
void SetMuted(bool muted);
private:
// Pan is -1.0 to +1.0
void SpeakerData(const u8* data, int length, float speaker_pan);
void ProcessSpeakerData(std::span<const u8>);
// TODO: enum class
static const u8 DATA_FORMAT_ADPCM = 0x00;
static const u8 DATA_FORMAT_PCM = 0x40;
u32 GetCurrentSampleRate() const;
// Much of the information here comes from reverse engineering of an original (non-TR) Wii remote.
// -TR Wii remotes, for whatever reason, seem to perform better buffering of data,
// resulting in fewer dropped packets and notably better audio playback.
// However, the situation is still not ideal unless "sniff mode" is enabled.
// Despite wiibrew claims, this dividend seems to be constant, regardless of the PCM/ADPCM mode.
static constexpr u32 SAMPLE_RATE_DIVIDEND = 12'000'000;
// There are 2 somewhat understood bits at addr:0x01.
// Setting bit:0x01 seems to stop basically everything.
// Written audio data is dropped (not buffered).
// Already buffered audio data does not play.
// The speaker audibly "clicks" off/on.
static constexpr u8 SPEAKER_STOP_BIT = 0x01;
// Setting bit 0x80 seems to halt playback.
// Written audio data is buffered, but it will not decode/play.
// The effect is perhaps indirect, because this also seems to clear the state at addr:0x08.
// Restoring playback after this bit is written requires *first* clearing it,
// then afterward setting bit:0x01 at addr:0x08.
static constexpr u8 SPEAKER_DISABLE_PLAYBACK_BIT = 0x80;
// FYI: There seems to be a whopping 127 byte buffer for undecoded data regardless of format.
// This buffering isn't at all implemented by us.
// There are 4 somewhat understood bits at addr:0x07.
// Unlike an original remote, reading this byte on a -TR remote seems to always produce 0x00.
// Therefore, much of this is not applicable to -TR (and maybe other versions) of remotes.
// The audio decoder IC has changed at least once from a ROHM BU7849 to a BU8849.
// Seems to set/unset itself when the buffer has room for data.
// Writing exactly 64 bytes of speaker data without output enabled will clear the bit.
// Letting some samples play will set the bit again.
// I think when the bit is observed, 64 more bytes of speaker data may be safely written.
static constexpr u8 DECODER_READY_FOR_DATA_BIT = 0x01;
// Writing this bit resets the ADPCM decoder.
// This, expectedly, does work on a -TR remote.
// It also sets the above "ready" bit so I assume it resets the buffer.
// And, if any data was buffered, it sets bit 0x08 (processed data).
// And, if 64 or more bytes were buffered, it sets bit 0x04 (dropped? data). Odd..
// That happens even when also writting those bits (to clear them),
// so actually clearing all the bits can take two writes.
static constexpr u8 DECODER_RESET_BIT = 0x02;
// Seems to latch on after data was dropped, or something like that?
// Writing 128 bytes or more of speaker data without output enabled will set the bit.
// This also sets the above "ready" bit, allowing overwrite of existing data I suppose?
// Resetting the decoder with 64 bytes or more buffered will set this bit. Odd..
// Enabling output with with 64 bytes or more buffered will set this bit. Odd..
// Those two are unusual and make me think this isn't really a "dropped data" flag.
// This bit can be cleared by writing to it.
static constexpr u8 DECODER_DROPPED_DATA_BIT = 0x04;
// Seems to latch on after any playback.
// This bit can be cleared by writing to it.
static constexpr u8 DECODER_PROCESSED_DATA_BIT = 0x08;
// TODO: It seems reading address 0x00 should always return 0xff.
#pragma pack(push, 1)
struct Register
{
// Speaker reports result in a write of samples to addr 0x00 (which also plays sound)
u8 speaker_data;
u8 unk_1;
u8 format;
// seems to always play at 6khz no matter what this is set to?
// or maybe it only applies to pcm input
// Little-endian:
u16 sample_rate;
// Note: Even though games always write the entire configuration as 7-bytes,
// it seems all of the parameters can be individually altered on the fly.
// Games write 0x80 here when enabling speaker and 0x01 when disabling.
// They also write 0x00 here in their 7-byte "configuration" of this and the following bytes.
// In testing, it seems 0x80 and 0x01 are the only bits here that stop sound.
u8 speaker_flags;
// While the audio hardware itself appers to support 16bit samples, it's not practical
// because of the Wii remote's pitiful Bluetooth communication throughput.
// One could maybe approach 32Kb/s (20bytes every 5ms) with "sniff mode" properly enabled.
// Wii games only ever use 4bit Yamaha ADPCM at 24Kb/s (20bytes every ~6.66ms).
//
// If the 0x01 bit is set then no audio is produced,
// but it seems to be otherwise processed normally based on the decoder flags.
//
// The unknown format bit sizes are calculated from buffer "readiness" timing.
// All these guesses come from testing on an original (non-TR) Wii remote.
//
// 0x00-0x1e = 4bit Yamaha ADPCM
// 0x20-0x3e = 8bit? Plays static atop signal when given PCM ?
// 0x40-0x5e = 8bit signed PCM
// 0x60-0x7e = Seems to be 16bit signed little-endian PCM.
// 0x80-0x9e = 8bit? PCM-like but quiet and different ?
// 0xa0-0xbe = 8bit? Sounds overdriven when given PCM ?
// 0xc0-0xde = 8bit?
// 0xe0-0xfe = Seems to also be s16le PCM ?
u8 audio_format;
// Little-endian. 12,000,000 dividend.
u16 sample_rate_divisor;
// Games never set a value higher than 0x7f.
u8 volume;
u8 unk_5;
u8 unk_6;
// Reading this byte on real hardware seems to return 0x09:
u8 unk_7;
u8 unk_8;
// Games write 0x0c here when enabling the speaker.
// Purpose is entirely unknown. Real hardware seems unaffected by this.
u8 unknown_flags;
// Games write 0x0e here when enabling the speaker to clear the decoder state.
u8 decoder_flags;
// Games write 0x01 here to enable playback.
// When this bit is cleared, data can be buffered, but decoding/playback is paused.
u8 audio_playback_enable;
// Games write 0x01 to enable speaker and 0x00 to disable.
// When this bit is cleared, written audio data is ignored.
u8 audio_input_enable;
u8 unknown[0xf6];
};
#pragma pack(pop)
@ -66,15 +165,15 @@ private:
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
Register reg_data{};
Register m_register_data{};
// TODO: What actions reset this state?
// Is this actually in the register somewhere?
ADPCMState adpcm_state{};
ADPCMState m_adpcm_state{};
ControllerEmu::SettingValue<double> m_speaker_pan_setting;
bool m_speaker_enabled = false;
// FYI: Real hardware seems to be not-enabled and not-muted on power up.
bool m_is_enabled = false;
bool m_is_muted = false;
};
} // namespace WiimoteEmu

View File

@ -306,15 +306,9 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i
{SIDEWAYS_OPTION, nullptr, nullptr, _trans("Sideways Wii Remote")}, false);
Reset();
m_config_changed_callback_id = Config::AddConfigChangedCallback([this] { RefreshConfig(); });
RefreshConfig();
}
Wiimote::~Wiimote()
{
Config::RemoveConfigChangedCallback(m_config_changed_callback_id);
}
Wiimote::~Wiimote() = default;
std::string Wiimote::GetName() const
{
@ -823,11 +817,6 @@ void Wiimote::SetRumble(bool on)
m_rumble->controls.front()->control_ref->State(on);
}
void Wiimote::RefreshConfig()
{
m_speaker_logic.SetSpeakerEnabled(Config::Get(Config::MAIN_WIIMOTE_ENABLE_SPEAKER));
}
void Wiimote::StepDynamics()
{
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);

View File

@ -187,8 +187,6 @@ private:
// This is the region exposed over bluetooth:
static constexpr int EEPROM_FREE_SIZE = 0x1700;
void RefreshConfig();
void StepDynamics();
void UpdateButtonsStatus(const DesiredWiimoteState& target_state);
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state, SensorBarState sensor_bar_state);
@ -348,7 +346,5 @@ private:
PositionalState m_shake_state;
IMUCursorState m_imu_cursor_state;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
};
} // namespace WiimoteEmu

View File

@ -185,14 +185,6 @@ void Wiimote::WriteReport(Report rpt)
return;
break;
case OutputReportID::SpeakerEnable:
m_speaker_enable = (rpt[2] & 0x4) != 0;
break;
case OutputReportID::SpeakerMute:
m_speaker_mute = (rpt[2] & 0x4) != 0;
break;
default:
break;
}
@ -283,8 +275,7 @@ void Wiimote::InterruptDataOutput(const u8* data, const u32 size)
leds_rpt.leds = 0xf;
}
}
else if (rpt[1] == u8(OutputReportID::SpeakerData) &&
(!m_speaker_enabled_in_dolphin_config || !m_speaker_enable || m_speaker_mute))
else if (rpt[1] == u8(OutputReportID::SpeakerData) && !m_speaker_enabled_in_dolphin_config)
{
rpt.resize(3);
// Translate undesired speaker data reports into rumble reports.

View File

@ -152,10 +152,6 @@ private:
std::atomic<bool> m_is_linked = false;
// We track the speaker state to convert unnecessary speaker data into rumble reports.
bool m_speaker_enable = false;
bool m_speaker_mute = false;
// And we track the rumble state to drop unnecessary rumble reports.
bool m_rumble_state = false;