WiimoteEmu: Add wiimote speaker logic to i2c bus. Temporarily break the "pan" setting.
This commit is contained in:
parent
62b66580c3
commit
ec460da36d
|
@ -405,7 +405,10 @@ struct wm_write_data
|
|||
u8 rumble : 1;
|
||||
u8 space : 2; // see WM_SPACE_*
|
||||
u8 : 5;
|
||||
u8 address[3];
|
||||
// used only for register space (i2c bus)
|
||||
u8 slave_address;
|
||||
// big endian:
|
||||
u8 address[2];
|
||||
u8 size;
|
||||
u8 data[16];
|
||||
};
|
||||
|
@ -424,8 +427,11 @@ struct wm_read_data
|
|||
u8 rumble : 1;
|
||||
u8 space : 2; // see WM_SPACE_*
|
||||
u8 : 5;
|
||||
u8 address[3];
|
||||
u16 size;
|
||||
// used only for register space (i2c bus)
|
||||
u8 slave_address;
|
||||
// big endian:
|
||||
u8 address[2];
|
||||
u8 size[2];
|
||||
};
|
||||
static_assert(sizeof(wm_read_data) == 6, "Wrong size");
|
||||
|
||||
|
|
|
@ -44,8 +44,9 @@ void Wiimote::ReportMode(const wm_report_mode* const dr)
|
|||
// DEBUG_LOG(WIIMOTE, " All The Time: %x", dr->all_the_time);
|
||||
// DEBUG_LOG(WIIMOTE, " Mode: 0x%02x", dr->mode);
|
||||
|
||||
// TODO: what does the 'all_the_time' bit really do?
|
||||
// m_reporting_auto = dr->all_the_time;
|
||||
m_reporting_auto = dr->continuous; // this right?
|
||||
m_reporting_auto = dr->continuous;
|
||||
m_reporting_mode = dr->mode;
|
||||
// m_reporting_channel = _channelID; // this is set in every Interrupt/Control Channel now
|
||||
|
||||
|
@ -126,8 +127,14 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack)
|
|||
break;
|
||||
|
||||
case RT_WRITE_SPEAKER_DATA: // 0x18
|
||||
// Not sure if speaker mute stops the bus write on real hardware, but it's not that important
|
||||
if (!m_speaker_mute)
|
||||
Wiimote::SpeakerData(reinterpret_cast<const wm_speaker_data*>(sr->data));
|
||||
{
|
||||
auto sd = reinterpret_cast<const wm_speaker_data*>(sr->data);
|
||||
if (sd->length > 20)
|
||||
PanicAlert("EmuWiimote: bad speaker data length!");
|
||||
m_i2c_bus.BusWrite(0x51, 0x00, sd->length, sd->data);
|
||||
}
|
||||
return; // no ack
|
||||
break;
|
||||
|
||||
|
@ -218,10 +225,9 @@ void Wiimote::RequestStatus(const wm_request_status* const rs)
|
|||
/* Write data to Wiimote and Extensions registers. */
|
||||
void Wiimote::WriteData(const wm_write_data* const wd)
|
||||
{
|
||||
u32 address = Common::swap24(wd->address);
|
||||
u16 address = Common::swap16(wd->address);
|
||||
|
||||
// ignore the 0x010000 bit
|
||||
address &= ~0x010000;
|
||||
INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x (%d)", wd->space, address, wd->size);
|
||||
|
||||
if (wd->size > 16)
|
||||
{
|
||||
|
@ -261,8 +267,8 @@ void Wiimote::WriteData(const wm_write_data* const wd)
|
|||
{
|
||||
// Write to Control Register
|
||||
|
||||
// TODO: generate a writedata error reply for wrong number of bytes written..
|
||||
m_i2c_bus.BusWrite(address >> 17, address & 0xff, wd->size, wd->data);
|
||||
// TODO: generate a writedata error reply, 7 == no such slave (no ack)
|
||||
m_i2c_bus.BusWrite(wd->slave_address >> 1, address & 0xff, wd->size, wd->data);
|
||||
|
||||
return;
|
||||
|
||||
|
@ -290,11 +296,10 @@ void Wiimote::WriteData(const wm_write_data* const wd)
|
|||
/* Read data from Wiimote and Extensions registers. */
|
||||
void Wiimote::ReadData(const wm_read_data* const rd)
|
||||
{
|
||||
u32 address = Common::swap24(rd->address);
|
||||
u16 address = Common::swap16(rd->address);
|
||||
u16 size = Common::swap16(rd->size);
|
||||
|
||||
// ignore the 0x010000 bit
|
||||
address &= 0xFEFFFF;
|
||||
//INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", rd->space, rd->slave_address, address, size);
|
||||
|
||||
ReadRequest rr;
|
||||
u8* const block = new u8[size];
|
||||
|
@ -312,7 +317,8 @@ void Wiimote::ReadData(const wm_read_data* const rd)
|
|||
delete[] block;
|
||||
return;
|
||||
}
|
||||
// generate a read error
|
||||
|
||||
// generate a read error, even if the start of the block is readable a real wiimote just sends error code 8
|
||||
size = 0;
|
||||
}
|
||||
|
||||
|
@ -337,23 +343,24 @@ void Wiimote::ReadData(const wm_read_data* const rd)
|
|||
{
|
||||
// Read from Control Register
|
||||
|
||||
m_i2c_bus.BusRead(address >> 17, address & 0xff, rd->size, block);
|
||||
// TODO: generate read errors
|
||||
m_i2c_bus.BusRead(rd->slave_address >> 1, address & 0xff, size, block);
|
||||
// TODO: generate read errors, 7 == no such slave (no ack)
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("WmReadData: unimplemented parameters (size: %i, address: 0x%x)!", size, rd->space);
|
||||
PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", rd->space);
|
||||
break;
|
||||
}
|
||||
|
||||
// want the requested address, not the above modified one
|
||||
rr.address = Common::swap24(rd->address);
|
||||
rr.address = address;
|
||||
rr.size = size;
|
||||
// rr.channel = _channelID;
|
||||
rr.position = 0;
|
||||
rr.data = block;
|
||||
|
||||
// TODO: read requests suppress normal input reports
|
||||
// TODO: if there is currently an active read request ignore new ones
|
||||
|
||||
// send up to 16 bytes
|
||||
SendReadDataReply(rr);
|
||||
|
||||
|
@ -391,7 +398,7 @@ void Wiimote::SendReadDataReply(ReadRequest& request)
|
|||
{
|
||||
// Limit the amt to 16 bytes
|
||||
// AyuanX: the MTU is 640B though... what a waste!
|
||||
const int amt = std::min((unsigned int)16, request.size);
|
||||
const int amt = std::min((u16)16, request.size);
|
||||
|
||||
// no error
|
||||
reply->error = 0;
|
||||
|
@ -432,13 +439,13 @@ void Wiimote::DoState(PointerWrap& p)
|
|||
p.Do(m_shake_step);
|
||||
p.Do(m_sensor_bar_on_top);
|
||||
p.Do(m_status);
|
||||
p.Do(m_adpcm_state);
|
||||
p.Do(m_speaker_logic.adpcm_state);
|
||||
p.Do(m_ext_logic.ext_key);
|
||||
p.DoArray(m_eeprom);
|
||||
p.Do(m_reg_motion_plus);
|
||||
p.Do(m_camera_logic.reg_data);
|
||||
p.Do(m_ext_logic.reg_data);
|
||||
p.Do(m_reg_speaker);
|
||||
p.Do(m_speaker_logic.reg_data);
|
||||
|
||||
// Do 'm_read_requests' queue
|
||||
{
|
||||
|
|
|
@ -68,39 +68,40 @@ void stopdamnwav()
|
|||
}
|
||||
#endif
|
||||
|
||||
void Wiimote::SpeakerData(const wm_speaker_data* sd)
|
||||
void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length)
|
||||
{
|
||||
if (!SConfig::GetInstance().m_WiimoteEnableSpeaker)
|
||||
return;
|
||||
if (m_reg_speaker.volume == 0 || m_reg_speaker.sample_rate == 0 || sd->length == 0)
|
||||
if (reg_data.volume == 0 || reg_data.sample_rate == 0 ||
|
||||
length == 0)
|
||||
return;
|
||||
|
||||
// TODO consider using static max size instead of new
|
||||
std::unique_ptr<s16[]> samples(new s16[sd->length * 2]);
|
||||
std::unique_ptr<s16[]> samples(new s16[length * 2]);
|
||||
|
||||
unsigned int sample_rate_dividend, sample_length;
|
||||
u8 volume_divisor;
|
||||
|
||||
if (m_reg_speaker.format == 0x40)
|
||||
if (reg_data.format == 0x40)
|
||||
{
|
||||
// 8 bit PCM
|
||||
for (int i = 0; i < sd->length; ++i)
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
samples[i] = ((s16)(s8)sd->data[i]) << 8;
|
||||
samples[i] = ((s16)(s8)data[i]) << 8;
|
||||
}
|
||||
|
||||
// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
|
||||
sample_rate_dividend = 12000000;
|
||||
volume_divisor = 0xff;
|
||||
sample_length = (unsigned int)sd->length;
|
||||
sample_length = (unsigned int)length;
|
||||
}
|
||||
else if (m_reg_speaker.format == 0x00)
|
||||
else if (reg_data.format == 0x00)
|
||||
{
|
||||
// 4 bit Yamaha ADPCM (same as dreamcast)
|
||||
for (int i = 0; i < sd->length; ++i)
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
samples[i * 2] = adpcm_yamaha_expand_nibble(m_adpcm_state, (sd->data[i] >> 4) & 0xf);
|
||||
samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(m_adpcm_state, sd->data[i] & 0xf);
|
||||
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);
|
||||
}
|
||||
|
||||
// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
|
||||
|
@ -109,19 +110,20 @@ void Wiimote::SpeakerData(const wm_speaker_data* sd)
|
|||
// 0 - 127
|
||||
// TODO: does it go beyond 127 for format == 0x40?
|
||||
volume_divisor = 0x7F;
|
||||
sample_length = (unsigned int)sd->length * 2;
|
||||
sample_length = (unsigned int)length * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", m_reg_speaker.format);
|
||||
ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", reg_data.format);
|
||||
return;
|
||||
}
|
||||
|
||||
// Speaker Pan
|
||||
unsigned int vol = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100);
|
||||
// TODO: fix
|
||||
unsigned int vol = 0;//(unsigned int)(m_options->numeric_settings[0]->GetValue() * 100);
|
||||
|
||||
unsigned int sample_rate = sample_rate_dividend / m_reg_speaker.sample_rate;
|
||||
float speaker_volume_ratio = (float)m_reg_speaker.volume / volume_divisor;
|
||||
unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
|
||||
float speaker_volume_ratio = (float)reg_data.volume / volume_divisor;
|
||||
unsigned int left_volume = (unsigned int)((128 + vol) * speaker_volume_ratio);
|
||||
unsigned int right_volume = (unsigned int)((128 - vol) * speaker_volume_ratio);
|
||||
|
||||
|
@ -147,12 +149,12 @@ void Wiimote::SpeakerData(const wm_speaker_data* sd)
|
|||
File::OpenFStream(ofile, "rmtdump.bin", ofile.binary | ofile.out);
|
||||
wav.Start("rmtdump.wav", 6000);
|
||||
}
|
||||
wav.AddMonoSamples(samples.get(), sd->length * 2);
|
||||
wav.AddMonoSamples(samples.get(), length * 2);
|
||||
if (ofile.good())
|
||||
{
|
||||
for (int i = 0; i < sd->length; i++)
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
ofile << sd->data[i];
|
||||
ofile << data[i];
|
||||
}
|
||||
}
|
||||
num++;
|
||||
|
|
|
@ -328,9 +328,9 @@ void Wiimote::Reset()
|
|||
memcpy(m_eeprom + 0x16D0, eeprom_data_16D0, sizeof(eeprom_data_16D0));
|
||||
|
||||
// set up the register
|
||||
memset(&m_reg_speaker, 0, sizeof(m_reg_speaker));
|
||||
|
||||
// TODO: kill/move these
|
||||
memset(&m_speaker_logic.reg_data, 0, sizeof(m_speaker_logic.reg_data));
|
||||
memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data));
|
||||
memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data));
|
||||
|
||||
|
@ -361,14 +361,15 @@ void Wiimote::Reset()
|
|||
}
|
||||
|
||||
// Yamaha ADPCM state initialize
|
||||
m_adpcm_state.predictor = 0;
|
||||
m_adpcm_state.step = 127;
|
||||
m_speaker_logic.adpcm_state.predictor = 0;
|
||||
m_speaker_logic.adpcm_state.step = 127;
|
||||
|
||||
// Initialize i2c bus
|
||||
// TODO: kill magic numbers
|
||||
m_i2c_bus.Reset();
|
||||
m_i2c_bus.AddSlave(0x58, &m_camera_logic);
|
||||
m_i2c_bus.AddSlave(0x52, &m_ext_logic);
|
||||
m_i2c_bus.AddSlave(0x51, &m_speaker_logic);
|
||||
}
|
||||
|
||||
Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1)
|
||||
|
|
|
@ -255,6 +255,8 @@ public:
|
|||
{
|
||||
INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
|
||||
// TODO: reads loop around at end of address space (0xff)
|
||||
|
||||
auto it = m_slaves.find(slave_addr);
|
||||
if (m_slaves.end() != it)
|
||||
return it->second->BusRead(addr, count, data_out);
|
||||
|
@ -266,6 +268,8 @@ public:
|
|||
{
|
||||
INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
|
||||
// TODO: writes loop around at end of address space (0xff)
|
||||
|
||||
auto it = m_slaves.find(slave_addr);
|
||||
if (m_slaves.end() != it)
|
||||
return it->second->BusWrite(addr, count, data_in);
|
||||
|
@ -396,10 +400,49 @@ private:
|
|||
|
||||
} m_ext_logic;
|
||||
|
||||
struct SpeakerLogic : public I2CSlave
|
||||
{
|
||||
struct
|
||||
{
|
||||
// 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
|
||||
u16 sample_rate;
|
||||
u8 volume;
|
||||
u8 unk_6;
|
||||
u8 unk_7;
|
||||
u8 play;
|
||||
u8 unk_9;
|
||||
} reg_data;
|
||||
|
||||
ADPCMState adpcm_state;
|
||||
|
||||
void SpeakerData(const u8* data, int length);
|
||||
|
||||
int BusRead(u8 addr, int count, u8* data_out) override
|
||||
{
|
||||
return raw_read(®_data, addr, count, data_out);
|
||||
}
|
||||
|
||||
int BusWrite(u8 addr, int count, const u8* data_in) override
|
||||
{
|
||||
if (0x00 == addr)
|
||||
{
|
||||
SpeakerData(data_in, count);
|
||||
return count;
|
||||
}
|
||||
else
|
||||
return raw_write(®_data, addr, count, data_in);
|
||||
}
|
||||
|
||||
} m_speaker_logic;
|
||||
|
||||
struct ReadRequest
|
||||
{
|
||||
// u16 channel;
|
||||
u32 address, size, position;
|
||||
u16 address, size, position;
|
||||
u8* data;
|
||||
};
|
||||
|
||||
|
@ -409,7 +452,6 @@ private:
|
|||
void ReadData(const wm_read_data* rd);
|
||||
void WriteData(const wm_write_data* wd);
|
||||
void SendReadDataReply(ReadRequest& request);
|
||||
void SpeakerData(const wm_speaker_data* sd);
|
||||
bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode);
|
||||
|
||||
// control groups
|
||||
|
@ -463,8 +505,6 @@ private:
|
|||
|
||||
wm_status_report m_status;
|
||||
|
||||
ADPCMState m_adpcm_state;
|
||||
|
||||
// read data request queue
|
||||
// maybe it isn't actually a queue
|
||||
// maybe read requests cancel any current requests
|
||||
|
@ -485,21 +525,6 @@ private:
|
|||
u8 ext_identifier[6];
|
||||
} m_reg_motion_plus;
|
||||
|
||||
struct SpeakerReg
|
||||
{
|
||||
u8 unused_0;
|
||||
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
|
||||
u16 sample_rate;
|
||||
u8 volume;
|
||||
u8 unk_6;
|
||||
u8 unk_7;
|
||||
u8 play;
|
||||
u8 unk_9;
|
||||
} m_reg_speaker;
|
||||
|
||||
#pragma pack(pop)
|
||||
};
|
||||
} // namespace WiimoteEmu
|
||||
|
|
Loading…
Reference in New Issue