Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
dolphin/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
211 lines (175 sloc)
5.54 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Copyright 2010 Dolphin Emulator Project | |
| // Licensed under GPLv2+ | |
| // Refer to the license.txt file included. | |
| #include "Core/HW/WiimoteEmu/Speaker.h" | |
| #include <memory> | |
| #include "AudioCommon/AudioCommon.h" | |
| #include "Common/CommonTypes.h" | |
| #include "Common/Logging/Log.h" | |
| #include "Common/MathUtil.h" | |
| #include "Core/ConfigManager.h" | |
| #include "Core/HW/WiimoteEmu/WiimoteEmu.h" | |
| #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" | |
| #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" | |
| //#define WIIMOTE_SPEAKER_DUMP | |
| #ifdef WIIMOTE_SPEAKER_DUMP | |
| #include <cstdlib> | |
| #include <fstream> | |
| #include "AudioCommon/WaveFile.h" | |
| #include "Common/FileUtil.h" | |
| #endif | |
| namespace WiimoteEmu | |
| { | |
| // Yamaha ADPCM decoder code based on The ffmpeg Project (Copyright (s) 2001-2003) | |
| static const s32 yamaha_difflookup[] = {1, 3, 5, 7, 9, 11, 13, 15, | |
| -1, -3, -5, -7, -9, -11, -13, -15}; | |
| static const s32 yamaha_indexscale[] = {230, 230, 230, 230, 307, 409, 512, 614, | |
| 230, 230, 230, 230, 307, 409, 512, 614}; | |
| static s16 av_clip16(s32 a) | |
| { | |
| if ((a + 32768) & ~65535) | |
| return (a >> 31) ^ 32767; | |
| else | |
| return a; | |
| } | |
| static s32 av_clip(s32 a, s32 amin, s32 amax) | |
| { | |
| if (a < amin) | |
| return amin; | |
| else if (a > amax) | |
| return amax; | |
| else | |
| return a; | |
| } | |
| static s16 adpcm_yamaha_expand_nibble(ADPCMState& s, u8 nibble) | |
| { | |
| s.predictor += (s.step * yamaha_difflookup[nibble]) / 8; | |
| s.predictor = av_clip16(s.predictor); | |
| s.step = (s.step * yamaha_indexscale[nibble]) >> 8; | |
| s.step = av_clip(s.step, 127, 24576); | |
| return s.predictor; | |
| } | |
| #ifdef WIIMOTE_SPEAKER_DUMP | |
| std::ofstream ofile; | |
| WaveFileWriter wav; | |
| void stopdamnwav() | |
| { | |
| wav.Stop(); | |
| ofile.close(); | |
| } | |
| #endif | |
| void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan) | |
| { | |
| // TODO: should we still process samples for the decoder state? | |
| if (!SConfig::GetInstance().m_WiimoteEnableSpeaker) | |
| return; | |
| if (reg_data.sample_rate == 0 || length == 0) | |
| return; | |
| // Even if volume is zero we process samples to maintain proper decoder state. | |
| // TODO consider using static max size instead of new | |
| std::unique_ptr<s16[]> samples(new s16[length * 2]); | |
| unsigned int sample_rate_dividend, sample_length; | |
| u8 volume_divisor; | |
| if (reg_data.format == SpeakerLogic::DATA_FORMAT_PCM) | |
| { | |
| // 8 bit PCM | |
| for (int i = 0; i < length; ++i) | |
| { | |
| samples[i] = ((s16)(s8)data[i]) * 0x100; | |
| } | |
| // 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) | |
| { | |
| 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 | |
| sample_rate_dividend = 6000000; | |
| volume_divisor = 0x7F; | |
| sample_length = (unsigned int)length * 2; | |
| } | |
| else | |
| { | |
| ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", reg_data.format); | |
| return; | |
| } | |
| if (reg_data.volume > volume_divisor) | |
| { | |
| DEBUG_LOG(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum!"); | |
| volume_divisor = reg_data.volume; | |
| } | |
| // SetWiimoteSpeakerVolume expects values from 0 to 255. | |
| // Multiply by 256, cast to int, and clamp to 255 for a uniform conversion. | |
| const double volume = float(reg_data.volume) / volume_divisor * 256; | |
| // Speaker pan using "Constant Power Pan Law" | |
| const double pan_prime = MathUtil::PI * (speaker_pan + 1) / 4; | |
| const auto left_volume = std::min(int(std::cos(pan_prime) * volume), 255); | |
| const auto right_volume = std::min(int(std::sin(pan_prime) * volume), 255); | |
| g_sound_stream->GetMixer()->SetWiimoteSpeakerVolume(left_volume, right_volume); | |
| // ADPCM sample rate is thought to be x2.(3000 x2 = 6000). | |
| const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate; | |
| g_sound_stream->GetMixer()->PushWiimoteSpeakerSamples(samples.get(), sample_length, | |
| sample_rate * 2); | |
| #ifdef WIIMOTE_SPEAKER_DUMP | |
| static int num = 0; | |
| if (num == 0) | |
| { | |
| File::Delete("rmtdump.wav"); | |
| File::Delete("rmtdump.bin"); | |
| atexit(stopdamnwav); | |
| File::OpenFStream(ofile, "rmtdump.bin", ofile.binary | ofile.out); | |
| wav.Start("rmtdump.wav", 6000); | |
| } | |
| wav.AddMonoSamples(samples.get(), length * 2); | |
| if (ofile.good()) | |
| { | |
| for (int i = 0; i < length; i++) | |
| { | |
| ofile << data[i]; | |
| } | |
| } | |
| num++; | |
| #endif | |
| } | |
| void SpeakerLogic::Reset() | |
| { | |
| reg_data = {}; | |
| // Yamaha ADPCM state initialize | |
| adpcm_state.predictor = 0; | |
| adpcm_state.step = 127; | |
| } | |
| void SpeakerLogic::DoState(PointerWrap& p) | |
| { | |
| p.Do(adpcm_state); | |
| p.Do(reg_data); | |
| } | |
| 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); | |
| } | |
| int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) | |
| { | |
| if (I2C_ADDR != slave_addr) | |
| return 0; | |
| if (0x00 == addr) | |
| { | |
| ERROR_LOG(WIIMOTE, "Writing of speaker data to address 0x00 is unimplemented!"); | |
| return count; | |
| } | |
| else | |
| { | |
| // 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); | |
| } | |
| } | |
| } // namespace WiimoteEmu |