diff options
| author | degasus <wickmarkus@web.de> | 2013-01-24 16:58:28 +0100 |
|---|---|---|
| committer | degasus <wickmarkus@web.de> | 2013-01-24 16:58:28 +0100 |
| commit | e0ffdda26e5fb34b2f0d59d5d45b0deda9c90c19 (patch) | |
| tree | 8e289ea93c97fe7430ae501200b60327f572c7e2 /Source/Core/AudioCommon | |
| parent | d5748ebaef93cd601ae6ab5e12f39dd201593ad0 (diff) | |
| parent | d60cc373d1b389215eb63c4e20fe24e902680c7a (diff) | |
Merge branch 'immediate-removal' into GLSL-master
Conflicts:
Source/Core/VideoCommon/Src/PixelShaderGen.cpp
Source/Plugins/Plugin_VideoSoftware/Src/SWRenderer.cpp
immediate-removal is a new created branch seperated from master but reverted the revert of immediate-removal
so we get less conflicts by merging
Diffstat (limited to 'Source/Core/AudioCommon')
| -rw-r--r-- | Source/Core/AudioCommon/AudioCommon.vcxproj | 72 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/AudioCommon.vcxproj.filters | 4 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/AudioCommon.cpp | 40 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/AudioCommon.h | 3 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/AudioCommonConfig.cpp | 70 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/AudioCommonConfig.h | 52 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/DPL2Decoder.cpp | 400 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/DPL2Decoder.h | 24 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/Mixer.h | 5 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/OpenALStream.cpp | 225 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/OpenALStream.h | 26 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/PulseAudioStream.cpp | 287 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/PulseAudioStream.h | 23 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/WaveFile.cpp | 31 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/WaveFile.h | 4 | ||||
| -rw-r--r-- | Source/Core/AudioCommon/Src/aldlist.cpp | 6 |
17 files changed, 788 insertions, 488 deletions
diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj b/Source/Core/AudioCommon/AudioCommon.vcxproj index 0c5f256c19..abb5dbf505 100644 --- a/Source/Core/AudioCommon/AudioCommon.vcxproj +++ b/Source/Core/AudioCommon/AudioCommon.vcxproj @@ -109,78 +109,89 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" /> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouchD.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win32;..\..\..\Externals\SoundTouch\Win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouchD.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win64;..\..\..\Externals\SoundTouch\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouch.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win32;..\..\..\Externals\SoundTouch\Win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouch.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win32;..\..\..\Externals\SoundTouch\Win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouch.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win64;..\..\..\Externals\SoundTouch\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'"> <ClCompile> - <AdditionalIncludeDirectories>..\Common\Src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\Core\Src;..\Common\Src;..\..\..\Externals;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> </Link> - <Lib /> + <Lib> + <AdditionalDependencies>SoundTouch.lib;OpenAL32.lib;dsound.lib;dxerr.lib</AdditionalDependencies> + <AdditionalLibraryDirectories>..\..\..\Externals\OpenAL\Win64;..\..\..\Externals\SoundTouch\Win64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="Src\aldlist.cpp"> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> - </ClCompile> + <ClCompile Include="Src\aldlist.cpp" /> <ClCompile Include="Src\AOSoundStream.cpp" /> <ClCompile Include="Src\AudioCommon.cpp" /> - <ClCompile Include="Src\AudioCommonConfig.cpp" /> + <ClCompile Include="Src\DPL2Decoder.cpp" /> <ClCompile Include="Src\DSoundStream.cpp" /> <ClCompile Include="Src\Mixer.cpp" /> <ClCompile Include="Src\NullSoundStream.cpp" /> @@ -189,17 +200,10 @@ <ClCompile Include="Src\XAudio2Stream.cpp" /> </ItemGroup> <ItemGroup> - <ClInclude Include="Src\aldlist.h"> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> - </ClInclude> + <ClInclude Include="Src\aldlist.h" /> <ClInclude Include="Src\AOSoundStream.h" /> <ClInclude Include="Src\AudioCommon.h" /> - <ClInclude Include="Src\AudioCommonConfig.h" /> + <ClInclude Include="Src\DPL2Decoder.h" /> <ClInclude Include="Src\DSoundStream.h" /> <ClInclude Include="Src\Mixer.h" /> <ClInclude Include="Src\NullSoundStream.h" /> @@ -212,6 +216,14 @@ <None Include="CMakeLists.txt" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\..\Externals\SoundTouch\SoundTouch.vcxproj"> + <Project>{68a5dd20-7057-448b-8fe0-b6ac8d205509}</Project> + <Private>true</Private> + <ReferenceOutputAssembly>true</ReferenceOutputAssembly> + <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies> + <LinkLibraryDependencies>true</LinkLibraryDependencies> + <UseLibraryDependencyInputs>false</UseLibraryDependencyInputs> + </ProjectReference> <ProjectReference Include="..\Common\Common.vcxproj"> <Project>{c87a4178-44f6-49b2-b7aa-c79af1b8c534}</Project> <Private>true</Private> diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters index 7e098ecb85..30ff35a073 100644 --- a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters +++ b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters @@ -3,7 +3,6 @@ <ItemGroup> <ClCompile Include="Src\aldlist.cpp" /> <ClCompile Include="Src\AudioCommon.cpp" /> - <ClCompile Include="Src\AudioCommonConfig.cpp" /> <ClCompile Include="Src\Mixer.cpp" /> <ClCompile Include="Src\WaveFile.cpp" /> <ClCompile Include="Src\AOSoundStream.cpp"> @@ -21,11 +20,11 @@ <ClCompile Include="Src\NullSoundStream.cpp"> <Filter>SoundStreams</Filter> </ClCompile> + <ClCompile Include="Src\DPL2Decoder.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="Src\aldlist.h" /> <ClInclude Include="Src\AudioCommon.h" /> - <ClInclude Include="Src\AudioCommonConfig.h" /> <ClInclude Include="Src\Mixer.h" /> <ClInclude Include="Src\SoundStream.h" /> <ClInclude Include="Src\WaveFile.h" /> @@ -44,6 +43,7 @@ <ClInclude Include="Src\XAudio2Stream.h"> <Filter>SoundStreams</Filter> </ClInclude> + <ClInclude Include="Src\DPL2Decoder.h" /> </ItemGroup> <ItemGroup> <None Include="CMakeLists.txt" /> diff --git a/Source/Core/AudioCommon/CMakeLists.txt b/Source/Core/AudioCommon/CMakeLists.txt index be8c58d14a..3b6d0cea4d 100644 --- a/Source/Core/AudioCommon/CMakeLists.txt +++ b/Source/Core/AudioCommon/CMakeLists.txt @@ -1,5 +1,5 @@ set(SRCS Src/AudioCommon.cpp - Src/AudioCommonConfig.cpp + Src/DPL2Decoder.cpp Src/Mixer.cpp Src/WaveFile.cpp Src/NullSoundStream.cpp) @@ -18,7 +18,7 @@ endif(AO_FOUND) if(OPENAL_FOUND) set(SRCS ${SRCS} Src/OpenALStream.cpp Src/aldlist.cpp) - set(LIBS ${LIBS} ${OPENAL_LIBRARY}) + set(LIBS ${LIBS} ${OPENAL_LIBRARY} SoundTouch ) endif(OPENAL_FOUND) if(PULSEAUDIO_FOUND) diff --git a/Source/Core/AudioCommon/Src/AudioCommon.cpp b/Source/Core/AudioCommon/Src/AudioCommon.cpp index 8a568448a3..e14b9ac45e 100644 --- a/Source/Core/AudioCommon/Src/AudioCommon.cpp +++ b/Source/Core/AudioCommon/Src/AudioCommon.cpp @@ -27,6 +27,10 @@ #include "OpenALStream.h" #include "PulseAudioStream.h" #include "../../Core/Src/Movie.h" +#include "../../Core/Src/ConfigManager.h" + +// This shouldn't be a global, at least not here. +SoundStream *soundStream; namespace AudioCommon { @@ -34,7 +38,7 @@ namespace AudioCommon { // TODO: possible memleak with mixer - std::string backend = ac_Config.sBackend; + std::string backend = SConfig::GetInstance().sBackend; if (backend == BACKEND_OPENAL && OpenALStream::isValid()) soundStream = new OpenALStream(mixer); else if (backend == BACKEND_NULLSOUND && NullSound::isValid()) @@ -54,10 +58,10 @@ namespace AudioCommon if (soundStream != NULL) { - ac_Config.Update(); + UpdateSoundStream(); if (soundStream->Start()) { - if (ac_Config.m_DumpAudio) + if (SConfig::GetInstance().m_DumpAudio) { std::string audio_file_name = File::GetUserPath(D_DUMPAUDIO_IDX) + "audiodump.wav"; File::CreateFullPath(audio_file_name); @@ -82,7 +86,7 @@ namespace AudioCommon if (soundStream) { soundStream->Stop(); - if (ac_Config.m_DumpAudio) + if (SConfig::GetInstance().m_DumpAudio) soundStream->GetMixer()->StopLogAudio(); //soundStream->StopLogAudio(); delete soundStream; @@ -96,22 +100,22 @@ namespace AudioCommon { std::vector<std::string> backends; - if (NullSound::isValid()) + if (NullSound::isValid()) backends.push_back(BACKEND_NULLSOUND); - if (DSound::isValid()) + if (DSound::isValid()) backends.push_back(BACKEND_DIRECTSOUND); - if (XAudio2::isValid()) + if (XAudio2::isValid()) backends.push_back(BACKEND_XAUDIO2); - if (OpenALStream::isValid()) - backends.push_back(BACKEND_OPENAL); - if (AOSound::isValid()) + if (AOSound::isValid()) backends.push_back(BACKEND_AOSOUND); - if (AlsaSound::isValid()) + if (AlsaSound::isValid()) backends.push_back(BACKEND_ALSA); - if (CoreAudioSound::isValid()) + if (CoreAudioSound::isValid()) backends.push_back(BACKEND_COREAUDIO); - if (PulseAudio::isValid()) + if (PulseAudio::isValid()) backends.push_back(BACKEND_PULSEAUDIO); + if (OpenALStream::isValid()) + backends.push_back(BACKEND_OPENAL); return backends; } @@ -122,7 +126,7 @@ namespace AudioCommon { return true; } - return ac_Config.m_EnableJIT; + return SConfig::GetInstance().m_EnableJIT; } void PauseAndLock(bool doLock, bool unpauseOnUnlock) @@ -143,4 +147,12 @@ namespace AudioCommon } } } + void UpdateSoundStream() + { + if (soundStream) + { + soundStream->GetMixer()->SetThrottle(SConfig::GetInstance().m_Framelimit == 2); + soundStream->SetVolume(SConfig::GetInstance().m_Volume); + } + } } diff --git a/Source/Core/AudioCommon/Src/AudioCommon.h b/Source/Core/AudioCommon/Src/AudioCommon.h index e7a111b0a1..b7b32e7498 100644 --- a/Source/Core/AudioCommon/Src/AudioCommon.h +++ b/Source/Core/AudioCommon/Src/AudioCommon.h @@ -19,14 +19,12 @@ #define _AUDIO_COMMON_H_ #include "Common.h" -#include "AudioCommonConfig.h" #include "SoundStream.h" class CMixer; extern SoundStream *soundStream; -extern AudioCommonConfig ac_Config; // UDSPControl union UDSPControl @@ -60,6 +58,7 @@ namespace AudioCommon std::vector<std::string> GetSoundBackends(); bool UseJIT(); void PauseAndLock(bool doLock, bool unpauseOnUnlock=true); + void UpdateSoundStream(); } #endif // _AUDIO_COMMON_H_ diff --git a/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp b/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp deleted file mode 100644 index 08902e1c3f..0000000000 --- a/Source/Core/AudioCommon/Src/AudioCommonConfig.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "AudioCommon.h" -#include "CommonPaths.h" -#include "FileUtil.h" -#include "../../Core/Src/ConfigManager.h" - -AudioCommonConfig ac_Config; - -// This shouldn't be a global, at least not here. -SoundStream *soundStream; - -// Load from given file -void AudioCommonConfig::Load() -{ - IniFile file; - file.Load(File::GetUserPath(F_DSPCONFIG_IDX)); - - file.Get("Config", "EnableJIT", &m_EnableJIT, true); - file.Get("Config", "DumpAudio", &m_DumpAudio, false); -#if defined __linux__ && HAVE_ALSA - file.Get("Config", "Backend", &sBackend, BACKEND_ALSA); -#elif defined __APPLE__ - file.Get("Config", "Backend", &sBackend, BACKEND_COREAUDIO); -#elif defined _WIN32 - file.Get("Config", "Backend", &sBackend, BACKEND_DIRECTSOUND); -#else - file.Get("Config", "Backend", &sBackend, BACKEND_NULLSOUND); -#endif - file.Get("Config", "Frequency", &iFrequency, 48000); - file.Get("Config", "Volume", &m_Volume, 100); -} - -// Set the values for the file -void AudioCommonConfig::SaveSettings() -{ - IniFile file; - file.Load(File::GetUserPath(F_DSPCONFIG_IDX)); - - file.Set("Config", "EnableJIT", m_EnableJIT); - file.Set("Config", "DumpAudio", m_DumpAudio); - file.Set("Config", "Backend", sBackend); - file.Set("Config", "Frequency", iFrequency); - file.Set("Config", "Volume", m_Volume); - - file.Save(File::GetUserPath(F_DSPCONFIG_IDX)); -} - -// Update according to the values (stream/mixer) -void AudioCommonConfig::Update() { - if (soundStream) { - soundStream->GetMixer()->SetThrottle(SConfig::GetInstance().m_Framelimit == 2); - soundStream->SetVolume(m_Volume); - } -} diff --git a/Source/Core/AudioCommon/Src/AudioCommonConfig.h b/Source/Core/AudioCommon/Src/AudioCommonConfig.h deleted file mode 100644 index 76c50c6408..0000000000 --- a/Source/Core/AudioCommon/Src/AudioCommonConfig.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifndef _AUDIO_COMMON_CONFIG_H_ -#define _AUDIO_COMMON_CONFIG_H_ - -#include <string> -#include "IniFile.h" - -// Backend Types -#define BACKEND_NULLSOUND "No audio output" -#define BACKEND_ALSA "ALSA" -#define BACKEND_AOSOUND "AOSound" -#define BACKEND_COREAUDIO "CoreAudio" -#define BACKEND_DIRECTSOUND "DSound" -#define BACKEND_OPENAL "OpenAL" -#define BACKEND_PULSEAUDIO "Pulse" -#define BACKEND_XAUDIO2 "XAudio2" - -struct AudioCommonConfig -{ - bool m_EnableJIT; - bool m_DumpAudio; - int m_Volume; - std::string sBackend; - int iFrequency; - - // Load from given file - void Load(); - - // Self explanatory - void SaveSettings(); - - // Update according to the values (stream/mixer) - void Update(); -}; - -#endif //AUDIO_COMMON_CONFIG diff --git a/Source/Core/AudioCommon/Src/DPL2Decoder.cpp b/Source/Core/AudioCommon/Src/DPL2Decoder.cpp new file mode 100644 index 0000000000..a5d2720878 --- /dev/null +++ b/Source/Core/AudioCommon/Src/DPL2Decoder.cpp @@ -0,0 +1,400 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Dolby Pro Logic 2 decoder from ffdshow-tryout +// * Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au +// * Copyright (c) 2004-2006 Milan Cutka +// * based on mplayer HRTF plugin by ylai + +#include <functional> +#include <vector> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include "DPL2Decoder.h" + +#define M_PI 3.14159265358979323846 +#define M_SQRT1_2 0.70710678118654752440 + +int olddelay = -1; +unsigned int oldfreq = 0; +unsigned int dlbuflen; +int cyc_pos; +float l_fwr, r_fwr, lpr_fwr, lmr_fwr; +std::vector<float> fwrbuf_l, fwrbuf_r; +float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain; +std::vector<float> lf, rf, lr, rr, cf, cr; +float LFE_buf[256]; +unsigned int lfe_pos; +float *filter_coefs_lfe; +unsigned int len125; + +template<class T,class _ftype_t> static _ftype_t dotproduct(int count,const T *buf,const _ftype_t *coefficients) +{ + float sum0=0,sum1=0,sum2=0,sum3=0; + for (;count>=4;buf+=4,coefficients+=4,count-=4) + { + sum0+=buf[0]*coefficients[0]; + sum1+=buf[1]*coefficients[1]; + sum2+=buf[2]*coefficients[2]; + sum3+=buf[3]*coefficients[3]; + } + while (count--) sum0+= *buf++ * *coefficients++; + return sum0+sum1+sum2+sum3; +} + +template<class T> static T firfilter(const T *buf, int pos, int len, int count, const float *coefficients) +{ + int count1, count2; + + if (pos >= count) + { + pos -= count; + count1 = count; count2 = 0; + } + else + { + count2 = pos; + count1 = count - pos; + pos = len - count1; + } + + // high part of window + const T *ptr = &buf[pos]; + + float r1=dotproduct(count1,ptr,coefficients);coefficients+=count1; + float r2=dotproduct(count2,buf,coefficients); + return T(r1+r2); +} + +template<class T> inline const T& limit(const T& val, const T& min, const T& max) +{ + if (val < min) { + return min; + } else if (val > max) { + return max; + } else { + return val; + } +} + +/* +// Hamming +// 2*pi*k +// w(k) = 0.54 - 0.46*cos(------), where 0 <= k < N +// N-1 +// +// n window length +// w buffer for the window parameters +*/ +void hamming(int n, float* w) +{ + int i; + float k = float(2*M_PI/((float)(n-1))); // 2*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = float(0.54 - 0.46*cos(k*(float)i)); +} + +/****************************************************************************** +* FIR filter design +******************************************************************************/ + +/* Design FIR filter using the Window method + +n filter length must be odd for HP and BS filters +w buffer for the filter taps (must be n long) +fc cutoff frequencies (1 for LP and HP, 2 for BP and BS) +0 < fc < 1 where 1 <=> Fs/2 +flags window and filter type as defined in filter.h +variables are ored together: i.e. LP|HAMMING will give a +low pass filter designed using a hamming window +opt beta constant used only when designing using kaiser windows + +returns 0 if OK, -1 if fail +*/ +float* design_fir(unsigned int *n, float* fc, float opt) +{ + unsigned int o = *n & 1; // Indicator for odd filter length + unsigned int end = ((*n + 1) >> 1) - o; // Loop end + unsigned int i; // Loop index + + float k1 = 2 * float(M_PI); // 2*pi*fc1 + float k2 = 0.5f * (float)(1 - o);// Constant used if the filter has even length + float g = 0.0f; // Gain + float t1; // Temporary variables + float fc1; // Cutoff frequencies + + // Sanity check + if(*n==0) return NULL; + fc[0]=limit(fc[0],float(0.001),float(1)); + + float *w=(float*)calloc(sizeof(float),*n); + + // Get window coefficients + hamming(*n,w); + + fc1=*fc; + // Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2 + fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25f; + k1 *= fc1; + + // Low pass filter + + // If the filter length is odd, there is one point which is exactly + // in the middle. The value at this point is 2*fCutoff*sin(x)/x, + // where x is zero. To make sure nothing strange happens, we set this + // value separately. + if (o) + { + w[end] = fc1 * w[end] * 2.0f; + g=w[end]; + } + + // Create filter + for (i=0 ; i<end ; i++) + { + t1 = (float)(i+1) - k2; + w[end-i-1] = w[*n-end+i] = float(w[end-i-1] * sin(k1 * t1)/(M_PI * t1)); // Sinc + g += 2*w[end-i-1]; // Total gain in filter + } + + + // Normalize gain + g=1/g; + for (i=0; i<*n; i++) + w[i] *= g; + + return w; +} + +void onSeek(void) +{ + l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0; + std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f); + std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f); + adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0; + std::fill(lf.begin(), lf.end(), 0.0f); + std::fill(rf.begin(), rf.end(), 0.0f); + std::fill(lr.begin(), lr.end(), 0.0f); + std::fill(rr.begin(), rr.end(), 0.0f); + std::fill(cf.begin(), cf.end(), 0.0f); + std::fill(cr.begin(), cr.end(), 0.0f); + lfe_pos = 0; + memset(LFE_buf, 0, sizeof(LFE_buf)); +} + +void done(void) +{ + onSeek(); + if (filter_coefs_lfe) + { + free(filter_coefs_lfe); + } + filter_coefs_lfe = NULL; +} + +float* calc_coefficients_125Hz_lowpass(int rate) +{ + len125 = 256; + float f = 125.0f / (rate / 2); + float *coeffs = design_fir(&len125, &f, 0); + static const float M3_01DB = 0.7071067812f; + for (unsigned int i = 0; i < len125; i++) + { + coeffs[i] *= M3_01DB; + } + return coeffs; +} + +float passive_lock(float x) +{ + static const float MATAGCLOCK = 0.2f; /* AGC range (around 1) where the matrix behaves passively */ + const float x1 = x - 1; + const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK); + return x1 - x1 / (1 + ax1s * ax1s) + 1; +} + +void matrix_decode(const float *in, const int k, const int il, + const int ir, bool decode_rear, + const int dlbuflen, + float l_fwr, float r_fwr, + float lpr_fwr, float lmr_fwr, + float *adapt_l_gain, float *adapt_r_gain, + float *adapt_lpr_gain, float *adapt_lmr_gain, + float *lf, float *rf, float *lr, + float *rr, float *cf) +{ + static const float M9_03DB = 0.3535533906f; + static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */ + static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */ + static const float MATCOMPGAIN = 0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */ + + const int kr = (k + olddelay) % dlbuflen; + float l_gain = (l_fwr + r_fwr) / (1 + l_fwr + l_fwr); + float r_gain = (l_fwr + r_fwr) / (1 + r_fwr + r_fwr); + /* The 2nd axis has strong gain fluctuations, and therefore require + limits. The factor corresponds to the 1 / amplification of (Lt + - Rt) when (Lt, Rt) is strongly correlated. (e.g. during + dialogues). It should be bigger than -12 dB to prevent + distortion. */ + float lmr_lim_fwr = lmr_fwr > M9_03DB * lpr_fwr ? lmr_fwr : M9_03DB * lpr_fwr; + float lpr_gain = (lpr_fwr + lmr_lim_fwr) / (1 + lpr_fwr + lpr_fwr); + float lmr_gain = (lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr); + float lmr_unlim_gain = (lpr_fwr + lmr_fwr) / (1 + lmr_fwr + lmr_fwr); + float lpr, lmr; + float l_agc, r_agc, lpr_agc, lmr_agc; + float f, d_gain, c_gain, c_agc_cfk; + + /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/ + /* AGC adaption */ + d_gain = (fabs(l_gain - *adapt_l_gain) + fabs(r_gain - *adapt_r_gain)) * 0.5f; + f = d_gain * (1.0f / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain; + *adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain; + /* Matrix */ + l_agc = in[il] * passive_lock(*adapt_l_gain); + r_agc = in[ir] * passive_lock(*adapt_r_gain); + cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2; + if (decode_rear) + { + lr[kr] = rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2; + /* Stereo rear channel is steered with the same AGC steering as + the decoding matrix. Note this requires a fast updating AGC + at the order of 20 ms (which is the case here). */ + lr[kr] *= (l_fwr + l_fwr) / (1 + l_fwr + r_fwr); + rr[kr] *= (r_fwr + r_fwr) / (1 + l_fwr + r_fwr); + } + + /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/ + lpr = (in[il] + in[ir]) * (float)M_SQRT1_2; + lmr = (in[il] - in[ir]) * (float)M_SQRT1_2; + /* AGC adaption */ + d_gain = fabs(lmr_unlim_gain - *adapt_lmr_gain); + f = d_gain * (1.0f / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain; + *adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain; + /* Matrix */ + lpr_agc = lpr * passive_lock(*adapt_lpr_gain); + lmr_agc = lmr * passive_lock(*adapt_lmr_gain); + lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2; + rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2; + + /*** CENTER FRONT CANCELLATION ***/ + /* A heuristic approach exploits that Lt + Rt gain contains the + information about Lt, Rt correlation. This effectively reshapes + the front and rear "cones" to concentrate Lt + Rt to C and + introduce Lt - Rt in L, R. */ + /* 0.67677 is the empirical lower bound for lpr_gain. */ + c_gain = 8 * (*adapt_lpr_gain - 0.67677f); + c_gain = c_gain > 0 ? c_gain : 0; + /* c_gain should not be too high, not even reaching full + cancellation (~ 0.50 - 0.55 at current AGC implementation), or + the center will sound too narrow. */ + c_gain = MATCOMPGAIN / (1 + c_gain * c_gain); + c_agc_cfk = c_gain * cf[k]; + lf[k] -= c_agc_cfk; + rf[k] -= c_agc_cfk; + cf[k] += c_agc_cfk + c_agc_cfk; +} + +void dpl2decode(float *samples, int numsamples, float *out) +{ + static const unsigned int FWRDURATION = 240; /* FWR average duration (samples) */ + static const int cfg_delay = 0; + static const unsigned int fmt_freq = 48000; + static const unsigned int fmt_nchannels = 2; // input channels + + int cur = 0; + + if (olddelay != cfg_delay || oldfreq != fmt_freq) + { + done(); + olddelay = cfg_delay; + oldfreq = fmt_freq; + dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1); + cyc_pos = dlbuflen - 1; + fwrbuf_l.resize(dlbuflen); + fwrbuf_r.resize(dlbuflen); + lf.resize(dlbuflen); + rf.resize(dlbuflen); + lr.resize(dlbuflen); + rr.resize(dlbuflen); + cf.resize(dlbuflen); + cr.resize(dlbuflen); + filter_coefs_lfe = calc_coefficients_125Hz_lowpass(fmt_freq); + lfe_pos = 0; + memset(LFE_buf, 0, sizeof(LFE_buf)); + } + + float *in = samples; // Input audio data + float *end = in + numsamples * fmt_nchannels; // Loop end + + while (in < end) + { + const int k = cyc_pos; + + const int fwr_pos = (k + FWRDURATION) % dlbuflen; + /* Update the full wave rectified total amplitude */ + /* Input matrix decoder */ + l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]); + r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]); + lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]); + lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]); + + /* Matrix encoded 2 channel sources */ + fwrbuf_l[k] = in[0]; + fwrbuf_r[k] = in[1]; + matrix_decode(in, k, 0, 1, true, dlbuflen, + l_fwr, r_fwr, + lpr_fwr, lmr_fwr, + &adapt_l_gain, &adapt_r_gain, + &adapt_lpr_gain, &adapt_lmr_gain, + &lf[0], &rf[0], &lr[0], &rr[0], &cf[0]); + + out[cur + 0] = lf[k]; + out[cur + 1] = rf[k]; + out[cur + 2] = cf[k]; + LFE_buf[lfe_pos] = (out[0] + out[1]) / 2; + out[cur + 3] = firfilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe); + lfe_pos++; + if (lfe_pos == len125) + { + lfe_pos = 0; + } + out[cur + 4] = lr[k]; + out[cur + 5] = rr[k]; + // Next sample... + in += 2; + cur += 6; + cyc_pos--; + if (cyc_pos < 0) + { + cyc_pos += dlbuflen; + } + } +} + +void dpl2reset() +{ + olddelay = -1; + oldfreq = 0; + filter_coefs_lfe = NULL; +} diff --git a/Source/Core/AudioCommon/Src/DPL2Decoder.h b/Source/Core/AudioCommon/Src/DPL2Decoder.h new file mode 100644 index 0000000000..eee8fbf15e --- /dev/null +++ b/Source/Core/AudioCommon/Src/DPL2Decoder.h @@ -0,0 +1,24 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _DPL2DECODER_H_ +#define _DPL2DECODER_H_ + +void dpl2decode(float *samples, int numsamples, float *out); +void dpl2reset(); + +#endif // _DPL2DECODER_H_ diff --git a/Source/Core/AudioCommon/Src/Mixer.h b/Source/Core/AudioCommon/Src/Mixer.h index da456d5bf7..c1a387cf49 100644 --- a/Source/Core/AudioCommon/Src/Mixer.h +++ b/Source/Core/AudioCommon/Src/Mixer.h @@ -92,6 +92,9 @@ public: std::mutex& MixerCritical() { return m_csMixing; } + volatile float GetCurrentSpeed() const { return m_speed; } + void UpdateSpeed(volatile float val) { m_speed = val; } + protected: unsigned int m_sampleRate; unsigned int m_aiSampleRate; @@ -113,6 +116,8 @@ protected: bool m_AIplaying; std::mutex m_csMixing; + + volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed) private: }; diff --git a/Source/Core/AudioCommon/Src/OpenALStream.cpp b/Source/Core/AudioCommon/Src/OpenALStream.cpp index 2abd357782..cfe6a40630 100644 --- a/Source/Core/AudioCommon/Src/OpenALStream.cpp +++ b/Source/Core/AudioCommon/Src/OpenALStream.cpp @@ -15,13 +15,14 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#include <functional> - #include "aldlist.h" #include "OpenALStream.h" +#include "DPL2Decoder.h" #if defined HAVE_OPENAL && HAVE_OPENAL +soundtouch::SoundTouch soundTouch; + // // AyuanX: Spec says OpenAL1.1 is thread safe already // @@ -35,14 +36,21 @@ bool OpenALStream::Start() pDeviceList = new ALDeviceList(); if ((pDeviceList) && (pDeviceList->GetNumDevices())) { - char *defDevName = pDeviceList-> \ - GetDeviceName(pDeviceList->GetDefaultDevice()); + char *defDevName = pDeviceList->GetDeviceName(pDeviceList->GetDefaultDevice()); + + WARN_LOG(AUDIO, "Found OpenAL device %s", defDevName); + pDevice = alcOpenDevice(defDevName); if (pDevice) { pContext = alcCreateContext(pDevice, NULL); if (pContext) { + // Used to determine an appropriate period size (2x period = total buffer size) + //ALCint refresh; + //alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh); + //period_size_in_millisec = 1000 / refresh; + alcMakeContextCurrent(pContext); thread = std::thread(std::mem_fun(&OpenALStream::SoundLoop), this); bReturn = true; @@ -50,8 +58,7 @@ bool OpenALStream::Start() else { alcCloseDevice(pDevice); - PanicAlertT("OpenAL: can't create context " - "for device %s", defDevName); + PanicAlertT("OpenAL: can't create context for device %s", defDevName); } } else @@ -65,6 +72,10 @@ bool OpenALStream::Start() PanicAlertT("OpenAL: can't find sound devices"); } + // Initialise DPL2 parameters + dpl2reset(); + + soundTouch.clear(); return bReturn; } @@ -74,6 +85,8 @@ void OpenALStream::Stop() // kick the thread if it's waiting soundSyncEvent.Set(); + soundTouch.clear(); + thread.join(); alSourceStop(uiSource); @@ -82,7 +95,7 @@ void OpenALStream::Stop() // Clean up buffers and sources alDeleteSources(1, &uiSource); uiSource = 0; - alDeleteBuffers(OAL_NUM_BUFFERS, uiBuffers); + alDeleteBuffers(numBuffers, uiBuffers); ALCcontext *pContext = alcGetCurrentContext(); ALCdevice *pDevice = alcGetContextsDevice(pContext); @@ -111,6 +124,7 @@ void OpenALStream::Clear(bool mute) if(m_muted) { + soundTouch.clear(); alSourceStop(uiSource); } else @@ -124,20 +138,29 @@ void OpenALStream::SoundLoop() Common::SetCurrentThreadName("Audio thread - openal"); u32 ulFrequency = m_mixer->GetSampleRate(); + numBuffers = Core::g_CoreStartupParameter.iLatency + 2; // OpenAL requires a minimum of two buffers - memset(uiBuffers, 0, OAL_NUM_BUFFERS * sizeof(ALuint)); + memset(uiBuffers, 0, numBuffers * sizeof(ALuint)); uiSource = 0; // Generate some AL Buffers for streaming - alGenBuffers(OAL_NUM_BUFFERS, (ALuint *)uiBuffers); + alGenBuffers(numBuffers, (ALuint *)uiBuffers); // Generate a Source to playback the Buffers alGenSources(1, &uiSource); // Short Silence + memset(sampleBuffer, 0, OAL_MAX_SAMPLES * SIZE_FLOAT * SURROUND_CHANNELS * numBuffers); memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * 4); - for (int i = 0; i < OAL_NUM_BUFFERS; i++) - alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, OAL_MAX_SAMPLES, ulFrequency); - alSourceQueueBuffers(uiSource, OAL_NUM_BUFFERS, uiBuffers); + for (int i = 0; i < numBuffers; i++) + { +#if !defined(__APPLE__) + if (Core::g_CoreStartupParameter.bDPL2Decoder) + alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * SIZE_FLOAT * SURROUND_CHANNELS, ulFrequency); + else +#endif + alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * 2 * 2, ulFrequency); + } + alSourceQueueBuffers(uiSource, numBuffers, uiBuffers); alSourcePlay(uiSource); // Set the default sound volume as saved in the config file. @@ -148,41 +171,175 @@ void OpenALStream::SoundLoop() ALint iBuffersFilled = 0; ALint iBuffersProcessed = 0; - ALuint uiBufferTemp[OAL_NUM_BUFFERS] = {0}; + ALint iState = 0; + ALuint uiBufferTemp[OAL_MAX_BUFFERS] = {0}; + + soundTouch.setChannels(2); + soundTouch.setSampleRate(ulFrequency); + soundTouch.setTempo(1.0); + soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0); + soundTouch.setSetting(SETTING_USE_AA_FILTER, 0); + soundTouch.setSetting(SETTING_SEQUENCE_MS, 1); + soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28); + soundTouch.setSetting(SETTING_OVERLAP_MS, 12); + + bool surround_capable = Core::g_CoreStartupParameter.bDPL2Decoder; +#if defined(__APPLE__) + bool float32_capable = false; +#else + bool float32_capable = true; +#endif while (!threadData) { + // num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD. + const u32 stereo_16_bit_size = 4; + const u32 dma_length = 32; + const u64 ais_samples_per_second = 48000 * stereo_16_bit_size; + u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length); + u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond(); + + unsigned int numSamples = (unsigned int)num_samples_to_render; + + numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples; + numSamples = m_mixer->Mix(realtimeBuffer, numSamples); + + // Convert the samples from short to float + float dest[OAL_MAX_SAMPLES * 2 * 2 * OAL_MAX_BUFFERS]; + for (u32 i = 0; i < numSamples; ++i) + { + dest[i * 2 + 0] = (float)realtimeBuffer[i * 2 + 0] / (1 << 16); + dest[i * 2 + 1] = (float)realtimeBuffer[i * 2 + 1] / (1 << 16); + } + + soundTouch.putSamples(dest, numSamples); + if (iBuffersProcessed == iBuffersFilled) { alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed); iBuffersFilled = 0; } - unsigned int numSamples = m_mixer->GetNumSamples(); - - if (iBuffersProcessed && (numSamples >= OAL_THRESHOLD)) + if (iBuffersProcessed) { - numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples; - // Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer) - if (iBuffersFilled == 0) - alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp); - - m_mixer->Mix(realtimeBuffer, numSamples); - alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, realtimeBuffer, numSamples * 4, ulFrequency); - alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]); - iBuffersFilled++; - - if (iBuffersFilled == OAL_NUM_BUFFERS) - alSourcePlay(uiSource); + float rate = m_mixer->GetCurrentSpeed(); + if (rate <= 0) + { + Core::RequestRefreshInfo(); + rate = m_mixer->GetCurrentSpeed(); + } + + // Place a lower limit of 10% speed. When a game boots up, there will be + // many silence samples. These do not need to be timestretched. + if (rate > 0.10) + { + // Adjust SETTING_SEQUENCE_MS to balance between lag vs hollow audio + soundTouch.setSetting(SETTING_SEQUENCE_MS, (int)(1 / (rate * rate))); + soundTouch.setTempo(rate); + } + unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * SIZE_FLOAT * SURROUND_CHANNELS * OAL_MAX_BUFFERS); + if (nSamples > 0) + { + // Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer) + if (iBuffersFilled == 0) + { + alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp); + ALenum err = alGetError(); + if (err != 0) + { + ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err); + } + } +#if defined(__APPLE__) + // OSX does not have the alext AL_FORMAT_51CHN32 yet. + surround_capable = false; +#else + if (surround_capable) + { + float dpl2[OAL_MAX_SAMPLES * SIZE_FLOAT * SURROUND_CHANNELS * OAL_MAX_BUFFERS]; + dpl2decode(sampleBuffer, nSamples, dpl2); + + alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2, nSamples * SIZE_FLOAT * SURROUND_CHANNELS, ulFrequency); + ALenum err = alGetError(); + if (err == AL_INVALID_ENUM) + { + // 5.1 is not supported by the host, fallback to stereo + WARN_LOG(AUDIO, "Unable to set 5.1 surround mode. Updating OpenAL Soft might fix this issue."); + surround_capable = false; + } + else if (err != 0) + { + ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err); + } + } +#endif + if (!surround_capable) + { +#if !defined(__APPLE__) + if (float32_capable) + { + alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer, nSamples * 4 * 2, ulFrequency); + ALenum err = alGetError(); + if (err == AL_INVALID_ENUM) + { + float32_capable = false; + } + else if (err != 0) + { + ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err); + } + + } +#endif + if (!float32_capable) + { + // Convert the samples from float to short + short stereo[OAL_MAX_SAMPLES * 2 * 2 * OAL_MAX_BUFFERS]; + for (u32 i = 0; i < nSamples; ++i) + { + stereo[i * 2 + 0] = (short)((float)sampleBuffer[i * 2 + 0] * (1 << 16)); + stereo[i * 2 + 1] = (short)((float)sampleBuffer[i * 2 + 1] * (1 << 16)); + } + alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, stereo, nSamples * 2 * 2, ulFrequency); + + } + } + + alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]); + ALenum err = alGetError(); + if (err != 0) + { + ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err); + } + iBuffersFilled++; + + if (iBuffersFilled == numBuffers) + { + alSourcePlay(uiSource); + ALenum err = alGetError(); + if (err != 0) + { + ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err); + } + } + + alGetSourcei(uiSource, AL_SOURCE_STATE, &iState); + if (iState != AL_PLAYING) + { + // Buffer underrun occurred, resume playback + alSourcePlay(uiSource); + ALenum err = alGetError(); + if (err != 0) + { + ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err); + } + } + } } - else if (numSamples >= OAL_THRESHOLD) + else { - ALint state = 0; - alGetSourcei(uiSource, AL_SOURCE_STATE, &state); - if (state == AL_STOPPED) - alSourcePlay(uiSource); + soundSyncEvent.Wait(); } - soundSyncEvent.Wait(); } } diff --git a/Source/Core/AudioCommon/Src/OpenALStream.h b/Source/Core/AudioCommon/Src/OpenALStream.h index 2c0d75b510..a808fcefc9 100644 --- a/Source/Core/AudioCommon/Src/OpenALStream.h +++ b/Source/Core/AudioCommon/Src/OpenALStream.h @@ -24,21 +24,30 @@ #if defined HAVE_OPENAL && HAVE_OPENAL #ifdef _WIN32 -#include "../../../../Externals/OpenAL/include/al.h" -#include "../../../../Externals/OpenAL/include/alc.h" +#include <OpenAL/include/al.h> +#include <OpenAL/include/alc.h> +#include <OpenAL/include/alext.h> #elif defined __APPLE__ #include <OpenAL/al.h> #include <OpenAL/alc.h> #else #include <AL/al.h> #include <AL/alc.h> +#include <AL/alext.h> #endif +#include "Core.h" +#include "HW/SystemTimers.h" +#include "HW/AudioInterface.h" +#include <soundtouch/SoundTouch.h> +#include <soundtouch/STTypes.h> + // 16 bit Stereo #define SFX_MAX_SOURCE 1 -#define OAL_NUM_BUFFERS 16 -#define OAL_MAX_SAMPLES 512 // AyuanX: Don't make it too large, as larger buffer means longer delay -#define OAL_THRESHOLD 128 // Some games are quite sensitive to delay +#define OAL_MAX_BUFFERS 32 +#define OAL_MAX_SAMPLES 256 +#define SURROUND_CHANNELS 6 // number of channels in surround mode +#define SIZE_FLOAT 4 // size of a float in bytes #endif class OpenALStream: public SoundStream @@ -64,11 +73,14 @@ public: private: std::thread thread; Common::Event soundSyncEvent; - + short realtimeBuffer[OAL_MAX_SAMPLES * 2]; - ALuint uiBuffers[OAL_NUM_BUFFERS]; + soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SIZE_FLOAT * SURROUND_CHANNELS * OAL_MAX_BUFFERS]; + ALuint uiBuffers[OAL_MAX_BUFFERS]; ALuint uiSource; ALfloat fVolume; + + u8 numBuffers; #else public: OpenALStream(CMixer *mixer, void *hWnd = NULL): SoundStream(mixer) {} diff --git a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp index 4851b4bd12..faa94c22e0 100644 --- a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp +++ b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp @@ -22,31 +22,31 @@ #include "PulseAudioStream.h" -#define BUFFER_SIZE 4096 -#define BUFFER_SIZE_BYTES (BUFFER_SIZE * 4) - -PulseAudio::PulseAudio(CMixer *mixer) - : SoundStream(mixer), thread_running(false), mainloop(NULL) - , context(NULL), stream(NULL), iVolume(100) +namespace { - mix_buffer = new u8[BUFFER_SIZE_BYTES]; +const size_t BUFFER_SAMPLES = 512; +const size_t CHANNEL_COUNT = 2; +const size_t BUFFER_SIZE = BUFFER_SAMPLES * CHANNEL_COUNT; } -PulseAudio::~PulseAudio() -{ - delete [] mix_buffer; -} +PulseAudio::PulseAudio(CMixer *mixer) + : SoundStream(mixer) + , mix_buffer(BUFFER_SIZE) + , thread() + , run_thread() + , pa() +{} bool PulseAudio::Start() { - thread_running = true; + run_thread = true; thread = std::thread(std::mem_fun(&PulseAudio::SoundLoop), this); return true; } void PulseAudio::Stop() { - thread_running = false; + run_thread = false; thread.join(); } @@ -60,260 +60,53 @@ void PulseAudio::SoundLoop() { Common::SetCurrentThreadName("Audio thread - pulse"); - thread_running = PulseInit(); - - while (thread_running) - { - int frames_to_deliver = 512; - m_mixer->Mix((short *)mix_buffer, frames_to_deliver); - if (!Write(mix_buffer, frames_to_deliver * 4)) - ERROR_LOG(AUDIO, "PulseAudio failure writing data"); - } - PulseShutdown(); -} - -bool PulseAudio::PulseInit() -{ - // The Sample format to use - const pa_sample_spec ss = - { - PA_SAMPLE_S16LE, - m_mixer->GetSampleRate(), - 2 - }; - - mainloop = pa_threaded_mainloop_new(); - - context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "dolphin-emu"); - pa_context_set_state_callback(context, ContextStateCB, this); - - if (pa_context_connect(context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) + if (PulseInit()) { - ERROR_LOG(AUDIO, "PulseAudio failed to connect context: %s", - pa_strerror(pa_context_errno(context))); - return false; - } - - pa_threaded_mainloop_lock(mainloop); - pa_threaded_mainloop_start(mainloop); - - for (;;) - { - pa_context_state_t state; - - state = pa_context_get_state(context); - - if (state == PA_CONTEXT_READY) - break; - - if (!PA_CONTEXT_IS_GOOD(state)) + while (run_thread) { - ERROR_LOG(AUDIO, "PulseAudio context state failure: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; + m_mixer->Mix(&mix_buffer[0], mix_buffer.size() / CHANNEL_COUNT); + Write(&mix_buffer[0], mix_buffer.size() * sizeof(s16)); } - // Wait until the context is ready - pa_threaded_mainloop_wait(mainloop); + PulseShutdown(); } - - if (!(stream = pa_stream_new(context, "emulator", &ss, NULL))) - { - ERROR_LOG(AUDIO, "PulseAudio failed to create playback stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - // Set callbacks for the playback stream - pa_stream_set_state_callback(stream, StreamStateCB, this); - pa_stream_set_write_callback(stream, StreamWriteCB, this); - - if (pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL) < 0) - { - ERROR_LOG(AUDIO, "PulseAudio failed to connect playback stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - for (;;) - { - pa_stream_state_t state; - - state = pa_stream_get_state(stream); - - if (state == PA_STREAM_READY) - break; - - if (!PA_STREAM_IS_GOOD(state)) - { - ERROR_LOG(AUDIO, "PulseAudio stream state failure: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - // Wait until the stream is ready - pa_threaded_mainloop_wait(mainloop); - } - - pa_threaded_mainloop_unlock(mainloop); - - SetVolume(iVolume); - - NOTICE_LOG(AUDIO, "Pulse successfully initialized."); - return true; } -void PulseAudio::PulseShutdown() +bool PulseAudio::PulseInit() { - if (mainloop) - pa_threaded_mainloop_stop(mainloop); + pa_sample_spec ss = {}; + ss.format = PA_SAMPLE_S16LE; + ss.channels = 2; + ss.rate = m_mixer->GetSampleRate(); - if (stream) - pa_stream_unref(stream); + int error; + pa = pa_simple_new(nullptr, "dolphin-emu", PA_STREAM_PLAYBACK, + nullptr, "audio", &ss, nullptr, nullptr, &error); - if (context) + if (!pa) { - pa_context_disconnect(context); - pa_context_unref(context); - } - - if (mainloop) - pa_threaded_mainloop_free(mainloop); -} - -void PulseAudio::SignalMainLoop() -{ - pa_threaded_mainloop_signal(mainloop, 0); -} - -void PulseAudio::ContextStateCB(pa_context *c, void *userdata) -{ - switch (pa_context_get_state(c)) - { - case PA_CONTEXT_READY: - case PA_CONTEXT_TERMINATED: - case PA_CONTEXT_FAILED: - ((PulseAudio *)userdata)->SignalMainLoop(); - break; - - default: - break; + ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", + pa_strerror(error)); + return false; } -} - -void PulseAudio::StreamStateCB(pa_stream *s, void * userdata) -{ - switch (pa_stream_get_state(s)) + else { - case PA_STREAM_READY: - case PA_STREAM_TERMINATED: - case PA_STREAM_FAILED: - ((PulseAudio *)userdata)->SignalMainLoop(); - break; - - default: - break; + NOTICE_LOG(AUDIO, "Pulse successfully initialized."); + return true; } } -void PulseAudio::StreamWriteCB(pa_stream *s, size_t length, void *userdata) -{ - ((PulseAudio *)userdata)->SignalMainLoop(); -} - -static bool StateIsGood(pa_context *context, pa_stream *stream) +void PulseAudio::PulseShutdown() { - if (!context || !PA_CONTEXT_IS_GOOD(pa_context_get_state(context)) || - !stream || !PA_STREAM_IS_GOOD(pa_stream_get_state(stream))) - { - if ((context && pa_context_get_state(context) == PA_CONTEXT_FAILED) || - (stream && pa_stream_get_state(stream) == PA_STREAM_FAILED)) - { - ERROR_LOG(AUDIO, "PulseAudio state failure: %s", - pa_strerror(pa_context_errno(context))); - } - else - { - ERROR_LOG(AUDIO, "PulseAudio state failure: %s", - pa_strerror(PA_ERR_BADSTATE)); - } - return false; - } - return true; + pa_simple_free(pa); } -bool PulseAudio::Write(const void *data, size_t length) +void PulseAudio::Write(const void *data, size_t length) { - if (!data || length == 0 || !stream) - return false; - - pa_threaded_mainloop_lock(mainloop); - - if (!StateIsGood(context, stream)) + int error; + if (pa_simple_write(pa, data, length, &error) < 0) { - pa_threaded_mainloop_unlock(mainloop); - return false; + ERROR_LOG(AUDIO, "PulseAudio failed to write data: %s", + pa_strerror(error)); } - - while (length > 0) - { - size_t l; - int r; - - while (!(l = pa_stream_writable_size(stream))) - { - pa_threaded_mainloop_wait(mainloop); - if (!StateIsGood(context, stream)) - { - pa_threaded_mainloop_unlock(mainloop); - return false; - } - } - - if (l == (size_t)-1) - { - ERROR_LOG(AUDIO, "PulseAudio invalid stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - if (l > length) - l = length; - - r = pa_stream_write(stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); - if (r < 0) - { - ERROR_LOG(AUDIO, "PulseAudio error writing to stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - data = (const uint8_t*) data + l; - length -= l; - } - - pa_threaded_mainloop_unlock(mainloop); - return true; -} - -void PulseAudio::SetVolume(int volume) -{ - iVolume = volume; - - if (!stream) - return; - - pa_cvolume cvolume; - const pa_channel_map *channels = pa_stream_get_channel_map(stream); - pa_cvolume_set(&cvolume, channels->channels, - iVolume * (PA_VOLUME_NORM - PA_VOLUME_MUTED) / 100); - - pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), - &cvolume, NULL, this); } diff --git a/Source/Core/AudioCommon/Src/PulseAudioStream.h b/Source/Core/AudioCommon/Src/PulseAudioStream.h index f3a5a43e79..38399e2506 100644 --- a/Source/Core/AudioCommon/Src/PulseAudioStream.h +++ b/Source/Core/AudioCommon/Src/PulseAudioStream.h @@ -19,7 +19,8 @@ #define _PULSE_AUDIO_STREAM_H #if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO -#include <pulse/pulseaudio.h> +#include <pulse/simple.h> +#include <pulse/error.h> #endif #include "Common.h" @@ -27,16 +28,16 @@ #include "Thread.h" +#include <vector> + class PulseAudio : public SoundStream { #if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO public: PulseAudio(CMixer *mixer); - virtual ~PulseAudio(); virtual bool Start(); virtual void Stop(); - virtual void SetVolume(int volume); static bool isValid() {return true;} @@ -46,22 +47,16 @@ public: private: virtual void SoundLoop(); + bool PulseInit(); void PulseShutdown(); - bool Write(const void *data, size_t bytes); - void SignalMainLoop(); - static void ContextStateCB(pa_context *c, void *userdata); - static void StreamStateCB(pa_stream *s, void * userdata); - static void StreamWriteCB(pa_stream *s, size_t length, void *userdata); + void Write(const void *data, size_t bytes); - u8 *mix_buffer; + std::vector<s16> mix_buffer; std::thread thread; - volatile bool thread_running; + volatile bool run_thread; - pa_threaded_mainloop *mainloop; - pa_context *context; - pa_stream *stream; - int iVolume; + pa_simple* pa; #else public: PulseAudio(CMixer *mixer) : SoundStream(mixer) {} diff --git a/Source/Core/AudioCommon/Src/WaveFile.cpp b/Source/Core/AudioCommon/Src/WaveFile.cpp index 802f3f7417..d98348b8bf 100644 --- a/Source/Core/AudioCommon/Src/WaveFile.cpp +++ b/Source/Core/AudioCommon/Src/WaveFile.cpp @@ -100,39 +100,52 @@ void WaveFileWriter::Write4(const char *ptr) file.WriteBytes(ptr, 4); } -void WaveFileWriter::AddStereoSamples(const short *sample_data, int count) +void WaveFileWriter::AddStereoSamples(const short *sample_data, u32 count) { if (!file) PanicAlertT("WaveFileWriter - file not open."); - if (skip_silence) { + + if (skip_silence) + { bool all_zero = true; - for (int i = 0; i < count * 2; i++) - if (sample_data[i]) all_zero = false; - if (all_zero) return; + + for (u32 i = 0; i < count * 2; i++) + { + if (sample_data[i]) + all_zero = false; + } + + if (all_zero) + return; } + file.WriteBytes(sample_data, count * 4); audio_size += count * 4; } -void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, int count) +void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, u32 count) { if (!file) PanicAlertT("WaveFileWriter - file not open."); if (count > BUF_SIZE * 2) - PanicAlert("WaveFileWriter - buffer too small (count = %i).", count); + PanicAlert("WaveFileWriter - buffer too small (count = %u).", count); if (skip_silence) { bool all_zero = true; - for (int i = 0; i < count * 2; i++) + + for (u32 i = 0; i < count * 2; i++) + { if (sample_data[i]) all_zero = false; + } + if (all_zero) return; } - for (int i = 0; i < count * 2; i++) + for (u32 i = 0; i < count * 2; i++) conv_buffer[i] = Common::swap16((u16)sample_data[i]); file.WriteBytes(conv_buffer, count * 4); diff --git a/Source/Core/AudioCommon/Src/WaveFile.h b/Source/Core/AudioCommon/Src/WaveFile.h index 72a090814b..11fbb4ced5 100644 --- a/Source/Core/AudioCommon/Src/WaveFile.h +++ b/Source/Core/AudioCommon/Src/WaveFile.h @@ -50,8 +50,8 @@ public: void SetSkipSilence(bool skip) { skip_silence = skip; } - void AddStereoSamples(const short *sample_data, int count); - void AddStereoSamplesBE(const short *sample_data, int count); // big endian + void AddStereoSamples(const short *sample_data, u32 count); + void AddStereoSamplesBE(const short *sample_data, u32 count); // big endian u32 GetAudioSize() { return audio_size; } }; diff --git a/Source/Core/AudioCommon/Src/aldlist.cpp b/Source/Core/AudioCommon/Src/aldlist.cpp index 69d42148ae..fa11fe3282 100644 --- a/Source/Core/AudioCommon/Src/aldlist.cpp +++ b/Source/Core/AudioCommon/Src/aldlist.cpp @@ -48,7 +48,7 @@ ALDeviceList::ALDeviceList() const char *actualDeviceName = NULL; // DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support - vDeviceInfo.empty(); + vDeviceInfo.clear(); vDeviceInfo.reserve(10); defaultDeviceIndex = 0; @@ -146,12 +146,12 @@ ALDeviceList::~ALDeviceList() { for (u32 i = 0; i < vDeviceInfo.size(); i++) { if (vDeviceInfo[i].pvstrExtensions) { - vDeviceInfo[i].pvstrExtensions->empty(); + vDeviceInfo[i].pvstrExtensions->clear(); delete vDeviceInfo[i].pvstrExtensions; } } - vDeviceInfo.empty(); + vDeviceInfo.clear(); } /* |
