diff --git a/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp b/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp index ebec56e41d..f97b0ba75d 100644 --- a/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp +++ b/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp @@ -26,14 +26,19 @@ void AudioCommonConfig::Load(IniFile &file) { file.Get("Config", "Volume", &m_Volume, 75); #ifdef _WIN32 file.Get("Config", "Backend", &sBackend, BACKEND_DIRECTSOUND); + file.Get("Config", "Frequency", &sFrequency, "32,000 Hz"); #elif defined __APPLE__ std::string temp; file.Get("Config", "Backend", &temp, BACKEND_COREAUDIO); strncpy(sBackend, temp.c_str(), 128); + file.Get("Config", "Frequency", &temp, "32,000 Hz"); + strncpy(sFrequency, temp.c_str(), 128); #elif defined __linux__ file.Get("Config", "Backend", &sBackend, BACKEND_ALSA); + file.Get("Config", "Frequency", &sFrequency, "32,000 Hz"); #else file.Get("Config", "Backend", &sBackend, BACKEND_OPENAL); + file.Get("Config", "Frequency", &sFrequency, "32,000 Hz"); #endif } @@ -43,6 +48,7 @@ void AudioCommonConfig::Set(IniFile &file) { file.Set("Config", "EnableThrottle", m_EnableThrottle); file.Set("Config", "EnableJIT", m_EnableJIT); file.Set("Config", "Backend", sBackend); + file.Set("Config", "Frequency", sFrequency); file.Set("Config", "Volume", m_Volume); } diff --git a/Source/Core/AudioCommon/Src/AudioCommonConfig.h b/Source/Core/AudioCommon/Src/AudioCommonConfig.h index 1267b320b8..524251bb86 100644 --- a/Source/Core/AudioCommon/Src/AudioCommonConfig.h +++ b/Source/Core/AudioCommon/Src/AudioCommonConfig.h @@ -39,8 +39,10 @@ struct AudioCommonConfig int m_Volume; #ifdef __APPLE__ char sBackend[128]; + char sFrequency[128]; #else std::string sBackend; + std::string sFrequency; #endif // Load from given file diff --git a/Source/Core/AudioCommon/Src/Mixer.cpp b/Source/Core/AudioCommon/Src/Mixer.cpp index b4f1be935d..d914f634c5 100644 --- a/Source/Core/AudioCommon/Src/Mixer.cpp +++ b/Source/Core/AudioCommon/Src/Mixer.cpp @@ -57,74 +57,39 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples) samples[i] = Common::swap16(m_buffer[(m_indexR + i) & INDEX_MASK]); m_indexR += numLeft * 2; } - else + else //linear interpolation { - // AyuanX: Up-sampling is not implemented yet - PanicAlert("Mixer: Up-sampling is not implemented yet!"); -/* - static int PV1l=0,PV2l=0,PV3l=0,PV4l=0; - static int PV1r=0,PV2r=0,PV3r=0,PV4r=0; - static int acc=0; + //render numleft sample pairs to samples[] + //advance m_indexR with sample position + //remember fractional offset - while (num_stereo_samples) { - acc += core_sample_rate; - while (num_stereo_samples && (acc >= 48000)) { - PV4l=PV3l; - PV3l=PV2l; - PV2l=PV1l; - PV1l=*(samples++); //32bit processing - PV4r=PV3r; - PV3r=PV2r; - PV2r=PV1r; - PV1r=*(samples++); //32bit processing - num_stereo_samples--; - acc-=48000; - } - - // defaults to nearest - s32 DataL = PV1l; - s32 DataR = PV1r; - - if (m_mode == 1) { //linear - - DataL = PV1l + ((PV2l - PV1l)*acc)/48000; - DataR = PV1r + ((PV2r - PV1r)*acc)/48000; - } - else if (m_mode == 2) {//cubic - s32 a0l = PV1l - PV2l - PV4l + PV3l; - s32 a0r = PV1r - PV2r - PV4r + PV3r; - s32 a1l = PV4l - PV3l - a0l; - s32 a1r = PV4r - PV3r - a0r; - s32 a2l = PV1l - PV4l; - s32 a2r = PV1r - PV4r; - s32 a3l = PV2l; - s32 a3r = PV2r; - - s32 t0l = ((a0l )*acc)/48000; - s32 t0r = ((a0r )*acc)/48000; - s32 t1l = ((t0l+a1l)*acc)/48000; - s32 t1r = ((t0r+a1r)*acc)/48000; - s32 t2l = ((t1l+a2l)*acc)/48000; - s32 t2r = ((t1r+a2r)*acc)/48000; - s32 t3l = ((t2l+a3l)); - s32 t3r = ((t2r+a3r)); - - DataL = t3l; - DataR = t3r; - } - - int l = DataL, r = DataR; - if (l < -32767) l = -32767; - if (r < -32767) r = -32767; - if (l > 32767) l = 32767; - if (r > 32767) r = 32767; - sample_queue.push(l); - sample_queue.push(r); - m_queueSize += 2; - } -*/ + static u32 frac = 0; + const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)m_sampleRate ); + + for (u32 i = 0; i < numLeft * 2; i+=2) { + u32 m_indexR2 = m_indexR + 2; //next sample + if ((m_indexR2 & INDEX_MASK) == (m_indexW & INDEX_MASK)) //..if it exists + m_indexR2 = m_indexR; + + + s16 l1 = Common::swap16(m_buffer[m_indexR & INDEX_MASK]); //current + s16 l2 = Common::swap16(m_buffer[m_indexR2 & INDEX_MASK]); //next + int sampleL = (l1 << 16) + (l2 - l1) * (u16)frac >> 16; + samples[i] = sampleL; + + s16 r1 = Common::swap16(m_buffer[(m_indexR + 1) & INDEX_MASK]); //current + s16 r2 = Common::swap16(m_buffer[(m_indexR2 + 1) & INDEX_MASK]); //next + int sampleR = (r1 << 16) + (r2 - r1) * (u16)frac >> 16; + samples[i+1] = sampleR; + + frac += ratio; + m_indexR += 2 * (u16)(frac >> 16); + frac &= 0xffff; + } } + + } else { numLeft = 0; } @@ -154,7 +119,7 @@ void CMixer::PushSamples(short *samples, unsigned int num_samples) if (m_throttle) { // The auto throttle function. This loop will put a ceiling on the CPU MHz. - while (Common::AtomicLoad(m_numSamples) >= MAX_SAMPLES - RESERVED_SAMPLES) + while (Common::AtomicLoad(m_numSamples) + RESERVED_SAMPLES >= MAX_SAMPLES) { if (g_dspInitialize.pEmulatorState) { @@ -171,7 +136,7 @@ void CMixer::PushSamples(short *samples, unsigned int num_samples) } // Check if we have enough free space - if (num_samples > MAX_SAMPLES - Common::AtomicLoad(m_numSamples)) + if (num_samples + Common::AtomicLoad(m_numSamples) > MAX_SAMPLES) return; // AyuanX: Actual re-sampling work has been moved to sound thread @@ -190,12 +155,12 @@ void CMixer::PushSamples(short *samples, unsigned int num_samples) m_indexW += num_samples * 2; - if (m_sampleRate != 32000) - { - PanicAlert("Mixer: Up-sampling is not implemented yet!"); - } - - Common::AtomicAdd(m_numSamples, num_samples); + if (m_sampleRate == 32000) + Common::AtomicAdd(m_numSamples, num_samples); + else if (m_sampleRate == 48000) + Common::AtomicAdd(m_numSamples, num_samples * 1.5); + else + PanicAlert("Mixer: Unsupported sample rate."); return; } diff --git a/Source/Core/AudioCommon/Src/Mixer.h b/Source/Core/AudioCommon/Src/Mixer.h index dfa4d41828..984ec1c211 100644 --- a/Source/Core/AudioCommon/Src/Mixer.h +++ b/Source/Core/AudioCommon/Src/Mixer.h @@ -26,7 +26,7 @@ class CMixer { public: - CMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000) + CMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000) : m_aiSampleRate(AISampleRate) , m_dacSampleRate(DACSampleRate) , m_bits(16) @@ -39,9 +39,8 @@ public: { // AyuanX: The internal (Core & DSP) sample rate is fixed at 32KHz // So when AI/DAC sample rate differs than 32KHz, we have to do re-sampling - // I prefer speed so let's do down-sampling instead of up-sampling - // If you like better sound than speed, feel free to implement the up-sampling code - m_sampleRate = 32000; + m_sampleRate = BackendSampleRate; + INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized (AISampleRate:%i, DACSampleRate:%i)", AISampleRate, DACSampleRate); } diff --git a/Source/Core/Core/Src/HW/AudioInterface.cpp b/Source/Core/Core/Src/HW/AudioInterface.cpp index 2a3bfdfcdd..1aceee6653 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.cpp +++ b/Source/Core/Core/Src/HW/AudioInterface.cpp @@ -281,17 +281,11 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples const int lvolume = g_AudioRegister.m_Volume.leftVolume; const int rvolume = g_AudioRegister.m_Volume.rightVolume; - if (g_AISampleRate == 48000 && _sampleRate == 32000) { _dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples & 1), "Number of Samples: %i must be even!", _numSamples); _numSamples = _numSamples * 3 / 2; } - else if (g_AISampleRate == 32000 && _sampleRate == 48000) - { - // AyuanX: Up-sampling is not implemented yet - PanicAlert("AUDIO_INTERFACE: Up-sampling is not implemented yet!"); - } int pcm_l = 0, pcm_r = 0; for (unsigned int i = 0; i < _numSamples; i++) @@ -299,45 +293,82 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples if (pos == 0) ReadStreamBlock(pcm); - if (g_AISampleRate == 48000 && _sampleRate == 32000) + if (g_AISampleRate == 48000 && _sampleRate == 32000) //downsample 48>32 { if (i % 3) { pcm_l = (((pcm_l + (int)pcm[pos*2]) / 2 * lvolume) >> 8) + (int)(*_pDestBuffer); - if (pcm_l > 32767) - pcm_l = 32767; - else if (pcm_l < -32767) - pcm_l = -32767; + if (pcm_l > 32767) pcm_l = 32767; + else if (pcm_l < -32767) pcm_l = -32767; *_pDestBuffer++ = pcm_l; pcm_r = (((pcm_r + (int)pcm[pos*2+1]) / 2 * rvolume) >> 8) + (int)(*_pDestBuffer); - if (pcm_r > 32767) - pcm_r = 32767; - else if (pcm_r < -32767) - pcm_r = -32767; + if (pcm_r > 32767) pcm_r = 32767; + else if (pcm_r < -32767) pcm_r = -32767; *_pDestBuffer++ = pcm_r; } pcm_l = pcm[pos*2]; pcm_r = pcm[pos*2+1]; + + pos++; } - else + else if (g_AISampleRate == 32000 && _sampleRate == 48000) //upsample 32>48 { + //starts with one sample of 0 + const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)_sampleRate ); + static u32 frac = 0; + + static s16 l1 = 0; + static s16 l2 = 0; + static s16 r1 = 0; + static s16 r2 = 0; + + + if ( frac >= 0x10000 || frac == 0) + { + frac &= 0xffff; + + l1 = l2; //current + l2 = pcm[pos * 2]; //next + + r1 = r2; //current + r2 = pcm[pos * 2 + 1]; //next + } + + pcm_l = (l1 << 16) + (l2 - l1) * (u16)frac >> 16; + pcm_r = (l1 << 16) + (l2 - l1) * (u16)frac >> 16; + + + pcm_l = (pcm_l * lvolume >> 8) + (int)(*_pDestBuffer); + if (pcm_l > 32767) pcm_l = 32767; + else if (pcm_l < -32767) pcm_l = -32767; + *_pDestBuffer++ = pcm_l; + + pcm_r = (pcm_r * lvolume >> 8) + (int)(*_pDestBuffer); + if (pcm_r > 32767) pcm_r = 32767; + else if (pcm_r < -32767) pcm_r = -32767; + *_pDestBuffer++ = pcm_r; + + frac += ratio; + pos += frac >> 16; + + } + else //1:1 no resampling + { pcm_l = (((int)pcm[pos*2] * lvolume) >> 8) + (int)(*_pDestBuffer); - if (pcm_l > 32767) - pcm_l = 32767; - else if (pcm_l < -32767) - pcm_l = -32767; + if (pcm_l > 32767) pcm_l = 32767; + else if (pcm_l < -32767) pcm_l = -32767; *_pDestBuffer++ = pcm_l; pcm_r = (((int)pcm[pos*2+1] * rvolume) >> 8) + (int)(*_pDestBuffer); - if (pcm_r > 32767) - pcm_r = 32767; - else if (pcm_r < -32767) - pcm_r = -32767; + if (pcm_r > 32767) pcm_r = 32767; + else if (pcm_r < -32767) pcm_r = -32767; *_pDestBuffer++ = pcm_r; + + pos++; } - if (++pos == 28) + if (pos == 28) pos = 0; } } diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.cpp index 8619a25118..ab9d810b8b 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.cpp +++ b/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.cpp @@ -23,6 +23,7 @@ BEGIN_EVENT_TABLE(DSPConfigDialogHLE, wxDialog) EVT_CHECKBOX(ID_ENABLE_HLE_AUDIO, DSPConfigDialogHLE::SettingsChanged) EVT_CHECKBOX(ID_ENABLE_DTK_MUSIC, DSPConfigDialogHLE::SettingsChanged) EVT_CHECKBOX(ID_ENABLE_THROTTLE, DSPConfigDialogHLE::SettingsChanged) + EVT_CHOICE(ID_FREQUENCY, DSPConfigDialogHLE::SettingsChanged) EVT_CHOICE(ID_BACKEND, DSPConfigDialogHLE::BackendChanged) EVT_COMMAND_SCROLL(ID_VOLUME, DSPConfigDialogHLE::VolumeChanged) END_EVENT_TABLE() @@ -44,6 +45,12 @@ DSPConfigDialogHLE::DSPConfigDialogHLE(wxWindow *parent, wxWindowID id, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); m_buttonEnableThrottle = new wxCheckBox(this, ID_ENABLE_THROTTLE, wxT("Enable Audio Throttle"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + wxStaticText *FrequencyText = new wxStaticText(this, wxID_ANY, wxT("Sample Rate"), + wxDefaultPosition, wxDefaultSize, 0); + m_FrequencySelection = new wxChoice(this, ID_FREQUENCY, wxDefaultPosition, wxSize(110, 20), + wxArrayRates, 0, wxDefaultValidator, wxEmptyString); + wxStaticText *BackendText = new wxStaticText(this, wxID_ANY, wxT("Audio Backend"), wxDefaultPosition, wxDefaultSize, 0); m_BackendSelection = new wxChoice(this, ID_BACKEND, wxDefaultPosition, wxSize(110, 20), @@ -67,22 +74,41 @@ DSPConfigDialogHLE::DSPConfigDialogHLE(wxWindow *parent, wxWindowID id, wxT("Disabling this could cause abnormal game speed, such as too fast.\n") wxT("But sometimes enabling this could cause constant noise.\n") wxT("\nKeyboard Shortcut : Hold down to instantly disable Throttle.")); + m_FrequencySelection-> + SetToolTip(wxT("Changing this will have no effect while the emulator is running!")); m_BackendSelection-> SetToolTip(wxT("Changing this will have no effect while the emulator is running!")); - m_volumeSlider->SetToolTip(wxT("This setting only affects DSound, OpenAL, and PulseAudio.")); + m_volumeSlider->SetToolTip(wxT("This setting only affects DSound, OpenAL, XAudio2, and PulseAudio.")); // Create sizer and add items to dialog wxBoxSizer *sMain = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sSettings = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer *sBackend = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *sBackend = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *sFrequency = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sButtons = new wxBoxSizer(wxHORIZONTAL); sbSettings->Add(m_buttonEnableHLEAudio, 0, wxALL, 5); sbSettings->Add(m_buttonEnableDTKMusic, 0, wxALL, 5); sbSettings->Add(m_buttonEnableThrottle, 0, wxALL, 5); - sBackend->Add(BackendText, 0, wxALIGN_CENTER|wxALL, 5); + + sFrequency->Add(FrequencyText, 0, wxALIGN_LEFT|wxALL, 1); + sFrequency->Add(m_FrequencySelection, 0, wxALL, 1); + + m_FrequencySelection->Append(wxString::FromAscii("48,000 Hz")); + m_FrequencySelection->Append(wxString::FromAscii("32,000 Hz")); + #ifdef __APPLE__ + int num = m_FrequencySelection->FindString(wxString::FromAscii(ac_Config.sFrequency)); + #else + int num = m_FrequencySelection->FindString(wxString::FromAscii(ac_Config.sFrequency.c_str())); + #endif + m_FrequencySelection->SetSelection(num); + + sbSettings->Add(sFrequency, 0, wxALL, 7); + + + sBackend->Add(BackendText, 0, wxALIGN_LEFT|wxALL, 1); sBackend->Add(m_BackendSelection, 0, wxALL, 1); - sbSettings->Add(sBackend, 0, wxALL, 2); + sbSettings->Add(sBackend, 0, wxALL, 7); sbSettingsV->Add(m_volumeSlider, 1, wxLEFT|wxRIGHT|wxALIGN_CENTER, 6); sbSettingsV->Add(m_volumeText, 0, wxALL|wxALIGN_LEFT, 4); @@ -138,8 +164,10 @@ void DSPConfigDialogHLE::SettingsChanged(wxCommandEvent& event) #ifdef __APPLE__ strncpy(ac_Config.sBackend, m_BackendSelection->GetStringSelection().mb_str(), 128); + strncpy(ac_Config.sFrequency, m_FrequencySelection->GetStringSelection().mb_str(), 128); #else ac_Config.sBackend = m_BackendSelection->GetStringSelection().mb_str(); + ac_Config.sFrequency = m_FrequencySelection->GetStringSelection().mb_str(); #endif ac_Config.Update(); g_Config.Save(); diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.h b/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.h index 7c606333ab..48634699cc 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/ConfigDlg.h @@ -46,13 +46,16 @@ private: wxCheckBox* m_buttonEnableDTKMusic; wxCheckBox* m_buttonEnableThrottle; wxArrayString wxArrayBackends; + wxArrayString wxArrayRates; wxChoice* m_BackendSelection; + wxChoice* m_FrequencySelection; enum { ID_ENABLE_HLE_AUDIO, ID_ENABLE_DTK_MUSIC, ID_ENABLE_THROTTLE, + ID_FREQUENCY, ID_BACKEND, ID_VOLUME }; diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/HLEMixer.h b/Source/Plugins/Plugin_DSP_HLE/Src/HLEMixer.h index 575ea618e6..a407147373 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/HLEMixer.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/HLEMixer.h @@ -6,8 +6,8 @@ class HLEMixer : public CMixer { public: - HLEMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000) - : CMixer(AISampleRate, DACSampleRate) {}; + HLEMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000) + : CMixer(AISampleRate, DACSampleRate, BackendSampleRate) {}; virtual void Premix(short *samples, unsigned int numSamples); }; diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/main.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/main.cpp index 4482c2370b..0cc39408de 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/main.cpp +++ b/Source/Plugins/Plugin_DSP_HLE/Src/main.cpp @@ -250,9 +250,15 @@ unsigned short DSP_WriteControlRegister(unsigned short _Value) { if (!Temp.DSPHalt && Temp.DSPInit) { - unsigned int AISampleRate, DACSampleRate; + unsigned int AISampleRate, DACSampleRate, BackendSampleRate; g_dspInitialize.pGetSampleRate(AISampleRate, DACSampleRate); - soundStream = AudioCommon::InitSoundStream(new HLEMixer(AISampleRate, DACSampleRate)); + std::string frequency = ac_Config.sFrequency; + if (frequency == "48,000 Hz") + BackendSampleRate = 48000; + else + BackendSampleRate = 32000; + + soundStream = AudioCommon::InitSoundStream(new HLEMixer(AISampleRate, DACSampleRate, BackendSampleRate)); if(!soundStream) PanicAlert("Error starting up sound stream"); // Mixer is initialized g_InitMixer = true;