mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
Merge dadddf5c4e into ed2fe134aa
This commit is contained in:
commit
7dee9b40b5
@ -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)
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(®_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(®_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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user