summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2025-10-11 18:29:51 -0500
committerJordan Woyak <jordan.woyak@gmail.com>2026-10-08 17:41:22 -0500
commitbaa05e32f1db379e98bbd48b35b2aaa2e6fa4e94 (patch)
tree66004b6719fe090e41887fa7f61a0184aca084ce /Source
parente6f3ae17627e4344da95b13424af5baf4c892b08 (diff)
HW/Wiimote: Speaker logic accuracy fixes, comments, and code cleanups.
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/AudioCommon/Mixer.cpp4
-rw-r--r--Source/Core/AudioCommon/Mixer.h1
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp31
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Speaker.cpp189
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Speaker.h151
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp13
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h4
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp11
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.h4
9 files changed, 282 insertions, 126 deletions
diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp
index fe7f0e7349..aac346d31b 100644
--- a/Source/Core/AudioCommon/Mixer.cpp
+++ b/Source/Core/AudioCommon/Mixer.cpp
@@ -268,6 +268,9 @@ void Mixer::PushWiimoteSpeakerSamples(std::size_t wiimote_index, const s16* samp
if (!IsOutputSampleRateValid() || wiimote_index >= m_wiimote_speaker_mixers.size())
return;
+ if (!m_config_wiimote_enable_speaker)
+ return;
+
// WiimoteEmu produces host-endian mono samples.
m_wiimote_speaker_mixers[wiimote_index].SetInputSampleRateDivisor(sample_rate_divisor);
@@ -448,6 +451,7 @@ void Mixer::RefreshConfig()
m_config_gba_routing_enabled = Config::Get(Config::MAIN_GBA_AUDIO_ROUTING_ENABLED);
for (std::size_t i = 0; i < m_config_gba_output_enabled.size(); ++i)
m_config_gba_output_enabled[i] = Config::Get(Config::MAIN_GBA_AUDIO_OUTPUT_ENABLED[i]);
+ m_config_wiimote_enable_speaker = Config::Get(Config::MAIN_WIIMOTE_ENABLE_SPEAKER);
}
void Mixer::MixerFifo::DoState(PointerWrap& p)
diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h
index 26e179f03b..01cbae78a3 100644
--- a/Source/Core/AudioCommon/Mixer.h
+++ b/Source/Core/AudioCommon/Mixer.h
@@ -204,6 +204,7 @@ private:
std::array<bool, 4> m_config_wiimote_output_enabled{};
bool m_config_gba_routing_enabled = false;
std::array<bool, Config::GBA_SPEAKER_COUNT> m_config_gba_output_enabled{};
+ bool m_config_wiimote_enable_speaker;
Config::ConfigChangedCallbackID m_config_changed_callback_id;
};
diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
index 22fbe7d234..28dbdefdb2 100644
--- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
@@ -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);
diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
index 7fec3bd651..b76e69c61d 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
@@ -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,32 +159,52 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
SoundStream* sound_stream = system.GetSoundStream();
sound_stream->GetMixer()->SetWiimoteSpeakerVolume(m_wiimote_index, l_volume, r_volume);
+ sound_stream->GetMixer()->PushWiimoteSpeakerSamples(m_wiimote_index, 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();
+ }
- // 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(
- m_wiimote_index, samples.data(), sample_length,
- Mixer::FIXED_SAMPLE_RATE_DIVIDEND / (sample_rate * 2));
+ return SAMPLE_RATE_DIVIDEND / sr_divisor;
}
void SpeakerLogic::Reset()
{
- reg_data = {};
+ m_register_data = {};
+
+ m_register_data.decoder_flags |= DECODER_READY_FOR_DATA_BIT;
- // Yamaha ADPCM state initialize
- adpcm_state.predictor = 0;
- adpcm_state.step = 127;
+ 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::SetEnabled(bool enabled)
+{
+ m_is_enabled = enabled;
}
-void SpeakerLogic::SetSpeakerEnabled(bool enabled)
+void SpeakerLogic::SetMuted(bool muted)
{
- m_speaker_enabled = enabled;
+ m_is_muted = muted;
}
int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
@@ -163,7 +212,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)
@@ -171,17 +226,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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.h b/Source/Core/Core/HW/WiimoteEmu/Speaker.h
index 445ef23f92..c285283ca6 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Speaker.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.h
@@ -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,35 +32,132 @@ public:
void Reset();
void DoState(PointerWrap& p);
- void SetSpeakerEnabled(bool enabled);
void SetWiimoteIndex(u8 index) { m_wiimote_index = index; }
-private:
- // Pan is -1.0 to +1.0
- void SpeakerData(const u8* data, int length, float speaker_pan);
+ void SetEnabled(bool enabled);
+ void SetMuted(bool muted);
- // TODO: enum class
- static const u8 DATA_FORMAT_ADPCM = 0x00;
- static const u8 DATA_FORMAT_PCM = 0x40;
+private:
+ void ProcessSpeakerData(std::span<const u8>);
+
+ 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)
@@ -67,16 +167,17 @@ 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;
u8 m_wiimote_index = 0;
+
+ // 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
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
index bbbea66e37..e0c4ebb9d2 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
@@ -310,15 +310,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
{
@@ -827,11 +821,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);
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
index 1e07e2abca..905f2ab548 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
@@ -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
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
index 79129b3a92..171a9574cb 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
@@ -180,14 +180,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;
}
@@ -278,8 +270,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.
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
index 1512d134ec..3096fb6921 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
@@ -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;