aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rwxr-xr-xMakefile.in2
-rw-r--r--XBMC-ATV2.xcodeproj/project.pbxproj270
-rw-r--r--XBMC-IOS.xcodeproj/project.pbxproj271
-rw-r--r--XBMC.xcodeproj/project.pbxproj326
-rwxr-xr-xconfigure.in32
-rw-r--r--language/English/strings.po37
-rw-r--r--language/English/strings.xml10
-rw-r--r--lib/DllAvUtil.h4
-rw-r--r--project/VS2010Express/XBMC.vcxproj78
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters250
-rw-r--r--xbmc/Application.cpp148
-rw-r--r--xbmc/Application.h4
-rw-r--r--xbmc/GUIInfoManager.cpp2
-rw-r--r--xbmc/SystemGlobals.cpp2
-rw-r--r--xbmc/addons/Visualisation.cpp55
-rw-r--r--xbmc/addons/Visualisation.h10
-rw-r--r--xbmc/addons/include/xbmc_vis_dll.h2
-rw-r--r--xbmc/addons/include/xbmc_vis_types.h2
-rw-r--r--xbmc/cores/AudioEngine/AEAudioFormat.h110
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.cpp110
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.h (renamed from xbmc/cores/AudioRenderers/AudioRendererFactory.h)33
-rw-r--r--xbmc/cores/AudioEngine/AESinkFactory.cpp167
-rw-r--r--xbmc/cores/AudioEngine/AESinkFactory.h (renamed from xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/IDVDAudioEncoder.h)36
-rw-r--r--xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp274
-rw-r--r--xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h72
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAE.cpp706
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAE.h164
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.cpp125
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.h36
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.cpp1343
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.h203
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.cpp3303
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.h373
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAESound.cpp131
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAESound.h59
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.cpp767
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.h170
-rw-r--r--xbmc/cores/AudioEngine/Engines/CoreAudioRingBuffer.h (renamed from xbmc/cores/AudioRenderers/IOSAudioRingBuffer.h)67
-rw-r--r--xbmc/cores/AudioEngine/Engines/ICoreAudioAEHAL.h51
-rw-r--r--xbmc/cores/AudioEngine/Engines/ICoreAudioSource.h50
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAE.cpp271
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAE.h77
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAESound.cpp225
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAESound.h66
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAEStream.cpp602
-rw-r--r--xbmc/cores/AudioEngine/Engines/PulseAEStream.h131
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAE.cpp1182
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAE.h215
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAESound.cpp115
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAESound.h58
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAEStream.cpp655
-rw-r--r--xbmc/cores/AudioEngine/Engines/SoftAEStream.h156
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AE.h162
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEEncoder.h101
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEPostProc.h (renamed from xbmc/utils/PCMAmplifier.h)34
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AESink.h80
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AESound.h50
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEStream.h216
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h32
-rw-r--r--xbmc/cores/AudioEngine/Makefile.in48
-rw-r--r--xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.cpp109
-rw-r--r--xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.h59
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp756
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkALSA.h80
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp748
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h79
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp99
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkNULL.h49
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp519
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkOSS.h57
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp91
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h48
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp1279
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h79
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp139
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h52
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEBuffer.cpp73
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEBuffer.h172
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp263
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEChannelInfo.h99
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEConvert.cpp1116
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEConvert.h58
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp65
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h57
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEELDParser.cpp207
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEELDParser.h31
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp222
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h84
-rw-r--r--xbmc/cores/AudioEngine/Utils/AERemap.cpp412
-rw-r--r--xbmc/cores/AudioEngine/Utils/AERemap.h56
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp717
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEStreamInfo.h105
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEUtil.cpp457
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEUtil.h77
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp315
-rw-r--r--xbmc/cores/AudioEngine/Utils/AEWAVLoader.h (renamed from xbmc/guilib/GUISound.h)53
-rw-r--r--xbmc/cores/AudioRenderers/ALSADirectSound.cpp699
-rw-r--r--xbmc/cores/AudioRenderers/ALSADirectSound.h98
-rw-r--r--xbmc/cores/AudioRenderers/AudioRendererFactory.cpp234
-rw-r--r--xbmc/cores/AudioRenderers/CoreAudioRenderer.cpp1650
-rw-r--r--xbmc/cores/AudioRenderers/CoreAudioRenderer.h225
-rw-r--r--xbmc/cores/AudioRenderers/IAudioRenderer.h86
-rw-r--r--xbmc/cores/AudioRenderers/IOSAudioRenderer.cpp426
-rw-r--r--xbmc/cores/AudioRenderers/IOSAudioRenderer.h101
-rw-r--r--xbmc/cores/AudioRenderers/Makefile.in28
-rw-r--r--xbmc/cores/AudioRenderers/Makefile.include0
-rw-r--r--xbmc/cores/AudioRenderers/NullDirectSound.cpp214
-rw-r--r--xbmc/cores/AudioRenderers/NullDirectSound.h74
-rw-r--r--xbmc/cores/AudioRenderers/PulseAudioDirectSound.cpp754
-rw-r--r--xbmc/cores/AudioRenderers/PulseAudioDirectSound.h101
-rw-r--r--xbmc/cores/AudioRenderers/Win32DirectSound.cpp674
-rw-r--r--xbmc/cores/AudioRenderers/Win32DirectSound.h108
-rw-r--r--xbmc/cores/AudioRenderers/Win32WASAPI.cpp754
-rw-r--r--xbmc/cores/AudioRenderers/Win32WASAPI.h110
-rw-r--r--xbmc/cores/DummyVideoPlayer.h2
-rw-r--r--xbmc/cores/ExternalPlayer/ExternalPlayer.cpp14
-rw-r--r--xbmc/cores/ExternalPlayer/ExternalPlayer.h2
-rw-r--r--xbmc/cores/IAudioCallback.h2
-rw-r--r--xbmc/cores/IPlayer.h2
-rw-r--r--xbmc/cores/dvdplayer/DVDAudio.cpp207
-rw-r--r--xbmc/cores/dvdplayer/DVDAudio.h20
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h18
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp123
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h27
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp104
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h14
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp173
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h57
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp177
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h17
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp25
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h4
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.cpp211
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.h62
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in5
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp17
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayer.h4
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayerAudio.cpp48
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayerAudio.h23
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayerAudioResampler.cpp8
-rw-r--r--xbmc/cores/paplayer/ADPCMCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/ASAPCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/AudioDecoder.cpp148
-rw-r--r--xbmc/cores/paplayer/AudioDecoder.h22
-rw-r--r--xbmc/cores/paplayer/BXAcodec.cpp1
-rw-r--r--xbmc/cores/paplayer/CDDAcodec.cpp1
-rw-r--r--xbmc/cores/paplayer/DVDPlayerCodec.cpp11
-rw-r--r--xbmc/cores/paplayer/DVDPlayerCodec.h3
-rw-r--r--xbmc/cores/paplayer/FLACcodec.cpp25
-rw-r--r--xbmc/cores/paplayer/FLACcodec.h2
-rw-r--r--xbmc/cores/paplayer/ICodec.h23
-rw-r--r--xbmc/cores/paplayer/MP3codec.cpp118
-rw-r--r--xbmc/cores/paplayer/MP3codec.h8
-rw-r--r--xbmc/cores/paplayer/ModplugCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/NSFCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/OGGcodec.cpp41
-rw-r--r--xbmc/cores/paplayer/OGGcodec.h2
-rw-r--r--xbmc/cores/paplayer/PAPlayer.cpp1388
-rw-r--r--xbmc/cores/paplayer/PAPlayer.h195
-rw-r--r--xbmc/cores/paplayer/SPCCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/TimidityCodec.cpp1
-rw-r--r--xbmc/cores/paplayer/VGMCodec.cpp8
-rw-r--r--xbmc/cores/paplayer/WAVcodec.cpp28
-rw-r--r--xbmc/cores/paplayer/WAVcodec.h3
-rw-r--r--xbmc/cores/paplayer/YMCodec.cpp1
-rw-r--r--xbmc/dialogs/GUIDialogMuteBug.cpp2
-rw-r--r--xbmc/guilib/AudioContext.cpp273
-rw-r--r--xbmc/guilib/AudioContext.h76
-rw-r--r--xbmc/guilib/GUIAudioManager.cpp330
-rw-r--r--xbmc/guilib/GUIAudioManager.h55
-rw-r--r--xbmc/guilib/GUISound.cpp280
-rw-r--r--xbmc/guilib/Makefile.in2
-rw-r--r--xbmc/interfaces/Builtins.cpp2
-rw-r--r--xbmc/interfaces/http-api/XBMChttp.cpp8
-rw-r--r--xbmc/interfaces/json-rpc/ApplicationOperations.cpp2
-rw-r--r--xbmc/network/AirPlayServer.cpp2
-rw-r--r--xbmc/network/UPnP.cpp5
-rw-r--r--xbmc/osx/CocoaInterface.h2
-rw-r--r--xbmc/osx/CocoaInterface.mm47
-rw-r--r--xbmc/osx/CoreAudio.cpp1971
-rw-r--r--xbmc/osx/CoreAudio.h315
-rw-r--r--xbmc/osx/CoreAudioSoundManager.cpp333
-rw-r--r--xbmc/osx/CoreAudioSoundManager.h57
-rw-r--r--xbmc/osx/IOSCoreAudio.cpp559
-rw-r--r--xbmc/osx/IOSCoreAudio.h103
-rw-r--r--xbmc/peripherals/devices/PeripheralCecAdapter.cpp2
-rw-r--r--xbmc/settings/AdvancedSettings.cpp13
-rw-r--r--xbmc/settings/AdvancedSettings.h7
-rw-r--r--xbmc/settings/GUISettings.cpp72
-rw-r--r--xbmc/settings/GUIWindowSettingsCategory.cpp172
-rw-r--r--xbmc/settings/Settings.cpp14
-rw-r--r--xbmc/settings/Settings.h10
-rw-r--r--xbmc/system.h5
-rw-r--r--xbmc/utils/Makefile2
-rw-r--r--xbmc/utils/MathUtils.h11
-rw-r--r--xbmc/utils/PCMAmplifier.cpp71
-rw-r--r--xbmc/utils/PCMRemap.cpp804
-rw-r--r--xbmc/utils/PCMRemap.h147
-rw-r--r--xbmc/video/VideoDatabase.cpp5
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp10
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.cpp10
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoInfo.h1
-rw-r--r--xbmc/visualizations/DirectXSpectrum/directx_spectrum.cpp6
-rw-r--r--xbmc/visualizations/DirectXSpectrum/directx_spectrum.sln6
-rw-r--r--xbmc/visualizations/DirectXSpectrum/directx_spectrum.vcxproj1
-rw-r--r--xbmc/visualizations/Goom/Main.cpp11
-rw-r--r--xbmc/visualizations/Milkdrop/MilkdropXBMC.cpp2
-rw-r--r--xbmc/visualizations/OpenGLSpectrum/opengl_spectrum.cpp7
-rw-r--r--xbmc/visualizations/WaveForm/Main.cpp23
-rw-r--r--xbmc/visualizations/XBMCProjectM/Main.cpp4
-rw-r--r--xbmc/windows/GUIWindowHome.cpp2
212 files changed, 24361 insertions, 14434 deletions
diff --git a/.gitignore b/.gitignore
index 00ace597c0..3748323009 100644
--- a/.gitignore
+++ b/.gitignore
@@ -510,8 +510,8 @@ lib/cmyth/Makefile
# /xbmc/cdrip/
/xbmc/cdrip/Makefile
-# /xbmc/cores/AudioRenderers/
-/xbmc/cores/AudioRenderers/Makefile
+# /xbmc/cores/AudioEngine/
+/xbmc/cores/AudioEngine/Makefile
# /xbmc/cores/DllLoader/exports/
/xbmc/cores/DllLoader/exports/build_wrapper.sh
diff --git a/Makefile.in b/Makefile.in
index 6c92bc9b3f..2596c67a27 100755
--- a/Makefile.in
+++ b/Makefile.in
@@ -30,7 +30,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \
xbmc/addons/addons.a \
xbmc/cdrip/cdrip.a \
xbmc/commons/commons.a \
- xbmc/cores/AudioRenderers/audiorenderers.a \
+ xbmc/cores/AudioEngine/audioengine.a \
xbmc/cores/DllLoader/dllloader.a \
xbmc/cores/DllLoader/exports/exports.a \
xbmc/cores/DllLoader/exports/util/exports_utils.a \
diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj
index 4bcf7b8405..0cf2d775b9 100644
--- a/XBMC-ATV2.xcodeproj/project.pbxproj
+++ b/XBMC-ATV2.xcodeproj/project.pbxproj
@@ -23,6 +23,7 @@
7C0A7FC813A9E75400AFC2BD /* DirtyRegionSolvers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FC413A9E75400AFC2BD /* DirtyRegionSolvers.cpp */; };
7C0A7FC913A9E75400AFC2BD /* DirtyRegionTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FC613A9E75400AFC2BD /* DirtyRegionTracker.cpp */; };
7C0A7FCC13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FCA13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp */; };
+ 7C0B990A154B80200065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B9908154B80200065A238 /* AEDeviceInfo.cpp */; };
7C1A89BB152671FB00C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A89B9152671FB00C63311 /* TextureCacheJob.cpp */; };
7C1F6F8C13ED17CC001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6F8A13ED17CC001726AB /* LibraryDirectory.cpp */; };
7C89627013B702F3003631FE /* GUIWindowScreensaverDim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C89626E13B702F3003631FE /* GUIWindowScreensaverDim.cpp */; };
@@ -78,6 +79,25 @@
DFA6BE8713FED2A10048CC11 /* AirPlayServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA6BE8513FED2A10048CC11 /* AirPlayServer.cpp */; };
DFA6BE8A13FED2B40048CC11 /* HttpParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA6BE8813FED2B40048CC11 /* HttpParser.cpp */; };
DFAB04C113F8385F00B70BFB /* InertialScrollingHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAB04BF13F8385F00B70BFB /* InertialScrollingHandler.cpp */; };
+ DFB662DC15376810006B8FF1 /* AEFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6628C15376810006B8FF1 /* AEFactory.cpp */; };
+ DFB662DE15376810006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629115376810006B8FF1 /* AEEncoderFFmpeg.cpp */; };
+ DFB662DF15376810006B8FF1 /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629415376810006B8FF1 /* CoreAudioAE.cpp */; };
+ DFB662E015376810006B8FF1 /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629615376810006B8FF1 /* CoreAudioAEHAL.cpp */; };
+ DFB662E115376810006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629815376810006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
+ DFB662E215376810006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629A15376810006B8FF1 /* CoreAudioAEHALOSX.cpp */; };
+ DFB662E315376810006B8FF1 /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629C15376810006B8FF1 /* CoreAudioAESound.cpp */; };
+ DFB662E415376810006B8FF1 /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6629E15376810006B8FF1 /* CoreAudioAEStream.cpp */; };
+ DFB662ED15376810006B8FF1 /* AEPPAnimationFade.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662BA15376810006B8FF1 /* AEPPAnimationFade.cpp */; };
+ DFB662F415376810006B8FF1 /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662CA15376810006B8FF1 /* AEBitstreamPacker.cpp */; };
+ DFB662F515376810006B8FF1 /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662CC15376810006B8FF1 /* AEBuffer.cpp */; };
+ DFB662F615376810006B8FF1 /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662CE15376810006B8FF1 /* AEChannelInfo.cpp */; };
+ DFB662F715376810006B8FF1 /* AEConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662D015376810006B8FF1 /* AEConvert.cpp */; };
+ DFB662F815376810006B8FF1 /* AEPackIEC61937.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662D215376810006B8FF1 /* AEPackIEC61937.cpp */; };
+ DFB662F915376810006B8FF1 /* AERemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662D415376810006B8FF1 /* AERemap.cpp */; };
+ DFB662FA15376810006B8FF1 /* AEStreamInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662D615376810006B8FF1 /* AEStreamInfo.cpp */; };
+ DFB662FB15376810006B8FF1 /* AEUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662D815376810006B8FF1 /* AEUtil.cpp */; };
+ DFB662FC15376810006B8FF1 /* AEWAVLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB662DA15376810006B8FF1 /* AEWAVLoader.cpp */; };
+ DFB6631115376991006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6630F15376991006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
DFC5393A1526659D00D5FD5C /* AppIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = DFC539391526659D00D5FD5C /* AppIcon.png */; };
DFCA6B0B15224684000BFAAE /* HTTPApiHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6AFE15224684000BFAAE /* HTTPApiHandler.cpp */; };
DFCA6B0C15224684000BFAAE /* HTTPJsonRpcHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6B0015224684000BFAAE /* HTTPJsonRpcHandler.cpp */; };
@@ -203,7 +223,6 @@
F56C78E7131EC154000AD0F6 /* DVDAudioCodecLPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C725C131EC151000AD0F6 /* DVDAudioCodecLPcm.cpp */; };
F56C78E8131EC154000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C725E131EC151000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.cpp */; };
F56C78E9131EC154000AD0F6 /* DVDAudioCodecPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7260131EC151000AD0F6 /* DVDAudioCodecPcm.cpp */; };
- F56C78EA131EC154000AD0F6 /* DVDAudioEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7263131EC151000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */; };
F56C78EB131EC154000AD0F6 /* DVDCodecUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7269131EC151000AD0F6 /* DVDCodecUtils.cpp */; };
F56C78EC131EC154000AD0F6 /* DVDFactoryCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C726B131EC151000AD0F6 /* DVDFactoryCodec.cpp */; };
F56C78ED131EC154000AD0F6 /* DVDOverlayCodecSSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C726E131EC151000AD0F6 /* DVDOverlayCodecSSA.cpp */; };
@@ -284,8 +303,6 @@
F56C793C131EC154000AD0F6 /* TimidityCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C732D131EC151000AD0F6 /* TimidityCodec.cpp */; };
F56C793D131EC154000AD0F6 /* WAVcodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C732F131EC151000AD0F6 /* WAVcodec.cpp */; };
F56C793F131EC154000AD0F6 /* YMCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7333131EC151000AD0F6 /* YMCodec.cpp */; };
- F56C7941131EC154000AD0F6 /* AudioRendererFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C733A131EC151000AD0F6 /* AudioRendererFactory.cpp */; };
- F56C7942131EC154000AD0F6 /* NullDirectSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C733B131EC151000AD0F6 /* NullDirectSound.cpp */; };
F56C7943131EC154000AD0F6 /* BaseRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C733E131EC151000AD0F6 /* BaseRenderer.cpp */; };
F56C7946131EC154000AD0F6 /* OverlayRendererGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7344131EC151000AD0F6 /* OverlayRendererGL.cpp */; };
F56C7947131EC154000AD0F6 /* OverlayRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7346131EC151000AD0F6 /* OverlayRenderer.cpp */; };
@@ -431,7 +448,6 @@
F56C79EE131EC154000AD0F6 /* ZipDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C749B131EC152000AD0F6 /* ZipDirectory.cpp */; };
F56C79EF131EC154000AD0F6 /* ZipManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C749D131EC152000AD0F6 /* ZipManager.cpp */; };
F56C79F0131EC154000AD0F6 /* AnimatedGif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C74FE131EC152000AD0F6 /* AnimatedGif.cpp */; };
- F56C79F1131EC154000AD0F6 /* AudioContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C74FF131EC152000AD0F6 /* AudioContext.cpp */; };
F56C79F2131EC154000AD0F6 /* D3DResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7500131EC152000AD0F6 /* D3DResource.cpp */; };
F56C79F3131EC154000AD0F6 /* DDSImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7501131EC152000AD0F6 /* DDSImage.cpp */; };
F56C79F4131EC154000AD0F6 /* DirectXGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7502131EC152000AD0F6 /* DirectXGraphics.cpp */; };
@@ -482,7 +498,6 @@
F56C7A22131EC154000AD0F6 /* GUISettingsSliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7530131EC152000AD0F6 /* GUISettingsSliderControl.cpp */; };
F56C7A23131EC154000AD0F6 /* GUIShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7531131EC152000AD0F6 /* GUIShader.cpp */; };
F56C7A24131EC154000AD0F6 /* GUISliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7532131EC152000AD0F6 /* GUISliderControl.cpp */; };
- F56C7A25131EC154000AD0F6 /* GUISound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7533131EC152000AD0F6 /* GUISound.cpp */; };
F56C7A26131EC154000AD0F6 /* GUISpinControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7534131EC152000AD0F6 /* GUISpinControl.cpp */; };
F56C7A27131EC154000AD0F6 /* GUISpinControlEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7535131EC152000AD0F6 /* GUISpinControlEx.cpp */; };
F56C7A28131EC154000AD0F6 /* GUIStandardWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7536131EC152000AD0F6 /* GUIStandardWindow.cpp */; };
@@ -730,8 +745,6 @@
F56C7B2F131EC155000AD0F6 /* LCD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7748131EC154000AD0F6 /* LCD.cpp */; };
F56C7B30131EC155000AD0F6 /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C774A131EC154000AD0F6 /* log.cpp */; };
F56C7B31131EC155000AD0F6 /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C774C131EC154000AD0F6 /* md5.cpp */; };
- F56C7B32131EC155000AD0F6 /* PCMAmplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C774E131EC154000AD0F6 /* PCMAmplifier.cpp */; };
- F56C7B33131EC155000AD0F6 /* PCMRemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7750131EC154000AD0F6 /* PCMRemap.cpp */; };
F56C7B34131EC155000AD0F6 /* PerformanceSample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7752131EC154000AD0F6 /* PerformanceSample.cpp */; };
F56C7B35131EC155000AD0F6 /* PerformanceStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7754131EC154000AD0F6 /* PerformanceStats.cpp */; };
F56C7B37131EC155000AD0F6 /* RegExp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7758131EC154000AD0F6 /* RegExp.cpp */; };
@@ -819,13 +832,11 @@
F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.m in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BC2131EC2DB000AD0F6 /* XBMCAppliance.m */; };
F56C7BCA131EC2DB000AD0F6 /* XBMCController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BC5131EC2DB000AD0F6 /* XBMCController.mm */; };
F56C7BD0131EC301000AD0F6 /* XBMC.png in Resources */ = {isa = PBXBuildFile; fileRef = F56C7BCD131EC301000AD0F6 /* XBMC.png */; };
- F56C7BD6131EC332000AD0F6 /* IOSCoreAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BD2131EC332000AD0F6 /* IOSCoreAudio.cpp */; };
F56C7BDC131EC390000AD0F6 /* WinEventsIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BD9131EC390000AD0F6 /* WinEventsIOS.mm */; };
F56C7BDD131EC390000AD0F6 /* WinSystemIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BDB131EC390000AD0F6 /* WinSystemIOS.mm */; };
F56C7BE3131EC3F3000AD0F6 /* RenderSystemGLES.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BE0131EC3F3000AD0F6 /* RenderSystemGLES.cpp */; };
F56C7BE6131EC455000AD0F6 /* LinuxRendererGLES.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BE4131EC455000AD0F6 /* LinuxRendererGLES.cpp */; };
F56C7BE9131EC46E000AD0F6 /* yuv2rgb.neon.S in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BE7131EC46E000AD0F6 /* yuv2rgb.neon.S */; };
- F56C7BEC131EC495000AD0F6 /* IOSAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BEA131EC495000AD0F6 /* IOSAudioRenderer.cpp */; };
F56C7BF5131EC4E8000AD0F6 /* fastmemcpy-arm.S in Sources */ = {isa = PBXBuildFile; fileRef = F56C7BF3131EC4E8000AD0F6 /* fastmemcpy-arm.S */; };
F56C7D83131EF8D9000AD0F6 /* NptZip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7CB2131EF8D8000AD0F6 /* NptZip.cpp */; };
F56C7D84131EF8D9000AD0F6 /* NptStreams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C7CB4131EF8D8000AD0F6 /* NptStreams.cpp */; };
@@ -1001,6 +1012,8 @@
7C0A7FC713A9E75400AFC2BD /* DirtyRegionTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirtyRegionTracker.h; sourceTree = "<group>"; };
7C0A7FCA13A9E76E00AFC2BD /* GUIWindowDebugInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowDebugInfo.cpp; sourceTree = "<group>"; };
7C0A7FCB13A9E76E00AFC2BD /* GUIWindowDebugInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowDebugInfo.h; sourceTree = "<group>"; };
+ 7C0B9908154B80200065A238 /* AEDeviceInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEDeviceInfo.cpp; sourceTree = "<group>"; };
+ 7C0B9909154B80200065A238 /* AEDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEDeviceInfo.h; sourceTree = "<group>"; };
7C1A89B9152671FB00C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A89BA152671FB00C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
7C1F6F8A13ED17CC001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
@@ -1015,7 +1028,6 @@
7CEE2E6C13D6B7A8000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; };
8316267613B670FF004AED87 /* README.ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.ios; sourceTree = "<group>"; };
8D576316048677EA00EA77CD /* XBMC.frappliance */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XBMC.frappliance; sourceTree = BUILT_PRODUCTS_DIR; };
- A192FD47135E46C800D92E9B /* IOSAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSAudioRingBuffer.h; path = AudioRenderers/IOSAudioRingBuffer.h; sourceTree = "<group>"; };
C807119D135DB842002F601B /* InputOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputOperations.cpp; sourceTree = "<group>"; };
C807119E135DB842002F601B /* InputOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputOperations.h; sourceTree = "<group>"; };
C893605E152C86EC00812418 /* PythonMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonMonitor.cpp; sourceTree = "<group>"; };
@@ -1114,6 +1126,55 @@
DFA6BE8913FED2B40048CC11 /* HttpParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpParser.h; sourceTree = "<group>"; };
DFAB04BF13F8385F00B70BFB /* InertialScrollingHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InertialScrollingHandler.cpp; sourceTree = "<group>"; };
DFAB04C013F8385F00B70BFB /* InertialScrollingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InertialScrollingHandler.h; sourceTree = "<group>"; };
+ DFB6628B15376810006B8FF1 /* AEAudioFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEAudioFormat.h; sourceTree = "<group>"; };
+ DFB6628C15376810006B8FF1 /* AEFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEFactory.cpp; sourceTree = "<group>"; };
+ DFB6628D15376810006B8FF1 /* AEFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFactory.h; sourceTree = "<group>"; };
+ DFB6629115376810006B8FF1 /* AEEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEEncoderFFmpeg.cpp; sourceTree = "<group>"; };
+ DFB6629215376810006B8FF1 /* AEEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoderFFmpeg.h; sourceTree = "<group>"; };
+ DFB6629415376810006B8FF1 /* CoreAudioAE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAE.cpp; sourceTree = "<group>"; };
+ DFB6629515376810006B8FF1 /* CoreAudioAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAE.h; sourceTree = "<group>"; };
+ DFB6629615376810006B8FF1 /* CoreAudioAEHAL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHAL.cpp; sourceTree = "<group>"; };
+ DFB6629715376810006B8FF1 /* CoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB6629815376810006B8FF1 /* CoreAudioAEHALIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALIOS.cpp; sourceTree = "<group>"; };
+ DFB6629915376810006B8FF1 /* CoreAudioAEHALIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALIOS.h; sourceTree = "<group>"; };
+ DFB6629A15376810006B8FF1 /* CoreAudioAEHALOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALOSX.cpp; sourceTree = "<group>"; };
+ DFB6629B15376810006B8FF1 /* CoreAudioAEHALOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALOSX.h; sourceTree = "<group>"; };
+ DFB6629C15376810006B8FF1 /* CoreAudioAESound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAESound.cpp; sourceTree = "<group>"; };
+ DFB6629D15376810006B8FF1 /* CoreAudioAESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAESound.h; sourceTree = "<group>"; };
+ DFB6629E15376810006B8FF1 /* CoreAudioAEStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEStream.cpp; sourceTree = "<group>"; };
+ DFB6629F15376810006B8FF1 /* CoreAudioAEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEStream.h; sourceTree = "<group>"; };
+ DFB662A015376810006B8FF1 /* CoreAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioRingBuffer.h; sourceTree = "<group>"; };
+ DFB662A115376810006B8FF1 /* ICoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB662A215376810006B8FF1 /* ICoreAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioSource.h; sourceTree = "<group>"; };
+ DFB662B015376810006B8FF1 /* AE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AE.h; sourceTree = "<group>"; };
+ DFB662B115376810006B8FF1 /* AEEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoder.h; sourceTree = "<group>"; };
+ DFB662B215376810006B8FF1 /* AEPostProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPostProc.h; sourceTree = "<group>"; };
+ DFB662B315376810006B8FF1 /* AESink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESink.h; sourceTree = "<group>"; };
+ DFB662B415376810006B8FF1 /* AESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESound.h; sourceTree = "<group>"; };
+ DFB662B515376810006B8FF1 /* AEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStream.h; sourceTree = "<group>"; };
+ DFB662B615376810006B8FF1 /* ThreadedAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadedAE.h; sourceTree = "<group>"; };
+ DFB662BA15376810006B8FF1 /* AEPPAnimationFade.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPPAnimationFade.cpp; sourceTree = "<group>"; };
+ DFB662BB15376810006B8FF1 /* AEPPAnimationFade.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPPAnimationFade.h; sourceTree = "<group>"; };
+ DFB662CA15376810006B8FF1 /* AEBitstreamPacker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBitstreamPacker.cpp; sourceTree = "<group>"; };
+ DFB662CB15376810006B8FF1 /* AEBitstreamPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBitstreamPacker.h; sourceTree = "<group>"; };
+ DFB662CC15376810006B8FF1 /* AEBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBuffer.cpp; sourceTree = "<group>"; };
+ DFB662CD15376810006B8FF1 /* AEBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBuffer.h; sourceTree = "<group>"; };
+ DFB662CE15376810006B8FF1 /* AEChannelInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEChannelInfo.cpp; sourceTree = "<group>"; };
+ DFB662CF15376810006B8FF1 /* AEChannelInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEChannelInfo.h; sourceTree = "<group>"; };
+ DFB662D015376810006B8FF1 /* AEConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEConvert.cpp; sourceTree = "<group>"; };
+ DFB662D115376810006B8FF1 /* AEConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEConvert.h; sourceTree = "<group>"; };
+ DFB662D215376810006B8FF1 /* AEPackIEC61937.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPackIEC61937.cpp; sourceTree = "<group>"; };
+ DFB662D315376810006B8FF1 /* AEPackIEC61937.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPackIEC61937.h; sourceTree = "<group>"; };
+ DFB662D415376810006B8FF1 /* AERemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AERemap.cpp; sourceTree = "<group>"; };
+ DFB662D515376810006B8FF1 /* AERemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AERemap.h; sourceTree = "<group>"; };
+ DFB662D615376810006B8FF1 /* AEStreamInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEStreamInfo.cpp; sourceTree = "<group>"; };
+ DFB662D715376810006B8FF1 /* AEStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStreamInfo.h; sourceTree = "<group>"; };
+ DFB662D815376810006B8FF1 /* AEUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEUtil.cpp; sourceTree = "<group>"; };
+ DFB662D915376810006B8FF1 /* AEUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEUtil.h; sourceTree = "<group>"; };
+ DFB662DA15376810006B8FF1 /* AEWAVLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEWAVLoader.cpp; sourceTree = "<group>"; };
+ DFB662DB15376810006B8FF1 /* AEWAVLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEWAVLoader.h; sourceTree = "<group>"; };
+ DFB6630F15376991006B8FF1 /* DVDAudioCodecPassthrough.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecPassthrough.cpp; sourceTree = "<group>"; };
+ DFB6631015376991006B8FF1 /* DVDAudioCodecPassthrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPassthrough.h; sourceTree = "<group>"; };
DFC539391526659D00D5FD5C /* AppIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AppIcon.png; path = media/AppIcon.png; sourceTree = "<group>"; };
DFCA6AFE15224684000BFAAE /* HTTPApiHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPApiHandler.cpp; sourceTree = "<group>"; };
DFCA6AFF15224684000BFAAE /* HTTPApiHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPApiHandler.h; sourceTree = "<group>"; };
@@ -1434,9 +1495,6 @@
F56C725F131EC151000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPassthroughFFmpeg.h; sourceTree = "<group>"; };
F56C7260131EC151000AD0F6 /* DVDAudioCodecPcm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecPcm.cpp; sourceTree = "<group>"; };
F56C7261131EC151000AD0F6 /* DVDAudioCodecPcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPcm.h; sourceTree = "<group>"; };
- F56C7263131EC151000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DVDAudioEncoderFFmpeg.cpp; path = Encoders/DVDAudioEncoderFFmpeg.cpp; sourceTree = "<group>"; };
- F56C7264131EC151000AD0F6 /* DVDAudioEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVDAudioEncoderFFmpeg.h; path = Encoders/DVDAudioEncoderFFmpeg.h; sourceTree = "<group>"; };
- F56C7265131EC151000AD0F6 /* IDVDAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDVDAudioEncoder.h; path = Encoders/IDVDAudioEncoder.h; sourceTree = "<group>"; };
F56C7267131EC151000AD0F6 /* mad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mad.h; sourceTree = "<group>"; };
F56C7268131EC151000AD0F6 /* DVDCodecs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDCodecs.h; sourceTree = "<group>"; };
F56C7269131EC151000AD0F6 /* DVDCodecUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDCodecUtils.cpp; sourceTree = "<group>"; };
@@ -1626,11 +1684,6 @@
F56C7330131EC151000AD0F6 /* WAVcodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAVcodec.h; sourceTree = "<group>"; };
F56C7333131EC151000AD0F6 /* YMCodec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YMCodec.cpp; sourceTree = "<group>"; };
F56C7334131EC151000AD0F6 /* YMCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YMCodec.h; sourceTree = "<group>"; };
- F56C7338131EC151000AD0F6 /* IAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAudioRenderer.h; path = AudioRenderers/IAudioRenderer.h; sourceTree = "<group>"; };
- F56C7339131EC151000AD0F6 /* AudioRendererFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioRendererFactory.h; path = AudioRenderers/AudioRendererFactory.h; sourceTree = "<group>"; };
- F56C733A131EC151000AD0F6 /* AudioRendererFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioRendererFactory.cpp; path = AudioRenderers/AudioRendererFactory.cpp; sourceTree = "<group>"; };
- F56C733B131EC151000AD0F6 /* NullDirectSound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NullDirectSound.cpp; path = AudioRenderers/NullDirectSound.cpp; sourceTree = "<group>"; };
- F56C733C131EC151000AD0F6 /* NullDirectSound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NullDirectSound.h; path = AudioRenderers/NullDirectSound.h; sourceTree = "<group>"; };
F56C733E131EC151000AD0F6 /* BaseRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseRenderer.cpp; sourceTree = "<group>"; };
F56C733F131EC151000AD0F6 /* BaseRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseRenderer.h; sourceTree = "<group>"; };
F56C7344131EC151000AD0F6 /* OverlayRendererGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OverlayRendererGL.cpp; sourceTree = "<group>"; };
@@ -1920,7 +1973,6 @@
F56C749D131EC152000AD0F6 /* ZipManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZipManager.cpp; sourceTree = "<group>"; };
F56C749E131EC152000AD0F6 /* ZipManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipManager.h; sourceTree = "<group>"; };
F56C74A0131EC152000AD0F6 /* AnimatedGif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimatedGif.h; sourceTree = "<group>"; };
- F56C74A1131EC152000AD0F6 /* AudioContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContext.h; sourceTree = "<group>"; };
F56C74A2131EC152000AD0F6 /* D3DResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = D3DResource.h; sourceTree = "<group>"; };
F56C74A3131EC152000AD0F6 /* DDSImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDSImage.h; sourceTree = "<group>"; };
F56C74A4131EC152000AD0F6 /* DirectXGraphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectXGraphics.h; sourceTree = "<group>"; };
@@ -1974,7 +2026,6 @@
F56C74D6131EC152000AD0F6 /* GUISettingsSliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISettingsSliderControl.h; sourceTree = "<group>"; };
F56C74D7131EC152000AD0F6 /* GUIShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIShader.h; sourceTree = "<group>"; };
F56C74D8131EC152000AD0F6 /* GUISliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISliderControl.h; sourceTree = "<group>"; };
- F56C74D9131EC152000AD0F6 /* GUISound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISound.h; sourceTree = "<group>"; };
F56C74DA131EC152000AD0F6 /* GUISpinControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControl.h; sourceTree = "<group>"; };
F56C74DB131EC152000AD0F6 /* GUISpinControlEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControlEx.h; sourceTree = "<group>"; };
F56C74DC131EC152000AD0F6 /* GUIStandardWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIStandardWindow.h; sourceTree = "<group>"; };
@@ -2012,7 +2063,6 @@
F56C74FC131EC152000AD0F6 /* XBTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTF.h; sourceTree = "<group>"; };
F56C74FD131EC152000AD0F6 /* XBTFReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTFReader.h; sourceTree = "<group>"; };
F56C74FE131EC152000AD0F6 /* AnimatedGif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnimatedGif.cpp; sourceTree = "<group>"; };
- F56C74FF131EC152000AD0F6 /* AudioContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioContext.cpp; sourceTree = "<group>"; };
F56C7500131EC152000AD0F6 /* D3DResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = D3DResource.cpp; sourceTree = "<group>"; };
F56C7501131EC152000AD0F6 /* DDSImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DDSImage.cpp; sourceTree = "<group>"; };
F56C7502131EC152000AD0F6 /* DirectXGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DirectXGraphics.cpp; sourceTree = "<group>"; };
@@ -2063,7 +2113,6 @@
F56C7530131EC152000AD0F6 /* GUISettingsSliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISettingsSliderControl.cpp; sourceTree = "<group>"; };
F56C7531131EC152000AD0F6 /* GUIShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIShader.cpp; sourceTree = "<group>"; };
F56C7532131EC152000AD0F6 /* GUISliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISliderControl.cpp; sourceTree = "<group>"; };
- F56C7533131EC152000AD0F6 /* GUISound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISound.cpp; sourceTree = "<group>"; };
F56C7534131EC152000AD0F6 /* GUISpinControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControl.cpp; sourceTree = "<group>"; };
F56C7535131EC152000AD0F6 /* GUISpinControlEx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControlEx.cpp; sourceTree = "<group>"; };
F56C7536131EC152000AD0F6 /* GUIStandardWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIStandardWindow.cpp; sourceTree = "<group>"; };
@@ -2533,10 +2582,6 @@
F56C774B131EC154000AD0F6 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
F56C774C131EC154000AD0F6 /* md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cpp; sourceTree = "<group>"; };
F56C774D131EC154000AD0F6 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
- F56C774E131EC154000AD0F6 /* PCMAmplifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMAmplifier.cpp; sourceTree = "<group>"; };
- F56C774F131EC154000AD0F6 /* PCMAmplifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMAmplifier.h; sourceTree = "<group>"; };
- F56C7750131EC154000AD0F6 /* PCMRemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMRemap.cpp; sourceTree = "<group>"; };
- F56C7751131EC154000AD0F6 /* PCMRemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMRemap.h; sourceTree = "<group>"; };
F56C7752131EC154000AD0F6 /* PerformanceSample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceSample.cpp; sourceTree = "<group>"; };
F56C7753131EC154000AD0F6 /* PerformanceSample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceSample.h; sourceTree = "<group>"; };
F56C7754131EC154000AD0F6 /* PerformanceStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceStats.cpp; sourceTree = "<group>"; };
@@ -2720,8 +2765,6 @@
F56C7BC8131EC2DB000AD0F6 /* XBMCDebugHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCDebugHelpers.h; sourceTree = "<group>"; };
F56C7BCD131EC301000AD0F6 /* XBMC.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = XBMC.png; sourceTree = "<group>"; };
F56C7BCE131EC301000AD0F6 /* XBMCATV2-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "XBMCATV2-Info.plist"; sourceTree = "<group>"; };
- F56C7BD2131EC332000AD0F6 /* IOSCoreAudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IOSCoreAudio.cpp; sourceTree = "<group>"; };
- F56C7BD3131EC332000AD0F6 /* IOSCoreAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOSCoreAudio.h; sourceTree = "<group>"; };
F56C7BD8131EC390000AD0F6 /* WinEventsIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WinEventsIOS.h; sourceTree = "<group>"; };
F56C7BD9131EC390000AD0F6 /* WinEventsIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WinEventsIOS.mm; sourceTree = "<group>"; };
F56C7BDA131EC390000AD0F6 /* WinSystemIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WinSystemIOS.h; sourceTree = "<group>"; };
@@ -2732,8 +2775,6 @@
F56C7BE5131EC455000AD0F6 /* LinuxRendererGLES.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinuxRendererGLES.h; sourceTree = "<group>"; };
F56C7BE7131EC46E000AD0F6 /* yuv2rgb.neon.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = yuv2rgb.neon.S; sourceTree = "<group>"; };
F56C7BE8131EC46E000AD0F6 /* yuv2rgb.neon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv2rgb.neon.h; sourceTree = "<group>"; };
- F56C7BEA131EC495000AD0F6 /* IOSAudioRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IOSAudioRenderer.cpp; path = AudioRenderers/IOSAudioRenderer.cpp; sourceTree = "<group>"; };
- F56C7BEB131EC495000AD0F6 /* IOSAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSAudioRenderer.h; path = AudioRenderers/IOSAudioRenderer.h; sourceTree = "<group>"; };
F56C7BF3131EC4E8000AD0F6 /* fastmemcpy-arm.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "fastmemcpy-arm.S"; sourceTree = "<group>"; };
F56C7BF4131EC4E8000AD0F6 /* fastmemcpy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fastmemcpy.h; sourceTree = "<group>"; };
F56C7CA6131EF8D8000AD0F6 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README.txt; path = Neptune/README.txt; sourceTree = "<group>"; };
@@ -3107,6 +3148,102 @@
path = websocket;
sourceTree = "<group>";
};
+ DFB6628A15376810006B8FF1 /* AudioEngine */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6629015376810006B8FF1 /* Encoders */,
+ DFB6629315376810006B8FF1 /* Engines */,
+ DFB662AF15376810006B8FF1 /* Interfaces */,
+ DFB662B915376810006B8FF1 /* PostProc */,
+ DFB662C915376810006B8FF1 /* Utils */,
+ DFB6628B15376810006B8FF1 /* AEAudioFormat.h */,
+ DFB6628C15376810006B8FF1 /* AEFactory.cpp */,
+ DFB6628D15376810006B8FF1 /* AEFactory.h */,
+ );
+ path = AudioEngine;
+ sourceTree = "<group>";
+ };
+ DFB6629015376810006B8FF1 /* Encoders */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6629115376810006B8FF1 /* AEEncoderFFmpeg.cpp */,
+ DFB6629215376810006B8FF1 /* AEEncoderFFmpeg.h */,
+ );
+ path = Encoders;
+ sourceTree = "<group>";
+ };
+ DFB6629315376810006B8FF1 /* Engines */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6629415376810006B8FF1 /* CoreAudioAE.cpp */,
+ DFB6629515376810006B8FF1 /* CoreAudioAE.h */,
+ DFB6629615376810006B8FF1 /* CoreAudioAEHAL.cpp */,
+ DFB6629715376810006B8FF1 /* CoreAudioAEHAL.h */,
+ DFB6629815376810006B8FF1 /* CoreAudioAEHALIOS.cpp */,
+ DFB6629915376810006B8FF1 /* CoreAudioAEHALIOS.h */,
+ DFB6629A15376810006B8FF1 /* CoreAudioAEHALOSX.cpp */,
+ DFB6629B15376810006B8FF1 /* CoreAudioAEHALOSX.h */,
+ DFB6629C15376810006B8FF1 /* CoreAudioAESound.cpp */,
+ DFB6629D15376810006B8FF1 /* CoreAudioAESound.h */,
+ DFB6629E15376810006B8FF1 /* CoreAudioAEStream.cpp */,
+ DFB6629F15376810006B8FF1 /* CoreAudioAEStream.h */,
+ DFB662A015376810006B8FF1 /* CoreAudioRingBuffer.h */,
+ DFB662A115376810006B8FF1 /* ICoreAudioAEHAL.h */,
+ DFB662A215376810006B8FF1 /* ICoreAudioSource.h */,
+ );
+ path = Engines;
+ sourceTree = "<group>";
+ };
+ DFB662AF15376810006B8FF1 /* Interfaces */ = {
+ isa = PBXGroup;
+ children = (
+ DFB662B015376810006B8FF1 /* AE.h */,
+ DFB662B115376810006B8FF1 /* AEEncoder.h */,
+ DFB662B215376810006B8FF1 /* AEPostProc.h */,
+ DFB662B315376810006B8FF1 /* AESink.h */,
+ DFB662B415376810006B8FF1 /* AESound.h */,
+ DFB662B515376810006B8FF1 /* AEStream.h */,
+ DFB662B615376810006B8FF1 /* ThreadedAE.h */,
+ );
+ path = Interfaces;
+ sourceTree = "<group>";
+ };
+ DFB662B915376810006B8FF1 /* PostProc */ = {
+ isa = PBXGroup;
+ children = (
+ DFB662BA15376810006B8FF1 /* AEPPAnimationFade.cpp */,
+ DFB662BB15376810006B8FF1 /* AEPPAnimationFade.h */,
+ );
+ path = PostProc;
+ sourceTree = "<group>";
+ };
+ DFB662C915376810006B8FF1 /* Utils */ = {
+ isa = PBXGroup;
+ children = (
+ DFB662CA15376810006B8FF1 /* AEBitstreamPacker.cpp */,
+ DFB662CB15376810006B8FF1 /* AEBitstreamPacker.h */,
+ DFB662CC15376810006B8FF1 /* AEBuffer.cpp */,
+ DFB662CD15376810006B8FF1 /* AEBuffer.h */,
+ DFB662CE15376810006B8FF1 /* AEChannelInfo.cpp */,
+ DFB662CF15376810006B8FF1 /* AEChannelInfo.h */,
+ DFB662D015376810006B8FF1 /* AEConvert.cpp */,
+ DFB662D115376810006B8FF1 /* AEConvert.h */,
+ 7C0B9908154B80200065A238 /* AEDeviceInfo.cpp */,
+ 7C0B9909154B80200065A238 /* AEDeviceInfo.h */,
+ DFB662D215376810006B8FF1 /* AEPackIEC61937.cpp */,
+ DFB662D315376810006B8FF1 /* AEPackIEC61937.h */,
+ DFB662D415376810006B8FF1 /* AERemap.cpp */,
+ DFB662D515376810006B8FF1 /* AERemap.h */,
+ DFB662D615376810006B8FF1 /* AEStreamInfo.cpp */,
+ DFB662D715376810006B8FF1 /* AEStreamInfo.h */,
+ DFB662D815376810006B8FF1 /* AEUtil.cpp */,
+ DFB662D915376810006B8FF1 /* AEUtil.h */,
+ DFB662DA15376810006B8FF1 /* AEWAVLoader.cpp */,
+ DFB662DB15376810006B8FF1 /* AEWAVLoader.h */,
+ );
+ path = Utils;
+ sourceTree = "<group>";
+ };
DFCA6AFD15224684000BFAAE /* httprequesthandler */ = {
isa = PBXGroup;
children = (
@@ -3569,7 +3706,7 @@
F56C721B131EC151000AD0F6 /* cores */ = {
isa = PBXGroup;
children = (
- F56C7335131EC151000AD0F6 /* AudioRenderers */,
+ DFB6628A15376810006B8FF1 /* AudioEngine */,
F56C7222131EC151000AD0F6 /* DllLoader */,
F56C724A131EC151000AD0F6 /* dvdplayer */,
F56C72FF131EC151000AD0F6 /* ExternalPlayer */,
@@ -3722,7 +3859,6 @@
F56C7255131EC151000AD0F6 /* Audio */ = {
isa = PBXGroup;
children = (
- F56C7262131EC151000AD0F6 /* Encoders */,
F56C7266131EC151000AD0F6 /* libmad */,
F56C7256131EC151000AD0F6 /* DllLibMad.h */,
F56C7257131EC151000AD0F6 /* DVDAudioCodec.h */,
@@ -3732,6 +3868,8 @@
F56C725B131EC151000AD0F6 /* DVDAudioCodecLibMad.h */,
F56C725C131EC151000AD0F6 /* DVDAudioCodecLPcm.cpp */,
F56C725D131EC151000AD0F6 /* DVDAudioCodecLPcm.h */,
+ DFB6630F15376991006B8FF1 /* DVDAudioCodecPassthrough.cpp */,
+ DFB6631015376991006B8FF1 /* DVDAudioCodecPassthrough.h */,
F56C725E131EC151000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.cpp */,
F56C725F131EC151000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.h */,
F56C7260131EC151000AD0F6 /* DVDAudioCodecPcm.cpp */,
@@ -3740,16 +3878,6 @@
path = Audio;
sourceTree = "<group>";
};
- F56C7262131EC151000AD0F6 /* Encoders */ = {
- isa = PBXGroup;
- children = (
- F56C7263131EC151000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */,
- F56C7264131EC151000AD0F6 /* DVDAudioEncoderFFmpeg.h */,
- F56C7265131EC151000AD0F6 /* IDVDAudioEncoder.h */,
- );
- name = Encoders;
- sourceTree = "<group>";
- };
F56C7266131EC151000AD0F6 /* libmad */ = {
isa = PBXGroup;
children = (
@@ -3989,21 +4117,6 @@
path = paplayer;
sourceTree = "<group>";
};
- F56C7335131EC151000AD0F6 /* AudioRenderers */ = {
- isa = PBXGroup;
- children = (
- F56C733A131EC151000AD0F6 /* AudioRendererFactory.cpp */,
- F56C7339131EC151000AD0F6 /* AudioRendererFactory.h */,
- F56C7338131EC151000AD0F6 /* IAudioRenderer.h */,
- F56C7BEA131EC495000AD0F6 /* IOSAudioRenderer.cpp */,
- F56C7BEB131EC495000AD0F6 /* IOSAudioRenderer.h */,
- A192FD47135E46C800D92E9B /* IOSAudioRingBuffer.h */,
- F56C733B131EC151000AD0F6 /* NullDirectSound.cpp */,
- F56C733C131EC151000AD0F6 /* NullDirectSound.h */,
- );
- name = AudioRenderers;
- sourceTree = "<group>";
- };
F56C733D131EC151000AD0F6 /* VideoRenderers */ = {
isa = PBXGroup;
children = (
@@ -4431,8 +4544,6 @@
children = (
F56C74FE131EC152000AD0F6 /* AnimatedGif.cpp */,
F56C74A0131EC152000AD0F6 /* AnimatedGif.h */,
- F56C74FF131EC152000AD0F6 /* AudioContext.cpp */,
- F56C74A1131EC152000AD0F6 /* AudioContext.h */,
F56C7500131EC152000AD0F6 /* D3DResource.cpp */,
F56C74A2131EC152000AD0F6 /* D3DResource.h */,
F56C7501131EC152000AD0F6 /* DDSImage.cpp */,
@@ -4543,8 +4654,6 @@
F56C74D7131EC152000AD0F6 /* GUIShader.h */,
F56C7532131EC152000AD0F6 /* GUISliderControl.cpp */,
F56C74D8131EC152000AD0F6 /* GUISliderControl.h */,
- F56C7533131EC152000AD0F6 /* GUISound.cpp */,
- F56C74D9131EC152000AD0F6 /* GUISound.h */,
F56C7534131EC152000AD0F6 /* GUISpinControl.cpp */,
F56C74DA131EC152000AD0F6 /* GUISpinControl.h */,
F56C7535131EC152000AD0F6 /* GUISpinControlEx.cpp */,
@@ -5026,8 +5135,6 @@
F5B13DFF13344F2A0045076D /* DarwinUtils.h */,
F5B13E0013344F310045076D /* DarwinUtils.mm */,
F56C7682131EC153000AD0F6 /* eprintf.cpp */,
- F56C7BD2131EC332000AD0F6 /* IOSCoreAudio.cpp */,
- F56C7BD3131EC332000AD0F6 /* IOSCoreAudio.h */,
DFFD594B1506B6300088DE4B /* IOSEAGLView.h */,
DFFD594C1506B6300088DE4B /* IOSEAGLView.mm */,
DFFEFC2015160927001294DC /* IOSExternalTouchController.h */,
@@ -5313,10 +5420,6 @@
F56C774D131EC154000AD0F6 /* md5.h */,
188F76271522186C009870CE /* Mime.cpp */,
188F76281522186C009870CE /* Mime.h */,
- F56C774E131EC154000AD0F6 /* PCMAmplifier.cpp */,
- F56C774F131EC154000AD0F6 /* PCMAmplifier.h */,
- F56C7750131EC154000AD0F6 /* PCMRemap.cpp */,
- F56C7751131EC154000AD0F6 /* PCMRemap.h */,
F56C7752131EC154000AD0F6 /* PerformanceSample.cpp */,
F56C7753131EC154000AD0F6 /* PerformanceSample.h */,
F56C7754131EC154000AD0F6 /* PerformanceStats.cpp */,
@@ -6247,7 +6350,6 @@
F56C78E7131EC154000AD0F6 /* DVDAudioCodecLPcm.cpp in Sources */,
F56C78E8131EC154000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
F56C78E9131EC154000AD0F6 /* DVDAudioCodecPcm.cpp in Sources */,
- F56C78EA131EC154000AD0F6 /* DVDAudioEncoderFFmpeg.cpp in Sources */,
F56C78EB131EC154000AD0F6 /* DVDCodecUtils.cpp in Sources */,
F56C78EC131EC154000AD0F6 /* DVDFactoryCodec.cpp in Sources */,
F56C78ED131EC154000AD0F6 /* DVDOverlayCodecSSA.cpp in Sources */,
@@ -6328,8 +6430,6 @@
F56C793C131EC154000AD0F6 /* TimidityCodec.cpp in Sources */,
F56C793D131EC154000AD0F6 /* WAVcodec.cpp in Sources */,
F56C793F131EC154000AD0F6 /* YMCodec.cpp in Sources */,
- F56C7941131EC154000AD0F6 /* AudioRendererFactory.cpp in Sources */,
- F56C7942131EC154000AD0F6 /* NullDirectSound.cpp in Sources */,
F56C7943131EC154000AD0F6 /* BaseRenderer.cpp in Sources */,
F56C7946131EC154000AD0F6 /* OverlayRendererGL.cpp in Sources */,
F56C7947131EC154000AD0F6 /* OverlayRenderer.cpp in Sources */,
@@ -6475,7 +6575,6 @@
F56C79EE131EC154000AD0F6 /* ZipDirectory.cpp in Sources */,
F56C79EF131EC154000AD0F6 /* ZipManager.cpp in Sources */,
F56C79F0131EC154000AD0F6 /* AnimatedGif.cpp in Sources */,
- F56C79F1131EC154000AD0F6 /* AudioContext.cpp in Sources */,
F56C79F2131EC154000AD0F6 /* D3DResource.cpp in Sources */,
F56C79F3131EC154000AD0F6 /* DDSImage.cpp in Sources */,
F56C79F4131EC154000AD0F6 /* DirectXGraphics.cpp in Sources */,
@@ -6526,7 +6625,6 @@
F56C7A22131EC154000AD0F6 /* GUISettingsSliderControl.cpp in Sources */,
F56C7A23131EC154000AD0F6 /* GUIShader.cpp in Sources */,
F56C7A24131EC154000AD0F6 /* GUISliderControl.cpp in Sources */,
- F56C7A25131EC154000AD0F6 /* GUISound.cpp in Sources */,
F56C7A26131EC154000AD0F6 /* GUISpinControl.cpp in Sources */,
F56C7A27131EC154000AD0F6 /* GUISpinControlEx.cpp in Sources */,
F56C7A28131EC154000AD0F6 /* GUIStandardWindow.cpp in Sources */,
@@ -6774,8 +6872,6 @@
F56C7B2F131EC155000AD0F6 /* LCD.cpp in Sources */,
F56C7B30131EC155000AD0F6 /* log.cpp in Sources */,
F56C7B31131EC155000AD0F6 /* md5.cpp in Sources */,
- F56C7B32131EC155000AD0F6 /* PCMAmplifier.cpp in Sources */,
- F56C7B33131EC155000AD0F6 /* PCMRemap.cpp in Sources */,
F56C7B34131EC155000AD0F6 /* PerformanceSample.cpp in Sources */,
F56C7B35131EC155000AD0F6 /* PerformanceStats.cpp in Sources */,
F56C7B37131EC155000AD0F6 /* RegExp.cpp in Sources */,
@@ -6862,13 +6958,11 @@
F56C7B9B131EC1B4000AD0F6 /* AutoPool.mm in Sources */,
F56C7BC9131EC2DB000AD0F6 /* XBMCAppliance.m in Sources */,
F56C7BCA131EC2DB000AD0F6 /* XBMCController.mm in Sources */,
- F56C7BD6131EC332000AD0F6 /* IOSCoreAudio.cpp in Sources */,
F56C7BDC131EC390000AD0F6 /* WinEventsIOS.mm in Sources */,
F56C7BDD131EC390000AD0F6 /* WinSystemIOS.mm in Sources */,
F56C7BE3131EC3F3000AD0F6 /* RenderSystemGLES.cpp in Sources */,
F56C7BE6131EC455000AD0F6 /* LinuxRendererGLES.cpp in Sources */,
F56C7BE9131EC46E000AD0F6 /* yuv2rgb.neon.S in Sources */,
- F56C7BEC131EC495000AD0F6 /* IOSAudioRenderer.cpp in Sources */,
F56C7BF5131EC4E8000AD0F6 /* fastmemcpy-arm.S in Sources */,
F56C7D83131EF8D9000AD0F6 /* NptZip.cpp in Sources */,
F56C7D84131EF8D9000AD0F6 /* NptStreams.cpp in Sources */,
@@ -7078,6 +7172,26 @@
F5ED8D931551FAEA00842059 /* BlurayDirectory.cpp in Sources */,
F5ED90C91553994800842059 /* XBMCTinyXML.cpp in Sources */,
F5ED90CC1553995B00842059 /* POUtils.cpp in Sources */,
+ DFB662DC15376810006B8FF1 /* AEFactory.cpp in Sources */,
+ DFB662DE15376810006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */,
+ DFB662DF15376810006B8FF1 /* CoreAudioAE.cpp in Sources */,
+ DFB662E015376810006B8FF1 /* CoreAudioAEHAL.cpp in Sources */,
+ DFB662E115376810006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */,
+ DFB662E215376810006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */,
+ DFB662E315376810006B8FF1 /* CoreAudioAESound.cpp in Sources */,
+ DFB662E415376810006B8FF1 /* CoreAudioAEStream.cpp in Sources */,
+ DFB662ED15376810006B8FF1 /* AEPPAnimationFade.cpp in Sources */,
+ DFB662F415376810006B8FF1 /* AEBitstreamPacker.cpp in Sources */,
+ DFB662F515376810006B8FF1 /* AEBuffer.cpp in Sources */,
+ DFB662F615376810006B8FF1 /* AEChannelInfo.cpp in Sources */,
+ DFB662F715376810006B8FF1 /* AEConvert.cpp in Sources */,
+ DFB662F815376810006B8FF1 /* AEPackIEC61937.cpp in Sources */,
+ DFB662F915376810006B8FF1 /* AERemap.cpp in Sources */,
+ DFB662FA15376810006B8FF1 /* AEStreamInfo.cpp in Sources */,
+ DFB662FB15376810006B8FF1 /* AEUtil.cpp in Sources */,
+ DFB662FC15376810006B8FF1 /* AEWAVLoader.cpp in Sources */,
+ DFB6631115376991006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */,
+ 7C0B990A154B80200065A238 /* AEDeviceInfo.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7135,6 +7249,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
@@ -7249,6 +7365,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
diff --git a/XBMC-IOS.xcodeproj/project.pbxproj b/XBMC-IOS.xcodeproj/project.pbxproj
index e3c0991115..9815942f67 100644
--- a/XBMC-IOS.xcodeproj/project.pbxproj
+++ b/XBMC-IOS.xcodeproj/project.pbxproj
@@ -24,6 +24,7 @@
7C0A7F9D13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7F9B13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp */; };
7C0A7FB213A9E72E00AFC2BD /* DirtyRegionSolvers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FAE13A9E72E00AFC2BD /* DirtyRegionSolvers.cpp */; };
7C0A7FB313A9E72E00AFC2BD /* DirtyRegionTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FB013A9E72E00AFC2BD /* DirtyRegionTracker.cpp */; };
+ 7C0B98F9154B7FF30065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B98F7154B7FF30065A238 /* AEDeviceInfo.cpp */; };
7C1A89CE1526722200C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A89CC1526722200C63311 /* TextureCacheJob.cpp */; };
7C1F6F7A13ED178F001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6F7813ED178F001726AB /* LibraryDirectory.cpp */; };
7C89628013B7031E003631FE /* GUIWindowScreensaverDim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C89627E13B7031E003631FE /* GUIWindowScreensaverDim.cpp */; };
@@ -79,6 +80,25 @@
DFA6BE4313FECA010048CC11 /* AirPlayServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA6BE4113FECA010048CC11 /* AirPlayServer.cpp */; };
DFA6BE7713FED09C0048CC11 /* HttpParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA6BE7513FED09C0048CC11 /* HttpParser.cpp */; };
DFAB04B013F8383300B70BFB /* InertialScrollingHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAB04AE13F8383300B70BFB /* InertialScrollingHandler.cpp */; };
+ DFB6625615376791006B8FF1 /* AEFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6620615376791006B8FF1 /* AEFactory.cpp */; };
+ DFB6625815376791006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6620B15376791006B8FF1 /* AEEncoderFFmpeg.cpp */; };
+ DFB6625915376791006B8FF1 /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6620E15376791006B8FF1 /* CoreAudioAE.cpp */; };
+ DFB6625A15376791006B8FF1 /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6621015376791006B8FF1 /* CoreAudioAEHAL.cpp */; };
+ DFB6625B15376791006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6621215376791006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
+ DFB6625C15376791006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6621415376791006B8FF1 /* CoreAudioAEHALOSX.cpp */; };
+ DFB6625D15376791006B8FF1 /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6621615376791006B8FF1 /* CoreAudioAESound.cpp */; };
+ DFB6625E15376791006B8FF1 /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6621815376791006B8FF1 /* CoreAudioAEStream.cpp */; };
+ DFB6626715376791006B8FF1 /* AEPPAnimationFade.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6623415376791006B8FF1 /* AEPPAnimationFade.cpp */; };
+ DFB6626E15376791006B8FF1 /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624415376791006B8FF1 /* AEBitstreamPacker.cpp */; };
+ DFB6626F15376791006B8FF1 /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624615376791006B8FF1 /* AEBuffer.cpp */; };
+ DFB6627015376791006B8FF1 /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624815376791006B8FF1 /* AEChannelInfo.cpp */; };
+ DFB6627115376791006B8FF1 /* AEConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624A15376791006B8FF1 /* AEConvert.cpp */; };
+ DFB6627215376791006B8FF1 /* AEPackIEC61937.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624C15376791006B8FF1 /* AEPackIEC61937.cpp */; };
+ DFB6627315376791006B8FF1 /* AERemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6624E15376791006B8FF1 /* AERemap.cpp */; };
+ DFB6627415376791006B8FF1 /* AEStreamInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6625015376791006B8FF1 /* AEStreamInfo.cpp */; };
+ DFB6627515376791006B8FF1 /* AEUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6625215376791006B8FF1 /* AEUtil.cpp */; };
+ DFB6627615376791006B8FF1 /* AEWAVLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6625415376791006B8FF1 /* AEWAVLoader.cpp */; };
+ DFB6630C1537697E006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6630A1537697E006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
DFCA6AEB15224671000BFAAE /* HTTPApiHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6ADE15224671000BFAAE /* HTTPApiHandler.cpp */; };
DFCA6AEC15224671000BFAAE /* HTTPJsonRpcHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6AE015224671000BFAAE /* HTTPJsonRpcHandler.cpp */; };
DFCA6AED15224671000BFAAE /* HTTPVfsHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6AE215224671000BFAAE /* HTTPVfsHandler.cpp */; };
@@ -261,9 +281,6 @@
F56C88B4131F42ED000AD0F6 /* EncoderLame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C81EF131F42E6000AD0F6 /* EncoderLame.cpp */; };
F56C88B5131F42ED000AD0F6 /* EncoderVorbis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C81F1131F42E6000AD0F6 /* EncoderVorbis.cpp */; };
F56C88B6131F42ED000AD0F6 /* EncoderWav.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C81F3131F42E6000AD0F6 /* EncoderWav.cpp */; };
- F56C88B7131F42ED000AD0F6 /* IOSAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C81FF131F42E6000AD0F6 /* IOSAudioRenderer.cpp */; };
- F56C88B8131F42ED000AD0F6 /* AudioRendererFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8203131F42E6000AD0F6 /* AudioRendererFactory.cpp */; };
- F56C88B9131F42ED000AD0F6 /* NullDirectSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8204131F42E6000AD0F6 /* NullDirectSound.cpp */; };
F56C88BA131F42ED000AD0F6 /* coff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8207131F42E6000AD0F6 /* coff.cpp */; };
F56C88BB131F42ED000AD0F6 /* dll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C820A131F42E6000AD0F6 /* dll.cpp */; };
F56C88BC131F42ED000AD0F6 /* dll_tracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C820C131F42E6000AD0F6 /* dll_tracker.cpp */; };
@@ -286,7 +303,6 @@
F56C88CE131F42ED000AD0F6 /* DVDTSCorrection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8231131F42E6000AD0F6 /* DVDTSCorrection.cpp */; };
F56C88CF131F42ED000AD0F6 /* DVDAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8234131F42E6000AD0F6 /* DVDAudio.cpp */; };
F56C88D0131F42ED000AD0F6 /* DVDClock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8236131F42E6000AD0F6 /* DVDClock.cpp */; };
- F56C88D1131F42ED000AD0F6 /* DVDAudioEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C823B131F42E6000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */; };
F56C88D2131F42ED000AD0F6 /* DVDAudioCodecFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8240131F42E6000AD0F6 /* DVDAudioCodecFFmpeg.cpp */; };
F56C88D3131F42ED000AD0F6 /* DVDAudioCodecLibMad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8242131F42E6000AD0F6 /* DVDAudioCodecLibMad.cpp */; };
F56C88D4131F42ED000AD0F6 /* DVDAudioCodecLPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8244131F42E6000AD0F6 /* DVDAudioCodecLPcm.cpp */; };
@@ -522,7 +538,6 @@
F56C89D8131F42ED000AD0F6 /* ZipDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C847E131F42E9000AD0F6 /* ZipDirectory.cpp */; };
F56C89D9131F42ED000AD0F6 /* ZipManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8480131F42E9000AD0F6 /* ZipManager.cpp */; };
F56C89DA131F42ED000AD0F6 /* AnimatedGif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C84E1131F42E9000AD0F6 /* AnimatedGif.cpp */; };
- F56C89DB131F42ED000AD0F6 /* AudioContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C84E2131F42E9000AD0F6 /* AudioContext.cpp */; };
F56C89DC131F42ED000AD0F6 /* D3DResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C84E3131F42E9000AD0F6 /* D3DResource.cpp */; };
F56C89DD131F42ED000AD0F6 /* DDSImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C84E4131F42E9000AD0F6 /* DDSImage.cpp */; };
F56C89DE131F42ED000AD0F6 /* DirectXGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C84E5131F42E9000AD0F6 /* DirectXGraphics.cpp */; };
@@ -573,7 +588,6 @@
F56C8A0C131F42ED000AD0F6 /* GUISettingsSliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8513131F42E9000AD0F6 /* GUISettingsSliderControl.cpp */; };
F56C8A0D131F42ED000AD0F6 /* GUIShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8514131F42E9000AD0F6 /* GUIShader.cpp */; };
F56C8A0E131F42ED000AD0F6 /* GUISliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8515131F42E9000AD0F6 /* GUISliderControl.cpp */; };
- F56C8A0F131F42ED000AD0F6 /* GUISound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8516131F42E9000AD0F6 /* GUISound.cpp */; };
F56C8A10131F42ED000AD0F6 /* GUISpinControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8517131F42E9000AD0F6 /* GUISpinControl.cpp */; };
F56C8A11131F42ED000AD0F6 /* GUISpinControlEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8518131F42E9000AD0F6 /* GUISpinControlEx.cpp */; };
F56C8A12131F42ED000AD0F6 /* GUIStandardWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8519131F42E9000AD0F6 /* GUIStandardWindow.cpp */; };
@@ -740,7 +754,6 @@
F56C8AB9131F42ED000AD0F6 /* ZeroconfBrowser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8654131F42EB000AD0F6 /* ZeroconfBrowser.cpp */; };
F56C8ABA131F42ED000AD0F6 /* cddb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8656131F42EB000AD0F6 /* cddb.cpp */; };
F56C8ABB131F42ED000AD0F6 /* AutoPool.mm in Sources */ = {isa = PBXBuildFile; fileRef = F56C865A131F42EB000AD0F6 /* AutoPool.mm */; };
- F56C8AC2131F42ED000AD0F6 /* IOSCoreAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8669131F42EB000AD0F6 /* IOSCoreAudio.cpp */; };
F56C8AC3131F42ED000AD0F6 /* OSXGNUReplacements.c in Sources */ = {isa = PBXBuildFile; fileRef = F56C866B131F42EB000AD0F6 /* OSXGNUReplacements.c */; };
F56C8AC5131F42ED000AD0F6 /* eprintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C866F131F42EB000AD0F6 /* eprintf.cpp */; };
F56C8AC6131F42ED000AD0F6 /* GUIDialogPictureInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8673131F42EB000AD0F6 /* GUIDialogPictureInfo.cpp */; };
@@ -825,8 +838,6 @@
F56C8B1E131F42ED000AD0F6 /* LCD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8737131F42EC000AD0F6 /* LCD.cpp */; };
F56C8B1F131F42ED000AD0F6 /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8739131F42EC000AD0F6 /* log.cpp */; };
F56C8B20131F42ED000AD0F6 /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C873B131F42EC000AD0F6 /* md5.cpp */; };
- F56C8B21131F42ED000AD0F6 /* PCMAmplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C873D131F42EC000AD0F6 /* PCMAmplifier.cpp */; };
- F56C8B22131F42ED000AD0F6 /* PCMRemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C873F131F42EC000AD0F6 /* PCMRemap.cpp */; };
F56C8B23131F42ED000AD0F6 /* PerformanceSample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8741131F42EC000AD0F6 /* PerformanceSample.cpp */; };
F56C8B24131F42ED000AD0F6 /* PerformanceStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8743131F42EC000AD0F6 /* PerformanceStats.cpp */; };
F56C8B26131F42ED000AD0F6 /* RegExp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F56C8747131F42EC000AD0F6 /* RegExp.cpp */; };
@@ -1001,6 +1012,8 @@
7C0A7FAF13A9E72E00AFC2BD /* DirtyRegionSolvers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirtyRegionSolvers.h; sourceTree = "<group>"; };
7C0A7FB013A9E72E00AFC2BD /* DirtyRegionTracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DirtyRegionTracker.cpp; sourceTree = "<group>"; };
7C0A7FB113A9E72E00AFC2BD /* DirtyRegionTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirtyRegionTracker.h; sourceTree = "<group>"; };
+ 7C0B98F7154B7FF30065A238 /* AEDeviceInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEDeviceInfo.cpp; sourceTree = "<group>"; };
+ 7C0B98F8154B7FF30065A238 /* AEDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEDeviceInfo.h; sourceTree = "<group>"; };
7C1A89CC1526722200C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A89CD1526722200C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
7C1F6F7813ED178F001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
@@ -1113,6 +1126,55 @@
DFA6BE7613FED09C0048CC11 /* HttpParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpParser.h; sourceTree = "<group>"; };
DFAB04AE13F8383300B70BFB /* InertialScrollingHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InertialScrollingHandler.cpp; sourceTree = "<group>"; };
DFAB04AF13F8383300B70BFB /* InertialScrollingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InertialScrollingHandler.h; sourceTree = "<group>"; };
+ DFB6620515376791006B8FF1 /* AEAudioFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEAudioFormat.h; sourceTree = "<group>"; };
+ DFB6620615376791006B8FF1 /* AEFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEFactory.cpp; sourceTree = "<group>"; };
+ DFB6620715376791006B8FF1 /* AEFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFactory.h; sourceTree = "<group>"; };
+ DFB6620B15376791006B8FF1 /* AEEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEEncoderFFmpeg.cpp; sourceTree = "<group>"; };
+ DFB6620C15376791006B8FF1 /* AEEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoderFFmpeg.h; sourceTree = "<group>"; };
+ DFB6620E15376791006B8FF1 /* CoreAudioAE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAE.cpp; sourceTree = "<group>"; };
+ DFB6620F15376791006B8FF1 /* CoreAudioAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAE.h; sourceTree = "<group>"; };
+ DFB6621015376791006B8FF1 /* CoreAudioAEHAL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHAL.cpp; sourceTree = "<group>"; };
+ DFB6621115376791006B8FF1 /* CoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB6621215376791006B8FF1 /* CoreAudioAEHALIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALIOS.cpp; sourceTree = "<group>"; };
+ DFB6621315376791006B8FF1 /* CoreAudioAEHALIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALIOS.h; sourceTree = "<group>"; };
+ DFB6621415376791006B8FF1 /* CoreAudioAEHALOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALOSX.cpp; sourceTree = "<group>"; };
+ DFB6621515376791006B8FF1 /* CoreAudioAEHALOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALOSX.h; sourceTree = "<group>"; };
+ DFB6621615376791006B8FF1 /* CoreAudioAESound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAESound.cpp; sourceTree = "<group>"; };
+ DFB6621715376791006B8FF1 /* CoreAudioAESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAESound.h; sourceTree = "<group>"; };
+ DFB6621815376791006B8FF1 /* CoreAudioAEStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEStream.cpp; sourceTree = "<group>"; };
+ DFB6621915376791006B8FF1 /* CoreAudioAEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEStream.h; sourceTree = "<group>"; };
+ DFB6621A15376791006B8FF1 /* CoreAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioRingBuffer.h; sourceTree = "<group>"; };
+ DFB6621B15376791006B8FF1 /* ICoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB6621C15376791006B8FF1 /* ICoreAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioSource.h; sourceTree = "<group>"; };
+ DFB6622A15376791006B8FF1 /* AE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AE.h; sourceTree = "<group>"; };
+ DFB6622B15376791006B8FF1 /* AEEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoder.h; sourceTree = "<group>"; };
+ DFB6622C15376791006B8FF1 /* AEPostProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPostProc.h; sourceTree = "<group>"; };
+ DFB6622D15376791006B8FF1 /* AESink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESink.h; sourceTree = "<group>"; };
+ DFB6622E15376791006B8FF1 /* AESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESound.h; sourceTree = "<group>"; };
+ DFB6622F15376791006B8FF1 /* AEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStream.h; sourceTree = "<group>"; };
+ DFB6623015376791006B8FF1 /* ThreadedAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadedAE.h; sourceTree = "<group>"; };
+ DFB6623415376791006B8FF1 /* AEPPAnimationFade.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPPAnimationFade.cpp; sourceTree = "<group>"; };
+ DFB6623515376791006B8FF1 /* AEPPAnimationFade.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPPAnimationFade.h; sourceTree = "<group>"; };
+ DFB6624415376791006B8FF1 /* AEBitstreamPacker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBitstreamPacker.cpp; sourceTree = "<group>"; };
+ DFB6624515376791006B8FF1 /* AEBitstreamPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBitstreamPacker.h; sourceTree = "<group>"; };
+ DFB6624615376791006B8FF1 /* AEBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBuffer.cpp; sourceTree = "<group>"; };
+ DFB6624715376791006B8FF1 /* AEBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBuffer.h; sourceTree = "<group>"; };
+ DFB6624815376791006B8FF1 /* AEChannelInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEChannelInfo.cpp; sourceTree = "<group>"; };
+ DFB6624915376791006B8FF1 /* AEChannelInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEChannelInfo.h; sourceTree = "<group>"; };
+ DFB6624A15376791006B8FF1 /* AEConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEConvert.cpp; sourceTree = "<group>"; };
+ DFB6624B15376791006B8FF1 /* AEConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEConvert.h; sourceTree = "<group>"; };
+ DFB6624C15376791006B8FF1 /* AEPackIEC61937.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPackIEC61937.cpp; sourceTree = "<group>"; };
+ DFB6624D15376791006B8FF1 /* AEPackIEC61937.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPackIEC61937.h; sourceTree = "<group>"; };
+ DFB6624E15376791006B8FF1 /* AERemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AERemap.cpp; sourceTree = "<group>"; };
+ DFB6624F15376791006B8FF1 /* AERemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AERemap.h; sourceTree = "<group>"; };
+ DFB6625015376791006B8FF1 /* AEStreamInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEStreamInfo.cpp; sourceTree = "<group>"; };
+ DFB6625115376791006B8FF1 /* AEStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStreamInfo.h; sourceTree = "<group>"; };
+ DFB6625215376791006B8FF1 /* AEUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEUtil.cpp; sourceTree = "<group>"; };
+ DFB6625315376791006B8FF1 /* AEUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEUtil.h; sourceTree = "<group>"; };
+ DFB6625415376791006B8FF1 /* AEWAVLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEWAVLoader.cpp; sourceTree = "<group>"; };
+ DFB6625515376791006B8FF1 /* AEWAVLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEWAVLoader.h; sourceTree = "<group>"; };
+ DFB6630A1537697E006B8FF1 /* DVDAudioCodecPassthrough.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecPassthrough.cpp; sourceTree = "<group>"; };
+ DFB6630B1537697E006B8FF1 /* DVDAudioCodecPassthrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPassthrough.h; sourceTree = "<group>"; };
DFCA6ADE15224671000BFAAE /* HTTPApiHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPApiHandler.cpp; sourceTree = "<group>"; };
DFCA6ADF15224671000BFAAE /* HTTPApiHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPApiHandler.h; sourceTree = "<group>"; };
DFCA6AE015224671000BFAAE /* HTTPJsonRpcHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPJsonRpcHandler.cpp; sourceTree = "<group>"; };
@@ -1561,13 +1623,7 @@
F56C81F2131F42E6000AD0F6 /* EncoderVorbis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EncoderVorbis.h; sourceTree = "<group>"; };
F56C81F3131F42E6000AD0F6 /* EncoderWav.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EncoderWav.cpp; sourceTree = "<group>"; };
F56C81F4131F42E6000AD0F6 /* EncoderWav.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EncoderWav.h; sourceTree = "<group>"; };
- F56C81FF131F42E6000AD0F6 /* IOSAudioRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IOSAudioRenderer.cpp; path = AudioRenderers/IOSAudioRenderer.cpp; sourceTree = "<group>"; };
- F56C8200131F42E6000AD0F6 /* IOSAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSAudioRenderer.h; path = AudioRenderers/IOSAudioRenderer.h; sourceTree = "<group>"; };
- F56C8201131F42E6000AD0F6 /* IAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAudioRenderer.h; path = AudioRenderers/IAudioRenderer.h; sourceTree = "<group>"; };
- F56C8202131F42E6000AD0F6 /* AudioRendererFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioRendererFactory.h; path = AudioRenderers/AudioRendererFactory.h; sourceTree = "<group>"; };
- F56C8203131F42E6000AD0F6 /* AudioRendererFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioRendererFactory.cpp; path = AudioRenderers/AudioRendererFactory.cpp; sourceTree = "<group>"; };
- F56C8204131F42E6000AD0F6 /* NullDirectSound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NullDirectSound.cpp; path = AudioRenderers/NullDirectSound.cpp; sourceTree = "<group>"; };
- F56C8205131F42E6000AD0F6 /* NullDirectSound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NullDirectSound.h; path = AudioRenderers/NullDirectSound.h; sourceTree = "<group>"; };
+ F56C81F6131F42E6000AD0F6 /* lame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lame.h; sourceTree = "<group>"; };
F56C8207131F42E6000AD0F6 /* coff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coff.cpp; sourceTree = "<group>"; };
F56C8208131F42E6000AD0F6 /* coff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coff.h; sourceTree = "<group>"; };
F56C8209131F42E6000AD0F6 /* coffldr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coffldr.h; sourceTree = "<group>"; };
@@ -1613,9 +1669,6 @@
F56C8235131F42E6000AD0F6 /* DVDAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudio.h; sourceTree = "<group>"; };
F56C8236131F42E6000AD0F6 /* DVDClock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDClock.cpp; sourceTree = "<group>"; };
F56C8237131F42E6000AD0F6 /* DVDClock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDClock.h; sourceTree = "<group>"; };
- F56C823B131F42E6000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DVDAudioEncoderFFmpeg.cpp; path = Encoders/DVDAudioEncoderFFmpeg.cpp; sourceTree = "<group>"; };
- F56C823C131F42E6000AD0F6 /* DVDAudioEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVDAudioEncoderFFmpeg.h; path = Encoders/DVDAudioEncoderFFmpeg.h; sourceTree = "<group>"; };
- F56C823D131F42E6000AD0F6 /* IDVDAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDVDAudioEncoder.h; path = Encoders/IDVDAudioEncoder.h; sourceTree = "<group>"; };
F56C823E131F42E6000AD0F6 /* DllLibMad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DllLibMad.h; sourceTree = "<group>"; };
F56C823F131F42E6000AD0F6 /* DVDAudioCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodec.h; sourceTree = "<group>"; };
F56C8240131F42E6000AD0F6 /* DVDAudioCodecFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecFFmpeg.cpp; sourceTree = "<group>"; };
@@ -2117,7 +2170,6 @@
F56C8480131F42E9000AD0F6 /* ZipManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZipManager.cpp; sourceTree = "<group>"; };
F56C8481131F42E9000AD0F6 /* ZipManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipManager.h; sourceTree = "<group>"; };
F56C8483131F42E9000AD0F6 /* AnimatedGif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimatedGif.h; sourceTree = "<group>"; };
- F56C8484131F42E9000AD0F6 /* AudioContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContext.h; sourceTree = "<group>"; };
F56C8485131F42E9000AD0F6 /* D3DResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = D3DResource.h; sourceTree = "<group>"; };
F56C8486131F42E9000AD0F6 /* DDSImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDSImage.h; sourceTree = "<group>"; };
F56C8487131F42E9000AD0F6 /* DirectXGraphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectXGraphics.h; sourceTree = "<group>"; };
@@ -2171,7 +2223,6 @@
F56C84B9131F42E9000AD0F6 /* GUISettingsSliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISettingsSliderControl.h; sourceTree = "<group>"; };
F56C84BA131F42E9000AD0F6 /* GUIShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIShader.h; sourceTree = "<group>"; };
F56C84BB131F42E9000AD0F6 /* GUISliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISliderControl.h; sourceTree = "<group>"; };
- F56C84BC131F42E9000AD0F6 /* GUISound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISound.h; sourceTree = "<group>"; };
F56C84BD131F42E9000AD0F6 /* GUISpinControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControl.h; sourceTree = "<group>"; };
F56C84BE131F42E9000AD0F6 /* GUISpinControlEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControlEx.h; sourceTree = "<group>"; };
F56C84BF131F42E9000AD0F6 /* GUIStandardWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIStandardWindow.h; sourceTree = "<group>"; };
@@ -2209,7 +2260,6 @@
F56C84DF131F42E9000AD0F6 /* XBTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTF.h; sourceTree = "<group>"; };
F56C84E0131F42E9000AD0F6 /* XBTFReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTFReader.h; sourceTree = "<group>"; };
F56C84E1131F42E9000AD0F6 /* AnimatedGif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnimatedGif.cpp; sourceTree = "<group>"; };
- F56C84E2131F42E9000AD0F6 /* AudioContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioContext.cpp; sourceTree = "<group>"; };
F56C84E3131F42E9000AD0F6 /* D3DResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = D3DResource.cpp; sourceTree = "<group>"; };
F56C84E4131F42E9000AD0F6 /* DDSImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DDSImage.cpp; sourceTree = "<group>"; };
F56C84E5131F42E9000AD0F6 /* DirectXGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DirectXGraphics.cpp; sourceTree = "<group>"; };
@@ -2260,7 +2310,6 @@
F56C8513131F42E9000AD0F6 /* GUISettingsSliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISettingsSliderControl.cpp; sourceTree = "<group>"; };
F56C8514131F42E9000AD0F6 /* GUIShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIShader.cpp; sourceTree = "<group>"; };
F56C8515131F42E9000AD0F6 /* GUISliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISliderControl.cpp; sourceTree = "<group>"; };
- F56C8516131F42E9000AD0F6 /* GUISound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISound.cpp; sourceTree = "<group>"; };
F56C8517131F42E9000AD0F6 /* GUISpinControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControl.cpp; sourceTree = "<group>"; };
F56C8518131F42E9000AD0F6 /* GUISpinControlEx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControlEx.cpp; sourceTree = "<group>"; };
F56C8519131F42E9000AD0F6 /* GUIStandardWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIStandardWindow.cpp; sourceTree = "<group>"; };
@@ -2558,8 +2607,6 @@
F56C8657131F42EB000AD0F6 /* cddb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cddb.h; sourceTree = "<group>"; };
F56C8659131F42EB000AD0F6 /* AutoPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoPool.h; sourceTree = "<group>"; };
F56C865A131F42EB000AD0F6 /* AutoPool.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoPool.mm; sourceTree = "<group>"; };
- F56C8669131F42EB000AD0F6 /* IOSCoreAudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IOSCoreAudio.cpp; sourceTree = "<group>"; };
- F56C866A131F42EB000AD0F6 /* IOSCoreAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOSCoreAudio.h; sourceTree = "<group>"; };
F56C866B131F42EB000AD0F6 /* OSXGNUReplacements.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = OSXGNUReplacements.c; sourceTree = "<group>"; };
F56C866C131F42EB000AD0F6 /* OSXGNUReplacements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSXGNUReplacements.h; sourceTree = "<group>"; };
F56C866F131F42EB000AD0F6 /* eprintf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = eprintf.cpp; sourceTree = "<group>"; };
@@ -2738,10 +2785,6 @@
F56C873A131F42EC000AD0F6 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
F56C873B131F42EC000AD0F6 /* md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cpp; sourceTree = "<group>"; };
F56C873C131F42EC000AD0F6 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
- F56C873D131F42EC000AD0F6 /* PCMAmplifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMAmplifier.cpp; sourceTree = "<group>"; };
- F56C873E131F42EC000AD0F6 /* PCMAmplifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMAmplifier.h; sourceTree = "<group>"; };
- F56C873F131F42EC000AD0F6 /* PCMRemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMRemap.cpp; sourceTree = "<group>"; };
- F56C8740131F42EC000AD0F6 /* PCMRemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMRemap.h; sourceTree = "<group>"; };
F56C8741131F42EC000AD0F6 /* PerformanceSample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceSample.cpp; sourceTree = "<group>"; };
F56C8742131F42EC000AD0F6 /* PerformanceSample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceSample.h; sourceTree = "<group>"; };
F56C8743131F42EC000AD0F6 /* PerformanceStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceStats.cpp; sourceTree = "<group>"; };
@@ -2972,7 +3015,6 @@
F5AE415A134175520004BD79 /* XBMCOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCOperations.h; sourceTree = "<group>"; };
F5AE452E134D2E3E0004BD79 /* JSONServiceDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSONServiceDescription.cpp; sourceTree = "<group>"; };
F5AE452F134D2E3E0004BD79 /* JSONServiceDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONServiceDescription.h; sourceTree = "<group>"; };
- F5AE539813673FC70004BD79 /* IOSAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSAudioRingBuffer.h; path = AudioRenderers/IOSAudioRingBuffer.h; sourceTree = "<group>"; };
F5B13DCD1334490D0045076D /* DarwinUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinUtils.h; sourceTree = "<group>"; };
F5B13DCE1334490D0045076D /* DarwinUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DarwinUtils.mm; sourceTree = "<group>"; };
F5BD034D148D496A001B5583 /* CryptThreading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptThreading.cpp; sourceTree = "<group>"; };
@@ -3104,6 +3146,102 @@
path = websocket;
sourceTree = "<group>";
};
+ DFB6620415376791006B8FF1 /* AudioEngine */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6620A15376791006B8FF1 /* Encoders */,
+ DFB6620D15376791006B8FF1 /* Engines */,
+ DFB6622915376791006B8FF1 /* Interfaces */,
+ DFB6623315376791006B8FF1 /* PostProc */,
+ DFB6624315376791006B8FF1 /* Utils */,
+ DFB6620515376791006B8FF1 /* AEAudioFormat.h */,
+ DFB6620615376791006B8FF1 /* AEFactory.cpp */,
+ DFB6620715376791006B8FF1 /* AEFactory.h */,
+ );
+ path = AudioEngine;
+ sourceTree = "<group>";
+ };
+ DFB6620A15376791006B8FF1 /* Encoders */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6620B15376791006B8FF1 /* AEEncoderFFmpeg.cpp */,
+ DFB6620C15376791006B8FF1 /* AEEncoderFFmpeg.h */,
+ );
+ path = Encoders;
+ sourceTree = "<group>";
+ };
+ DFB6620D15376791006B8FF1 /* Engines */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6620E15376791006B8FF1 /* CoreAudioAE.cpp */,
+ DFB6620F15376791006B8FF1 /* CoreAudioAE.h */,
+ DFB6621015376791006B8FF1 /* CoreAudioAEHAL.cpp */,
+ DFB6621115376791006B8FF1 /* CoreAudioAEHAL.h */,
+ DFB6621215376791006B8FF1 /* CoreAudioAEHALIOS.cpp */,
+ DFB6621315376791006B8FF1 /* CoreAudioAEHALIOS.h */,
+ DFB6621415376791006B8FF1 /* CoreAudioAEHALOSX.cpp */,
+ DFB6621515376791006B8FF1 /* CoreAudioAEHALOSX.h */,
+ DFB6621615376791006B8FF1 /* CoreAudioAESound.cpp */,
+ DFB6621715376791006B8FF1 /* CoreAudioAESound.h */,
+ DFB6621815376791006B8FF1 /* CoreAudioAEStream.cpp */,
+ DFB6621915376791006B8FF1 /* CoreAudioAEStream.h */,
+ DFB6621A15376791006B8FF1 /* CoreAudioRingBuffer.h */,
+ DFB6621B15376791006B8FF1 /* ICoreAudioAEHAL.h */,
+ DFB6621C15376791006B8FF1 /* ICoreAudioSource.h */,
+ );
+ path = Engines;
+ sourceTree = "<group>";
+ };
+ DFB6622915376791006B8FF1 /* Interfaces */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6622A15376791006B8FF1 /* AE.h */,
+ DFB6622B15376791006B8FF1 /* AEEncoder.h */,
+ DFB6622C15376791006B8FF1 /* AEPostProc.h */,
+ DFB6622D15376791006B8FF1 /* AESink.h */,
+ DFB6622E15376791006B8FF1 /* AESound.h */,
+ DFB6622F15376791006B8FF1 /* AEStream.h */,
+ DFB6623015376791006B8FF1 /* ThreadedAE.h */,
+ );
+ path = Interfaces;
+ sourceTree = "<group>";
+ };
+ DFB6623315376791006B8FF1 /* PostProc */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6623415376791006B8FF1 /* AEPPAnimationFade.cpp */,
+ DFB6623515376791006B8FF1 /* AEPPAnimationFade.h */,
+ );
+ path = PostProc;
+ sourceTree = "<group>";
+ };
+ DFB6624315376791006B8FF1 /* Utils */ = {
+ isa = PBXGroup;
+ children = (
+ DFB6624415376791006B8FF1 /* AEBitstreamPacker.cpp */,
+ DFB6624515376791006B8FF1 /* AEBitstreamPacker.h */,
+ DFB6624615376791006B8FF1 /* AEBuffer.cpp */,
+ DFB6624715376791006B8FF1 /* AEBuffer.h */,
+ DFB6624815376791006B8FF1 /* AEChannelInfo.cpp */,
+ DFB6624915376791006B8FF1 /* AEChannelInfo.h */,
+ DFB6624A15376791006B8FF1 /* AEConvert.cpp */,
+ DFB6624B15376791006B8FF1 /* AEConvert.h */,
+ 7C0B98F7154B7FF30065A238 /* AEDeviceInfo.cpp */,
+ 7C0B98F8154B7FF30065A238 /* AEDeviceInfo.h */,
+ DFB6624C15376791006B8FF1 /* AEPackIEC61937.cpp */,
+ DFB6624D15376791006B8FF1 /* AEPackIEC61937.h */,
+ DFB6624E15376791006B8FF1 /* AERemap.cpp */,
+ DFB6624F15376791006B8FF1 /* AERemap.h */,
+ DFB6625015376791006B8FF1 /* AEStreamInfo.cpp */,
+ DFB6625115376791006B8FF1 /* AEStreamInfo.h */,
+ DFB6625215376791006B8FF1 /* AEUtil.cpp */,
+ DFB6625315376791006B8FF1 /* AEUtil.h */,
+ DFB6625415376791006B8FF1 /* AEWAVLoader.cpp */,
+ DFB6625515376791006B8FF1 /* AEWAVLoader.h */,
+ );
+ path = Utils;
+ sourceTree = "<group>";
+ };
DFCA6ADD15224671000BFAAE /* httprequesthandler */ = {
isa = PBXGroup;
children = (
@@ -3925,7 +4063,7 @@
F56C81FD131F42E6000AD0F6 /* cores */ = {
isa = PBXGroup;
children = (
- F56C81FE131F42E6000AD0F6 /* AudioRenderers */,
+ DFB6620415376791006B8FF1 /* AudioEngine */,
F56C8206131F42E6000AD0F6 /* DllLoader */,
F56C822E131F42E6000AD0F6 /* dvdplayer */,
F56C82E5131F42E7000AD0F6 /* ExternalPlayer */,
@@ -3940,21 +4078,6 @@
path = cores;
sourceTree = "<group>";
};
- F56C81FE131F42E6000AD0F6 /* AudioRenderers */ = {
- isa = PBXGroup;
- children = (
- F56C8203131F42E6000AD0F6 /* AudioRendererFactory.cpp */,
- F56C8202131F42E6000AD0F6 /* AudioRendererFactory.h */,
- F56C8201131F42E6000AD0F6 /* IAudioRenderer.h */,
- F56C81FF131F42E6000AD0F6 /* IOSAudioRenderer.cpp */,
- F56C8200131F42E6000AD0F6 /* IOSAudioRenderer.h */,
- F5AE539813673FC70004BD79 /* IOSAudioRingBuffer.h */,
- F56C8204131F42E6000AD0F6 /* NullDirectSound.cpp */,
- F56C8205131F42E6000AD0F6 /* NullDirectSound.h */,
- );
- name = AudioRenderers;
- sourceTree = "<group>";
- };
F56C8206131F42E6000AD0F6 /* DllLoader */ = {
isa = PBXGroup;
children = (
@@ -4081,7 +4204,6 @@
F56C8239131F42E6000AD0F6 /* Audio */ = {
isa = PBXGroup;
children = (
- F56C823A131F42E6000AD0F6 /* Encoders */,
F56C8249131F42E6000AD0F6 /* libmad */,
F56C823E131F42E6000AD0F6 /* DllLibMad.h */,
F56C823F131F42E6000AD0F6 /* DVDAudioCodec.h */,
@@ -4091,6 +4213,8 @@
F56C8243131F42E6000AD0F6 /* DVDAudioCodecLibMad.h */,
F56C8244131F42E6000AD0F6 /* DVDAudioCodecLPcm.cpp */,
F56C8245131F42E6000AD0F6 /* DVDAudioCodecLPcm.h */,
+ DFB6630A1537697E006B8FF1 /* DVDAudioCodecPassthrough.cpp */,
+ DFB6630B1537697E006B8FF1 /* DVDAudioCodecPassthrough.h */,
F56C8246131F42E6000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.cpp */,
F56C8247131F42E6000AD0F6 /* DVDAudioCodecPassthroughFFmpeg.h */,
F56C8248131F42E6000AD0F6 /* DVDAudioCodecPcm.cpp */,
@@ -4099,16 +4223,6 @@
path = Audio;
sourceTree = "<group>";
};
- F56C823A131F42E6000AD0F6 /* Encoders */ = {
- isa = PBXGroup;
- children = (
- F56C823B131F42E6000AD0F6 /* DVDAudioEncoderFFmpeg.cpp */,
- F56C823C131F42E6000AD0F6 /* DVDAudioEncoderFFmpeg.h */,
- F56C823D131F42E6000AD0F6 /* IDVDAudioEncoder.h */,
- );
- name = Encoders;
- sourceTree = "<group>";
- };
F56C8249131F42E6000AD0F6 /* libmad */ = {
isa = PBXGroup;
children = (
@@ -4787,8 +4901,6 @@
children = (
F56C84E1131F42E9000AD0F6 /* AnimatedGif.cpp */,
F56C8483131F42E9000AD0F6 /* AnimatedGif.h */,
- F56C84E2131F42E9000AD0F6 /* AudioContext.cpp */,
- F56C8484131F42E9000AD0F6 /* AudioContext.h */,
F56C84E3131F42E9000AD0F6 /* D3DResource.cpp */,
F56C8485131F42E9000AD0F6 /* D3DResource.h */,
F56C84E4131F42E9000AD0F6 /* DDSImage.cpp */,
@@ -4899,8 +5011,6 @@
F56C84BA131F42E9000AD0F6 /* GUIShader.h */,
F56C8515131F42E9000AD0F6 /* GUISliderControl.cpp */,
F56C84BB131F42E9000AD0F6 /* GUISliderControl.h */,
- F56C8516131F42E9000AD0F6 /* GUISound.cpp */,
- F56C84BC131F42E9000AD0F6 /* GUISound.h */,
F56C8517131F42E9000AD0F6 /* GUISpinControl.cpp */,
F56C84BD131F42E9000AD0F6 /* GUISpinControl.h */,
F56C8518131F42E9000AD0F6 /* GUISpinControlEx.cpp */,
@@ -5382,8 +5492,6 @@
F5B13DCD1334490D0045076D /* DarwinUtils.h */,
F5B13DCE1334490D0045076D /* DarwinUtils.mm */,
F56C866F131F42EB000AD0F6 /* eprintf.cpp */,
- F56C8669131F42EB000AD0F6 /* IOSCoreAudio.cpp */,
- F56C866A131F42EB000AD0F6 /* IOSCoreAudio.h */,
DFFD593E1506B5B10088DE4B /* IOSEAGLView.h */,
DFFD593F1506B5B10088DE4B /* IOSEAGLView.mm */,
DFFEFC0215160808001294DC /* IOSExternalTouchController.h */,
@@ -5678,10 +5786,6 @@
F56C873C131F42EC000AD0F6 /* md5.h */,
188F761F1522184E009870CE /* Mime.cpp */,
188F76201522184E009870CE /* Mime.h */,
- F56C873D131F42EC000AD0F6 /* PCMAmplifier.cpp */,
- F56C873E131F42EC000AD0F6 /* PCMAmplifier.h */,
- F56C873F131F42EC000AD0F6 /* PCMRemap.cpp */,
- F56C8740131F42EC000AD0F6 /* PCMRemap.h */,
F56C8741131F42EC000AD0F6 /* PerformanceSample.cpp */,
F56C8742131F42EC000AD0F6 /* PerformanceSample.h */,
F56C8743131F42EC000AD0F6 /* PerformanceStats.cpp */,
@@ -6317,9 +6421,6 @@
F56C88B4131F42ED000AD0F6 /* EncoderLame.cpp in Sources */,
F56C88B5131F42ED000AD0F6 /* EncoderVorbis.cpp in Sources */,
F56C88B6131F42ED000AD0F6 /* EncoderWav.cpp in Sources */,
- F56C88B7131F42ED000AD0F6 /* IOSAudioRenderer.cpp in Sources */,
- F56C88B8131F42ED000AD0F6 /* AudioRendererFactory.cpp in Sources */,
- F56C88B9131F42ED000AD0F6 /* NullDirectSound.cpp in Sources */,
F56C88BA131F42ED000AD0F6 /* coff.cpp in Sources */,
F56C88BB131F42ED000AD0F6 /* dll.cpp in Sources */,
F56C88BC131F42ED000AD0F6 /* dll_tracker.cpp in Sources */,
@@ -6342,7 +6443,6 @@
F56C88CE131F42ED000AD0F6 /* DVDTSCorrection.cpp in Sources */,
F56C88CF131F42ED000AD0F6 /* DVDAudio.cpp in Sources */,
F56C88D0131F42ED000AD0F6 /* DVDClock.cpp in Sources */,
- F56C88D1131F42ED000AD0F6 /* DVDAudioEncoderFFmpeg.cpp in Sources */,
F56C88D2131F42ED000AD0F6 /* DVDAudioCodecFFmpeg.cpp in Sources */,
F56C88D3131F42ED000AD0F6 /* DVDAudioCodecLibMad.cpp in Sources */,
F56C88D4131F42ED000AD0F6 /* DVDAudioCodecLPcm.cpp in Sources */,
@@ -6578,7 +6678,6 @@
F56C89D8131F42ED000AD0F6 /* ZipDirectory.cpp in Sources */,
F56C89D9131F42ED000AD0F6 /* ZipManager.cpp in Sources */,
F56C89DA131F42ED000AD0F6 /* AnimatedGif.cpp in Sources */,
- F56C89DB131F42ED000AD0F6 /* AudioContext.cpp in Sources */,
F56C89DC131F42ED000AD0F6 /* D3DResource.cpp in Sources */,
F56C89DD131F42ED000AD0F6 /* DDSImage.cpp in Sources */,
F56C89DE131F42ED000AD0F6 /* DirectXGraphics.cpp in Sources */,
@@ -6629,7 +6728,6 @@
F56C8A0C131F42ED000AD0F6 /* GUISettingsSliderControl.cpp in Sources */,
F56C8A0D131F42ED000AD0F6 /* GUIShader.cpp in Sources */,
F56C8A0E131F42ED000AD0F6 /* GUISliderControl.cpp in Sources */,
- F56C8A0F131F42ED000AD0F6 /* GUISound.cpp in Sources */,
F56C8A10131F42ED000AD0F6 /* GUISpinControl.cpp in Sources */,
F56C8A11131F42ED000AD0F6 /* GUISpinControlEx.cpp in Sources */,
F56C8A12131F42ED000AD0F6 /* GUIStandardWindow.cpp in Sources */,
@@ -6796,7 +6894,6 @@
F56C8AB9131F42ED000AD0F6 /* ZeroconfBrowser.cpp in Sources */,
F56C8ABA131F42ED000AD0F6 /* cddb.cpp in Sources */,
F56C8ABB131F42ED000AD0F6 /* AutoPool.mm in Sources */,
- F56C8AC2131F42ED000AD0F6 /* IOSCoreAudio.cpp in Sources */,
F56C8AC3131F42ED000AD0F6 /* OSXGNUReplacements.c in Sources */,
F56C8AC5131F42ED000AD0F6 /* eprintf.cpp in Sources */,
F56C8AC6131F42ED000AD0F6 /* GUIDialogPictureInfo.cpp in Sources */,
@@ -6881,8 +6978,6 @@
F56C8B1E131F42ED000AD0F6 /* LCD.cpp in Sources */,
F56C8B1F131F42ED000AD0F6 /* log.cpp in Sources */,
F56C8B20131F42ED000AD0F6 /* md5.cpp in Sources */,
- F56C8B21131F42ED000AD0F6 /* PCMAmplifier.cpp in Sources */,
- F56C8B22131F42ED000AD0F6 /* PCMRemap.cpp in Sources */,
F56C8B23131F42ED000AD0F6 /* PerformanceSample.cpp in Sources */,
F56C8B24131F42ED000AD0F6 /* PerformanceStats.cpp in Sources */,
F56C8B26131F42ED000AD0F6 /* RegExp.cpp in Sources */,
@@ -7089,6 +7184,26 @@
F5ED8D961551FB1000842059 /* BlurayDirectory.cpp in Sources */,
F5ED90B8155398BB00842059 /* POUtils.cpp in Sources */,
F5ED90BB155398CB00842059 /* XBMCTinyXML.cpp in Sources */,
+ DFB6625615376791006B8FF1 /* AEFactory.cpp in Sources */,
+ DFB6625815376791006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */,
+ DFB6625915376791006B8FF1 /* CoreAudioAE.cpp in Sources */,
+ DFB6625A15376791006B8FF1 /* CoreAudioAEHAL.cpp in Sources */,
+ DFB6625B15376791006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */,
+ DFB6625C15376791006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */,
+ DFB6625D15376791006B8FF1 /* CoreAudioAESound.cpp in Sources */,
+ DFB6625E15376791006B8FF1 /* CoreAudioAEStream.cpp in Sources */,
+ DFB6626715376791006B8FF1 /* AEPPAnimationFade.cpp in Sources */,
+ DFB6626E15376791006B8FF1 /* AEBitstreamPacker.cpp in Sources */,
+ DFB6626F15376791006B8FF1 /* AEBuffer.cpp in Sources */,
+ DFB6627015376791006B8FF1 /* AEChannelInfo.cpp in Sources */,
+ DFB6627115376791006B8FF1 /* AEConvert.cpp in Sources */,
+ DFB6627215376791006B8FF1 /* AEPackIEC61937.cpp in Sources */,
+ DFB6627315376791006B8FF1 /* AERemap.cpp in Sources */,
+ DFB6627415376791006B8FF1 /* AEStreamInfo.cpp in Sources */,
+ DFB6627515376791006B8FF1 /* AEUtil.cpp in Sources */,
+ DFB6627615376791006B8FF1 /* AEWAVLoader.cpp in Sources */,
+ DFB6630C1537697E006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */,
+ 7C0B98F9154B7FF30065A238 /* AEDeviceInfo.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7143,6 +7258,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
@@ -7256,6 +7373,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj
index 81d10f4f1b..827ffd4b28 100644
--- a/XBMC.xcodeproj/project.pbxproj
+++ b/XBMC.xcodeproj/project.pbxproj
@@ -92,7 +92,6 @@
18B7C3A812942132009E7A26 /* AdvancedSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C3A612942132009E7A26 /* AdvancedSettings.cpp */; };
18B7C3A912942132009E7A26 /* AdvancedSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C3A612942132009E7A26 /* AdvancedSettings.cpp */; };
18B7C7A91294222E009E7A26 /* AnimatedGif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7541294222E009E7A26 /* AnimatedGif.cpp */; };
- 18B7C7AA1294222E009E7A26 /* AudioContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7551294222E009E7A26 /* AudioContext.cpp */; };
18B7C7AB1294222E009E7A26 /* D3DResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7561294222E009E7A26 /* D3DResource.cpp */; };
18B7C7AC1294222E009E7A26 /* DDSImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7571294222E009E7A26 /* DDSImage.cpp */; };
18B7C7AD1294222E009E7A26 /* DirectXGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7581294222E009E7A26 /* DirectXGraphics.cpp */; };
@@ -143,7 +142,6 @@
18B7C7DB1294222E009E7A26 /* GUISettingsSliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7861294222E009E7A26 /* GUISettingsSliderControl.cpp */; };
18B7C7DC1294222E009E7A26 /* GUIShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7871294222E009E7A26 /* GUIShader.cpp */; };
18B7C7DD1294222E009E7A26 /* GUISliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7881294222E009E7A26 /* GUISliderControl.cpp */; };
- 18B7C7DE1294222E009E7A26 /* GUISound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7891294222E009E7A26 /* GUISound.cpp */; };
18B7C7DF1294222E009E7A26 /* GUISpinControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78A1294222E009E7A26 /* GUISpinControl.cpp */; };
18B7C7E01294222E009E7A26 /* GUISpinControlEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78B1294222E009E7A26 /* GUISpinControlEx.cpp */; };
18B7C7E11294222E009E7A26 /* GUIStandardWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78C1294222E009E7A26 /* GUIStandardWindow.cpp */; };
@@ -176,7 +174,6 @@
18B7C7FC1294222E009E7A26 /* XBTF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7A71294222E009E7A26 /* XBTF.cpp */; };
18B7C7FD1294222E009E7A26 /* XBTFReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7A81294222E009E7A26 /* XBTFReader.cpp */; };
18B7C7FE1294222E009E7A26 /* AnimatedGif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7541294222E009E7A26 /* AnimatedGif.cpp */; };
- 18B7C7FF1294222E009E7A26 /* AudioContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7551294222E009E7A26 /* AudioContext.cpp */; };
18B7C8001294222E009E7A26 /* D3DResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7561294222E009E7A26 /* D3DResource.cpp */; };
18B7C8011294222E009E7A26 /* DDSImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7571294222E009E7A26 /* DDSImage.cpp */; };
18B7C8021294222E009E7A26 /* DirectXGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7581294222E009E7A26 /* DirectXGraphics.cpp */; };
@@ -227,7 +224,6 @@
18B7C8301294222E009E7A26 /* GUISettingsSliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7861294222E009E7A26 /* GUISettingsSliderControl.cpp */; };
18B7C8311294222E009E7A26 /* GUIShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7871294222E009E7A26 /* GUIShader.cpp */; };
18B7C8321294222E009E7A26 /* GUISliderControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7881294222E009E7A26 /* GUISliderControl.cpp */; };
- 18B7C8331294222E009E7A26 /* GUISound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C7891294222E009E7A26 /* GUISound.cpp */; };
18B7C8341294222E009E7A26 /* GUISpinControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78A1294222E009E7A26 /* GUISpinControl.cpp */; };
18B7C8351294222E009E7A26 /* GUISpinControlEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78B1294222E009E7A26 /* GUISpinControlEx.cpp */; };
18B7C8361294222E009E7A26 /* GUIStandardWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C78C1294222E009E7A26 /* GUIStandardWindow.cpp */; };
@@ -367,8 +363,6 @@
18B7C9841294385F009E7A26 /* XMLUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18B7C9811294385F009E7A26 /* XMLUtils.cpp */; };
18C1D22D13033F6A00CFFE59 /* GLUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18C1D22B13033F6A00CFFE59 /* GLUtils.cpp */; };
18C1D22E13033F6A00CFFE59 /* GLUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18C1D22B13033F6A00CFFE59 /* GLUtils.cpp */; };
- 18CCEAEE1112F5B800615FC6 /* PCMRemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18CCEAEC1112F5B800615FC6 /* PCMRemap.cpp */; };
- 18CCEAEF1112F5B800615FC6 /* PCMRemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18CCEAEC1112F5B800615FC6 /* PCMRemap.cpp */; };
18ECC96213CF178D00A9ED6C /* StreamUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 18ECC96013CF178D00A9ED6C /* StreamUtils.cpp */; };
32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; };
3802709A13D5A653009493DD /* SystemClock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3802709813D5A653009493DD /* SystemClock.cpp */; };
@@ -381,8 +375,6 @@
431AE5D9109C1A63007428C3 /* OverlayRendererUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 431AE5D7109C1A63007428C3 /* OverlayRendererUtil.cpp */; };
431AE5DA109C1A63007428C3 /* OverlayRendererUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 431AE5D7109C1A63007428C3 /* OverlayRendererUtil.cpp */; };
43248C4E0FBE224000B88866 /* LockFree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B950FBC8E3B00171871 /* LockFree.cpp */; };
- 43248C510FBE224C00B88866 /* CoreAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B920FBC8DFF00171871 /* CoreAudio.cpp */; };
- 43248C520FBE224E00B88866 /* CoreAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */; };
432D7CE412D86DA500CE4C49 /* NetworkLinux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 432D7CE312D86DA500CE4C49 /* NetworkLinux.cpp */; };
432D7CE512D86DA500CE4C49 /* NetworkLinux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 432D7CE312D86DA500CE4C49 /* NetworkLinux.cpp */; };
432D7CF712D870E800CE4C49 /* TCPServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 432D7CF612D870E800CE4C49 /* TCPServer.cpp */; };
@@ -505,6 +497,8 @@
43EA42B0136C2274002C82A5 /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807114B135DB5CC002F601B /* InputOperations.cpp */; };
7C0A7EC013A5DBCE00AFC2BD /* AppParamParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7EBE13A5DBCE00AFC2BD /* AppParamParser.cpp */; };
7C0A7EC113A5DBCE00AFC2BD /* AppParamParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7EBE13A5DBCE00AFC2BD /* AppParamParser.cpp */; };
+ 7C0B98A3154B79C30065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B98A1154B79C30065A238 /* AEDeviceInfo.cpp */; };
+ 7C0B98A4154B79C30065A238 /* AEDeviceInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0B98A1154B79C30065A238 /* AEDeviceInfo.cpp */; };
7C1A85651520522500C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A85631520522500C63311 /* TextureCacheJob.cpp */; };
7C1A85661520522500C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A85631520522500C63311 /* TextureCacheJob.cpp */; };
7C1F6EBB13ECCFA7001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */; };
@@ -586,8 +580,6 @@
810C9FA90D67D1FB0095F5DD /* MythDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810C9FA50D67D1FB0095F5DD /* MythDirectory.cpp */; };
810C9FAA0D67D1FB0095F5DD /* MythFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810C9FA70D67D1FB0095F5DD /* MythFile.cpp */; };
815EE6350E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */; };
- 83A72B910FBC8DB000171871 /* CoreAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */; };
- 83A72B940FBC8DFF00171871 /* CoreAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B920FBC8DFF00171871 /* CoreAudio.cpp */; };
83A72B970FBC8E3B00171871 /* LockFree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B950FBC8E3B00171871 /* LockFree.cpp */; settings = {COMPILER_FLAGS = "-O0"; }; };
83E0B2490F7C95FF0091643F /* Atomics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E0B2480F7C95FF0091643F /* Atomics.cpp */; };
83E0B24A0F7C95FF0091643F /* Atomics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83E0B2480F7C95FF0091643F /* Atomics.cpp */; };
@@ -709,6 +701,44 @@
DF98D98D1434F47D00A6EBE1 /* SkinVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF98D98A1434F47D00A6EBE1 /* SkinVariable.cpp */; };
DFAB049813F8376700B70BFB /* InertialScrollingHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAB049613F8376700B70BFB /* InertialScrollingHandler.cpp */; };
DFAB049913F8376700B70BFB /* InertialScrollingHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAB049613F8376700B70BFB /* InertialScrollingHandler.cpp */; };
+ DFB65FB515373AE7006B8FF1 /* AEFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6515373AE7006B8FF1 /* AEFactory.cpp */; };
+ DFB65FB715373AE7006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */; };
+ DFB65FB815373AE7006B8FF1 /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */; };
+ DFB65FB915373AE7006B8FF1 /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */; };
+ DFB65FBA15373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
+ DFB65FBB15373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7315373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp */; };
+ DFB65FBC15373AE7006B8FF1 /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */; };
+ DFB65FBD15373AE7006B8FF1 /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */; };
+ DFB65FC515373AE7006B8FF1 /* AEPPAnimationFade.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F9315373AE7006B8FF1 /* AEPPAnimationFade.cpp */; };
+ DFB65FCC15373AE7006B8FF1 /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */; };
+ DFB65FCD15373AE7006B8FF1 /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */; };
+ DFB65FCE15373AE7006B8FF1 /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */; };
+ DFB65FCF15373AE7006B8FF1 /* AEConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA915373AE7006B8FF1 /* AEConvert.cpp */; };
+ DFB65FD015373AE7006B8FF1 /* AEPackIEC61937.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAB15373AE7006B8FF1 /* AEPackIEC61937.cpp */; };
+ DFB65FD115373AE7006B8FF1 /* AERemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAD15373AE7006B8FF1 /* AERemap.cpp */; };
+ DFB65FD215373AE7006B8FF1 /* AEStreamInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAF15373AE7006B8FF1 /* AEStreamInfo.cpp */; };
+ DFB65FD315373AE7006B8FF1 /* AEUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FB115373AE7006B8FF1 /* AEUtil.cpp */; };
+ DFB65FD415373AE7006B8FF1 /* AEWAVLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FB315373AE7006B8FF1 /* AEWAVLoader.cpp */; };
+ DFB65FD515373AE7006B8FF1 /* AEFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6515373AE7006B8FF1 /* AEFactory.cpp */; };
+ DFB65FD715373AE7006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */; };
+ DFB65FD815373AE7006B8FF1 /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */; };
+ DFB65FD915373AE7006B8FF1 /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */; };
+ DFB65FDA15373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
+ DFB65FDB15373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7315373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp */; };
+ DFB65FDC15373AE7006B8FF1 /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */; };
+ DFB65FDD15373AE7006B8FF1 /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */; };
+ DFB65FE515373AE7006B8FF1 /* AEPPAnimationFade.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F9315373AE7006B8FF1 /* AEPPAnimationFade.cpp */; };
+ DFB65FEC15373AE7006B8FF1 /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */; };
+ DFB65FED15373AE7006B8FF1 /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */; };
+ DFB65FEE15373AE7006B8FF1 /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */; };
+ DFB65FEF15373AE7006B8FF1 /* AEConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA915373AE7006B8FF1 /* AEConvert.cpp */; };
+ DFB65FF015373AE7006B8FF1 /* AEPackIEC61937.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAB15373AE7006B8FF1 /* AEPackIEC61937.cpp */; };
+ DFB65FF115373AE7006B8FF1 /* AERemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAD15373AE7006B8FF1 /* AERemap.cpp */; };
+ DFB65FF215373AE7006B8FF1 /* AEStreamInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FAF15373AE7006B8FF1 /* AEStreamInfo.cpp */; };
+ DFB65FF315373AE7006B8FF1 /* AEUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FB115373AE7006B8FF1 /* AEUtil.cpp */; };
+ DFB65FF415373AE7006B8FF1 /* AEWAVLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FB315373AE7006B8FF1 /* AEWAVLoader.cpp */; };
+ DFB6610915374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
+ DFB6610B15374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
DFC1B8F01464840E00B1BE79 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFC1B8EF1464840E00B1BE79 /* SystemConfiguration.framework */; };
DFC1BA3414648D6500B1BE79 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFC1B8EF1464840E00B1BE79 /* SystemConfiguration.framework */; };
DFCA6AC6152245CD000BFAAE /* HTTPApiHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFCA6AB9152245CD000BFAAE /* HTTPApiHandler.cpp */; };
@@ -1113,7 +1143,6 @@
E38E22E40D25F9FE00618676 /* MusicAlbumInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E650D25F9FD00618676 /* MusicAlbumInfo.cpp */; };
E38E22E50D25F9FE00618676 /* MusicInfoScraper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E670D25F9FD00618676 /* MusicInfoScraper.cpp */; };
E38E22E70D25F9FE00618676 /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6B0D25F9FD00618676 /* Network.cpp */; };
- E38E22E80D25F9FE00618676 /* PCMAmplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6D0D25F9FD00618676 /* PCMAmplifier.cpp */; };
E38E22E90D25F9FE00618676 /* PerformanceSample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6F0D25F9FD00618676 /* PerformanceSample.cpp */; };
E38E22EA0D25F9FE00618676 /* PerformanceStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E710D25F9FD00618676 /* PerformanceStats.cpp */; };
E38E22EB0D25F9FE00618676 /* RegExp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E730D25F9FD00618676 /* RegExp.cpp */; };
@@ -1302,8 +1331,6 @@
F599CD2C108E65370010EC2A /* IoSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F599CD29108E65370010EC2A /* IoSupport.cpp */; };
F599CD74108E6A7A0010EC2A /* DarwinStorageProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F599CD73108E6A7A0010EC2A /* DarwinStorageProvider.cpp */; };
F599CD75108E6A7A0010EC2A /* DarwinStorageProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F599CD73108E6A7A0010EC2A /* DarwinStorageProvider.cpp */; };
- F5A00B080EFDDDFC00CD59F3 /* AudioRendererFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A00B070EFDDDFC00CD59F3 /* AudioRendererFactory.cpp */; };
- F5A00B260EFDE44100CD59F3 /* NullDirectSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A00B240EFDE44100CD59F3 /* NullDirectSound.cpp */; };
F5A1C8C00F6B06CF00A96ABD /* Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E14640D25F9F900618676 /* Application.cpp */; };
F5A1C8C10F6B06CF00A96ABD /* ApplicationMessenger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E14660D25F9F900618676 /* ApplicationMessenger.cpp */; };
F5A1C8C40F6B06CF00A96ABD /* Autorun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E146E0D25F9F900618676 /* Autorun.cpp */; };
@@ -1679,7 +1706,6 @@
F5A1CAD60F6B06CF00A96ABD /* MusicAlbumInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E650D25F9FD00618676 /* MusicAlbumInfo.cpp */; };
F5A1CAD70F6B06CF00A96ABD /* MusicInfoScraper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E670D25F9FD00618676 /* MusicInfoScraper.cpp */; };
F5A1CAD90F6B06CF00A96ABD /* Network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6B0D25F9FD00618676 /* Network.cpp */; };
- F5A1CADA0F6B06CF00A96ABD /* PCMAmplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6D0D25F9FD00618676 /* PCMAmplifier.cpp */; };
F5A1CADB0F6B06CF00A96ABD /* PerformanceSample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E6F0D25F9FD00618676 /* PerformanceSample.cpp */; };
F5A1CADC0F6B06CF00A96ABD /* PerformanceStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E710D25F9FD00618676 /* PerformanceStats.cpp */; };
F5A1CADD0F6B06CF00A96ABD /* RegExp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E1E730D25F9FD00618676 /* RegExp.cpp */; };
@@ -1781,8 +1807,6 @@
F5A1CB7C0F6B06CF00A96ABD /* VTPFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5FAB0700EFABAC800BAD4AE /* VTPFile.cpp */; };
F5A1CB7D0F6B06CF00A96ABD /* VTPDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5FAB0750EFABE2C00BAD4AE /* VTPDirectory.cpp */; };
F5A1CB7E0F6B06CF00A96ABD /* VTPSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5FAB0790EFABE4A00BAD4AE /* VTPSession.cpp */; };
- F5A1CB7F0F6B06CF00A96ABD /* AudioRendererFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A00B070EFDDDFC00CD59F3 /* AudioRendererFactory.cpp */; };
- F5A1CB800F6B06CF00A96ABD /* NullDirectSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5A00B240EFDE44100CD59F3 /* NullDirectSound.cpp */; };
F5A1CB810F6B06CF00A96ABD /* ExternalPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C5608C40F1754930056433A /* ExternalPlayer.cpp */; };
F5A1CB830F6B06CF00A96ABD /* HTTPDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F584E12D0F257C5100DB26A5 /* HTTPDirectory.cpp */; };
F5A1CB840F6B06CF00A96ABD /* GUIDialogKaraokeSongSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F54C51D00F1E783200D46E3C /* GUIDialogKaraokeSongSelector.cpp */; };
@@ -1948,8 +1972,6 @@
F5F245DB1112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */; };
F5F245EE1112C9AB009126C6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245EC1112C9AB009126C6 /* FileUtils.cpp */; };
F5F245EF1112C9AB009126C6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245EC1112C9AB009126C6 /* FileUtils.cpp */; };
- F5F24E8611232488009126C6 /* DVDAudioEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F24E8311232488009126C6 /* DVDAudioEncoderFFmpeg.cpp */; };
- F5F24E8711232488009126C6 /* DVDAudioEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F24E8311232488009126C6 /* DVDAudioEncoderFFmpeg.cpp */; };
F5F2EF4B0E593E0D0092C37F /* DVDFileInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F2EF4A0E593E0D0092C37F /* DVDFileInfo.cpp */; };
F5F8E1DA0E427E8000A8E96F /* VGMCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F8E1D90E427E8000A8E96F /* VGMCodec.cpp */; };
F5F8E1E80E427F6700A8E96F /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F8E1E60E427F6700A8E96F /* md5.cpp */; };
@@ -2040,7 +2062,6 @@
18B7C3A612942132009E7A26 /* AdvancedSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AdvancedSettings.cpp; sourceTree = "<group>"; };
18B7C3A712942132009E7A26 /* AdvancedSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdvancedSettings.h; sourceTree = "<group>"; };
18B7C6F61294222D009E7A26 /* AnimatedGif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimatedGif.h; sourceTree = "<group>"; };
- 18B7C6F71294222D009E7A26 /* AudioContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContext.h; sourceTree = "<group>"; };
18B7C6F81294222D009E7A26 /* D3DResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = D3DResource.h; sourceTree = "<group>"; };
18B7C6F91294222D009E7A26 /* DDSImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDSImage.h; sourceTree = "<group>"; };
18B7C6FA1294222D009E7A26 /* DirectXGraphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectXGraphics.h; sourceTree = "<group>"; };
@@ -2094,7 +2115,6 @@
18B7C72C1294222D009E7A26 /* GUISettingsSliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISettingsSliderControl.h; sourceTree = "<group>"; };
18B7C72D1294222D009E7A26 /* GUIShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIShader.h; sourceTree = "<group>"; };
18B7C72E1294222D009E7A26 /* GUISliderControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISliderControl.h; sourceTree = "<group>"; };
- 18B7C72F1294222D009E7A26 /* GUISound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISound.h; sourceTree = "<group>"; };
18B7C7301294222D009E7A26 /* GUISpinControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControl.h; sourceTree = "<group>"; };
18B7C7311294222D009E7A26 /* GUISpinControlEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUISpinControlEx.h; sourceTree = "<group>"; };
18B7C7321294222D009E7A26 /* GUIStandardWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIStandardWindow.h; sourceTree = "<group>"; };
@@ -2132,7 +2152,6 @@
18B7C7521294222E009E7A26 /* XBTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTF.h; sourceTree = "<group>"; };
18B7C7531294222E009E7A26 /* XBTFReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBTFReader.h; sourceTree = "<group>"; };
18B7C7541294222E009E7A26 /* AnimatedGif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnimatedGif.cpp; sourceTree = "<group>"; };
- 18B7C7551294222E009E7A26 /* AudioContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioContext.cpp; sourceTree = "<group>"; };
18B7C7561294222E009E7A26 /* D3DResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = D3DResource.cpp; sourceTree = "<group>"; };
18B7C7571294222E009E7A26 /* DDSImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DDSImage.cpp; sourceTree = "<group>"; };
18B7C7581294222E009E7A26 /* DirectXGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DirectXGraphics.cpp; sourceTree = "<group>"; };
@@ -2183,7 +2202,6 @@
18B7C7861294222E009E7A26 /* GUISettingsSliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISettingsSliderControl.cpp; sourceTree = "<group>"; };
18B7C7871294222E009E7A26 /* GUIShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIShader.cpp; sourceTree = "<group>"; };
18B7C7881294222E009E7A26 /* GUISliderControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISliderControl.cpp; sourceTree = "<group>"; };
- 18B7C7891294222E009E7A26 /* GUISound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISound.cpp; sourceTree = "<group>"; };
18B7C78A1294222E009E7A26 /* GUISpinControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControl.cpp; sourceTree = "<group>"; };
18B7C78B1294222E009E7A26 /* GUISpinControlEx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUISpinControlEx.cpp; sourceTree = "<group>"; };
18B7C78C1294222E009E7A26 /* GUIStandardWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIStandardWindow.cpp; sourceTree = "<group>"; };
@@ -2329,8 +2347,6 @@
18B7C9E7129447B9009E7A26 /* MathUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathUtils.h; sourceTree = "<group>"; };
18C1D22B13033F6A00CFFE59 /* GLUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GLUtils.cpp; sourceTree = "<group>"; };
18C1D22C13033F6A00CFFE59 /* GLUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLUtils.h; sourceTree = "<group>"; };
- 18CCEAEC1112F5B800615FC6 /* PCMRemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMRemap.cpp; sourceTree = "<group>"; };
- 18CCEAED1112F5B800615FC6 /* PCMRemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMRemap.h; sourceTree = "<group>"; };
18ECC96013CF178D00A9ED6C /* StreamUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamUtils.cpp; sourceTree = "<group>"; };
18ECC96113CF178D00A9ED6C /* StreamUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamUtils.h; sourceTree = "<group>"; };
32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = "<group>"; };
@@ -2485,6 +2501,8 @@
6E97BDC40DA2B620003A2A89 /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Socket.h; sourceTree = "<group>"; };
7C0A7EBE13A5DBCE00AFC2BD /* AppParamParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppParamParser.cpp; sourceTree = "<group>"; };
7C0A7EBF13A5DBCE00AFC2BD /* AppParamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppParamParser.h; sourceTree = "<group>"; };
+ 7C0B98A1154B79C30065A238 /* AEDeviceInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEDeviceInfo.cpp; sourceTree = "<group>"; };
+ 7C0B98A2154B79C30065A238 /* AEDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEDeviceInfo.h; sourceTree = "<group>"; };
7C1A85631520522500C63311 /* TextureCacheJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureCacheJob.cpp; sourceTree = "<group>"; };
7C1A85641520522500C63311 /* TextureCacheJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCacheJob.h; sourceTree = "<group>"; };
7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; };
@@ -2579,11 +2597,6 @@
810C9FA80D67D1FB0095F5DD /* MythFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MythFile.h; sourceTree = "<group>"; };
815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamRTMP.cpp; sourceTree = "<group>"; };
815EE6340E17F1DC009FBE3C /* DVDInputStreamRTMP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamRTMP.h; sourceTree = "<group>"; };
- 83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioRenderer.cpp; path = AudioRenderers/CoreAudioRenderer.cpp; sourceTree = "<group>"; };
- 83A72B8F0FBC8DB000171871 /* CoreAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioRenderer.h; path = AudioRenderers/CoreAudioRenderer.h; sourceTree = "<group>"; };
- 83A72B900FBC8DB000171871 /* IAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAudioRenderer.h; path = AudioRenderers/IAudioRenderer.h; sourceTree = "<group>"; };
- 83A72B920FBC8DFF00171871 /* CoreAudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudio.cpp; sourceTree = "<group>"; };
- 83A72B930FBC8DFF00171871 /* CoreAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudio.h; sourceTree = "<group>"; };
83A72B950FBC8E3B00171871 /* LockFree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockFree.cpp; sourceTree = "<group>"; };
83A72B960FBC8E3B00171871 /* LockFree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockFree.h; sourceTree = "<group>"; };
83E0B2470F7C95FF0091643F /* Atomics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atomics.h; sourceTree = "<group>"; };
@@ -2724,6 +2737,55 @@
DF98D98B1434F47D00A6EBE1 /* SkinVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkinVariable.h; sourceTree = "<group>"; };
DFAB049613F8376700B70BFB /* InertialScrollingHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InertialScrollingHandler.cpp; sourceTree = "<group>"; };
DFAB049713F8376700B70BFB /* InertialScrollingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InertialScrollingHandler.h; sourceTree = "<group>"; };
+ DFB65F6415373AE7006B8FF1 /* AEAudioFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEAudioFormat.h; sourceTree = "<group>"; };
+ DFB65F6515373AE7006B8FF1 /* AEFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEFactory.cpp; sourceTree = "<group>"; };
+ DFB65F6615373AE7006B8FF1 /* AEFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFactory.h; sourceTree = "<group>"; };
+ DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEEncoderFFmpeg.cpp; sourceTree = "<group>"; };
+ DFB65F6B15373AE7006B8FF1 /* AEEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoderFFmpeg.h; sourceTree = "<group>"; };
+ DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAE.cpp; sourceTree = "<group>"; };
+ DFB65F6E15373AE7006B8FF1 /* CoreAudioAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAE.h; sourceTree = "<group>"; };
+ DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHAL.cpp; sourceTree = "<group>"; };
+ DFB65F7015373AE7006B8FF1 /* CoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALIOS.cpp; sourceTree = "<group>"; };
+ DFB65F7215373AE7006B8FF1 /* CoreAudioAEHALIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALIOS.h; sourceTree = "<group>"; };
+ DFB65F7315373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEHALOSX.cpp; sourceTree = "<group>"; };
+ DFB65F7415373AE7006B8FF1 /* CoreAudioAEHALOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEHALOSX.h; sourceTree = "<group>"; };
+ DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAESound.cpp; sourceTree = "<group>"; };
+ DFB65F7615373AE7006B8FF1 /* CoreAudioAESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAESound.h; sourceTree = "<group>"; };
+ DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CoreAudioAEStream.cpp; sourceTree = "<group>"; };
+ DFB65F7815373AE7006B8FF1 /* CoreAudioAEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioAEStream.h; sourceTree = "<group>"; };
+ DFB65F7915373AE7006B8FF1 /* CoreAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreAudioRingBuffer.h; sourceTree = "<group>"; };
+ DFB65F7A15373AE7006B8FF1 /* ICoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioAEHAL.h; sourceTree = "<group>"; };
+ DFB65F7B15373AE7006B8FF1 /* ICoreAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICoreAudioSource.h; sourceTree = "<group>"; };
+ DFB65F8915373AE7006B8FF1 /* AE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AE.h; sourceTree = "<group>"; };
+ DFB65F8A15373AE7006B8FF1 /* AEEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEEncoder.h; sourceTree = "<group>"; };
+ DFB65F8B15373AE7006B8FF1 /* AEPostProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPostProc.h; sourceTree = "<group>"; };
+ DFB65F8C15373AE7006B8FF1 /* AESink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESink.h; sourceTree = "<group>"; };
+ DFB65F8D15373AE7006B8FF1 /* AESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESound.h; sourceTree = "<group>"; };
+ DFB65F8E15373AE7006B8FF1 /* AEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStream.h; sourceTree = "<group>"; };
+ DFB65F8F15373AE7006B8FF1 /* ThreadedAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadedAE.h; sourceTree = "<group>"; };
+ DFB65F9315373AE7006B8FF1 /* AEPPAnimationFade.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPPAnimationFade.cpp; sourceTree = "<group>"; };
+ DFB65F9415373AE7006B8FF1 /* AEPPAnimationFade.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPPAnimationFade.h; sourceTree = "<group>"; };
+ DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBitstreamPacker.cpp; sourceTree = "<group>"; };
+ DFB65FA415373AE7006B8FF1 /* AEBitstreamPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBitstreamPacker.h; sourceTree = "<group>"; };
+ DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEBuffer.cpp; sourceTree = "<group>"; };
+ DFB65FA615373AE7006B8FF1 /* AEBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEBuffer.h; sourceTree = "<group>"; };
+ DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEChannelInfo.cpp; sourceTree = "<group>"; };
+ DFB65FA815373AE7006B8FF1 /* AEChannelInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEChannelInfo.h; sourceTree = "<group>"; };
+ DFB65FA915373AE7006B8FF1 /* AEConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEConvert.cpp; sourceTree = "<group>"; };
+ DFB65FAA15373AE7006B8FF1 /* AEConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEConvert.h; sourceTree = "<group>"; };
+ DFB65FAB15373AE7006B8FF1 /* AEPackIEC61937.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEPackIEC61937.cpp; sourceTree = "<group>"; };
+ DFB65FAC15373AE7006B8FF1 /* AEPackIEC61937.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEPackIEC61937.h; sourceTree = "<group>"; };
+ DFB65FAD15373AE7006B8FF1 /* AERemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AERemap.cpp; sourceTree = "<group>"; };
+ DFB65FAE15373AE7006B8FF1 /* AERemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AERemap.h; sourceTree = "<group>"; };
+ DFB65FAF15373AE7006B8FF1 /* AEStreamInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEStreamInfo.cpp; sourceTree = "<group>"; };
+ DFB65FB015373AE7006B8FF1 /* AEStreamInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEStreamInfo.h; sourceTree = "<group>"; };
+ DFB65FB115373AE7006B8FF1 /* AEUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEUtil.cpp; sourceTree = "<group>"; };
+ DFB65FB215373AE7006B8FF1 /* AEUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEUtil.h; sourceTree = "<group>"; };
+ DFB65FB315373AE7006B8FF1 /* AEWAVLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AEWAVLoader.cpp; sourceTree = "<group>"; };
+ DFB65FB415373AE7006B8FF1 /* AEWAVLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEWAVLoader.h; sourceTree = "<group>"; };
+ DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecPassthrough.cpp; sourceTree = "<group>"; };
+ DFB6610715374E80006B8FF1 /* DVDAudioCodecPassthrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPassthrough.h; sourceTree = "<group>"; };
DFC1B8EF1464840E00B1BE79 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
DFCA6AB9152245CD000BFAAE /* HTTPApiHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPApiHandler.cpp; sourceTree = "<group>"; };
DFCA6ABA152245CD000BFAAE /* HTTPApiHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPApiHandler.h; sourceTree = "<group>"; };
@@ -3641,8 +3703,6 @@
E38E1E680D25F9FD00618676 /* MusicInfoScraper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicInfoScraper.h; sourceTree = "<group>"; };
E38E1E6B0D25F9FD00618676 /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
E38E1E6C0D25F9FD00618676 /* Network.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Network.h; sourceTree = "<group>"; };
- E38E1E6D0D25F9FD00618676 /* PCMAmplifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMAmplifier.cpp; sourceTree = "<group>"; };
- E38E1E6E0D25F9FD00618676 /* PCMAmplifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMAmplifier.h; sourceTree = "<group>"; };
E38E1E6F0D25F9FD00618676 /* PerformanceSample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceSample.cpp; sourceTree = "<group>"; };
E38E1E700D25F9FD00618676 /* PerformanceSample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceSample.h; sourceTree = "<group>"; };
E38E1E710D25F9FD00618676 /* PerformanceStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceStats.cpp; sourceTree = "<group>"; };
@@ -3878,10 +3938,6 @@
F599CD2A108E65370010EC2A /* IoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IoSupport.h; sourceTree = "<group>"; };
F599CD72108E6A7A0010EC2A /* DarwinStorageProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinStorageProvider.h; sourceTree = "<group>"; };
F599CD73108E6A7A0010EC2A /* DarwinStorageProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DarwinStorageProvider.cpp; sourceTree = "<group>"; };
- F5A00B070EFDDDFC00CD59F3 /* AudioRendererFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = AudioRendererFactory.cpp; path = xbmc/cores/AudioRenderers/AudioRendererFactory.cpp; sourceTree = SOURCE_ROOT; };
- F5A00B090EFDDE5F00CD59F3 /* AudioRendererFactory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AudioRendererFactory.h; path = xbmc/cores/AudioRenderers/AudioRendererFactory.h; sourceTree = SOURCE_ROOT; };
- F5A00B240EFDE44100CD59F3 /* NullDirectSound.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = NullDirectSound.cpp; path = xbmc/cores/AudioRenderers/NullDirectSound.cpp; sourceTree = SOURCE_ROOT; };
- F5A00B250EFDE44100CD59F3 /* NullDirectSound.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = NullDirectSound.h; path = xbmc/cores/AudioRenderers/NullDirectSound.h; sourceTree = SOURCE_ROOT; };
F5A1CBD20F6B06CF00A96ABD /* XBMC */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = XBMC; sourceTree = BUILT_PRODUCTS_DIR; };
F5A7A700112893E50059D6AA /* AnnouncementManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnnouncementManager.cpp; sourceTree = "<group>"; };
F5A7A701112893E50059D6AA /* AnnouncementManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementManager.h; sourceTree = "<group>"; };
@@ -4012,9 +4068,6 @@
F5F245D91112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDAudioCodecPassthroughFFmpeg.h; sourceTree = "<group>"; };
F5F245EC1112C9AB009126C6 /* FileUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileUtils.cpp; sourceTree = "<group>"; };
F5F245ED1112C9AB009126C6 /* FileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileUtils.h; sourceTree = "<group>"; };
- F5F24E8311232488009126C6 /* DVDAudioEncoderFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DVDAudioEncoderFFmpeg.cpp; path = Encoders/DVDAudioEncoderFFmpeg.cpp; sourceTree = "<group>"; };
- F5F24E8411232488009126C6 /* IDVDAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDVDAudioEncoder.h; path = Encoders/IDVDAudioEncoder.h; sourceTree = "<group>"; };
- F5F24E8511232488009126C6 /* DVDAudioEncoderFFmpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVDAudioEncoderFFmpeg.h; path = Encoders/DVDAudioEncoderFFmpeg.h; sourceTree = "<group>"; };
F5F2EF490E593E0D0092C37F /* DVDFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DVDFileInfo.h; sourceTree = "<group>"; };
F5F2EF4A0E593E0D0092C37F /* DVDFileInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DVDFileInfo.cpp; sourceTree = "<group>"; };
F5F8E1D80E427E8000A8E96F /* VGMCodec.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = VGMCodec.h; sourceTree = "<group>"; };
@@ -4205,8 +4258,6 @@
children = (
18B7C7541294222E009E7A26 /* AnimatedGif.cpp */,
18B7C6F61294222D009E7A26 /* AnimatedGif.h */,
- 18B7C7551294222E009E7A26 /* AudioContext.cpp */,
- 18B7C6F71294222D009E7A26 /* AudioContext.h */,
18B7C7561294222E009E7A26 /* D3DResource.cpp */,
18B7C6F81294222D009E7A26 /* D3DResource.h */,
18B7C7571294222E009E7A26 /* DDSImage.cpp */,
@@ -4317,8 +4368,6 @@
18B7C72D1294222D009E7A26 /* GUIShader.h */,
18B7C7881294222E009E7A26 /* GUISliderControl.cpp */,
18B7C72E1294222D009E7A26 /* GUISliderControl.h */,
- 18B7C7891294222E009E7A26 /* GUISound.cpp */,
- 18B7C72F1294222D009E7A26 /* GUISound.h */,
18B7C78A1294222E009E7A26 /* GUISpinControl.cpp */,
18B7C7301294222D009E7A26 /* GUISpinControl.h */,
18B7C78B1294222E009E7A26 /* GUISpinControlEx.cpp */,
@@ -5134,6 +5183,102 @@
path = websocket;
sourceTree = "<group>";
};
+ DFB65F6315373AE7006B8FF1 /* AudioEngine */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65F6915373AE7006B8FF1 /* Encoders */,
+ DFB65F6C15373AE7006B8FF1 /* Engines */,
+ DFB65F8815373AE7006B8FF1 /* Interfaces */,
+ DFB65F9215373AE7006B8FF1 /* PostProc */,
+ DFB65FA215373AE7006B8FF1 /* Utils */,
+ DFB65F6415373AE7006B8FF1 /* AEAudioFormat.h */,
+ DFB65F6515373AE7006B8FF1 /* AEFactory.cpp */,
+ DFB65F6615373AE7006B8FF1 /* AEFactory.h */,
+ );
+ path = AudioEngine;
+ sourceTree = "<group>";
+ };
+ DFB65F6915373AE7006B8FF1 /* Encoders */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */,
+ DFB65F6B15373AE7006B8FF1 /* AEEncoderFFmpeg.h */,
+ );
+ path = Encoders;
+ sourceTree = "<group>";
+ };
+ DFB65F6C15373AE7006B8FF1 /* Engines */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */,
+ DFB65F6E15373AE7006B8FF1 /* CoreAudioAE.h */,
+ DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */,
+ DFB65F7015373AE7006B8FF1 /* CoreAudioAEHAL.h */,
+ DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */,
+ DFB65F7215373AE7006B8FF1 /* CoreAudioAEHALIOS.h */,
+ DFB65F7315373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp */,
+ DFB65F7415373AE7006B8FF1 /* CoreAudioAEHALOSX.h */,
+ DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */,
+ DFB65F7615373AE7006B8FF1 /* CoreAudioAESound.h */,
+ DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */,
+ DFB65F7815373AE7006B8FF1 /* CoreAudioAEStream.h */,
+ DFB65F7915373AE7006B8FF1 /* CoreAudioRingBuffer.h */,
+ DFB65F7A15373AE7006B8FF1 /* ICoreAudioAEHAL.h */,
+ DFB65F7B15373AE7006B8FF1 /* ICoreAudioSource.h */,
+ );
+ path = Engines;
+ sourceTree = "<group>";
+ };
+ DFB65F8815373AE7006B8FF1 /* Interfaces */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65F8915373AE7006B8FF1 /* AE.h */,
+ DFB65F8A15373AE7006B8FF1 /* AEEncoder.h */,
+ DFB65F8B15373AE7006B8FF1 /* AEPostProc.h */,
+ DFB65F8C15373AE7006B8FF1 /* AESink.h */,
+ DFB65F8D15373AE7006B8FF1 /* AESound.h */,
+ DFB65F8E15373AE7006B8FF1 /* AEStream.h */,
+ DFB65F8F15373AE7006B8FF1 /* ThreadedAE.h */,
+ );
+ path = Interfaces;
+ sourceTree = "<group>";
+ };
+ DFB65F9215373AE7006B8FF1 /* PostProc */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65F9315373AE7006B8FF1 /* AEPPAnimationFade.cpp */,
+ DFB65F9415373AE7006B8FF1 /* AEPPAnimationFade.h */,
+ );
+ path = PostProc;
+ sourceTree = "<group>";
+ };
+ DFB65FA215373AE7006B8FF1 /* Utils */ = {
+ isa = PBXGroup;
+ children = (
+ DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */,
+ DFB65FA415373AE7006B8FF1 /* AEBitstreamPacker.h */,
+ DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */,
+ DFB65FA615373AE7006B8FF1 /* AEBuffer.h */,
+ DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */,
+ DFB65FA815373AE7006B8FF1 /* AEChannelInfo.h */,
+ DFB65FA915373AE7006B8FF1 /* AEConvert.cpp */,
+ DFB65FAA15373AE7006B8FF1 /* AEConvert.h */,
+ 7C0B98A1154B79C30065A238 /* AEDeviceInfo.cpp */,
+ 7C0B98A2154B79C30065A238 /* AEDeviceInfo.h */,
+ DFB65FAB15373AE7006B8FF1 /* AEPackIEC61937.cpp */,
+ DFB65FAC15373AE7006B8FF1 /* AEPackIEC61937.h */,
+ DFB65FAD15373AE7006B8FF1 /* AERemap.cpp */,
+ DFB65FAE15373AE7006B8FF1 /* AERemap.h */,
+ DFB65FAF15373AE7006B8FF1 /* AEStreamInfo.cpp */,
+ DFB65FB015373AE7006B8FF1 /* AEStreamInfo.h */,
+ DFB65FB115373AE7006B8FF1 /* AEUtil.cpp */,
+ DFB65FB215373AE7006B8FF1 /* AEUtil.h */,
+ DFB65FB315373AE7006B8FF1 /* AEWAVLoader.cpp */,
+ DFB65FB415373AE7006B8FF1 /* AEWAVLoader.h */,
+ );
+ path = Utils;
+ sourceTree = "<group>";
+ };
DFCA6AB8152245CD000BFAAE /* httprequesthandler */ = {
isa = PBXGroup;
children = (
@@ -5160,8 +5305,6 @@
F57A1D1D1329B15300498CC7 /* AutoPool.mm */,
F5EA05C30F73381A005C2EC5 /* CocoaInterface.h */,
F5EA05C00F733812005C2EC5 /* CocoaInterface.mm */,
- 83A72B920FBC8DFF00171871 /* CoreAudio.cpp */,
- 83A72B930FBC8DFF00171871 /* CoreAudio.h */,
F5B13C8B1334056B0045076D /* DarwinUtils.h */,
F5B13C8C1334056B0045076D /* DarwinUtils.mm */,
F5ACB5370FC3DF3D00AAA056 /* eprintf.cpp */,
@@ -5317,7 +5460,7 @@
E38E149A0D25F9F900618676 /* cores */ = {
isa = PBXGroup;
children = (
- F5A00B060EFDDDB700CD59F3 /* AudioRenderers */,
+ DFB65F6315373AE7006B8FF1 /* AudioEngine */,
E38E149D0D25F9F900618676 /* DllLoader */,
E38E14F80D25F9F900618676 /* dvdplayer */,
7C5608C30F1754930056433A /* ExternalPlayer */,
@@ -5458,7 +5601,6 @@
E38E15010D25F9F900618676 /* Audio */ = {
isa = PBXGroup;
children = (
- F5F24E801123242B009126C6 /* Encoders */,
E38E151D0D25F9F900618676 /* libmad */,
E38E15050D25F9F900618676 /* DllLibMad.h */,
E38E15060D25F9F900618676 /* DVDAudioCodec.h */,
@@ -5468,6 +5610,8 @@
E38E15100D25F9F900618676 /* DVDAudioCodecLibMad.h */,
E38E15110D25F9F900618676 /* DVDAudioCodecLPcm.cpp */,
E38E15120D25F9F900618676 /* DVDAudioCodecLPcm.h */,
+ DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */,
+ DFB6610715374E80006B8FF1 /* DVDAudioCodecPassthrough.h */,
F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */,
F5F245D91112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.h */,
E38E15150D25F9F900618676 /* DVDAudioCodecPcm.cpp */,
@@ -6752,10 +6896,6 @@
F5F8E1E70E427F6700A8E96F /* md5.h */,
188F75FC152217BC009870CE /* Mime.cpp */,
188F75FD152217BC009870CE /* Mime.h */,
- E38E1E6D0D25F9FD00618676 /* PCMAmplifier.cpp */,
- E38E1E6E0D25F9FD00618676 /* PCMAmplifier.h */,
- 18CCEAEC1112F5B800615FC6 /* PCMRemap.cpp */,
- 18CCEAED1112F5B800615FC6 /* PCMRemap.h */,
E38E1E6F0D25F9FD00618676 /* PerformanceSample.cpp */,
E38E1E700D25F9FD00618676 /* PerformanceSample.h */,
E38E1E710D25F9FD00618676 /* PerformanceStats.cpp */,
@@ -6855,20 +6995,6 @@
name = "internal libs";
sourceTree = "<group>";
};
- F5A00B060EFDDDB700CD59F3 /* AudioRenderers */ = {
- isa = PBXGroup;
- children = (
- F5A00B070EFDDDFC00CD59F3 /* AudioRendererFactory.cpp */,
- F5A00B090EFDDE5F00CD59F3 /* AudioRendererFactory.h */,
- 83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */,
- 83A72B8F0FBC8DB000171871 /* CoreAudioRenderer.h */,
- 83A72B900FBC8DB000171871 /* IAudioRenderer.h */,
- F5A00B240EFDE44100CD59F3 /* NullDirectSound.cpp */,
- F5A00B250EFDE44100CD59F3 /* NullDirectSound.h */,
- );
- name = AudioRenderers;
- sourceTree = "<group>";
- };
F5AE406E13415D8C0004BD79 /* http-api */ = {
isa = PBXGroup;
children = (
@@ -7092,16 +7218,6 @@
name = playercorefactory;
sourceTree = "<group>";
};
- F5F24E801123242B009126C6 /* Encoders */ = {
- isa = PBXGroup;
- children = (
- F5F24E8311232488009126C6 /* DVDAudioEncoderFFmpeg.cpp */,
- F5F24E8511232488009126C6 /* DVDAudioEncoderFFmpeg.h */,
- F5F24E8411232488009126C6 /* IDVDAudioEncoder.h */,
- );
- name = Encoders;
- sourceTree = "<group>";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -7636,7 +7752,6 @@
E38E22E40D25F9FE00618676 /* MusicAlbumInfo.cpp in Sources */,
E38E22E50D25F9FE00618676 /* MusicInfoScraper.cpp in Sources */,
E38E22E70D25F9FE00618676 /* Network.cpp in Sources */,
- E38E22E80D25F9FE00618676 /* PCMAmplifier.cpp in Sources */,
E38E22E90D25F9FE00618676 /* PerformanceSample.cpp in Sources */,
E38E22EA0D25F9FE00618676 /* PerformanceStats.cpp in Sources */,
E38E22EB0D25F9FE00618676 /* RegExp.cpp in Sources */,
@@ -7738,8 +7853,6 @@
F5FAB0710EFABAC800BAD4AE /* VTPFile.cpp in Sources */,
F5FAB0760EFABE2C00BAD4AE /* VTPDirectory.cpp in Sources */,
F5FAB07A0EFABE4A00BAD4AE /* VTPSession.cpp in Sources */,
- F5A00B080EFDDDFC00CD59F3 /* AudioRendererFactory.cpp in Sources */,
- F5A00B260EFDE44100CD59F3 /* NullDirectSound.cpp in Sources */,
7C5608C70F1754930056433A /* ExternalPlayer.cpp in Sources */,
F584E12E0F257C5100DB26A5 /* HTTPDirectory.cpp in Sources */,
F54C51D20F1E783200D46E3C /* GUIDialogKaraokeSongSelector.cpp in Sources */,
@@ -7779,8 +7892,6 @@
F59876C00FBA351D008EF4FB /* VideoReferenceClock.cpp in Sources */,
F5987B250FBB9682008EF4FB /* librefmscrobbler.cpp in Sources */,
F5987B260FBB9682008EF4FB /* lastfmscrobbler.cpp in Sources */,
- 83A72B910FBC8DB000171871 /* CoreAudioRenderer.cpp in Sources */,
- 83A72B940FBC8DFF00171871 /* CoreAudio.cpp in Sources */,
83A72B970FBC8E3B00171871 /* LockFree.cpp in Sources */,
F5987F050FBDF274008EF4FB /* DPMSSupport.cpp in Sources */,
F5987FDB0FBE2DFD008EF4FB /* PAPlayer.cpp in Sources */,
@@ -7886,8 +7997,6 @@
F5F244651110DC6B009126C6 /* FileOperationJob.cpp in Sources */,
F5F245DA1112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
F5F245EE1112C9AB009126C6 /* FileUtils.cpp in Sources */,
- 18CCEAEE1112F5B800615FC6 /* PCMRemap.cpp in Sources */,
- F5F24E8611232488009126C6 /* DVDAudioEncoderFFmpeg.cpp in Sources */,
F5A7A702112893E50059D6AA /* AnnouncementManager.cpp in Sources */,
F5A7A85B112908F00059D6AA /* WebServer.cpp in Sources */,
7C7B2B301134F36400713D6D /* mysqldataset.cpp in Sources */,
@@ -7931,7 +8040,6 @@
18B7C3A112942114009E7A26 /* GUIWindowSettingsScreenCalibration.cpp in Sources */,
18B7C3A812942132009E7A26 /* AdvancedSettings.cpp in Sources */,
18B7C7A91294222E009E7A26 /* AnimatedGif.cpp in Sources */,
- 18B7C7AA1294222E009E7A26 /* AudioContext.cpp in Sources */,
18B7C7AB1294222E009E7A26 /* D3DResource.cpp in Sources */,
18B7C7AC1294222E009E7A26 /* DDSImage.cpp in Sources */,
18B7C7AD1294222E009E7A26 /* DirectXGraphics.cpp in Sources */,
@@ -7982,7 +8090,6 @@
18B7C7DB1294222E009E7A26 /* GUISettingsSliderControl.cpp in Sources */,
18B7C7DC1294222E009E7A26 /* GUIShader.cpp in Sources */,
18B7C7DD1294222E009E7A26 /* GUISliderControl.cpp in Sources */,
- 18B7C7DE1294222E009E7A26 /* GUISound.cpp in Sources */,
18B7C7DF1294222E009E7A26 /* GUISpinControl.cpp in Sources */,
18B7C7E01294222E009E7A26 /* GUISpinControlEx.cpp in Sources */,
18B7C7E11294222E009E7A26 /* GUIStandardWindow.cpp in Sources */,
@@ -8195,6 +8302,26 @@
F5ED8D6C1551F91400842059 /* BlurayDirectory.cpp in Sources */,
F5ED908815538DCE00842059 /* XBMCTinyXML.cpp in Sources */,
F5ED908E15538E2300842059 /* POUtils.cpp in Sources */,
+ DFB65FB515373AE7006B8FF1 /* AEFactory.cpp in Sources */,
+ DFB65FB715373AE7006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */,
+ DFB65FB815373AE7006B8FF1 /* CoreAudioAE.cpp in Sources */,
+ DFB65FB915373AE7006B8FF1 /* CoreAudioAEHAL.cpp in Sources */,
+ DFB65FBA15373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */,
+ DFB65FBB15373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */,
+ DFB65FBC15373AE7006B8FF1 /* CoreAudioAESound.cpp in Sources */,
+ DFB65FBD15373AE7006B8FF1 /* CoreAudioAEStream.cpp in Sources */,
+ DFB65FC515373AE7006B8FF1 /* AEPPAnimationFade.cpp in Sources */,
+ DFB65FCC15373AE7006B8FF1 /* AEBitstreamPacker.cpp in Sources */,
+ DFB65FCD15373AE7006B8FF1 /* AEBuffer.cpp in Sources */,
+ DFB65FCE15373AE7006B8FF1 /* AEChannelInfo.cpp in Sources */,
+ DFB65FCF15373AE7006B8FF1 /* AEConvert.cpp in Sources */,
+ DFB65FD015373AE7006B8FF1 /* AEPackIEC61937.cpp in Sources */,
+ DFB65FD115373AE7006B8FF1 /* AERemap.cpp in Sources */,
+ DFB65FD215373AE7006B8FF1 /* AEStreamInfo.cpp in Sources */,
+ DFB65FD315373AE7006B8FF1 /* AEUtil.cpp in Sources */,
+ DFB65FD415373AE7006B8FF1 /* AEWAVLoader.cpp in Sources */,
+ DFB6610915374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */,
+ 7C0B98A4154B79C30065A238 /* AEDeviceInfo.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -8577,7 +8704,6 @@
F5A1CAD60F6B06CF00A96ABD /* MusicAlbumInfo.cpp in Sources */,
F5A1CAD70F6B06CF00A96ABD /* MusicInfoScraper.cpp in Sources */,
F5A1CAD90F6B06CF00A96ABD /* Network.cpp in Sources */,
- F5A1CADA0F6B06CF00A96ABD /* PCMAmplifier.cpp in Sources */,
F5A1CADB0F6B06CF00A96ABD /* PerformanceSample.cpp in Sources */,
F5A1CADC0F6B06CF00A96ABD /* PerformanceStats.cpp in Sources */,
F5A1CADD0F6B06CF00A96ABD /* RegExp.cpp in Sources */,
@@ -8679,8 +8805,6 @@
F5A1CB7C0F6B06CF00A96ABD /* VTPFile.cpp in Sources */,
F5A1CB7D0F6B06CF00A96ABD /* VTPDirectory.cpp in Sources */,
F5A1CB7E0F6B06CF00A96ABD /* VTPSession.cpp in Sources */,
- F5A1CB7F0F6B06CF00A96ABD /* AudioRendererFactory.cpp in Sources */,
- F5A1CB800F6B06CF00A96ABD /* NullDirectSound.cpp in Sources */,
F5A1CB810F6B06CF00A96ABD /* ExternalPlayer.cpp in Sources */,
F5A1CB830F6B06CF00A96ABD /* HTTPDirectory.cpp in Sources */,
F5A1CB840F6B06CF00A96ABD /* GUIDialogKaraokeSongSelector.cpp in Sources */,
@@ -8722,8 +8846,6 @@
F5987B280FBB9682008EF4FB /* lastfmscrobbler.cpp in Sources */,
F5987F060FBDF274008EF4FB /* DPMSSupport.cpp in Sources */,
43248C4E0FBE224000B88866 /* LockFree.cpp in Sources */,
- 43248C510FBE224C00B88866 /* CoreAudio.cpp in Sources */,
- 43248C520FBE224E00B88866 /* CoreAudioRenderer.cpp in Sources */,
F5987FDC0FBE2DFD008EF4FB /* PAPlayer.cpp in Sources */,
F548786E0FE060FF00E506FD /* DVDSubtitleParserMPL2.cpp in Sources */,
F5487B4D0FE6F02700E506FD /* StreamDetails.cpp in Sources */,
@@ -8825,8 +8947,6 @@
F5F244661110DC6B009126C6 /* FileOperationJob.cpp in Sources */,
F5F245DB1112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
F5F245EF1112C9AB009126C6 /* FileUtils.cpp in Sources */,
- 18CCEAEF1112F5B800615FC6 /* PCMRemap.cpp in Sources */,
- F5F24E8711232488009126C6 /* DVDAudioEncoderFFmpeg.cpp in Sources */,
F5A7A703112893E50059D6AA /* AnnouncementManager.cpp in Sources */,
F5A7A85C112908F00059D6AA /* WebServer.cpp in Sources */,
7C7B2B311134F36400713D6D /* mysqldataset.cpp in Sources */,
@@ -8871,7 +8991,6 @@
18B7C3A512942114009E7A26 /* GUIWindowSettingsScreenCalibration.cpp in Sources */,
18B7C3A912942132009E7A26 /* AdvancedSettings.cpp in Sources */,
18B7C7FE1294222E009E7A26 /* AnimatedGif.cpp in Sources */,
- 18B7C7FF1294222E009E7A26 /* AudioContext.cpp in Sources */,
18B7C8001294222E009E7A26 /* D3DResource.cpp in Sources */,
18B7C8011294222E009E7A26 /* DDSImage.cpp in Sources */,
18B7C8021294222E009E7A26 /* DirectXGraphics.cpp in Sources */,
@@ -8922,7 +9041,6 @@
18B7C8301294222E009E7A26 /* GUISettingsSliderControl.cpp in Sources */,
18B7C8311294222E009E7A26 /* GUIShader.cpp in Sources */,
18B7C8321294222E009E7A26 /* GUISliderControl.cpp in Sources */,
- 18B7C8331294222E009E7A26 /* GUISound.cpp in Sources */,
18B7C8341294222E009E7A26 /* GUISpinControl.cpp in Sources */,
18B7C8351294222E009E7A26 /* GUISpinControlEx.cpp in Sources */,
18B7C8361294222E009E7A26 /* GUIStandardWindow.cpp in Sources */,
@@ -9134,6 +9252,26 @@
F5ED8D6D1551F91400842059 /* BlurayDirectory.cpp in Sources */,
F5ED908915538DCE00842059 /* XBMCTinyXML.cpp in Sources */,
F5ED908F15538E2300842059 /* POUtils.cpp in Sources */,
+ DFB65FD515373AE7006B8FF1 /* AEFactory.cpp in Sources */,
+ DFB65FD715373AE7006B8FF1 /* AEEncoderFFmpeg.cpp in Sources */,
+ DFB65FD815373AE7006B8FF1 /* CoreAudioAE.cpp in Sources */,
+ DFB65FD915373AE7006B8FF1 /* CoreAudioAEHAL.cpp in Sources */,
+ DFB65FDA15373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp in Sources */,
+ DFB65FDB15373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp in Sources */,
+ DFB65FDC15373AE7006B8FF1 /* CoreAudioAESound.cpp in Sources */,
+ DFB65FDD15373AE7006B8FF1 /* CoreAudioAEStream.cpp in Sources */,
+ DFB65FE515373AE7006B8FF1 /* AEPPAnimationFade.cpp in Sources */,
+ DFB65FEC15373AE7006B8FF1 /* AEBitstreamPacker.cpp in Sources */,
+ DFB65FED15373AE7006B8FF1 /* AEBuffer.cpp in Sources */,
+ DFB65FEE15373AE7006B8FF1 /* AEChannelInfo.cpp in Sources */,
+ DFB65FEF15373AE7006B8FF1 /* AEConvert.cpp in Sources */,
+ DFB65FF015373AE7006B8FF1 /* AEPackIEC61937.cpp in Sources */,
+ DFB65FF115373AE7006B8FF1 /* AERemap.cpp in Sources */,
+ DFB65FF215373AE7006B8FF1 /* AEStreamInfo.cpp in Sources */,
+ DFB65FF315373AE7006B8FF1 /* AEUtil.cpp in Sources */,
+ DFB65FF415373AE7006B8FF1 /* AEWAVLoader.cpp in Sources */,
+ DFB6610B15374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */,
+ 7C0B98A3154B79C30065A238 /* AEDeviceInfo.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -9190,6 +9328,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
@@ -9305,6 +9445,8 @@
xbmc/osx,
xbmc/linux,
xbmc/cores/dvdplayer,
+ xbmc/cores/AudioEngine,
+ xbmc/cores/AudioEngine/Utils,
lib,
lib/ffmpeg,
$XBMC_DEPENDS/include,
diff --git a/configure.in b/configure.in
index 394a7188f1..85e5d1016b 100755
--- a/configure.in
+++ b/configure.in
@@ -77,6 +77,7 @@ xrandr_not_found="== Could not find libXRandR. SDL will be used for resolution s
xrandr_disabled="== XRandR support disabled. SDL will be used for resolution support. =="
goom_enabled="== GOOM enabled. =="
goom_disabled="== GOOM disabled. =="
+alsa_disabled="== ALSA support disabled. =="
rsxs_enabled="== RSXS enabled. =="
rsxs_disabled="== RSXS disabled. =="
projectm_enabled="== ProjectM enabled. =="
@@ -277,11 +278,17 @@ AC_ARG_ENABLE([ccache],
[use_ccache=$enableval],
[use_ccache=auto])
+AC_ARG_ENABLE([alsa],
+ [AS_HELP_STRING([--disable-alsa],
+ [disable ALSA support (only for linux/freebsd)])],
+ [use_alsa=$enableval],
+ [use_alsa=yes])
+
AC_ARG_ENABLE([pulse],
[AS_HELP_STRING([--enable-pulse],
- [enable PulseAudio support (default is auto)])],
+ [enable PulseAudio support (default is no)])],
[use_pulse=$enableval],
- [use_pulse=auto])
+ [use_pulse=no])
AC_ARG_ENABLE([rtmp],
[AS_HELP_STRING([--enable-rtmp],
@@ -785,7 +792,6 @@ AS_CASE([x$use_libbluray],
if test "$host_vendor" = "apple" ; then
AC_CHECK_LIB([iconv], [main],, AC_MSG_ERROR($missing_library))
if test "$use_arch" != "arm"; then
- AC_CHECK_LIB([SDL_mixer],[main],, AC_MSG_ERROR($missing_library))
AC_CHECK_LIB([SDL], [main],, AC_MSG_ERROR($missing_library))
AC_DEFINE([HAVE_SDL],[1],["Define to 1 if using sdl"])
fi
@@ -809,7 +815,6 @@ else
PKG_CHECK_MODULES([SDL], [sdl],
[INCLUDES="$INCLUDES $SDL_CFLAGS"; LIBS="$LIBS $SDL_LIBS"],
AC_MSG_ERROR($missing_library))
- AC_CHECK_LIB([SDL_mixer], [main],, AC_MSG_ERROR($missing_library))
AC_CHECK_LIB([SDL_image], [main],, AC_MSG_ERROR($missing_library))
AC_DEFINE([HAVE_SDL],[1],["Define to 1 if using sdl"])
fi
@@ -866,6 +871,17 @@ if test "$use_optical_drive" = "yes"; then
AC_DEFINE([HAS_DVD_DRIVE], [1], [Define to 1 to have optical drive support])
fi
+# Alsa
+if test "$use_alsa" = "yes" && test "$host_vendor" != "apple"; then
+ PKG_CHECK_MODULES([ALSA], [alsa],
+ [INCLUDES="$INCLUDES $ALSA_CFLAGS"; LIBS="$LIBS $ALSA_LIBS"],
+ AC_MSG_ERROR($missing_library))
+ AC_DEFINE([HAS_ALSA], [1], [Define to 0 to disable ALSA support])
+else
+ use_alsa="no"
+ AC_MSG_RESULT($alsa_disabled)
+fi
+
# PulseAudio
if test "x$use_pulse" != "xno"; then
if test "$host_vendor" = "apple" ; then
@@ -1706,6 +1722,12 @@ else
final_message="$final_message\n ccache:\tNo"
fi
+if test "$use_alsa" = "yes"; then
+ final_message="$final_message\n ALSA Support:\tYes"
+else
+ final_message="$final_message\n ALSA Support:\tNo"
+fi
+
if test "x$use_pulse" != "xno"; then
XBMC_STANDALONE_SH_PULSE=tools/Linux/xbmc-standalone.sh.pulse
final_message="$final_message\n PulseAudio:\tYes"
@@ -1887,7 +1909,7 @@ OUTPUT_FILES="Makefile \
xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile \
xbmc/cores/dvdplayer/DVDDemuxers/Makefile \
xbmc/cores/dvdplayer/DVDSubtitles/Makefile \
- xbmc/cores/AudioRenderers/Makefile \
+ xbmc/cores/AudioEngine/Makefile \
xbmc/cores/paplayer/Makefile \
lib/timidity/Makefile \
lib/xbadpcm/Makefile \
diff --git a/language/English/strings.po b/language/English/strings.po
index e9f4cca626..a56431a3a2 100644
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -1278,7 +1278,17 @@ msgstr ""
msgid "Boost volume level on downmix"
msgstr ""
-#empty strings from id 347 to 349
+#: id:347
+msgid "- DTS-HD capable receiver"
+msgstr ""
+
+#: id:348
+msgid "- Multichannel LPCM capable receiver"
+msgstr ""
+
+#: id:349
+msgid "- TrueHD capable receiver"
+msgstr ""
#: id:350
msgctxt "Auto context with id 350"
@@ -1520,7 +1530,7 @@ msgstr ""
msgid "Humidity"
msgstr ""
-#empty strings from id 407 to 408
+#empty strings from 407 to 408
#: id:409
msgid "Defaults"
@@ -9343,8 +9353,27 @@ msgstr ""
msgid "7.1"
msgstr ""
-#34112-34200 reserved for future use
-#empty strings from id 34111 to 34200
+#34111-34119 reserved for future use
+#empty strings from id 34111 to 34119
+
+#: id:34120
+msgid "Play GUI sounds"
+msgstr ""
+
+#: id:34121
+msgid "Only when playback stopped"
+msgstr ""
+
+#: id:34122
+msgid "Always"
+msgstr ""
+
+#: id:34123
+msgid "Never"
+msgstr ""
+
+#34124-34200 reserved for future use
+#empty strings from id 34124 to 34200
#: id:34201
msgid "Can't find a next item to play"
diff --git a/language/English/strings.xml b/language/English/strings.xml
index ccb4a46963..a820477fa7 100644
--- a/language/English/strings.xml
+++ b/language/English/strings.xml
@@ -220,7 +220,7 @@
<string id="249">Music</string>
<string id="250">Visualization</string>
<string id="251">Select destination directory</string>
- <string id="252">Output stereo to all speakers</string>
+ <string id="252">Enable stereo upmix</string>
<string id="253">Number of channels</string>
<string id="254">- DTS capable receiver</string>
<string id="255">CDDB</string>
@@ -309,6 +309,9 @@
<string id="344">Actors</string>
<string id="345">Year</string>
<string id="346">Boost volume level on downmix</string>
+ <string id="347">Use exclusive mode</string>
+ <string id="348">- Multichannel LPCM capable receiver</string>
+ <string id="349">- TrueHD capable receiver</string>
<string id="350">Programs</string>
<string id="351">Off</string>
<string id="352">Dim</string>
@@ -366,6 +369,7 @@
<string id="404">Wind</string>
<string id="405">Dew point</string>
<string id="406">Humidity</string>
+ <string id="407">- DTS-HD capable receiver</string>
<string id="409">Defaults</string>
<string id="410">Accessing weather service</string>
@@ -2367,7 +2371,9 @@
<string id="34108">5.1</string>
<string id="34109">7.0</string>
<string id="34110">7.1</string>
- <!-- 34112-34200 reserved for future use -->
+
+ <string id="34120">Enable GUI sounds during playback</string>
+ <!-- 34121-34200 reserved for future use -->
<string id="34201">Can't find a next item to play</string>
<string id="34202">Can't find a previous item to play</string>
diff --git a/lib/DllAvUtil.h b/lib/DllAvUtil.h
index 8ef67dc131..336f53732a 100644
--- a/lib/DllAvUtil.h
+++ b/lib/DllAvUtil.h
@@ -92,6 +92,7 @@ public:
virtual void av_freep(void *ptr)=0;
virtual int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding)=0;
virtual int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)=0;
+ virtual int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size)=0;
virtual const AVCRC* av_crc_get_table(AVCRCId crc_id)=0;
virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)=0;
virtual int av_opt_set(void *obj, const char *name, const char *val, int search_flags)=0;
@@ -124,6 +125,7 @@ public:
virtual void av_freep(void *ptr) { ::av_freep(ptr); }
virtual int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding d) { return ::av_rescale_rnd(a, b, c, d); }
virtual int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) { return ::av_rescale_q(a, bq, cq); }
+ virtual int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size) { return ::av_crc_init(ctx, le, bits, poly, ctx_size); }
virtual const AVCRC* av_crc_get_table(AVCRCId crc_id) { return ::av_crc_get_table(crc_id); }
virtual uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length) { return ::av_crc(ctx, crc, buffer, length); }
virtual int av_opt_set(void *obj, const char *name, const char *val, int search_flags) { return ::av_opt_set(obj, name, val, search_flags); }
@@ -170,6 +172,7 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
DEFINE_METHOD4(int64_t, av_rescale_rnd, (int64_t p1, int64_t p2, int64_t p3, enum AVRounding p4));
DEFINE_METHOD3(int64_t, av_rescale_q, (int64_t p1, AVRational p2, AVRational p3));
DEFINE_METHOD1(const AVCRC*, av_crc_get_table, (AVCRCId p1))
+ DEFINE_METHOD5(int, av_crc_init, (AVCRC *p1, int p2, int p3, uint32_t p4, int p5));
DEFINE_METHOD4(uint32_t, av_crc, (const AVCRC *p1, uint32_t p2, const uint8_t *p3, size_t p4));
DEFINE_METHOD4(int, av_opt_set, (void *p1, const char *p2, const char *p3, int p4));
DEFINE_METHOD1(AVFifoBuffer*, av_fifo_alloc, (unsigned int p1))
@@ -195,6 +198,7 @@ class DllAvUtilBase : public DllDynamic, DllAvUtilInterface
RESOLVE_METHOD(av_freep)
RESOLVE_METHOD(av_rescale_rnd)
RESOLVE_METHOD(av_rescale_q)
+ RESOLVE_METHOD(av_crc_init)
RESOLVE_METHOD(av_crc_get_table)
RESOLVE_METHOD(av_crc)
RESOLVE_METHOD(av_opt_set)
diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
index 59145473c1..133f2cec20 100644
--- a/project/VS2010Express/XBMC.vcxproj
+++ b/project/VS2010Express/XBMC.vcxproj
@@ -147,7 +147,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
- <AdditionalIncludeDirectories>..\..\;..\..\xbmc\;..\..\xbmc\cores\dvdplayer;..\..\xbmc\win32;..\..\lib;..\..\lib\ffmpeg;..\..\lib\ffmpeg\include-xbmc-win32;..\..\lib\liblame\include;..\..\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\lib\libUPnP\Platinum\Source\Platinum;..\..\lib\libUPnP\Platinum\Source\Core;..\..\lib\libUPnP\Neptune\Source\Core;..\..\lib\libUPnP\Neptune\Source\System\Win32;..\..\lib\win32\pcre;..\..\lib\win32</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>..\..\;..\..\xbmc\;..\..\xbmc\cores\dvdplayer;..\..\xbmc\win32;..\..\lib;..\..\lib\ffmpeg;..\..\lib\ffmpeg\include-xbmc-win32;..\..\lib\liblame\include;..\..\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\lib\libUPnP\Platinum\Source\Platinum;..\..\lib\libUPnP\Platinum\Source\Core;..\..\lib\libUPnP\Neptune\Source\Core;..\..\lib\libUPnP\Neptune\Source\System\Win32;..\..\lib\win32\pcre;..\..\lib\win32;..\..\xbmc\cores\AudioEngine\;..\..\xbmc\cores\AudioEngine\Utils\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_WINDOWS;_WINDOWS;_MSVC;WIN32;_DEBUG;_WIN32_WINNT=0x0501;NTDDI_VERSION=0x05010300;NOMINMAX;_USE_32BIT_TIME_T;HAS_DX;D3D_DEBUG_INFO;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Async</ExceptionHandling>
@@ -194,7 +194,7 @@
<InlineFunctionExpansion>Default</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
- <AdditionalIncludeDirectories>..\..\;..\..\xbmc\;..\..\xbmc\win32\;..\..\xbmc\cores\dvdplayer;..\..\lib;..\..\lib\ffmpeg;..\..\lib\ffmpeg\include-xbmc-win32;..\..\lib\liblame\include;..\..\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\lib\libUPnP\Platinum\Source\Platinum;..\..\lib\libUPnP\Platinum\Source\Core;..\..\lib\libUPnP\Neptune\Source\Core;..\..\lib\libUPnP\Neptune\Source\System\Win32;..\..\lib\win32\pcre;..\..\lib\win32</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>..\..\;..\..\xbmc\;..\..\xbmc\win32\;..\..\xbmc\cores\dvdplayer;..\..\lib;..\..\lib\ffmpeg;..\..\lib\ffmpeg\include-xbmc-win32;..\..\lib\liblame\include;..\..\lib\libUPnP\Platinum\Source\Devices\MediaRenderer;..\..\lib\libUPnP\Platinum\Source\Devices\MediaConnect;..\..\lib\libUPnP\Platinum\Source\Devices\MediaServer;..\..\lib\libUPnP\Platinum\Source\Platinum;..\..\lib\libUPnP\Platinum\Source\Core;..\..\lib\libUPnP\Neptune\Source\Core;..\..\lib\libUPnP\Neptune\Source\System\Win32;..\..\lib\win32\pcre;..\..\lib\win32;..\..\xbmc\cores\AudioEngine</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_WINDOWS;_WINDOWS;_MSVC;WIN32;NDEBUG;_WIN32_WINNT=0x0501;NTDDI_VERSION=0x05010300;NOMINMAX;_USE_32BIT_TIME_T;HAS_DX;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
@@ -297,6 +297,28 @@
<ClCompile Include="..\..\xbmc\Autorun.cpp" />
<ClCompile Include="..\..\xbmc\AutoSwitch.cpp" />
<ClCompile Include="..\..\xbmc\BackgroundInfoLoader.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\AEFactory.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAESound.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAEStream.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\PostProc\AEPPAnimationFade.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkDirectSound.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkNULL.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkProfiler.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkWASAPI.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEBitstreamPacker.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEBuffer.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEChannelInfo.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEConvert.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEDeviceInfo.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEPackIEC61937.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AERemap.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEStreamInfo.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEUtil.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEWAVLoader.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthrough.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.cpp" />
<ClCompile Include="..\..\xbmc\cores\paplayer\BXAcodec.cpp" />
@@ -440,7 +462,6 @@
<ClCompile Include="..\..\xbmc\GUIInfoManager.cpp" />
<ClCompile Include="..\..\xbmc\GUILargeTextureManager.cpp" />
<ClCompile Include="..\..\xbmc\guilib\AnimatedGif.cpp" />
- <ClCompile Include="..\..\xbmc\guilib\AudioContext.cpp" />
<ClCompile Include="..\..\xbmc\guilib\D3DResource.cpp" />
<ClCompile Include="..\..\xbmc\guilib\DDSImage.cpp" />
<ClCompile Include="..\..\xbmc\guilib\DirectXGraphics.cpp" />
@@ -500,7 +521,6 @@
<ClCompile Include="..\..\xbmc\guilib\GUISettingsSliderControl.cpp" />
<ClCompile Include="..\..\xbmc\guilib\GUIShader.cpp" />
<ClCompile Include="..\..\xbmc\guilib\GUISliderControl.cpp" />
- <ClCompile Include="..\..\xbmc\guilib\GUISound.cpp" />
<ClCompile Include="..\..\xbmc\guilib\GUISpinControl.cpp" />
<ClCompile Include="..\..\xbmc\guilib\GUISpinControlEx.cpp" />
<ClCompile Include="..\..\xbmc\guilib\GUIStandardWindow.cpp" />
@@ -879,7 +899,36 @@
<ClCompile Include="..\..\xbmc\threads\Event.cpp" />
<ClCompile Include="..\..\xbmc\threads\LockFree.cpp" />
<ClCompile Include="..\..\xbmc\threads\platform\Implementation.cpp" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\IAudioRenderer.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AEAudioFormat.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AEFactory.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAESound.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAEStream.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AE.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEEncoder.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEPostProc.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AESink.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AESound.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEStream.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\ThreadedAE.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\PostProc\AEPPAnimationFade.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkDirectSound.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkNULL.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkProfiler.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkWASAPI.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEBitstreamPacker.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEBuffer.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEChannelInfo.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEConvert.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEDeviceInfo.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEPackIEC61937.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AERemap.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEStreamInfo.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEUtil.h" />
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEWAVLoader.h" />
+ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthrough.h" />
<ClInclude Include="..\..\xbmc\cores\paplayer\PCMCodec.h" />
<ClInclude Include="..\..\xbmc\filesystem\windows\WINFileSMB.h" />
<ClInclude Include="..\..\xbmc\filesystem\windows\WINSMBDirectory.h" />
@@ -1050,7 +1099,6 @@
<ClCompile Include="..\..\xbmc\utils\log.cpp" />
<ClCompile Include="..\..\xbmc\utils\md5.cpp" />
<ClCompile Include="..\..\xbmc\utils\Mime.cpp" />
- <ClCompile Include="..\..\xbmc\utils\PCMAmplifier.cpp" />
<ClCompile Include="..\..\xbmc\utils\PerformanceSample.cpp" />
<ClCompile Include="..\..\xbmc\utils\PerformanceStats.cpp" />
<ClCompile Include="..\..\xbmc\utils\POUtils.cpp" />
@@ -1143,7 +1191,6 @@
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLPcm.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthroughFFmpeg.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.cpp" />
- <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\DVDAudioEncoderFFmpeg.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecCrystalHD.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecFFmpeg.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecLibMpeg2.cpp" />
@@ -1261,12 +1308,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\YUV2RGBShader.cpp" />
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\AudioRendererFactory.cpp" />
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\NullDirectSound.cpp" />
- <ClCompile Include="..\..\xbmc\utils\PCMRemap.cpp" />
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\PulseAudioDirectSound.cpp" />
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\Win32DirectSound.cpp" />
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\Win32WASAPI.cpp" />
<ClCompile Include="..\..\xbmc\cores\ExternalPlayer\ExternalPlayer.cpp" />
<ClCompile Include="..\..\xbmc\cores\playercorefactory\PlayerCoreFactory.cpp" />
<ClCompile Include="..\..\xbmc\cores\playercorefactory\PlayerSelectionRule.cpp" />
@@ -1452,7 +1493,6 @@
<ClInclude Include="..\..\xbmc\GUIInfoManager.h" />
<ClInclude Include="..\..\xbmc\GUILargeTextureManager.h" />
<ClInclude Include="..\..\xbmc\guilib\AnimatedGif.h" />
- <ClInclude Include="..\..\xbmc\guilib\AudioContext.h" />
<ClInclude Include="..\..\xbmc\guilib\D3DResource.h" />
<ClInclude Include="..\..\xbmc\guilib\DDSImage.h" />
<ClInclude Include="..\..\xbmc\guilib\DirectXGraphics.h" />
@@ -1516,7 +1556,6 @@
<ClInclude Include="..\..\xbmc\guilib\GUISettingsSliderControl.h" />
<ClInclude Include="..\..\xbmc\guilib\GUIShader.h" />
<ClInclude Include="..\..\xbmc\guilib\GUISliderControl.h" />
- <ClInclude Include="..\..\xbmc\guilib\GUISound.h" />
<ClInclude Include="..\..\xbmc\guilib\GUISpinControl.h" />
<ClInclude Include="..\..\xbmc\guilib\GUISpinControlEx.h" />
<ClInclude Include="..\..\xbmc\guilib\GUIStandardWindow.h" />
@@ -1854,7 +1893,6 @@
<ClInclude Include="..\..\xbmc\utils\MathUtils.h" />
<ClInclude Include="..\..\xbmc\utils\md5.h" />
<ClInclude Include="..\..\xbmc\utils\Mime.h" />
- <ClInclude Include="..\..\xbmc\utils\PCMAmplifier.h" />
<ClInclude Include="..\..\xbmc\utils\PerformanceSample.h" />
<ClInclude Include="..\..\xbmc\utils\PerformanceStats.h" />
<ClInclude Include="..\..\xbmc\utils\POUtils.h" />
@@ -1959,8 +1997,6 @@
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLPcm.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthroughFFmpeg.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.h" />
- <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\DVDAudioEncoderFFmpeg.h" />
- <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\IDVDAudioEncoder.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DllLibMpeg2.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodec.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecCrystalHD.h" />
@@ -2095,12 +2131,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\YUV2RGBShader.h" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\AudioRendererFactory.h" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\NullDirectSound.h" />
- <ClInclude Include="..\..\xbmc\utils\PCMRemap.h" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\PulseAudioDirectSound.h" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\Win32DirectSound.h" />
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\Win32WASAPI.h" />
<ClInclude Include="..\..\xbmc\cores\ExternalPlayer\ExternalPlayer.h" />
<ClInclude Include="..\..\xbmc\cores\playercorefactory\PlayerCoreConfig.h" />
<ClInclude Include="..\..\xbmc\cores\playercorefactory\PlayerCoreFactory.h" />
diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
index 1923899f5b..0613aa9680 100644
--- a/project/VS2010Express/XBMC.vcxproj.filters
+++ b/project/VS2010Express/XBMC.vcxproj.filters
@@ -16,9 +16,6 @@
<Filter Include="cores\dvdplayer\DVDCodecs\Audio">
<UniqueIdentifier>{5bee29f5-b152-4416-9413-0bed2a669575}</UniqueIdentifier>
</Filter>
- <Filter Include="cores\dvdplayer\DVDCodecs\Audio\Encoders">
- <UniqueIdentifier>{950294bc-2e98-414e-8bf4-57c43fb73b31}</UniqueIdentifier>
- </Filter>
<Filter Include="cores\dvdplayer\DVDCodecs\Video">
<UniqueIdentifier>{09e9057e-7017-4f3d-b5d3-2f5e9a23a53c}</UniqueIdentifier>
</Filter>
@@ -247,6 +244,27 @@
<Filter Include="network\httprequesthandler">
<UniqueIdentifier>{9029e610-aa5a-4414-9e96-1d69f03b3bd8}</UniqueIdentifier>
</Filter>
+ <Filter Include="cores\AudioEngine">
+ <UniqueIdentifier>{19314641-c5c4-49ff-ae42-6ebcc1a6a038}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\Encoders">
+ <UniqueIdentifier>{0aad3f05-0330-4d6f-9407-388b56c9aa24}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\Engines">
+ <UniqueIdentifier>{1354dfbc-8fa8-4621-8fd1-f4a01fdc1c51}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\Interfaces">
+ <UniqueIdentifier>{7382f639-6a03-4343-87cd-5745a838b687}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\PostProc">
+ <UniqueIdentifier>{96532068-4749-4764-b26f-a5d07723dd98}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\Sinks">
+ <UniqueIdentifier>{b71a9c57-2640-4506-b99e-58a9a73dd0e1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="cores\AudioEngine\Utils">
+ <UniqueIdentifier>{775154f3-9284-488f-8f2f-26597f264d0e}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\xbmc\win32\pch.cpp">
@@ -354,9 +372,6 @@
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.cpp">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\DVDAudioEncoderFFmpeg.cpp">
- <Filter>cores\dvdplayer\DVDCodecs\Audio\Encoders</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecCrystalHD.cpp">
<Filter>cores\dvdplayer\DVDCodecs\Video</Filter>
</ClCompile>
@@ -615,24 +630,6 @@
<ClCompile Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\YUV2RGBShader.cpp">
<Filter>cores\VideoRenderers\Shaders</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\AudioRendererFactory.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\NullDirectSound.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
- <ClCompile Include="..\..\xbmc\utils\PCMRemap.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\PulseAudioDirectSound.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\Win32DirectSound.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
- <ClCompile Include="..\..\xbmc\cores\AudioRenderers\Win32WASAPI.cpp">
- <Filter>cores\AudioRenderers</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\cores\ExternalPlayer\ExternalPlayer.cpp">
<Filter>cores\ExternalPlayer</Filter>
</ClCompile>
@@ -994,9 +991,6 @@
<ClCompile Include="..\..\xbmc\guilib\AnimatedGif.cpp">
<Filter>guilib</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\guilib\AudioContext.cpp">
- <Filter>guilib</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\guilib\D3DResource.cpp">
<Filter>guilib</Filter>
</ClCompile>
@@ -1132,9 +1126,6 @@
<ClCompile Include="..\..\xbmc\guilib\GUISliderControl.cpp">
<Filter>guilib</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\guilib\GUISound.cpp">
- <Filter>guilib</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\guilib\GUISpinControl.cpp">
<Filter>guilib</Filter>
</ClCompile>
@@ -1660,9 +1651,6 @@
<ClCompile Include="..\..\xbmc\utils\md5.cpp">
<Filter>utils</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\utils\PCMAmplifier.cpp">
- <Filter>utils</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\utils\PerformanceSample.cpp">
<Filter>utils</Filter>
</ClCompile>
@@ -1801,9 +1789,6 @@
<ClCompile Include="..\..\xbmc\windowing\windows\WinSystemWin32DX.cpp">
<Filter>windowing\windows</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\windowing\windows\WinSystemWin32GL.cpp">
- <Filter>windowing\windows</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\addons\GUIViewStateAddonBrowser.cpp">
<Filter>addons</Filter>
</ClCompile>
@@ -2620,6 +2605,73 @@
<ClCompile Include="..\..\xbmc\network\windows\ZeroconfBrowserWIN.cpp">
<Filter>network\windows</Filter>
</ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.cpp">
+ <Filter>cores\AudioEngine\Encoders</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\AEFactory.cpp">
+ <Filter>cores\AudioEngine</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.cpp">
+ <Filter>cores\AudioEngine</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE.cpp">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAESound.cpp">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAEStream.cpp">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\PostProc\AEPPAnimationFade.cpp">
+ <Filter>cores\AudioEngine\PostProc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkDirectSound.cpp">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkNULL.cpp">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkProfiler.cpp">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkWASAPI.cpp">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEBitstreamPacker.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEBuffer.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEChannelInfo.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEConvert.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEPackIEC61937.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AERemap.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEStreamInfo.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEUtil.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEWAVLoader.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEDeviceInfo.cpp">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\xbmc\windowing\windows\WinSystemWin32GL.cpp" />
+ <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthrough.cpp">
+ <Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -2742,12 +2794,6 @@
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.h">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\DVDAudioEncoderFFmpeg.h">
- <Filter>cores\dvdplayer\DVDCodecs\Audio\Encoders</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\Encoders\IDVDAudioEncoder.h">
- <Filter>cores\dvdplayer\DVDCodecs\Audio\Encoders</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DllLibMpeg2.h">
<Filter>cores\dvdplayer\DVDCodecs\Video</Filter>
</ClInclude>
@@ -3117,24 +3163,6 @@
<ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\YUV2RGBShader.h">
<Filter>cores\VideoRenderers\Shaders</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\AudioRendererFactory.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\NullDirectSound.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\utils\PCMRemap.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\PulseAudioDirectSound.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\Win32DirectSound.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\Win32WASAPI.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\cores\ExternalPlayer\ExternalPlayer.h">
<Filter>cores\ExternalPlayer</Filter>
</ClInclude>
@@ -3524,9 +3552,6 @@
<ClInclude Include="..\..\xbmc\guilib\AnimatedGif.h">
<Filter>guilib</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\guilib\AudioContext.h">
- <Filter>guilib</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\guilib\D3DResource.h">
<Filter>guilib</Filter>
</ClInclude>
@@ -3671,9 +3696,6 @@
<ClInclude Include="..\..\xbmc\guilib\GUISliderControl.h">
<Filter>guilib</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\guilib\GUISound.h">
- <Filter>guilib</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\guilib\GUISpinControl.h">
<Filter>guilib</Filter>
</ClInclude>
@@ -4280,9 +4302,6 @@
<ClInclude Include="..\..\xbmc\utils\md5.h">
<Filter>utils</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\utils\PCMAmplifier.h">
- <Filter>utils</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\utils\PerformanceSample.h">
<Filter>utils</Filter>
</ClInclude>
@@ -5239,9 +5258,6 @@
<ClInclude Include="..\..\xbmc\utils\POUtils.h">
<Filter>utils</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\cores\AudioRenderers\IAudioRenderer.h">
- <Filter>cores\AudioRenderers</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\interfaces\python\xbmcmodule\pythreadstate.h">
<Filter>interfaces\python\xbmcmodule</Filter>
</ClInclude>
@@ -5253,6 +5269,96 @@
<ClInclude Include="..\..\xbmc\network\windows\ZeroconfBrowserWIN.h">
<Filter>network\windows</Filter>
</ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Encoders\AEEncoderFFmpeg.h">
+ <Filter>cores\AudioEngine\Encoders</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AEAudioFormat.h">
+ <Filter>cores\AudioEngine</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AEFactory.h">
+ <Filter>cores\AudioEngine</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\AESinkFactory.h">
+ <Filter>cores\AudioEngine</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAE.h">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAESound.h">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Engines\SoftAEStream.h">
+ <Filter>cores\AudioEngine\Engines</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AE.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEEncoder.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEPostProc.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AESink.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AESound.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\AEStream.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Interfaces\ThreadedAE.h">
+ <Filter>cores\AudioEngine\Interfaces</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\PostProc\AEPPAnimationFade.h">
+ <Filter>cores\AudioEngine\PostProc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkDirectSound.h">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkNULL.h">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkProfiler.h">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Sinks\AESinkWASAPI.h">
+ <Filter>cores\AudioEngine\Sinks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEBitstreamPacker.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEBuffer.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEChannelInfo.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEConvert.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEPackIEC61937.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AERemap.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEStreamInfo.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEUtil.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEWAVLoader.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\AudioEngine\Utils\AEDeviceInfo.h">
+ <Filter>cores\AudioEngine\Utils</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthrough.h">
+ <Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index e9aab1686b..df63910b41 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -31,6 +31,8 @@
#include "pictures/Picture.h"
#include "guilib/TextureManager.h"
#include "cores/dvdplayer/DVDFileInfo.h"
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
#include "PlayListPlayer.h"
#include "Autorun.h"
#include "video/Bookmark.h"
@@ -135,7 +137,6 @@
#include "music/karaoke/GUIDialogKaraokeSongSelector.h"
#include "music/karaoke/GUIWindowKaraokeLyrics.h"
#endif
-#include "guilib/AudioContext.h"
#include "guilib/GUIFontTTF.h"
#include "network/Network.h"
#include "storage/IoSupport.h"
@@ -271,9 +272,6 @@
#define MEASURE_FUNCTION
#endif
-#ifdef HAS_SDL_AUDIO
-#include <SDL/SDL_mixer.h>
-#endif
#ifdef TARGET_WINDOWS
#include <shlobj.h>
#include "win32util.h"
@@ -646,10 +644,6 @@ bool CApplication::Create()
sdlFlags |= SDL_INIT_VIDEO;
#endif
-#ifdef HAS_SDL_AUDIO
- sdlFlags |= SDL_INIT_AUDIO;
-#endif
-
#ifdef HAS_SDL_JOYSTICK
sdlFlags |= SDL_INIT_JOYSTICK;
#endif
@@ -702,6 +696,13 @@ bool CApplication::Create()
g_powerManager.Initialize();
+ // Load the AudioEngine before settings as they need to query the engine
+ if (!CAEFactory::LoadEngine())
+ {
+ CLog::Log(LOGFATAL, "CApplication::Create: Failed to load an AudioEngine");
+ FatalErrorHandler(true, true, true);
+ }
+
CLog::Log(LOGNOTICE, "load settings...");
g_guiSettings.Initialize(); // Initialize default Settings - don't move
@@ -739,6 +740,18 @@ bool CApplication::Create()
if (!g_localizeStrings.Load(strLanguagePath))
FatalErrorHandler(false, false, true);
+ // start the AudioEngine
+ if (!CAEFactory::StartEngine())
+ {
+ CLog::Log(LOGFATAL, "CApplication::Create: Failed to start the AudioEngine");
+ FatalErrorHandler(true, true, true);
+ }
+
+ // restore AE's previous volume state
+ SetHardwareVolume(g_settings.m_fVolumeLevel);
+ CAEFactory::AE->SetMute (g_settings.m_bMute);
+ CAEFactory::AE->SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode"));
+
// start-up Addons Framework
// currently bails out if either cpluff Dll is unavailable or system dir can not be scanned
if (!CAddonMgr::Get().Init())
@@ -1258,17 +1271,6 @@ bool CApplication::Initialize()
CLog::Log(LOGINFO, "removing tempfiles");
CUtil::RemoveTempFiles();
- // Restore volume
- if (g_settings.m_bMute)
- {
- SetVolume(g_settings.m_iPreMuteVolumeLevel);
- Mute();
- }
- else
- {
- SetVolume(g_settings.m_nVolumeLevel, false);
- }
-
// if the user shutoff the xbox during music scan
// restore the settings
if (g_settings.m_bMyMusicIsScanning)
@@ -2533,7 +2535,7 @@ bool CApplication::OnAction(const CAction &action)
{ // unpaused - set the playspeed back to normal
SetPlaySpeed(1);
}
- g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
+ g_audioManager.Enable(m_pPlayer->IsPaused());
return true;
}
if (!m_pPlayer->IsPaused())
@@ -2593,7 +2595,7 @@ bool CApplication::OnAction(const CAction &action)
{
// unpause, and set the playspeed back to normal
m_pPlayer->Pause();
- g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
+ g_audioManager.Enable(m_pPlayer->IsPaused());
g_application.SetPlaySpeed(1);
return true;
@@ -2633,29 +2635,19 @@ bool CApplication::OnAction(const CAction &action)
{
if (!m_pPlayer || !m_pPlayer->IsPassthrough())
{
- // increase or decrease the volume
- int volume;
if (g_settings.m_bMute)
- {
- volume = (int)((float)g_settings.m_iPreMuteVolumeLevel * 0.01f * (VOLUME_MAXIMUM - VOLUME_MINIMUM) + VOLUME_MINIMUM);
UnMute();
- }
- else
- volume = g_settings.m_nVolumeLevel + g_settings.m_dynamicRangeCompressionLevel;
-
- // calculate speed so that a full press will equal 1 second from min to max
- float speed = float(VOLUME_MAXIMUM - VOLUME_MINIMUM);
+ float volume = g_settings.m_fVolumeLevel;
+ float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / VOLUME_CONTROL_STEPS;
if (action.GetRepeat())
- speed *= action.GetRepeat();
- else
- speed /= 50; //50 fps
+ step *= action.GetRepeat() * 50; // 50 fps
if (action.GetID() == ACTION_VOLUME_UP)
- volume += (int)((float)fabs(action.GetAmount()) * action.GetAmount() * speed);
+ volume += (float)fabs(action.GetAmount()) * action.GetAmount() * step;
else
- volume -= (int)((float)fabs(action.GetAmount()) * action.GetAmount() * speed);
+ volume -= (float)fabs(action.GetAmount()) * action.GetAmount() * step;
- SetVolume(volume, false);
+ SetHardwareVolume(volume);
}
// show visual feedback of volume change...
ShowVolumeBar(&action);
@@ -3462,6 +3454,9 @@ void CApplication::Stop(int exitCode)
g_Windowing.DestroyWindow();
g_Windowing.DestroyWindowSystem();
+ // shutdown the AudioEngine
+ CAEFactory::AE->Shutdown();
+
CLog::Log(LOGNOTICE, "stopped");
}
catch (...)
@@ -3886,12 +3881,6 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart)
m_pPlayer = CPlayerCoreFactory::CreatePlayer(eNewCore, *this);
}
- // Workaround for bug/quirk in SDL_Mixer on OSX.
- // TODO: Remove after GUI Sounds redux
-#if defined(__APPLE__) || defined(_LINUX)
- g_audioManager.Enable(false);
-#endif
-
bool bResult;
if (m_pPlayer)
{
@@ -4881,11 +4870,6 @@ void CApplication::Process()
m_applicationMessenger.ProcessMessages();
if (g_application.m_bStop) return; //we're done, everything has been unloaded
- // check if we can free unused memory
-#ifndef _LINUX
- g_audioManager.FreeUnused();
-#endif
-
// check how far we are through playing the current item
// and do anything that needs doing (lastfm submission, playcount updates etc)
CheckPlayingProgress();
@@ -5026,6 +5010,8 @@ void CApplication::ProcessSlow()
if (!IsPlayingVideo())
CAddonInstaller::Get().UpdateRepos();
+
+ CAEFactory::AE->GarbageCollect();
}
// Global Idle Time in Seconds
@@ -5127,7 +5113,7 @@ bool CApplication::IsMuted() const
{
if (g_peripherals.IsMuted())
return true;
- return g_settings.m_bMute;
+ return CAEFactory::AE->IsMuted();
}
void CApplication::ToggleMute(void)
@@ -5143,9 +5129,8 @@ void CApplication::Mute()
if (g_peripherals.Mute())
return;
- g_settings.m_iPreMuteVolumeLevel = GetVolume();
+ CAEFactory::AE->SetMute(true);
g_settings.m_bMute = true;
- SetVolume(0);
}
void CApplication::UnMute()
@@ -5153,65 +5138,44 @@ void CApplication::UnMute()
if (g_peripherals.UnMute())
return;
+ CAEFactory::AE->SetMute(false);
g_settings.m_bMute = false;
- SetVolume(g_settings.m_iPreMuteVolumeLevel);
- g_settings.m_iPreMuteVolumeLevel = 0;
}
-void CApplication::SetVolume(long iValue, bool isPercentage /* = true */)
+void CApplication::SetVolume(float iValue, bool isPercentage/*=true*/)
{
- // convert the percentage to a mB (milliBell) value (*100 for dB)
- if (isPercentage)
- iValue = (long)((float)iValue * 0.01f * (VOLUME_MAXIMUM - VOLUME_MINIMUM) + VOLUME_MINIMUM);
+ float hardwareVolume = iValue;
- SetHardwareVolume(iValue);
-#ifndef HAS_SDL_AUDIO
- g_audioManager.SetVolume(g_settings.m_nVolumeLevel);
-#else
- g_audioManager.SetVolume((int)(128.f * (g_settings.m_nVolumeLevel - VOLUME_MINIMUM) / (float)(VOLUME_MAXIMUM - VOLUME_MINIMUM)));
-#endif
+ if(isPercentage)
+ hardwareVolume /= 100.0f;
+
+ SetHardwareVolume(hardwareVolume);
CVariant data(CVariant::VariantTypeObject);
- data["volume"] = (int)(((float)(g_settings.m_nVolumeLevel - VOLUME_MINIMUM)) / (VOLUME_MAXIMUM - VOLUME_MINIMUM) * 100.0f + 0.5f);
- /* TODO: add once DRC is available
- data["drc"] = (int)(((float)(g_settings.m_dynamicRangeCompressionLevel - VOLUME_DRC_MINIMUM)) / (VOLUME_DRC_MAXIMUM - VOLUME_DRC_MINIMUM) * 100.0f + 0.5f);*/
+ data["volume"] = (int)(hardwareVolume * 100.0f + 0.5f);
data["muted"] = g_settings.m_bMute;
CAnnouncementManager::Announce(Application, "xbmc", "OnVolumeChanged", data);
}
-void CApplication::SetHardwareVolume(long hardwareVolume)
+void CApplication::SetHardwareVolume(float hardwareVolume)
{
- // TODO DRC
- if (hardwareVolume >= VOLUME_MAXIMUM) // + VOLUME_DRC_MAXIMUM
- hardwareVolume = VOLUME_MAXIMUM;// + VOLUME_DRC_MAXIMUM;
- if (hardwareVolume <= VOLUME_MINIMUM)
- hardwareVolume = VOLUME_MINIMUM;
+ hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, hardwareVolume));
+ g_settings.m_fVolumeLevel = hardwareVolume;
- // update our settings
- if (hardwareVolume > VOLUME_MAXIMUM)
- {
- g_settings.m_dynamicRangeCompressionLevel = hardwareVolume - VOLUME_MAXIMUM;
- g_settings.m_nVolumeLevel = VOLUME_MAXIMUM;
- }
- else
- {
- g_settings.m_dynamicRangeCompressionLevel = 0;
- g_settings.m_nVolumeLevel = hardwareVolume;
- }
+ float value = 0.0f;
+ if (hardwareVolume > VOLUME_MINIMUM)
+ value = CAEUtil::LinToLog(VOLUME_DYNAMIC_RANGE, hardwareVolume);
- // and tell our player to update the volume
- if (m_pPlayer)
- {
- m_pPlayer->SetVolume(g_settings.m_nVolumeLevel);
- // TODO DRC
-// m_pPlayer->SetDynamicRangeCompression(g_settings.m_dynamicRangeCompressionLevel);
- }
+ if (value >= 0.99f)
+ value = 1.0f;
+
+ CAEFactory::AE->SetVolume(value);
}
int CApplication::GetVolume() const
{
// converts the hardware volume (in mB) to a percentage
- return int(((float)(g_settings.m_nVolumeLevel + g_settings.m_dynamicRangeCompressionLevel - VOLUME_MINIMUM)) / (VOLUME_MAXIMUM - VOLUME_MINIMUM)*100.0f + 0.5f);
+ return g_settings.m_fVolumeLevel * 100.0f;
}
int CApplication::GetSubtitleDelay() const
@@ -5250,7 +5214,7 @@ void CApplication::SetPlaySpeed(int iSpeed)
m_pPlayer->ToFFRW(m_iPlaySpeed);
if (m_iPlaySpeed == 1)
{ // restore volume
- m_pPlayer->SetVolume(g_settings.m_nVolumeLevel);
+ m_pPlayer->SetVolume(VOLUME_MAXIMUM);
}
else
{ // mute volume
diff --git a/xbmc/Application.h b/xbmc/Application.h
index edc8dfc4a4..26798342de 100644
--- a/xbmc/Application.h
+++ b/xbmc/Application.h
@@ -188,7 +188,7 @@ public:
void ProcessSlow();
void ResetScreenSaver();
int GetVolume() const;
- void SetVolume(long iValue, bool isPercentage = true);
+ void SetVolume(float iValue, bool isPercentage = true);
bool IsMuted() const;
void ToggleMute(void);
void ShowVolumeBar(const CAction *action = NULL);
@@ -399,7 +399,7 @@ protected:
void Mute();
void UnMute();
- void SetHardwareVolume(long hardwareVolume);
+ void SetHardwareVolume(float hardwareVolume);
void UpdateLCD();
void FatalErrorHandler(bool WindowSystemInitialized, bool MapDrives, bool InitNetwork);
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index 73a9875280..16f9b28b42 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -1146,7 +1146,7 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa
strLabel.Format("%02.2f", m_fps);
break;
case PLAYER_VOLUME:
- strLabel.Format("%2.1f dB", (float)(g_settings.m_nVolumeLevel + g_settings.m_dynamicRangeCompressionLevel) * 0.01f);
+ strLabel.Format("%2.1f dB", g_settings.m_fVolumeLevel);
break;
case PLAYER_SUBTITLE_DELAY:
strLabel.Format("%2.3f s", g_settings.m_currentVideoSettings.m_SubtitleDelay);
diff --git a/xbmc/SystemGlobals.cpp b/xbmc/SystemGlobals.cpp
index 71aeca0300..19a0d849b7 100644
--- a/xbmc/SystemGlobals.cpp
+++ b/xbmc/SystemGlobals.cpp
@@ -24,7 +24,6 @@
#include "Application.h"
#include "GUILargeTextureManager.h"
#include "guilib/TextureManager.h"
-#include "guilib/AudioContext.h"
#include "settings/GUISettings.h"
#include "settings/Settings.h"
#include "utils/AlarmClock.h"
@@ -47,7 +46,6 @@
CSettings g_settings;
CXBMCRenderManager g_renderManager;
- CAudioContext g_audioContext;
CLangInfo g_langInfo;
CLangCodeExpander g_LangCodeExpander;
CLocalizeStrings g_localizeStrings;
diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp
index 9335f722a1..41e32c07b1 100644
--- a/xbmc/addons/Visualisation.cpp
+++ b/xbmc/addons/Visualisation.cpp
@@ -29,6 +29,8 @@
#include "windowing/WindowingFactory.h"
#include "utils/URIUtils.h"
#include "utils/StringUtils.h"
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/Utils/AEConvert.h"
#ifdef _LINUX
#include <dlfcn.h>
#include "filesystem/SpecialProtocol.h"
@@ -41,7 +43,7 @@ using namespace ADDON;
CAudioBuffer::CAudioBuffer(int iSize)
{
m_iLen = iSize;
- m_pBuffer = new short[iSize];
+ m_pBuffer = new float[iSize];
}
CAudioBuffer::~CAudioBuffer()
@@ -49,42 +51,17 @@ CAudioBuffer::~CAudioBuffer()
delete [] m_pBuffer;
}
-const short* CAudioBuffer::Get() const
+const float* CAudioBuffer::Get() const
{
return m_pBuffer;
}
-void CAudioBuffer::Set(const unsigned char* psBuffer, int iSize, int iBitsPerSample)
+void CAudioBuffer::Set(const float* psBuffer, int iSize)
{
if (iSize<0)
- {
return;
- }
-
- if (iBitsPerSample == 16)
- {
- iSize /= 2;
- for (int i = 0; i < iSize && i < m_iLen; i++)
- { // 16 bit -> convert to short directly
- m_pBuffer[i] = ((short *)psBuffer)[i];
- }
- }
- else if (iBitsPerSample == 8)
- {
- for (int i = 0; i < iSize && i < m_iLen; i++)
- { // 8 bit -> convert to signed short by multiplying by 256
- m_pBuffer[i] = ((short)((char *)psBuffer)[i]) << 8;
- }
- }
- else // assume 24 bit data
- {
- iSize /= 3;
- for (int i = 0; i < iSize && i < m_iLen; i++)
- { // 24 bit -> ignore least significant byte and convert to signed short
- m_pBuffer[i] = (((int)psBuffer[3 * i + 1]) << 0) + (((int)((char *)psBuffer)[3 * i + 2]) << 8);
- }
- }
- for (int i = iSize; i < m_iLen;++i) m_pBuffer[i] = 0;
+ memcpy(m_pBuffer, psBuffer, iSize * sizeof(float));
+ for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0;
}
bool CVisualisation::Create(int x, int y, int w, int h)
@@ -155,7 +132,7 @@ void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample
}
}
-void CVisualisation::AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
// pass audio data to visz.
// audio data: is short audiodata [channel][iAudioDataLength] containing the raw audio data
@@ -274,7 +251,7 @@ void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPe
CLog::Log(LOGDEBUG, "OnInitialize() done");
}
-void CVisualisation::OnAudioData(const unsigned char* pAudioData, int iAudioDataLength)
+void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength)
{
if (!m_pStruct)
return ;
@@ -284,8 +261,8 @@ void CVisualisation::OnAudioData(const unsigned char* pAudioData, int iAudioData
return;
// Save our audio data in the buffers
- auto_ptr<CAudioBuffer> pBuffer ( new CAudioBuffer(2*AUDIO_BUFFER_SIZE) );
- pBuffer->Set(pAudioData, iAudioDataLength, m_iBitsPerSample);
+ auto_ptr<CAudioBuffer> pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) );
+ pBuffer->Set(pAudioData, iAudioDataLength);
m_vecBuffers.push_back( pBuffer.release() );
if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ;
@@ -295,12 +272,8 @@ void CVisualisation::OnAudioData(const unsigned char* pAudioData, int iAudioData
// Fourier transform the data if the vis wants it...
if (m_bWantsFreq)
{
- // Convert to floats
- const short* psAudioData = ptrAudioBuffer->Get();
- for (int i = 0; i < 2*AUDIO_BUFFER_SIZE; i++)
- {
- m_fFreq[i] = (float)psAudioData[i];
- }
+ const float *psAudioData = ptrAudioBuffer->Get();
+ memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float));
// FFT the data
twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE);
@@ -314,7 +287,7 @@ void CVisualisation::OnAudioData(const unsigned char* pAudioData, int iAudioData
}
// Transfer data to our visualisation
- AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE);
+ AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE);
}
else
{ // Transfer data to our visualisation
diff --git a/xbmc/addons/Visualisation.h b/xbmc/addons/Visualisation.h
index 0fd2ec9297..e2a0ed22de 100644
--- a/xbmc/addons/Visualisation.h
+++ b/xbmc/addons/Visualisation.h
@@ -41,11 +41,11 @@ class CAudioBuffer
public:
CAudioBuffer(int iSize);
virtual ~CAudioBuffer();
- const short* Get() const;
- void Set(const unsigned char* psBuffer, int iSize, int iBitsPerSample);
+ const float* Get() const;
+ void Set(const float* psBuffer, int iSize);
private:
CAudioBuffer();
- short* m_pBuffer;
+ float* m_pBuffer;
int m_iLen;
};
@@ -58,10 +58,10 @@ namespace ADDON
CVisualisation(const ADDON::AddonProps &props) : CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>(props) {}
CVisualisation(const cp_extension_t *ext) : CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>(ext) {}
virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample);
- virtual void OnAudioData(const unsigned char* pAudioData, int iAudioDataLength);
+ virtual void OnAudioData(const float* pAudioData, int iAudioDataLength);
bool Create(int x, int y, int w, int h);
void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const CStdString strSongName);
- void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
+ void AudioData(const float *pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
void Render();
void Stop();
void GetInfo(VIS_INFO *info);
diff --git a/xbmc/addons/include/xbmc_vis_dll.h b/xbmc/addons/include/xbmc_vis_dll.h
index cd70fe7647..42691e8b98 100644
--- a/xbmc/addons/include/xbmc_vis_dll.h
+++ b/xbmc/addons/include/xbmc_vis_dll.h
@@ -29,7 +29,7 @@ extern "C"
{
// Functions that your visualisation must implement
void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName);
- void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
+ void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
void Render();
bool OnAction(long action, const void *param);
void GetInfo(VIS_INFO* pInfo);
diff --git a/xbmc/addons/include/xbmc_vis_types.h b/xbmc/addons/include/xbmc_vis_types.h
index ecb6a3a200..42801b01b9 100644
--- a/xbmc/addons/include/xbmc_vis_types.h
+++ b/xbmc/addons/include/xbmc_vis_types.h
@@ -97,7 +97,7 @@ extern "C"
struct Visualisation
{
void (__cdecl* Start)(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName);
- void (__cdecl* AudioData)(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
+ void (__cdecl* AudioData)(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength);
void (__cdecl* Render) ();
void (__cdecl* GetInfo)(VIS_INFO *info);
bool (__cdecl* OnAction)(long flags, const void *param);
diff --git a/xbmc/cores/AudioEngine/AEAudioFormat.h b/xbmc/cores/AudioEngine/AEAudioFormat.h
new file mode 100644
index 0000000000..927dfdba35
--- /dev/null
+++ b/xbmc/cores/AudioEngine/AEAudioFormat.h
@@ -0,0 +1,110 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Utils/AEChannelInfo.h"
+
+/**
+ * The various data formats
+ * LE = Little Endian, BE = Big Endian, NE = Native Endian
+ * @note This is ordered from the worst to best preferred formats
+ */
+enum AEDataFormat
+{
+ AE_FMT_INVALID = -1,
+
+ AE_FMT_U8,
+ AE_FMT_S8,
+
+ AE_FMT_S16BE,
+ AE_FMT_S16LE,
+ AE_FMT_S16NE,
+
+ AE_FMT_S32BE,
+ AE_FMT_S32LE,
+ AE_FMT_S32NE,
+
+ AE_FMT_S24BE4,
+ AE_FMT_S24LE4,
+ AE_FMT_S24NE4, /* S24 in 4 bytes */
+
+ AE_FMT_S24BE3,
+ AE_FMT_S24LE3,
+ AE_FMT_S24NE3, /* S24 in 3 bytes */
+
+ AE_FMT_DOUBLE,
+ AE_FMT_FLOAT,
+
+ /* Bitstream formats */
+ AE_FMT_AAC,
+ AE_FMT_AC3,
+ AE_FMT_DTS,
+ AE_FMT_EAC3,
+ AE_FMT_TRUEHD,
+ AE_FMT_DTSHD,
+ AE_FMT_LPCM,
+
+ AE_FMT_MAX
+};
+
+#define AE_IS_RAW(x) ((x) >= AE_FMT_AAC && (x) < AE_FMT_MAX)
+#define AE_IS_RAW_HD(x) ((x) >= AE_FMT_EAC3 && (x) < AE_FMT_MAX)
+
+/**
+ * The audio format structure that fully defines a stream's audio information
+ */
+typedef struct {
+ /**
+ * The stream's data format (eg, AE_FMT_S16LE)
+ */
+ enum AEDataFormat m_dataFormat;
+
+ /**
+ * The stream's sample rate (eg, 48000)
+ */
+ unsigned int m_sampleRate;
+
+ /**
+ * The encoded streams sample rate if a bitstream, otherwise undefined
+ */
+ unsigned int m_encodedRate;
+
+ /**
+ * The stream's channel layout
+ */
+ CAEChannelInfo m_channelLayout;
+
+ /**
+ * The number of frames per period
+ */
+ unsigned int m_frames;
+
+ /**
+ * The number of samples in one frame
+ */
+ unsigned int m_frameSamples;
+
+ /**
+ * The size of one frame in bytes
+ */
+ unsigned int m_frameSize;
+} AEAudioFormat;
+
diff --git a/xbmc/cores/AudioEngine/AEFactory.cpp b/xbmc/cores/AudioEngine/AEFactory.cpp
new file mode 100644
index 0000000000..2ddd4c832f
--- /dev/null
+++ b/xbmc/cores/AudioEngine/AEFactory.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include "system.h"
+
+#include "AEFactory.h"
+
+#if defined(TARGET_DARWIN)
+ #include "Engines/CoreAudioAE.h"
+#else
+ #include "Engines/SoftAE.h"
+#endif
+
+#if defined(HAS_PULSEAUDIO)
+ #include "Engines/PulseAE.h"
+#endif
+
+IAE* CAEFactory::AE = NULL;
+
+bool CAEFactory::LoadEngine()
+{
+ bool loaded = false;
+
+ std::string engine;
+
+#if defined(TARGET_LINUX)
+ if (getenv("AE_ENGINE"))
+ {
+ engine = (std::string)getenv("AE_ENGINE");
+ std::transform(engine.begin(), engine.end(), engine.begin(), ::toupper);
+
+ #if defined(HAS_PULSEAUDIO)
+ if (!loaded && engine == "PULSE")
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_PULSE);
+ #endif
+ if (!loaded && engine == "SOFT" )
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_SOFT);
+ }
+#endif
+
+#if defined(HAS_PULSEAUDIO)
+ if (!loaded)
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_PULSE);
+#endif
+
+#if defined(TARGET_DARWIN)
+ if (!loaded)
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_COREAUDIO);
+#else
+ if (!loaded)
+ loaded = CAEFactory::LoadEngine(AE_ENGINE_SOFT);
+#endif
+
+ return loaded;
+}
+
+bool CAEFactory::LoadEngine(enum AEEngine engine)
+{
+ /* can only load the engine once, XBMC restart is required to change it */
+ if (AE)
+ return false;
+
+ switch(engine)
+ {
+ case AE_ENGINE_NULL :
+#if defined(TARGET_DARWIN)
+ case AE_ENGINE_COREAUDIO: AE = new CCoreAudioAE(); break;
+#else
+ case AE_ENGINE_SOFT : AE = new CSoftAE(); break;
+#endif
+#if defined(HAS_PULSEAUDIO)
+ case AE_ENGINE_PULSE : AE = new CPulseAE(); break;
+#endif
+
+ default:
+ return false;
+ }
+
+ return AE != NULL;
+}
+
+bool CAEFactory::StartEngine()
+{
+ if (!AE)
+ return false;
+
+ if (AE->Initialize())
+ return true;
+
+ delete AE;
+ AE = NULL;
+ return false;
+}
diff --git a/xbmc/cores/AudioRenderers/AudioRendererFactory.h b/xbmc/cores/AudioEngine/AEFactory.h
index 6d714e795d..6259740b98 100644
--- a/xbmc/cores/AudioRenderers/AudioRendererFactory.h
+++ b/xbmc/cores/AudioEngine/AEFactory.h
@@ -1,6 +1,7 @@
+#pragma once
/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
*
* 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
@@ -18,22 +19,26 @@
* http://www.gnu.org/copyleft/gpl.html
*
*/
-#ifndef __AUDIO_RENDERER_FACTORY_H__
-#define __AUDIO_RENDERER_FACTORY_H__
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
+#include "Interfaces/AE.h"
+#include "threads/Thread.h"
-#include "IAudioRenderer.h"
-#include "cores/IAudioCallback.h"
+enum AEEngine
+{
+ AE_ENGINE_NULL,
+ AE_ENGINE_SOFT,
+ AE_ENGINE_COREAUDIO,
+ AE_ENGINE_PULSE
+};
-class CAudioRendererFactory
+class IAE;
+class CAEFactory
{
public:
- static IAudioRenderer *Create(IAudioCallback* pCallback, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, IAudioRenderer::EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough);
+ static IAE *AE;
+ static bool LoadEngine();
+ static bool StartEngine();
private:
- static IAudioRenderer *CreateFromUri(const CStdString &soundsystem, CStdString &renderer);
+ static bool LoadEngine(enum AEEngine engine);
};
-#endif
+
diff --git a/xbmc/cores/AudioEngine/AESinkFactory.cpp b/xbmc/cores/AudioEngine/AESinkFactory.cpp
new file mode 100644
index 0000000000..a5a1f25d77
--- /dev/null
+++ b/xbmc/cores/AudioEngine/AESinkFactory.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AESinkFactory.h"
+#include "utils/SystemInfo.h"
+#include "utils/log.h"
+#include "settings/AdvancedSettings.h"
+
+#if defined(TARGET_WINDOWS)
+ #include "Sinks/AESinkWASAPI.h"
+ #include "Sinks/AESinkDirectSound.h"
+#elif defined(TARGET_LINUX)
+ #if defined(HAS_ALSA)
+ #include "Sinks/AESinkALSA.h"
+ #endif
+ #include "Sinks/AESinkOSS.h"
+#else
+ #pragma message("NOTICE: No audio sink for target platform. Audio output will not be available.")
+#endif
+#include "Sinks/AESinkProfiler.h"
+#include "Sinks/AESinkNULL.h"
+
+void CAESinkFactory::ParseDevice(std::string &device, std::string &driver)
+{
+ int pos = device.find_first_of(':');
+ if (pos > 0)
+ {
+ driver = device.substr(0, pos);
+ std::transform(driver.begin(), driver.end(), driver.begin(), ::toupper);
+
+ /* check that it is a valid driver name */
+ if (
+#if defined(TARGET_LINUX)
+ #if defined(HAS_ALSA)
+ driver == "ALSA" ||
+ #endif
+ driver == "OSS" ||
+#elif defined(TARGET_WINDOWS)
+ driver == "WASAPI" ||
+ driver == "DIRECTSOUND" ||
+#endif
+ driver == "PROFILER"
+ )
+ device = device.substr(pos + 1, device.length() - pos - 1);
+ else
+ driver.clear();
+ }
+ else
+ driver.clear();
+}
+
+#define TRY_SINK(SINK) \
+{ \
+ tmpFormat = desiredFormat; \
+ tmpDevice = device; \
+ sink = new CAESink ##SINK(); \
+ if (sink->Initialize(tmpFormat, tmpDevice)) \
+ { \
+ desiredFormat = tmpFormat; \
+ device = tmpDevice; \
+ return sink; \
+ } \
+ sink->Deinitialize(); \
+ delete sink; \
+}
+
+IAESink *CAESinkFactory::Create(std::string &device, AEAudioFormat &desiredFormat, bool rawPassthrough)
+{
+#if !defined(TARGET_DARWIN)
+
+ /* extract the driver from the device string if it exists */
+ std::string driver;
+ ParseDevice(device, driver);
+
+ AEAudioFormat tmpFormat;
+ IAESink *sink;
+ std::string tmpDevice;
+
+ if (driver == "PROFILER")
+ TRY_SINK(Profiler);
+
+
+#if defined(TARGET_WINDOWS)
+
+ if ((driver.empty() && g_sysinfo.IsVistaOrHigher() && !g_advancedSettings.m_audioForceDirectSound) || driver == "WASAPI")
+ TRY_SINK(WASAPI)
+
+ if (driver.empty() || driver == "DIRECTSOUND")
+ TRY_SINK(DirectSound)
+
+#elif defined(TARGET_LINUX)
+
+ #if defined(HAS_ALSA)
+ if (driver.empty() || driver == "ALSA")
+ TRY_SINK(ALSA)
+ #endif
+ if (driver.empty() || driver == "OSS")
+ TRY_SINK(OSS)
+
+ /* no need to try others as both will have been attempted if driver is empty */
+ if (driver.empty())
+ TRY_SINK(NULL);
+
+ /* if we failed to get a sink, try to open one of the others */
+ #if defined(HAS_ALSA)
+ if (driver != "ALSA")
+ TRY_SINK(ALSA)
+ #endif
+ if (driver != "OSS")
+ TRY_SINK(OSS)
+
+#endif /* defined TARGET_WINDOWS || defined TARGET_LINUX */
+
+ //Complete failure.
+ TRY_SINK(NULL);
+
+#endif /* defined TARGET_DARWIN */
+
+ /* should never get here */
+ ASSERT(false);
+ return NULL;
+}
+
+#define ENUMERATE_SINK(SINK) { \
+ AESinkInfo info; \
+ info.m_sinkName = #SINK; \
+ CAESink ##SINK::EnumerateDevicesEx(info.m_deviceInfoList); \
+ if(!info.m_deviceInfoList.empty()) \
+ list.push_back(info); \
+}
+
+void CAESinkFactory::EnumerateEx(AESinkInfoList &list)
+{
+
+#if defined(HAS_ALSA)
+ ENUMERATE_SINK(ALSA);
+#endif
+
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
+ ENUMERATE_SINK(OSS);
+#endif
+
+#if defined(TARGET_WINDOWS)
+ if (g_sysinfo.IsVistaOrHigher() && !g_advancedSettings.m_audioForceDirectSound)
+ ENUMERATE_SINK(WASAPI);
+
+ ENUMERATE_SINK(DirectSound);
+#endif
+}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/IDVDAudioEncoder.h b/xbmc/cores/AudioEngine/AESinkFactory.h
index 566763f81c..b2a905727a 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/IDVDAudioEncoder.h
+++ b/xbmc/cores/AudioEngine/AESinkFactory.h
@@ -1,7 +1,7 @@
#pragma once
/*
- * Copyright (C) 2005-2010 Team XBMC
- * http://www.xbmc.org
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
*
* 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
@@ -20,24 +20,26 @@
*
*/
-#include "utils/PCMRemap.h"
-#include "DVDStreamInfo.h"
+#include <stdint.h>
+#include <string>
+#include <vector>
-class IDVDAudioEncoder
+#include "Interfaces/AESink.h"
+#include "Utils/AEDeviceInfo.h"
+
+typedef struct
{
-public:
- IDVDAudioEncoder() {};
- virtual ~IDVDAudioEncoder() {};
- virtual bool Initialize(unsigned int channels, enum PCMChannels *channelMap, unsigned int bitsPerSample, unsigned int sampleRate) = 0;
- virtual void Reset() = 0;
+ std::string m_sinkName;
+ AEDeviceInfoList m_deviceInfoList;
+} AESinkInfo;
- /* returns this DSPs output format */
- virtual unsigned int GetBitRate () = 0;
- virtual CodecID GetCodecID () = 0;
- virtual unsigned int GetPacketSize() = 0;
+typedef std::vector<AESinkInfo> AESinkInfoList;
- /* add/get packets to/from the DSP */
- virtual int Encode (uint8_t *data, int size) = 0;
- virtual int GetData(uint8_t **data) = 0;
+class CAESinkFactory
+{
+public:
+ static void ParseDevice(std::string &device, std::string &driver);
+ static IAESink *Create(std::string &device, AEAudioFormat &desiredFormat, bool rawPassthrough);
+ static void EnumerateEx(AESinkInfoList &list);
};
diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
new file mode 100644
index 0000000000..ed3f7c40d4
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define AC3_ENCODE_BITRATE 640000
+#define DTS_ENCODE_BITRATE 1411200
+
+#include "AEEncoderFFmpeg.h"
+#include "Utils/AEUtil.h"
+#include "utils/log.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/GUISettings.h"
+#include <string.h>
+
+CAEEncoderFFmpeg::CAEEncoderFFmpeg():
+ m_CodecCtx(NULL)
+{
+}
+
+CAEEncoderFFmpeg::~CAEEncoderFFmpeg()
+{
+ Reset();
+ m_dllAvUtil.av_freep(&m_CodecCtx);
+}
+
+bool CAEEncoderFFmpeg::IsCompatible(AEAudioFormat format)
+{
+ if (!m_CodecCtx)
+ return false;
+
+ bool match = (
+ format.m_dataFormat == m_CurrentFormat.m_dataFormat &&
+ format.m_sampleRate == m_CurrentFormat.m_sampleRate
+ );
+
+ if (match)
+ {
+ CAEChannelInfo layout;
+ BuildChannelLayout(AV_CH_LAYOUT_5POINT1_BACK, layout); /* hard coded for AC3 & DTS currently */
+ match = (m_CurrentFormat.m_channelLayout == layout);
+ }
+
+ return match;
+}
+
+unsigned int CAEEncoderFFmpeg::BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout)
+{
+ /* build the channel layout and count the channels */
+ layout.Reset();
+ if (ffmap & AV_CH_FRONT_LEFT ) layout += AE_CH_FL ;
+ if (ffmap & AV_CH_FRONT_RIGHT ) layout += AE_CH_FR ;
+ if (ffmap & AV_CH_FRONT_CENTER ) layout += AE_CH_FC ;
+ if (ffmap & AV_CH_LOW_FREQUENCY ) layout += AE_CH_LFE ;
+ if (ffmap & AV_CH_BACK_LEFT ) layout += AE_CH_BL ;
+ if (ffmap & AV_CH_BACK_RIGHT ) layout += AE_CH_BR ;
+ if (ffmap & AV_CH_FRONT_LEFT_OF_CENTER ) layout += AE_CH_FLOC;
+ if (ffmap & AV_CH_FRONT_RIGHT_OF_CENTER) layout += AE_CH_FROC;
+ if (ffmap & AV_CH_BACK_CENTER ) layout += AE_CH_BC ;
+ if (ffmap & AV_CH_SIDE_LEFT ) layout += AE_CH_SL ;
+ if (ffmap & AV_CH_SIDE_RIGHT ) layout += AE_CH_SR ;
+ if (ffmap & AV_CH_TOP_CENTER ) layout += AE_CH_TC ;
+ if (ffmap & AV_CH_TOP_FRONT_LEFT ) layout += AE_CH_TFL ;
+ if (ffmap & AV_CH_TOP_FRONT_CENTER ) layout += AE_CH_TFC ;
+ if (ffmap & AV_CH_TOP_FRONT_RIGHT ) layout += AE_CH_TFR ;
+ if (ffmap & AV_CH_TOP_BACK_LEFT ) layout += AE_CH_TBL ;
+ if (ffmap & AV_CH_TOP_BACK_CENTER ) layout += AE_CH_TBC ;
+ if (ffmap & AV_CH_TOP_BACK_RIGHT ) layout += AE_CH_TBR ;
+
+ return layout.Count();
+}
+
+bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
+{
+ Reset();
+
+ if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load())
+ return false;
+
+ m_dllAvCodec.avcodec_register_all();
+
+ bool ac3 = g_guiSettings.GetBool("audiooutput.ac3passthrough");
+
+ AVCodec *codec = NULL;
+#if 0
+ /* the DCA encoder is currently useless for transcode, it creates a 196 kHz DTS-HD like mongrel which is useless for SPDIF */
+ bool dts = g_guiSettings.GetBool("audiooutput.dtspassthrough");
+ if (dts && (!ac3 || g_advancedSettings.m_audioTranscodeTo.Equals("dts")))
+ {
+ m_CodecName = "DTS";
+ m_CodecID = CODEC_ID_DTS;
+ m_PackFunc = &CAEPackIEC61937::PackDTS_1024;
+ m_BitRate = DTS_ENCODE_BITRATE;
+ codec = m_dllAvCodec.avcodec_find_encoder(m_CodecID);
+ }
+#endif
+
+ /* fallback to ac3 if we support it, we might not have DTS support */
+ if (!codec && ac3)
+ {
+ m_CodecName = "AC3";
+ m_CodecID = CODEC_ID_AC3;
+ m_PackFunc = &CAEPackIEC61937::PackAC3;
+ m_BitRate = AC3_ENCODE_BITRATE;
+ codec = m_dllAvCodec.avcodec_find_encoder(m_CodecID);
+ }
+
+ /* check we got the codec */
+ if (!codec)
+ return false;
+
+ m_CodecCtx = m_dllAvCodec.avcodec_alloc_context3(codec);
+ m_CodecCtx->bit_rate = m_BitRate;
+ m_CodecCtx->sample_rate = format.m_sampleRate;
+ m_CodecCtx->channel_layout = AV_CH_LAYOUT_5POINT1_BACK;
+
+ /* select a suitable data format */
+ if (codec->sample_fmts)
+ {
+ bool hasFloat = false;
+ bool hasDouble = false;
+ bool hasS32 = false;
+ bool hasS16 = false;
+ bool hasU8 = false;
+
+ for(int i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i)
+ {
+ switch (codec->sample_fmts[i])
+ {
+ case AV_SAMPLE_FMT_FLT: hasFloat = true; break;
+ case AV_SAMPLE_FMT_DBL: hasDouble = true; break;
+ case AV_SAMPLE_FMT_S32: hasS32 = true; break;
+ case AV_SAMPLE_FMT_S16: hasS16 = true; break;
+ case AV_SAMPLE_FMT_U8 : hasU8 = true; break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (hasFloat)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
+ format.m_dataFormat = AE_FMT_FLOAT;
+ }
+ else if (hasDouble)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
+ format.m_dataFormat = AE_FMT_DOUBLE;
+ }
+ else if (hasS32)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S32;
+ format.m_dataFormat = AE_FMT_S32NE;
+ }
+ else if (hasS16)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
+ format.m_dataFormat = AE_FMT_S16NE;
+ }
+ else if (hasU8)
+ {
+ m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
+ format.m_dataFormat = AE_FMT_U8;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Unable to find a suitable data format for the codec (%s)", m_CodecName.c_str());
+ return false;
+ }
+ }
+
+ m_CodecCtx->channels = BuildChannelLayout(m_CodecCtx->channel_layout, m_Layout);
+
+ /* open the codec */
+ if (m_dllAvCodec.avcodec_open2(m_CodecCtx, codec, NULL))
+ {
+ m_dllAvUtil.av_freep(&m_CodecCtx);
+ return false;
+ }
+
+ format.m_dataFormat = AE_FMT_FLOAT;
+ format.m_frames = m_CodecCtx->frame_size;
+ format.m_frameSamples = m_CodecCtx->frame_size * m_CodecCtx->channels;
+ format.m_frameSize = m_CodecCtx->channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+ format.m_channelLayout = m_Layout;
+
+ m_CurrentFormat = format;
+ m_NeededFrames = format.m_frames;
+ m_OutputSize = m_PackFunc(NULL, 0, m_Buffer);
+ m_OutputRatio = (double)m_NeededFrames / m_OutputSize;
+ m_SampleRateMul = 1.0 / (double)m_CodecCtx->sample_rate;
+
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - %s encoder ready", m_CodecName.c_str());
+ return true;
+}
+
+void CAEEncoderFFmpeg::Reset()
+{
+ m_BufferSize = 0;
+}
+
+unsigned int CAEEncoderFFmpeg::GetBitRate()
+{
+ return m_BitRate;
+}
+
+CodecID CAEEncoderFFmpeg::GetCodecID()
+{
+ return m_CodecID;
+}
+
+unsigned int CAEEncoderFFmpeg::GetFrames()
+{
+ return m_NeededFrames;
+}
+
+int CAEEncoderFFmpeg::Encode(float *data, unsigned int frames)
+{
+ if (!m_CodecCtx || frames < m_NeededFrames)
+ return 0;
+
+ /* encode it */
+ int size = m_dllAvCodec.avcodec_encode_audio(m_CodecCtx, m_Buffer + IEC61937_DATA_OFFSET, FF_MIN_BUFFER_SIZE, (short*)data);
+
+ /* pack it into an IEC958 frame */
+ m_BufferSize = m_PackFunc(NULL, size, m_Buffer);
+ if (m_BufferSize != m_OutputSize)
+ {
+ m_OutputSize = m_BufferSize;
+ m_OutputRatio = (float)m_NeededFrames / m_OutputSize;
+ }
+
+ /* return the number of frames used */
+ return m_NeededFrames;
+}
+
+int CAEEncoderFFmpeg::GetData(uint8_t **data)
+{
+ int size;
+ *data = m_Buffer;
+ size = m_BufferSize;
+ m_BufferSize = 0;
+ return size;
+}
+
+double CAEEncoderFFmpeg::GetDelay(unsigned int bufferSize)
+{
+ if (!m_CodecCtx)
+ return 0;
+
+ int frames = m_CodecCtx->delay;
+ if (m_BufferSize)
+ frames += m_NeededFrames;
+
+ return ((double)frames + ((double)bufferSize * m_OutputRatio)) * m_SampleRateMul;
+}
+
diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
new file mode 100644
index 0000000000..11445e73dc
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
@@ -0,0 +1,72 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Interfaces/AEEncoder.h"
+#include "Utils/AERemap.h"
+#include "Utils/AEPackIEC61937.h"
+
+/* ffmpeg re-defines this, so undef it to squash the warning */
+#undef restrict
+#include "DllAvCodec.h"
+#include "DllAvFormat.h"
+
+class CAEEncoderFFmpeg: public IAEEncoder
+{
+public:
+ CAEEncoderFFmpeg();
+ virtual ~CAEEncoderFFmpeg();
+
+ virtual bool IsCompatible(AEAudioFormat format);
+ virtual bool Initialize(AEAudioFormat &format);
+ virtual void Reset();
+
+ virtual unsigned int GetBitRate ();
+ virtual CodecID GetCodecID ();
+ virtual unsigned int GetFrames ();
+
+ virtual int Encode (float *data, unsigned int frames);
+ virtual int GetData(uint8_t **data);
+ virtual double GetDelay(unsigned int bufferSize);
+private:
+ DllAvCodec m_dllAvCodec;
+ DllAvFormat m_dllAvFormat;
+ DllAvUtil m_dllAvUtil;
+
+ std::string m_CodecName;
+ CodecID m_CodecID;
+ unsigned int m_BitRate;
+ CAEPackIEC61937::PackFunc m_PackFunc;
+
+ AEAudioFormat m_CurrentFormat;
+ AVCodecContext *m_CodecCtx;
+ CAEChannelInfo m_Layout;
+ uint8_t m_Buffer[IEC61937_DATA_OFFSET + FF_MIN_BUFFER_SIZE];
+ int m_BufferSize;
+ int m_OutputSize;
+ double m_OutputRatio;
+ double m_SampleRateMul;
+
+ unsigned int m_NeededFrames;
+
+ unsigned int BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout);
+};
+
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAE.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAE.cpp
new file mode 100644
index 0000000000..eedd031b48
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAE.cpp
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include "system.h"
+
+#include "CoreAudioAE.h"
+#include "CoreAudioAEStream.h"
+#include "CoreAudioAESound.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "settings/Settings.h"
+#include "AEUtil.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/TimeUtils.h"
+#include "utils/SystemInfo.h"
+#include "MathUtils.h"
+#include "utils/EndianSwap.h"
+
+#define DELAY_FRAME_TIME 20
+#define BUFFERSIZE 16416
+
+CCoreAudioAE::CCoreAudioAE() :
+ m_Initialized (false ),
+ m_rawPassthrough (false ),
+ m_volume (1.0f ),
+ m_volumeBeforeMute (1.0f ),
+ m_muted (false ),
+ m_soundMode (AE_SOUND_OFF ),
+ m_streamsPlaying (false ),
+ m_chLayoutCount (0 ),
+ m_callbackRunning (false )
+{
+ HAL = new CCoreAudioAEHAL;
+}
+
+CCoreAudioAE::~CCoreAudioAE()
+{
+ Shutdown();
+}
+
+void CCoreAudioAE::Shutdown()
+{
+ Stop();
+
+ Deinitialize();
+
+ /* free the streams */
+ CSingleLock streamLock(m_streamLock);
+ while (!m_streams.empty())
+ {
+ CCoreAudioAEStream *s = m_streams.front();
+ m_sounds.pop_front();
+ delete s;
+ }
+
+ /* free the suonds */
+ CSingleLock soundLock(m_soundLock);
+ while (!m_sounds.empty())
+ {
+ CCoreAudioAESound *s = m_sounds.front();
+ m_sounds.pop_front();
+ delete s;
+ }
+
+ delete HAL;
+ HAL = NULL;
+}
+
+bool CCoreAudioAE::Initialize()
+{
+ Stop();
+
+ Deinitialize();
+
+ bool ret = OpenCoreAudio();
+
+ Start();
+
+ return ret;
+}
+
+bool CCoreAudioAE::OpenCoreAudio(unsigned int sampleRate, bool forceRaw, enum AEDataFormat rawDataFormat)
+{
+
+ /* remove any deleted streams */
+ CSingleLock streamLock(m_streamLock);
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();)
+ {
+ CCoreAudioAEStream *stream = *itt;
+ if (stream->IsDestroyed())
+ {
+ itt = m_streams.erase(itt);
+ delete stream;
+ continue;
+ }
+ else
+ {
+ /* close all converter */
+ stream->CloseConverter();
+ }
+ ++itt;
+ }
+
+ /* override the sample rate based on the oldest stream if there is one */
+ if (!m_streams.empty())
+ sampleRate = m_streams.front()->GetSampleRate();
+ streamLock.Leave();
+
+ if (forceRaw)
+ m_rawPassthrough = true;
+ else
+ m_rawPassthrough = !m_streams.empty() && m_streams.front()->IsRaw();
+
+ if (m_rawPassthrough)
+ CLog::Log(LOGINFO, "CCoreAudioAE::OpenCoreAudio - RAW passthrough enabled");
+
+ std::string m_outputDevice = g_guiSettings.GetString("audiooutput.audiodevice");
+
+ /* on iOS devices we set fixed to two channels. */
+ m_stdChLayout = AE_CH_LAYOUT_2_0;
+#if defined(TARGET_DARWIN_OSX)
+ switch (g_guiSettings.GetInt("audiooutput.channellayout"))
+ {
+ default:
+ case 0: m_stdChLayout = AE_CH_LAYOUT_2_0; break; /* dont alow 1_0 output */
+ case 1: m_stdChLayout = AE_CH_LAYOUT_2_0; break;
+ case 2: m_stdChLayout = AE_CH_LAYOUT_2_1; break;
+ case 3: m_stdChLayout = AE_CH_LAYOUT_3_0; break;
+ case 4: m_stdChLayout = AE_CH_LAYOUT_3_1; break;
+ case 5: m_stdChLayout = AE_CH_LAYOUT_4_0; break;
+ case 6: m_stdChLayout = AE_CH_LAYOUT_4_1; break;
+ case 7: m_stdChLayout = AE_CH_LAYOUT_5_0; break;
+ case 8: m_stdChLayout = AE_CH_LAYOUT_5_1; break;
+ case 9: m_stdChLayout = AE_CH_LAYOUT_7_0; break;
+ case 10: m_stdChLayout = AE_CH_LAYOUT_7_1; break;
+ }
+#endif
+
+ /* setup the desired format */
+ m_format.m_channelLayout = CAEChannelInfo(m_stdChLayout);
+
+ /* if there is an audio resample rate set, use it. */
+ if (g_advancedSettings.m_audioResample && !m_rawPassthrough)
+ {
+ sampleRate = g_advancedSettings.m_audioResample;
+ CLog::Log(LOGINFO, "CCoreAudioAE::passthrough - Forcing samplerate to %d", sampleRate);
+ }
+
+ if (m_rawPassthrough)
+ {
+ switch (rawDataFormat)
+ {
+ case AE_FMT_AC3:
+ case AE_FMT_DTS:
+ m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
+ m_format.m_sampleRate = 48000;
+ m_format.m_dataFormat = AE_FMT_S16NE;
+ break;
+ case AE_FMT_EAC3:
+ m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
+ m_format.m_sampleRate = 192000;
+ m_format.m_dataFormat = AE_FMT_S16NE;
+ break;
+ case AE_FMT_DTSHD:
+ case AE_FMT_TRUEHD:
+ m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_7_1);
+ m_format.m_sampleRate = 192000;
+ m_format.m_dataFormat = AE_FMT_S16NE;
+ break;
+ case AE_FMT_LPCM:
+ m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_7_1);
+ m_format.m_sampleRate = sampleRate;
+ m_format.m_dataFormat = AE_FMT_FLOAT;
+ break;
+ }
+ }
+ else
+ {
+ m_format.m_sampleRate = sampleRate;
+ m_format.m_channelLayout = CAEChannelInfo(m_stdChLayout);
+ m_format.m_dataFormat = AE_FMT_FLOAT;
+ }
+
+ m_format.m_encodedRate = 0;
+
+ if (m_outputDevice.empty())
+ m_outputDevice = "default";
+
+ AEAudioFormat initformat = m_format;
+
+ // initialize audio hardware
+ m_Initialized = HAL->Initialize(this, m_rawPassthrough, initformat, rawDataFormat, m_outputDevice);
+
+ unsigned int bps = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
+ m_chLayoutCount = m_format.m_channelLayout.Count();
+ m_format.m_frameSize = (bps>>3) * m_chLayoutCount; //initformat.m_frameSize;
+ //m_format.m_frames = (unsigned int)(((float)m_format.m_sampleRate / 1000.0f) * (float)DELAY_FRAME_TIME);
+ //m_format.m_frameSamples = m_format.m_frames * m_format.m_channelLayout.Count();
+
+ if ((initformat.m_channelLayout.Count() != m_chLayoutCount) && !m_rawPassthrough)
+ {
+ /* readjust parameters. hardware didn't accept channel count*/
+ CLog::Log(LOGINFO, "CCoreAudioAE::Initialize: Setup channels (%d) greater than possible hardware channels (%d).",
+ m_chLayoutCount, initformat.m_channelLayout.Count());
+
+ m_format.m_channelLayout = CAEChannelInfo(initformat.m_channelLayout);
+ m_chLayoutCount = m_format.m_channelLayout.Count();
+ m_format.m_frameSize = (bps>>3) * m_chLayoutCount; //initformat.m_frameSize;
+ //m_format.m_frameSamples = m_format.m_frames * m_format.m_channelLayout.Count();
+ }
+
+ CLog::Log(LOGINFO, "CCoreAudioAE::Initialize:");
+ CLog::Log(LOGINFO, " Output Device : %s", m_outputDevice.c_str());
+ CLog::Log(LOGINFO, " Sample Rate : %d", m_format.m_sampleRate);
+ CLog::Log(LOGINFO, " Sample Format : %s", CAEUtil::DataFormatToStr(m_format.m_dataFormat));
+ CLog::Log(LOGINFO, " Channel Count : %d", m_chLayoutCount);
+ CLog::Log(LOGINFO, " Channel Layout: %s", ((std::string)m_format.m_channelLayout).c_str());
+ CLog::Log(LOGINFO, " Frame Size : %d", m_format.m_frameSize);
+ CLog::Log(LOGINFO, " Volume Level : %f", m_volume);
+ CLog::Log(LOGINFO, " Passthrough : %d", m_rawPassthrough);
+
+ SetVolume(m_volume);
+
+ CSingleLock soundLock(m_soundLock);
+ StopAllSounds();
+
+ /* re-init sounds and unlock */
+ for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ (*itt)->Initialize();
+
+ soundLock.Leave();
+
+ /* if we are not in m_rawPassthrough reinit the streams */
+ if (!m_rawPassthrough)
+ {
+ /* re-init streams */
+ streamLock.Enter();
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ (*itt)->Initialize();
+ streamLock.Leave();
+ }
+
+ return m_Initialized;
+}
+
+void CCoreAudioAE::Deinitialize()
+{
+ if (!m_Initialized)
+ return;
+
+ /* close all open converters */
+ CSingleLock streamLock(m_streamLock);
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();++itt)
+ (*itt)->CloseConverter();
+ streamLock.Leave();
+
+ m_Initialized = false;
+
+ CSingleLock callbackLock(m_callbackLock);
+
+ /*
+ while(m_callbackRunning)
+ Sleep(100);
+ */
+
+ HAL->Deinitialize();
+
+ CLog::Log(LOGINFO, "CCoreAudioAE::Deinitialize: Audio device has been closed.");
+}
+
+void CCoreAudioAE::OnSettingsChange(std::string setting)
+{
+ if (setting == "audiooutput.dontnormalizelevels")
+ {
+ /* re-init streams reampper */
+ CSingleLock streamLock(m_streamLock);
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ (*itt)->InitializeRemap();
+ streamLock.Leave();
+ }
+
+ if (setting == "audiooutput.passthroughdevice" ||
+ setting == "audiooutput.custompassthrough" ||
+ setting == "audiooutput.audiodevice" ||
+ setting == "audiooutput.customdevice" ||
+ setting == "audiooutput.mode" ||
+ setting == "audiooutput.ac3passthrough" ||
+ setting == "audiooutput.dtspassthrough" ||
+ setting == "audiooutput.channellayout" ||
+ setting == "audiooutput.multichannellpcm")
+ {
+ Initialize();
+ }
+}
+
+unsigned int CCoreAudioAE::GetSampleRate()
+{
+ return m_format.m_sampleRate;
+}
+
+unsigned int CCoreAudioAE::GetEncodedSampleRate()
+{
+ return m_format.m_encodedRate;
+}
+
+CAEChannelInfo CCoreAudioAE::GetChannelLayout()
+{
+ return m_format.m_channelLayout;
+}
+
+unsigned int CCoreAudioAE::GetChannelCount()
+{
+ return m_chLayoutCount;
+}
+
+enum AEDataFormat CCoreAudioAE::GetDataFormat()
+{
+ return m_format.m_dataFormat;
+}
+
+AEAudioFormat CCoreAudioAE::GetAudioFormat()
+{
+ return m_format;
+}
+
+double CCoreAudioAE::GetDelay()
+{
+ return HAL->GetDelay();
+}
+
+float CCoreAudioAE::GetVolume()
+{
+ return m_volume;
+}
+
+void CCoreAudioAE::SetVolume(float volume)
+{
+ if (m_rawPassthrough)
+ return;
+
+ m_volume = volume;
+
+ HAL->SetVolume(m_volume);
+}
+
+void CCoreAudioAE::SetMute(const bool enabled)
+{
+ m_muted = enabled;
+ if(m_muted)
+ {
+ m_volumeBeforeMute = m_volume;
+ SetVolume(VOLUME_MINIMUM);
+ }
+ else
+ {
+ SetVolume(m_volumeBeforeMute);
+ }
+}
+
+bool CCoreAudioAE::IsMuted()
+{
+ return m_muted;
+}
+
+void CCoreAudioAE::SetSoundMode(const int mode)
+{
+ m_soundMode = mode;
+
+ /* stop all currently playing sounds if they are being turned off */
+ if (mode == AE_SOUND_OFF || (mode == AE_SOUND_IDLE && m_streamsPlaying))
+ StopAllSounds();
+}
+
+bool CCoreAudioAE::SupportsRaw()
+{
+ return true;
+}
+
+CCoreAudioAEHAL *CCoreAudioAE::GetHAL()
+{
+ return HAL;
+}
+
+IAEStream *CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
+ unsigned int sampleRate,
+ unsigned int encodedSamplerate,
+ CAEChannelInfo channelLayout,
+ unsigned int options/* = 0 */)
+{
+ CAEChannelInfo channelInfo(channelLayout);
+ CLog::Log(LOGINFO, "CCoreAudioAE::MakeStream - %s, %u, %u, %s",
+ CAEUtil::DataFormatToStr(dataFormat),
+ sampleRate,
+ encodedSamplerate,
+ ((std::string)channelInfo).c_str()
+ );
+
+ CSingleLock streamLock(m_streamLock);
+ //bool wasEmpty = m_streams.empty();
+ CCoreAudioAEStream *stream = new CCoreAudioAEStream(dataFormat, sampleRate, encodedSamplerate, channelLayout, options);
+ m_streams.push_back(stream);
+ streamLock.Leave();
+
+ Stop();
+
+ if (COREAUDIO_IS_RAW(dataFormat))
+ {
+ Deinitialize();
+ m_Initialized = OpenCoreAudio(sampleRate, true, dataFormat);
+ }
+ else if (/* wasEmpty || */ m_rawPassthrough)
+ {
+ Deinitialize();
+ m_Initialized = OpenCoreAudio(sampleRate);
+ }
+
+ /* if the stream was not initialized, do it now */
+ if (!stream->IsValid())
+ stream->Initialize();
+
+ Start();
+
+ m_streamsPlaying = true;
+
+ return stream;
+}
+
+IAEStream *CCoreAudioAE::FreeStream(IAEStream *stream)
+{
+ CSingleLock streamLock(m_streamLock);
+ /* ensure the stream still exists */
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); )
+ {
+ CCoreAudioAEStream *del = *itt;
+ if (*itt == stream)
+ {
+ itt = m_streams.erase(itt);
+ delete (CCoreAudioAEStream *)stream;
+ }
+ else if (del->IsDestroyed())
+ {
+ itt = m_streams.erase(itt);
+ delete del;
+ }
+ else
+ {
+ ++itt;
+ }
+
+ }
+ m_streamsPlaying = !m_streams.empty();
+
+ streamLock.Leave();
+
+ // When we have been in passthrough mode, reinit the hardware to come back to anlog out
+ if (/*m_streams.empty() || */ m_rawPassthrough)
+ {
+ CLog::Log(LOGINFO, "CCoreAudioAE::FreeStream Reinit, no streams left" );
+ Initialize();
+ }
+
+ return NULL;
+}
+
+void CCoreAudioAE::PlaySound(IAESound *sound)
+{
+ if (m_soundMode == AE_SOUND_OFF || (m_soundMode == AE_SOUND_IDLE && m_streamsPlaying))
+ return;
+
+ float *samples = ((CCoreAudioAESound*)sound)->GetSamples();
+ if (!samples && !m_Initialized)
+ return;
+
+ /* add the sound to the play list */
+ CSingleLock soundSampleLock(m_soundSampleLock);
+ SoundState ss = {
+ ((CCoreAudioAESound*)sound),
+ samples,
+ ((CCoreAudioAESound*)sound)->GetSampleCount()
+ };
+ m_playing_sounds.push_back(ss);
+}
+
+void CCoreAudioAE::StopSound(IAESound *sound)
+{
+ CSingleLock lock(m_soundSampleLock);
+ for (SoundStateList::iterator itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
+ {
+ if ((*itt).owner == sound)
+ {
+ (*itt).owner->ReleaseSamples();
+ itt = m_playing_sounds.erase(itt);
+ }
+ else
+ ++itt;
+ }
+}
+
+IAESound *CCoreAudioAE::MakeSound(const std::string& file)
+{
+ CSingleLock soundLock(m_soundLock);
+
+ /* first check if we have the file cached */
+ for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ {
+ if ((*itt)->GetFileName() == file)
+ return *itt;
+ }
+
+ CCoreAudioAESound *sound = new CCoreAudioAESound(file);
+ if (!sound->Initialize())
+ {
+ delete sound;
+ return NULL;
+ }
+
+ m_sounds.push_back(sound);
+ return sound;
+}
+
+void CCoreAudioAE::FreeSound(IAESound *sound)
+{
+ if (!sound)
+ return;
+
+ sound->Stop();
+ CSingleLock soundLock(m_soundLock);
+ for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ if (*itt == sound)
+ {
+ m_sounds.erase(itt);
+ break;
+ }
+
+ delete (CCoreAudioAESound*)sound;
+}
+
+void CCoreAudioAE::StopAllSounds()
+{
+ CSingleLock lock(m_soundSampleLock);
+ while (!m_playing_sounds.empty())
+ {
+ SoundState *ss = &(*m_playing_sounds.begin());
+ m_playing_sounds.pop_front();
+ ss->owner->ReleaseSamples();
+ }
+}
+
+void CCoreAudioAE::MixSounds(float *buffer, unsigned int samples)
+{
+ if (!m_Initialized)
+ return;
+
+ SoundStateList::iterator itt;
+
+ CSingleLock lock(m_soundSampleLock);
+ for (itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
+ {
+ SoundState *ss = &(*itt);
+
+ /* no more frames, so remove it from the list */
+ if (ss->sampleCount == 0)
+ {
+ ss->owner->ReleaseSamples();
+ itt = m_playing_sounds.erase(itt);
+ continue;
+ }
+
+ unsigned int mixSamples = std::min(ss->sampleCount, samples);
+ float volume = ss->owner->GetVolume();
+
+ for (unsigned int i = 0; i < mixSamples; ++i)
+ buffer[i] = (buffer[i] + (ss->samples[i] * volume));
+
+ ss->sampleCount -= mixSamples;
+ ss->samples += mixSamples;
+
+ ++itt;
+ }
+}
+
+void CCoreAudioAE::GarbageCollect()
+{
+}
+
+void CCoreAudioAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ HAL->EnumerateOutputDevices(devices, passthrough);
+}
+
+void CCoreAudioAE::Start()
+{
+ if (!m_Initialized)
+ return;
+
+ HAL->Start();
+}
+
+void CCoreAudioAE::Stop()
+{
+ if (!m_Initialized)
+ return;
+
+ HAL->Stop();
+}
+
+//***********************************************************************************************
+// Rendering Methods
+//***********************************************************************************************
+OSStatus CCoreAudioAE::OnRender(AudioUnitRenderActionFlags *actionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ UInt32 frames = inNumberFrames;
+
+ unsigned int rSamples = frames * m_chLayoutCount;
+ int size = frames * m_format.m_frameSize;
+ //int size = frames * HAL->m_BytesPerFrame;
+
+ for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
+ bzero(ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize);
+
+ if (!m_Initialized)
+ {
+ m_callbackRunning = false;
+ return noErr;
+ }
+
+ CSingleLock callbackLock(m_callbackLock);
+
+ m_callbackRunning = true;
+
+ /*
+ CSingleLock streamLock(m_streamLock);
+ // Remove any destroyed stream
+ if (!m_streams.empty())
+ {
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();)
+ {
+ CCoreAudioAEStream *stream = *itt;
+
+ if (stream->IsDestroyed())
+ {
+ itt = m_streams.erase(itt);
+ delete stream;
+ continue;
+ }
+ ++itt;
+ }
+ }
+ streamLock.Leave();
+ */
+
+ // when not in passthrough output mix sounds
+ if (!m_rawPassthrough && m_soundMode != AE_SOUND_OFF)
+ {
+ MixSounds((float *)ioData->mBuffers[0].mData, rSamples);
+ ioData->mBuffers[0].mDataByteSize = size;
+ if (!size && actionFlags)
+ *actionFlags |= kAudioUnitRenderAction_OutputIsSilence;
+ }
+
+ m_callbackRunning = false;
+
+ return noErr;
+}
+
+// Static Callback from AudioUnit
+OSStatus CCoreAudioAE::Render(AudioUnitRenderActionFlags* actionFlags,
+ const AudioTimeStamp* pTimeStamp,
+ UInt32 busNumber,
+ UInt32 frameCount,
+ AudioBufferList* pBufList)
+{
+ OSStatus ret = noErr;
+
+ ret = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
+
+ return ret;
+}
+
+
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAE.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAE.h
new file mode 100644
index 0000000000..f5f7d66b11
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAE.h
@@ -0,0 +1,164 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <list>
+#include <map>
+
+#include "system.h"
+
+#include "Interfaces/AE.h"
+#include "ICoreAudioAEHAL.h"
+#include "ICoreAudioSource.h"
+#include "CoreAudioAEStream.h"
+#include "CoreAudioAESound.h"
+#include "threads/CriticalSection.h"
+
+#if defined(TARGET_DARWIN_IOS)
+#include "CoreAudioAEHALIOS.h"
+#else
+#include "CoreAudioAEHALOSX.h"
+#endif
+
+#define COREAUDIO_IS_RAW(x) ((x) == AE_FMT_AC3 || (x) == AE_FMT_DTS || (x) == AE_FMT_LPCM || (x) == AE_FMT_EAC3 || (x) == AE_FMT_DTSHD || (x) == AE_FMT_TRUEHD)
+
+#if defined(TARGET_DARWIN_IOS)
+# define CCoreAudioAEHAL CCoreAudioAEHALIOS
+#else
+# define CCoreAudioAEHAL CCoreAudioAEHALOSX
+#endif
+
+class CCoreAudioAEStream;
+class CCoreAudioAESound;
+class CCoreAudioAEEventThread;
+
+class CCoreAudioAE : public IAE, public ICoreAudioSource
+{
+protected:
+ friend class CAEFactory;
+ CCoreAudioAE();
+ virtual ~CCoreAudioAE();
+
+ /* Give the HAL access to the engine */
+ friend class CCoreAudioAEHAL;
+ CCoreAudioAEHAL *HAL;
+
+public:
+ virtual void Shutdown();
+
+ virtual bool Initialize();
+ virtual void OnSettingsChange(std::string setting);
+
+ unsigned int GetSampleRate();
+ unsigned int GetEncodedSampleRate();
+ CAEChannelInfo GetChannelLayout();
+ unsigned int GetChannelCount();
+ enum AEDataFormat GetDataFormat();
+ AEAudioFormat GetAudioFormat();
+
+ virtual double GetDelay();
+ virtual float GetVolume();
+ virtual void SetVolume(float volume);
+ virtual void SetMute(const bool enabled);
+ virtual bool IsMuted();
+ virtual void SetSoundMode(const int mode);
+
+
+ virtual bool SupportsRaw();
+
+ CCoreAudioAEHAL *GetHAL();
+
+ /* returns a new stream for data in the specified format */
+ virtual IAEStream *MakeStream(enum AEDataFormat dataFormat,
+ unsigned int sampleRate,
+ unsigned int encodedSamplerate,
+ CAEChannelInfo channelLayout,
+ unsigned int options = 0);
+
+ virtual IAEStream *FreeStream(IAEStream *stream);
+
+ /* returns a new sound object */
+ virtual IAESound *MakeSound(const std::string& file);
+ void StopAllSounds();
+ virtual void FreeSound(IAESound *sound);
+ virtual void PlaySound(IAESound *sound);
+ virtual void StopSound(IAESound *sound);
+ void MixSounds(float *buffer, unsigned int samples);
+
+ /* free's sounds that have expired */
+ virtual void GarbageCollect();
+
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+
+ virtual OSStatus Render(AudioUnitRenderActionFlags* actionFlags,
+ const AudioTimeStamp* pTimeStamp,
+ UInt32 busNumber,
+ UInt32 frameCount,
+ AudioBufferList* pBufList);
+
+
+private:
+ CCriticalSection m_callbackLock;
+ CCriticalSection m_streamLock;
+ CCriticalSection m_soundLock;
+ CCriticalSection m_soundSampleLock;
+
+ /* currently playing sounds */
+ typedef struct {
+ CCoreAudioAESound *owner;
+ float *samples;
+ unsigned int sampleCount;
+ } SoundState;
+
+ typedef std::list<CCoreAudioAEStream*> StreamList;
+ typedef std::list<CCoreAudioAESound* > SoundList;
+ typedef std::list<SoundState > SoundStateList;
+
+ StreamList m_streams;
+ SoundList m_sounds;
+ SoundStateList m_playing_sounds;
+
+ bool m_Initialized; // Prevent multiple init/deinit
+ bool m_callbackRunning;
+
+ AEAudioFormat m_format;
+ unsigned int m_chLayoutCount;
+ bool m_rawPassthrough;
+
+ enum AEStdChLayout m_stdChLayout;
+
+ bool OpenCoreAudio(unsigned int sampleRate = 44100, bool forceRaw = false, enum AEDataFormat rawDataFormat = AE_FMT_AC3);
+
+ void Deinitialize();
+ void Start();
+ void Stop();
+
+ OSStatus OnRender(AudioUnitRenderActionFlags *actionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+ float m_volume;
+ float m_volumeBeforeMute;
+ bool m_muted;
+ int m_soundMode;
+ bool m_streamsPlaying;
+};
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.cpp
new file mode 100644
index 0000000000..139fae7f85
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "CoreAudioAE.h"
+#include "CoreAudioAEHAL.h"
+#include "utils/log.h"
+#include <sstream>
+
+// Helper Functions
+std::string GetError(OSStatus error)
+{
+ char buffer[128];
+
+ *(UInt32 *)(buffer + 1) = CFSwapInt32HostToBig(error);
+ if (isprint(buffer[1]) && isprint(buffer[2]) &&
+ isprint(buffer[3]) && isprint(buffer[4]))
+ {
+ buffer[0] = buffer[5] = '\'';
+ buffer[6] = '\0';
+ }
+ else
+ {
+ // no, format it as an integer
+ sprintf(buffer, "%d", (int)error);
+ }
+
+ return std::string(buffer);
+}
+
+char* UInt32ToFourCC(UInt32* pVal) // NOT NULL TERMINATED! Modifies input value.
+{
+ UInt32 inVal = *pVal;
+ char* pIn = (char*)&inVal;
+ char* fourCC = (char*)pVal;
+ fourCC[3] = pIn[0];
+ fourCC[2] = pIn[1];
+ fourCC[1] = pIn[2];
+ fourCC[0] = pIn[3];
+ return fourCC;
+}
+
+const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string& str)
+{
+ UInt32 formatId = desc.mFormatID;
+ char fourCC[5];
+ fourCC[0]='\0';
+ strncat(fourCC, UInt32ToFourCC(&formatId), 4);
+ std::stringstream sstr;
+
+ switch (desc.mFormatID)
+ {
+ case kAudioFormatLinearPCM:
+ sstr << "["
+ << fourCC
+ << "] "
+ << ((desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "Mixable " )
+ << ((desc.mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? "Non-" : "" )
+ << "Interleaved "
+ << desc.mChannelsPerFrame
+ << " Channel "
+ << desc.mBitsPerChannel
+ << "-bit "
+ << ((desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Floating Point " : "Signed Integer ")
+ << ((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
+ << " ("
+ << (UInt32)desc.mSampleRate
+ << "Hz)";
+ str = sstr.str();
+ break;
+ case kAudioFormatAC3:
+ sstr << "["
+ << fourCC
+ << "] "
+ << ((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
+ << " AC-3/DTS ("
+ << (UInt32)desc.mSampleRate
+ << "Hz)";
+ str = sstr.str();
+ break;
+ case kAudioFormat60958AC3:
+ sstr << "["
+ << fourCC
+ << "] AC-3/DTS for S/PDIF ("
+ << (UInt32)desc.mSampleRate
+ << "Hz)";
+ str = sstr.str();
+ break;
+ default:
+ sstr << "["
+ << fourCC
+ << "]";
+ break;
+ }
+ return str.c_str();
+}
+
+void CheckOutputBufferSize(void **buffer, int *oldSize, int newSize)
+{
+ if (newSize > *oldSize)
+ {
+ if (*buffer)
+ _aligned_free(*buffer);
+ *buffer = _aligned_malloc(newSize, 16);
+ *oldSize = newSize;
+ }
+ memset(*buffer, 0x0, *oldSize);
+}
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.h
new file mode 100644
index 0000000000..b9bd13de93
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHAL.h
@@ -0,0 +1,36 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <list>
+#include "utils/StdString.h"
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+#ifdef TARGET_DARWIN_OSX
+#include <CoreAudio/CoreAudio.h>
+#endif
+
+// Helper Functions
+std::string GetError(OSStatus error);
+char* UInt32ToFourCC(UInt32* val);
+const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string& str);
+void CheckOutputBufferSize(void **buffer, int *oldSize, int newSize);
+
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.cpp
new file mode 100644
index 0000000000..7a3ed2989a
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.cpp
@@ -0,0 +1,1343 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#if defined(__APPLE__) && defined(__arm__)
+#include <math.h>
+
+#include "CoreAudioAEHALIOS.h"
+
+#include "PlatformDefs.h"
+#include "utils/log.h"
+#include "system.h"
+#include "CoreAudioAE.h"
+#include "AEUtil.h"
+#include "AEFactory.h"
+
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#define BUFFERED_FRAMES 1024
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CIOSCoreAudioHardware
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+AudioComponentInstance CIOSCoreAudioHardware::FindAudioDevice(std::string searchName)
+{
+ if (!searchName.length())
+ return 0;
+
+ AudioComponentInstance defaultDevice = GetDefaultOutputDevice();
+ CLog::Log(LOGDEBUG, "CIOSCoreAudioHardware::FindAudioDevice: Returning default device [0x%04x].", (uint32_t)defaultDevice);
+
+ return defaultDevice;
+}
+
+AudioComponentInstance CIOSCoreAudioHardware::GetDefaultOutputDevice()
+{
+ AudioComponentInstance ret = (AudioComponentInstance)1;
+
+ return ret;
+}
+
+UInt32 CIOSCoreAudioHardware::GetOutputDevices(IOSCoreAudioDeviceList* pList)
+{
+ if (!pList)
+ return 0;
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioUnit
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioUnit::CCoreAudioUnit() :
+m_Initialized (false ),
+m_pSource (NULL ),
+m_renderProc (NULL ),
+m_audioUnit (NULL ),
+m_audioNode (NULL ),
+m_audioGraph (NULL ),
+m_busNumber (INVALID_BUS )
+{
+}
+
+CCoreAudioUnit::~CCoreAudioUnit()
+{
+ Close();
+}
+
+bool CCoreAudioUnit::Open(AUGraph audioGraph, AudioComponentDescription desc)
+{
+ if (m_audioUnit)
+ Close();
+
+ OSStatus ret;
+
+ m_Initialized = false;
+
+ ret = AUGraphAddNode(audioGraph, &desc, &m_audioNode);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error add m_outputNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ ret = AUGraphNodeInfo(audioGraph, m_audioNode, NULL, &m_audioUnit);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error getting m_outputNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_audioGraph = audioGraph;
+ m_Initialized = true;
+
+ Start();
+
+ return true;
+}
+
+bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
+{
+ AudioComponentDescription desc;
+ desc.componentType = type;
+ desc.componentSubType = subType;
+ desc.componentManufacturer = manufacturer;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ return Open(audioGraph, desc);
+}
+
+void CCoreAudioUnit::Close()
+{
+ if (!m_Initialized && !m_audioUnit)
+ return;
+
+ if (m_renderProc)
+ SetInputSource(NULL);
+
+ Stop();
+
+ if (m_busNumber != INVALID_BUS)
+ {
+ OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
+ }
+ }
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ m_Initialized = false;
+ m_audioUnit = NULL;
+ m_audioNode = NULL;
+ m_pSource = NULL;
+}
+
+bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
+{
+ if (!m_audioUnit || !pDesc)
+ return false;
+
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
+{
+ if (!m_audioUnit || !pDesc)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
+{
+ m_pSource = pSource;
+ if (pSource)
+ return SetRenderProc();
+ else
+ return RemoveRenderProc();
+}
+
+bool CCoreAudioUnit::SetRenderProc()
+{
+ if (!m_audioUnit || m_renderProc)
+ return false;
+
+ AURenderCallbackStruct callbackInfo;
+ callbackInfo.inputProc = RenderCallback; // Function to be called each time the AudioUnit needs data
+ callbackInfo.inputProcRefCon = this; // Pointer to be returned in the callback proc
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_renderProc = RenderCallback;
+
+ CLog::Log(LOGDEBUG, "CCoreAudioUnit::SetRenderProc: Set RenderProc 0x%08x for unit 0x%08x.", (unsigned int)m_renderProc, (unsigned int)m_audioUnit);
+
+ return true;
+}
+
+bool CCoreAudioUnit::RemoveRenderProc()
+{
+ if (!m_audioUnit || !m_renderProc)
+ return false;
+
+
+ AURenderCallbackStruct callbackInfo;
+ callbackInfo.inputProc = nil;
+ callbackInfo.inputProcRefCon = nil;
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CCoreAudioUnit::RemoveRenderProc: Remove RenderProc 0x%08x for unit 0x%08x.", (unsigned int)m_renderProc, (unsigned int)m_audioUnit);
+
+ m_renderProc = NULL;
+
+ Sleep(100);
+
+ return true;
+}
+
+OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ OSStatus ret = noErr;
+ CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
+
+ if (audioUnit->m_pSource)
+ {
+ ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+ }
+ else
+ {
+ ioData->mBuffers[0].mDataByteSize = 0;
+ if (ioActionFlags)
+ *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
+ }
+
+
+ return ret;
+}
+
+void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
+ AudioStreamBasicDescription *streamDesc)
+{
+ unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
+
+ // Set the input stream format for the AudioUnit
+ // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
+ // The autput format is automaticaly set to the input format.
+ streamDesc->mFormatID = kAudioFormatLinearPCM; // Data encoding format
+ streamDesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
+ switch (format.m_dataFormat)
+ {
+ case AE_FMT_FLOAT:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
+ break;
+ case AE_FMT_S16NE:
+ case AE_FMT_AC3:
+ case AE_FMT_DTS:
+ case AE_FMT_DTSHD:
+ case AE_FMT_TRUEHD:
+ case AE_FMT_EAC3:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ case AE_FMT_S16LE:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ case AE_FMT_S16BE:
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ default:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ }
+ streamDesc->mChannelsPerFrame = format.m_channelLayout.Count(); // Number of interleaved audiochannels
+ streamDesc->mSampleRate = (Float64)format.m_sampleRate; // the sample rate of the audio stream
+ streamDesc->mBitsPerChannel = bps; // Number of bits per sample, per channel
+ streamDesc->mBytesPerFrame = (bps>>3) * format.m_channelLayout.Count(); // Size of a frame == 1 sample per channel
+ streamDesc->mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
+ streamDesc->mBytesPerPacket = streamDesc->mBytesPerFrame * streamDesc->mFramesPerPacket;
+ streamDesc->mReserved = 0;
+}
+
+float CCoreAudioUnit::GetLatency()
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ //kAudioSessionProperty_CurrentHardwareIOBufferDuration
+ //kAudioSessionProperty_CurrentHardwareOutputLatency
+
+ Float32 preferredBufferSize = 0.0f;
+ UInt32 size = sizeof(preferredBufferSize);
+ AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, &size, &preferredBufferSize);
+ return preferredBufferSize;
+}
+
+bool CCoreAudioUnit::SetSampleRate(Float64 sampleRate, AudioUnitScope scope, AudioUnitElement bus)
+{
+ if (!m_audioUnit)
+ return false;
+
+ UInt32 size = sizeof(Float64);
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SampleRate, scope, bus, &sampleRate, size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetSampleRate: Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s", (int)scope, (int)bus, GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::Stop()
+{
+ if (!m_audioUnit)
+ return false;
+
+ AudioOutputUnitStop(m_audioUnit);
+
+ return true;
+}
+
+bool CCoreAudioUnit::Start()
+{
+ if (!m_audioUnit)
+ return false;
+
+ AudioOutputUnitStart(m_audioUnit);
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CAUOutputDevice
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CAUOutputDevice::CAUOutputDevice()
+{
+}
+
+CAUOutputDevice::~CAUOutputDevice()
+{
+}
+
+/*
+Float32 CAUOutputDevice::GetCurrentVolume()
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float32 volPct = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return volPct;
+}
+
+bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
+ kAudioUnitScope_Global, 0, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+*/
+
+UInt32 CAUOutputDevice::GetBufferFrameSize()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ return BUFFERED_FRAMES;
+}
+
+bool CAUOutputDevice::EnableInputOuput()
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret;
+ UInt32 enable;
+ UInt32 hasio;
+ UInt32 size=sizeof(UInt32);
+
+ ret = AudioUnitGetProperty(m_audioUnit,kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
+
+ if (hasio)
+ {
+ enable = 1;
+ ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ enable = 1;
+ ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CAUMultiChannelMixer
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CAUMultiChannelMixer::CAUMultiChannelMixer()
+{
+}
+
+CAUMultiChannelMixer::~CAUMultiChannelMixer()
+{
+
+}
+
+UInt32 CAUMultiChannelMixer::GetInputBusCount()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ UInt32 busCount = 0;
+ UInt32 size = sizeof(busCount);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetInputBusCount: Unable to get input bus count. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return busCount;
+}
+
+bool CAUMultiChannelMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
+{
+ if (!m_audioUnit)
+ return false;
+
+ for (UInt32 i = 0; i < busCount; i++)
+ {
+ if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
+ return false;
+ }
+
+ return true;
+}
+
+bool CAUMultiChannelMixer::SetInputBusCount(UInt32 busCount)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetInputBusCount: Unable to set input bus count. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+UInt32 CAUMultiChannelMixer::GetOutputBusCount()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ UInt32 busCount = 0;
+ UInt32 size = sizeof(busCount);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetOutputBusCount: Unable to get output bus count. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return busCount;
+}
+
+bool CAUMultiChannelMixer::SetOutputBusCount(UInt32 busCount)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetOutputBusCount: Unable to set output bus count. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+Float32 CAUMultiChannelMixer::GetCurrentVolume()
+{
+
+ if (!m_audioUnit)
+ return false;
+
+ Float32 volPct = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, &volPct);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::GetCurrentVolume: Unable to get Mixer volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return volPct;
+
+}
+
+bool CAUMultiChannelMixer::SetCurrentVolume(Float32 vol)
+{
+
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMultiChannelMixer::SetCurrentVolume: Unable to set Mixer volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioGraph
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioGraph::CCoreAudioGraph() :
+m_audioGraph (NULL ),
+m_audioUnit (NULL ),
+m_mixerUnit (NULL ),
+m_inputUnit (NULL ),
+m_initialized (false),
+m_allowMixing (false)
+{
+ for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
+ {
+ m_reservedBusNumber[i] = false;
+ }
+}
+
+CCoreAudioGraph::~CCoreAudioGraph()
+{
+ Close();
+}
+
+bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing)
+{
+ OSStatus ret;
+
+ AudioStreamBasicDescription inputFormat;
+ AudioStreamBasicDescription outputFormat;
+
+ m_allowMixing = allowMixing;
+
+ ret = NewAUGraph(&m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error create audio grpah. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ ret = AUGraphOpen(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error open audio grpah. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // get output unit
+ if (m_audioUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error audio unit already open. double call ?");
+ return false;
+ }
+
+ m_audioUnit = new CAUOutputDevice();
+ if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple))
+ return false;
+
+ if (!m_audioUnit->EnableInputOuput())
+ return false;
+
+ m_audioUnit->GetFormatDesc(format, &inputFormat);
+
+ //if(!allowMixing)
+ //{
+ if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
+ return false;
+
+ if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
+ return false;
+ //}
+
+ if (allowMixing)
+ {
+ // get mixer unit
+ if (m_mixerUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
+ return false;
+ }
+
+ m_mixerUnit = new CAUMultiChannelMixer();
+
+ if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple))
+ return false;
+
+ // set number of input buses
+ if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
+ return false;
+
+ //if(!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
+ // return false;
+
+ m_mixerUnit->SetBus(0);
+
+ if (!m_audioUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
+ return false;
+
+ /*
+ if(!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &outputFormat))
+ return false;
+ */
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // get output unit
+ if (m_inputUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
+ return false;
+ }
+
+ m_inputUnit = new CAUOutputDevice();
+
+ if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
+ return false;
+
+ if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
+ return false;
+
+ /*
+ if(!m_inputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
+ return false;
+ */
+
+ // configure output unit
+ int busNumber = GetFreeBus();
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_converterNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_inputUnit->SetBus(busNumber);
+
+ ret = AUGraphUpdate(m_audioGraph, NULL);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ ret = AUGraphInitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // Regenerate audio format and copy format for the Output AU
+ }
+
+ ret = AUGraphUpdate(m_audioGraph, NULL);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ std::string formatString;
+ AudioStreamBasicDescription inputDesc_end, outputDesc_end;
+ m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+
+ if (m_mixerUnit)
+ {
+ m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+ }
+
+ if (m_inputUnit)
+ {
+ m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+ }
+
+ ret = AUGraphInitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
+
+ m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
+ if (m_inputUnit)
+ m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
+
+ SetInputSource(pSource);
+
+ ShowGraph();
+
+ return Start();
+}
+
+bool CCoreAudioGraph::Close()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+
+ Stop();
+
+ SetInputSource(NULL);
+
+ while (!m_auUnitList.empty())
+ {
+ CAUOutputDevice *d = m_auUnitList.front();
+ m_auUnitList.pop_front();
+ ReleaseBus(d->GetBus());
+ d->Close();
+ delete d;
+ }
+
+ if (m_inputUnit)
+ {
+ ReleaseBus(m_inputUnit->GetBus());
+ m_inputUnit->Close();
+ delete m_inputUnit;
+ m_inputUnit = NULL;
+ }
+
+ if (m_mixerUnit)
+ {
+ m_mixerUnit->Close();
+ delete m_mixerUnit;
+ m_mixerUnit = NULL;
+ }
+
+ if (m_audioUnit)
+ {
+ m_audioUnit->Close();
+ delete m_audioUnit;
+ m_audioUnit = NULL;
+ }
+
+ ret = AUGraphUninitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error unitialize. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = AUGraphClose(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error close. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = DisposeAUGraph(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error dispose. Error = %s", GetError(ret).c_str());
+ }
+
+ return true;
+}
+
+bool CCoreAudioGraph::Start()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+ Boolean isRunning = false;
+
+ ret = AUGraphIsRunning(m_audioGraph, &isRunning);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Audio graph not running. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ if (!isRunning)
+ {
+
+ if (m_audioUnit)
+ m_audioUnit->Start();
+ if (m_mixerUnit)
+ m_mixerUnit->Start();
+ if (m_inputUnit)
+ m_inputUnit->Start();
+
+ ret = AUGraphStart(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Error starting audio graph. Error = %s", GetError(ret).c_str());
+ }
+ ShowGraph();
+ }
+
+ return true;
+}
+
+bool CCoreAudioGraph::Stop()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+ Boolean isRunning = false;
+
+ ret = AUGraphIsRunning(m_audioGraph, &isRunning);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Audio graph not running. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ if (isRunning)
+ {
+
+ if (m_inputUnit)
+ m_inputUnit->Stop();
+ if (m_mixerUnit)
+ m_mixerUnit->Stop();
+ if (m_audioUnit)
+ m_audioUnit->Stop();
+
+ ret = AUGraphStop(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Error stopping audio graph. Error = %s", GetError(ret).c_str());
+ }
+ }
+
+ return true;
+}
+
+bool CCoreAudioGraph::SetInputSource(ICoreAudioSource* pSource)
+{
+ if (m_inputUnit)
+ return m_inputUnit->SetInputSource(pSource);
+ else if (m_audioUnit)
+ return m_audioUnit->SetInputSource(pSource);
+
+ return false;
+}
+
+bool CCoreAudioGraph::SetCurrentVolume(Float32 vol)
+{
+ if (!m_mixerUnit)
+ return false;
+
+ return m_mixerUnit->SetCurrentVolume(vol);
+}
+
+CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit)
+{
+ if (!outputUnit)
+ return NULL;
+
+ Stop();
+
+ for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt)
+ if (*itt == outputUnit)
+ {
+ m_auUnitList.erase(itt);
+ break;
+ }
+
+ ReleaseBus(outputUnit->GetBus());
+ outputUnit->Close();
+ delete outputUnit;
+ outputUnit = NULL;
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ printf("Remove unit\n\n");
+ ShowGraph();
+ printf("\n");
+
+ Start();
+
+ return NULL;
+}
+
+CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
+{
+ if (!m_audioUnit || !m_mixerUnit)
+ return NULL;
+
+ std::string formatString;
+ AudioStreamBasicDescription inputFormat;
+ AudioStreamBasicDescription outputFormat;
+
+ OSStatus ret;
+
+ int busNumber = GetFreeBus();
+ if (busNumber == INVALID_BUS)
+ return NULL;
+
+ // create output unit
+ CAUOutputDevice *outputUnit = new CAUOutputDevice();
+ if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
+ goto error;
+
+ m_audioUnit->GetFormatDesc(format, &inputFormat);
+
+ // get the format frm the mixer
+ if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
+ goto error;
+
+ if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
+ goto error;
+
+ if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
+ goto error;
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: Error connecting outputUnit. Error = %s", GetError(ret).c_str());
+ goto error;
+ }
+
+ // TODO: setup mixmap, get free bus number for connection
+
+ outputUnit->SetBus(busNumber);
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ printf("Add unit\n\n");
+ ShowGraph();
+ printf("\n");
+
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputFormat, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputFormat, formatString));
+
+ m_auUnitList.push_back(outputUnit);
+
+ return outputUnit;
+
+error:
+ delete outputUnit;
+ return NULL;
+}
+
+int CCoreAudioGraph::GetFreeBus()
+{
+ for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
+ {
+ if (!m_reservedBusNumber[i])
+ {
+ m_reservedBusNumber[i] = true;
+ return i;
+ }
+ }
+ return INVALID_BUS;
+}
+
+void CCoreAudioGraph::ReleaseBus(int busNumber)
+{
+ if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
+ return;
+
+ m_reservedBusNumber[busNumber] = false;
+}
+
+bool CCoreAudioGraph::IsBusFree(int busNumber)
+{
+ if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
+ return false;
+ return m_reservedBusNumber[busNumber];
+}
+
+int CCoreAudioGraph::GetMixerChannelOffset(int busNumber)
+{
+ if (!m_mixerUnit)
+ return 0;
+
+ int offset = 0;
+ AudioStreamBasicDescription fmt;
+
+ for (int i = 0; i < busNumber; i++)
+ {
+ memset(&fmt, 0x0, sizeof(fmt));
+ m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
+ offset += fmt.mChannelsPerFrame;
+ }
+ return offset;
+}
+
+void CCoreAudioGraph::ShowGraph()
+{
+ CAShow(m_audioGraph);
+}
+
+float CCoreAudioGraph::GetLatency()
+{
+ float delay = 0.0f;
+
+ if (m_audioUnit)
+ delay += m_audioUnit->GetLatency();
+
+ return delay;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioAEHALIOS
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CCoreAudioAEHALIOS::CCoreAudioAEHALIOS() :
+m_Initialized (false ),
+m_Passthrough (false ),
+m_NumLatencyFrames (0 ),
+m_OutputBufferIndex (0 ),
+m_allowMixing (false ),
+m_encoded (false ),
+m_audioGraph (NULL )
+{
+}
+
+CCoreAudioAEHALIOS::~CCoreAudioAEHALIOS()
+{
+ Deinitialize();
+
+ delete m_audioGraph;
+}
+
+bool CCoreAudioAEHALIOS::InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing)
+{
+
+ if (m_audioGraph)
+ {
+ m_audioGraph->Close();
+ delete m_audioGraph;
+ }
+ m_audioGraph = new CCoreAudioGraph();
+
+ if (!m_audioGraph)
+ return false;
+
+ if (!m_audioGraph->Open(pSource, format, allowMixing))
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALIOS::Initialize: Unable to initialize audio due a missconfiguration. Try 2.0 speaker configuration.");
+ return false;
+ }
+
+ m_NumLatencyFrames = 0;
+
+ m_allowMixing = allowMixing;
+
+ return true;
+}
+
+bool CCoreAudioAEHALIOS::InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format)
+{
+ if (!InitializePCM(pSource, format, false))
+ return false;
+
+ return true;
+}
+
+bool CCoreAudioAEHALIOS::Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device)
+{
+ m_ae = (CCoreAudioAE *)ae;
+
+ if (!m_ae)
+ return false;
+
+ m_initformat = format;
+ m_Passthrough = passThrough;
+ m_encoded = false;
+ m_OutputBufferIndex = 0;
+ m_rawDataFormat = rawDataFormat;
+
+ if (format.m_channelLayout.Count() == 0)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioAEHALIOS::Initialize - Unable to open the requested channel layout");
+ return false;
+ }
+
+ if (device.find("CoreAudio:"))
+ device.erase(0, strlen("CoreAudio:"));
+
+ // If this is a passthrough (AC3/DTS) stream, attempt to handle it natively
+ if (m_Passthrough)
+ {
+ m_encoded = false;
+ }
+
+ // If this is a PCM stream, or we failed to handle a passthrough stream natively,
+ // prepare the standard interleaved PCM interface
+ if (!m_encoded)
+ {
+ // If we are here and this is a passthrough stream, native handling failed.
+ // Try to handle it as IEC61937 data over straight PCM (DD-Wav)
+ bool configured = false;
+ if (m_Passthrough)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALIOS::Initialize: No suitable AC3 output format found. Attempting DD-Wav.");
+ configured = InitializePCMEncoded(ae, format);
+ }
+ else
+ {
+ // Standard PCM data
+ configured = InitializePCM(ae, format, true);
+ }
+
+ if (!configured) // No suitable output format was able to be configured
+ return false;
+ }
+
+ if (m_audioGraph)
+ m_audioGraph->ShowGraph();
+
+ m_Initialized = true;
+
+ return true;
+}
+
+CAUOutputDevice *CCoreAudioAEHALIOS::DestroyUnit(CAUOutputDevice *outputUnit)
+{
+ if (m_audioGraph && outputUnit)
+ return m_audioGraph->DestroyUnit(outputUnit);
+
+ return NULL;
+}
+
+CAUOutputDevice *CCoreAudioAEHALIOS::CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format)
+{
+ CAUOutputDevice *outputUnit = NULL;
+
+ // when HAL is using a mixer, the input is routed through converter units.
+ // therefore we create a converter unit attach the source and give it back.
+ if (m_allowMixing && m_audioGraph)
+ {
+ outputUnit = m_audioGraph->CreateUnit(format);
+
+ if (pSource && outputUnit)
+ outputUnit->SetInputSource(pSource);
+ }
+
+ return outputUnit;
+}
+
+void CCoreAudioAEHALIOS::Deinitialize()
+{
+ if (!m_Initialized)
+ return;
+
+ Stop();
+
+ //if (m_encoded)
+
+ if (m_audioGraph)
+ m_audioGraph->SetInputSource(NULL);
+
+ if (m_audioGraph)
+ {
+ //m_audioGraph->Close();
+ delete m_audioGraph;
+ }
+ m_audioGraph = NULL;
+
+ m_NumLatencyFrames = 0;
+ m_OutputBufferIndex = 0;
+
+ m_Initialized = false;
+ m_Passthrough = false;
+
+ CLog::Log(LOGINFO, "CCoreAudioAEHALIOS::Deinitialize: Audio device has been closed.");
+}
+
+void CCoreAudioAEHALIOS::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ IOSCoreAudioDeviceList deviceList;
+ CIOSCoreAudioHardware::GetOutputDevices(&deviceList);
+
+ // Add default output device if GetOutputDevices return nothing
+ devices.push_back(AEDevice("Default", "IOSCoreAudio:default"));
+
+ std::string deviceName;
+ for (int i = 0; !deviceList.empty(); i++)
+ {
+ std::string deviceName_Internal = std::string("IOSCoreAudio:") + deviceName;
+ devices.push_back(AEDevice(deviceName, deviceName_Internal));
+
+ deviceList.pop_front();
+
+ }
+}
+
+void CCoreAudioAEHALIOS::Stop()
+{
+ if (!m_Initialized)
+ return;
+
+ m_audioGraph->Stop();
+}
+
+bool CCoreAudioAEHALIOS::Start()
+{
+ if (!m_Initialized)
+ return false;
+
+ m_audioGraph->Start();
+
+ return true;
+}
+
+void CCoreAudioAEHALIOS::SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format)
+{
+ if (!m_Initialized)
+ return;
+
+ // when HAL is initialized encoded we use directIO
+ // when HAL is not in encoded mode and there is no mixer attach source the audio unit
+ // when mixing is allowed in HAL, HAL is working with converter units where we attach the source.
+
+ if (!m_encoded && !m_allowMixing)
+ {
+ // register render callback for the audio unit
+ m_audioGraph->SetInputSource(pSource);
+ }
+
+ if (m_audioGraph)
+ m_audioGraph->ShowGraph();
+
+}
+
+double CCoreAudioAEHALIOS::GetDelay()
+{
+ /*
+ float delay;
+
+ delay = (float)(m_NumLatencyFrames) / (m_initformat.m_sampleRate);
+
+ return delay;
+ */
+
+ return (double)(BUFFERED_FRAMES) / (double)(m_initformat.m_sampleRate);
+}
+
+void CCoreAudioAEHALIOS::SetVolume(float volume)
+{
+ if (!m_encoded)
+ m_audioGraph->SetCurrentVolume(volume);
+}
+
+unsigned int CCoreAudioAEHALIOS::GetBufferIndex()
+{
+ return m_OutputBufferIndex;
+}
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.h
new file mode 100644
index 0000000000..3ae6e8ff9c
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALIOS.h
@@ -0,0 +1,203 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifdef __arm__
+
+#include "ICoreAudioAEHAL.h"
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioUnit/AudioUnitProperties.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioToolbox/AudioServices.h>
+#include <CoreAudio/CoreAudioTypes.h>
+#include <AudioToolbox/AUGraph.h>
+#include <list>
+#include <vector>
+
+#include "utils/StdString.h"
+
+#include "CoreAudioAEHAL.h"
+
+#define kOutputBus 0
+#define kInputBus 1
+#define MAX_CONNECTION_LIMIT 8
+#define INVALID_BUS -1
+
+// Forward declarations
+class CCoreAudioAE;
+class CIOSCoreAudioConverter;
+
+typedef std::list<AudioComponentInstance> IOSCoreAudioDeviceList;
+
+// There is only one AudioSystemObject instance system-side.
+// Therefore, all CIOSCoreAudioHardware methods are static
+class CIOSCoreAudioHardware
+{
+public:
+ static AudioComponentInstance FindAudioDevice(std::string deviceName);
+ static AudioComponentInstance GetDefaultOutputDevice();
+ static UInt32 GetOutputDevices(IOSCoreAudioDeviceList* pList);
+};
+
+class CCoreAudioUnit
+{
+public:
+ CCoreAudioUnit();
+ virtual ~CCoreAudioUnit();
+
+ virtual bool Open(AUGraph audioGraph, AudioComponentDescription desc);
+ virtual bool Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer);
+ virtual void Close();
+ virtual bool SetInputSource(ICoreAudioSource* pSource);
+ virtual bool IsInitialized() {return m_Initialized;}
+ virtual bool GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus);
+ virtual bool SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus);
+ virtual bool SetMaxFramesPerSlice(UInt32 maxFrames);
+ virtual void GetFormatDesc(AEAudioFormat format, AudioStreamBasicDescription *streamDesc);
+ virtual float GetLatency();
+ virtual bool SetSampleRate(Float64 sampleRate, AudioUnitScope scope, AudioUnitElement bus);
+ virtual bool Stop();
+ virtual bool Start();
+ virtual AudioUnit GetUnit (){return m_audioUnit;}
+ virtual AUGraph GetGraph (){return m_audioGraph;}
+ virtual AUNode GetNode (){return m_audioNode;}
+ virtual int GetBus (){return m_busNumber;}
+ virtual void SetBus (int busNumber){m_busNumber = busNumber;}
+protected:
+ bool SetRenderProc();
+ bool RemoveRenderProc();
+ static OSStatus RenderCallback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+ ICoreAudioSource* m_pSource;
+ AudioUnit m_audioUnit;
+ AUNode m_audioNode;
+ AUGraph m_audioGraph;
+ bool m_Initialized;
+ AURenderCallback m_renderProc;
+ int m_busNumber;
+};
+
+class CAUOutputDevice : public CCoreAudioUnit
+{
+public:
+ CAUOutputDevice();
+ virtual ~CAUOutputDevice();
+ UInt32 GetBufferFrameSize();
+
+ /*
+ Float32 GetCurrentVolume();
+ bool SetCurrentVolume(Float32 vol);
+ */
+ bool EnableInputOuput();
+};
+
+class CAUMultiChannelMixer : public CAUOutputDevice
+{
+public:
+ CAUMultiChannelMixer();
+ virtual ~CAUMultiChannelMixer();
+
+ UInt32 GetInputBusCount();
+ bool SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat);
+ bool SetInputBusCount(UInt32 busCount);
+ UInt32 GetOutputBusCount();
+ bool SetOutputBusCount(UInt32 busCount);
+
+ Float32 GetCurrentVolume();
+ bool SetCurrentVolume(Float32 vol);
+};
+
+class CCoreAudioGraph
+{
+private:
+ AUGraph m_audioGraph;
+
+ CAUOutputDevice *m_audioUnit;
+ CAUMultiChannelMixer *m_mixerUnit;
+ CAUOutputDevice *m_inputUnit;
+
+ int m_reservedBusNumber[MAX_CONNECTION_LIMIT];
+ bool m_initialized;
+ bool m_allowMixing;
+
+ typedef std::list<CAUOutputDevice*> AUUnitList;
+ AUUnitList m_auUnitList;
+
+public:
+ CCoreAudioGraph();
+ ~CCoreAudioGraph();
+
+ bool Open(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing);
+ bool Close();
+ bool Start();
+ bool Stop();
+ bool SetInputSource(ICoreAudioSource* pSource);
+ bool SetCurrentVolume(Float32 vol);
+ CAUOutputDevice *DestroyUnit(CAUOutputDevice *outputUnit);
+ CAUOutputDevice *CreateUnit(AEAudioFormat &format);
+ int GetFreeBus();
+ void ReleaseBus(int busNumber);
+ bool IsBusFree(int busNumber);
+ int GetMixerChannelOffset(int busNumber);
+ void ShowGraph();
+ float GetLatency();
+};
+
+class CCoreAudioAEHALIOS : public ICoreAudioAEHAL
+{
+protected:
+ CCoreAudioGraph *m_audioGraph;
+ bool m_Initialized;
+ bool m_Passthrough;
+ AEAudioFormat m_initformat;
+ bool m_allowMixing;
+ bool m_encoded;
+ AEDataFormat m_rawDataFormat;
+public:
+ unsigned int m_NumLatencyFrames;
+ unsigned int m_OutputBufferIndex;
+ CCoreAudioAE *m_ae;
+
+ CCoreAudioAEHALIOS();
+ virtual ~CCoreAudioAEHALIOS();
+
+ virtual bool InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing);
+ virtual bool InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual bool Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device);
+ virtual void Deinitialize();
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+ virtual void SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual void Stop();
+ virtual bool Start();
+ virtual double GetDelay();
+ virtual void SetVolume(float volume);
+ virtual unsigned int GetBufferIndex();
+ virtual CAUOutputDevice *DestroyUnit(CAUOutputDevice *outputUnit);
+ virtual CAUOutputDevice *CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual bool AllowMixing() { return m_allowMixing; }
+};
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.cpp
new file mode 100644
index 0000000000..39a858765b
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.cpp
@@ -0,0 +1,3303 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __arm__
+#include "CoreAudioAEHALOSX.h"
+
+#include <PlatformDefs.h>
+#include <math.h>
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "system.h"
+#include "CoreAudioAE.h"
+#include "AEUtil.h"
+#include "AEFactory.h"
+#include "utils/SystemInfo.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+
+const char* g_ChannelLabels[] =
+{
+ "Unused", // kAudioChannelLabel_Unused
+ "Left", // kAudioChannelLabel_Left
+ "Right", // kAudioChannelLabel_Right
+ "Center", // kAudioChannelLabel_Center
+ "LFE", // kAudioChannelLabel_LFEScreen
+ "Side Left", // kAudioChannelLabel_LeftSurround
+ "Side Right", // kAudioChannelLabel_RightSurround
+ "Left Center", // kAudioChannelLabel_LeftCenter
+ "Right Center", // kAudioChannelLabel_RightCenter
+ "Back Center", // kAudioChannelLabel_CenterSurround
+ "Back Left", // kAudioChannelLabel_LeftSurroundDirect
+ "Back Right", // kAudioChannelLabel_RightSurroundDirect
+ "Top Center", // kAudioChannelLabel_TopCenterSurround
+ "Top Back Left", // kAudioChannelLabel_VerticalHeightLeft
+ "Top Back Center", // kAudioChannelLabel_VerticalHeightCenter
+ "Top Back Right", // kAudioChannelLabel_VerticalHeightRight
+};
+
+const AudioChannelLabel g_LabelMap[] =
+{
+ kAudioChannelLabel_Unused, // PCM_FRONT_LEFT,
+ kAudioChannelLabel_Left, // PCM_FRONT_LEFT,
+ kAudioChannelLabel_Right, // PCM_FRONT_RIGHT,
+ kAudioChannelLabel_Center, // PCM_FRONT_CENTER,
+ kAudioChannelLabel_LFEScreen, // PCM_LOW_FREQUENCY,
+ kAudioChannelLabel_LeftSurroundDirect, // PCM_BACK_LEFT, *** This is incorrect, but has been changed to match dvdplayer
+ kAudioChannelLabel_RightSurroundDirect, // PCM_BACK_RIGHT, *** This is incorrect, but has been changed to match dvdplayer
+ kAudioChannelLabel_LeftCenter, // PCM_FRONT_LEFT_OF_CENTER,
+ kAudioChannelLabel_RightCenter, // PCM_FRONT_RIGHT_OF_CENTER,
+ kAudioChannelLabel_CenterSurround, // PCM_BACK_CENTER,
+ kAudioChannelLabel_LeftSurround, // PCM_SIDE_LEFT, *** This is incorrect, but has been changed to match dvdplayer
+ kAudioChannelLabel_RightSurround, // PCM_SIDE_RIGHT, *** This is incorrect, but has been changed to match dvdplayer
+ kAudioChannelLabel_VerticalHeightLeft, // PCM_TOP_FRONT_LEFT,
+ kAudioChannelLabel_VerticalHeightRight, // PCM_TOP_FRONT_RIGHT,
+ kAudioChannelLabel_VerticalHeightCenter, // PCM_TOP_FRONT_CENTER,
+ kAudioChannelLabel_TopCenterSurround, // PCM_TOP_CENTER,
+ kAudioChannelLabel_TopBackLeft, // PCM_TOP_BACK_LEFT,
+ kAudioChannelLabel_TopBackRight, // PCM_TOP_BACK_RIGHT,
+ kAudioChannelLabel_TopBackCenter // PCM_TOP_BACK_CENTER
+};
+
+const AudioChannelLayoutTag g_LayoutMap[] =
+{
+ kAudioChannelLayoutTag_Stereo, // PCM_LAYOUT_2_0 = 0,
+ kAudioChannelLayoutTag_Stereo, // PCM_LAYOUT_2_0 = 0,
+ kAudioChannelLayoutTag_DVD_4, // PCM_LAYOUT_2_1,
+ kAudioChannelLayoutTag_MPEG_3_0_A, // PCM_LAYOUT_3_0,
+ kAudioChannelLayoutTag_DVD_10, // PCM_LAYOUT_3_1,
+ kAudioChannelLayoutTag_DVD_3, // PCM_LAYOUT_4_0,
+ kAudioChannelLayoutTag_DVD_6, // PCM_LAYOUT_4_1,
+ kAudioChannelLayoutTag_MPEG_5_0_A, // PCM_LAYOUT_5_0,
+ kAudioChannelLayoutTag_MPEG_5_1_A, // PCM_LAYOUT_5_1,
+ kAudioChannelLayoutTag_AudioUnit_7_0, // PCM_LAYOUT_7_0, ** This layout may be incorrect...no content to testß˚ **
+ kAudioChannelLayoutTag_MPEG_7_1_A, // PCM_LAYOUT_7_1
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioHardware
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool CCoreAudioHardware::GetAutoHogMode()
+{
+ UInt32 val = 0;
+ UInt32 size = sizeof(val);
+ OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyHogModeIsAllowed, &size, &val);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetAutoHogMode: Unable to get auto 'hog' mode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return (val == 1);
+}
+
+void CCoreAudioHardware::SetAutoHogMode(bool enable)
+{
+ UInt32 val = enable ? 1 : 0;
+ OSStatus ret = AudioHardwareSetProperty(kAudioHardwarePropertyHogModeIsAllowed, sizeof(val), &val);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: Unable to set auto 'hog' mode. Error = %s", GetError(ret).c_str());
+}
+
+AudioStreamBasicDescription *CCoreAudioHardware::FormatsList(AudioStreamID stream)
+{
+ OSStatus ret;
+ AudioStreamBasicDescription *list;
+ UInt32 listSize;
+ AudioDevicePropertyID p;
+
+
+ // This is deprecated for kAudioStreamPropertyAvailablePhysicalFormats,
+ // but compiling on 10.3 requires the older constant
+ p = kAudioStreamPropertyPhysicalFormats;
+
+ // Retrieve all the stream formats supported by this output stream
+ ret = AudioStreamGetPropertyInfo(stream, 0, p, &listSize, NULL);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: Unable to get list size. Error = %s", GetError(ret).c_str());
+ return NULL;
+ }
+
+ // Space for a terminating ID:
+ listSize += sizeof(AudioStreamBasicDescription);
+ list = (AudioStreamBasicDescription *)malloc(listSize);
+
+ if (list == NULL)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::FormatsList: Out of memory?");
+ return NULL;
+ }
+
+ ret = AudioStreamGetProperty(stream, 0, p, &listSize, list);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FormatsList: Unable to get list. Error = %s", GetError(ret).c_str());
+ free(list);
+ return NULL;
+ }
+
+ // Add a terminating ID:
+ list[listSize/sizeof(AudioStreamID)].mFormatID = 0;
+
+ return list;
+}
+
+/**
+ * Get a list of all the streams on this device
+ */
+AudioStreamID *CCoreAudioHardware::StreamsList(AudioDeviceID device)
+{
+ OSStatus ret;
+ UInt32 listSize;
+ AudioStreamID *list;
+
+
+ ret = AudioDeviceGetPropertyInfo(device, 0, FALSE,
+ kAudioDevicePropertyStreams,
+ &listSize, NULL);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: Unable to get list size. Error = %s", GetError(ret).c_str());
+ return NULL;
+ }
+
+ // Space for a terminating ID:
+ listSize += sizeof(AudioStreamID);
+ list = (AudioStreamID *)malloc(listSize);
+
+ if (list == NULL)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: Out of memory?");
+ return NULL;
+ }
+
+ ret = AudioDeviceGetProperty(device, 0, FALSE,
+ kAudioDevicePropertyStreams,
+ &listSize, list);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::StreamsList: Unable to get list. Error = %s", GetError(ret).c_str());
+ return NULL;
+ }
+
+ // Add a terminating ID:
+ list[listSize/sizeof(AudioStreamID)] = kAudioHardwareBadStreamError;
+
+ return list;
+}
+
+/**
+ * Reset any devices with an AC3 stream back to a Linear PCM
+ * so that they can become a default output device
+ */
+void CCoreAudioHardware::ResetAudioDevices()
+{
+ AudioDeviceID *devices;
+ int numDevices;
+ UInt32 size;
+
+ AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
+ devices = (AudioDeviceID*)malloc(size);
+ if (!devices)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::ResetAudioDevices: ResetAudioDevices - out of memory?");
+ return;
+ }
+ numDevices = size / sizeof(AudioDeviceID);
+ AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
+
+ for (int i = 0; i < numDevices; i++)
+ {
+ AudioStreamID *streams;
+
+ streams = StreamsList(devices[i]);
+ for (int j = 0; streams[j] != kAudioHardwareBadStreamError; j++)
+ ResetStream(streams[j]);
+
+ free(streams);
+ }
+ free(devices);
+}
+
+void CCoreAudioHardware::ResetStream(AudioStreamID stream)
+{
+ AudioStreamBasicDescription currentFormat;
+ OSStatus ret;
+ UInt32 paramSize;
+
+ // Find the streams current physical format
+ paramSize = sizeof(currentFormat);
+ AudioStreamGetProperty(stream, 0, kAudioStreamPropertyPhysicalFormat,
+ &paramSize, &currentFormat);
+
+ // If it's currently AC-3/SPDIF then reset it to some mixable format
+ if (currentFormat.mFormatID == 'IAC3' ||
+ currentFormat.mFormatID == kAudioFormat60958AC3)
+ {
+ AudioStreamBasicDescription *formats = CCoreAudioHardware::FormatsList(stream);
+ bool streamReset = false;
+
+
+ if (!formats)
+ return;
+
+ for (int i = 0; !streamReset && formats[i].mFormatID != 0; i++)
+ {
+ if (formats[i].mFormatID == kAudioFormatLinearPCM)
+ {
+ ret = AudioStreamSetProperty(stream, NULL, 0, kAudioStreamPropertyPhysicalFormat, sizeof(formats[i]), &(formats[i]));
+ if (ret != noErr)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream: Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
+ continue;
+ }
+ else
+ {
+ streamReset = true;
+ Sleep(10);
+ }
+ }
+ }
+ free(formats);
+ }
+}
+
+AudioDeviceID CCoreAudioHardware::FindAudioDevice(std::string searchName)
+{
+ if (!searchName.length())
+ return 0;
+
+ UInt32 size = 0;
+ AudioDeviceID deviceId = 0;
+ OSStatus ret;
+ std::string searchNameLowerCase = searchName;
+
+ std::transform( searchNameLowerCase.begin(), searchNameLowerCase.end(), searchNameLowerCase.begin(), ::tolower );
+ if (searchNameLowerCase.compare("default") == 0)
+ {
+ AudioDeviceID defaultDevice = GetDefaultOutputDevice();
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: Returning default device [0x%04x].", defaultDevice);
+ return defaultDevice;
+ }
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: Searching for device - %s.", searchName.c_str());
+
+ // Obtain a list of all available audio devices
+ AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
+ ret = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, pDevices);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
+ delete[] pDevices;
+ return 0;
+ }
+
+ // Attempt to locate the requested device
+ std::string deviceName;
+ for (UInt32 dev = 0; dev < deviceCount; dev++)
+ {
+ CCoreAudioDevice device;
+ device.Open((pDevices[dev]));
+ deviceName = device.GetName();
+ UInt32 totalChannels = device.GetTotalOutputChannels();
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: Device[0x%04x] - Name: '%s', Total Ouput Channels: %u. ", pDevices[dev], deviceName.c_str(), totalChannels);
+
+ std::transform( deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower );
+ if (searchNameLowerCase.compare(deviceName) == 0)
+ deviceId = pDevices[dev];
+ if (deviceId)
+ break;
+ }
+ delete[] pDevices;
+
+ return deviceId;
+}
+
+AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
+{
+ UInt32 size = sizeof(AudioDeviceID);
+ AudioDeviceID deviceId = 0;
+
+ OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &deviceId);
+ if (ret || !deviceId) // outputDevice is set to 0 if there is no audio device available, or if the default device is set to an encoded format
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice: Unable to identify default output device. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return deviceId;
+}
+
+void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
+{
+ UInt32 size = 0;
+ char *m_buffer;
+ AudioDeviceID deviceId = GetDefaultOutputDevice();
+
+ if(deviceId)
+ {
+ AudioDeviceGetPropertyInfo(deviceId,0, false, kAudioDevicePropertyDeviceName, &size, NULL); // TODO: Change to kAudioObjectPropertyObjectName
+ m_buffer = (char *)malloc(size);
+
+ OSStatus ret = AudioDeviceGetProperty(deviceId, 0, false, kAudioDevicePropertyDeviceName, &size, m_buffer);
+ if (ret && !m_buffer)
+ {
+ name ="Default";
+ }
+ else
+ {
+ name = m_buffer;
+ free(m_buffer);
+ }
+ }
+ else
+ {
+ name = "Default";
+ }
+
+
+}
+
+UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList* pList)
+{
+ if (!pList)
+ return 0;
+
+ // Obtain a list of all available audio devices
+ UInt32 found = 0;
+ UInt32 size = 0;
+ AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
+ OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, pDevices);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices: Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
+ else
+ {
+ for (UInt32 dev = 0; dev < deviceCount; dev++)
+ {
+ CCoreAudioDevice device(pDevices[dev]);
+ if (device.GetTotalOutputChannels() == 0)
+ continue;
+ found++;
+ pList->push_back(pDevices[dev]);
+ }
+ }
+ delete[] pDevices;
+ return found;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioDevice
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioDevice::CCoreAudioDevice() :
+ m_DeviceId (0 ),
+ m_Started (false ),
+ m_HogPid (-1 ),
+ m_MixerRestore (-1 ),
+ m_IoProc (NULL ),
+ m_ObjectListenerProc (NULL ),
+ m_SampleRateRestore (0.0f ),
+ m_frameSize (0 ),
+ m_OutputBufferIndex (0 ),
+ m_pSource (NULL )
+{
+}
+
+CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
+ m_DeviceId (deviceId ),
+ m_Started (false ),
+ m_HogPid (-1 ),
+ m_MixerRestore (-1 ),
+ m_IoProc (NULL ),
+ m_ObjectListenerProc (NULL ),
+ m_SampleRateRestore (0.0f ),
+ m_frameSize (0 ),
+ m_OutputBufferIndex (0 ),
+ m_pSource (NULL )
+{
+}
+
+CCoreAudioDevice::~CCoreAudioDevice()
+{
+ Close();
+}
+
+bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
+{
+ m_DeviceId = deviceId;
+ return true;
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::Open: Opened device 0x%04x", m_DeviceId);
+}
+
+void CCoreAudioDevice::Close()
+{
+ if (!m_DeviceId)
+ return;
+
+ Stop(); // Stop the device if it was started
+
+ // Unregister the IOProc if we have one
+ if (m_IoProc)
+ SetInputSource(NULL, 0, 0);
+
+ SetHogStatus(false);
+ if (m_MixerRestore > -1) // We changed the mixer status
+ SetMixingSupport((m_MixerRestore ? true : false));
+ m_MixerRestore = -1;
+
+ if (m_SampleRateRestore != 0.0f)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::Close: Restoring original nominal samplerate.");
+ SetNominalSampleRate(m_SampleRateRestore);
+ }
+
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::Close: Closed device 0x%04x", m_DeviceId);
+ m_DeviceId = 0;
+ m_IoProc = NULL;
+ m_ObjectListenerProc = NULL;
+ m_pSource = NULL;
+}
+
+void CCoreAudioDevice::Start()
+{
+ if (!m_DeviceId || m_Started)
+ return;
+
+ OSStatus ret = AudioDeviceStart(m_DeviceId, m_IoProc);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::Start: Unable to start device. Error = %s", GetError(ret).c_str());
+ else
+ m_Started = true;
+}
+
+void CCoreAudioDevice::Stop()
+{
+ if (!m_DeviceId || !m_Started)
+ return;
+
+ OSStatus ret = AudioDeviceStop(m_DeviceId, m_IoProc);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: Unable to stop device. Error = %s", GetError(ret).c_str());
+ m_Started = false;
+}
+
+void CCoreAudioDevice::RemoveObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
+{
+ if (!m_DeviceId)
+ return;
+
+ AudioObjectPropertyAddress audioProperty;
+ audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
+ audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
+ audioProperty.mElement = kAudioObjectPropertyElementWildcard;
+
+ OSStatus ret = AudioObjectRemovePropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveObjectListenerProc: Unable to set ObjectListener callback. Error = %s", GetError(ret).c_str());
+ }
+ m_ObjectListenerProc = NULL;
+}
+
+bool CCoreAudioDevice::SetObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData)
+{
+ if (!m_DeviceId || m_ObjectListenerProc) // Only one ObjectListener at a time
+ return false;
+
+ AudioObjectPropertyAddress audioProperty;
+ audioProperty.mSelector = kAudioObjectPropertySelectorWildcard;
+ audioProperty.mScope = kAudioObjectPropertyScopeWildcard;
+ audioProperty.mElement = kAudioObjectPropertyElementWildcard;
+
+ OSStatus ret = AudioObjectAddPropertyListener(m_DeviceId, &audioProperty, callback, pClientData);
+
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetObjectListenerProc: Unable to remove ObjectListener callback. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_ObjectListenerProc = callback;
+ return true;
+}
+
+bool CCoreAudioDevice::SetInputSource(ICoreAudioSource* pSource, unsigned int frameSize, unsigned int outputBufferIndex)
+{
+ m_pSource = pSource;
+ m_frameSize = frameSize;
+ m_OutputBufferIndex = outputBufferIndex;
+
+ if (pSource)
+ return AddIOProc();
+ else
+ return RemoveIOProc();
+}
+
+bool CCoreAudioDevice::AddIOProc()
+{
+ if (!m_DeviceId || m_IoProc) // Only one IOProc at a time
+ return false;
+
+ OSStatus ret = AudioDeviceCreateIOProcID(m_DeviceId, DirectRenderCallback, this, &m_IoProc);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::AddIOProc: Unable to add IOProc. Error = %s", GetError(ret).c_str());
+ m_IoProc = NULL;
+ return false;
+ }
+
+ Start();
+
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::AddIOProc: IOProc 0x%08x set for device 0x%04x", m_IoProc, m_DeviceId);
+ return true;
+}
+
+bool CCoreAudioDevice::RemoveIOProc()
+{
+ if (!m_DeviceId || !m_IoProc)
+ return false;
+
+ Stop();
+
+ OSStatus ret = AudioDeviceDestroyIOProcID(m_DeviceId, m_IoProc);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveIOProc: Unable to remove IOProc. Error = %s", GetError(ret).c_str());
+ else
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::RemoveIOProc: IOProc 0x%08x removed for device 0x%04x", m_IoProc, m_DeviceId);
+ m_IoProc = NULL; // Clear the reference no matter what
+
+ m_pSource = NULL;
+
+ Sleep(100);
+
+ return true;
+}
+
+std::string CCoreAudioDevice::GetName()
+{
+ std::string name;
+ if (!m_DeviceId)
+ return NULL;
+
+ UInt32 size = 0;
+ AudioDeviceGetPropertyInfo(m_DeviceId,0, false, kAudioDevicePropertyDeviceName, &size, NULL); // TODO: Change to kAudioObjectPropertyObjectName
+ char *buff = new char[size];
+ OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyDeviceName, &size, buff);
+ name.assign(buff, size-1);
+ delete [] buff;
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: Unable to get device name - id: 0x%04x. Error = %s", GetError(ret).c_str());
+ return NULL;
+ }
+ return name;
+}
+
+UInt32 CCoreAudioDevice::GetTotalOutputChannels()
+{
+ if (!m_DeviceId)
+ return 0;
+ UInt32 channels = 0;
+ UInt32 size = 0;
+ AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyStreamConfiguration, &size, NULL);
+ AudioBufferList* pList = (AudioBufferList*)malloc(size);
+ OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyStreamConfiguration, &size, pList);
+ if (!ret)
+ for(UInt32 buffer = 0; buffer < pList->mNumberBuffers; ++buffer)
+ channels += pList->mBuffers[buffer].mNumberChannels;
+ else
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetTotalOutputChannels: Unable to get total device output channels - id: 0x%04x. Error = %s", m_DeviceId, GetError(ret).c_str());
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::GetTotalOutputChannels: Found %u channels in %u buffers", channels, pList->mNumberBuffers);
+ free(pList);
+ return channels;
+}
+
+bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
+{
+ if (!pList || !m_DeviceId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyStreams, &propertySize, &writable);
+ if (ret)
+ return false;
+ UInt32 streamCount = propertySize / sizeof(AudioStreamID);
+ AudioStreamID* pStreamList = new AudioStreamID[streamCount];
+ ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyStreams, &propertySize, pStreamList);
+ if (!ret)
+ {
+ for (UInt32 stream = 0; stream < streamCount; stream++)
+ pList->push_back(pStreamList[stream]);
+ }
+ delete[] pStreamList;
+ return (ret == noErr);
+}
+
+
+bool CCoreAudioDevice::IsRunning()
+{
+ UInt32 isRunning = false;
+ UInt32 size = sizeof(isRunning);
+ OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyDeviceIsRunning, &size, &isRunning);
+ if (ret)
+ return false;
+ return (isRunning != 0);
+}
+
+bool CCoreAudioDevice::SetHogStatus(bool hog)
+{
+ // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
+ // is a toggle and the only way to tell if you do get hog mode is to compare
+ // the returned pid against getpid, if the match, you have hog mode, if not you don't.
+ if (!m_DeviceId)
+ return false;
+
+ if (hog)
+ {
+ if (m_HogPid == -1) // Not already set
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: Setting 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyHogMode, sizeof(m_HogPid), &m_HogPid);
+ if (ret || m_HogPid != getpid())
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: "
+ "Successfully set 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
+ }
+ }
+ else
+ {
+ if (m_HogPid > -1) // Currently Set
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: "
+ "Releasing 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
+ pid_t hogPid = -1;
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyHogMode, sizeof(hogPid), &hogPid);
+ if (ret || hogPid == getpid())
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: Unable to release 'hog' status. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ m_HogPid = hogPid; // Reset internal state
+ }
+ }
+ return true;
+}
+
+pid_t CCoreAudioDevice::GetHogStatus()
+{
+ if (!m_DeviceId)
+ return false;
+
+ pid_t hogPid = -1;
+ UInt32 size = sizeof(hogPid);
+ AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyHogMode, &size, &hogPid);
+
+ return hogPid;
+}
+
+bool CCoreAudioDevice::SetMixingSupport(UInt32 mix)
+{
+ if (!m_DeviceId)
+ return false;
+
+ if (!GetMixingSupport())
+ return false;
+
+ int restore = -1;
+ if (m_MixerRestore == -1) // This is our first change to this setting. Store the original setting for restore
+ restore = (GetMixingSupport() ? 1 : 0);
+ UInt32 mixEnable = mix ? 1 : 0;
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetMixingSupport: "
+ "%sabling mixing for device 0x%04x", mix ? "En" : "Dis", (unsigned int)m_DeviceId);
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertySupportsMixing, sizeof(mixEnable), &mixEnable);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetMixingSupport: Unable to set MixingSupport to %s. Error = %s", mix ? "'On'" : "'Off'", GetError(ret).c_str());
+ return false;
+ }
+ if (m_MixerRestore == -1)
+ m_MixerRestore = restore;
+ return true;
+}
+
+bool CCoreAudioDevice::GetMixingSupport()
+{
+ if (!m_DeviceId)
+ return false;
+
+ OSStatus ret;
+ UInt32 size;
+ Boolean writable = false;
+ UInt32 mix = 0;
+
+ size = sizeof(writable);
+ ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, FALSE, kAudioDevicePropertySupportsMixing, &size, &writable);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: Unable to get propertyinfo mixing support. Error = %s", GetError(ret).c_str());
+ writable = false;
+ }
+
+ if (writable)
+ {
+ size = sizeof(mix);
+ ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertySupportsMixing, &size, &mix);
+ if (ret)
+ mix = 0;
+ }
+
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SupportsMixing: Device mixing support : %s.", mix ? "'Yes'" : "'No'");
+
+ return (mix > 0);
+}
+
+bool CCoreAudioDevice::SetCurrentVolume(Float32 vol)
+{
+ if (!m_DeviceId)
+ return false;
+
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kHALOutputParam_Volume, sizeof(Float32), &vol);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetCurrentVolume: Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
+{
+ if (!m_DeviceId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout, &propertySize, &writable);
+ if (ret)
+ return false;
+
+ void* pBuf = malloc(propertySize);
+ ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout, &propertySize, pBuf);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
+ "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
+ else
+ layout.CopyLayout(*((AudioChannelLayout*)pBuf)); // Copy the result into the caller's instance
+ free(pBuf);
+ return (ret == noErr);
+}
+
+bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
+{
+ if (!pList || !m_DeviceId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyDataSources, &propertySize, &writable);
+ if (ret)
+ return false;
+ UInt32 sources = propertySize / sizeof(UInt32);
+ UInt32* pSources = new UInt32[sources];
+ ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyDataSources, &propertySize, pSources);
+ if (!ret)
+ for (UInt32 i = 0; i < sources; i++)
+ pList->push_back(pSources[i]);;
+ delete[] pSources;
+ return (!ret);
+}
+
+Float64 CCoreAudioDevice::GetNominalSampleRate()
+{
+ if (!m_DeviceId)
+ return 0.0f;
+
+ Float64 sampleRate = 0.0f;
+ UInt32 size = sizeof(Float64);
+ OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sampleRate);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetNominalSampleRate: Unable to retrieve current device sample rate. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return sampleRate;
+}
+
+bool CCoreAudioDevice::SetNominalSampleRate(Float64 sampleRate)
+{
+ if (!m_DeviceId || sampleRate == 0.0f)
+ return false;
+
+ Float64 currentRate = GetNominalSampleRate();
+ if (currentRate == sampleRate)
+ return true; //No need to change
+
+ UInt32 size = sizeof(Float64);
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sampleRate);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: Unable to set current device sample rate to %0.0f. Error = %s", (float)sampleRate, ret, GetError(ret).c_str());
+ return false;
+ }
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetNominalSampleRate: Changed device sample rate from %0.0f to %0.0f.", (float)currentRate, (float)sampleRate);
+ if (m_SampleRateRestore == 0.0f)
+ m_SampleRateRestore = currentRate;
+
+ return true;
+}
+
+UInt32 CCoreAudioDevice::GetNumLatencyFrames()
+{
+ UInt32 i_param, i_param_size, num_latency_frames = 0;
+ if (!m_DeviceId)
+ return 0;
+
+ i_param_size = sizeof(uint32_t);
+
+ // number of frames of latency in the AudioDevice
+ if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertyLatency, &i_param_size, &i_param))
+ {
+ num_latency_frames += i_param;
+ }
+
+ // number of frames in the IO buffers
+ if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertyBufferFrameSize, &i_param_size, &i_param))
+ {
+ num_latency_frames += i_param;
+ }
+
+ // number for frames in ahead the current hardware position that is safe to do IO
+ if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertySafetyOffset, &i_param_size, &i_param))
+ {
+ num_latency_frames += i_param;
+ }
+
+ return (num_latency_frames);
+}
+
+UInt32 CCoreAudioDevice::GetBufferSize()
+{
+ if (!m_DeviceId)
+ return false;
+
+ UInt32 size = 0;
+ UInt32 propertySize = sizeof(size);
+ OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertyBufferFrameSize, &propertySize, &size);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetBufferSize: Unable to retrieve buffer size. Error = %s", GetError(ret).c_str());
+ return size;
+}
+
+bool CCoreAudioDevice::SetBufferSize(UInt32 size)
+{
+ if (!m_DeviceId)
+ return false;
+
+ UInt32 propertySize = sizeof(size);
+ OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false,
+ kAudioDevicePropertyBufferFrameSize, propertySize, &size);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Unable to set buffer size. Error = %s", GetError(ret).c_str());
+
+ if (GetBufferSize() != size)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Buffer size change not applied.");
+ else
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetBufferSize: Set buffer size to %d", (int)size);
+
+ return (ret == noErr);
+}
+
+OSStatus CCoreAudioDevice::DirectRenderCallback(AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* inClientData)
+{
+ OSStatus ret = noErr;
+ CCoreAudioDevice *audioDevice = (CCoreAudioDevice*)inClientData;
+
+ if (audioDevice->m_pSource && audioDevice->m_frameSize)
+ {
+ UInt32 frames = outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize / audioDevice->m_frameSize;
+ ret = audioDevice->m_pSource->Render(NULL, inInputTime, 0, frames, outOutputData);
+ }
+ else
+ {
+ outOutputData->mBuffers[audioDevice->m_OutputBufferIndex].mDataByteSize = 0;
+ }
+
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioStream
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioStream::CCoreAudioStream() :
+ m_StreamId (0 )
+{
+ m_OriginalVirtualFormat.mFormatID = 0;
+ m_OriginalPhysicalFormat.mFormatID = 0;
+}
+
+CCoreAudioStream::~CCoreAudioStream()
+{
+ Close();
+}
+
+bool CCoreAudioStream::Open(AudioStreamID streamId)
+{
+ m_StreamId = streamId;
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Open: Opened stream 0x%04x.", m_StreamId);
+ return true;
+}
+
+// TODO: Should it even be possible to change both the physical and virtual formats, since the devices do it themselves?
+void CCoreAudioStream::Close()
+{
+ if (!m_StreamId)
+ return;
+
+ std::string formatString;
+
+ // Revert any format changes we made
+ if (m_OriginalVirtualFormat.mFormatID && m_StreamId)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Restoring original virtual format for stream 0x%04x. (%s)", m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
+ AudioStreamBasicDescription setFormat = m_OriginalVirtualFormat;
+ SetVirtualFormat(&setFormat);
+ }
+ if (m_OriginalPhysicalFormat.mFormatID && m_StreamId)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Restoring original physical format for stream 0x%04x. (%s)", m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
+ AudioStreamBasicDescription setFormat = m_OriginalPhysicalFormat;
+ SetPhysicalFormat(&setFormat);
+ }
+
+ m_OriginalPhysicalFormat.mFormatID = 0;
+ m_OriginalVirtualFormat.mFormatID = 0;
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", m_StreamId);
+ m_StreamId = 0;
+}
+
+UInt32 CCoreAudioStream::GetDirection()
+{
+ if (!m_StreamId)
+ return 0;
+ UInt32 size = sizeof(UInt32);
+ UInt32 val = 0;
+ OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyDirection, &size, &val);
+ if (ret)
+ return 0;
+ return val;
+}
+
+UInt32 CCoreAudioStream::GetTerminalType()
+{
+ if (!m_StreamId)
+ return 0;
+ UInt32 size = sizeof(UInt32);
+ UInt32 val = 0;
+ OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyTerminalType, &size, &val);
+ if (ret)
+ return 0;
+ return val;
+}
+
+UInt32 CCoreAudioStream::GetNumLatencyFrames()
+{
+ UInt32 i_param, i_param_size, num_latency_frames = 0;
+ if (!m_StreamId)
+ return 0;
+
+ i_param_size = sizeof(uint32_t);
+
+ // number of frames of latency in the AudioStream
+ if (noErr == AudioStreamGetProperty(m_StreamId, 0,
+ kAudioStreamPropertyLatency, &i_param_size, &i_param))
+ {
+ num_latency_frames += i_param;
+ }
+
+ return (num_latency_frames);
+}
+
+bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyVirtualFormat, &size, pDesc);
+ if (ret)
+ return false;
+ return true;
+}
+
+bool CCoreAudioStream::SetVirtualFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+
+ std::string formatString;
+
+ if (!m_OriginalVirtualFormat.mFormatID)
+ {
+ if (!GetVirtualFormat(&m_OriginalVirtualFormat)) // Store the original format (as we found it) so that it can be restored later
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: Unable to retrieve current virtual format for stream 0x%04x.", m_StreamId);
+ return false;
+ }
+ }
+ OSStatus ret = AudioStreamSetProperty(m_StreamId, NULL, 0, kAudioStreamPropertyVirtualFormat, sizeof(AudioStreamBasicDescription), pDesc);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: Unable to set virtual format for stream 0x%04x. Error = %s", m_StreamId, GetError(ret).c_str());
+ return false;
+ }
+
+ /* The AudioStreamSetProperty is not only asynchronious,
+ * it is also not Atomic, in its behaviour.
+ * Therefore we check 5 times before we really give up.
+ * FIXME: failing isn't actually implemented yet. */
+ for (int i = 0; i < 10; ++i)
+ {
+ AudioStreamBasicDescription checkVirtualFormat;
+ if (!GetVirtualFormat(&checkVirtualFormat))
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: Unable to retrieve current physical format for stream 0x%04x.", m_StreamId);
+ return false;
+ }
+ if (checkVirtualFormat.mSampleRate == pDesc->mSampleRate &&
+ checkVirtualFormat.mFormatID == pDesc->mFormatID &&
+ checkVirtualFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
+ {
+ /* The right format is now active. */
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::SetVirtualFormat: Virtual format for stream 0x%04x. now active (%s)", m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
+ break;
+ }
+ Sleep(100);
+ }
+ return true;
+}
+
+bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyPhysicalFormat, &size, pDesc);
+ if (ret)
+ return false;
+ return true;
+}
+
+bool CCoreAudioStream::SetPhysicalFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+
+ std::string formatString;
+
+ if (!m_OriginalPhysicalFormat.mFormatID)
+ {
+ if (!GetPhysicalFormat(&m_OriginalPhysicalFormat)) // Store the original format (as we found it) so that it can be restored later
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: Unable to retrieve current physical format for stream 0x%04x.", m_StreamId);
+ return false;
+ }
+ }
+ OSStatus ret = AudioStreamSetProperty(m_StreamId, NULL, 0, kAudioStreamPropertyPhysicalFormat, sizeof(AudioStreamBasicDescription), pDesc);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: Unable to set physical format for stream 0x%04x. Error = %s", m_StreamId, GetError(ret).c_str());
+ return false;
+ }
+
+ /* The AudioStreamSetProperty is not only asynchronious,
+ * it is also not Atomic, in its behaviour.
+ * Therefore we check 5 times before we really give up.
+ * FIXME: failing isn't actually implemented yet. */
+ for(int i = 0; i < 10; ++i)
+ {
+ AudioStreamBasicDescription checkPhysicalFormat;
+ if (!GetPhysicalFormat(&checkPhysicalFormat))
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: Unable to retrieve current physical format for stream 0x%04x.", m_StreamId);
+ return false;
+ }
+ if (checkPhysicalFormat.mSampleRate == pDesc->mSampleRate &&
+ checkPhysicalFormat.mFormatID == pDesc->mFormatID &&
+ checkPhysicalFormat.mFramesPerPacket == pDesc->mFramesPerPacket)
+ {
+ /* The right format is now active. */
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::SetPhysicalFormat: Physical format for stream 0x%04x. now active (%s)", m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
+ break;
+ }
+ Sleep(100);
+ }
+ return true;
+}
+
+bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
+{
+ if (!pList || !m_StreamId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioStreamGetPropertyInfo(m_StreamId, 0, kAudioStreamPropertyAvailableVirtualFormats, &propertySize, &writable);
+ if (ret)
+ return false;
+ UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
+ AudioStreamRangedDescription* pFormatList = new AudioStreamRangedDescription[formatCount];
+ ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyAvailableVirtualFormats, &propertySize, pFormatList);
+ if (!ret)
+ {
+ for (UInt32 format = 0; format < formatCount; format++)
+ pList->push_back(pFormatList[format]);
+ }
+ delete[] pFormatList;
+ return (ret == noErr);
+}
+
+bool CCoreAudioStream::GetAvailablePhysicalFormats(StreamFormatList* pList)
+{
+ if (!pList || !m_StreamId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioStreamGetPropertyInfo(m_StreamId, 0, kAudioStreamPropertyAvailablePhysicalFormats, &propertySize, &writable);
+ if (ret)
+ return false;
+ UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
+ AudioStreamRangedDescription* pFormatList = new AudioStreamRangedDescription[formatCount];
+ ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyAvailablePhysicalFormats, &propertySize, pFormatList);
+ if (!ret)
+ {
+ for (UInt32 format = 0; format < formatCount; format++)
+ pList->push_back(pFormatList[format]);
+ }
+ delete[] pFormatList;
+ return (ret == noErr);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioUnit
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioUnit::CCoreAudioUnit() :
+ m_Initialized (false ),
+ m_pSource (NULL ),
+ m_renderProc (NULL ),
+ m_audioUnit (NULL ),
+ m_audioNode (NULL ),
+ m_audioGraph (NULL ),
+ m_busNumber (INVALID_BUS )
+{
+}
+
+CCoreAudioUnit::~CCoreAudioUnit()
+{
+ Close();
+}
+
+bool CCoreAudioUnit::Open(AUGraph audioGraph, ComponentDescription desc)
+{
+ if (m_audioUnit)
+ Close();
+
+ OSStatus ret;
+
+ m_Initialized = false;
+
+ ret = AUGraphNewNode(audioGraph, &desc, 0, NULL, &m_audioNode);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error add m_outputNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ ret = AUGraphGetNodeInfo(audioGraph, m_audioNode, 0, 0, 0, &m_audioUnit);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error getting m_outputNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_audioGraph = audioGraph;
+ m_Initialized = true;
+
+ return true;
+}
+
+bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
+{
+ ComponentDescription desc;
+ desc.componentType = type;
+ desc.componentSubType = subType;
+ desc.componentManufacturer = manufacturer;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ return Open(audioGraph, desc);
+}
+
+void CCoreAudioUnit::Close()
+{
+ if (!m_Initialized && !m_audioUnit)
+ return;
+
+ if (m_renderProc)
+ SetInputSource(NULL);
+
+ Stop();
+
+ if (m_busNumber != INVALID_BUS)
+ {
+ OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::Close: Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
+ }
+ }
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ m_Initialized = false;
+ m_audioUnit = NULL;
+ m_audioNode = NULL;
+ m_pSource = NULL;
+}
+
+bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
+{
+ if (!m_audioUnit || !pDesc)
+ return false;
+
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s", scope, bus, GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
+{
+ if (!m_audioUnit || !pDesc)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s", scope, bus, GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+bool CCoreAudioUnit::GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts)
+{
+ if (!m_audioUnit)
+ return false;
+ if (!pLayouts)
+ return false;
+
+ UInt32 propSize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioUnitGetPropertyInfo(m_audioUnit, kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Input, 0, &propSize, &writable);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
+ "Unable to retrieve supported channel layout property info. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ UInt32 layoutCount = propSize / sizeof(AudioChannelLayoutTag);
+ AudioChannelLayoutTag* pSuppLayouts = new AudioChannelLayoutTag[layoutCount];
+ ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Output, 0, pSuppLayouts, &propSize);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
+ "Unable to retrieve supported channel layouts. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ for (UInt32 layout = 0; layout < layoutCount; layout++)
+ pLayouts->push_back(pSuppLayouts[layout]);
+ delete[] pSuppLayouts;
+ return true;
+}
+
+bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
+{
+ m_pSource = pSource;
+ if (pSource)
+ return SetRenderProc();
+ else
+ return RemoveRenderProc();
+}
+
+bool CCoreAudioUnit::SetRenderProc()
+{
+ if (!m_audioUnit || m_renderProc)
+ return false;
+
+ AURenderCallbackStruct callbackInfo;
+ callbackInfo.inputProc = RenderCallback; // Function to be called each time the AudioUnit needs data
+ callbackInfo.inputProcRefCon = this; // Pointer to be returned in the callback proc
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_renderProc = RenderCallback;
+
+ CLog::Log(LOGDEBUG, "CCoreAudioUnit::SetRenderProc: Set RenderProc 0x%08x for unit 0x%08x.", m_renderProc, m_audioUnit);
+
+ return true;
+}
+
+bool CCoreAudioUnit::RemoveRenderProc()
+{
+ if (!m_audioUnit || !m_renderProc)
+ return false;
+
+
+ AURenderCallbackStruct callbackInfo;
+ callbackInfo.inputProc = nil;
+ callbackInfo.inputProcRefCon = nil;
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CCoreAudioUnit::RemoveRenderProc: Remove RenderProc 0x%08x for unit 0x%08x.", m_renderProc, m_audioUnit);
+
+ m_renderProc = NULL;
+ Sleep(100);
+
+ return true;
+}
+
+OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ OSStatus ret = noErr;
+ CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
+
+ if (audioUnit->m_pSource)
+ {
+ ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+ }
+ else
+ {
+ ioData->mBuffers[0].mDataByteSize = 0;
+ if (ioActionFlags)
+ *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
+ }
+
+
+ return ret;
+}
+
+void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
+ AudioStreamBasicDescription *streamDesc,
+ AudioStreamBasicDescription *coreaudioDesc)
+{
+ unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
+
+ // Set the input stream format for the AudioUnit
+ // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
+ // The autput format is automaticaly set to the input format.
+ streamDesc->mFormatID = kAudioFormatLinearPCM; // Data encoding format
+ streamDesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
+ switch (format.m_dataFormat)
+ {
+ case AE_FMT_FLOAT:
+ case AE_FMT_LPCM:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
+ break;
+ case AE_FMT_AC3:
+ case AE_FMT_DTS:
+ case AE_FMT_DTSHD:
+ case AE_FMT_TRUEHD:
+ case AE_FMT_EAC3:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ case AE_FMT_S16LE:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ case AE_FMT_S16BE:
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ default:
+ streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
+ streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ }
+ streamDesc->mChannelsPerFrame = format.m_channelLayout.Count(); // Number of interleaved audiochannels
+ streamDesc->mSampleRate = (Float64)format.m_sampleRate; // the sample rate of the audio stream
+ streamDesc->mBitsPerChannel = bps; // Number of bits per sample, per channel
+ streamDesc->mBytesPerFrame = (bps>>3) * format.m_channelLayout.Count(); // Size of a frame == 1 sample per channel
+ streamDesc->mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
+ streamDesc->mBytesPerPacket = streamDesc->mBytesPerFrame * streamDesc->mFramesPerPacket;
+ streamDesc->mReserved = 0;
+
+ // Audio units use noninterleaved 32-bit floating point linear PCM data for input and output,
+ // ...except in the case of an audio unit that is a data format converter, which converts to or from this format.
+ coreaudioDesc->mFormatID = kAudioFormatLinearPCM;
+ coreaudioDesc->mFormatFlags = kAudioFormatFlagsNativeEndian |
+ kAudioFormatFlagIsPacked |
+ kAudioFormatFlagIsNonInterleaved;
+ switch (format.m_dataFormat)
+ {
+ case AE_FMT_FLOAT:
+ coreaudioDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
+ default:
+ coreaudioDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ }
+ coreaudioDesc->mBitsPerChannel = bps; //sizeof(Float32)<<3;
+ coreaudioDesc->mSampleRate = (Float64)format.m_sampleRate;;
+ coreaudioDesc->mFramesPerPacket = 1;
+ coreaudioDesc->mChannelsPerFrame = streamDesc->mChannelsPerFrame;
+ coreaudioDesc->mBytesPerFrame = (bps>>3); //sizeof(Float32);
+ coreaudioDesc->mBytesPerPacket = (bps>>3); //sizeof(Float32);
+
+}
+
+float CCoreAudioUnit::GetLatency()
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float64 latency;
+ UInt32 size = sizeof(latency);
+
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &latency, &size);
+
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetLatency: Unable to set AudioUnit latency. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+
+ return latency;
+}
+
+bool CCoreAudioUnit::Stop()
+{
+ if (!m_audioUnit)
+ return false;
+
+ AudioOutputUnitStop(m_audioUnit);
+
+ return true;
+}
+
+bool CCoreAudioUnit::Start()
+{
+ if (!m_audioUnit)
+ return false;
+
+ AudioOutputUnitStart(m_audioUnit);
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CAUOutputDevice
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CAUOutputDevice::CAUOutputDevice() :
+ m_DeviceId (NULL )
+{
+}
+
+CAUOutputDevice::~CAUOutputDevice()
+{
+}
+
+bool CAUOutputDevice::SetCurrentDevice(AudioDeviceID deviceId)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret;
+
+ ret = AudioUnitSetProperty(m_audioUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ kOutputBus,
+ &deviceId,
+ sizeof(AudioDeviceID));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentDevice: Unable to set current device. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_DeviceId = deviceId;
+
+ CLog::Log(LOGDEBUG, "CCoreAudioUnit::SetCurrentDevice: Current device 0x%08x", m_DeviceId);
+
+ return true;
+}
+
+bool CAUOutputDevice::GetChannelMap(CoreAudioChannelList* pChannelMap)
+{
+ if (!m_audioUnit)
+ return false;
+
+ UInt32 size = 0;
+ Boolean writable = false;
+ AudioUnitGetPropertyInfo(m_audioUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, &size, &writable);
+ UInt32 channels = size/sizeof(SInt32);
+ SInt32* pMap = new SInt32[channels];
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, &size);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputChannelMap: Unable to retrieve AudioUnit input channel map. Error = %s", GetError(ret).c_str());
+ else
+ for (UInt32 i = 0; i < channels; i++)
+ pChannelMap->push_back(pMap[i]);
+ delete[] pMap;
+ return (!ret);
+}
+
+bool CAUOutputDevice::SetChannelMap(CoreAudioChannelList* pChannelMap)
+{
+ // The number of array elements must match the number of output channels provided by the device
+ if (!m_audioUnit || !pChannelMap)
+ return false;
+ UInt32 channels = pChannelMap->size();
+ UInt32 size = sizeof(SInt32) * channels;
+ SInt32* pMap = new SInt32[channels];
+ for (UInt32 i = 0; i < channels; i++)
+ pMap[i] = (*pChannelMap)[i];
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, size);
+ if (ret)
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
+ delete[] pMap;
+ return (!ret);
+}
+
+Float32 CAUOutputDevice::GetCurrentVolume()
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float32 volPct = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return volPct;
+}
+
+bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
+ kAudioUnitScope_Global, 0, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+UInt32 CAUOutputDevice::GetBufferFrameSize()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ UInt32 size = sizeof(UInt32);
+ UInt32 bufferSize = 0;
+
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Input, 0, &bufferSize, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return bufferSize;
+}
+
+bool CAUOutputDevice::EnableInputOuput()
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret;
+ UInt32 enable;
+ UInt32 hasio;
+ UInt32 size=sizeof(UInt32);
+
+ ret = AudioUnitGetProperty(m_audioUnit,kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
+
+ if (hasio)
+ {
+ enable = 1;
+ ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ enable = 1;
+ ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CAUOutputDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
+{
+ if (!m_DeviceId)
+ return false;
+
+ UInt32 propertySize = 0;
+ Boolean writable = false;
+ OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout, &propertySize, &writable);
+ if (ret)
+ return false;
+
+ void* pBuf = malloc(propertySize);
+ ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
+ kAudioDevicePropertyPreferredChannelLayout, &propertySize, pBuf);
+ if (ret)
+ CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
+ else
+ layout.CopyLayout(*((AudioChannelLayout*)pBuf)); // Copy the result into the caller's instance
+ free(pBuf);
+ return (ret == noErr);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CAUMatrixMixer
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CAUMatrixMixer::CAUMatrixMixer()
+{
+}
+
+CAUMatrixMixer::~CAUMatrixMixer()
+{
+
+}
+
+bool CAUMatrixMixer::InitMatrixMixerVolumes()
+{
+ // Fetch thechannel configuration
+ UInt32 dims[2];
+ UInt32 size = sizeof(dims);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::Initialize:: Get matrix dimesion. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // Initialize global, input, and output levels
+ if (!SetGlobalVolume(1.0f))
+ return false;
+ for (UInt32 i = 0; i < dims[0]; i++)
+ if (!SetInputVolume(i, 1.0f))
+ return false;
+ for (UInt32 i = 0; i < dims[1]; i++)
+ if (!SetOutputVolume(i, 1.0f))
+ return false;
+
+ return true;
+}
+
+UInt32 CAUMatrixMixer::GetInputBusCount()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ UInt32 busCount = 0;
+ UInt32 size = sizeof(busCount);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputBusCount: Unable to get input bus count. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return busCount;
+}
+
+bool CAUMatrixMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
+{
+ if (!m_audioUnit)
+ return false;
+
+ UInt32 enable = 1;
+ for (UInt32 i = 0; i < busCount; i++)
+ {
+ AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Input, i, enable, 0);
+ if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
+ return false;
+ }
+
+ return true;
+}
+
+bool CAUMatrixMixer::SetInputBusCount(UInt32 busCount)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputBusCount: Unable to set input bus count. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+UInt32 CAUMatrixMixer::GetOutputBusCount()
+{
+ if (!m_audioUnit)
+ return 0;
+
+ UInt32 busCount = 0;
+ UInt32 size = sizeof(busCount);
+ OSStatus ret = AudioUnitGetProperty(m_audioUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputBusCount: Unable to get output bus count. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+ return busCount;
+}
+
+bool CAUMatrixMixer::SetOutputBusCount(UInt32 busCount)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputBusCount: Unable to set output bus count. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+Float32 CAUMatrixMixer::GetGlobalVolume()
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float32 vol = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, &vol);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::GetGlobalVolume: Unable to get global volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return vol;
+}
+
+bool CAUMatrixMixer::SetGlobalVolume(Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::SetGlobalVolume: Unable to set global volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+Float32 CAUMatrixMixer::GetInputVolume(UInt32 element)
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float32 vol = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, &vol);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputVolume: Unable to get input volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return vol;
+}
+
+bool CAUMatrixMixer::SetInputVolume(UInt32 element, Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputVolume: Unable to set input volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+Float32 CAUMatrixMixer::GetOutputVolume(UInt32 element)
+{
+ if (!m_audioUnit)
+ return 0.0f;
+
+ Float32 vol = 0.0f;
+ OSStatus ret = AudioUnitGetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, &vol);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputVolume: Unable to get output volume. Error = %s", GetError(ret).c_str());
+ return 0.0f;
+ }
+ return vol;
+}
+
+bool CAUMatrixMixer::SetOutputVolume(UInt32 element, Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ OSStatus ret = AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, vol, 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputVolume: Unable to set output volume. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioMixMap
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioMixMap::CCoreAudioMixMap() :
+ m_isValid(false)
+{
+ m_pMap = (Float32*)calloc(sizeof(AudioChannelLayout), 1);
+}
+
+CCoreAudioMixMap::CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout) :
+ m_isValid(false)
+{
+ Rebuild(inLayout, outLayout);
+}
+
+CCoreAudioMixMap::~CCoreAudioMixMap()
+{
+ free(m_pMap);
+ m_pMap = NULL;
+}
+
+void CCoreAudioMixMap::Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout)
+{
+ // map[in][out] = mix-level of input_channel[in] into output_channel[out]
+
+ free(m_pMap);
+ m_pMap = NULL;
+
+ m_inChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(inLayout);
+ m_outChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(outLayout);
+
+ // Try to find a 'well-known' matrix
+ const AudioChannelLayout* layouts[] = {&inLayout, &outLayout};
+ UInt32 propSize = 0;
+ OSStatus ret = AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize);
+ m_pMap = (Float32*)calloc(1,propSize);
+
+ // Try and get a predefined mixmap
+ ret = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize, m_pMap);
+ if (!ret)
+ {
+ m_isValid = true;
+ return; // Nothing else to do...a map already exists
+ }
+
+ // No predefined mixmap was available. Going to have to build it manually
+ CLog::Log(LOGDEBUG, "CCoreAudioMixMap::CreateMap: Unable to locate pre-defined mixing matrix");
+
+ m_isValid = false;
+}
+
+CCoreAudioMixMap *CCoreAudioMixMap::CreateMixMap(CAUOutputDevice *audioUnit, AEAudioFormat &format, AudioChannelLayoutTag layoutTag)
+{
+ if (!audioUnit)
+ return NULL;
+
+ AudioStreamBasicDescription inputFormat;
+ AudioStreamBasicDescription fmt;
+
+ // get the stream input format
+ audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
+
+ unsigned int channels = format.m_channelLayout.Count();
+ CAEChannelInfo channelLayout = format.m_channelLayout;
+ bool hasLFE = false;
+ // Convert XBMC input channel layout format to CoreAudio layout format
+ AudioChannelLayout* pInLayout = (AudioChannelLayout*)malloc(sizeof(AudioChannelLayout) + sizeof(AudioChannelDescription) * channels);
+ pInLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ pInLayout->mChannelBitmap = 0;
+ pInLayout->mNumberChannelDescriptions = channels;
+ for (unsigned int chan=0; chan < channels; chan++)
+ {
+ AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
+ pDesc->mChannelLabel = g_LabelMap[(unsigned int)channelLayout[chan]]; // Convert from XBMC channel tag to CoreAudio channel tag
+ pDesc->mChannelFlags = kAudioChannelFlags_AllOff;
+ pDesc->mCoordinates[0] = 0.0f;
+ pDesc->mCoordinates[1] = 0.0f;
+ pDesc->mCoordinates[2] = 0.0f;
+ if (pDesc->mChannelLabel == kAudioChannelLabel_LFEScreen)
+ hasLFE = true;
+ }
+ // HACK: Fix broken channel layouts coming from some aac sources that include rear channel but no side channels.
+ // 5.1 streams should include front and side channels. Rear channels are added by 6.1 and 7.1, so any 5.1
+ // source that claims to have rear channels is wrong.
+ if (inputFormat.mChannelsPerFrame == 6 && hasLFE) // Check for 5.1 configuration (as best we can without getting too silly)
+ {
+ for (unsigned int chan=0; chan < inputFormat.mChannelsPerFrame; chan++)
+ {
+ AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
+ if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround || pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround)
+ break; // Required condition cannot be true
+
+ if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurroundDirect)
+ {
+ pDesc->mChannelLabel = kAudioChannelLabel_LeftSurround; // Change [Back Left] to [Side Left]
+ CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: Detected faulty input channel map...fixing(Back Left-->Side Left)");
+ }
+ if (pDesc->mChannelLabel == kAudioChannelLabel_RightSurroundDirect)
+ {
+ pDesc->mChannelLabel = kAudioChannelLabel_RightSurround; // Change [Back Left] to [Side Left]
+ CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: Detected faulty input channel map...fixing(Back Right-->Side Right)");
+ }
+ }
+ }
+
+ CCoreAudioChannelLayout sourceLayout(*pInLayout);
+ free(pInLayout);
+ pInLayout = NULL;
+
+ std::string strInLayout;
+ CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: Source Stream Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)sourceLayout, strInLayout));
+
+ // Get User-Configured (XBMC) Speaker Configuration
+ AudioChannelLayout guiLayout;
+ guiLayout.mChannelLayoutTag = layoutTag;
+ CCoreAudioChannelLayout userLayout(guiLayout);
+ std::string strUserLayout;
+ CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: User-Configured Speaker Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)userLayout, strUserLayout));
+
+ // Get OS-Configured (Audio MIDI Setup) Speaker Configuration (Channel Layout)
+ CCoreAudioChannelLayout deviceLayout;
+ if (!audioUnit->GetPreferredChannelLayout(deviceLayout))
+ return NULL;
+
+ // When all channels on the output device are unknown take the gui layout
+ //if(deviceLayout.AllChannelUnknown())
+ // deviceLayout.CopyLayout(guiLayout);
+
+ std::string strOutLayout;
+ CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: Output Device Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)deviceLayout, strOutLayout));
+
+ // TODO:
+ // Reconcile the OS and GUI layout configurations. Clamp to the minimum number of speakers
+ // For each OS-defined output, see if it exists in the GUI configuration
+ // If it does, add it to the 'union' layout (bitmap?)
+ // User may have configured 5.1 in GUI, but only 2.0 in OS
+ // Resulting layout would be {FL, FR}
+ // User may have configured 2.0 in GUI, and 5.1 in OS
+ // Resulting layout would be {FL, FR}
+
+ // Correct any configuration incompatibilities
+ //if (CCoreAudioChannelLayout::GetChannelCountForLayout(guiLayout) < CCoreAudioChannelLayout::GetChannelCountForLayout(deviceLayout))
+ // deviceLayout.CopyLayout(guiLayout);
+
+ // TODO: Skip matrix mixer if input/output are compatible
+
+ AudioChannelLayout* layoutCandidates[] = {(AudioChannelLayout*)deviceLayout, (AudioChannelLayout*)userLayout, NULL};
+
+ // Try to construct a mapping matrix for the mixer. Work through the layout candidates and see if any will work
+ CCoreAudioMixMap *mixMap = new CCoreAudioMixMap();
+ for (AudioChannelLayout** pLayout = layoutCandidates; *pLayout != NULL; pLayout++)
+ {
+ mixMap->Rebuild(*sourceLayout, **pLayout);
+ if (mixMap->IsValid())
+ break;
+ }
+ return mixMap;
+}
+
+bool CCoreAudioMixMap::SetMixingMatrix(CAUMatrixMixer *mixerUnit, CCoreAudioMixMap *mixMap, AudioStreamBasicDescription *inputFormat, AudioStreamBasicDescription *fmt, int channelOffset)
+{
+ if (!mixerUnit || !inputFormat || !fmt)
+ return false;
+
+ OSStatus ret;
+ // Configure the mixing matrix
+ Float32* val = (Float32*)*mixMap;
+ CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: Loading matrix mixer configuration");
+ for (UInt32 i = 0; i < inputFormat->mChannelsPerFrame; ++i)
+ {
+ for (UInt32 j = 0; j < fmt->mChannelsPerFrame; ++j)
+ {
+ ret = AudioUnitSetParameter(mixerUnit->GetUnit(),
+ kMatrixMixerParam_Volume, kAudioUnitScope_Global,
+ ( (i + channelOffset) << 16 ) | j, *val++, 0);
+ if (!ret)
+ {
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: \t[%d][%d][%0.1f]",
+ (int)i + channelOffset, (int)j, *(val-1));
+ }
+ }
+ }
+
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: "
+ "Mixer Output Format: %d channels, %0.1f kHz, %d bits, %d bytes per frame",
+ (int)fmt->mChannelsPerFrame, fmt->mSampleRate / 1000.0f, (int)fmt->mBitsPerChannel, (int)fmt->mBytesPerFrame);
+
+ if (!mixerUnit->InitMatrixMixerVolumes())
+ return false;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioAEMixMap
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioChannelLayout::CCoreAudioChannelLayout() :
+ m_pLayout(NULL)
+{
+
+}
+
+CCoreAudioChannelLayout::CCoreAudioChannelLayout(AudioChannelLayout& layout) :
+m_pLayout(NULL)
+{
+ CopyLayout(layout);
+}
+
+CCoreAudioChannelLayout::~CCoreAudioChannelLayout()
+{
+ free(m_pLayout);
+}
+
+bool CCoreAudioChannelLayout::CopyLayout(AudioChannelLayout& layout)
+{
+ free(m_pLayout);
+ m_pLayout = NULL;
+
+ // This method always produces a layout with a ChannelDescriptions structure
+
+ OSStatus ret = 0;
+ UInt32 channels = GetChannelCountForLayout(layout);
+ UInt32 size = sizeof(AudioChannelLayout) + (channels - kVariableLengthArray) * sizeof(AudioChannelDescription);
+
+ if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) // We can copy the whole layout
+ {
+ m_pLayout = (AudioChannelLayout*)malloc(size);
+ memcpy(m_pLayout, &layout, size);
+ }
+ else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Deconstruct the bitmap to get the layout
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize);
+ m_pLayout = (AudioChannelLayout*)malloc(propSize);
+ ret = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize, m_pLayout);
+ m_pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ }
+ else // Convert the known layout to a custom layout
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize);
+ m_pLayout = (AudioChannelLayout*)malloc(propSize);
+ ret = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize, m_pLayout);
+ m_pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ }
+
+ return (ret == noErr);
+}
+
+UInt32 CCoreAudioChannelLayout::GetChannelCountForLayout(AudioChannelLayout& layout)
+{
+ UInt32 channels = 0;
+ if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Channels are in fixed-order('USB Order'), any combination
+ {
+ UInt32 bitmap = layout.mChannelBitmap;
+ for (UInt32 c = 0; c < (sizeof(layout.mChannelBitmap) << 3); c++)
+ {
+ if (bitmap & 0x1)
+ channels++;
+ bitmap >>= 1;
+ }
+ }
+ else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) // Channels are in any order, any combination
+ channels = layout.mNumberChannelDescriptions;
+ else // Channels are in a predefined order and combination
+ channels = AudioChannelLayoutTag_GetNumberOfChannels(layout.mChannelLayoutTag);
+
+ return channels;
+}
+
+const char* CCoreAudioChannelLayout::ChannelLabelToString(UInt32 label)
+{
+ if (label > MAX_CHANNEL_LABEL)
+ return "Unknown";
+ return g_ChannelLabels[label];
+}
+
+const char* CCoreAudioChannelLayout::ChannelLayoutToString(AudioChannelLayout& layout, std::string& str)
+{
+ AudioChannelLayout* pLayout = NULL;
+
+ if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions)
+ {
+ pLayout = &layout;
+ }
+ else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Deconstruct the bitmap to get the layout
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize);
+ pLayout = (AudioChannelLayout*)calloc(propSize, 1);
+ AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize, pLayout);
+ }
+ else // Predefinied layout 'tag'
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize);
+ pLayout = (AudioChannelLayout*)calloc(propSize, 1);
+ AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize, pLayout);
+ }
+
+ for (UInt32 c = 0; c < pLayout->mNumberChannelDescriptions; c++)
+ {
+ str += "[";
+ str += ChannelLabelToString(pLayout->mChannelDescriptions[c].mChannelLabel);
+ str += "] ";
+ }
+
+ if (layout.mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
+ free(pLayout);
+
+ return str.c_str();
+}
+
+bool CCoreAudioChannelLayout::AllChannelUnknown()
+{
+ AudioChannelLayout* pLayout = NULL;
+
+ if (!m_pLayout)
+ return false;
+
+ if (m_pLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions)
+ {
+ pLayout = m_pLayout;
+ }
+ else if (m_pLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Deconstruct the bitmap to get the layout
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(m_pLayout->mChannelBitmap), &m_pLayout->mChannelBitmap, &propSize);
+ pLayout = (AudioChannelLayout*)calloc(propSize, 1);
+ AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(m_pLayout->mChannelBitmap), &m_pLayout->mChannelBitmap, &propSize, pLayout);
+ }
+ else // Predefinied layout 'tag'
+ {
+ UInt32 propSize = 0;
+ AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(m_pLayout->mChannelLayoutTag), &m_pLayout->mChannelLayoutTag, &propSize);
+ pLayout = (AudioChannelLayout*)calloc(propSize, 1);
+ AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(m_pLayout->mChannelLayoutTag), &m_pLayout->mChannelLayoutTag, &propSize, pLayout);
+ }
+
+ for (UInt32 c = 0; c < pLayout->mNumberChannelDescriptions; c++)
+ {
+ if (pLayout->mChannelDescriptions[c].mChannelLabel != kAudioChannelLabel_Unknown)
+ {
+ return false;
+ }
+ }
+
+ if (m_pLayout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
+ free(pLayout);
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioGraph
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioGraph::CCoreAudioGraph() :
+ m_audioGraph (NULL ),
+ m_audioUnit (NULL ),
+ m_mixerUnit (NULL ),
+ m_inputUnit (NULL ),
+ m_initialized (false),
+ m_deviceId (NULL ),
+ m_allowMixing (false),
+ m_mixMap (NULL ),
+ m_ATV1 (false)
+{
+ for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
+ {
+ m_reservedBusNumber[i] = false;
+ }
+
+ m_ATV1 = g_sysinfo.IsAppleTV();
+}
+
+CCoreAudioGraph::~CCoreAudioGraph()
+{
+ Close();
+
+ delete m_mixMap;
+}
+
+bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID deviceId, bool allowMixing, AudioChannelLayoutTag layoutTag)
+{
+ OSStatus ret;
+
+ AudioStreamBasicDescription inputFormat;
+ AudioStreamBasicDescription outputFormat;
+ AudioStreamBasicDescription fmt;
+
+ m_allowMixing = allowMixing;
+ m_deviceId = deviceId;
+
+ ret = NewAUGraph(&m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error create audio grpah. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ ret = AUGraphOpen(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error open audio grpah. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // get output unit
+ if (m_audioUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error audio unit already open. double call ?");
+ return false;
+ }
+
+ m_audioUnit = new CAUOutputDevice();
+ if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple))
+ return false;
+
+ m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
+
+ if (!m_audioUnit->EnableInputOuput())
+ return false;
+
+ if (!m_audioUnit->SetCurrentDevice(deviceId))
+ return false;
+
+ if (allowMixing)
+ {
+ delete m_mixMap;
+ m_mixMap = CCoreAudioMixMap::CreateMixMap(m_audioUnit, format, layoutTag);
+
+ if (m_mixMap || m_mixMap->IsValid())
+ {
+ // maximum input channel ber input bus
+ //fmt.mChannelsPerFrame = MAXIMUM_MIXER_CHANNELS;
+
+ // get output unit
+ if (m_inputUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
+ return false;
+ }
+
+ m_inputUnit = new CAUOutputDevice();
+
+ if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
+ return false;
+
+ if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
+ return false;
+
+ if (!m_inputUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
+ return false;
+
+ // get mixer unit
+ if (m_mixerUnit)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
+ return false;
+ }
+
+ m_mixerUnit = new CAUMatrixMixer();
+
+ if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple))
+ return false;
+
+ // set number of input buses
+ if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
+ return false;
+
+ // set number of output buses
+ if (!m_mixerUnit->SetOutputBusCount(1))
+ return false;
+
+ if (!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &fmt))
+ return false;
+
+ if (!m_mixerUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
+ return false;
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_mixerUnit->SetBus(0);
+
+ // configure output unit
+ int busNumber = GetFreeBus();
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error connecting m_converterNode. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ m_inputUnit->SetBus(busNumber);
+
+ ret = AUGraphUpdate(m_audioGraph, NULL);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ ret = AUGraphInitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ // Update format structure to reflect the desired format from the mixer
+ fmt.mChannelsPerFrame = m_mixMap->GetOutputChannels(); // The output format of the mixer is identical to the input format, except for the channel count
+
+ UInt32 inputNumber = m_inputUnit->GetBus();
+ int channelOffset = GetMixerChannelOffset(inputNumber);
+ if (!CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &inputFormat, &fmt, channelOffset))
+ return false;
+
+ // Regenerate audio format and copy format for the Output AU
+ outputFormat = fmt;
+ }
+ else
+ {
+ outputFormat = inputFormat;
+ }
+
+ }
+ else
+ {
+ outputFormat = inputFormat;
+ }
+
+ if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error setting input format on audio device. Channel count %d, set it to %d",
+ outputFormat.mChannelsPerFrame, format.m_channelLayout.Count());
+ outputFormat.mChannelsPerFrame = format.m_channelLayout.Count();
+ if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
+ return false;
+ }
+
+ std::string formatString;
+ // asume we are in dd-wave mode
+ if (!m_ATV1 && !m_inputUnit)
+ {
+ if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error setting Device Output Stream Format %s", StreamDescriptionToString(inputFormat, formatString));
+ }
+ }
+
+#ifdef TAGRGET_IOS
+ if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error setting Device Output Stream Format %s", StreamDescriptionToString(inputFormat, formatString));
+ }
+#endif
+
+ ret = AUGraphUpdate(m_audioGraph, NULL);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error update graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ AudioStreamBasicDescription inputDesc_end, outputDesc_end;
+ m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+
+ if (m_mixerUnit)
+ {
+ m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+ }
+
+ if (m_inputUnit)
+ {
+ m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
+ m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Input Stream Format %s", StreamDescriptionToString(inputDesc_end, formatString));
+ CLog::Log(LOGINFO, "CCoreAudioGraph::Open: Output Stream Format %s", StreamDescriptionToString(outputDesc_end, formatString));
+ }
+
+ ret = AUGraphInitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error initialize graph. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+
+ UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
+ if (!m_audioUnit->SetMaxFramesPerSlice(bufferFrames))
+ return false;
+
+ SetInputSource(pSource);
+
+ ShowGraph();
+
+ return Start();
+}
+
+bool CCoreAudioGraph::Close()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+
+ Stop();
+
+ SetInputSource(NULL);
+
+ while (!m_auUnitList.empty())
+ {
+ CAUOutputDevice *d = m_auUnitList.front();
+ m_auUnitList.pop_front();
+ ReleaseBus(d->GetBus());
+ d->Close();
+ delete d;
+ }
+
+ if (m_inputUnit)
+ {
+ ReleaseBus(m_inputUnit->GetBus());
+ m_inputUnit->Close();
+ delete m_inputUnit;
+ m_inputUnit = NULL;
+ }
+
+ if (m_mixerUnit)
+ {
+ m_mixerUnit->Close();
+ delete m_mixerUnit;
+ m_mixerUnit = NULL;
+ }
+
+ if (m_audioUnit)
+ {
+ m_audioUnit->Close();
+ delete m_audioUnit;
+ m_audioUnit = NULL;
+ }
+
+ ret = AUGraphUninitialize(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error unitialize. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = AUGraphClose(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error close. Error = %s", GetError(ret).c_str());
+ }
+
+ ret = DisposeAUGraph(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Close: Error dispose. Error = %s", GetError(ret).c_str());
+ }
+
+ return true;
+}
+
+bool CCoreAudioGraph::Start()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+ Boolean isRunning = false;
+
+ ret = AUGraphIsRunning(m_audioGraph, &isRunning);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Audio graph not running. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ if (!isRunning)
+ {
+
+ if (m_audioUnit)
+ m_audioUnit->Start();
+ if (m_mixerUnit)
+ m_mixerUnit->Start();
+ if (m_inputUnit)
+ m_inputUnit->Start();
+
+ ret = AUGraphStart(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Start: Error starting audio graph. Error = %s", GetError(ret).c_str());
+ }
+ ShowGraph();
+ }
+
+ return true;
+}
+
+bool CCoreAudioGraph::Stop()
+{
+ if (!m_audioGraph)
+ return false;
+
+ OSStatus ret;
+ Boolean isRunning = false;
+
+ ret = AUGraphIsRunning(m_audioGraph, &isRunning);
+ if (ret)
+ {
+
+ if (m_inputUnit)
+ m_inputUnit->Stop();
+ if (m_mixerUnit)
+ m_mixerUnit->Stop();
+ if (m_audioUnit)
+ m_audioUnit->Stop();
+
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Audio graph not running. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ if (isRunning)
+ {
+ ret = AUGraphStop(m_audioGraph);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: Error stopping audio graph. Error = %s", GetError(ret).c_str());
+ }
+ }
+
+ return true;
+}
+
+AudioChannelLayoutTag CCoreAudioGraph::GetChannelLayoutTag(int layout)
+{
+ return g_LayoutMap[layout];
+}
+
+bool CCoreAudioGraph::SetInputSource(ICoreAudioSource* pSource)
+{
+ if (m_inputUnit)
+ return m_inputUnit->SetInputSource(pSource);
+ else if (m_audioUnit)
+ return m_audioUnit->SetInputSource(pSource);
+
+ return false;
+}
+
+bool CCoreAudioGraph::SetCurrentVolume(Float32 vol)
+{
+ if (!m_audioUnit)
+ return false;
+
+ return m_audioUnit->SetCurrentVolume(vol);
+}
+
+CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit)
+{
+ if (!outputUnit)
+ return NULL;
+
+ Stop();
+
+ for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt)
+ if (*itt == outputUnit)
+ {
+ m_auUnitList.erase(itt);
+ break;
+ }
+
+ ReleaseBus(outputUnit->GetBus());
+ outputUnit->SetInputSource(NULL);
+ outputUnit->Close();
+ delete outputUnit;
+ outputUnit = NULL;
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ printf("Remove unit\n\n");
+ ShowGraph();
+ printf("\n");
+
+ Start();
+
+ return NULL;
+}
+
+CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
+{
+ if (!m_audioUnit || !m_mixerUnit)
+ return NULL;
+
+ AudioStreamBasicDescription inputFormat;
+ AudioStreamBasicDescription outputFormat;
+ AudioStreamBasicDescription fmt;
+
+ OSStatus ret;
+
+ int busNumber = GetFreeBus();
+ if (busNumber == INVALID_BUS)
+ return NULL;
+
+ // create output unit
+ CAUOutputDevice *outputUnit = new CAUOutputDevice();
+ if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
+ goto error;
+
+ m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
+
+ // get the format frm the mixer
+ if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
+ goto error;
+
+ if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
+ goto error;
+
+ if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
+ goto error;
+
+ ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: Error connecting outputUnit. Error = %s", GetError(ret).c_str());
+ goto error;
+ }
+
+ // TODO: setup mixmap, get free bus number for connection
+
+ outputUnit->SetBus(busNumber);
+
+ if (m_mixMap || m_mixMap->IsValid())
+ {
+ UInt32 inputNumber = outputUnit->GetBus();
+ int channelOffset = GetMixerChannelOffset(inputNumber);
+ CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &inputFormat, &fmt, channelOffset);
+ }
+
+
+ AUGraphUpdate(m_audioGraph, NULL);
+
+ printf("Add unit\n\n");
+ ShowGraph();
+ printf("\n");
+
+ m_auUnitList.push_back(outputUnit);
+
+ return outputUnit;
+
+error:
+ delete outputUnit;
+ return NULL;
+}
+
+int CCoreAudioGraph::GetFreeBus()
+{
+ for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
+ {
+ if (!m_reservedBusNumber[i])
+ {
+ m_reservedBusNumber[i] = true;
+ return i;
+ }
+ }
+ return INVALID_BUS;
+}
+
+void CCoreAudioGraph::ReleaseBus(int busNumber)
+{
+ if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
+ return;
+
+ m_reservedBusNumber[busNumber] = false;
+}
+
+bool CCoreAudioGraph::IsBusFree(int busNumber)
+{
+ if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
+ return false;
+ return m_reservedBusNumber[busNumber];
+}
+
+int CCoreAudioGraph::GetMixerChannelOffset(int busNumber)
+{
+ if (!m_mixerUnit)
+ return 0;
+
+ int offset = 0;
+ AudioStreamBasicDescription fmt;
+
+ for (int i = 0; i < busNumber; i++)
+ {
+ memset(&fmt, 0x0, sizeof(fmt));
+ m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
+ offset += fmt.mChannelsPerFrame;
+ }
+ return offset;
+}
+
+void CCoreAudioGraph::ShowGraph()
+{
+ CAShow(m_audioGraph);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioAEHALOSX
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CCoreAudioAEHALOSX::CCoreAudioAEHALOSX() :
+ m_Initialized (false ),
+ m_Passthrough (false ),
+ m_NumLatencyFrames (0 ),
+ m_OutputBufferIndex (0 ),
+ m_allowMixing (false ),
+ m_encoded (false ),
+ m_audioGraph (NULL )
+{
+ m_AudioDevice = new CCoreAudioDevice();
+ m_OutputStream = new CCoreAudioStream();
+
+#if defined(__APPLE__) && !defined(__arm__)
+ SInt32 major, minor;
+ Gestalt(gestaltSystemVersionMajor, &major);
+ Gestalt(gestaltSystemVersionMinor, &minor);
+
+ // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
+ // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
+ // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
+ // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
+ if (major == 10 && minor >=6)
+ {
+ CFRunLoopRef theRunLoop = NULL;
+ AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
+ if (theError != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
+ }
+ }
+#endif
+}
+
+CCoreAudioAEHALOSX::~CCoreAudioAEHALOSX()
+{
+ Deinitialize();
+
+ delete m_audioGraph;
+ delete m_AudioDevice;
+ delete m_OutputStream;
+}
+
+bool CCoreAudioAEHALOSX::InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing, AudioDeviceID outputDevice)
+{
+
+ if (m_audioGraph)
+ {
+ m_audioGraph->Close();
+ delete m_audioGraph;
+ }
+ m_audioGraph = new CCoreAudioGraph();
+
+ if (!m_audioGraph)
+ return false;
+
+ if (!m_audioGraph->Open(pSource, format, outputDevice, allowMixing, g_LayoutMap[ g_guiSettings.GetInt("audiooutput.channellayout") ] ))
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::Initialize: Unable to initialize audio due a missconfiguration. Try 2.0 speaker configuration.");
+ return false;
+ }
+
+ m_NumLatencyFrames = m_AudioDevice->GetNumLatencyFrames();
+
+ m_allowMixing = allowMixing;
+
+ return true;
+}
+
+bool CCoreAudioAEHALOSX::InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID outputDevice)
+{
+ m_AudioDevice->SetHogStatus(true); // Prevent any other application from using this device.
+ m_AudioDevice->SetMixingSupport(false); // Try to disable mixing support. Effectiveness depends on the device.
+
+ // Set the Sample Rate as defined by the spec.
+ m_AudioDevice->SetNominalSampleRate((float)format.m_sampleRate);
+
+ if (!InitializePCM(pSource, format, false, outputDevice))
+ return false;
+
+ return true;
+}
+
+bool CCoreAudioAEHALOSX::InitializeEncoded(AudioDeviceID outputDevice, AEAudioFormat &format)
+{
+ std::string formatString;
+ AudioStreamBasicDescription outputFormat = {0};
+ AudioStreamID outputStream = 0;
+
+ // Fetch a list of the streams defined by the output device
+ AudioStreamIdList streams;
+ UInt32 streamIndex = 0;
+ m_AudioDevice->GetStreams(&streams);
+
+ m_OutputBufferIndex = 0;
+
+ while (!streams.empty())
+ {
+ // Get the next stream
+ CCoreAudioStream stream;
+ stream.Open(streams.front());
+ streams.pop_front(); // We copied it, now we are done with it
+
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Found %s stream - id: 0x%04X, Terminal Type: 0x%04lX",
+ stream.GetDirection() ? "Input" : "Output",
+ stream.GetId(),
+ stream.GetTerminalType());
+
+ // Probe physical formats
+ StreamFormatList physicalFormats;
+ stream.GetAvailablePhysicalFormats(&physicalFormats);
+ while (!physicalFormats.empty())
+ {
+ AudioStreamRangedDescription& desc = physicalFormats.front();
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Considering Physical Format: %s", StreamDescriptionToString(desc.mFormat, formatString));
+
+ if (m_rawDataFormat == AE_FMT_LPCM || m_rawDataFormat == AE_FMT_DTSHD ||
+ m_rawDataFormat == AE_FMT_TRUEHD || m_rawDataFormat == AE_FMT_EAC3)
+ {
+ unsigned int bps = CAEUtil::DataFormatToBits(AE_FMT_S16NE);
+ if (desc.mFormat.mChannelsPerFrame == m_initformat.m_channelLayout.Count() && desc.mFormat.mBitsPerChannel == bps &&
+ desc.mFormat.mSampleRate == m_initformat.m_sampleRate )
+ {
+ outputFormat = desc.mFormat; // Select this format
+ m_OutputBufferIndex = streamIndex;
+ outputStream = stream.GetId();
+ break;
+ }
+ }
+ else
+ {
+ if (desc.mFormat.mFormatID == kAudioFormat60958AC3 || desc.mFormat.mFormatID == 'IAC3')
+ {
+ outputFormat = desc.mFormat; // Select this format
+ m_OutputBufferIndex = streamIndex;
+ outputStream = stream.GetId();
+ break;
+ }
+ }
+ physicalFormats.pop_front();
+ }
+
+ // TODO: How do we determine if this is the right stream (not just the right format) to use?
+ if (outputFormat.mFormatID)
+ break; // We found a suitable format. No need to continue.
+ streamIndex++;
+ }
+
+ if (!outputFormat.mFormatID) // No match found
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Unable to identify suitable output format.");
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Selected stream[%lu] - id: 0x%04lX, Physical Format: %s", m_OutputBufferIndex, outputStream, StreamDescriptionToString(outputFormat, formatString));
+
+ // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
+ // It appears that leaving this set will aslo restore the previous stream format when the
+ // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
+ // From the SDK docs: "If the AudioDevice is in a non-mixable mode, the HAL will automatically take hog mode on behalf of the first process to start an IOProc."
+
+ // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
+ // If it is attempted after the format change, there is a high likelihood of a deadlock
+ // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
+
+ CCoreAudioHardware::SetAutoHogMode(false); // Auto-Hog does not always un-hog the device when changing back to a mixable mode. Handle this on our own until it is fixed.
+ bool autoHog = CCoreAudioHardware::GetAutoHogMode();
+ CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
+ if (!autoHog) // Try to handle this ourselves
+ {
+ m_AudioDevice->SetHogStatus(true); // Hog the device if it is not set to be done automatically
+ m_AudioDevice->SetMixingSupport(false); // Try to disable mixing. If we cannot, it may not be a problem
+ }
+
+ m_NumLatencyFrames = m_AudioDevice->GetNumLatencyFrames();
+
+ // Configure the output stream object
+ m_OutputStream->Open(outputStream); // This is the one we will keep
+
+ AudioStreamBasicDescription virtualFormat;
+ m_OutputStream->GetVirtualFormat(&virtualFormat);
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Previous Virtual Format: %s", StreamDescriptionToString(virtualFormat, formatString));
+
+ AudioStreamBasicDescription previousPhysicalFormat;
+ m_OutputStream->GetPhysicalFormat(&previousPhysicalFormat);
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: Previous Physical Format: %s", StreamDescriptionToString(previousPhysicalFormat, formatString));
+
+ m_OutputStream->SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
+ m_NumLatencyFrames += m_OutputStream->GetNumLatencyFrames();
+
+ m_OutputStream->GetVirtualFormat(&virtualFormat);
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: New Virtual Format: %s", StreamDescriptionToString(virtualFormat, formatString));
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::InitializeEncoded: New Physical Format: %s", StreamDescriptionToString(outputFormat, formatString));
+
+ m_allowMixing = false;
+
+ return true;
+}
+
+bool CCoreAudioAEHALOSX::Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device)
+{
+ // Reset all the devices to a default 'non-hog' and mixable format.
+ // If we don't do this we may be unable to find the Default Output device.
+ // (e.g. if we crashed last time leaving it stuck in AC-3 mode)
+
+ CCoreAudioHardware::ResetAudioDevices();
+
+ m_ae = (CCoreAudioAE *)ae;
+
+ if (!m_ae)
+ return false;
+
+ m_initformat = format;
+ m_rawDataFormat = rawDataFormat;
+ m_Passthrough = passThrough;
+ m_encoded = false;
+ m_OutputBufferIndex = 0;
+
+ if (format.m_channelLayout.Count() == 0)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioAEHALOSX::Initialize - Unable to open the requested channel layout");
+ return false;
+ }
+
+ if (device.find("CoreAudio:") != std::string::npos)
+ device.erase(0, strlen("CoreAudio:"));
+
+ AudioDeviceID outputDevice = CCoreAudioHardware::FindAudioDevice(device);
+
+ if (!outputDevice) // Fall back to the default device if no match is found
+ {
+ CLog::Log(LOGWARNING, "CCoreAudioAEHALOSX::Initialize: Unable to locate configured device, falling-back to the system default.");
+ outputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
+ if (!outputDevice) // Not a lot to be done with no device. TODO: Should we just grab the first existing device?
+ return false;
+ }
+
+ // Attach our output object to the device
+ m_AudioDevice->Open(outputDevice);
+
+ // If this is a passthrough (AC3/DTS) stream, attempt to handle it natively
+ if (m_Passthrough)
+ {
+ m_encoded = InitializeEncoded(outputDevice, format);
+ }
+
+ // If this is a PCM stream, or we failed to handle a passthrough stream natively,
+ // prepare the standard interleaved PCM interface
+ if (!m_encoded)
+ {
+ // If we are here and this is a passthrough stream, native handling failed.
+ // Try to handle it as IEC61937 data over straight PCM (DD-Wav)
+ bool configured = false;
+ if (m_Passthrough)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioAEHALOSX::Initialize: No suitable AC3 output format found. Attempting DD-Wav.");
+ configured = InitializePCMEncoded(ae, format, outputDevice);
+ }
+ else
+ {
+ // Standard PCM data
+ configured = InitializePCM(ae, format, true, outputDevice);
+ }
+
+ if (!configured) // No suitable output format was able to be configured
+ return false;
+ }
+
+ if (m_audioGraph)
+ m_audioGraph->ShowGraph();
+
+ m_Initialized = true;
+
+ return true;
+}
+
+CAUOutputDevice *CCoreAudioAEHALOSX::DestroyUnit(CAUOutputDevice *outputUnit)
+{
+ if (m_audioGraph && outputUnit)
+ return m_audioGraph->DestroyUnit(outputUnit);
+
+ return NULL;
+}
+
+CAUOutputDevice *CCoreAudioAEHALOSX::CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format)
+{
+ CAUOutputDevice *outputUnit = NULL;
+
+ // when HAL is using a mixer, the input is routed through converter units.
+ // therefore we create a converter unit attach the source and give it back.
+ if (m_allowMixing && m_audioGraph)
+ {
+ outputUnit = m_audioGraph->CreateUnit(format);
+
+ if (pSource && outputUnit)
+ outputUnit->SetInputSource(pSource);
+ }
+
+ return outputUnit;
+}
+
+void CCoreAudioAEHALOSX::Deinitialize()
+{
+ if (!m_Initialized)
+ return;
+
+ Stop();
+
+ //if (m_encoded)
+
+ if (m_encoded)
+ m_AudioDevice->SetInputSource(NULL, 0, 0);
+
+ if (m_audioGraph)
+ m_audioGraph->SetInputSource(NULL);
+
+ m_OutputStream->Close();
+ m_AudioDevice->Close();
+
+ if (m_audioGraph)
+ {
+ //m_audioGraph->Close();
+ delete m_audioGraph;
+ }
+ m_audioGraph = NULL;
+
+ m_NumLatencyFrames = 0;
+ m_OutputBufferIndex = 0;
+
+ m_Initialized = false;
+ m_Passthrough = false;
+
+ CLog::Log(LOGINFO, "CCoreAudioAEHALOSX::Deinitialize: Audio device has been closed.");
+}
+
+void CCoreAudioAEHALOSX::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ CoreAudioDeviceList deviceList;
+ CCoreAudioHardware::GetOutputDevices(&deviceList);
+
+ std::string defaultDeviceName;
+ CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
+
+ std::string deviceName;
+ for (int i = 0; !deviceList.empty(); i++)
+ {
+ CCoreAudioDevice device(deviceList.front());
+ deviceName = device.GetName();
+
+ std::string deviceName_Internal = std::string("CoreAudio:");
+ deviceName_Internal.append(deviceName);
+ devices.push_back(AEDevice(deviceName, deviceName_Internal));
+
+ deviceList.pop_front();
+ }
+}
+
+void CCoreAudioAEHALOSX::Stop()
+{
+ if (!m_Initialized)
+ return;
+
+ if (m_encoded)
+ m_AudioDevice->Stop();
+ else
+ m_audioGraph->Stop();
+}
+
+bool CCoreAudioAEHALOSX::Start()
+{
+ if (!m_Initialized)
+ return false;
+
+ if (m_encoded)
+ m_AudioDevice->Start();
+ else
+ m_audioGraph->Start();
+
+ return true;
+}
+
+void CCoreAudioAEHALOSX::SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format)
+{
+ if (!m_Initialized)
+ return;
+
+ // when HAL is initialized encoded we use directIO
+ // when HAL is not in encoded mode and there is no mixer attach source the audio unit
+ // when mixing is allowed in HAL, HAL is working with converter units where we attach the source.
+
+ if (m_encoded)
+ {
+ // register directcallback for the audio HAL
+ // direct render callback need to know the framesize and buffer index
+ if (pSource)
+ {
+ m_AudioDevice->SetInputSource(pSource, format.m_frameSize, m_OutputBufferIndex);
+ }
+ else
+ {
+ m_AudioDevice->SetInputSource(pSource, 0, 0);
+ }
+ }
+ else if (!m_encoded && !m_allowMixing)
+ {
+ // register render callback for the audio unit
+ m_audioGraph->SetInputSource(pSource);
+ }
+
+ if (m_audioGraph)
+ m_audioGraph->ShowGraph();
+
+}
+
+double CCoreAudioAEHALOSX::GetDelay()
+{
+ double delay;
+
+ delay = (double)(m_NumLatencyFrames) / (m_initformat.m_sampleRate);
+
+ return delay;
+}
+
+void CCoreAudioAEHALOSX::SetVolume(float volume)
+{
+ if (m_encoded || m_Passthrough)
+ return;
+
+ m_audioGraph->SetCurrentVolume(volume);
+}
+
+unsigned int CCoreAudioAEHALOSX::GetBufferIndex()
+{
+ return m_OutputBufferIndex;
+}
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.h
new file mode 100644
index 0000000000..128a33aee4
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEHALOSX.h
@@ -0,0 +1,373 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __arm__
+
+#include "ICoreAudioAEHAL.h"
+#include "ICoreAudioSource.h"
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioToolbox/AUGraph.h>
+#include <list>
+#include <vector>
+#include "utils/StdString.h"
+#include "CoreAudioAEHAL.h"
+
+#define kOutputBus 0
+#define kInputBus 1
+#define MAX_CONNECTION_LIMIT 8
+#define INVALID_BUS -1
+#define MAXIMUM_MIXER_CHANNELS 9
+#define MAX_CHANNEL_LABEL 15
+
+// Forward declarations
+class CCoreAudioHardware;
+class CCoreAudioDevice;
+class CCoreAudioStream;
+class CCoreAudioUnit;
+class CCoreAudioAE;
+class CCoreAudioChannelLayout;
+class CAUGenericSource;
+
+typedef std::list<AudioDeviceID> CoreAudioDeviceList;
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
+/* AudioDeviceIOProcID does not exist in Mac OS X 10.4. We can emulate
+ * this by using AudioDeviceAddIOProc() and AudioDeviceRemoveIOProc(). */
+#define AudioDeviceIOProcID AudioDeviceIOProc
+#define AudioDeviceDestroyIOProcID AudioDeviceRemoveIOProc
+static OSStatus AudioDeviceCreateIOProcID(AudioDeviceID dev,
+ AudioDeviceIOProc proc,
+ void *data,
+ AudioDeviceIOProcID *procid)
+{
+ *procid = proc;
+ return AudioDeviceAddIOProc(dev, proc, data);
+}
+#endif
+
+// There is only one AudioSystemObject instance system-side.
+// Therefore, all CCoreAudioHardware methods are static
+class CCoreAudioHardware
+{
+public:
+ static bool GetAutoHogMode();
+ static void SetAutoHogMode(bool enable);
+ static AudioStreamBasicDescription *CCoreAudioHardware::FormatsList(AudioStreamID stream);
+ static AudioStreamID *StreamsList(AudioDeviceID device);
+ static void ResetAudioDevices();
+ static void ResetStream(AudioStreamID stream);
+ static AudioDeviceID FindAudioDevice(std::string deviceName);
+ static AudioDeviceID GetDefaultOutputDevice();
+ static void GetOutputDeviceName(std::string& name);
+ static UInt32 GetOutputDevices(CoreAudioDeviceList* pList);
+};
+
+typedef std::list<AudioStreamID> AudioStreamIdList;
+typedef std::vector<SInt32> CoreAudioChannelList;
+typedef std::list<UInt32> CoreAudioDataSourceList;
+
+class CCoreAudioDevice
+{
+public:
+ CCoreAudioDevice();
+ CCoreAudioDevice(AudioDeviceID deviceId);
+ virtual ~CCoreAudioDevice();
+
+ bool Open(AudioDeviceID deviceId);
+ void Close();
+
+ void Start();
+ void Stop();
+ void RemoveObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData);
+ bool SetObjectListenerProc(AudioObjectPropertyListenerProc callback, void* pClientData);
+
+ AudioDeviceID GetId() {return m_DeviceId;}
+ std::string GetName();
+ UInt32 GetTotalOutputChannels();
+ bool GetStreams(AudioStreamIdList* pList);
+ bool IsRunning();
+ bool SetHogStatus(bool hog);
+ pid_t GetHogStatus();
+ bool SetMixingSupport(UInt32 mix);
+ bool GetMixingSupport();
+ bool SetCurrentVolume(Float32 vol);
+ bool GetPreferredChannelLayout(CCoreAudioChannelLayout& layout);
+ bool GetDataSources(CoreAudioDataSourceList* pList);
+ Float64 GetNominalSampleRate();
+ bool SetNominalSampleRate(Float64 sampleRate);
+ UInt32 GetNumLatencyFrames();
+ UInt32 GetBufferSize();
+ bool SetBufferSize(UInt32 size);
+ virtual bool SetInputSource(ICoreAudioSource* pSource, unsigned int frameSize, unsigned int outputBufferIndex);
+protected:
+ bool AddIOProc();
+ bool RemoveIOProc();
+ ICoreAudioSource* m_pSource;
+ static OSStatus DirectRenderCallback(AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* inClientData);
+ AudioDeviceID m_DeviceId;
+ bool m_Started;
+ int m_MixerRestore;
+ AudioDeviceIOProc m_IoProc;
+ AudioObjectPropertyListenerProc m_ObjectListenerProc;
+
+ Float64 m_SampleRateRestore;
+ pid_t m_HogPid;
+ unsigned int m_frameSize;
+ unsigned int m_OutputBufferIndex;
+};
+
+typedef std::list<AudioStreamRangedDescription> StreamFormatList;
+
+class CCoreAudioStream
+{
+public:
+ CCoreAudioStream();
+ virtual ~CCoreAudioStream();
+
+ bool Open(AudioStreamID streamId);
+ void Close();
+
+ AudioStreamID GetId() {return m_StreamId;}
+ UInt32 GetDirection();
+ UInt32 GetTerminalType();
+ UInt32 GetNumLatencyFrames();
+ bool GetVirtualFormat(AudioStreamBasicDescription* pDesc);
+ bool GetPhysicalFormat(AudioStreamBasicDescription* pDesc);
+ bool SetVirtualFormat(AudioStreamBasicDescription* pDesc);
+ bool SetPhysicalFormat(AudioStreamBasicDescription* pDesc);
+ bool GetAvailableVirtualFormats(StreamFormatList* pList);
+ bool GetAvailablePhysicalFormats(StreamFormatList* pList);
+
+protected:
+ AudioStreamID m_StreamId;
+ AudioStreamBasicDescription m_OriginalVirtualFormat;
+ AudioStreamBasicDescription m_OriginalPhysicalFormat;
+};
+
+typedef std::list<AudioChannelLayoutTag> AudioChannelLayoutList;
+
+class CCoreAudioUnit
+{
+public:
+ CCoreAudioUnit();
+ virtual ~CCoreAudioUnit();
+
+ virtual bool Open(AUGraph audioGraph, ComponentDescription desc);
+ virtual bool Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer);
+ virtual void Close();
+ virtual bool SetInputSource(ICoreAudioSource* pSource);
+ virtual bool IsInitialized() {return m_Initialized;}
+ virtual bool GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus);
+ virtual bool SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus);
+ virtual bool SetMaxFramesPerSlice(UInt32 maxFrames);
+ virtual bool GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts);
+ virtual void GetFormatDesc(AEAudioFormat format,
+ AudioStreamBasicDescription *streamDesc,
+ AudioStreamBasicDescription *coreaudioDesc);
+ virtual float GetLatency();
+ virtual bool Stop();
+ virtual bool Start();
+ virtual AudioUnit GetUnit (){return m_audioUnit;}
+ virtual AUGraph GetGraph (){return m_audioGraph;}
+ virtual AUNode GetNode (){return m_audioNode;}
+ virtual int GetBus (){return m_busNumber;}
+ virtual void SetBus (int busNumber){m_busNumber = busNumber;}
+protected:
+ bool SetRenderProc();
+ bool RemoveRenderProc();
+ static OSStatus RenderCallback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+ ICoreAudioSource* m_pSource;
+ AudioUnit m_audioUnit;
+ AUNode m_audioNode;
+ AUGraph m_audioGraph;
+ bool m_Initialized;
+ AURenderCallback m_renderProc;
+ int m_busNumber;
+};
+
+class CAUOutputDevice : public CCoreAudioUnit
+{
+public:
+ CAUOutputDevice();
+ virtual ~CAUOutputDevice();
+ bool SetCurrentDevice(AudioDeviceID deviceId);
+ bool GetChannelMap(CoreAudioChannelList* pChannelMap);
+ bool SetChannelMap(CoreAudioChannelList* pChannelMap);
+ UInt32 GetBufferFrameSize();
+
+ Float32 GetCurrentVolume();
+ bool SetCurrentVolume(Float32 vol);
+ bool EnableInputOuput();
+ virtual bool GetPreferredChannelLayout(CCoreAudioChannelLayout& layout);
+protected:
+ AudioDeviceID m_DeviceId;
+};
+
+class CAUMatrixMixer : public CAUOutputDevice
+{
+public:
+ CAUMatrixMixer();
+ virtual ~CAUMatrixMixer();
+ bool InitMatrixMixerVolumes();
+
+ UInt32 GetInputBusCount();
+ bool SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat);
+ bool SetInputBusCount(UInt32 busCount);
+ UInt32 GetOutputBusCount();
+ bool SetOutputBusCount(UInt32 busCount);
+
+ Float32 GetGlobalVolume();
+ bool SetGlobalVolume(Float32 vol);
+ Float32 GetInputVolume(UInt32 element);
+ bool SetInputVolume(UInt32 element, Float32 vol);
+ Float32 GetOutputVolume(UInt32 element);
+ bool SetOutputVolume(UInt32 element, Float32 vol);
+};
+
+class CCoreAudioMixMap
+{
+public:
+ CCoreAudioMixMap();
+ CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout);
+ virtual ~CCoreAudioMixMap();
+ operator Float32*() const {return m_pMap;}
+ const Float32* GetBuffer() {return m_pMap;}
+ UInt32 GetInputChannels() {return m_inChannels;}
+ UInt32 GetOutputChannels() {return m_outChannels;}
+ bool IsValid() {return m_isValid;}
+ void Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout);
+ static CCoreAudioMixMap *CreateMixMap(CAUOutputDevice *audioUnit, AEAudioFormat &format, AudioChannelLayoutTag layoutTag);
+ static bool SetMixingMatrix(CAUMatrixMixer *mixerUnit, CCoreAudioMixMap *mixMap, AudioStreamBasicDescription *inputFormat, AudioStreamBasicDescription *fmt, int channelOffset);
+private:
+ Float32* m_pMap;
+ UInt32 m_inChannels;
+ UInt32 m_outChannels;
+ bool m_isValid;
+};
+
+class CCoreAudioChannelLayout
+{
+public:
+ CCoreAudioChannelLayout();
+ CCoreAudioChannelLayout(AudioChannelLayout& layout);
+ virtual ~CCoreAudioChannelLayout();
+ operator AudioChannelLayout*() {return m_pLayout;}
+ bool CopyLayout(AudioChannelLayout& layout);
+ static UInt32 GetChannelCountForLayout(AudioChannelLayout& layout);
+ static const char* ChannelLabelToString(UInt32 label);
+ static const char* ChannelLayoutToString(AudioChannelLayout& layout, std::string& str);
+ bool AllChannelUnknown();
+protected:
+ AudioChannelLayout* m_pLayout;
+};
+
+class CCoreAudioGraph
+{
+private:
+ AUGraph m_audioGraph;
+
+ CAUOutputDevice *m_audioUnit;
+ CAUMatrixMixer *m_mixerUnit;
+ CAUOutputDevice *m_inputUnit;
+
+ int m_reservedBusNumber[MAX_CONNECTION_LIMIT];
+ bool m_initialized;
+ AudioDeviceID m_deviceId;
+ bool m_allowMixing;
+ CCoreAudioMixMap *m_mixMap;
+
+ typedef std::list<CAUOutputDevice*> AUUnitList;
+ AUUnitList m_auUnitList;
+
+ bool m_ATV1;
+
+public:
+ CCoreAudioGraph();
+ ~CCoreAudioGraph();
+
+ bool Open(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID deviceId, bool allowMixing, AudioChannelLayoutTag layoutTag);
+ bool Close();
+ bool Start();
+ bool Stop();
+ AudioChannelLayoutTag GetChannelLayoutTag(int layout);
+ bool SetInputSource(ICoreAudioSource* pSource);
+ bool SetCurrentVolume(Float32 vol);
+ CAUOutputDevice *DestroyUnit(CAUOutputDevice *outputUnit);
+ CAUOutputDevice *CreateUnit(AEAudioFormat &format);
+ int GetFreeBus();
+ void ReleaseBus(int busNumber);
+ bool IsBusFree(int busNumber);
+ int GetMixerChannelOffset(int busNumber);
+ void ShowGraph();
+};
+
+class CCoreAudioAEHALOSX : public ICoreAudioAEHAL
+{
+protected:
+ CCoreAudioGraph *m_audioGraph;
+ CCoreAudioDevice *m_AudioDevice;
+ CCoreAudioStream *m_OutputStream;
+ bool m_Initialized;
+ bool m_Passthrough;
+ AEAudioFormat m_initformat;
+ bool m_allowMixing;
+ bool m_encoded;
+ AEDataFormat m_rawDataFormat;
+public:
+ unsigned int m_NumLatencyFrames;
+ unsigned int m_OutputBufferIndex;
+ CCoreAudioAE *m_ae;
+
+ CCoreAudioAEHALOSX();
+ virtual ~CCoreAudioAEHALOSX();
+
+ virtual bool InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing, AudioDeviceID outputDevice);
+ virtual bool InitializePCMEncoded(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID outputDevice);
+ virtual bool InitializeEncoded(AudioDeviceID outputDevice, AEAudioFormat &format);
+ virtual bool Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device);
+ virtual void Deinitialize();
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+ virtual void SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual void Stop();
+ virtual bool Start();
+ virtual double GetDelay();
+ virtual void SetVolume(float volume);
+ virtual unsigned int GetBufferIndex();
+ virtual CAUOutputDevice *DestroyUnit(CAUOutputDevice *outputUnit);
+ virtual CAUOutputDevice *CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual bool AllowMixing() { return m_allowMixing; }
+};
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.cpp
new file mode 100644
index 0000000000..db130ba44b
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Interfaces/AESound.h"
+
+#include <samplerate.h>
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/EndianSwap.h"
+
+#include "AEFactory.h"
+#include "AEAudioFormat.h"
+#include "AEConvert.h"
+#include "AERemap.h"
+#include "AEUtil.h"
+
+#include "CoreAudioAE.h"
+#include "CoreAudioAESound.h"
+
+/* typecast AE to CCoreAudioAE */
+#define AE (*(CCoreAudioAE*)CAEFactory::AE)
+
+typedef struct
+{
+ char chunk_id[4];
+ uint32_t chunksize;
+} WAVE_CHUNK;
+
+CCoreAudioAESound::CCoreAudioAESound(const std::string &filename) :
+ IAESound (filename),
+ m_filename (filename),
+ m_volume (1.0f ),
+ m_inUse (0 )
+{
+}
+
+CCoreAudioAESound::~CCoreAudioAESound()
+{
+ DeInitialize();
+}
+
+
+std::string CCoreAudioAESound::GetFileName()
+{
+ return m_filename;
+}
+
+void CCoreAudioAESound::DeInitialize()
+{
+ m_wavLoader.DeInitialize();
+}
+
+bool CCoreAudioAESound::Initialize()
+{
+
+ DeInitialize();
+
+ if (!m_wavLoader.Initialize(m_filename, AE.GetSampleRate()))
+ return false;
+
+ return m_wavLoader.Remap(AE.GetChannelLayout());
+}
+
+void CCoreAudioAESound::SetVolume(float volume)
+{
+ m_volume = std::max(0.0f, std::min(1.0f, volume));
+}
+
+float CCoreAudioAESound::GetVolume()
+{
+ return m_volume;
+}
+
+unsigned int CCoreAudioAESound::GetSampleCount()
+{
+ CSingleLock cs(m_critSection);
+ if (m_wavLoader.IsValid())
+ return m_wavLoader.GetSampleCount();
+ return 0;
+}
+
+float* CCoreAudioAESound::GetSamples()
+{
+ CSingleLock cs(m_critSection);
+ if (!m_wavLoader.IsValid())
+ return NULL;
+
+ ++m_inUse;
+ return m_wavLoader.GetSamples();
+}
+
+void CCoreAudioAESound::ReleaseSamples()
+{
+ CSingleLock cs(m_critSection);
+ ASSERT(m_inUse > 0);
+ --m_inUse;
+}
+
+bool CCoreAudioAESound::IsPlaying()
+{
+ CSingleLock cs(m_critSection);
+ return (m_inUse > 0);
+}
+
+void CCoreAudioAESound::Play()
+{
+ AE.PlaySound(this);
+}
+
+void CCoreAudioAESound::Stop()
+{
+ AE.StopSound(this);
+}
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.h
new file mode 100644
index 0000000000..4fa721b644
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAESound.h
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils/StdString.h"
+#include "Interfaces/AESound.h"
+#include "Utils/AEWAVLoader.h"
+#include "threads/CriticalSection.h"
+#include "threads/SharedSection.h"
+
+class CWAVLoader;
+class CCoreAudioAESound : public IAESound
+{
+public:
+ CCoreAudioAESound (const std::string &filename);
+ virtual ~CCoreAudioAESound();
+
+ virtual std::string GetFileName();
+ virtual void DeInitialize();
+ virtual bool Initialize();
+
+ virtual void Play();
+ virtual void Stop();
+ virtual bool IsPlaying();
+
+ virtual void SetVolume(float volume);
+ virtual float GetVolume();
+
+ unsigned int GetSampleCount();
+
+ /* ReleaseSamples must be called for each time GetSamples has been called */
+ virtual float* GetSamples ();
+ void ReleaseSamples();
+private:
+ CCriticalSection m_critSection;
+ std::string m_filename;
+ CAEWAVLoader m_wavLoader;
+ float m_volume;
+ int m_inUse;
+};
+
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.cpp b/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.cpp
new file mode 100644
index 0000000000..bf4d4edc6f
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.cpp
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+
+#include "Interfaces/AE.h"
+#include "AEFactory.h"
+#include "AEUtil.h"
+
+#include "CoreAudioAE.h"
+#include "CoreAudioAEStream.h"
+
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#include "MathUtils.h"
+
+/* typecast AE to CCoreAudioAE */
+#define AE (*(CCoreAudioAE*)CAEFactory::AE)
+
+using namespace std;
+
+template <class AudioDataType>
+static inline void _Upmix(AudioDataType *input, unsigned int channelsInput, AudioDataType *output, unsigned int channelsOutput, unsigned int frames)
+{
+ unsigned int unused = channelsOutput - channelsInput;
+ unsigned int j, i;
+
+ AudioDataType *_output = output;
+ AudioDataType *_input = input;
+ for (i = 0; i < frames; i++)
+ {
+ /* get input channels */
+ for(j = 0; j < channelsInput; j++)
+ *_output++ = *_input++;
+ /* set unused channels */
+ for(j = 0; j < unused; j++)
+ *_output++ = 0;
+ }
+}
+
+void CCoreAudioAEStream::Upmix(void *input, unsigned int channelsInput, void *output, unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat)
+{
+ /* input channels must be less than output channels */
+ if (channelsInput >= channelsOutput)
+ return;
+
+ switch (CAEUtil::DataFormatToBits(dataFormat))
+ {
+ case 8: _Upmix ( (unsigned char *) input, channelsInput, (unsigned char *) output, channelsOutput, frames ); break;
+ case 16: _Upmix ( (short *) input, channelsInput, (short *) output, channelsOutput, frames ); break;
+ case 32: _Upmix ( (float *) input, channelsInput, (float *) output, channelsOutput, frames ); break;
+ default: _Upmix ( (int *) input, channelsInput, (int *) output, channelsOutput, frames ); break;
+ }
+}
+
+CCoreAudioAEStream::CCoreAudioAEStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options) :
+ m_convertBuffer (NULL ),
+ m_valid (false),
+ m_delete (false),
+ m_volume (1.0f ),
+ m_rgain (1.0f ),
+ m_slave (NULL ),
+ m_convertFn (NULL ),
+ m_ssrc (NULL ),
+ m_draining (false),
+ m_audioCallback (NULL ),
+ m_AvgBytesPerSec (0 ),
+ m_Buffer (NULL ),
+ m_fadeRunning (false),
+ m_outputUnit (NULL ),
+ m_frameSize (0 ),
+ m_doRemap (true ),
+ m_firstInput (true )
+{
+ m_ssrcData.data_out = NULL;
+
+ m_rawDataFormat = dataFormat;
+ m_StreamFormat.m_dataFormat = dataFormat;
+ m_StreamFormat.m_sampleRate = sampleRate;
+ m_StreamFormat.m_encodedRate = 0; //we don't support this
+ m_StreamFormat.m_channelLayout = channelLayout;
+ m_chLayoutCountStream = m_StreamFormat.m_channelLayout.Count();
+ m_StreamFormat.m_frameSize = (CAEUtil::DataFormatToBits(dataFormat) >> 3) * m_chLayoutCountStream;
+
+ m_OutputFormat = AE.GetAudioFormat();
+ m_chLayoutCountOutput = m_OutputFormat.m_channelLayout.Count();
+
+ //m_forceResample = (options & AESTREAM_FORCE_RESAMPLE) != 0;
+ m_paused = (options & AESTREAM_PAUSED) != 0;
+
+ m_vizRemapBufferSize = m_remapBufferSize = /*m_resampleBufferSize = */ m_upmixBufferSize = m_convertBufferSize = 16*1024;
+ m_convertBuffer = (float *)_aligned_malloc(m_convertBufferSize,16);
+ //m_resampleBuffer = (float *)_aligned_malloc(m_resampleBufferSize,16);
+ m_upmixBuffer = (uint8_t *)_aligned_malloc(m_upmixBufferSize,16);
+ m_remapBuffer = (uint8_t *)_aligned_malloc(m_remapBufferSize,16);
+ m_vizRemapBuffer = (uint8_t *)_aligned_malloc(m_vizRemapBufferSize,16);
+
+ m_isRaw = COREAUDIO_IS_RAW(dataFormat);
+}
+
+CCoreAudioAEStream::~CCoreAudioAEStream()
+{
+ CloseConverter();
+
+ m_delete = true;
+ m_valid = false;
+
+ InternalFlush();
+
+ _aligned_free(m_convertBuffer);
+ //_aligned_free(m_resampleBuffer);
+ _aligned_free(m_remapBuffer);
+ _aligned_free(m_vizRemapBuffer);
+
+ delete m_Buffer;
+
+ /*
+ if (m_resample)
+ {
+ _aligned_free(m_ssrcData.data_out);
+ src_delete(m_ssrc);
+ m_ssrc = NULL;
+ }
+ */
+
+ CLog::Log(LOGDEBUG, "CCoreAudioAEStream::~CCoreAudioAEStream - Destructed");
+}
+
+void CCoreAudioAEStream::InitializeRemap()
+{
+//#if defined(TARGET_DARWIN_OSX)
+ if (!m_isRaw)
+ {
+ if (m_OutputFormat.m_channelLayout != AE.GetChannelLayout())
+ {
+ m_OutputFormat = AE.GetAudioFormat();
+ m_chLayoutCountOutput = m_OutputFormat.m_channelLayout.Count();
+ m_OutputBytesPerSample = (CAEUtil::DataFormatToBits(m_OutputFormat.m_dataFormat) >> 3);
+
+ /* re-init the remappers */
+ m_remap .Initialize(m_StreamFormat.m_channelLayout, m_OutputFormat.m_channelLayout, false);
+ m_vizRemap.Initialize(m_StreamFormat.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
+
+ InternalFlush();
+ }
+ }
+//#endif
+
+}
+
+void CCoreAudioAEStream::ReinitConverter()
+{
+ CloseConverter();
+ OpenConverter();
+}
+
+// The source logic is in the HAL. The only thing we have to do here
+// is to allocate the convrter and set the direct input call.
+void CCoreAudioAEStream::CloseConverter()
+{
+ // we have a converter, delete it
+ if (m_outputUnit)
+ m_outputUnit = (CAUOutputDevice *) AE.GetHAL()->DestroyUnit(m_outputUnit);
+
+ // it is save to unregister any direct input. the HAL takes care about it.
+ AE.GetHAL()->SetDirectInput(NULL, m_OutputFormat);
+}
+
+void CCoreAudioAEStream::OpenConverter()
+{
+ // we allways allocate a converter
+ // the HAL decides if we get converter.
+ // if there is already a converter delete it.
+ if (m_outputUnit)
+ m_outputUnit = (CAUOutputDevice *) AE.GetHAL()->DestroyUnit(m_outputUnit);
+
+ AEAudioFormat format = m_OutputFormat;
+
+ format.m_sampleRate = m_StreamFormat.m_sampleRate;
+ m_outputUnit = (CAUOutputDevice *) AE.GetHAL()->CreateUnit(this, format);
+
+ // it is save to register any direct input. the HAL takes care about it.
+ AE.GetHAL()->SetDirectInput(this, m_OutputFormat);
+}
+
+void CCoreAudioAEStream::Initialize()
+{
+ if (m_valid)
+ {
+ InternalFlush();
+ }
+
+ m_OutputFormat = AE.GetAudioFormat();
+ m_chLayoutCountOutput = m_OutputFormat.m_channelLayout.Count();
+
+ if (m_rawDataFormat == AE_FMT_LPCM)
+ m_OutputBytesPerSample = (CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3);
+ else
+ m_OutputBytesPerSample = (CAEUtil::DataFormatToBits(m_OutputFormat.m_dataFormat) >> 3);
+
+ if (m_isRaw)
+ {
+ /* we are raw, which means we need to work in the output format */
+ if (m_rawDataFormat != AE_FMT_LPCM)
+ {
+ m_StreamFormat = AE.GetAudioFormat();
+ m_chLayoutCountStream = m_StreamFormat.m_channelLayout.Count();
+ }
+ m_StreamBytesPerSample = (CAEUtil::DataFormatToBits(m_StreamFormat.m_dataFormat) >> 3);
+ m_doRemap = false;
+ }
+ else
+ {
+ if (!m_chLayoutCountStream)
+ {
+ m_valid = false;
+ return;
+ }
+ /* Work around a bug in TrueHD and DTSHD deliver */
+ if (m_StreamFormat.m_dataFormat == AE_FMT_TRUEHD || m_StreamFormat.m_dataFormat == AE_FMT_DTSHD)
+ {
+ m_StreamBytesPerSample = (CAEUtil::DataFormatToBits(AE_FMT_S16NE) >> 3);
+ }
+ else
+ {
+ m_StreamBytesPerSample = (CAEUtil::DataFormatToBits(m_StreamFormat.m_dataFormat) >> 3);
+ }
+ m_StreamFormat.m_frameSize = m_StreamBytesPerSample * m_chLayoutCountStream;
+ }
+
+ if (!m_isRaw)
+ {
+ if (!m_remap.Initialize(m_StreamFormat.m_channelLayout, m_OutputFormat.m_channelLayout, false))
+ {
+ m_valid = false;
+ return;
+ }
+
+ m_doRemap = m_chLayoutCountStream != 2;
+ }
+
+ if (!m_isRaw)
+ {
+ if (!m_vizRemap.Initialize(m_OutputFormat.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true))
+ {
+ m_valid = false;
+ return;
+ }
+ }
+
+ m_convert = m_StreamFormat.m_dataFormat != AE_FMT_FLOAT && !m_isRaw;
+ //m_resample = false; //(m_StreamFormat.m_sampleRate != m_OutputFormat.m_sampleRate) && !m_isRaw;
+
+ /* if we need to convert, set it up */
+ if (m_convert)
+ {
+ /* get the conversion function and allocate a buffer for the data */
+ CLog::Log(LOGDEBUG, "CCoreAudioAEStream::CCoreAudioAEStream - Converting from %s to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(m_StreamFormat.m_dataFormat));
+ m_convertFn = CAEConvert::ToFloat(m_StreamFormat.m_dataFormat);
+
+ if (!m_convertFn)
+ m_valid = false;
+ }
+
+ /* if we need to resample, set it up */
+ /*
+ if (m_resample)
+ {
+ int err;
+ m_ssrc = src_new(SRC_SINC_MEDIUM_QUALITY, m_chLayoutCountStream, &err);
+ m_ssrcData.src_ratio = (double)m_OutputFormat.m_sampleRate / (double)m_StreamFormat.m_sampleRate;
+ m_ssrcData.data_in = m_convertBuffer;
+ m_ssrcData.end_of_input = 0;
+ }
+ */
+
+ // m_AvgBytesPerSec is calculated based on the output format.
+ // we have to keep in mind that we convert our data to the output format
+ m_AvgBytesPerSec = m_OutputFormat.m_frameSize * m_OutputFormat.m_sampleRate;
+
+ delete m_Buffer;
+
+ m_Buffer = new CoreAudioRingBuffer(m_AvgBytesPerSec);
+
+ m_fadeRunning = false;
+
+ OpenConverter();
+
+ m_valid = true;
+}
+
+void CCoreAudioAEStream::Destroy()
+{
+ m_valid = false;
+ m_delete = true;
+ InternalFlush();
+}
+
+unsigned int CCoreAudioAEStream::AddData(void *data, unsigned int size)
+{
+ unsigned int frames = size / m_StreamFormat.m_frameSize;
+ unsigned int samples = size / m_StreamBytesPerSample;
+ uint8_t *adddata = (uint8_t *)data;
+ unsigned int addsize = size;
+ if (!m_valid || size == 0 || data == NULL || m_draining || !m_Buffer)
+ {
+ return 0;
+ }
+
+ /* convert the data if we need to */
+ if (m_convert)
+ {
+ CheckOutputBufferSize((void **)&m_convertBuffer, &m_convertBufferSize, frames * m_chLayoutCountStream * m_OutputBytesPerSample);
+
+ samples = m_convertFn(adddata, size / m_StreamBytesPerSample, m_convertBuffer);
+ frames = samples / m_chLayoutCountStream;
+ addsize = frames * m_chLayoutCountStream * m_OutputBytesPerSample;
+ adddata = (uint8_t *)m_convertBuffer;
+ }
+ else
+ {
+ samples = size / m_StreamBytesPerSample;
+ adddata = (uint8_t *)data;
+ addsize = size;
+ }
+
+ if (samples == 0)
+ return 0;
+
+ /* resample it if we need to */
+ /*
+ if (m_resample)
+ {
+ unsigned int resample_frames = samples / m_chLayoutCountStream;
+
+ CheckOutputBufferSize((void **)&m_resampleBuffer, &m_resampleBufferSize,
+ resample_frames * std::ceil(m_ssrcData.src_ratio) * sizeof(float) * 2);
+
+ m_ssrcData.input_frames = resample_frames;
+ m_ssrcData.output_frames = resample_frames * std::ceil(m_ssrcData.src_ratio);
+ m_ssrcData.data_in = (float *)adddata;
+ m_ssrcData.data_out = m_resampleBuffer;
+
+ if (src_process(m_ssrc, &m_ssrcData) != 0)
+ return 0;
+
+ frames = m_ssrcData.output_frames_gen;
+ samples = frames * m_chLayoutCountStream;
+ adddata = (uint8_t *)m_ssrcData.data_out;
+ }
+ else
+ {
+ frames = samples / m_chLayoutCountStream;
+ samples = frames * m_chLayoutCountStream;
+ }
+ */
+
+ if (m_doRemap)
+ {
+ addsize = frames * m_OutputBytesPerSample * m_chLayoutCountOutput;
+
+ CheckOutputBufferSize((void **)&m_remapBuffer, &m_remapBufferSize, addsize);
+
+ // downmix/remap the data
+ m_remap.Remap((float *)adddata, (float *)m_remapBuffer, frames);
+ adddata = (uint8_t *)m_remapBuffer;
+ }
+
+ /* upmix the ouput to 8 channels */
+ if ( (!m_isRaw || m_rawDataFormat == AE_FMT_LPCM) && (m_chLayoutCountOutput > m_chLayoutCountStream) )
+ {
+ frames = addsize / m_StreamFormat.m_frameSize;
+
+ CheckOutputBufferSize((void **)&m_upmixBuffer, &m_upmixBufferSize, frames * m_chLayoutCountOutput * sizeof(float));
+ Upmix(adddata, m_chLayoutCountStream, m_upmixBuffer, m_chLayoutCountOutput, frames, m_OutputFormat.m_dataFormat);
+ adddata = m_upmixBuffer;
+ addsize = frames * m_chLayoutCountOutput * sizeof(float);
+ }
+
+ unsigned int room = m_Buffer->GetWriteSize();
+
+ while (addsize > room && !m_paused)
+ {
+ // we got deleted
+ if (!m_valid || size == 0 || data == NULL || m_draining || !m_Buffer)
+ {
+ return 0;
+ }
+ // sleep buffer half empty
+ Sleep(100);
+ room = m_Buffer->GetWriteSize();
+ }
+
+ if (addsize > room)
+ {
+ //CLog::Log(LOGDEBUG, "CCoreAudioAEStream::AddData failed : free size %d add size %d", room, addsize);
+ size = 0;
+ }
+ else
+ {
+ m_Buffer->Write(adddata, addsize);
+ }
+
+ //printf("AddData : %d %d\n", addsize, m_Buffer->GetWriteSize());
+
+ return size;
+}
+
+unsigned int CCoreAudioAEStream::GetFrames(uint8_t *buffer, unsigned int size)
+{
+ /* if we have been deleted */
+ if (!m_valid || m_delete || !m_Buffer || m_paused)
+ {
+ return 0;
+ }
+
+ unsigned int readsize = std::min(m_Buffer->GetReadSize(), size);
+ m_Buffer->Read(buffer, readsize);
+
+ if (!m_isRaw)
+ {
+ float *floatBuffer = (float *)buffer;
+ unsigned int samples = readsize / m_OutputBytesPerSample;
+
+ /* we have a frame, if we have a viz we need to hand the data to it.
+ Keep in mind that our buffer is already in output format.
+ So we remap output format to viz format !!!*/
+ if (m_OutputFormat.m_dataFormat == AE_FMT_FLOAT)
+ {
+ // TODO : Why the hell is vizdata limited ?
+ unsigned int frames = samples / m_chLayoutCountOutput;
+ unsigned int samplesClamped = (samples > 512) ? 512 : samples;
+ if(samplesClamped) {
+ // Viz channel count is 2
+ CheckOutputBufferSize((void **)&m_vizRemapBuffer, &m_vizRemapBufferSize, frames * 2 * sizeof(float));
+
+ m_vizRemap.Remap(floatBuffer, (float*)m_vizRemapBuffer, frames);
+ if (m_audioCallback)
+ {
+ m_audioCallback->OnAudioData((float *)m_vizRemapBuffer, samplesClamped);
+ }
+ }
+ }
+
+ /* if we are fading */
+ if (m_fadeRunning)
+ {
+ // TODO: check if we correctly respect the amount of our blockoperation
+ m_volume += (m_fadeStep * ((float)readsize / (float)m_OutputFormat.m_frameSize));
+ m_volume = std::min(1.0f, std::max(0.0f, m_volume));
+ if (m_fadeDirUp)
+ {
+ if (m_volume >= m_fadeTarget)
+ m_fadeRunning = false;
+ }
+ else
+ {
+ if (m_volume <= m_fadeTarget)
+ m_fadeRunning = false;
+ }
+
+#ifdef __SSE__
+ CAEUtil::SSEMulArray(floatBuffer, m_volume, samples);
+#else
+ for(unsigned int i=0; i < samples; i++)
+ floatBuffer[i] *= m_volume;
+#endif
+ CAEUtil::ClampArray(floatBuffer, samples);
+ }
+ }
+
+ return readsize;
+}
+
+const unsigned int CCoreAudioAEStream::GetFrameSize() const
+{
+ return m_OutputFormat.m_frameSize;
+}
+
+unsigned int CCoreAudioAEStream::GetSpace()
+{
+ if (!m_valid || m_draining)
+ return 0;
+
+ return m_Buffer->GetWriteSize();
+}
+
+double CCoreAudioAEStream::GetDelay()
+{
+ if (m_delete || !m_Buffer)
+ return 0.0f;
+
+ double delay = (double)(m_Buffer->GetReadSize()) / (double)m_AvgBytesPerSec;
+
+ delay += AE.GetDelay();
+
+ return delay;
+}
+
+bool CCoreAudioAEStream::IsBuffering()
+{
+ return m_Buffer->GetReadSize() == 0;
+}
+
+double CCoreAudioAEStream::GetCacheTime()
+{
+ if (m_delete || !m_Buffer)
+ return 0.0f;
+
+ return (double)(m_Buffer->GetReadSize()) / (double)m_AvgBytesPerSec;
+}
+
+double CCoreAudioAEStream::GetCacheTotal()
+{
+ if (m_delete || !m_Buffer)
+ return 0.0f;
+
+ return (double)m_Buffer->GetMaxSize() / (double)m_AvgBytesPerSec;
+}
+
+
+bool CCoreAudioAEStream::IsPaused()
+{
+ return m_paused;
+}
+
+bool CCoreAudioAEStream::IsDraining()
+{
+ return m_draining;
+}
+
+bool CCoreAudioAEStream::IsDestroyed()
+{
+ return m_delete;
+}
+
+bool CCoreAudioAEStream::IsValid()
+{
+ return m_valid;
+}
+
+void CCoreAudioAEStream::Pause()
+{
+ m_paused = true;
+}
+
+void CCoreAudioAEStream::Resume()
+{
+ m_paused = false;
+}
+
+void CCoreAudioAEStream::Drain()
+{
+ m_draining = true;
+}
+
+
+bool CCoreAudioAEStream::IsDrained()
+{
+ return m_Buffer->GetReadSize() <= 0;
+}
+
+void CCoreAudioAEStream::Flush()
+{
+ InternalFlush();
+}
+
+float CCoreAudioAEStream::GetVolume()
+{
+ return m_volume;
+}
+
+float CCoreAudioAEStream::GetReplayGain()
+{
+ return m_rgain;
+}
+
+void CCoreAudioAEStream::SetVolume(float volume)
+{
+ m_volume = std::max( 0.0f, std::min(1.0f, volume));
+}
+
+void CCoreAudioAEStream::SetReplayGain(float factor)
+{
+ m_rgain = std::max(-1.0f, std::max(1.0f, factor));
+}
+
+void CCoreAudioAEStream::InternalFlush()
+{
+ /* reset the resampler */
+ /*
+ if (m_resample) {
+ m_ssrcData.end_of_input = 0;
+ src_reset(m_ssrc);
+ }
+ */
+
+ // Read the buffer empty to avoid Reset
+ // Reset is not lock free.
+ if (m_Buffer)
+ {
+ unsigned int readsize = m_Buffer->GetReadSize();
+
+ if (readsize)
+ {
+ uint8_t *buffer = (uint8_t *)_aligned_malloc(readsize, 16);
+ m_Buffer->Read(buffer, readsize);
+ _aligned_free(buffer);
+ }
+
+ /* if we are draining and are out of packets, tell the slave to resume */
+ if (m_draining && m_slave)
+ {
+ m_slave->Resume();
+ m_slave = NULL;
+ }
+ }
+
+ //if(m_Buffer)
+ // m_Buffer->Reset();
+}
+
+const unsigned int CCoreAudioAEStream::GetChannelCount() const
+{
+ return m_chLayoutCountStream;
+}
+
+const unsigned int CCoreAudioAEStream::GetSampleRate() const
+{
+ return m_StreamFormat.m_sampleRate;
+}
+
+const unsigned int CCoreAudioAEStream::GetEncodedSampleRate() const
+{
+ return m_StreamFormat.m_encodedRate;
+}
+
+const enum AEDataFormat CCoreAudioAEStream::GetDataFormat() const
+{
+ return m_StreamFormat.m_dataFormat;
+}
+
+const bool CCoreAudioAEStream::IsRaw() const
+{
+ return m_isRaw;
+}
+
+double CCoreAudioAEStream::GetResampleRatio()
+{
+ /*
+ if (!m_resample)
+ return 1.0f;
+
+ double ret = m_ssrcData.src_ratio;
+ return ret;
+ */
+
+ return 1.0f;
+}
+
+bool CCoreAudioAEStream::SetResampleRatio(double ratio)
+{
+ return false;
+ /*
+ if (!m_resample)
+ return;
+
+ src_set_ratio(m_ssrc, ratio);
+ m_ssrcData.src_ratio = ratio;
+ */
+}
+
+void CCoreAudioAEStream::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+ m_audioCallback = pCallback;
+ if (m_audioCallback)
+ m_audioCallback->OnInitialize(2, m_StreamFormat.m_sampleRate, 32);
+}
+
+void CCoreAudioAEStream::UnRegisterAudioCallback()
+{
+ m_audioCallback = NULL;
+}
+
+void CCoreAudioAEStream::FadeVolume(float from, float target, unsigned int time)
+{
+ if (m_isRaw)
+ {
+ m_fadeRunning = false;
+ }
+ else
+ {
+ float delta = target - from;
+ m_fadeDirUp = target > from;
+ m_fadeTarget = target;
+ m_fadeStep = delta / (((float)m_OutputFormat.m_sampleRate / 1000.0f) * (float)time);
+ m_fadeRunning = true;
+ }
+
+}
+
+bool CCoreAudioAEStream::IsFading()
+{
+ return m_fadeRunning;
+}
+
+void CCoreAudioAEStream::RegisterSlave(IAEStream *stream)
+{
+ m_slave = stream;
+}
+
+OSStatus CCoreAudioAEStream::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+ // the index is important if we run encoded
+ unsigned outputBufferIndex = AE.GetHAL()->GetBufferIndex();
+
+ // if we have no valid data output silence
+ if (!m_valid || m_delete || !m_Buffer || m_firstInput || m_paused)
+ {
+ for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
+ bzero(ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize);
+ if (ioActionFlags)
+ *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
+ m_firstInput = false;
+ return noErr;
+ }
+
+ unsigned int size = inNumberFrames * m_OutputFormat.m_frameSize;
+ //unsigned int size = inNumberFrames * m_StreamFormat.m_frameSize;
+
+ ioData->mBuffers[outputBufferIndex].mDataByteSize = GetFrames((unsigned char *)ioData->mBuffers[outputBufferIndex].mData, size);
+ if (!ioData->mBuffers[outputBufferIndex].mDataByteSize && ioActionFlags)
+ *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
+
+ return noErr;
+}
+
+OSStatus CCoreAudioAEStream::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
+{
+
+ OSStatus ret = noErr;
+
+ ret = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
+
+ return ret;
+}
diff --git a/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.h b/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.h
new file mode 100644
index 0000000000..6045b015ff
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioAEStream.h
@@ -0,0 +1,170 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <samplerate.h>
+#include <list>
+
+#include "Interfaces/AEStream.h"
+#include "AEAudioFormat.h"
+#include "AEConvert.h"
+#include "AERemap.h"
+#include "CoreAudioRingBuffer.h"
+#include "threads/CriticalSection.h"
+#include <AudioUnit/AudioUnit.h>
+#include "ICoreAudioSource.h"
+
+#if defined(TARGET_DARWIN_IOS)
+# include "CoreAudioAEHALIOS.h"
+#else
+# include "CoreAudioAEHALOSX.h"
+#endif
+
+class CCoreAudioAEStream : public IAEStream, public ICoreAudioSource
+{
+protected:
+ friend class CCoreAudioAE;
+ CCoreAudioAEStream(enum AEDataFormat format, unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options);
+ virtual ~CCoreAudioAEStream();
+
+ CAUOutputDevice *m_outputUnit;
+
+public:
+ void ReinitConverter();
+ void CloseConverter();
+ void OpenConverter();
+
+ void Initialize();
+ void InitializeRemap();
+ virtual void Destroy();
+
+ virtual const unsigned int GetFrameSize() const;
+ virtual unsigned int GetSpace();
+ virtual unsigned int AddData(void *data, unsigned int size);
+ unsigned int GetFrames(uint8_t *buffer, unsigned int size);
+ virtual double GetDelay();
+ virtual bool IsBuffering();
+ virtual double GetCacheTime();
+ virtual double GetCacheTotal();
+
+ bool IsPaused();
+ virtual bool IsDraining();
+ virtual bool IsDrained();
+ bool IsDestroyed();
+ bool IsValid();
+
+ virtual void Pause();
+ virtual void Resume();
+ virtual void Drain();
+ virtual void Flush();
+
+ virtual float GetVolume();
+ virtual float GetReplayGain();
+ virtual void SetVolume(float volume);
+ virtual void SetReplayGain(float factor);
+
+ virtual const unsigned int GetChannelCount() const;
+ virtual const unsigned int GetSampleRate() const;
+ virtual const unsigned int GetEncodedSampleRate() const;
+ virtual const enum AEDataFormat GetDataFormat() const;
+ virtual const bool IsRaw() const;
+
+ /* for dynamic sample rate changes (smoothvideo) */
+ virtual double GetResampleRatio();
+ virtual bool SetResampleRatio(double ratio);
+
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnRegisterAudioCallback();
+
+ virtual void FadeVolume(float from, float to, unsigned int time);
+ virtual bool IsFading();
+ virtual void RegisterSlave(IAEStream *stream);
+
+ OSStatus Render(AudioUnitRenderActionFlags* actionFlags,
+ const AudioTimeStamp* pTimeStamp,
+ UInt32 busNumber,
+ UInt32 frameCount,
+ AudioBufferList* pBufList);
+ OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+
+private:
+ void InternalFlush();
+
+ AEDataFormat m_rawDataFormat;
+
+ AEAudioFormat m_OutputFormat;
+ unsigned int m_chLayoutCountOutput;
+ AEAudioFormat m_StreamFormat;
+ unsigned int m_chLayoutCountStream;
+ unsigned int m_StreamBytesPerSample;
+ unsigned int m_OutputBytesPerSample;
+
+ //bool m_forceResample; /* true if we are to force resample even when the rates match */
+ //bool m_resample; /* true if the audio needs to be resampled */
+ bool m_convert; /* true if the bitspersample needs converting */
+ bool m_valid; /* true if the stream is valid */
+ bool m_delete; /* true if CCoreAudioAE is to free this object */
+ CAERemap m_remap; /* the remapper */
+ float m_volume; /* the volume level */
+ float m_rgain; /* replay gain level */
+ IAEStream *m_slave; /* slave aestream */
+
+ CAEConvert::AEConvertToFn m_convertFn;
+
+ CoreAudioRingBuffer *m_Buffer;
+ float *m_convertBuffer; /* buffer for converted data */
+ int m_convertBufferSize;
+ //float *m_resampleBuffer; /* buffer for resample data */
+ //int m_resampleBufferSize;
+ uint8_t *m_upmixBuffer; /* buffer for remap data */
+ int m_upmixBufferSize;
+ uint8_t *m_remapBuffer; /* buffer for remap data */
+ int m_remapBufferSize;
+ uint8_t *m_vizRemapBuffer; /* buffer for remap data */
+ int m_vizRemapBufferSize;
+
+ SRC_STATE *m_ssrc;
+ SRC_DATA m_ssrcData;
+ bool m_paused;
+ bool m_draining;
+ unsigned int m_AvgBytesPerSec;
+
+ /* vizualization internals */
+ CAERemap m_vizRemap;
+ IAudioCallback *m_audioCallback;
+
+ /* fade values */
+ bool m_fadeRunning;
+ bool m_fadeDirUp;
+ float m_fadeStep;
+ float m_fadeTarget;
+ unsigned int m_fadeTime;
+ bool m_isRaw;
+ unsigned int m_frameSize;
+ bool m_doRemap;
+ void Upmix(void *input, unsigned int channelsInput, void *output, unsigned int channelsOutput, unsigned int frames, AEDataFormat dataFormat);
+ bool m_firstInput;
+};
+
diff --git a/xbmc/cores/AudioRenderers/IOSAudioRingBuffer.h b/xbmc/cores/AudioEngine/Engines/CoreAudioRingBuffer.h
index 7e3160cb42..8b4b427765 100644
--- a/xbmc/cores/AudioRenderers/IOSAudioRingBuffer.h
+++ b/xbmc/cores/AudioEngine/Engines/CoreAudioRingBuffer.h
@@ -1,5 +1,6 @@
+#pragma once
/*
- * Copyright (C) 2010 Team XBMC
+ * Copyright (C) 2010-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
@@ -19,9 +20,6 @@
*
*/
-#ifndef IOSAUDIORINGBUFFER_H_
-#define IOSAUDIORINGBUFFER_H_
-
#define RING_BUFFER_OK 0;
#define RING_BUFFER_EMPTY 1;
#define RING_BUFFER_FULL 2;
@@ -38,10 +36,10 @@
* If you intend to call the Reset() method, please use Locks.
* All other operations are thread-safe.
*/
-class IOSAudioRingBuffer {
+class CoreAudioRingBuffer {
public:
- IOSAudioRingBuffer() :
+ CoreAudioRingBuffer() :
m_iReadPos(0),
m_iWritePos(0),
m_iRead(0),
@@ -51,7 +49,7 @@ public:
{
}
- IOSAudioRingBuffer(unsigned int size) :
+ CoreAudioRingBuffer(unsigned int size) :
m_iReadPos(0),
m_iWritePos(0),
m_iRead(0),
@@ -62,12 +60,12 @@ public:
Create(size);
}
- ~IOSAudioRingBuffer()
+ ~CoreAudioRingBuffer()
{
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer::~IOSAudioRingBuffer: Deleting buffer.");
+ CLog::Log(LOGDEBUG, "CoreAudioRingBuffer::~CoereAudioRingBuffer: Deleting buffer.");
#endif
- delete[] m_Buffer;
+ _aligned_free(m_Buffer);
}
/**
@@ -77,7 +75,7 @@ public:
*/
bool Create(int size)
{
- m_Buffer = new unsigned char[ size ];
+ m_Buffer = (unsigned char*)_aligned_malloc(size,16);
if ( m_Buffer )
{
m_iSize = size;
@@ -94,7 +92,7 @@ public:
*/
void Reset() {
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer::Reset: Buffer reset.");
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer::Reset: Buffer reset.");
#endif
m_iWritten = 0;
m_iRead = 0;
@@ -108,14 +106,14 @@ public:
*
* @return RING_BUFFER_OK on success, otherwise an error code
*/
- int Write(unsigned char *src, unsigned int size)
+ int Write(unsigned char *src, unsigned int size)
{
unsigned int space = GetWriteSize();
//do we have enough space for all the data?
if (size > space) {
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Not enough space, ignoring data. Requested: %u Available: %u",size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Not enough space, ignoring data. Requested: %u Available: %u",size, space);
#endif
return RING_BUFFER_FULL;
}
@@ -124,7 +122,7 @@ public:
if ( m_iSize > size + m_iWritePos )
{
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Written to: %u size: %u space before: %u\n", m_iWritePos, size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Written to: %u size: %u space before: %u\n", m_iWritePos, size, space);
#endif
memcpy(&(m_Buffer[m_iWritePos]), src, size);
m_iWritePos+=size;
@@ -135,7 +133,7 @@ public:
unsigned int first = m_iSize - m_iWritePos;
unsigned int second = size - first;
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Written to (split) first: %u second: %u size: %u space before: %u\n", first, second, size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Written to (split) first: %u second: %u size: %u space before: %u\n", first, second, size, space);
#endif
memcpy(&(m_Buffer[m_iWritePos]), src, first);
memcpy(&(m_Buffer[0]), &src[first], second);
@@ -154,7 +152,7 @@ public:
*
* @return RING_BUFFER_OK on success, otherwise an error code
*/
- int Read(unsigned char *dest, unsigned int size)
+ int Read(unsigned char *dest, unsigned int size)
{
unsigned int space = GetReadSize();
@@ -162,7 +160,7 @@ public:
if( space <= 0 )
{
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Can't read from empty buffer.");
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Can't read from empty buffer.");
#endif
return RING_BUFFER_EMPTY;
}
@@ -171,7 +169,7 @@ public:
if( size > space )
{
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Can't read %u bytes when we only have %u.", size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Can't read %u bytes when we only have %u.", size, space);
#endif
return RING_BUFFER_NOTAVAILABLE;
}
@@ -180,7 +178,7 @@ public:
if ( size + m_iReadPos < m_iSize )
{
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Reading from: %u size: %u space before: %u\n", m_iWritePos, size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Reading from: %u size: %u space before: %u\n", m_iWritePos, size, space);
#endif
memcpy(dest, &(m_Buffer[m_iReadPos]), size);
m_iReadPos+=size;
@@ -191,7 +189,7 @@ public:
unsigned int first = m_iSize - m_iReadPos;
unsigned int second = size - first;
#ifdef RING_BUFFER_DEBUG
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer: Reading from (split) first: %u second: %u size: %u space before: %u\n", first, second, size, space);
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer: Reading from (split) first: %u second: %u size: %u space before: %u\n", first, second, size, space);
#endif
memcpy(dest, &(m_Buffer[m_iReadPos]), first);
memcpy(&dest[first], &(m_Buffer[0]), second);
@@ -206,9 +204,9 @@ public:
/**
* Dumps the buffer.
*/
- void Dump()
+ void Dump()
{
- unsigned char* bufferContents = new unsigned char[m_iSize + 1];
+ unsigned char* bufferContents = (unsigned char *)_aligned_malloc(m_iSize + 1,16);
for (unsigned int i=0; i<m_iSize; i++) {
if (i >= m_iReadPos && i<m_iWritePos)
bufferContents[i] = m_Buffer[i];
@@ -216,15 +214,15 @@ public:
bufferContents[i] = '_';
}
bufferContents[m_iSize] = '\0';
- CLog::Log(LOGDEBUG, "IOSAudioRingBuffer::Dump()\n%s",bufferContents);
- delete[] bufferContents;
+ CLog::Log(LOGDEBUG, "CoereAudioRingBuffer::Dump()\n%s",bufferContents);
+ _aligned_free(bufferContents);
}
/**
* Returns available space for writing to buffer.
* Attempt to write more bytes than available results in RING_BUFFER_FULL.
*/
- unsigned int GetWriteSize()
+ unsigned int GetWriteSize()
{
return m_iSize - ( m_iWritten - m_iRead );
}
@@ -233,7 +231,7 @@ public:
* Returns available space for reading from buffer.
* Attempt to read more bytes than available results in RING_BUFFER_EMPTY.
*/
- unsigned int GetReadSize()
+ unsigned int GetReadSize()
{
return m_iWritten - m_iRead;
}
@@ -241,17 +239,16 @@ public:
/**
* Returns the buffer size.
*/
- unsigned int GetMaxSize()
+ unsigned int GetMaxSize()
{
return m_iSize;
}
private:
- unsigned int m_iReadPos;
- unsigned int m_iWritePos;
- unsigned int m_iRead;
- unsigned int m_iWritten;
- unsigned int m_iSize;
- unsigned char *m_Buffer;
+ unsigned int m_iReadPos;
+ unsigned int m_iWritePos;
+ unsigned int m_iRead;
+ unsigned int m_iWritten;
+ unsigned int m_iSize;
+ unsigned char *m_Buffer;
};
-#endif //#define IOSAUDIORINGBUFFER_H_
diff --git a/xbmc/cores/AudioEngine/Engines/ICoreAudioAEHAL.h b/xbmc/cores/AudioEngine/Engines/ICoreAudioAEHAL.h
new file mode 100644
index 0000000000..6b893439d5
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ICoreAudioAEHAL.h
@@ -0,0 +1,51 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEAudioFormat.h"
+#include "Interfaces/AE.h"
+#include <AudioUnit/AudioUnit.h>
+#include "ICoreAudioSource.h"
+
+class ICoreAudioAEHAL;
+class CAUOutputDevice;
+
+/**
+ * ICoreAudioAEHAL Interface
+ */
+class ICoreAudioAEHAL
+{
+protected:
+ ICoreAudioAEHAL() {}
+ virtual ~ICoreAudioAEHAL() {}
+
+public:
+ virtual bool Initialize(ICoreAudioSource *ae, bool passThrough, AEAudioFormat &format, AEDataFormat rawDataFormat, std::string &device) = 0;
+ virtual void Deinitialize() = 0;
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough) = 0;
+ //virtual CAUOutputDevice *DestroyUnit(CAUOutputDevice *outputUnit);
+ //virtual CAUOutputDevice *CreateUnit(ICoreAudioSource *pSource, AEAudioFormat &format);
+ //virtual void SetDirectInput(ICoreAudioSource *pSource, AEAudioFormat &format);
+ virtual void Stop() = 0;
+ virtual bool Start() = 0;
+ virtual double GetDelay() = 0;
+ virtual void SetVolume(float volume) = 0;
+};
diff --git a/xbmc/cores/AudioEngine/Engines/ICoreAudioSource.h b/xbmc/cores/AudioEngine/Engines/ICoreAudioSource.h
new file mode 100644
index 0000000000..ce90ec04f2
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/ICoreAudioSource.h
@@ -0,0 +1,50 @@
+#pragma once
+/*
+ * Copyright (C) 2011-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEAudioFormat.h"
+#include "Interfaces/AE.h"
+#include "utils/StdString.h"
+#include <AudioUnit/AudioUnit.h>
+
+class ICoreAudioSource;
+
+/**
+ * ICoreAudioSource Interface
+ */
+class ICoreAudioSource
+{
+private:
+ std::string m_inputName;
+ AudioUnitElement m_inputBus;
+public:
+ // Function to request rendered data from a data source
+ virtual OSStatus Render(AudioUnitRenderActionFlags* actionFlags,
+ const AudioTimeStamp* pTimeStamp,
+ UInt32 busNumber,
+ UInt32 frameCount,
+ AudioBufferList* pBufList) = 0;
+ //std::string InputName() { return m_inputName; };
+ //void InputName(std::string inputName) { m_inputName = inputName; };
+
+ //AudioUnitElement InputBus() { return m_inputBus; };
+ //void InputBus(AudioUnitElement inputBus) { m_inputBus = m_inputBus; };
+};
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAE.cpp b/xbmc/cores/AudioEngine/Engines/PulseAE.cpp
new file mode 100644
index 0000000000..e838d70583
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAE.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_PULSEAUDIO
+
+#include "PulseAE.h"
+#include "PulseAEStream.h"
+#include "PulseAESound.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "settings/Settings.h"
+#include <pulse/pulseaudio.h>
+
+/* Static helpers */
+static const char *ContextStateToString(pa_context_state s)
+{
+ switch (s)
+ {
+ case PA_CONTEXT_UNCONNECTED:
+ return "unconnected";
+ case PA_CONTEXT_CONNECTING:
+ return "connecting";
+ case PA_CONTEXT_AUTHORIZING:
+ return "authorizing";
+ case PA_CONTEXT_SETTING_NAME:
+ return "setting name";
+ case PA_CONTEXT_READY:
+ return "ready";
+ case PA_CONTEXT_FAILED:
+ return "failed";
+ case PA_CONTEXT_TERMINATED:
+ return "terminated";
+ default:
+ return "none";
+ }
+}
+
+#if 0
+static const char *StreamStateToString(pa_stream_state s)
+{
+ switch(s)
+ {
+ case PA_STREAM_UNCONNECTED:
+ return "unconnected";
+ case PA_STREAM_CREATING:
+ return "creating";
+ case PA_STREAM_READY:
+ return "ready";
+ case PA_STREAM_FAILED:
+ return "failed";
+ case PA_STREAM_TERMINATED:
+ return "terminated";
+ default:
+ return "none";
+ }
+}
+#endif
+
+CPulseAE::CPulseAE()
+{
+ m_Context = NULL;
+ m_MainLoop = NULL;
+}
+
+CPulseAE::~CPulseAE()
+{
+ if (m_MainLoop)
+ pa_threaded_mainloop_stop(m_MainLoop);
+
+ if (m_Context)
+ {
+ pa_context_disconnect(m_Context);
+ pa_context_unref(m_Context);
+ m_Context = NULL;
+ }
+}
+
+bool CPulseAE::Initialize()
+{
+ m_Volume = g_settings.m_fVolumeLevel;
+
+ if ((m_MainLoop = pa_threaded_mainloop_new()) == NULL)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Failed to allocate main loop");
+ return false;
+ }
+
+ if ((m_Context = pa_context_new(pa_threaded_mainloop_get_api(m_MainLoop), "XBMC")) == NULL)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Failed to allocate context");
+ return false;
+ }
+
+ pa_context_set_state_callback(m_Context, ContextStateCallback, m_MainLoop);
+
+ if (pa_context_connect(m_Context, NULL, (pa_context_flags_t)0, NULL) < 0)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Failed to connect context");
+ return false;
+ }
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+ if (pa_threaded_mainloop_start(m_MainLoop) < 0)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Failed to start MainLoop");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return false;
+ }
+
+ /* Wait until the context is ready */
+ do
+ {
+ pa_threaded_mainloop_wait(m_MainLoop);
+ CLog::Log(LOGDEBUG, "PulseAudio: Context %s", ContextStateToString(pa_context_get_state(m_Context)));
+ }
+ while (pa_context_get_state(m_Context) != PA_CONTEXT_READY && pa_context_get_state(m_Context) != PA_CONTEXT_FAILED);
+
+ if (pa_context_get_state(m_Context) == PA_CONTEXT_FAILED)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Waited for the Context but it failed");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return false;
+ }
+
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return true;
+}
+
+void CPulseAE::OnSettingsChange(std::string setting)
+{
+}
+
+float CPulseAE::GetVolume()
+{
+ return m_Volume;
+}
+
+void CPulseAE::SetVolume(float volume)
+{
+ CSingleLock lock(m_lock);
+ m_Volume = volume;
+ std::list<CPulseAEStream*>::iterator itt;
+ for (itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ (*itt)->UpdateVolume(volume);
+}
+
+IAEStream *CPulseAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options)
+{
+ CPulseAEStream *st = new CPulseAEStream(m_Context, m_MainLoop, dataFormat, sampleRate, channelLayout, options);
+
+ CSingleLock lock(m_lock);
+ m_streams.push_back(st);
+ return st;
+}
+
+void CPulseAE::RemoveStream(IAEStream *stream)
+{
+ std::list<CPulseAEStream*>::iterator itt;
+
+ m_streams.remove((CPulseAEStream *)stream);
+
+ for (itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ if (*itt == stream)
+ {
+ m_streams.erase(itt);
+ return;
+ }
+ }
+}
+
+IAEStream *CPulseAE::FreeStream(IAEStream *stream)
+{
+ RemoveStream(stream);
+
+ CPulseAEStream *istream = (CPulseAEStream *)stream;
+
+ delete istream;
+
+ return NULL;
+}
+
+IAESound *CPulseAE::MakeSound(const std::string& file)
+{
+ CSingleLock lock(m_lock);
+
+ CPulseAESound *sound = new CPulseAESound(file, m_Context, m_MainLoop);
+ if (!sound->Initialize())
+ {
+ delete sound;
+ return NULL;
+ }
+
+ m_sounds.push_back(sound);
+ return sound;
+}
+
+void CPulseAE::FreeSound(IAESound *sound)
+{
+ if (!sound)
+ return;
+
+ sound->Stop();
+ CSingleLock lock(m_lock);
+ for (std::list<CPulseAESound*>::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ if (*itt == sound)
+ {
+ m_sounds.erase(itt);
+ break;
+ }
+
+ delete (CPulseAESound*)sound;
+}
+
+void CPulseAE::GarbageCollect()
+{
+ CSingleLock lock(m_lock);
+ std::list<CPulseAEStream*>::iterator itt;
+ for (itt = m_streams.begin(); itt != m_streams.end();)
+ {
+ if ((*itt)->IsDestroyed())
+ {
+ delete (*itt);
+ itt = m_streams.erase(itt);
+ continue;
+ }
+ ++itt;
+ }
+}
+
+void CPulseAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+}
+
+void CPulseAE::ContextStateCallback(pa_context *c, void *userdata)
+{
+ pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
+ switch (pa_context_get_state(c))
+ {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(m, 0);
+ break;
+ }
+}
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAE.h b/xbmc/cores/AudioEngine/Engines/PulseAE.h
new file mode 100644
index 0000000000..36a87c7ad4
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAE.h
@@ -0,0 +1,77 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_PULSEAUDIO
+
+#include "Interfaces/AE.h"
+#include "PulseAEStream.h"
+#include "PulseAESound.h"
+#include "threads/CriticalSection.h"
+#include <list>
+
+struct pa_context;
+struct pa_threaded_mainloop;
+struct pa_stream;
+
+class CPulseAEStream;
+class CPulseAESound;
+class CPulseAE : public IAE
+{
+protected:
+ friend class CAEFactory;
+ CPulseAE();
+ virtual ~CPulseAE();
+
+public:
+ virtual bool Initialize ();
+ virtual void OnSettingsChange(std::string setting);
+
+ virtual float GetVolume();
+ virtual void SetVolume(float volume);
+
+ /* returns a new stream for data in the specified format */
+ virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options = 0);
+ virtual IAEStream *FreeStream(IAEStream *stream);
+ void RemoveStream(IAEStream *stream);
+
+ /* returns a new sound object */
+ virtual IAESound *MakeSound(const std::string& file);
+ virtual void FreeSound(IAESound *sound);
+
+ /* free's sounds that have expired */
+ virtual void GarbageCollect();
+
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+private:
+ CCriticalSection m_lock;
+ std::list<CPulseAEStream*> m_streams;
+ std::list<CPulseAESound* > m_sounds;
+
+ static void ContextStateCallback(pa_context *c, void *userdata);
+
+ pa_context *m_Context;
+ pa_threaded_mainloop *m_MainLoop;
+ float m_Volume;
+};
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAESound.cpp b/xbmc/cores/AudioEngine/Engines/PulseAESound.cpp
new file mode 100644
index 0000000000..191c077ea3
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAESound.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_PULSEAUDIO
+
+#include "PulseAESound.h"
+#include "AEFactory.h"
+#include "utils/log.h"
+#include "MathUtils.h"
+#include "StringUtils.h"
+
+CPulseAESound::CPulseAESound(const std::string &filename, pa_context *context, pa_threaded_mainloop *mainLoop) :
+ IAESound (filename),
+ m_filename (filename),
+ m_dataSent (0 ),
+ m_context (context ),
+ m_mainLoop (mainLoop),
+ m_stream (NULL ),
+ m_op (NULL )
+{
+ m_pulseName = StringUtils::CreateUUID();
+}
+
+CPulseAESound::~CPulseAESound()
+{
+ DeInitialize();
+}
+
+bool CPulseAESound::Initialize()
+{
+ /* we dont re-init the wav loader in PA as PA handles the samplerate */
+ if (!m_wavLoader.IsValid() && !m_wavLoader.Initialize(m_filename, 0))
+ return false;
+
+ m_sampleSpec.format = PA_SAMPLE_FLOAT32NE;
+ m_sampleSpec.rate = m_wavLoader.GetSampleRate();
+ m_sampleSpec.channels = m_wavLoader.GetChannelCount();
+
+ if (!pa_sample_spec_valid(&m_sampleSpec))
+ {
+ CLog::Log(LOGERROR, "CPulseAESound::Initialize - Invalid sample spec");
+ return false;
+ }
+
+ struct pa_channel_map map;
+ map.channels = m_sampleSpec.channels;
+ switch (map.channels)
+ {
+ case 1:
+ map.map[0] = PA_CHANNEL_POSITION_MONO;
+ break;
+
+ case 2:
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ break;
+
+ default:
+ CLog::Log(LOGERROR, "CPulseAESound::Initialize - We do not yet support multichannel sounds");
+ return false;
+ }
+
+ m_maxVolume = CAEFactory::AE->GetVolume();
+ m_volume = 1.0f;
+ pa_volume_t paVolume = pa_sw_volume_from_linear((double)(m_volume * m_maxVolume));
+ pa_cvolume_set(&m_chVolume, m_sampleSpec.channels, paVolume);
+
+ pa_threaded_mainloop_lock(m_mainLoop);
+ if ((m_stream = pa_stream_new(m_context, m_pulseName.c_str(), &m_sampleSpec, &map)) == NULL)
+ {
+ CLog::Log(LOGERROR, "CPulseAESound::Initialize - Could not create a stream");
+ pa_threaded_mainloop_unlock(m_mainLoop);
+ return false;
+ }
+
+ pa_stream_set_state_callback(m_stream, CPulseAESound::StreamStateCallback, this);
+ pa_stream_set_write_callback(m_stream, CPulseAESound::StreamWriteCallback, this);
+
+ if (pa_stream_connect_upload(m_stream, m_wavLoader.GetFrameCount() * pa_frame_size(&m_sampleSpec)) != 0)
+ {
+ CLog::Log(LOGERROR, "CPulseAESound::Initialize - Could not initialize the stream");
+ pa_stream_disconnect(m_stream);
+ m_stream = NULL;
+ pa_threaded_mainloop_unlock(m_mainLoop);
+ return false;
+ }
+
+ /* check if the stream failed */
+ if (pa_stream_get_state(m_stream) == PA_STREAM_FAILED)
+ {
+ CLog::Log(LOGERROR, "CPulseAESound::Initialize - Waited for the stream but it failed");
+ pa_stream_disconnect(m_stream);
+ m_stream = NULL;
+ pa_threaded_mainloop_unlock(m_mainLoop);
+ return false;
+ }
+
+ pa_threaded_mainloop_unlock(m_mainLoop);
+ return true;
+}
+
+void CPulseAESound::DeInitialize()
+{
+ pa_threaded_mainloop_lock(m_mainLoop);
+ pa_operation *op = pa_context_remove_sample(m_context, m_pulseName.c_str(), NULL, NULL);
+ if (op)
+ pa_operation_unref(op);
+ pa_threaded_mainloop_unlock(m_mainLoop);
+
+ m_wavLoader.DeInitialize();
+}
+
+void CPulseAESound::Play()
+{
+ pa_threaded_mainloop_lock(m_mainLoop);
+ /* we only keep the most recent operation as it is the only one needed for IsPlaying to function */
+ if (m_op)
+ pa_operation_unref(m_op);
+ m_op = pa_context_play_sample(m_context, m_pulseName.c_str(), NULL, PA_VOLUME_INVALID, NULL, NULL);
+ pa_threaded_mainloop_unlock(m_mainLoop);
+}
+
+void CPulseAESound::Stop()
+{
+ if (m_op)
+ {
+ pa_operation_cancel(m_op);
+ pa_operation_unref(m_op);
+ m_op = NULL;
+ }
+}
+
+bool CPulseAESound::IsPlaying()
+{
+ if (m_op)
+ {
+ if (pa_operation_get_state(m_op) == PA_OPERATION_RUNNING)
+ return true;
+
+ pa_operation_unref(m_op);
+ m_op = NULL;
+ }
+
+ return false;
+}
+
+void CPulseAESound::SetVolume(float volume)
+{
+}
+
+float CPulseAESound::GetVolume()
+{
+ return 1.0f;
+}
+
+void CPulseAESound::StreamStateCallback(pa_stream *s, void *userdata)
+{
+ CPulseAESound *sound = (CPulseAESound*)userdata;
+ pa_stream_state_t state = pa_stream_get_state(s);
+
+ switch (state)
+ {
+ case PA_STREAM_FAILED:
+ CLog::Log(LOGERROR, "CPulseAESound::StreamStateCallback - %s", pa_strerror(pa_context_errno(sound->m_context)));
+
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ case PA_STREAM_READY:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(sound->m_mainLoop, 0);
+ break;
+ }
+}
+
+void CPulseAESound::StreamWriteCallback(pa_stream *s, size_t length, void *userdata)
+{
+ CPulseAESound *sound = (CPulseAESound*)userdata;
+ sound->Upload(length);
+}
+
+void CPulseAESound::Upload(size_t length)
+{
+ float *samples = m_wavLoader.GetSamples();
+ size_t left = (m_wavLoader.GetSampleCount() * sizeof(float)) - m_dataSent;
+ size_t send = std::min(length, left);
+
+ if (pa_stream_write(m_stream, samples + m_dataSent, send, 0, 0, PA_SEEK_RELATIVE) == 0)
+ m_dataSent += send;
+
+ /* if we have no more data disable the callback */
+ if (left == send)
+ {
+ pa_stream_set_write_callback(m_stream, NULL, NULL);
+ if (pa_stream_finish_upload(m_stream) != 0)
+ {
+ CLog::Log(LOGERROR, "CPulseAESound::Upload - Error occured");
+ /* FIXME: Better error handling */
+ }
+
+ /* disconnect the stream as we dont need it anymore */
+ pa_stream_disconnect(m_stream);
+ m_stream = NULL;
+ }
+}
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAESound.h b/xbmc/cores/AudioEngine/Engines/PulseAESound.h
new file mode 100644
index 0000000000..b8bfb9c7eb
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAESound.h
@@ -0,0 +1,66 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_PULSEAUDIO
+
+#include "Interfaces/AESound.h"
+#include "Utils/AEWAVLoader.h"
+#include <pulse/pulseaudio.h>
+
+class CPulseAESound : public IAESound
+{
+public:
+ /* this should NEVER be called directly, use AE.GetSound */
+ CPulseAESound(const std::string &filename, pa_context *context, pa_threaded_mainloop *mainLoop);
+ virtual ~CPulseAESound();
+
+ virtual void DeInitialize();
+ virtual bool Initialize();
+
+ virtual void Play();
+ virtual void Stop();
+ virtual bool IsPlaying();
+
+ virtual void SetVolume(float volume);
+ virtual float GetVolume();
+private:
+ static void StreamStateCallback(pa_stream *s, void *userdata);
+ static void StreamWriteCallback(pa_stream *s, size_t length, void *userdata);
+ void Upload(size_t length);
+
+ std::string m_pulseName;
+ std::string m_filename;
+ CAEWAVLoader m_wavLoader;
+ size_t m_dataSent;
+
+ pa_context *m_context;
+ pa_threaded_mainloop *m_mainLoop;
+ pa_stream *m_stream;
+ pa_sample_spec m_sampleSpec;
+ pa_cvolume m_chVolume;
+ pa_operation *m_op;
+
+ float m_maxVolume, m_volume;
+};
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAEStream.cpp b/xbmc/cores/AudioEngine/Engines/PulseAEStream.cpp
new file mode 100644
index 0000000000..9f55130100
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAEStream.cpp
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_PULSEAUDIO
+
+#include "PulseAEStream.h"
+#include "AEFactory.h"
+#include "Utils/AEUtil.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+#include "threads/SingleLock.h"
+
+static const char *StreamStateToString(pa_stream_state s)
+{
+ switch(s)
+ {
+ case PA_STREAM_UNCONNECTED:
+ return "unconnected";
+ case PA_STREAM_CREATING:
+ return "creating";
+ case PA_STREAM_READY:
+ return "ready";
+ case PA_STREAM_FAILED:
+ return "failed";
+ case PA_STREAM_TERMINATED:
+ return "terminated";
+ default:
+ return "none";
+ }
+}
+
+CPulseAEStream::CPulseAEStream(pa_context *context, pa_threaded_mainloop *mainLoop, enum AEDataFormat format, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options) : m_fader(this)
+{
+ ASSERT(channelLayout.Count());
+ m_Destroyed = false;
+ m_Initialized = false;
+ m_Paused = false;
+
+ m_Stream = NULL;
+ m_Context = context;
+ m_MainLoop = mainLoop;
+
+ m_format = format;
+ m_sampleRate = sampleRate;
+ m_channelLayout = channelLayout;
+ m_options = options;
+
+ m_DrainOperation = NULL;
+ m_slave = NULL;
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ m_SampleSpec.channels = channelLayout.Count();
+ m_SampleSpec.rate = m_sampleRate;
+
+ switch (m_format)
+ {
+ case AE_FMT_U8 : m_SampleSpec.format = PA_SAMPLE_U8; break;
+ case AE_FMT_S16NE : m_SampleSpec.format = PA_SAMPLE_S16NE; break;
+ case AE_FMT_S16LE : m_SampleSpec.format = PA_SAMPLE_S16LE; break;
+ case AE_FMT_S16BE : m_SampleSpec.format = PA_SAMPLE_S16BE; break;
+ case AE_FMT_S24NE3: m_SampleSpec.format = PA_SAMPLE_S24NE; break;
+ case AE_FMT_S24NE4: m_SampleSpec.format = PA_SAMPLE_S24_32NE; break;
+ case AE_FMT_S32NE : m_SampleSpec.format = PA_SAMPLE_S32NE; break;
+ case AE_FMT_S32LE : m_SampleSpec.format = PA_SAMPLE_S32LE; break;
+ case AE_FMT_S32BE : m_SampleSpec.format = PA_SAMPLE_S32BE; break;
+ case AE_FMT_FLOAT : m_SampleSpec.format = PA_SAMPLE_FLOAT32NE; break;
+
+ default:
+ CLog::Log(LOGERROR, "PulseAudio: Invalid format %i", format);
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ m_format = AE_FMT_INVALID;
+ return;
+ }
+
+ if (!pa_sample_spec_valid(&m_SampleSpec))
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ Destroy();
+ return /*false*/;
+ }
+
+ m_frameSize = pa_frame_size(&m_SampleSpec);
+
+ struct pa_channel_map map;
+ map.channels = m_channelLayout.Count();
+
+ for (unsigned int ch = 0; ch < m_channelLayout.Count(); ++ch)
+ switch(m_channelLayout[ch])
+ {
+ case AE_CH_NULL: break;
+ case AE_CH_MAX : break;
+ case AE_CH_RAW : break;
+ case AE_CH_FL : map.map[ch] = PA_CHANNEL_POSITION_FRONT_LEFT ; break;
+ case AE_CH_FR : map.map[ch] = PA_CHANNEL_POSITION_FRONT_RIGHT ; break;
+ case AE_CH_FC : map.map[ch] = PA_CHANNEL_POSITION_FRONT_CENTER ; break;
+ case AE_CH_BC : map.map[ch] = PA_CHANNEL_POSITION_REAR_CENTER ; break;
+ case AE_CH_BL : map.map[ch] = PA_CHANNEL_POSITION_REAR_LEFT ; break;
+ case AE_CH_BR : map.map[ch] = PA_CHANNEL_POSITION_REAR_RIGHT ; break;
+ case AE_CH_LFE : map.map[ch] = PA_CHANNEL_POSITION_LFE ; break;
+ case AE_CH_FLOC: map.map[ch] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ; break;
+ case AE_CH_FROC: map.map[ch] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; break;
+ case AE_CH_SL : map.map[ch] = PA_CHANNEL_POSITION_SIDE_LEFT ; break;
+ case AE_CH_SR : map.map[ch] = PA_CHANNEL_POSITION_SIDE_RIGHT ; break;
+ case AE_CH_TC : map.map[ch] = PA_CHANNEL_POSITION_TOP_CENTER ; break;
+ case AE_CH_TFL : map.map[ch] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT ; break;
+ case AE_CH_TFR : map.map[ch] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ; break;
+ case AE_CH_TFC : map.map[ch] = PA_CHANNEL_POSITION_TOP_CENTER ; break;
+ case AE_CH_TBL : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_LEFT ; break;
+ case AE_CH_TBR : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT ; break;
+ case AE_CH_TBC : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_CENTER ; break;
+ }
+
+ m_MaxVolume = CAEFactory::AE->GetVolume();
+ m_Volume = 1.0f;
+ pa_volume_t paVolume = pa_sw_volume_from_linear((double)(m_Volume * m_MaxVolume));
+ pa_cvolume_set(&m_ChVolume, m_SampleSpec.channels, paVolume);
+ if ((m_Stream = pa_stream_new(m_Context, "audio stream", &m_SampleSpec, &map)) == NULL)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Could not create a stream");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ Destroy();
+ return /*false*/;
+ }
+
+ pa_stream_set_state_callback(m_Stream, CPulseAEStream::StreamStateCallback, this);
+ pa_stream_set_write_callback(m_Stream, CPulseAEStream::StreamRequestCallback, this);
+ pa_stream_set_latency_update_callback(m_Stream, CPulseAEStream::StreamLatencyUpdateCallback, this);
+ pa_stream_set_underflow_callback(m_Stream, CPulseAEStream::StreamUnderflowCallback, this);
+
+ int flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
+ if (options && AESTREAM_FORCE_RESAMPLE)
+ flags |= PA_STREAM_VARIABLE_RATE;
+
+ if (pa_stream_connect_playback(m_Stream, NULL, NULL, (pa_stream_flags)flags, &m_ChVolume, NULL) < 0)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ Destroy();
+ return /*false*/;
+ }
+
+ /* Wait until the stream is ready */
+ do
+ {
+ pa_threaded_mainloop_wait(m_MainLoop);
+ CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream)));
+ }
+ while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED);
+
+ if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed");
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ Destroy();
+ return /*false*/;
+ }
+
+ m_cacheSize = pa_stream_writable_size(m_Stream);
+
+ pa_threaded_mainloop_unlock(m_MainLoop);
+
+ m_Initialized = true;
+
+ CLog::Log(LOGINFO, "PulseAEStream::Initialized");
+ CLog::Log(LOGINFO, " Sample Rate : %d", m_sampleRate);
+ CLog::Log(LOGINFO, " Sample Format : %s", CAEUtil::DataFormatToStr(m_format));
+ CLog::Log(LOGINFO, " Channel Count : %d", m_channelLayout.Count());
+ CLog::Log(LOGINFO, " Channel Layout: %s", ((std::string)m_channelLayout).c_str());
+ CLog::Log(LOGINFO, " Frame Size : %d", m_frameSize);
+ CLog::Log(LOGINFO, " Cache Size : %d", m_cacheSize);
+
+ Resume();
+
+ return /*true*/;
+}
+
+CPulseAEStream::~CPulseAEStream()
+{
+ Destroy();
+}
+
+/*
+ this method may be called inside the pulse main loop,
+ so be VERY careful with locking
+*/
+void CPulseAEStream::Destroy()
+{
+ if (!m_Initialized)
+ return;
+
+ if (m_Destroyed)
+ return;
+
+ m_fader.StopThread(true);
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ if (m_DrainOperation)
+ {
+ pa_operation_cancel(m_DrainOperation);
+ pa_operation_unref(m_DrainOperation);
+ m_DrainOperation = NULL;
+ }
+
+ if (m_Stream)
+ {
+ pa_stream_disconnect(m_Stream);
+ pa_stream_unref(m_Stream);
+ m_Stream = NULL;
+ }
+
+ /* signal CPulseAE to free us */
+ m_Destroyed = true;
+ m_Initialized = false;
+
+ pa_threaded_mainloop_unlock(m_MainLoop);
+}
+
+unsigned int CPulseAEStream::GetSpace()
+{
+ if (!m_Initialized)
+ return 0;
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+ unsigned int size = pa_stream_writable_size(m_Stream);
+ pa_threaded_mainloop_unlock(m_MainLoop);
+
+ return size;
+}
+
+unsigned int CPulseAEStream::AddData(void *data, unsigned int size)
+{
+ if (!m_Initialized)
+ return size;
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ int length = std::min((int)pa_stream_writable_size(m_Stream), (int)size);
+ if (length == 0)
+ {
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return 0;
+ }
+
+ int written = pa_stream_write(m_Stream, data, length, NULL, 0, PA_SEEK_RELATIVE);
+ pa_threaded_mainloop_unlock(m_MainLoop);
+
+ if (written < 0)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: AddPackets - pa_stream_write failed\n");
+ return 0;
+ }
+
+ return length;
+}
+
+double CPulseAEStream::GetDelay()
+{
+ if (!m_Initialized)
+ return 0.0;
+
+ pa_usec_t latency = 0;
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ if (pa_stream_get_latency(m_Stream, &latency, NULL) == PA_ERR_NODATA)
+ CLog::Log(LOGERROR, "PulseAudio: pa_stream_get_latency() failed");
+
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return (double)((double)latency / 1000000.0);
+}
+
+double CPulseAEStream::GetCacheTime()
+{
+ if (!m_Initialized)
+ return 0.0;
+
+ return (double)(m_cacheSize - GetSpace()) / (double)m_sampleRate;
+}
+
+double CPulseAEStream::GetCacheTotal()
+{
+ if (!m_Initialized)
+ return 0.0;
+
+ return (double)m_cacheSize / (double)m_sampleRate;
+}
+
+bool CPulseAEStream::IsPaused()
+{
+ return m_Paused;
+}
+
+bool CPulseAEStream::IsDraining()
+{
+ if (m_DrainOperation)
+ {
+ if (pa_operation_get_state(m_DrainOperation) == PA_OPERATION_RUNNING)
+ return true;
+
+ pa_operation_unref(m_DrainOperation);
+ m_DrainOperation = NULL;
+ }
+
+ return false;
+}
+
+bool CPulseAEStream::IsDestroyed()
+{
+ return m_Destroyed;
+}
+
+void CPulseAEStream::Pause()
+{
+ if (m_Initialized)
+ m_Paused = Cork(true);
+}
+
+void CPulseAEStream::Resume()
+{
+ if (m_Initialized)
+ m_Paused = Cork(false);
+}
+
+void CPulseAEStream::Drain()
+{
+ if (!m_Initialized)
+ return;
+
+ if (m_DrainOperation)
+ return;
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+ m_DrainOperation = pa_stream_drain(m_Stream, CPulseAEStream::StreamDrainComplete, this);
+ pa_threaded_mainloop_unlock(m_MainLoop);
+}
+
+void CPulseAEStream::Flush()
+{
+ if (!m_Initialized)
+ return;
+
+ pa_threaded_mainloop_lock(m_MainLoop);
+ pa_operation_unref(pa_stream_flush(m_Stream, NULL, NULL));
+ pa_threaded_mainloop_unlock(m_MainLoop);
+}
+
+float CPulseAEStream::GetVolume()
+{
+ return m_Volume;
+}
+
+float CPulseAEStream::GetReplayGain()
+{
+ return 0.0f;
+}
+
+void CPulseAEStream::SetVolume(float volume)
+{
+ if (!m_Initialized)
+ return;
+
+ if (!pa_threaded_mainloop_in_thread(m_MainLoop))
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ m_Volume = volume;
+ pa_volume_t paVolume = pa_sw_volume_from_linear((double)(m_Volume * m_MaxVolume));
+
+ pa_cvolume_set(&m_ChVolume, m_SampleSpec.channels, paVolume);
+ pa_operation *op = pa_context_set_sink_input_volume(m_Context, pa_stream_get_index(m_Stream), &m_ChVolume, NULL, NULL);
+
+ if (op == NULL)
+ CLog::Log(LOGERROR, "PulseAudio: Failed to set volume");
+ else
+ pa_operation_unref(op);
+
+ if (!pa_threaded_mainloop_in_thread(m_MainLoop))
+ pa_threaded_mainloop_unlock(m_MainLoop);
+}
+
+void CPulseAEStream::UpdateVolume(float max)
+{
+ if (!m_Initialized)
+ return;
+
+ m_MaxVolume = max;
+ SetVolume(m_Volume);
+}
+
+void CPulseAEStream::SetReplayGain(float factor)
+{
+}
+
+const unsigned int CPulseAEStream::GetFrameSize() const
+{
+ return m_frameSize;
+}
+
+const unsigned int CPulseAEStream::GetChannelCount() const
+{
+ return m_channelLayout.Count();
+}
+
+const unsigned int CPulseAEStream::GetSampleRate() const
+{
+ return m_sampleRate;
+}
+
+const enum AEDataFormat CPulseAEStream::GetDataFormat() const
+{
+ return m_format;
+}
+
+double CPulseAEStream::GetResampleRatio()
+{
+ return 1.0;
+}
+
+bool CPulseAEStream::SetResampleRatio(double ratio)
+{
+ return false;
+}
+
+void CPulseAEStream::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+ m_AudioCallback = pCallback;
+}
+
+void CPulseAEStream::UnRegisterAudioCallback()
+{
+ m_AudioCallback = NULL;
+}
+
+void CPulseAEStream::FadeVolume(float from, float target, unsigned int time)
+{
+ if (!m_Initialized)
+ return;
+
+ m_fader.SetupFader(from, target, time);
+}
+
+bool CPulseAEStream::IsFading()
+{
+ return m_fader.IsRunning();
+}
+
+void CPulseAEStream::StreamRequestCallback(pa_stream *s, size_t length, void *userdata)
+{
+ CPulseAEStream *stream = (CPulseAEStream *)userdata;
+ pa_threaded_mainloop_signal(stream->m_MainLoop, 0);
+}
+
+void CPulseAEStream::StreamLatencyUpdateCallback(pa_stream *s, void *userdata)
+{
+ CPulseAEStream *stream = (CPulseAEStream *)userdata;
+ pa_threaded_mainloop_signal(stream->m_MainLoop, 0);
+}
+
+void CPulseAEStream::StreamStateCallback(pa_stream *s, void *userdata)
+{
+ CPulseAEStream *stream = (CPulseAEStream *)userdata;
+ pa_stream_state_t state = pa_stream_get_state(s);
+
+ switch (state)
+ {
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(stream->m_MainLoop, 0);
+ break;
+ }
+}
+
+void CPulseAEStream::StreamUnderflowCallback(pa_stream *s, void *userdata)
+{
+ CPulseAEStream *stream = (CPulseAEStream *)userdata;
+ CLog::Log(LOGWARNING, "PulseAudio: Stream underflow");
+ pa_threaded_mainloop_signal(stream->m_MainLoop, 0);
+}
+
+void CPulseAEStream::StreamDrainComplete(pa_stream *s, int success, void *userdata)
+{
+ CPulseAEStream *stream = (CPulseAEStream *)userdata;
+ if (stream->m_slave)
+ stream->m_slave->Resume();
+ pa_threaded_mainloop_signal(stream->m_MainLoop, 0);
+}
+
+inline bool CPulseAEStream::WaitForOperation(pa_operation *op, pa_threaded_mainloop *mainloop, const char *LogEntry = "")
+{
+ if (op == NULL)
+ return false;
+
+ bool sucess = true;
+ ASSERT(!pa_threaded_mainloop_in_thread(mainloop));
+
+ while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait(mainloop);
+
+ if (pa_operation_get_state(op) != PA_OPERATION_DONE)
+ {
+ CLog::Log(LOGERROR, "PulseAudio: %s Operation failed", LogEntry);
+ sucess = false;
+ }
+
+ pa_operation_unref(op);
+ return sucess;
+}
+
+bool CPulseAEStream::Cork(bool cork)
+{
+ pa_threaded_mainloop_lock(m_MainLoop);
+
+ pa_operation *op = pa_stream_cork(m_Stream, cork ? 1 : 0, NULL, NULL);
+ if (!WaitForOperation(op, m_MainLoop, cork ? "Pause" : "Resume"))
+ cork = !cork;
+
+ pa_threaded_mainloop_unlock(m_MainLoop);
+ return cork;
+}
+
+void CPulseAEStream::RegisterSlave(IAEStream *stream)
+{
+ m_slave = stream;
+}
+
+CPulseAEStream::CLinearFader::CLinearFader(IAEStream *stream) : m_stream(stream)
+{
+ m_from = 0;
+ m_target = 0;
+ m_time = 0;
+ m_isRunning = false;
+}
+
+void CPulseAEStream::CLinearFader::SetupFader(float from, float target, unsigned int time)
+{
+ StopThread(true);
+
+ m_from = from;
+ m_target = target;
+ m_time = time;
+
+ if (m_time > 0)
+ Create();
+ else
+ m_stream->SetVolume(m_target);
+}
+
+void CPulseAEStream::CLinearFader::Process()
+{
+ if (m_stream == NULL)
+ return;
+
+ m_isRunning = true;
+ m_stream->SetVolume(m_from);
+ float k = m_target - m_from;
+
+ unsigned int begin = XbmcThreads::SystemClockMillis();
+ unsigned int end = begin + m_time;
+ unsigned int current = begin;
+ unsigned int step = std::max(1u, m_time / 100);
+
+ do
+ {
+ float x = ((float)current - (float)begin) / (float)m_time;
+
+ m_stream->SetVolume(m_from + k * x);
+ usleep(step * 1000);
+ current = XbmcThreads::SystemClockMillis();
+ } while (current <= end && !m_bStop);
+
+ m_stream->SetVolume(m_target);
+ m_isRunning = false;
+}
+
+bool CPulseAEStream::CLinearFader::IsRunning()
+{
+ return !m_isRunning;
+}
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/PulseAEStream.h b/xbmc/cores/AudioEngine/Engines/PulseAEStream.h
new file mode 100644
index 0000000000..3156c0a328
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/PulseAEStream.h
@@ -0,0 +1,131 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifdef HAS_PULSEAUDIO
+
+#include "Interfaces/AEStream.h"
+#include "threads/Thread.h"
+#include <pulse/pulseaudio.h>
+
+class CPulseAEStream : public IAEStream
+{
+public:
+ /* this should NEVER be called directly, use AE.GetStream */
+ CPulseAEStream(pa_context *context, pa_threaded_mainloop *mainLoop, enum AEDataFormat format, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options);
+ virtual ~CPulseAEStream();
+
+ virtual void Destroy();
+
+ virtual unsigned int GetSpace();
+ virtual unsigned int AddData(void *data, unsigned int size);
+ virtual double GetDelay();
+ virtual double GetCacheTime ();
+ virtual double GetCacheTotal();
+
+ virtual bool IsPaused ();
+ virtual bool IsDraining ();
+ virtual bool IsDestroyed ();
+
+ virtual void Pause ();
+ virtual void Resume ();
+ virtual void Drain ();
+ virtual void Flush ();
+
+ virtual float GetVolume ();
+ virtual float GetReplayGain();
+ virtual void SetVolume (float volume);
+ virtual void SetReplayGain(float factor);
+
+ virtual const unsigned int GetFrameSize () const;
+ virtual const unsigned int GetChannelCount() const;
+ virtual const unsigned int GetSampleRate () const;
+ virtual const enum AEDataFormat GetDataFormat () const;
+
+ /* for dynamic sample rate changes (smoothvideo) */
+ virtual double GetResampleRatio();
+ virtual bool SetResampleRatio(double ratio);
+
+ /* vizualization callback register function */
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnRegisterAudioCallback();
+
+ virtual void FadeVolume(float from, float target, unsigned int time);
+ virtual bool IsFading();
+
+ /* trigger the stream to update its volume relative to AE */
+ void UpdateVolume(float max);
+
+ virtual void RegisterSlave(IAEStream *stream);
+private:
+ static void StreamRequestCallback(pa_stream *s, size_t length, void *userdata);
+ static void StreamLatencyUpdateCallback(pa_stream *s, void *userdata);
+ static void StreamStateCallback(pa_stream *s, void *userdata);
+ static void StreamUnderflowCallback(pa_stream *s, void *userdata);
+ static void StreamDrainComplete(pa_stream *s, int success, void *userdata);
+
+ static inline bool WaitForOperation(pa_operation *op, pa_threaded_mainloop *mainloop, const char *LogEntry);
+ bool Cork(bool cork);
+
+ bool m_Destroyed;
+ bool m_Initialized;
+ bool m_Paused;
+
+ pa_stream *m_Stream;
+ pa_sample_spec m_SampleSpec;
+
+ float m_MaxVolume;
+ float m_Volume;
+ pa_cvolume m_ChVolume;
+
+ pa_context *m_Context;
+ pa_threaded_mainloop *m_MainLoop;
+
+ IAudioCallback* m_AudioCallback;
+
+ enum AEDataFormat m_format;
+ unsigned int m_sampleRate;
+ CAEChannelInfo m_channelLayout;
+ unsigned int m_options;
+ unsigned int m_frameSize;
+ unsigned int m_cacheSize;
+
+ pa_operation *m_DrainOperation;
+ IAEStream *m_slave;
+
+ class CLinearFader : public CThread
+ {
+ public:
+ CLinearFader(IAEStream *stream);
+ void SetupFader(float from, float target, unsigned int time);
+ bool IsRunning();
+ protected:
+ virtual void Process();
+ private:
+ IAEStream *m_stream;
+ float m_from;
+ float m_target;
+ unsigned int m_time;
+ volatile bool m_isRunning;
+ } m_fader;
+};
+
+#endif
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE.cpp b/xbmc/cores/AudioEngine/Engines/SoftAE.cpp
new file mode 100644
index 0000000000..a517162680
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAE.cpp
@@ -0,0 +1,1182 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string.h>
+#include <sstream>
+#include <iterator>
+
+#include "system.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "utils/MathUtils.h"
+#include "threads/SingleLock.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+
+#include "SoftAE.h"
+#include "SoftAESound.h"
+#include "SoftAEStream.h"
+#include "AESinkFactory.h"
+#include "Interfaces/AESink.h"
+#include "Utils/AEUtil.h"
+#include "Encoders/AEEncoderFFmpeg.h"
+
+using namespace std;
+
+CSoftAE::CSoftAE():
+ m_thread (NULL ),
+ m_audiophile (true ),
+ m_running (false ),
+ m_reOpen (false ),
+ m_sink (NULL ),
+ m_transcode (false ),
+ m_rawPassthrough (false ),
+ m_soundMode (AE_SOUND_OFF),
+ m_streamsPlaying (false ),
+ m_encoder (NULL ),
+ m_converted (NULL ),
+ m_convertedSize (0 ),
+ m_masterStream (NULL ),
+ m_outputStageFn (NULL ),
+ m_streamStageFn (NULL )
+{
+ CAESinkFactory::EnumerateEx(m_sinkInfoList);
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ CLog::Log(LOGINFO, "Enumerated %s devices:", itt->m_sinkName.c_str());
+ int count = 0;
+ for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2)
+ {
+ CLog::Log(LOGINFO, " Device %d", ++count);
+ CAEDeviceInfo& info = *itt2;
+ std::stringstream ss((std::string)info);
+ std::string line;
+ while(std::getline(ss, line, '\n'))
+ CLog::Log(LOGINFO, " %s", line.c_str());
+ }
+ }
+}
+
+CSoftAE::~CSoftAE()
+{
+ Deinitialize();
+
+ /* free the streams */
+ CSingleLock streamLock(m_streamLock);
+ while (!m_streams.empty())
+ {
+ CSoftAEStream *s = m_streams.front();
+ delete s;
+ }
+
+ /* free the sounds */
+ CSingleLock soundLock(m_soundLock);
+ while (!m_sounds.empty())
+ {
+ CSoftAESound *s = m_sounds.front();
+ m_sounds.pop_front();
+ delete s;
+ }
+}
+
+IAESink *CSoftAE::GetSink(AEAudioFormat &newFormat, bool passthrough, std::string &device)
+{
+ device = passthrough ? m_passthroughDevice : m_device;
+
+ /* if we are raw, force the sample rate */
+ if (AE_IS_RAW(newFormat.m_dataFormat))
+ {
+ switch (newFormat.m_dataFormat)
+ {
+ case AE_FMT_AC3:
+ case AE_FMT_DTS:
+ break;
+
+ case AE_FMT_EAC3:
+ newFormat.m_sampleRate = 192000;
+ break;
+
+ case AE_FMT_TRUEHD:
+ case AE_FMT_DTSHD:
+ newFormat.m_sampleRate = 192000;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ IAESink *sink = CAESinkFactory::Create(device, newFormat, passthrough);
+ return sink;
+}
+
+/* this method MUST be called while holding m_streamLock */
+inline CSoftAEStream *CSoftAE::GetMasterStream()
+{
+ /* remove any destroyed streams first */
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end();)
+ {
+ CSoftAEStream *stream = *itt;
+ if (stream->IsDestroyed())
+ {
+ RemoveStream(m_playingStreams, stream);
+ RemoveStream(m_streams , stream);
+ delete stream;
+ continue;
+ }
+ ++itt;
+ }
+
+ if (!m_newStreams.empty())
+ return m_newStreams.back();
+
+ if (!m_streams.empty())
+ return m_streams.back();
+
+ return NULL;
+}
+
+/* save method to call outside of the main thread, use this one */
+void CSoftAE::OpenSink()
+{
+ m_reOpenEvent.Reset();
+ m_reOpen = true;
+ m_reOpenEvent.Wait();
+}
+
+/* this must NEVER be called from outside the main thread or Initialization */
+void CSoftAE::InternalOpenSink()
+{
+ /* save off our raw/passthrough mode for checking */
+ bool wasTranscode = m_transcode;
+ bool wasRawPassthrough = m_rawPassthrough;
+ bool reInit = false;
+
+ LoadSettings();
+
+ /* initialize for analog output */
+ m_rawPassthrough = false;
+ m_streamStageFn = &CSoftAE::RunStreamStage;
+ m_outputStageFn = &CSoftAE::RunOutputStage;
+
+ /* initialize the new format for basic 2.0 output */
+ AEAudioFormat newFormat;
+ newFormat.m_dataFormat = AE_FMT_FLOAT;
+ newFormat.m_sampleRate = 44100;
+ newFormat.m_channelLayout = m_stereoUpmix ? m_stdChLayout : AE_CH_LAYOUT_2_0;
+
+ CSingleLock streamLock(m_streamLock);
+
+ m_masterStream = GetMasterStream();
+ if (m_masterStream)
+ {
+ /* choose the sample rate & channel layout based on the master stream */
+ newFormat.m_sampleRate = m_masterStream->GetSampleRate();
+ if (!m_stereoUpmix)
+ newFormat.m_channelLayout = m_masterStream->m_initChannelLayout;
+
+ if (m_masterStream->IsRaw())
+ {
+ newFormat.m_sampleRate = m_masterStream->GetSampleRate();
+ newFormat.m_encodedRate = m_masterStream->GetEncodedSampleRate();
+ newFormat.m_dataFormat = m_masterStream->GetDataFormat();
+ newFormat.m_channelLayout = m_masterStream->m_initChannelLayout;
+ m_rawPassthrough = true;
+ m_streamStageFn = &CSoftAE::RunRawStreamStage;
+ m_outputStageFn = &CSoftAE::RunRawOutputStage;
+ }
+ else
+ {
+ if (!m_transcode)
+ newFormat.m_channelLayout.ResolveChannels(m_stdChLayout);
+ else
+ {
+ if (m_masterStream->m_initChannelLayout == AE_CH_LAYOUT_2_0)
+ m_transcode = false;
+ }
+ }
+
+ /* if the stream is paused we cant use it for anything else */
+ if (m_masterStream->m_paused)
+ m_masterStream = NULL;
+ }
+ else
+ m_transcode = false;
+
+ if (!m_rawPassthrough && m_transcode)
+ newFormat.m_dataFormat = AE_FMT_AC3;
+
+ streamLock.Leave();
+
+ std::string device, driver;
+ if (m_transcode || m_rawPassthrough)
+ device = m_passthroughDevice;
+ else
+ device = m_device;
+
+ CAESinkFactory::ParseDevice(device, driver);
+ if (driver.empty() && m_sink)
+ driver = m_sink->GetName();
+
+ if (m_rawPassthrough)
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - RAW passthrough enabled");
+ else if (m_transcode)
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - Transcode passthrough enabled");
+
+ /*
+ try to use 48000hz if we are going to transcode, this prevents the sink
+ from being re-opened repeatedly when switching sources, which locks up
+ some receivers & crappy integrated sound drivers.
+ */
+ if (m_transcode && !m_rawPassthrough)
+ {
+ enum AEChannel ac3Layout[3] = {AE_CH_RAW, AE_CH_RAW, AE_CH_NULL};
+ newFormat.m_sampleRate = 48000;
+ newFormat.m_channelLayout = ac3Layout;
+ m_outputStageFn = &CSoftAE::RunTranscodeStage;
+ }
+
+ /*
+ if there is an audio resample rate set, use it, this MAY NOT be honoured as
+ the audio sink may not support the requested format, and may change it.
+ */
+ if (g_advancedSettings.m_audioResample)
+ {
+ newFormat.m_sampleRate = g_advancedSettings.m_audioResample;
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - Forcing samplerate to %d", newFormat.m_sampleRate);
+ }
+
+ /* only re-open the sink if its not compatible with what we need */
+ std::string sinkName;
+ if (m_sink)
+ {
+ sinkName = m_sink->GetName();
+ std::transform(sinkName.begin(), sinkName.end(), sinkName.begin(), ::toupper);
+ }
+
+ if (!m_sink || sinkName != driver || !m_sink->IsCompatible(newFormat, device))
+ {
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - sink incompatible, re-starting");
+
+ /* take the sink lock */
+ CExclusiveLock sinkLock(m_sinkLock);
+
+ reInit = true;
+
+ /* we are going to open, so close the old sink if it was open */
+ if (m_sink)
+ {
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+
+ /* if we already have a driver, prepend it to the device string */
+ if (!driver.empty())
+ device = driver + ":" + device;
+
+ /* create the new sink */
+ m_sink = GetSink(newFormat, m_transcode || m_rawPassthrough, device);
+
+ /* perform basic sanity checks on the format returned by the sink */
+ ASSERT(newFormat.m_channelLayout.Count() > 0);
+ ASSERT(newFormat.m_dataFormat <= AE_FMT_FLOAT);
+ ASSERT(newFormat.m_frames > 0);
+ ASSERT(newFormat.m_frameSamples > 0);
+ ASSERT(newFormat.m_frameSize == (CAEUtil::DataFormatToBits(newFormat.m_dataFormat) >> 3) * newFormat.m_channelLayout.Count());
+ ASSERT(newFormat.m_sampleRate > 0);
+
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - %s Initialized:", m_sink->GetName());
+ CLog::Log(LOGINFO, " Output Device : %s", device.c_str());
+ CLog::Log(LOGINFO, " Sample Rate : %d", newFormat.m_sampleRate);
+ CLog::Log(LOGINFO, " Sample Format : %s", CAEUtil::DataFormatToStr(newFormat.m_dataFormat));
+ CLog::Log(LOGINFO, " Channel Count : %d", newFormat.m_channelLayout.Count());
+ CLog::Log(LOGINFO, " Channel Layout: %s", ((std::string)newFormat.m_channelLayout).c_str());
+ CLog::Log(LOGINFO, " Frames : %d", newFormat.m_frames);
+ CLog::Log(LOGINFO, " Frame Samples : %d", newFormat.m_frameSamples);
+ CLog::Log(LOGINFO, " Frame Size : %d", newFormat.m_frameSize);
+
+ m_sinkFormat = newFormat;
+ m_sinkFormatSampleRateMul = 1.0 / (float)newFormat.m_sampleRate;
+ m_sinkFormatFrameSizeMul = 1.0 / (float)newFormat.m_frameSize;
+ m_sinkBlockSize = newFormat.m_frames * newFormat.m_frameSize;
+
+ /* invalidate the buffer */
+ m_buffer.Empty();
+ }
+ else
+ CLog::Log(LOGINFO, "CSoftAE::InternalOpenSink - keeping old sink with : %s, %s, %dhz",
+ CAEUtil::DataFormatToStr(newFormat.m_dataFormat),
+ ((std::string)newFormat.m_channelLayout).c_str(),
+ newFormat.m_sampleRate);
+
+ reInit = (reInit || m_chLayout != m_sinkFormat.m_channelLayout);
+ m_chLayout = m_sinkFormat.m_channelLayout;
+
+ size_t neededBufferSize = 0;
+ if (m_rawPassthrough)
+ {
+ if (!wasRawPassthrough)
+ m_buffer.Empty();
+
+ m_convertFn = NULL;
+ m_bytesPerSample = CAEUtil::DataFormatToBits(m_sinkFormat.m_dataFormat) >> 3;
+ m_frameSize = m_sinkFormat.m_frameSize;
+ neededBufferSize = m_sinkFormat.m_frames * m_sinkFormat.m_frameSize;
+ }
+ else
+ {
+ /* if we are transcoding */
+ if (m_transcode)
+ {
+ if (!wasTranscode || wasRawPassthrough)
+ {
+ /* invalidate the buffer */
+ m_buffer.Empty();
+ if (m_encoder)
+ m_encoder->Reset();
+ }
+
+ /* configure the encoder */
+ AEAudioFormat encoderFormat;
+ encoderFormat.m_sampleRate = m_sinkFormat.m_sampleRate;
+ encoderFormat.m_dataFormat = AE_FMT_FLOAT;
+ encoderFormat.m_channelLayout = m_chLayout;
+ if (!m_encoder || !m_encoder->IsCompatible(encoderFormat))
+ {
+ m_buffer.Empty();
+ SetupEncoder(encoderFormat);
+ m_encoderFormat = encoderFormat;
+ m_encoderFrameSizeMul = 1.0 / (float)encoderFormat.m_frameSize;
+ }
+
+ /* remap directly to the format we need for encode */
+ reInit = (reInit || m_chLayout != m_encoderFormat.m_channelLayout);
+ m_chLayout = m_encoderFormat.m_channelLayout;
+ m_convertFn = CAEConvert::FrFloat(m_encoderFormat.m_dataFormat);
+ neededBufferSize = m_encoderFormat.m_frames * sizeof(float) * m_chLayout.Count();
+ CLog::Log(LOGDEBUG, "CSoftAE::Initialize - Encoding using layout: %s", ((std::string)m_chLayout).c_str());
+ }
+ else
+ {
+ m_convertFn = CAEConvert::FrFloat(m_sinkFormat.m_dataFormat);
+ neededBufferSize = m_sinkFormat.m_frames * sizeof(float) * m_chLayout.Count();
+ CLog::Log(LOGDEBUG, "CSoftAE::Initialize - Using speaker layout: %s", CAEUtil::GetStdChLayoutName(m_stdChLayout));
+ }
+
+ m_bytesPerSample = CAEUtil::DataFormatToBits(AE_FMT_FLOAT) >> 3;
+ m_frameSize = m_bytesPerSample * m_chLayout.Count();
+ }
+
+ if (m_buffer.Size() < neededBufferSize)
+ m_buffer.Alloc(neededBufferSize);
+
+ if (reInit)
+ {
+ /* re-init sounds */
+ if (!m_rawPassthrough)
+ {
+ CSingleLock soundLock(m_soundLock);
+ StopAllSounds();
+ for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ (*itt)->Initialize();
+ }
+
+ /* re-init streams */
+ streamLock.Enter();
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ (*itt)->Initialize();
+ streamLock.Leave();
+ }
+
+ /* any new streams need to be initialized */
+ for (StreamList::iterator itt = m_newStreams.begin(); itt != m_newStreams.end(); ++itt)
+ {
+ (*itt)->Initialize();
+ m_streams.push_back(*itt);
+ if (!(*itt)->m_paused)
+ m_playingStreams.push_back(*itt);
+ }
+ m_newStreams.clear();
+ m_streamsPlaying = !m_playingStreams.empty();
+
+ /* notify any event listeners that we are done */
+ m_reOpen = false;
+ m_reOpenEvent.Set();
+}
+
+void CSoftAE::ResetEncoder()
+{
+ if (m_encoder)
+ m_encoder->Reset();
+ m_encodedBuffer.Empty();
+}
+
+bool CSoftAE::SetupEncoder(AEAudioFormat &format)
+{
+ ResetEncoder();
+ delete m_encoder;
+ m_encoder = NULL;
+
+ if (!m_transcode)
+ return false;
+
+ m_encoder = new CAEEncoderFFmpeg();
+ if (m_encoder->Initialize(format))
+ return true;
+
+ delete m_encoder;
+ m_encoder = NULL;
+ return false;
+}
+
+void CSoftAE::Shutdown()
+{
+ Deinitialize();
+}
+
+bool CSoftAE::Initialize()
+{
+ InternalOpenSink();
+ m_running = true;
+ m_thread = new CThread(this, "CSoftAE");
+ m_thread->Create();
+ m_thread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL);
+ return true;
+}
+
+void CSoftAE::OnSettingsChange(std::string setting)
+{
+ if (setting == "audiooutput.passthroughdevice" ||
+ setting == "audiooutput.audiodevice" ||
+ setting == "audiooutput.mode" ||
+ setting == "audiooutput.ac3passthrough" ||
+ setting == "audiooutput.dtspassthrough" ||
+ setting == "audiooutput.channellayout" ||
+ setting == "audiooutput.useexclusivemode" ||
+ setting == "audiooutput.multichannellpcm" ||
+ setting == "audiooutput.stereoupmix")
+ {
+ OpenSink();
+ }
+
+ if (setting == "audiooutput.dontnormalizelevels" || setting == "audiooutput.stereoupmix")
+ {
+ /* re-init stream reamppers */
+ CSingleLock streamLock(m_streamLock);
+ for (StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ (*itt)->InitializeRemap();
+ }
+}
+
+void CSoftAE::LoadSettings()
+{
+ m_audiophile = g_advancedSettings.m_audioAudiophile;
+ if (m_audiophile)
+ CLog::Log(LOGINFO, "CSoftAE::LoadSettings - Audiophile switch enabled");
+
+ m_stereoUpmix = g_guiSettings.GetBool("audiooutput.stereoupmix");
+ if (m_stereoUpmix)
+ CLog::Log(LOGINFO, "CSoftAE::LoadSettings - Stereo upmix is enabled");
+
+ /* load the configuration */
+ m_stdChLayout = AE_CH_LAYOUT_2_0;
+ switch (g_guiSettings.GetInt("audiooutput.channellayout"))
+ {
+ default:
+ case 0: m_stdChLayout = AE_CH_LAYOUT_2_0; break; /* dont alow 1_0 output */
+ case 1: m_stdChLayout = AE_CH_LAYOUT_2_0; break;
+ case 2: m_stdChLayout = AE_CH_LAYOUT_2_1; break;
+ case 3: m_stdChLayout = AE_CH_LAYOUT_3_0; break;
+ case 4: m_stdChLayout = AE_CH_LAYOUT_3_1; break;
+ case 5: m_stdChLayout = AE_CH_LAYOUT_4_0; break;
+ case 6: m_stdChLayout = AE_CH_LAYOUT_4_1; break;
+ case 7: m_stdChLayout = AE_CH_LAYOUT_5_0; break;
+ case 8: m_stdChLayout = AE_CH_LAYOUT_5_1; break;
+ case 9: m_stdChLayout = AE_CH_LAYOUT_7_0; break;
+ case 10: m_stdChLayout = AE_CH_LAYOUT_7_1; break;
+ }
+
+ /* get the output devices and ensure they exist */
+ m_device = g_guiSettings.GetString("audiooutput.audiodevice");
+ m_passthroughDevice = g_guiSettings.GetString("audiooutput.passthroughdevice");
+ VerifySoundDevice(m_device , false);
+ VerifySoundDevice(m_passthroughDevice, true );
+
+ m_transcode = (
+ g_guiSettings.GetBool("audiooutput.ac3passthrough") /*||
+ g_guiSettings.GetBool("audiooutput.dtspassthrough") */
+ ) && (
+ (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_IEC958) ||
+ (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI && !g_guiSettings.GetBool("audiooutput.multichannellpcm"))
+ );
+}
+
+void CSoftAE::VerifySoundDevice(std::string& device, bool passthrough)
+{
+ /* check that the specified device exists */
+ std::string firstDevice;
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo& devInfo = *itt2;
+ if (passthrough && devInfo.m_deviceType == AE_DEVTYPE_PCM)
+ continue;
+ std::string deviceName = sinkInfo.m_sinkName + ":" + devInfo.m_deviceName;
+
+ /* remember the first device so we can default to it if required */
+ if (firstDevice.empty())
+ firstDevice = deviceName;
+
+ if (device == deviceName)
+ return;
+ }
+ }
+
+ /* if the device wasnt found, set it to the first viable output */
+ device = firstDevice;
+}
+
+void CSoftAE::Deinitialize()
+{
+ if (m_thread)
+ {
+ Stop();
+ m_thread->StopThread(true);
+ delete m_thread;
+ m_thread = NULL;
+ }
+
+ if (m_sink)
+ {
+ /* shutdown the sink */
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+
+ delete m_encoder;
+ m_encoder = NULL;
+
+ ResetEncoder();
+ m_buffer.DeAlloc();
+
+ _aligned_free(m_converted);
+ m_converted = NULL;
+ m_convertedSize = 0;
+}
+
+void CSoftAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo devInfo = *itt2;
+ if (passthrough && devInfo.m_deviceType == AE_DEVTYPE_PCM)
+ continue;
+
+ std::string device = sinkInfo.m_sinkName + ":" + devInfo.m_deviceName;
+
+ std::stringstream ss;
+
+ /* add the sink name if we have more then one sink type */
+ if (m_sinkInfoList.size() > 1)
+ ss << sinkInfo.m_sinkName << ": ";
+
+ ss << devInfo.m_displayName;
+ if (!devInfo.m_displayNameExtra.empty())
+ ss << ", " << devInfo.m_displayNameExtra;
+
+ devices.push_back(AEDevice(ss.str(), device));
+ }
+ }
+}
+
+std::string CSoftAE::GetDefaultDevice(bool passthrough)
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ AESinkInfo sinkInfo = *itt;
+ for (AEDeviceInfoList::iterator itt2 = sinkInfo.m_deviceInfoList.begin(); itt2 != sinkInfo.m_deviceInfoList.end(); ++itt2)
+ {
+ CAEDeviceInfo devInfo = *itt2;
+ if (passthrough && devInfo.m_deviceType == AE_DEVTYPE_PCM)
+ continue;
+
+ std::string device = sinkInfo.m_sinkName + ":" + devInfo.m_deviceName;
+ return device;
+ }
+ }
+ return "default";
+}
+
+bool CSoftAE::SupportsRaw()
+{
+ /* CSoftAE supports raw formats */
+ return true;
+}
+
+void CSoftAE::PauseStream(CSoftAEStream *stream)
+{
+ CSingleLock streamLock(m_streamLock);
+ RemoveStream(m_playingStreams, stream);
+ stream->m_paused = true;
+ streamLock.Leave();
+
+ OpenSink();
+}
+
+void CSoftAE::ResumeStream(CSoftAEStream *stream)
+{
+ CSingleLock streamLock(m_streamLock);
+ m_playingStreams.push_back(stream);
+ stream->m_paused = false;
+ streamLock.Leave();
+
+ m_streamsPlaying = true;
+ OpenSink();
+}
+
+void CSoftAE::Stop()
+{
+ m_running = false;
+
+ /* wait for the thread to stop */
+ CSingleLock lock(m_runningLock);
+}
+
+void CSoftAE::SetSoundMode(const int mode)
+{
+ m_soundMode = mode;
+
+ /* stop all currently playing sounds if they are being turned off */
+ if (mode == AE_SOUND_OFF || (mode == AE_SOUND_IDLE && m_streamsPlaying))
+ StopAllSounds();
+}
+
+IAEStream *CSoftAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options/* = 0 */)
+{
+ CAEChannelInfo channelInfo(channelLayout);
+ CLog::Log(LOGINFO, "CSoftAE::MakeStream - %s, %u, %s",
+ CAEUtil::DataFormatToStr(dataFormat),
+ sampleRate, ((std::string)channelInfo).c_str());
+
+ /* ensure we have the encoded sample rate if the format is RAW */
+ if (AE_IS_RAW(dataFormat))
+ ASSERT(encodedSampleRate);
+
+ CSingleLock streamLock(m_streamLock);
+ CSoftAEStream *stream = new CSoftAEStream(dataFormat, sampleRate, encodedSampleRate, channelLayout, options);
+ m_newStreams.push_back(stream);
+ streamLock.Leave();
+
+ OpenSink();
+ return stream;
+}
+
+IAESound *CSoftAE::MakeSound(const std::string& file)
+{
+ CSingleLock soundLock(m_soundLock);
+
+ CSoftAESound *sound = new CSoftAESound(file);
+ if (!sound->Initialize())
+ {
+ delete sound;
+ return NULL;
+ }
+
+ m_sounds.push_back(sound);
+ return sound;
+}
+
+void CSoftAE::PlaySound(IAESound *sound)
+{
+ if (m_soundMode == AE_SOUND_OFF || (m_soundMode == AE_SOUND_IDLE && m_streamsPlaying))
+ return;
+
+ float *samples = ((CSoftAESound*)sound)->GetSamples();
+ if (!samples)
+ return;
+
+ /* add the sound to the play list */
+ CSingleLock soundSampleLock(m_soundSampleLock);
+ SoundState ss = {
+ ((CSoftAESound*)sound),
+ samples,
+ ((CSoftAESound*)sound)->GetSampleCount()
+ };
+ m_playing_sounds.push_back(ss);
+}
+
+void CSoftAE::FreeSound(IAESound *sound)
+{
+ if (!sound)
+ return;
+
+ sound->Stop();
+ CSingleLock soundLock(m_soundLock);
+ for (SoundList::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
+ if (*itt == sound)
+ {
+ m_sounds.erase(itt);
+ break;
+ }
+
+ delete (CSoftAESound*)sound;
+}
+
+void CSoftAE::GarbageCollect()
+{
+}
+
+unsigned int CSoftAE::GetSampleRate()
+{
+ if (m_transcode && m_encoder && !m_rawPassthrough)
+ return m_encoderFormat.m_sampleRate;
+
+ return m_sinkFormat.m_sampleRate;
+}
+
+void CSoftAE::StopSound(IAESound *sound)
+{
+ CSingleLock lock(m_soundSampleLock);
+ for (SoundStateList::iterator itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
+ {
+ if ((*itt).owner == sound)
+ {
+ (*itt).owner->ReleaseSamples();
+ itt = m_playing_sounds.erase(itt);
+ }
+ else
+ ++itt;
+ }
+}
+
+IAEStream *CSoftAE::FreeStream(IAEStream *stream)
+{
+ CSingleLock lock(m_streamLock);
+ RemoveStream(m_playingStreams, (CSoftAEStream*)stream);
+ RemoveStream(m_streams , (CSoftAEStream*)stream);
+ lock.Leave();
+
+ /* if it was the master stream we need to reopen before deletion */
+ if (m_masterStream == stream)
+ OpenSink();
+
+ delete (CSoftAEStream*)stream;
+ return NULL;
+}
+
+double CSoftAE::GetDelay()
+{
+ CSharedLock sinkLock(m_sinkLock);
+
+ double delay = m_sink->GetDelay();
+ if (m_transcode && m_encoder && !m_rawPassthrough)
+ delay += m_encoder->GetDelay((double)m_encodedBuffer.Used() * m_encoderFrameSizeMul);
+ double buffered = (double)m_buffer.Used() * m_sinkFormatFrameSizeMul;
+
+ return delay + (buffered * m_sinkFormatSampleRateMul);
+}
+
+double CSoftAE::GetCacheTime()
+{
+ CSharedLock sinkLock(m_sinkLock);
+
+ double time;
+ time = (double)m_buffer.Free() * m_sinkFormatFrameSizeMul * m_sinkFormatSampleRateMul;
+ time += m_sink->GetCacheTime();
+
+ return time;
+}
+
+double CSoftAE::GetCacheTotal()
+{
+ CSharedLock sinkLock(m_sinkLock);
+
+ double total;
+ total = (double)m_buffer.Size() * m_sinkFormatFrameSizeMul * m_sinkFormatSampleRateMul;
+ total += m_sink->GetCacheTotal();
+
+ return total;
+}
+
+float CSoftAE::GetVolume()
+{
+ return m_volume;
+}
+
+void CSoftAE::SetVolume(float volume)
+{
+ m_volume = volume;
+}
+
+void CSoftAE::StopAllSounds()
+{
+ CSingleLock lock(m_soundSampleLock);
+ while (!m_playing_sounds.empty())
+ {
+ SoundState *ss = &(*m_playing_sounds.begin());
+ ss->owner->ReleaseSamples();
+ m_playing_sounds.pop_front();
+ }
+}
+
+void CSoftAE::Run()
+{
+ /* we release this when we exit the thread unblocking anyone waiting on "Stop" */
+ CSingleLock runningLock(m_runningLock);
+ CLog::Log(LOGINFO, "CSoftAE::Run - Thread Started");
+
+ while (m_running)
+ {
+ bool restart = false;
+
+ (this->*m_outputStageFn)();
+
+ /* if we have enough room in the buffer */
+ if (m_buffer.Free() >= m_frameSize)
+ {
+ /* take some data for our use from the buffer */
+ uint8_t *out = (uint8_t*)m_buffer.Take(m_frameSize);
+ memset(out, 0, m_frameSize);
+
+ /* run the stream stage */
+ CSoftAEStream *oldMaster = m_masterStream;
+ (this->*m_streamStageFn)(m_chLayout.Count(), out, restart);
+
+ /* if in audiophile mode and the master stream has changed, flag for restart */
+ if (m_audiophile && oldMaster != m_masterStream)
+ restart = true;
+ }
+
+ /* if we are told to restart */
+ if (m_reOpen || restart)
+ {
+ CLog::Log(LOGDEBUG, "CSoftAE::Run - Sink restart flagged");
+ InternalOpenSink();
+ }
+ }
+}
+
+void CSoftAE::MixSounds(float *buffer, unsigned int samples)
+{
+ SoundStateList::iterator itt;
+
+ CSingleLock lock(m_soundSampleLock);
+ for (itt = m_playing_sounds.begin(); itt != m_playing_sounds.end(); )
+ {
+ SoundState *ss = &(*itt);
+
+ /* no more frames, so remove it from the list */
+ if (ss->sampleCount == 0)
+ {
+ ss->owner->ReleaseSamples();
+ itt = m_playing_sounds.erase(itt);
+ continue;
+ }
+
+ float volume = ss->owner->GetVolume();
+ unsigned int mixSamples = std::min(ss->sampleCount, samples);
+
+ #ifdef __SSE__
+ CAEUtil::SSEMulAddArray(buffer, ss->samples, volume, mixSamples);
+ #else
+ for (unsigned int i = 0; i < mixSamples; ++i)
+ buffer[i] = (buffer[i] + (ss->samples[i] * volume));
+ #endif
+
+ ss->sampleCount -= mixSamples;
+ ss->samples += mixSamples;
+
+ ++itt;
+ }
+}
+
+void CSoftAE::FinalizeSamples(float *buffer, unsigned int samples)
+{
+ if (m_soundMode != AE_SOUND_OFF)
+ MixSounds(buffer, samples);
+
+ if (m_muted)
+ {
+ memset(buffer, 0, samples * sizeof(float));
+ return;
+ }
+
+ /* deamplify */
+ bool clamp = false;
+ if (m_volume < 1.0)
+ {
+ #ifdef __SSE__
+ CAEUtil::SSEMulArray(buffer, m_volume, samples);
+ for (unsigned int i = 0; i < samples; ++i)
+ if (buffer[i] < -1.0f || buffer[i] > 1.0f)
+ {
+ clamp = true;
+ break;
+ }
+ #else
+ for (unsigned int i = 0; i < samples; ++i)
+ {
+ buffer[i] *= m_volume;
+ if (!clamp && (buffer[i] < -1.0f || buffer[i] > 1.0f))
+ clamp = true;
+ }
+ #endif
+ }
+ else
+ {
+ for (unsigned int i = 0; i < samples; ++i)
+ if (buffer[i] < -1.0f || buffer[i] > 1.0f)
+ {
+ clamp = true;
+ break;
+ }
+ }
+
+ /* if there were no samples outside of the range, dont clamp the buffer */
+ if (!clamp)
+ return;
+
+ CLog::Log(LOGDEBUG, "CSoftAE::FinalizeSamples - Clamping buffer of %d samples", samples);
+ CAEUtil::ClampArray(buffer, samples);
+}
+
+void CSoftAE::RunOutputStage()
+{
+ const unsigned int needSamples = m_sinkFormat.m_frames * m_sinkFormat.m_channelLayout.Count();
+ const size_t needBytes = needSamples * sizeof(float);
+ if (m_buffer.Used() < needBytes)
+ return;
+
+ void *data = m_buffer.Raw(needBytes);
+ FinalizeSamples((float*)data, needSamples);
+
+ int wroteFrames;
+ if (m_convertFn)
+ {
+ const unsigned int convertedBytes = m_sinkFormat.m_frames * m_sinkFormat.m_frameSize;
+ if (m_convertedSize < convertedBytes)
+ {
+ _aligned_free(m_converted);
+ m_converted = (uint8_t *)_aligned_malloc(convertedBytes, 16);
+ m_convertedSize = convertedBytes;
+ }
+ m_convertFn((float*)data, needSamples, m_converted);
+ data = m_converted;
+ }
+
+ wroteFrames = m_sink->AddPackets((uint8_t*)data, m_sinkFormat.m_frames);
+ m_buffer.Shift(NULL, wroteFrames * m_sinkFormat.m_channelLayout.Count() * sizeof(float));
+}
+
+void CSoftAE::RunRawOutputStage()
+{
+ if(m_buffer.Used() < m_sinkBlockSize)
+ return;
+
+ int wroteFrames = m_sink->AddPackets((uint8_t*)m_buffer.Raw(m_sinkBlockSize), m_sinkFormat.m_frames);
+ m_buffer.Shift(NULL, wroteFrames * m_sinkFormat.m_frameSize);
+}
+
+void CSoftAE::RunTranscodeStage()
+{
+ /* if we dont have enough samples to encode yet, return */
+ unsigned int block = m_encoderFormat.m_frames * m_encoderFormat.m_frameSize;
+ unsigned int sinkBlock = m_sinkFormat.m_frames * m_sinkFormat.m_frameSize;
+
+ if (m_buffer.Used() >= block && m_encodedBuffer.Used() < sinkBlock * 2)
+ {
+ FinalizeSamples((float*)m_buffer.Raw(block), m_encoderFormat.m_frameSamples);
+
+ void *buffer;
+ if (m_convertFn)
+ {
+ unsigned int newsize = m_encoderFormat.m_frames * m_encoderFormat.m_frameSize;
+ if (m_convertedSize < newsize)
+ {
+ _aligned_free(m_converted);
+ m_converted = (uint8_t *)_aligned_malloc(newsize, 16);
+ m_convertedSize = newsize;
+ }
+ m_convertFn(
+ (float*)m_buffer.Raw(block),
+ m_encoderFormat.m_frames * m_encoderFormat.m_channelLayout.Count(),
+ m_converted
+ );
+ buffer = m_converted;
+ }
+ else
+ buffer = m_buffer.Raw(block);
+
+ int encodedFrames = m_encoder->Encode((float*)buffer, m_encoderFormat.m_frames);
+ m_buffer.Shift(NULL, encodedFrames * m_encoderFormat.m_frameSize);
+
+ uint8_t *packet;
+ unsigned int size = m_encoder->GetData(&packet);
+
+ /* if there is not enough space for another encoded packet enlarge the buffer */
+ if (m_encodedBuffer.Free() < size)
+ m_encodedBuffer.ReAlloc(m_encodedBuffer.Used() + size);
+
+ m_encodedBuffer.Push(packet, size);
+ }
+
+ /* if we have enough data to write */
+ if (m_encodedBuffer.Used() >= sinkBlock)
+ {
+ int wroteFrames = m_sink->AddPackets((uint8_t*)m_encodedBuffer.Raw(sinkBlock), m_sinkFormat.m_frames);
+ m_encodedBuffer.Shift(NULL, wroteFrames * m_sinkFormat.m_frameSize);
+ }
+}
+
+unsigned int CSoftAE::RunRawStreamStage(unsigned int channelCount, void *out, bool &restart)
+{
+ StreamList resumeStreams;
+ static StreamList::iterator itt;
+ CSingleLock streamLock(m_streamLock);
+
+ /* handle playing streams */
+ for (itt = m_playingStreams.begin(); itt != m_playingStreams.end(); ++itt)
+ {
+ CSoftAEStream *sitt = *itt;
+ if (sitt == m_masterStream)
+ continue;
+
+ /* consume data from streams even though we cant use it */
+ uint8_t *frame = sitt->GetFrame();
+
+ /* flag the stream's slave to be resumed if it has drained */
+ if (!frame && sitt->IsDrained() && sitt->m_slave && sitt->m_slave->IsPaused())
+ resumeStreams.push_back(sitt);
+ }
+
+ /* nothing to do if we dont have a master stream */
+ if (!m_masterStream)
+ return 0;
+
+ /* get the frame and append it to the output */
+ uint8_t *frame = m_masterStream->GetFrame();
+ unsigned int mixed;
+ if (frame)
+ {
+ mixed = 1;
+ memcpy(out, frame, m_sinkFormat.m_frameSize);
+ }
+ else
+ {
+ mixed = 0;
+ if (m_masterStream->IsDrained() && m_masterStream->m_slave && m_masterStream->m_slave->IsPaused())
+ resumeStreams.push_back(m_masterStream);
+ }
+
+ ResumeSlaveStreams(resumeStreams);
+ return mixed;
+}
+
+unsigned int CSoftAE::RunStreamStage(unsigned int channelCount, void *out, bool &restart)
+{
+ StreamList resumeStreams;
+ static StreamList::iterator itt;
+
+ float *dst = (float*)out;
+ unsigned int mixed = 0;
+
+ /* identify the master stream */
+ CSingleLock streamLock(m_streamLock);
+
+ /* mix in any running streams */
+ for (itt = m_playingStreams.begin(); itt != m_playingStreams.end(); ++itt)
+ {
+ CSoftAEStream *stream = *itt;
+
+ float *frame = (float*)stream->GetFrame();
+ if (!frame && stream->IsDrained() && stream->m_slave && stream->m_slave->IsPaused())
+ resumeStreams.push_back(stream);
+
+ if (!frame)
+ continue;
+
+ float volume = stream->GetVolume() * stream->GetReplayGain();
+ #ifdef __SSE__
+ if (channelCount > 1)
+ CAEUtil::SSEMulAddArray(dst, frame, volume, channelCount);
+ else
+ #endif
+ {
+ /* unrolled loop for performance */
+ unsigned int blocks = channelCount & ~0x3;
+ unsigned int i = 0;
+ for (i = 0; i < blocks; i += 4)
+ {
+ dst[i+0] += frame[i+0] * volume;
+ dst[i+1] += frame[i+1] * volume;
+ dst[i+2] += frame[i+2] * volume;
+ dst[i+3] += frame[i+3] * volume;
+ }
+
+ switch (channelCount & 0x3)
+ {
+ case 3: dst[i] += frame[i] * volume; ++i;
+ case 2: dst[i] += frame[i] * volume; ++i;
+ case 1: dst[i] += frame[i] * volume;
+ }
+ }
+
+ ++mixed;
+ }
+
+ ResumeSlaveStreams(resumeStreams);
+ return mixed;
+}
+
+inline void CSoftAE::ResumeSlaveStreams(const StreamList &streams)
+{
+ if (streams.empty())
+ return;
+
+ /* resume any streams that need to be */
+ for (StreamList::const_iterator itt = streams.begin(); itt != streams.end(); ++itt)
+ {
+ CSoftAEStream *stream = *itt;
+ m_playingStreams.push_back(stream->m_slave);
+ stream->m_slave->m_paused = false;
+ stream->m_slave = NULL;
+ }
+}
+
+inline void CSoftAE::RemoveStream(StreamList &streams, CSoftAEStream *stream)
+{
+ StreamList::iterator f = std::find(streams.begin(), streams.end(), stream);
+ if (f != streams.end())
+ streams.erase(f);
+
+ if (streams == m_playingStreams)
+ m_streamsPlaying = !m_playingStreams.empty();
+}
+
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE.h b/xbmc/cores/AudioEngine/Engines/SoftAE.h
new file mode 100644
index 0000000000..7f4420b045
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAE.h
@@ -0,0 +1,215 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <list>
+#include <vector>
+#include <map>
+
+#include "system.h"
+#include "threads/Thread.h"
+#include "threads/CriticalSection.h"
+#include "threads/SharedSection.h"
+
+#include "Interfaces/ThreadedAE.h"
+#include "Interfaces/AESink.h"
+#include "Interfaces/AEEncoder.h"
+#include "Utils/AEConvert.h"
+#include "Utils/AERemap.h"
+#include "Utils/AEBuffer.h"
+#include "AEAudioFormat.h"
+#include "AESinkFactory.h"
+
+#include "SoftAEStream.h"
+#include "SoftAESound.h"
+
+#include "cores/IAudioCallback.h"
+
+/* forward declarations */
+class IThreadedAE;
+class CSoftAEStream;
+class CSoftAESound;
+class IAESink;
+
+class CSoftAE : public IThreadedAE
+{
+protected:
+ friend class CAEFactory;
+ CSoftAE();
+ virtual ~CSoftAE();
+
+public:
+ virtual void Shutdown();
+ virtual bool Initialize();
+ virtual void OnSettingsChange(std::string setting);
+
+ virtual void Run();
+ virtual void Stop();
+ virtual double GetDelay();
+
+ virtual float GetVolume();
+ virtual void SetVolume(const float volume);
+ virtual void SetMute(const bool enabled) { m_muted = enabled; }
+ virtual bool IsMuted() { return m_muted; }
+ virtual void SetSoundMode(const int mode);
+
+ /* returns a new stream for data in the specified format */
+ virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options = 0);
+ virtual IAEStream *FreeStream(IAEStream *stream);
+
+ /* returns a new sound object */
+ virtual IAESound *MakeSound(const std::string& file);
+ virtual void FreeSound(IAESound *sound);
+ void PlaySound(IAESound *sound);
+ void StopSound(IAESound *sound);
+
+ /* free's sounds that have expired */
+ virtual void GarbageCollect();
+
+ /* these are for the streams so they can provide compatible data */
+ unsigned int GetSampleRate ();
+ unsigned int GetChannelCount () {return m_chLayout.Count() ;}
+ CAEChannelInfo& GetChannelLayout() {return m_chLayout ;}
+ enum AEStdChLayout GetStdChLayout () {return m_stdChLayout ;}
+ unsigned int GetFrames () {return m_sinkFormat.m_frames ;}
+ unsigned int GetFrameSize () {return m_frameSize ;}
+
+ /* these are for streams that are in RAW mode */
+ const AEAudioFormat* GetSinkAudioFormat() {return &m_sinkFormat ;}
+ enum AEDataFormat GetSinkDataFormat () {return m_sinkFormat.m_dataFormat ;}
+ CAEChannelInfo& GetSinkChLayout () {return m_sinkFormat.m_channelLayout;}
+ unsigned int GetSinkFrameSize () {return m_sinkFormat.m_frameSize ;}
+
+ /* for streams so they can calc cachetimes correct */
+ double GetCacheTime();
+ double GetCacheTotal();
+
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
+ virtual std::string GetDefaultDevice(bool passthrough);
+ virtual bool SupportsRaw();
+
+ /* internal stream methods */
+ void PauseStream (CSoftAEStream *stream);
+ void ResumeStream(CSoftAEStream *stream);
+
+private:
+ CThread *m_thread;
+
+ CSoftAEStream *GetMasterStream();
+
+ void LoadSettings();
+ void VerifySoundDevice(std::string &device, bool passthrough);
+ void OpenSink();
+
+ void InternalOpenSink();
+ void ResetEncoder();
+ bool SetupEncoder(AEAudioFormat &format);
+ void Deinitialize();
+
+ IAESink *GetSink(AEAudioFormat &desiredFormat, bool passthrough, std::string &device);
+ void StopAllSounds();
+
+ enum AEStdChLayout m_stdChLayout;
+ std::string m_device;
+ std::string m_passthroughDevice;
+ bool m_audiophile;
+ bool m_stereoUpmix;
+
+ /* internal vars */
+ bool m_running, m_reOpen;
+ CEvent m_reOpenEvent;
+
+ CCriticalSection m_runningLock; /* released when the thread exits */
+ CCriticalSection m_streamLock; /* m_streams lock */
+ CCriticalSection m_soundLock; /* m_sounds lock */
+ CCriticalSection m_soundSampleLock; /* m_playing_sounds lock */
+ CSharedSection m_sinkLock; /* lock for m_sink on re-open */
+
+ /* the current configuration */
+ float m_volume;
+ bool m_muted;
+ CAEChannelInfo m_chLayout;
+ unsigned int m_frameSize;
+
+ /* the sink, its format information, and conversion function */
+ AESinkInfoList m_sinkInfoList;
+ IAESink *m_sink;
+ AEAudioFormat m_sinkFormat;
+ float m_sinkFormatSampleRateMul;
+ float m_sinkFormatFrameSizeMul;
+ unsigned int m_sinkBlockSize;
+ AEAudioFormat m_encoderFormat;
+ float m_encoderFrameSizeMul;
+ unsigned int m_bytesPerSample;
+ CAEConvert::AEConvertFrFn m_convertFn;
+
+ /* currently playing sounds */
+ typedef struct {
+ CSoftAESound *owner;
+ float *samples;
+ unsigned int sampleCount;
+ } SoundState;
+
+ typedef std::vector<CSoftAEStream*> StreamList;
+ typedef std::list <CSoftAESound* > SoundList;
+ typedef std::list <SoundState > SoundStateList;
+
+ /* the streams, sounds, output buffer and output buffer fill size */
+ bool m_transcode;
+ bool m_rawPassthrough;
+ StreamList m_newStreams, m_streams, m_playingStreams;
+ SoundList m_sounds;
+ SoundStateList m_playing_sounds;
+ int m_soundMode;
+ bool m_streamsPlaying;
+
+ /* this will contain either float, or uint8_t depending on if we are in raw mode or not */
+ CAEBuffer m_buffer;
+
+ /* the encoder */
+ IAEEncoder *m_encoder;
+ CAEBuffer m_encodedBuffer;
+
+ /* the output conversion buffer */
+ uint8_t *m_converted;
+ size_t m_convertedSize;
+
+ /* thread run stages */
+ void MixSounds (float *buffer, unsigned int samples);
+ void FinalizeSamples (float *buffer, unsigned int samples);
+
+ CSoftAEStream *m_masterStream;
+
+ void (CSoftAE::*m_outputStageFn)();
+ void RunOutputStage ();
+ void RunRawOutputStage();
+ void RunTranscodeStage();
+
+ unsigned int (CSoftAE::*m_streamStageFn)(unsigned int channelCount, void *out, bool &restart);
+ unsigned int RunRawStreamStage (unsigned int channelCount, void *out, bool &restart);
+ unsigned int RunStreamStage (unsigned int channelCount, void *out, bool &restart);
+
+ void ResumeSlaveStreams(const StreamList &streams);
+ void RunNormalizeStage (unsigned int channelCount, void *out, unsigned int mixed);
+
+ void RemoveStream(StreamList &streams, CSoftAEStream *stream);
+};
+
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAESound.cpp b/xbmc/cores/AudioEngine/Engines/SoftAESound.cpp
new file mode 100644
index 0000000000..028c3c8006
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAESound.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Interfaces/AESound.h"
+
+#include <samplerate.h>
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/EndianSwap.h"
+
+#include "AEFactory.h"
+#include "AEAudioFormat.h"
+#include "Utils/AEConvert.h"
+#include "Utils/AERemap.h"
+#include "Utils/AEUtil.h"
+
+#include "SoftAE.h"
+#include "SoftAESound.h"
+
+/* typecast AE to CSoftAE */
+#define AE (*((CSoftAE*)CAEFactory::AE))
+
+typedef struct
+{
+ char chunk_id[4];
+ uint32_t chunksize;
+} WAVE_CHUNK;
+
+CSoftAESound::CSoftAESound(const std::string &filename) :
+ IAESound (filename),
+ m_filename (filename),
+ m_volume (1.0f ),
+ m_inUse (0 )
+{
+}
+
+CSoftAESound::~CSoftAESound()
+{
+ DeInitialize();
+}
+
+void CSoftAESound::DeInitialize()
+{
+ m_wavLoader.DeInitialize();
+}
+
+bool CSoftAESound::Initialize()
+{
+ DeInitialize();
+
+ if (!m_wavLoader.Initialize(m_filename, AE.GetSampleRate()))
+ return false;
+
+ return m_wavLoader.Remap(AE.GetChannelLayout(), AE.GetStdChLayout());
+}
+
+unsigned int CSoftAESound::GetSampleCount()
+{
+ CSingleLock cs(m_critSection);
+ if (m_wavLoader.IsValid())
+ return m_wavLoader.GetSampleCount();
+ return 0;
+}
+
+float* CSoftAESound::GetSamples()
+{
+ CSingleLock cs(m_critSection);
+ if (!m_wavLoader.IsValid())
+ return NULL;
+
+ ++m_inUse;
+ return m_wavLoader.GetSamples();
+}
+
+void CSoftAESound::ReleaseSamples()
+{
+ CSingleLock cs(m_critSection);
+ ASSERT(m_inUse > 0);
+ --m_inUse;
+}
+
+bool CSoftAESound::IsPlaying()
+{
+ CSingleLock cs(m_critSection);
+ return (m_inUse > 0);
+}
+
+void CSoftAESound::Play()
+{
+ AE.PlaySound(this);
+}
+
+void CSoftAESound::Stop()
+{
+ AE.StopSound(this);
+}
+
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAESound.h b/xbmc/cores/AudioEngine/Engines/SoftAESound.h
new file mode 100644
index 0000000000..af64ba36fa
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAESound.h
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils/StdString.h"
+#include "threads/CriticalSection.h"
+#include "threads/SharedSection.h"
+#include "Interfaces/AESound.h"
+#include "Utils/AEWAVLoader.h"
+
+class CWAVLoader;
+class CSoftAESound : public IAESound
+{
+public:
+ CSoftAESound (const std::string &filename);
+ virtual ~CSoftAESound();
+
+ virtual void DeInitialize();
+ virtual bool Initialize();
+
+ virtual void Play();
+ virtual void Stop();
+ virtual bool IsPlaying();
+
+ virtual void SetVolume(float volume) { m_volume = std::max(0.0f, std::min(1.0f, volume)); }
+ virtual float GetVolume() { return m_volume ; }
+
+ unsigned int GetSampleCount();
+
+ /* ReleaseSamples must be called for each time GetSamples has been called */
+ virtual float* GetSamples ();
+ void ReleaseSamples();
+private:
+ CCriticalSection m_critSection;
+ std::string m_filename;
+ CAEWAVLoader m_wavLoader;
+ float m_volume;
+ int m_inUse;
+};
+
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAEStream.cpp b/xbmc/cores/AudioEngine/Engines/SoftAEStream.cpp
new file mode 100644
index 0000000000..35ba062184
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAEStream.cpp
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+
+#include "AEFactory.h"
+#include "Utils/AEUtil.h"
+
+#include "SoftAE.h"
+#include "SoftAEStream.h"
+
+/* typecast AE to CSoftAE */
+#define AE (*((CSoftAE*)CAEFactory::AE))
+
+using namespace std;
+
+CSoftAEStream::CSoftAEStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options) :
+ m_resampleRatio (1.0 ),
+ m_internalRatio (1.0 ),
+ m_convertBuffer (NULL ),
+ m_valid (false),
+ m_delete (false),
+ m_volume (1.0f ),
+ m_rgain (1.0f ),
+ m_refillBuffer (0 ),
+ m_convertFn (NULL ),
+ m_ssrc (NULL ),
+ m_framesBuffered (0 ),
+ m_newPacket (NULL ),
+ m_packet (NULL ),
+ m_vizPacketPos (NULL ),
+ m_draining (false),
+ m_vizBufferSamples(0 ),
+ m_audioCallback (NULL ),
+ m_fadeRunning (false),
+ m_slave (NULL )
+{
+ m_ssrcData.data_out = NULL;
+
+ m_initDataFormat = dataFormat;
+ m_initSampleRate = sampleRate;
+ m_initEncodedSampleRate = encodedSampleRate;
+ m_initChannelLayout = channelLayout;
+ m_chLayoutCount = channelLayout.Count();
+ m_forceResample = (options & AESTREAM_FORCE_RESAMPLE) != 0;
+ m_paused = (options & AESTREAM_PAUSED) != 0;
+ m_autoStart = (options & AESTREAM_AUTOSTART) != 0;
+
+ if (m_autoStart)
+ m_paused = true;
+
+ ASSERT(m_initChannelLayout.Count());
+}
+
+void CSoftAEStream::InitializeRemap()
+{
+ CExclusiveLock lock(m_lock);
+ if (!AE_IS_RAW(m_initDataFormat))
+ {
+ /* re-init the remappers */
+ m_remap .Initialize(m_initChannelLayout, AE.GetChannelLayout() , false, false, AE.GetStdChLayout());
+ m_vizRemap.Initialize(m_initChannelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
+
+ /*
+ if the layout has changed we need to drop data that was already remapped
+ */
+ if (AE.GetChannelLayout() != m_aeChannelLayout)
+ {
+ InternalFlush();
+ m_aeChannelLayout = AE.GetChannelLayout();
+ m_samplesPerFrame = AE.GetChannelLayout().Count();
+ m_aeBytesPerFrame = AE_IS_RAW(m_initDataFormat) ? m_bytesPerFrame : (m_samplesPerFrame * sizeof(float));
+ }
+ }
+}
+
+void CSoftAEStream::Initialize()
+{
+ CExclusiveLock lock(m_lock);
+ if (m_valid)
+ {
+ InternalFlush();
+ delete m_newPacket;
+
+ if (m_convert)
+ _aligned_free(m_convertBuffer);
+
+ if (m_resample)
+ {
+ _aligned_free(m_ssrcData.data_out);
+ m_ssrcData.data_out = NULL;
+ }
+ }
+
+ enum AEDataFormat useDataFormat = m_initDataFormat;
+ if (AE_IS_RAW(m_initDataFormat))
+ {
+ /* we are raw, which means we need to work in the output format */
+ useDataFormat = AE.GetSinkDataFormat();
+ m_initChannelLayout = AE.GetSinkChLayout ();
+ m_samplesPerFrame = m_initChannelLayout.Count();
+ }
+ else
+ {
+ if (!m_initChannelLayout.Count())
+ {
+ m_valid = false;
+ return;
+ }
+ m_samplesPerFrame = AE.GetChannelLayout().Count();
+ }
+
+ m_bytesPerSample = (CAEUtil::DataFormatToBits(useDataFormat) >> 3);
+ m_bytesPerFrame = m_bytesPerSample * m_initChannelLayout.Count();
+
+ m_aeChannelLayout = AE.GetChannelLayout();
+ m_aeBytesPerFrame = AE_IS_RAW(m_initDataFormat) ? m_bytesPerFrame : (m_samplesPerFrame * sizeof(float));
+ m_waterLevel = AE.GetSampleRate() / 2;
+ m_refillBuffer = m_waterLevel;
+
+ m_format.m_dataFormat = useDataFormat;
+ m_format.m_sampleRate = m_initSampleRate;
+ m_format.m_encodedRate = m_initEncodedSampleRate;
+ m_format.m_channelLayout = m_initChannelLayout;
+ m_format.m_frames = m_initSampleRate / 8;
+ m_format.m_frameSamples = m_format.m_frames * m_initChannelLayout.Count();
+ m_format.m_frameSize = m_bytesPerFrame;
+
+ m_newPacket = new PPacket();
+ if (AE_IS_RAW(m_initDataFormat))
+ m_newPacket->data.Alloc(m_format.m_frames * m_format.m_frameSize);
+ else
+ {
+ if (
+ !m_remap .Initialize(m_initChannelLayout, m_aeChannelLayout , false, false, AE.GetStdChLayout()) ||
+ !m_vizRemap.Initialize(m_initChannelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true))
+ {
+ m_valid = false;
+ return;
+ }
+
+ m_newPacket->data.Alloc(m_format.m_frameSamples * sizeof(float));
+ }
+
+ m_packet = NULL;
+
+ m_inputBuffer.Alloc(m_format.m_frames * m_format.m_frameSize);
+
+ m_resample = (m_forceResample || m_initSampleRate != AE.GetSampleRate()) && !AE_IS_RAW(m_initDataFormat);
+ m_convert = m_initDataFormat != AE_FMT_FLOAT && !AE_IS_RAW(m_initDataFormat);
+
+ /* if we need to convert, set it up */
+ if (m_convert)
+ {
+ /* get the conversion function and allocate a buffer for the data */
+ CLog::Log(LOGDEBUG, "CSoftAEStream::CSoftAEStream - Converting from %s to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(m_initDataFormat));
+ m_convertFn = CAEConvert::ToFloat(m_initDataFormat);
+ if (m_convertFn)
+ m_convertBuffer = (float*)_aligned_malloc(m_format.m_frameSamples * sizeof(float), 16);
+ else
+ m_valid = false;
+ }
+ else
+ m_convertBuffer = (float*)m_inputBuffer.Raw(m_format.m_frames * m_format.m_frameSize);
+
+ /* if we need to resample, set it up */
+ if (m_resample)
+ {
+ int err;
+ m_ssrc = src_new(SRC_SINC_MEDIUM_QUALITY, m_initChannelLayout.Count(), &err);
+ m_ssrcData.data_in = m_convertBuffer;
+ m_internalRatio = (double)AE.GetSampleRate() / (double)m_initSampleRate;
+ m_ssrcData.src_ratio = m_internalRatio;
+ m_ssrcData.data_out = (float*)_aligned_malloc(m_format.m_frameSamples * std::ceil(m_ssrcData.src_ratio) * sizeof(float), 16);
+ m_ssrcData.output_frames = m_format.m_frames * std::ceil(m_ssrcData.src_ratio);
+ m_ssrcData.end_of_input = 0;
+ }
+
+ m_chLayoutCount = m_format.m_channelLayout.Count();
+ m_valid = true;
+}
+
+void CSoftAEStream::Destroy()
+{
+ CExclusiveLock lock(m_lock);
+ m_valid = false;
+ m_delete = true;
+}
+
+CSoftAEStream::~CSoftAEStream()
+{
+ CExclusiveLock lock(m_lock);
+
+ InternalFlush();
+ if (m_convert)
+ _aligned_free(m_convertBuffer);
+
+ if (m_resample)
+ {
+ _aligned_free(m_ssrcData.data_out);
+ src_delete(m_ssrc);
+ m_ssrc = NULL;
+ }
+
+ CLog::Log(LOGDEBUG, "CSoftAEStream::~CSoftAEStream - Destructed");
+}
+
+unsigned int CSoftAEStream::GetSpace()
+{
+ if (!m_valid || m_draining)
+ return 0;
+
+ if (m_framesBuffered >= m_waterLevel)
+ return 0;
+
+ return m_inputBuffer.Free() + (std::max(0U, (m_waterLevel - m_framesBuffered)) * m_format.m_frameSize);
+}
+
+unsigned int CSoftAEStream::AddData(void *data, unsigned int size)
+{
+ CExclusiveLock lock(m_lock);
+ if (!m_valid || size == 0 || data == NULL)
+ return 0;
+
+ /* if the stream is draining */
+ if (m_draining)
+ {
+ /* if the stream has finished draining, cork it */
+ if (m_packet && !m_packet->data.Used() && m_outBuffer.empty())
+ m_draining = false;
+ else
+ return 0;
+ }
+
+ /* dont ever take more then GetSpace advertises */
+ size = std::min(size, GetSpace());
+ if (size == 0)
+ return 0;
+
+ unsigned int taken = 0;
+ while(size)
+ {
+ unsigned int copy = std::min((unsigned int)m_inputBuffer.Free(), size);
+ if (copy > 0)
+ {
+ m_inputBuffer.Push(data, copy);
+ size -= copy;
+ taken += copy;
+ data = (uint8_t*)data + copy;
+ }
+
+ if (m_inputBuffer.Free() == 0)
+ {
+ unsigned int consumed = ProcessFrameBuffer();
+ m_inputBuffer.Shift(NULL, consumed);
+ }
+ }
+
+ lock.Leave();
+
+ /* if the stream is flagged to autoStart when the buffer is full, then do it */
+ if (m_autoStart && m_framesBuffered >= m_waterLevel)
+ Resume();
+
+ return taken;
+}
+
+unsigned int CSoftAEStream::ProcessFrameBuffer()
+{
+ uint8_t *data;
+ unsigned int frames, consumed, sampleSize;
+
+ /* convert the data if we need to */
+ unsigned int samples;
+ if (m_convert)
+ {
+ data = (uint8_t*)m_convertBuffer;
+ samples = m_convertFn(
+ (uint8_t*)m_inputBuffer.Raw(m_inputBuffer.Used()),
+ m_inputBuffer.Used() / m_bytesPerSample,
+ m_convertBuffer
+ );
+ sampleSize = sizeof(float);
+ }
+ else
+ {
+ data = (uint8_t*)m_inputBuffer.Raw(m_inputBuffer.Used());
+ samples = m_inputBuffer.Used() / m_bytesPerSample;
+ sampleSize = m_bytesPerSample;
+ }
+
+ if (samples == 0)
+ return 0;
+
+ /* resample it if we need to */
+ if (m_resample)
+ {
+ m_ssrcData.input_frames = samples / m_chLayoutCount;
+ if (src_process(m_ssrc, &m_ssrcData) != 0)
+ return 0;
+ data = (uint8_t*)m_ssrcData.data_out;
+ frames = m_ssrcData.output_frames_gen;
+ consumed = m_ssrcData.input_frames_used * m_bytesPerFrame;
+ if (!frames)
+ return consumed;
+
+ samples = frames * m_chLayoutCount;
+ }
+ else
+ {
+ data = (uint8_t*)m_convertBuffer;
+ frames = samples / m_chLayoutCount;
+ consumed = frames * m_bytesPerFrame;
+ }
+
+ if (m_refillBuffer)
+ {
+ if (frames > m_refillBuffer)
+ m_refillBuffer = 0;
+ else
+ m_refillBuffer -= frames;
+ }
+
+ /* buffer the data */
+ m_framesBuffered += frames;
+ const unsigned int inputBlockSize = m_format.m_frames * m_format.m_channelLayout.Count() * sampleSize;
+
+ size_t remaining = samples * sampleSize;
+ while (remaining)
+ {
+ size_t copy = std::min(m_newPacket->data.Free(), remaining);
+ m_newPacket->data.Push(data, copy);
+ data += copy;
+ remaining -= copy;
+
+ /* wait till we have a full packet, or no more data before processing the packet */
+ if ((!m_draining || remaining) && m_newPacket->data.Free() > 0)
+ continue;
+
+ /* if we have a full block of data */
+ if (AE_IS_RAW(m_initDataFormat))
+ {
+ m_outBuffer.push_back(m_newPacket);
+ m_newPacket = new PPacket();
+ m_newPacket->data.Alloc(inputBlockSize);
+ continue;
+ }
+
+ /* make a new packet for downmix/remap */
+ PPacket *pkt = new PPacket();
+
+ /* downmix/remap the data */
+ size_t frames = m_newPacket->data.Used() / m_format.m_channelLayout.Count() / sizeof(float);
+ size_t used = frames * m_aeChannelLayout.Count() * sizeof(float);
+ pkt->data.Alloc(used);
+ m_remap.Remap(
+ (float*)m_newPacket->data.Raw (m_newPacket->data.Used()),
+ (float*)pkt ->data.Take(used),
+ frames
+ );
+
+ /* downmix for the viz if we have one */
+ if (m_audioCallback)
+ {
+ size_t vizUsed = frames * 2 * sizeof(float);
+ pkt->vizData.Alloc(vizUsed);
+ m_vizRemap.Remap(
+ (float*)m_newPacket->data .Raw (m_newPacket->data.Used()),
+ (float*)pkt ->vizData.Take(vizUsed),
+ frames
+ );
+ }
+
+ /* add the packet to the output */
+ m_outBuffer.push_back(pkt);
+ m_newPacket->data.Empty();
+ }
+
+ return consumed;
+}
+
+uint8_t* CSoftAEStream::GetFrame()
+{
+ CExclusiveLock lock(m_lock);
+
+ /* if we are fading, this runs even if we have underrun as it is time based */
+ if (m_fadeRunning)
+ {
+ m_volume += m_fadeStep;
+ m_volume = std::min(1.0f, std::max(0.0f, m_volume));
+ if (m_fadeDirUp)
+ {
+ if (m_volume >= m_fadeTarget)
+ m_fadeRunning = false;
+ }
+ else
+ {
+ if (m_volume <= m_fadeTarget)
+ m_fadeRunning = false;
+ }
+ }
+
+ /* if we have been deleted or are refilling but not draining */
+ if (!m_valid || m_delete || (m_refillBuffer && !m_draining))
+ return NULL;
+
+ /* if the packet is empty, advance to the next one */
+ if (!m_packet || m_packet->data.CursorEnd())
+ {
+ delete m_packet;
+ m_packet = NULL;
+
+ /* no more packets, return null */
+ if (m_outBuffer.empty())
+ {
+ if (m_draining)
+ return NULL;
+ else
+ {
+ /* underrun, we need to refill our buffers */
+ CLog::Log(LOGDEBUG, "CSoftAEStream::GetFrame - Underrun");
+ ASSERT(m_waterLevel > m_framesBuffered);
+ m_refillBuffer = m_waterLevel - m_framesBuffered;
+ return NULL;
+ }
+ }
+
+ /* get the next packet */
+ m_packet = m_outBuffer.front();
+ m_outBuffer.pop_front();
+ }
+
+ /* fetch one frame of data */
+ uint8_t *ret = (uint8_t*)m_packet->data.CursorRead(m_aeBytesPerFrame);
+
+ /* we have a frame, if we have a viz we need to hand the data to it */
+ if (m_audioCallback && !m_packet->vizData.CursorEnd())
+ {
+ float *vizData = (float*)m_packet->vizData.CursorRead(2 * sizeof(float));
+ memcpy(m_vizBuffer + m_vizBufferSamples, vizData, 2 * sizeof(float));
+ m_vizBufferSamples += 2;
+ if (m_vizBufferSamples == 512)
+ {
+ m_audioCallback->OnAudioData(m_vizBuffer, 512);
+ m_vizBufferSamples = 0;
+ }
+ }
+
+ --m_framesBuffered;
+ return ret;
+}
+
+double CSoftAEStream::GetDelay()
+{
+ if (m_delete)
+ return 0.0;
+
+ double delay = AE.GetDelay();
+ delay += (double)(m_inputBuffer.Used() / m_format.m_frameSize) / (double)m_format.m_sampleRate;
+ delay += (double)m_framesBuffered / (double)AE.GetSampleRate();
+
+ return delay;
+}
+
+double CSoftAEStream::GetCacheTime()
+{
+ if (m_delete)
+ return 0.0;
+
+ double time;
+ time = (double)(m_inputBuffer.Free() / m_format.m_frameSize) / (double)m_format.m_sampleRate;
+ time += (double)(m_waterLevel - m_refillBuffer) / (double)AE.GetSampleRate();
+ time += AE.GetCacheTime();
+ return time;
+}
+
+double CSoftAEStream::GetCacheTotal()
+{
+ if (m_delete)
+ return 0.0;
+
+ double total;
+ total = (double)(m_inputBuffer.Size() / m_format.m_frameSize) / (double)m_format.m_sampleRate;
+ total += (double)m_waterLevel / (double)AE.GetSampleRate();
+ total += AE.GetCacheTotal();
+ return total;
+}
+
+void CSoftAEStream::Pause()
+{
+ if (m_paused)
+ return;
+ m_paused = true;
+ AE.PauseStream(this);
+}
+
+void CSoftAEStream::Resume()
+{
+ if (!m_paused)
+ return;
+ m_paused = false;
+ m_autoStart = false;
+ AE.ResumeStream(this);
+}
+
+void CSoftAEStream::Drain()
+{
+ CSharedLock lock(m_lock);
+ m_draining = true;
+}
+
+bool CSoftAEStream::IsDrained()
+{
+ CSharedLock lock(m_lock);
+ return (m_draining && !m_packet && m_outBuffer.empty());
+}
+
+void CSoftAEStream::Flush()
+{
+ CLog::Log(LOGDEBUG, "CSoftAEStream::Flush");
+ CExclusiveLock lock(m_lock);
+ InternalFlush();
+
+ /* internal flush does not do this as these samples are still valid if we are re-initializing */
+ m_inputBuffer.Empty();
+}
+
+void CSoftAEStream::InternalFlush()
+{
+ /* reset the resampler */
+ if (m_resample)
+ {
+ m_ssrcData.end_of_input = 0;
+ src_reset(m_ssrc);
+ }
+
+ /* invalidate any incoming samples */
+ m_newPacket->data.Empty();
+
+ /*
+ clear the current buffered packet, we cant delete the data as it may be
+ in use by the AE thread, so we just seek to the end of the buffer
+ */
+ if (m_packet)
+ m_packet->data.CursorSeek(m_packet->data.Size());
+
+ /* clear any other buffered packets */
+ while (!m_outBuffer.empty())
+ {
+ PPacket *p = m_outBuffer.front();
+ m_outBuffer.pop_front();
+ delete p;
+ }
+
+ /* reset our counts */
+ m_framesBuffered = 0;
+ m_refillBuffer = m_waterLevel;
+}
+
+double CSoftAEStream::GetResampleRatio()
+{
+ if (!m_resample)
+ return 1.0f;
+
+ CSharedLock lock(m_lock);
+ return m_ssrcData.src_ratio;
+}
+
+bool CSoftAEStream::SetResampleRatio(double ratio)
+{
+ if (!m_resample)
+ return false;
+
+ CSharedLock lock(m_lock);
+
+ int oldRatioInt = std::ceil(m_ssrcData.src_ratio);
+
+ m_resampleRatio = ratio;
+
+ src_set_ratio(m_ssrc, m_resampleRatio * m_internalRatio);
+ m_ssrcData.src_ratio = m_resampleRatio * m_internalRatio;
+
+ //Check the resample buffer size and resize if necessary.
+ if (oldRatioInt < std::ceil(m_ssrcData.src_ratio))
+ {
+ _aligned_free(m_ssrcData.data_out);
+ m_ssrcData.data_out = (float*)_aligned_malloc(m_format.m_frameSamples * std::ceil(m_ssrcData.src_ratio) * sizeof(float), 16);
+ m_ssrcData.output_frames = m_format.m_frames * std::ceil(m_ssrcData.src_ratio);
+ }
+ return true;
+}
+
+void CSoftAEStream::RegisterAudioCallback(IAudioCallback* pCallback)
+{
+ CExclusiveLock lock(m_lock);
+ m_vizBufferSamples = 0;
+ m_audioCallback = pCallback;
+ if (m_audioCallback)
+ m_audioCallback->OnInitialize(2, m_initSampleRate, 32);
+}
+
+void CSoftAEStream::UnRegisterAudioCallback()
+{
+ CExclusiveLock lock(m_lock);
+ m_audioCallback = NULL;
+ m_vizBufferSamples = 0;
+}
+
+void CSoftAEStream::FadeVolume(float from, float target, unsigned int time)
+{
+ /* can't fade a RAW stream */
+ if (AE_IS_RAW(m_initDataFormat))
+ return;
+
+ CExclusiveLock lock(m_lock);
+ float delta = target - from;
+ m_fadeDirUp = target > from;
+ m_fadeTarget = target;
+ m_fadeStep = delta / (((float)AE.GetSampleRate() / 1000.0f) * (float)time);
+ m_fadeRunning = true;
+}
+
+bool CSoftAEStream::IsFading()
+{
+ CSharedLock lock(m_lock);
+ return m_fadeRunning;
+}
+
+void CSoftAEStream::RegisterSlave(IAEStream *slave)
+{
+ CSharedLock lock(m_lock);
+ m_slave = (CSoftAEStream*)slave;
+}
+
diff --git a/xbmc/cores/AudioEngine/Engines/SoftAEStream.h b/xbmc/cores/AudioEngine/Engines/SoftAEStream.h
new file mode 100644
index 0000000000..52460fbf52
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Engines/SoftAEStream.h
@@ -0,0 +1,156 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <samplerate.h>
+#include <list>
+
+#include "threads/SharedSection.h"
+
+#include "AEAudioFormat.h"
+#include "Interfaces/AEStream.h"
+#include "Utils/AEConvert.h"
+#include "Utils/AERemap.h"
+#include "Utils/AEBuffer.h"
+
+class IAEPostProc;
+class CSoftAEStream : public IAEStream
+{
+protected:
+ friend class CSoftAE;
+ CSoftAEStream(enum AEDataFormat format, unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options);
+ virtual ~CSoftAEStream();
+
+ void Initialize();
+ void InitializeRemap();
+ void Destroy();
+ uint8_t* GetFrame();
+
+ bool IsPaused () { return m_paused; }
+ bool IsDestroyed() { return m_delete; }
+ bool IsValid () { return m_valid; }
+ const bool IsRaw() const { return AE_IS_RAW(m_initDataFormat); }
+
+public:
+ virtual unsigned int GetSpace ();
+ virtual unsigned int AddData (void *data, unsigned int size);
+ virtual double GetDelay ();
+ virtual bool IsBuffering () { return m_refillBuffer > 0; }
+ virtual double GetCacheTime ();
+ virtual double GetCacheTotal ();
+
+ virtual void Pause ();
+ virtual void Resume ();
+ virtual void Drain ();
+ virtual bool IsDraining () { return m_draining; }
+ virtual bool IsDrained ();
+ virtual void Flush ();
+
+ virtual float GetVolume () { return m_volume; }
+ virtual float GetReplayGain () { return m_rgain ; }
+ virtual void SetVolume (float volume) { m_volume = std::max( 0.0f, std::min(1.0f, volume)); }
+ virtual void SetReplayGain (float factor) { m_rgain = std::max(-1.0f, std::max(1.0f, factor)); }
+
+ virtual const unsigned int GetFrameSize () const { return m_format.m_frameSize; }
+ virtual const unsigned int GetChannelCount() const { return m_initChannelLayout.Count(); }
+
+ virtual const unsigned int GetSampleRate () const { return m_initSampleRate; }
+ virtual const unsigned int GetEncodedSampleRate() const { return m_initEncodedSampleRate; }
+ virtual const enum AEDataFormat GetDataFormat () const { return m_initDataFormat; }
+
+ virtual double GetResampleRatio();
+ virtual bool SetResampleRatio(double ratio);
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnRegisterAudioCallback();
+ virtual void FadeVolume(float from, float to, unsigned int time);
+ virtual bool IsFading();
+ virtual void RegisterSlave(IAEStream *stream);
+private:
+ void InternalFlush();
+ void CheckResampleBuffers();
+
+ CSharedSection m_lock;
+ enum AEDataFormat m_initDataFormat;
+ unsigned int m_initSampleRate;
+ unsigned int m_initEncodedSampleRate;
+ CAEChannelInfo m_initChannelLayout;
+ unsigned int m_chLayoutCount;
+
+ typedef struct
+ {
+ CAEBuffer data;
+ CAEBuffer vizData;
+ } PPacket;
+
+ AEAudioFormat m_format;
+
+ bool m_forceResample; /* true if we are to force resample even when the rates match */
+ bool m_resample; /* true if the audio needs to be resampled */
+ double m_resampleRatio; /* user specified resample ratio */
+ double m_internalRatio; /* internal resample ratio */
+ bool m_convert; /* true if the bitspersample needs converting */
+ float *m_convertBuffer; /* buffer for converted data */
+ bool m_valid; /* true if the stream is valid */
+ bool m_delete; /* true if CSoftAE is to free this object */
+ CAERemap m_remap; /* the remapper */
+ float m_volume; /* the volume level */
+ float m_rgain; /* replay gain level */
+ unsigned int m_waterLevel; /* the fill level to fall below before calling the data callback */
+ unsigned int m_refillBuffer; /* how many frames that need to be buffered before we return any frames */
+
+ CAEConvert::AEConvertToFn m_convertFn;
+
+ CAEBuffer m_inputBuffer;
+ unsigned int m_bytesPerSample;
+ unsigned int m_bytesPerFrame;
+ unsigned int m_samplesPerFrame;
+ CAEChannelInfo m_aeChannelLayout;
+ unsigned int m_aeBytesPerFrame;
+ SRC_STATE *m_ssrc;
+ SRC_DATA m_ssrcData;
+ unsigned int m_framesBuffered;
+ std::list<PPacket*> m_outBuffer;
+ unsigned int ProcessFrameBuffer();
+ PPacket *m_newPacket;
+ PPacket *m_packet;
+ uint8_t *m_packetPos;
+ float *m_vizPacketPos;
+ bool m_paused;
+ bool m_autoStart;
+ bool m_draining;
+
+ /* vizualization internals */
+ CAERemap m_vizRemap;
+ float m_vizBuffer[512];
+ unsigned int m_vizBufferSamples;
+ IAudioCallback *m_audioCallback;
+
+ /* fade values */
+ bool m_fadeRunning;
+ bool m_fadeDirUp;
+ float m_fadeStep;
+ float m_fadeTarget;
+ unsigned int m_fadeTime;
+
+ /* slave stream */
+ CSoftAEStream *m_slave;
+};
+
diff --git a/xbmc/cores/AudioEngine/Interfaces/AE.h b/xbmc/cores/AudioEngine/Interfaces/AE.h
new file mode 100644
index 0000000000..2be733d9c9
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/AE.h
@@ -0,0 +1,162 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <list>
+#include <map>
+
+#include "system.h"
+#include "threads/CriticalSection.h"
+
+#include "../AEAudioFormat.h"
+#include "AEStream.h"
+#include "AESound.h"
+
+typedef std::pair<std::string, std::string> AEDevice;
+typedef std::vector<AEDevice> AEDeviceList;
+
+/* forward declarations */
+class IAEStream;
+class IAESound;
+class IAEPacketizer;
+
+/* sound options */
+#define AE_SOUND_OFF 0 /* disable sounds */
+#define AE_SOUND_IDLE 1 /* only play sounds while no streams are running */
+#define AE_SOUND_ALWAYS 2 /* always play sounds */
+
+/**
+ * IAE Interface
+ */
+class IAE
+{
+protected:
+ friend class CAEFactory;
+
+ IAE() {}
+ virtual ~IAE() {}
+
+ /**
+ * Initializes the AudioEngine, called by CFactory when it is time to initialize the audio engine.
+ * Do not call this directly, CApplication will call this when it is ready
+ */
+ virtual bool Initialize() = 0;
+public:
+ /**
+ * Called when the application needs to terminate the engine
+ */
+ virtual void Shutdown() { }
+
+ /**
+ * Callback to alert the AudioEngine of setting changes
+ * @param setting The name of the setting that was changed
+ */
+ virtual void OnSettingsChange(std::string setting) {}
+
+ /**
+ * Returns the current master volume level of the AudioEngine
+ * @return The volume level between 0.0 and 1.0
+ */
+ virtual float GetVolume() = 0;
+
+ /**
+ * Sets the master volume level of the AudioEngine
+ * @param volume The new volume level between 0.0 and 1.0
+ */
+ virtual void SetVolume(const float volume) = 0;
+
+ /**
+ * Set the mute state (does not affect volume level value)
+ * @param enabled The mute state
+ */
+ virtual void SetMute(const bool enabled) = 0;
+
+ /**
+ * Get the current mute state
+ * @return The current mute state
+ */
+ virtual bool IsMuted() = 0;
+
+ /**
+ * Sets the sound mode
+ * @param mode One of AE_SOUND_OFF, AE_SOUND_IDLE or AE_SOUND_ALWAYS
+ */
+ virtual void SetSoundMode(const int mode) = 0;
+
+ /**
+ * Creates and returns a new IAEStream in the format specified, this function should never fail
+ * @param dataFormat The data format the incoming audio will be in (eg, AE_FMT_S16LE)
+ * @param sampleRate The sample rate of the audio data (eg, 48000)
+ * @prarm encodedSampleRate The sample rate of the encoded audio data if AE_IS_RAW(dataFormat)
+ * @param channelLayout The order of the channels in the audio data
+ * @param options A bit field of stream options (see: enum AEStreamOptions)
+ * @return a new IAEStream that will accept data in the requested format
+ */
+ virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options = 0) = 0;
+
+ /**
+ * This method will remove the specifyed stream from the engine.
+ * For OSX/IOS this is essential to reconfigure the audio output.
+ * @param stream The stream to be altered
+ * @return NULL
+ */
+ virtual IAEStream *FreeStream(IAEStream *stream) = 0;
+
+ /**
+ * Creates a new IAESound that is ready to play the specified file
+ * @param file The WAV file to load, this supports XBMC's VFS
+ * @return A new IAESound if the file could be loaded, otherwise NULL
+ */
+ virtual IAESound *MakeSound(const std::string &file) = 0;
+
+ /**
+ * Free the supplied IAESound object
+ * @param sound The IAESound object to free
+ */
+ virtual void FreeSound(IAESound *sound) = 0;
+
+ /**
+ * Callback by CApplication for Garbage Collection. This method is called by CApplication every 500ms and can be used to clean up and free no-longer used resources.
+ */
+ virtual void GarbageCollect() = 0;
+
+ /**
+ * Enumerate the supported audio output devices
+ * @param devices The device list to append supported devices to
+ * @param passthrough True if only passthrough devices are wanted
+ */
+ virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough) = 0;
+
+ /**
+ * Returns the default audio device
+ * @param passthrough True if the default passthrough device is wanted
+ * @return the default audio device
+ */
+ virtual std::string GetDefaultDevice(bool passthrough) { return "default"; }
+
+ /**
+ * Returns true if the AudioEngine supports AE_FMT_RAW streams for use with formats such as IEC61937
+ * @see CAEPackIEC61937::CAEPackIEC61937()
+ * @returns true if the AudioEngine is capable of RAW output
+ */
+ virtual bool SupportsRaw() { return false; }
+};
+
diff --git a/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h
new file mode 100644
index 0000000000..1e2bb08f93
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h
@@ -0,0 +1,101 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DllAvCodec.h"
+#include "AEAudioFormat.h"
+
+/**
+ * IAEEncoder interface for on the fly audio compression
+ */
+class IAEEncoder
+{
+public:
+ /**
+ * Constructor
+ */
+ IAEEncoder() {};
+
+ /**
+ * Destructor
+ */
+ virtual ~IAEEncoder() {};
+
+ /**
+ * Return true if the supplied format is compatible with the current open encoder.
+ * @param format the format to compare
+ * @return true if compatible, false if not
+ */
+ virtual bool IsCompatible(AEAudioFormat format) = 0;
+
+ /**
+ * Called to setup the encoder to accept data in the specified format
+ * @param format the desired audio format, may be changed to suit the encoder
+ * @return true on success, false on failure
+ */
+ virtual bool Initialize(AEAudioFormat &format) = 0;
+
+ /**
+ * Reset the encoder for new data
+ */
+ virtual void Reset() = 0;
+
+ /**
+ * Returns the bitrate of the encoder
+ * @return bit rate in bits per second
+ */
+ virtual unsigned int GetBitRate() = 0;
+
+ /**
+ * Returns the CodecID of the encoder
+ * @return the ffmpeg codec id
+ */
+ virtual CodecID GetCodecID() = 0;
+
+ /**
+ * Return the number of frames needed to encode
+ * @return number of frames (frames * channels = samples * bits per sample = bytes)
+ */
+ virtual unsigned int GetFrames() = 0;
+
+ /**
+ * Encodes the supplied samples
+ * @param data the PCM samples in float format
+ * @param frames the number of audio frames in data (bytes / bits per sample = samples / channels = frames)
+ * @return the number of samples consumed
+ */
+ virtual int Encode(float *data, unsigned int frames) = 0;
+
+ /**
+ * Get the encoded data
+ * @param data return pointer to the buffer with the current encoded block
+ * @return the size in bytes of *data
+ */
+ virtual int GetData(uint8_t **data) = 0;
+
+ /**
+ * Get the delay in seconds
+ * @param bufferSize how much encoded data the caller has buffered to add to the delay
+ * @return the delay in seconds including any un-fetched encoded data
+ */
+ virtual double GetDelay(unsigned int bufferSize) = 0;
+};
+
diff --git a/xbmc/utils/PCMAmplifier.h b/xbmc/cores/AudioEngine/Interfaces/AEPostProc.h
index e5d4689949..52c0ce3f02 100644
--- a/xbmc/utils/PCMAmplifier.h
+++ b/xbmc/cores/AudioEngine/Interfaces/AEPostProc.h
@@ -1,9 +1,7 @@
-#ifndef __PCM_AMPLIFY__H__
-#define __PCM_AMPLIFY__H__
-
+#pragma once
/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
*
* 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
@@ -22,22 +20,16 @@
*
*/
-class CPCMAmplifier {
-public:
- CPCMAmplifier();
- virtual ~CPCMAmplifier();
-
- void SetVolume(int nVolume);
- int GetVolume();
-
- // only works on 16bit samples
- void DeAmplify(short *pcm, int nSamples);
-
-protected:
- int m_nVolume;
- double m_dFactor;
+#include "AEStream.h"
+class IAEStream;
+class IAEPostProc
+{
+public:
+ virtual bool Initialize(IAEStream *stream) = 0;
+ virtual void DeInitialize() = 0;
+ virtual void Flush() = 0;
+ virtual void Process(float *data, unsigned int frames) = 0;
+ virtual const char* GetName() = 0;
};
-#endif
-
diff --git a/xbmc/cores/AudioEngine/Interfaces/AESink.h b/xbmc/cores/AudioEngine/Interfaces/AESink.h
new file mode 100644
index 0000000000..1ef1333a4a
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/AESink.h
@@ -0,0 +1,80 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "threads/Thread.h"
+#include "AE.h"
+#include "AEAudioFormat.h"
+#include "utils/StdString.h"
+#include <stdint.h>
+
+class IAESink
+{
+public:
+ /* return the name of this sync for logging */
+ virtual const char *GetName() = 0;
+
+ IAESink() {};
+ virtual ~IAESink() {};
+
+ /*
+ The sink does NOT have to honour anything in the format struct or the device
+ if however it does not honour what is requested, it MUST update device/format
+ with what it does support.
+ */
+ virtual bool Initialize (AEAudioFormat &format, std::string &device) = 0;
+
+ /*
+ Deinitialize the sink for destruction
+ */
+ virtual void Deinitialize() = 0;
+
+ /*
+ Return true if the supplied format and device are compatible with the current open sink
+ */
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device) = 0;
+
+ /*
+ This method must return the delay in seconds till new data will be sent out
+ */
+ virtual double GetDelay() = 0;
+
+ /*
+ This method returns the time in seconds till the sink's cache is full
+ */
+ virtual double GetCacheTime() = 0;
+
+ /*
+ This method returns the total length of the cache in seconds
+ */
+ virtual double GetCacheTotal() = 0;
+
+ /*
+ Adds packets to be sent out, must block after at-least one block is being rendered
+ */
+ virtual unsigned int AddPackets(uint8_t *data, unsigned int frames) = 0;
+
+ /*
+ Drain the sink
+ */
+ virtual void Drain() {};
+};
+
diff --git a/xbmc/cores/AudioEngine/Interfaces/AESound.h b/xbmc/cores/AudioEngine/Interfaces/AESound.h
new file mode 100644
index 0000000000..b8def17500
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/AESound.h
@@ -0,0 +1,50 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils/StdString.h"
+#include "AE.h"
+
+class IAE;
+class IAESound
+{
+protected:
+ friend class IAE;
+ IAESound(const std::string &filename) {}
+ virtual ~IAESound() {}
+
+public:
+ /* play the sound this object represents */
+ virtual void Play() = 0;
+
+ /* stop playing the sound this object represents */
+ virtual void Stop() = 0;
+
+ /* return true if the sound is currently playing */
+ virtual bool IsPlaying() = 0;
+
+ /* set the playback volume of this sound */
+ virtual void SetVolume(float volume) = 0;
+
+ /* get the current playback volume of this sound */
+ virtual float GetVolume() = 0;
+};
+
diff --git a/xbmc/cores/AudioEngine/Interfaces/AEStream.h b/xbmc/cores/AudioEngine/Interfaces/AEStream.h
new file mode 100644
index 0000000000..e7f63f6517
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/AEStream.h
@@ -0,0 +1,216 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "../AEAudioFormat.h"
+#include "cores/IAudioCallback.h"
+#include <stdint.h>
+
+/**
+ * Bit options to pass to IAE::GetStream
+ */
+enum AEStreamOptions {
+ AESTREAM_FORCE_RESAMPLE = 0x01, /* force resample even if rates match */
+ AESTREAM_PAUSED = 0x02, /* create the stream paused */
+ AESTREAM_AUTOSTART = 0x04 /* autostart the stream when enough data is buffered */
+};
+
+/**
+ * IAEStream Stream Interface for streaming audio
+ */
+class IAEStream
+{
+protected:
+ friend class IAE;
+ IAEStream() {}
+ virtual ~IAEStream() {}
+
+public:
+ /**
+ * Returns the amount of space available in the stream
+ * @return The number of bytes AddData will consume
+ */
+ virtual unsigned int GetSpace() = 0;
+
+ /**
+ * Add interleaved PCM data to the stream
+ * @param data The interleaved PCM data
+ * @param size The size in bytes of data, if this is > GetSpace() only up to GetSpace() bytes will be consumed
+ * @return The number of bytes consumed
+ */
+ virtual unsigned int AddData(void *data, unsigned int size) = 0;
+
+ /**
+ * Returns how long until new data will be played
+ * @return The delay in seconds
+ */
+ virtual double GetDelay() = 0;
+
+ /**
+ * Returns if the stream is buffering
+ * @return True if the stream is buffering
+ */
+ virtual bool IsBuffering() = 0;
+
+ /**
+ * Returns how long until playback will start
+ * @return The delay in seconds
+ */
+ virtual double GetCacheTime() = 0;
+
+ /**
+ * Returns the total length of the cache before playback will start
+ * @return The delay in seconds
+ */
+ virtual double GetCacheTotal() = 0;
+
+ /**
+ * Pauses the stream playback
+ */
+ virtual void Pause() = 0;
+
+ /**
+ * Resumes the stream after pausing
+ */
+ virtual void Resume() = 0;
+
+ /**
+ * Start draining the stream
+ * @note Once called AddData will not consume more data.
+ */
+ virtual void Drain() = 0;
+
+ /**
+ * Returns true if the is stream draining
+ */
+ virtual bool IsDraining() = 0;
+
+ /**
+ * Returns true if the is stream has finished draining
+ */
+ virtual bool IsDrained() { return true; } /*FIXME: this should = 0 when done */
+
+ /**
+ * Flush all buffers dropping the audio data
+ */
+ virtual void Flush() = 0;
+
+ /**
+ * Return the stream's current volume level
+ * @return The volume level between 0.0 and 1.0
+ */
+ virtual float GetVolume() = 0;
+
+ /**
+ * Set the stream's volume level
+ * @param volume The new volume level between 0.0 and 1.0
+ */
+ virtual void SetVolume(float volume) = 0;
+
+ /**
+ * Returns the stream's current replay gain factor
+ * @return The replay gain factor between 0.0 and 1.0
+ */
+ virtual float GetReplayGain() = 0;
+
+ /**
+ * Sets the stream's replay gain factor, this is used by formats such as MP3 that have attenuation information in their streams
+ * @param factor The replay gain factor
+ */
+ virtual void SetReplayGain(float factor) = 0;
+
+ /**
+ * Returns the size of one audio frame in bytes (channelCount * resolution)
+ * @return The size in bytes of one frame
+ */
+ virtual const unsigned int GetFrameSize() const = 0;
+
+ /**
+ * Returns the number of channels the stream is configured to accept
+ * @return The channel count
+ */
+ virtual const unsigned int GetChannelCount() const = 0;
+
+ /**
+ * Returns the stream's sample rate, if the stream is using a dynamic sample rate, this value will NOT reflect any changes made by calls to SetResampleRatio()
+ * @return The stream's sample rate (eg, 48000)
+ */
+ virtual const unsigned int GetSampleRate() const = 0;
+
+ /**
+ * Returns the stream's encoded sample rate if the stream is RAW
+ * @return The stream's encoded sample rate
+ */
+ virtual const unsigned int GetEncodedSampleRate() const = 0;
+
+ /**
+ * Return the data format the stream has been configured with
+ * @return The stream's data format (eg, AE_FMT_S16LE)
+ */
+ virtual const enum AEDataFormat GetDataFormat() const = 0;
+
+ /**
+ * Return the resample ratio
+ * @note This will return an undefined value if the stream is not resampling
+ * @return the current resample ratio or undefined if the stream is not resampling
+ */
+ virtual double GetResampleRatio() = 0;
+
+ /**
+ * Sets the resample ratio
+ * @note This function may return false if the stream is not resampling, if you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option
+ * @param ratio the new sample rate ratio, calculated by ((double)desiredRate / (double)GetSampleRate())
+ */
+ virtual bool SetResampleRatio(double ratio) = 0;
+
+ /**
+ * Registers the audio callback to call with each block of data, this is used by Audio Visualizations
+ * @warning Currently the callbacks require stereo float data in blocks of 512 samples, any deviation from this may crash XBMC, or cause junk to be rendered
+ * @param pCallback The callback
+ */
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback) = 0;
+
+ /**
+ * Unregisters the current audio callback
+ */
+ virtual void UnRegisterAudioCallback() = 0;
+
+ /**
+ * Fade the volume level over the specified time
+ * @param from The volume level to fade from (0.0f-1.0f) - See notes
+ * @param target The volume level to fade to (0.0f-1.0f)
+ * @param time The amount of time in milliseconds for the fade to occur
+ * @note The from parameter does not set the streams volume, it is only used to calculate the fade time properly
+ */
+ virtual void FadeVolume(float from, float target, unsigned int time) {} /* FIXME: once all the engines have these new methods */
+
+ /**
+ * Returns if a fade is still running
+ * @return true if a fade is in progress, otherwise false
+ */
+ virtual bool IsFading() { return false; }
+
+ /**
+ * Slave a stream to resume when this stream has drained
+ */
+ virtual void RegisterSlave(IAEStream *stream) = 0;
+};
+
diff --git a/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h b/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h
new file mode 100644
index 0000000000..888d1cc9f5
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AE.h"
+#include "threads/Thread.h"
+
+class IThreadedAE : public IAE, public IRunnable
+{
+public:
+ virtual void Run () = 0;
+ virtual void Stop() = 0;
+};
+
diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in
new file mode 100644
index 0000000000..cac262b91d
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Makefile.in
@@ -0,0 +1,48 @@
+ARCH=@ARCH@
+
+INCLUDES=-I. -I../../ -I../../linux -I../../../guilib -I../../utils -I../dvdplayer -I../dvdplayer/Codecs/ffmpeg -I..
+
+OSSLIBDIR=0
+-include /etc/oss.conf
+ifneq (0,${OSSLIBDIR})
+ INCLUDES+=-I$(OSSLIBDIR)/include
+ CXXFLAGS+=-DOSS4
+endif
+
+CXXFLAGS+=-D__STDC_LIMIT_MACROS
+
+SRCS = \
+ AEFactory.cpp \
+ AESinkFactory.cpp \
+ Sinks/AESinkALSA.cpp \
+ Sinks/AESinkOSS.cpp \
+ Sinks/AESinkProfiler.cpp \
+ Sinks/AESinkNULL.cpp \
+ \
+ Utils/AEChannelInfo.cpp \
+ Utils/AEBuffer.cpp \
+ Utils/AEConvert.cpp \
+ Utils/AERemap.cpp \
+ Utils/AEUtil.cpp \
+ Utils/AEStreamInfo.cpp \
+ Utils/AEPackIEC61937.cpp \
+ Utils/AEBitstreamPacker.cpp \
+ Utils/AEWAVLoader.cpp \
+ Utils/AEELDParser.cpp \
+ Utils/AEDeviceInfo.cpp \
+ \
+ PostProc/AEPPAnimationFade.cpp \
+ \
+ Engines/SoftAE.cpp \
+ Engines/SoftAEStream.cpp \
+ Engines/SoftAESound.cpp \
+ Engines/PulseAE.cpp \
+ Engines/PulseAEStream.cpp \
+ Engines/PulseAESound.cpp \
+ \
+ Encoders/AEEncoderFFmpeg.cpp
+
+LIB=audioengine.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.cpp b/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.cpp
new file mode 100644
index 0000000000..d1fc488fbb
--- /dev/null
+++ b/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEPPAnimationFade.h"
+#include "AEFactory.h"
+
+CAEPPAnimationFade::CAEPPAnimationFade(float from, float to, unsigned int duration) :
+ m_stream (NULL),
+ m_callback(NULL)
+{
+ m_from = from;
+ m_to = to;
+ m_duration = duration;
+}
+
+CAEPPAnimationFade::~CAEPPAnimationFade()
+{
+}
+
+bool CAEPPAnimationFade::Initialize(IAEStream *stream)
+{
+ if (m_stream != stream)
+ DeInitialize();
+
+ m_stream = stream;
+ m_channelCount = stream->GetChannelCount();
+ m_step = (m_to - m_from) / ((stream->GetSampleRate() / 1000) * (float)m_duration);
+ m_running = false;
+ return true;
+}
+
+void CAEPPAnimationFade::DeInitialize()
+{
+ m_stream = NULL;
+}
+
+void CAEPPAnimationFade::Flush()
+{
+ /* we dont buffer, nothing to do */
+}
+
+void CAEPPAnimationFade::Process(float *data, unsigned int frames)
+{
+ /* apply the current level */
+ for (unsigned int f = 0; f < frames; ++f)
+ {
+ for (unsigned int c = 0; c < m_channelCount; ++c, ++data)
+ *data *= m_position;
+
+ /* if we are not fading, we are done */
+ if (!m_running)
+ continue;
+
+ /* perform the step and clamp the result */
+ m_position += m_step;
+ m_position = std::min(1.0f, std::max(0.0f, m_position));
+
+ if (
+ (m_step > 0.0f && m_position > m_to - m_step && m_position < m_to + m_step) ||
+ (m_step < 0.0f && m_position > m_to + m_step && m_position < m_to - m_step)
+ )
+ {
+ m_position = m_to;
+ m_running = false;
+ if (m_callback)
+ m_callback(this, m_cbArg);
+ }
+ }
+}
+
+void CAEPPAnimationFade::Run()
+{
+ m_running = true;
+}
+
+void CAEPPAnimationFade::Stop()
+{
+ m_running = false;
+}
+
+void CAEPPAnimationFade::SetPosition(const float position)
+{
+ m_position = std::min(1.0f, std::max(0.0f, position));
+}
+
+void CAEPPAnimationFade::SetDuration(const unsigned int duration)
+{
+ m_duration = duration;
+ m_step = (m_to - m_from) / ((m_stream->GetSampleRate() / 1000) * (float)m_duration);
+}
+
diff --git a/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.h b/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.h
new file mode 100644
index 0000000000..1c3cfec205
--- /dev/null
+++ b/xbmc/cores/AudioEngine/PostProc/AEPPAnimationFade.h
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "cores/AudioEngine/Interfaces/AEStream.h"
+#include "cores/AudioEngine/Interfaces/AEPostProc.h"
+#include "cores/IAudioCallback.h"
+
+class CAEPPAnimationFade : public IAEPostProc
+{
+public:
+ CAEPPAnimationFade(float from, float to, unsigned int duration);
+ ~CAEPPAnimationFade();
+
+ virtual bool Initialize(IAEStream *stream);
+ virtual void DeInitialize();
+ virtual void Flush();
+ virtual void Process(float *data, unsigned int frames);
+ virtual const char* GetName() { return "AnimationFade"; }
+
+ void Run();
+ void Stop();
+ void SetPosition(const float position);
+ void SetDuration(const unsigned int duration);
+
+ typedef void (DoneCallback)(CAEPPAnimationFade *sender, void *arg);
+ void SetDoneCallback(DoneCallback *callback, void *arg) { m_callback = callback; m_cbArg = arg; }
+private:
+ unsigned int m_channelCount; /* the AE channel count */
+ IAEStream *m_stream;
+
+ bool m_running; /* if the fade is running */
+ float m_position; /* current fade position */
+ float m_from; /* fade from */
+ float m_to; /* fade to */
+ unsigned int m_duration; /* fade duration in ms */
+ float m_step; /* the fade step size */
+ DoneCallback *m_callback; /* callback for on completion of fade */
+ void *m_cbArg; /* the argument to pass to the callback function */
+};
+
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
new file mode 100644
index 0000000000..7c3aa21c79
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include "system.h"
+#ifdef HAS_ALSA
+
+#include <stdint.h>
+#include <limits.h>
+#include <sstream>
+
+#include "AESinkALSA.h"
+#include "Utils/AEUtil.h"
+#include "Utils/AEELDParser.h"
+#include "utils/StdString.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+#include "threads/SingleLock.h"
+#include "settings/GUISettings.h"
+
+#define ALSA_OPTIONS (SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_RESAMPLE)
+#define ALSA_PERIODS 16
+
+#define ALSA_MAX_CHANNELS 16
+static enum AEChannel ALSAChannelMap[ALSA_MAX_CHANNELS + 1] = {
+ AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
+ AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
+ AE_CH_NULL
+};
+
+static unsigned int ALSASampleRateList[] =
+{
+ 5512,
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ 384000,
+ 0
+};
+
+CAESinkALSA::CAESinkALSA() :
+ m_pcm(NULL)
+{
+ /* ensure that ALSA has been initialized */
+ if (!snd_config)
+ snd_config_update();
+}
+
+CAESinkALSA::~CAESinkALSA()
+{
+ Deinitialize();
+}
+
+inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format)
+{
+ unsigned int count = 0;
+
+ if (format.m_dataFormat == AE_FMT_AC3 ||
+ format.m_dataFormat == AE_FMT_DTS ||
+ format.m_dataFormat == AE_FMT_EAC3)
+ count = 2;
+ else if (format.m_dataFormat == AE_FMT_TRUEHD ||
+ format.m_dataFormat == AE_FMT_DTSHD)
+ count = 8;
+ else
+ {
+ for (unsigned int c = 0; c < 8; ++c)
+ for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
+ if (format.m_channelLayout[i] == ALSAChannelMap[c])
+ {
+ count = c + 1;
+ break;
+ }
+ }
+
+ CAEChannelInfo info;
+ for (unsigned int i = 0; i < count; ++i)
+ info += ALSAChannelMap[i];
+
+ return info;
+}
+
+void CAESinkALSA::GetPassthroughDevice(AEAudioFormat format, std::string& device)
+{
+ device += ",AES0=0x06,AES1=0x82,AES2=0x00";
+ if (format.m_sampleRate == 192000) device += ",AES3=0x0e";
+ else if (format.m_sampleRate == 176400) device += ",AES3=0x0c";
+ else if (format.m_sampleRate == 96000) device += ",AES3=0x0a";
+ else if (format.m_sampleRate == 88200) device += ",AES3=0x08";
+ else if (format.m_sampleRate == 48000) device += ",AES3=0x02";
+ else if (format.m_sampleRate == 44100) device += ",AES3=0x00";
+ else if (format.m_sampleRate == 32000) device += ",AES3=0x03";
+ else device += ",AES3=0x01";
+}
+
+bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
+{
+ m_initDevice = device;
+ m_initFormat = format;
+
+ /* if we are raw, correct the data format */
+ if (AE_IS_RAW(format.m_dataFormat))
+ {
+ m_channelLayout = GetChannelLayout(format);
+ format.m_dataFormat = AE_FMT_S16NE;
+ m_passthrough = true;
+ }
+ else
+ {
+ m_channelLayout = GetChannelLayout(format);
+ m_passthrough = false;
+ }
+
+ if (m_channelLayout.Count() == 0)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
+ return false;
+ }
+
+ format.m_channelLayout = m_channelLayout;
+
+ /* if passthrough we need the additional AES flags */
+ if (m_passthrough)
+ GetPassthroughDevice(format, device);
+
+ m_device = device;
+ CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device %s", device.c_str());
+
+ /* get the sound config */
+ snd_config_t *config;
+ snd_config_copy(&config, snd_config);
+ int error;
+
+ error = snd_pcm_open_lconf(&m_pcm, device.c_str(), SND_PCM_STREAM_PLAYBACK, ALSA_OPTIONS, config);
+ if (error < 0)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::Initialize - snd_pcm_open_lconf(%d) - %s", error, device.c_str());
+ snd_config_delete(config);
+ return false;
+ }
+
+ /* free the sound config */
+ snd_config_delete(config);
+
+ if (!InitializeHW(format) || !InitializeSW(format))
+ return false;
+
+ snd_pcm_nonblock(m_pcm, 1);
+ snd_pcm_prepare (m_pcm);
+
+ m_format = format;
+ m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate;
+
+ return true;
+}
+
+bool CAESinkALSA::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ return (
+ /* compare against the requested format and the real format */
+ (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
+ (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
+ (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
+ (m_initDevice == device)
+ );
+}
+
+snd_pcm_format_t CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat format)
+{
+ if (AE_IS_RAW(format))
+ return SND_PCM_FORMAT_S16_LE;
+
+ switch (format)
+ {
+ case AE_FMT_S8 : return SND_PCM_FORMAT_S8;
+ case AE_FMT_U8 : return SND_PCM_FORMAT_U8;
+ case AE_FMT_S16NE : return SND_PCM_FORMAT_S16;
+ case AE_FMT_S24NE4: return SND_PCM_FORMAT_S24;
+#ifdef __BIG_ENDIAN__
+ case AE_FMT_S24NE3: return SND_PCM_FORMAT_S24_3BE;
+#else
+ case AE_FMT_S24NE3: return SND_PCM_FORMAT_S24_3LE;
+#endif
+ case AE_FMT_S32NE : return SND_PCM_FORMAT_S32;
+ case AE_FMT_FLOAT : return SND_PCM_FORMAT_FLOAT;
+
+ default:
+ return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
+{
+ snd_pcm_hw_params_t *hw_params;
+
+ snd_pcm_hw_params_alloca(&hw_params);
+ memset(hw_params, 0, snd_pcm_hw_params_sizeof());
+
+ snd_pcm_hw_params_any(m_pcm, hw_params);
+ snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+
+ unsigned int sampleRate = format.m_sampleRate;
+ unsigned int channelCount = format.m_channelLayout.Count();
+ snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL);
+ snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount);
+
+ /* ensure we opened X channels or more */
+ if (format.m_channelLayout.Count() > channelCount)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to open the required number of channels");
+ return false;
+ }
+
+ /* update the channelLayout to what we managed to open */
+ format.m_channelLayout.Reset();
+ for (unsigned int i = 0; i < channelCount; ++i)
+ format.m_channelLayout += ALSAChannelMap[i];
+
+ snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat);
+ if (fmt == SND_PCM_FORMAT_UNKNOWN)
+ {
+ /* if we dont support the requested format, fallback to float */
+ format.m_dataFormat = AE_FMT_FLOAT;
+ fmt = SND_PCM_FORMAT_FLOAT;
+ }
+
+ /* try the data format */
+ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
+ {
+ /* if the chosen format is not supported, try each one in decending order */
+ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
+ {
+ if (AE_IS_RAW(i) || i == AE_FMT_MAX)
+ continue;
+ fmt = AEFormatToALSAFormat(i);
+
+ if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
+ {
+ fmt = SND_PCM_FORMAT_UNKNOWN;
+ continue;
+ }
+
+ int fmtBits = CAEUtil::DataFormatToBits(i);
+ int bits = snd_pcm_hw_params_get_sbits(hw_params);
+ if (bits != fmtBits)
+ {
+ /* if we opened in 32bit and only have 24bits, pack into 24 */
+ if (fmtBits == 32 && bits == 24)
+ i = AE_FMT_S24NE4;
+ else
+ continue;
+ }
+
+ /* record that the format fell back to X */
+ format.m_dataFormat = i;
+ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ break;
+ }
+
+ /* if we failed to find a valid output format */
+ if (fmt == SND_PCM_FORMAT_UNKNOWN)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format");
+ return false;
+ }
+ }
+
+ unsigned int periods;
+
+ snd_pcm_uframes_t periodSize, bufferSize;
+ snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);
+
+ bufferSize = std::min(bufferSize, (snd_pcm_uframes_t)8192);
+ periodSize = bufferSize / ALSA_PERIODS;
+ periods = ALSA_PERIODS;
+
+ CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);
+
+ /* work on a copy of the hw params */
+ snd_pcm_hw_params_t *hw_params_copy;
+ snd_pcm_hw_params_alloca(&hw_params_copy);
+
+ /* try to set the buffer size then the period size */
+ snd_pcm_hw_params_copy(hw_params_copy, hw_params);
+ snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
+ snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
+ snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL);
+ if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
+ {
+ /* try to set the period size then the buffer size */
+ snd_pcm_hw_params_copy(hw_params_copy, hw_params);
+ snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
+ snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
+ snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL);
+ if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
+ {
+ /* try to just set the buffer size */
+ snd_pcm_hw_params_copy(hw_params_copy, hw_params);
+ snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
+ snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL);
+ if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
+ {
+ /* try to just set the period size */
+ snd_pcm_hw_params_copy(hw_params_copy, hw_params);
+ snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
+ snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL);
+ if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters");
+ return false;
+ }
+ }
+ }
+ }
+
+ snd_pcm_hw_params_get_period_size(hw_params_copy, &periodSize, NULL);
+ snd_pcm_hw_params_get_buffer_size(hw_params_copy, &bufferSize);
+
+
+ CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);
+
+ /* set the format parameters */
+ format.m_sampleRate = sampleRate;
+ format.m_frames = periodSize;
+ format.m_frameSamples = periodSize * format.m_channelLayout.Count();
+ format.m_frameSize = snd_pcm_frames_to_bytes(m_pcm, 1);
+
+ m_bufferSize = (unsigned int)bufferSize;
+ m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate);
+
+ CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout);
+
+ return true;
+}
+
+bool CAESinkALSA::InitializeSW(AEAudioFormat &format)
+{
+ snd_pcm_sw_params_t *sw_params;
+ snd_pcm_uframes_t boundary;
+
+ snd_pcm_sw_params_alloca(&sw_params);
+ memset(sw_params, 0, snd_pcm_sw_params_sizeof());
+
+ snd_pcm_sw_params_current (m_pcm, sw_params);
+ snd_pcm_sw_params_set_start_threshold (m_pcm, sw_params, INT_MAX);
+ snd_pcm_sw_params_set_silence_threshold(m_pcm, sw_params, 0);
+ snd_pcm_sw_params_get_boundary (sw_params, &boundary);
+ snd_pcm_sw_params_set_silence_size (m_pcm, sw_params, boundary);
+ snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, format.m_frames);
+
+ if (snd_pcm_sw_params(m_pcm, sw_params) < 0)
+ {
+ CLog::Log(LOGERROR, "CAESinkALSA::InitializeSW - Failed to set the parameters");
+ return false;
+ }
+
+ return true;
+}
+
+void CAESinkALSA::Deinitialize()
+{
+ Stop();
+
+ if (m_pcm)
+ {
+ snd_pcm_drop (m_pcm);
+ snd_pcm_close(m_pcm);
+ m_pcm = NULL;
+ }
+}
+
+void CAESinkALSA::Stop()
+{
+ if (!m_pcm)
+ return;
+ snd_pcm_drop(m_pcm);
+}
+
+double CAESinkALSA::GetDelay()
+{
+ if (!m_pcm)
+ return 0;
+ snd_pcm_sframes_t frames = 0;
+ snd_pcm_delay(m_pcm, &frames);
+
+ if (frames < 0)
+ {
+#if SND_LIB_VERSION >= 0x000901 /* snd_pcm_forward() exists since 0.9.0rc8 */
+ snd_pcm_forward(m_pcm, -frames);
+#endif
+ frames = 0;
+ }
+
+ return (double)frames * m_formatSampleRateMul;
+}
+
+double CAESinkALSA::GetCacheTime()
+{
+ if (!m_pcm)
+ return 0.0;
+
+ int space = snd_pcm_avail_update(m_pcm);
+ if (space == 0)
+ {
+ snd_pcm_state_t state = snd_pcm_state(m_pcm);
+ if (state < 0)
+ {
+ HandleError("snd_pcm_state", state);
+ space = m_bufferSize;
+ }
+
+ if (state != SND_PCM_STATE_RUNNING && state != SND_PCM_STATE_PREPARED)
+ space = m_bufferSize;
+ }
+
+ return (double)(m_bufferSize - space) * m_formatSampleRateMul;
+}
+
+double CAESinkALSA::GetCacheTotal()
+{
+ return (double)m_bufferSize * m_formatSampleRateMul;
+}
+
+unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames)
+{
+ if (!m_pcm)
+ return 0;
+
+ if (snd_pcm_state(m_pcm) == SND_PCM_STATE_PREPARED)
+ snd_pcm_start(m_pcm);
+
+ int ret;
+
+ ret = snd_pcm_avail(m_pcm);
+ if (ret < 0)
+ {
+ HandleError("snd_pcm_avail", ret);
+ ret = 0;
+ }
+
+ if ((unsigned int)ret < frames);
+ {
+ ret = snd_pcm_wait(m_pcm, m_timeout);
+ if (ret < 0)
+ HandleError("snd_pcm_wait", ret);
+ }
+
+ ret = snd_pcm_writei(m_pcm, (void*)data, frames);
+ if (ret < 0)
+ {
+ HandleError("snd_pcm_writei(1)", ret);
+ ret = snd_pcm_writei(m_pcm, (void*)data, frames);
+ if (ret < 0)
+ {
+ HandleError("snd_pcm_writei(2)", ret);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+void CAESinkALSA::HandleError(const char* name, int err)
+{
+ switch(err)
+ {
+ case -EPIPE:
+ CLog::Log(LOGERROR, "CAESinkALSA::HandleError(%s) - underrun", name);
+ if ((err = snd_pcm_prepare(m_pcm)) < 0)
+ CLog::Log(LOGERROR, "CAESinkALSA::HandleError(%s) - snd_pcm_prepare returned %d (%s)", name, err, snd_strerror(err));
+ break;
+
+ case -ESTRPIPE:
+ CLog::Log(LOGINFO, "CAESinkALSA::HandleError(%s) - Resuming after suspend", name);
+
+ /* try to resume the stream */
+ while((err = snd_pcm_resume(m_pcm)) == -EAGAIN)
+ Sleep(1);
+
+ /* if the hardware doesnt support resume, prepare the stream */
+ if (err == -ENOSYS)
+ if ((err = snd_pcm_prepare(m_pcm)) < 0)
+ CLog::Log(LOGERROR, "CAESinkALSA::HandleError(%s) - snd_pcm_prepare returned %d (%s)", name, err, snd_strerror(err));
+ break;
+
+ default:
+ CLog::Log(LOGERROR, "CAESinkALSA::HandleError(%s) - snd_pcm_writei returned %d (%s)", name, err, snd_strerror(err));
+ break;
+ }
+}
+
+void CAESinkALSA::Drain()
+{
+ if (!m_pcm)
+ return;
+
+ snd_pcm_nonblock(m_pcm, 0);
+ snd_pcm_drain(m_pcm);
+ snd_pcm_nonblock(m_pcm, 1);
+}
+
+void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list)
+{
+ /* ensure that ALSA has been initialized */
+ if(!snd_config)
+ snd_config_update();
+
+ snd_ctl_t *ctlhandle;
+ snd_pcm_t *pcmhandle;
+
+ snd_ctl_card_info_t *ctlinfo;
+ snd_ctl_card_info_alloca(&ctlinfo);
+ memset(ctlinfo, 0, snd_ctl_card_info_sizeof());
+
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_hw_params_alloca(&hwparams);
+ memset(hwparams, 0, snd_pcm_hw_params_sizeof());
+
+ snd_pcm_info_t *pcminfo;
+ snd_pcm_info_alloca(&pcminfo);
+ memset(pcminfo, 0, snd_pcm_info_sizeof());
+
+ /* get the sound config */
+ snd_config_t *config;
+ snd_config_copy(&config, snd_config);
+
+ std::string strHwName;
+ int n_cards = -1;
+ while (snd_card_next(&n_cards) == 0 && n_cards != -1)
+ {
+ std::stringstream sstr;
+ sstr << "hw:" << n_cards;
+ std::string strHwName = sstr.str();
+
+ if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0)
+ {
+ CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str());
+ continue;
+ }
+
+ if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0)
+ {
+ CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str());
+ snd_ctl_close(ctlhandle);
+ continue;
+ }
+
+ snd_hctl_t *hctl;
+ if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0)
+ hctl = NULL;
+ snd_hctl_load(hctl);
+
+ int pcm_index = 0;
+ int iec958_index = 0;
+ int hdmi_index = 0;
+
+ int dev = -1;
+ while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1)
+ {
+ snd_pcm_info_set_device (pcminfo, dev);
+ snd_pcm_info_set_subdevice(pcminfo, 0 );
+ snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
+
+ if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0)
+ {
+ CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev);
+ continue;
+ }
+
+ int dev_index;
+ sstr.str(std::string());
+ CAEDeviceInfo info;
+ std::string devname = snd_pcm_info_get_name(pcminfo);
+
+ if (devname.find("HDMI") != std::string::npos)
+ {
+ info.m_deviceType = AE_DEVTYPE_HDMI;
+ dev_index = hdmi_index++;
+ sstr << "hdmi";
+ }
+ else if (devname.find("Digital") != std::string::npos ||
+ devname.find("IEC958" ) != std::string::npos)
+ {
+ info.m_deviceType = AE_DEVTYPE_IEC958;
+ dev_index = iec958_index++;
+ sstr << "iec958";
+ }
+ else
+ {
+ info.m_deviceType = AE_DEVTYPE_PCM;
+ dev_index = pcm_index++;
+ sstr << "hw";
+ }
+
+ /* build the driver string to pass to ALSA */
+ sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
+ info.m_deviceName = sstr.str();
+
+ /* get the friendly display name*/
+ info.m_displayName = snd_ctl_card_info_get_name(ctlinfo);
+ info.m_displayNameExtra = devname;
+
+ /* see if we can get ELD for this device */
+ if (info.m_deviceType == AE_DEVTYPE_HDMI)
+ {
+ bool badHDMI = false;
+ if (hctl && !GetELD(hctl, dev, info, badHDMI))
+ CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str());
+
+ if (badHDMI)
+ {
+ CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str());
+ continue;
+ }
+ }
+
+ /* open the device for testing */
+ if (snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config) < 0)
+ {
+ CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", info.m_deviceName.c_str());
+ continue;
+ }
+
+ /* ensure we can get a playback configuration for the device */
+ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
+ {
+ CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str());
+ snd_pcm_close(pcmhandle);
+ continue;
+ }
+
+ /* detect the available sample rates */
+ for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
+ if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
+ info.m_sampleRates.push_back(*rate);
+
+ /* detect the channels available */
+ int channels = 0;
+ for (int i = 1; i <= ALSA_MAX_CHANNELS; ++i)
+ if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
+ channels = i;
+
+ CAEChannelInfo alsaChannels;
+ for (int i = 0; i < channels; ++i)
+ {
+ if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
+ info.m_channels += ALSAChannelMap[i];
+ alsaChannels += ALSAChannelMap[i];
+ }
+
+ /* remove the channels from m_channels that we cant use */
+ info.m_channels.ResolveChannels(alsaChannels);
+
+ /* detect the PCM sample formats that are available */
+ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
+ {
+ if (AE_IS_RAW(i) || i == AE_FMT_MAX)
+ continue;
+ snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
+ if (fmt == SND_PCM_FORMAT_UNKNOWN)
+ continue;
+
+ if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
+ info.m_dataFormats.push_back(i);
+ }
+
+ snd_pcm_close(pcmhandle);
+ list.push_back(info);
+ }
+
+ /* snd_hctl_close also closes ctlhandle */
+ if (hctl)
+ snd_hctl_close(hctl);
+ else
+ snd_ctl_close(ctlhandle);
+ }
+}
+
+bool CAESinkALSA::GetELD(snd_hctl_t *hctl, int device, CAEDeviceInfo& info, bool& badHDMI)
+{
+ badHDMI = false;
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *einfo;
+ snd_ctl_elem_value_t *control;
+ snd_hctl_elem_t *elem;
+
+ snd_ctl_elem_id_alloca(&id);
+ memset(id, 0, snd_ctl_elem_id_sizeof());
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
+ snd_ctl_elem_id_set_name (id, "ELD" );
+ snd_ctl_elem_id_set_device (id, device);
+ elem = snd_hctl_find_elem(hctl, id);
+ if (!elem)
+ return false;
+
+ snd_ctl_elem_info_alloca(&einfo);
+ memset(einfo, 0, snd_ctl_elem_info_sizeof());
+
+ if (snd_hctl_elem_info(elem, einfo) < 0)
+ return false;
+
+ if (!snd_ctl_elem_info_is_readable(einfo))
+ return false;
+
+ if (snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_BYTES)
+ return false;
+
+ snd_ctl_elem_value_alloca(&control);
+ memset(control, 0, snd_ctl_elem_value_sizeof());
+
+ if (snd_hctl_elem_read(elem, control) < 0)
+ return false;
+
+ int dataLength = snd_ctl_elem_info_get_count(einfo);
+ /* if there is no ELD data, then its a bad HDMI device, either nothing attached OR an invalid nVidia HDMI device */
+ if (!dataLength)
+ badHDMI = true;
+ else
+ CAEELDParser::Parse(
+ (const uint8_t*)snd_ctl_elem_value_get_bytes(control),
+ dataLength,
+ info
+ );
+
+ info.m_deviceType = AE_DEVTYPE_HDMI;
+ return true;
+}
+#endif
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
new file mode 100644
index 0000000000..b735cbc01b
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
@@ -0,0 +1,80 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_ALSA
+
+#include "Interfaces/AESink.h"
+#include "Utils/AEDeviceInfo.h"
+#include <stdint.h>
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include <alsa/asoundlib.h>
+
+#include "threads/CriticalSection.h"
+
+class CAESinkALSA : public IAESink
+{
+public:
+ virtual const char *GetName() { return "ALSA"; }
+
+ CAESinkALSA();
+ virtual ~CAESinkALSA();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual void Stop ();
+ virtual double GetDelay ();
+ virtual double GetCacheTime ();
+ virtual double GetCacheTotal ();
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ virtual void Drain ();
+
+ static void EnumerateDevicesEx(AEDeviceInfoList &list);
+private:
+ CAEChannelInfo GetChannelLayout(AEAudioFormat format);
+ void GetPassthroughDevice(const AEAudioFormat format, std::string& device);
+ void HandleError(const char* name, int err);
+
+ std::string m_initDevice;
+ AEAudioFormat m_initFormat;
+ AEAudioFormat m_format;
+ unsigned int m_bufferSize;
+ double m_formatSampleRateMul;
+ bool m_passthrough;
+ CAEChannelInfo m_channelLayout;
+ std::string m_device;
+ snd_pcm_t *m_pcm;
+ int m_timeout;
+
+ static snd_pcm_format_t AEFormatToALSAFormat(const enum AEDataFormat format);
+
+ bool InitializeHW(AEAudioFormat &format);
+ bool InitializeSW(AEAudioFormat &format);
+
+ static bool SoundDeviceExists(const std::string& device);
+ static bool GetELD(snd_hctl_t *hctl, int device, CAEDeviceInfo& info, bool& badHDMI);
+};
+#endif
+
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
new file mode 100644
index 0000000000..1c20bbcd1c
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define INITGUID
+
+#include "AESinkDirectSound.h"
+#include "utils/Log.h"
+#include <initguid.h>
+#include <Mmreg.h>
+#include <list>
+#include "threads/SingleLock.h"
+#include "utils/SystemInfo.h"
+#include "utils/TimeUtils.h"
+#include "utils/CharsetConverter.h"
+#include <Audioclient.h>
+#include <Mmreg.h>
+#include <mmdeviceapi.h>
+#include <Functiondiscoverykeys_devpkey.h>
+#include <Rpc.h>
+#pragma comment(lib, "Rpcrt4.lib")
+
+extern HWND g_hWnd;
+
+DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+
+#define EXIT_ON_FAILURE(hr, reason, ...) if(FAILED(hr)) {CLog::Log(LOGERROR, reason " - %s", __VA_ARGS__, WASAPIErrToStr(hr)); goto failed;}
+
+#define ERRTOSTR(err) case err: return #err
+
+#define DS_SPEAKER_COUNT 8
+static const unsigned int DSChannelOrder[] = {SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT};
+static const enum AEChannel AEChannelNames[] = {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_NULL};
+
+static enum AEChannel layoutsByChCount[9][9] = {
+ {AE_CH_NULL},
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_BC, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_LFE, AE_CH_NULL}};
+
+struct DSDevice
+{
+ std::string name;
+ LPGUID lpGuid;
+};
+
+struct winEndpointsToAEDeviceType
+{
+ std::string winEndpointType;
+ AEDeviceType aeDeviceType;
+};
+
+static const winEndpointsToAEDeviceType winEndpoints[EndpointFormFactor_enum_count] =
+{
+ {"Network Device - ", AE_DEVTYPE_PCM},
+ {"Speakers - ", AE_DEVTYPE_PCM},
+ {"LineLevel - ", AE_DEVTYPE_PCM},
+ {"Headphones - ", AE_DEVTYPE_PCM},
+ {"Microphone - ", AE_DEVTYPE_PCM},
+ {"Headset - ", AE_DEVTYPE_PCM},
+ {"Handset - ", AE_DEVTYPE_PCM},
+ {"Digital Passthrough - ", AE_DEVTYPE_IEC958},
+ {"SPDIF - ", AE_DEVTYPE_IEC958},
+ {"HDMI - ", AE_DEVTYPE_HDMI},
+ {"Unknown - ", AE_DEVTYPE_PCM},
+};
+
+static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
+{
+ DSDevice dev;
+ std::list<DSDevice> &enumerator = *static_cast<std::list<DSDevice>*>(lpContext);
+
+ dev.name = std::string(lpcstrDescription);
+
+ dev.lpGuid = lpGuid;
+
+ if (lpGuid)
+ enumerator.push_back(dev);
+
+ return TRUE;
+}
+
+CAESinkDirectSound::CAESinkDirectSound() :
+ m_initialized (false),
+ m_pBuffer (NULL ),
+ m_pDSound (NULL ),
+ m_BufferOffset (0 ),
+ m_CacheLen (0 ),
+ m_dwChunkSize (0 ),
+ m_dwBufferLen (0 )
+{
+ m_channelLayout.Reset();
+}
+
+CAESinkDirectSound::~CAESinkDirectSound()
+{
+ Deinitialize();
+}
+
+bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
+{
+ if (m_initialized)
+ return false;
+
+ LPGUID deviceGUID = NULL;
+ RPC_CSTR wszUuid = NULL;
+ HRESULT hr;
+ std::list<DSDevice> DSDeviceList;
+ DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);
+
+ for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++)
+ {
+ if ((*itt).lpGuid)
+ {
+ hr = (UuidToString((*itt).lpGuid, &wszUuid));
+ std::string sztmp = (char*)wszUuid;
+ std::string szGUID = "{" + std::string(sztmp.begin(), sztmp.end()) + "}";
+ if (strcasecmp(szGUID.c_str(), device.c_str()) == 0)
+ {
+ deviceGUID = (*itt).lpGuid;
+ break;
+ }
+ }
+ if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
+ }
+
+ hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL);
+
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device.");
+ CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
+ return false;
+ }
+
+ hr = m_pDSound->SetCooperativeLevel(g_hWnd, DSSCL_PRIORITY);
+
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device cooperative level.");
+ CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
+ m_pDSound->Release();
+ return false;
+ }
+
+ WAVEFORMATEXTENSIBLE wfxex = {0};
+
+ //fill waveformatex
+ ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ wfxex.Format.nChannels = format.m_channelLayout.Count();
+ wfxex.Format.nSamplesPerSec = format.m_sampleRate;
+ if (AE_IS_RAW(format.m_dataFormat))
+ {
+ wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
+ wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Format.nChannels = 2;
+ }
+ else
+ {
+ wfxex.dwChannelMask = SpeakerMaskFromAEChannels(format.m_channelLayout);
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ wfxex.Format.wBitsPerSample = 32;
+ }
+
+ wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+
+ m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;
+
+ unsigned int uiFrameCount = (int)(format.m_sampleRate * 0.01); //default to 10ms chunks
+ m_dwFrameSize = wfxex.Format.nBlockAlign;
+ m_dwChunkSize = m_dwFrameSize * uiFrameCount;
+ m_dwBufferLen = m_dwChunkSize * 6; //60ms total buffer
+
+ // fill in the secondary sound buffer descriptor
+ DSBUFFERDESC dsbdesc;
+ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
+ dsbdesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
+ | DSBCAPS_GLOBALFOCUS; /** Allows background playing */
+
+ if (!g_sysinfo.IsVistaOrHigher())
+ dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; /** Needed for 5.1 on emu101k, fails by design on Vista */
+
+ dsbdesc.dwBufferBytes = m_dwBufferLen;
+ dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;
+
+ // now create the stream buffer
+ HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
+ if (res != DS_OK)
+ {
+ if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
+ {
+ SAFE_RELEASE(m_pBuffer);
+ CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res));
+ // Try without DSBCAPS_LOCHARDWARE
+ dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
+ res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
+ }
+ if (res != DS_OK)
+ {
+ SAFE_RELEASE(m_pBuffer);
+ CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
+ return false;
+ }
+ }
+ CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");
+
+ m_pBuffer->Stop();
+
+ AEChannelsFromSpeakerMask(wfxex.dwChannelMask);
+ format.m_channelLayout = m_channelLayout;
+ format.m_frames = uiFrameCount;
+ format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
+ format.m_frameSize = (AE_IS_RAW(format.m_dataFormat) ? wfxex.Format.wBitsPerSample >> 3 : sizeof(float)) * format.m_channelLayout.Count();
+ format.m_dataFormat = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT;
+
+ m_format = format;
+ m_device = device;
+
+ m_BufferOffset = 0;
+ m_CacheLen = 0;
+ m_LastCacheCheck = XbmcThreads::SystemClockMillis();
+ m_initialized = true;
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Initializing DirectSound with the following parameters:");
+ CLog::Log(LOGDEBUG, " Audio Device : %s", device);
+ CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex.Format.nSamplesPerSec);
+ CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
+ CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
+ CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex.Format.nChannels);
+ CLog::Log(LOGDEBUG, " Block Align : %d", wfxex.Format.nBlockAlign);
+ CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex.Format.nAvgBytesPerSec);
+ CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex.Samples.wSamplesPerBlock);
+ CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex.Format.cbSize);
+ CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str());
+ CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex.dwChannelMask);
+ CLog::Log(LOGDEBUG, " Frames : %d", format.m_frames);
+ CLog::Log(LOGDEBUG, " Frame Samples : %d", format.m_frameSamples);
+ CLog::Log(LOGDEBUG, " Frame Size : %d", format.m_frameSize);
+
+ return true;
+}
+
+void CAESinkDirectSound::Deinitialize()
+{
+ if (!m_initialized)
+ return;
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Cleaning up");
+
+ if (m_pBuffer)
+ {
+ m_pBuffer->Stop();
+ SAFE_RELEASE(m_pBuffer);
+ }
+
+ if (m_pDSound)
+ {
+ m_pDSound->Release();
+ }
+
+ m_initialized = false;
+ m_pBuffer = NULL;
+ m_pDSound = NULL;
+ m_BufferOffset = 0;
+ m_CacheLen = 0;
+ m_dwChunkSize = 0;
+ m_dwBufferLen = 0;
+}
+
+bool CAESinkDirectSound::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ if (!m_initialized)
+ return false;
+
+ u_int notCompatible = 0;
+ const u_int numTests = 6;
+ std::string strDiffBecause ("");
+ static const char* compatibleParams[numTests] = {":Devices",
+ ":Channels",
+ ":Sample Rates",
+ ":Data Formats",
+ ":Bluray Formats",
+ ":Passthrough Formats"};
+
+ notCompatible = (notCompatible +!((AE_IS_RAW(format.m_dataFormat) == AE_IS_RAW(m_encodedFormat)) ||
+ (!AE_IS_RAW(format.m_dataFormat) == !AE_IS_RAW(m_encodedFormat)))) << 1;
+ notCompatible = (notCompatible +!((format.m_dataFormat == AE_FMT_EAC3) ||
+ (format.m_dataFormat == AE_FMT_DTSHD ||
+ (format.m_dataFormat == AE_FMT_TRUEHD)))) << 1;
+ notCompatible = (notCompatible + !(format.m_dataFormat == m_format.m_dataFormat)) << 1;
+ notCompatible = (notCompatible + !(format.m_sampleRate == m_format.m_sampleRate)) << 1;
+ notCompatible = (notCompatible + !(format.m_channelLayout.Count() == m_format.m_channelLayout.Count())) << 1;
+ notCompatible = (notCompatible + !(m_device == device));
+
+ if (!notCompatible)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Formats compatible - reusing existing sink");
+ return true;
+ }
+
+ for (int i = 0; i < numTests ; i++)
+ {
+ strDiffBecause += (notCompatible & 0x01) ? (std::string) compatibleParams[i] : "";
+ notCompatible = notCompatible >> 1;
+ }
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Formats Incompatible due to different %s", strDiffBecause.c_str());
+ return false;
+}
+
+unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames)
+{
+ if (!m_initialized)
+ return 0;
+
+ DWORD total = m_dwFrameSize * frames;
+ DWORD len = total;
+ unsigned char* pBuffer = (unsigned char*)data;
+
+ DWORD bufferStatus = 0;
+ m_pBuffer->GetStatus(&bufferStatus);
+ if (bufferStatus & DSBSTATUS_BUFFERLOST)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__ ": Buffer allocation was lost. Restoring buffer.");
+ m_pBuffer->Restore();
+ }
+
+ while (GetSpace() < total)
+ Sleep(1);
+
+ while (len)
+ {
+ LPVOID start = NULL, startWrap = NULL;
+ DWORD size = 0, sizeWrap = 0;
+ if (m_BufferOffset >= m_dwBufferLen) // Wrap-around manually
+ m_BufferOffset = 0;
+ HRESULT res = m_pBuffer->Lock(m_BufferOffset, m_dwChunkSize, &start, &size, &startWrap, &sizeWrap, 0);
+ if (DS_OK != res)
+ {
+ CLog::Log(LOGERROR, __FUNCTION__ ": Unable to lock buffer at offset %u. HRESULT: 0x%08x", m_BufferOffset, res);
+ break;
+ }
+
+ memcpy(start, pBuffer, size);
+
+ pBuffer += size;
+ len -= size;
+
+ m_BufferOffset += size;
+ if (startWrap) // Write-region wraps to beginning of buffer
+ {
+ memcpy(startWrap, pBuffer, sizeWrap);
+ m_BufferOffset = sizeWrap;
+
+ pBuffer += sizeWrap;
+ len -= sizeWrap;
+ }
+
+ m_CacheLen += size + sizeWrap; // This data is now in the cache
+ m_pBuffer->Unlock(start, size, startWrap, sizeWrap);
+ }
+
+ CheckPlayStatus();
+
+ return (total - len) / m_dwFrameSize; // Frames used
+}
+
+void CAESinkDirectSound::Stop()
+{
+ if (m_pBuffer)
+ m_pBuffer->Stop();
+}
+
+double CAESinkDirectSound::GetDelay()
+{
+ if (!m_initialized)
+ return 0.0;
+
+ /* Make sure we know how much data is in the cache */
+ UpdateCacheStatus();
+
+ /** returns current cached data duration in seconds */
+ double delay = (double)m_CacheLen / (double)m_AvgBytesPerSec;
+ return delay;
+}
+
+double CAESinkDirectSound::GetCacheTime()
+{
+ if (!m_initialized)
+ return 0.0;
+
+ /* Make sure we know how much data is in the cache */
+ UpdateCacheStatus();
+
+ /** returns current cached data duration in seconds */
+ double delay = (double)m_CacheLen / (double)m_AvgBytesPerSec;
+ return delay;
+}
+
+double CAESinkDirectSound::GetCacheTotal()
+{
+ /** returns total cache capacity in seconds */
+ return (double)m_dwBufferLen / (double)m_AvgBytesPerSec;
+}
+
+void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
+{
+ CAEDeviceInfo deviceInfo;
+
+ IMMDeviceEnumerator* pEnumerator = NULL;
+ IMMDeviceCollection* pEnumDevices = NULL;
+
+ WAVEFORMATEX* pwfxex = NULL;
+ HRESULT hr;
+
+ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr)
+
+ UINT uiCount = 0;
+
+ hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
+
+ hr = pEnumDevices->GetCount(&uiCount);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
+
+ for (UINT i = 0; i < uiCount; i++)
+ {
+ IMMDevice *pDevice = NULL;
+ IPropertyStore *pProperty = NULL;
+ PROPVARIANT varName;
+ PropVariantInit(&varName);
+
+ deviceInfo.m_channels.Reset();
+ deviceInfo.m_dataFormats.clear();
+ deviceInfo.m_sampleRates.clear();
+
+ hr = pEnumDevices->Item(i, &pDevice);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed.");
+ goto failed;
+ }
+
+ hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed.");
+ SAFE_RELEASE(pDevice);
+ goto failed;
+ }
+
+ hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+
+ std::wstring strRawFriendlyName(varName.pwszVal);
+ std::string strFriendlyName = std::string(strRawFriendlyName.begin(), strRawFriendlyName.end());
+
+ PropVariantClear(&varName);
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+
+ std::wstring strRawDevName(varName.pwszVal);
+ std::string strDevName = std::string(strRawDevName.begin(), strRawDevName.end());
+ PropVariantClear(&varName);
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+ std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType;
+ AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
+
+ PropVariantClear(&varName);
+
+ /* In shared mode Windows tells us what format the audio must be in. */
+ IAudioClient *pClient;
+ hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr));
+ }
+
+ //hr = pClient->GetMixFormat(&pwfxex);
+ hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName);
+ if (SUCCEEDED(hr))
+ {
+ WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData;
+ deviceInfo.m_channels = layoutsByChCount[std::min(smpwfxex->nChannels, (WORD) 2)];
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT));
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3));
+ deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 96000));
+ }
+ else
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetMixFormat failed (%s)", WASAPIErrToStr(hr));
+ }
+ pClient->Release();
+
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+
+ deviceInfo.m_deviceName = strDevName;
+ deviceInfo.m_displayName = strWinDevType.append(strFriendlyName);
+ deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName);
+ deviceInfo.m_deviceType = aeDeviceType;
+
+ /* Now logged by AESinkFactory on startup */
+ //CLog::Log(LOGDEBUG,"Audio Device %d: %s", i, ((std::string)deviceInfo).c_str());
+
+ deviceInfoList.push_back(deviceInfo);
+ }
+
+ return;
+
+failed:
+
+ if (FAILED(hr))
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr));
+
+ SAFE_RELEASE(pEnumDevices);
+ SAFE_RELEASE(pEnumerator);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CAESinkDirectSound::CheckPlayStatus()
+{
+ DWORD status = 0;
+ m_pBuffer->GetStatus(&status);
+
+ if (!(status & DSBSTATUS_PLAYING) && m_CacheLen != 0) // If we have some data, see if we can start playback
+ {
+ HRESULT hr = m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
+ dserr2str(hr);
+ CLog::Log(LOGDEBUG,__FUNCTION__ ": Resuming Playback");
+ }
+}
+
+void CAESinkDirectSound::UpdateCacheStatus()
+{
+ CSingleLock lock (m_runLock);
+ // TODO: Check to see if we may have cycled around since last time
+ unsigned int time = XbmcThreads::SystemClockMillis();
+ if (time == m_LastCacheCheck)
+ return; // Don't recalc more frequently than once/ms (that is our max resolution anyway)
+
+ DWORD playCursor = 0, writeCursor = 0;
+ HRESULT res = m_pBuffer->GetCurrentPosition(&playCursor, &writeCursor); // Get the current playback and safe write positions
+ if (DS_OK != res)
+ {
+ CLog::Log(LOGERROR,__FUNCTION__ ": GetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
+ return;
+ }
+
+ m_LastCacheCheck = time;
+ // Check the state of the ring buffer (P->O->W == underrun)
+ // These are the logical situations that can occur
+ // O: CurrentOffset W: WriteCursor P: PlayCursor
+ // | | | | | | | | | |
+ // ***O----W----P***** < underrun P > W && O < W (1)
+ // | | | | | | | | | |
+ // ---P****O----W----- < underrun O > P && O < W (2)
+ // | | | | | | | | | |
+ // ---W----P****O----- < underrun P > W && P < O (3)
+ // | | | | | | | | | |
+ // ***W****O----P***** P > W && P > O (4)
+ // | | | | | | | | | |
+ // ---P****W****O----- P < W && O > W (5)
+ // | | | | | | | | | |
+ // ***O----P****W***** P < W && O < P (6)
+
+ // Check for underruns
+ if ((playCursor > writeCursor && m_BufferOffset < writeCursor) || // (1)
+ (playCursor < m_BufferOffset && m_BufferOffset < writeCursor) || // (2)
+ (playCursor > writeCursor && playCursor < m_BufferOffset)) // (3)
+ {
+ CLog::Log(LOGWARNING, "CWin32DirectSound::GetSpace - buffer underrun - W:%u, P:%u, O:%u.", writeCursor, playCursor, m_BufferOffset);
+ m_BufferOffset = writeCursor; // Catch up
+ m_pBuffer->Stop(); // Wait until someone gives us some data to restart playback (prevents glitches)
+ }
+
+ // Calculate available space in the ring buffer
+ if (playCursor == m_BufferOffset && m_BufferOffset == writeCursor) // Playback is stopped and we are all at the same place
+ m_CacheLen = 0;
+ else if (m_BufferOffset > playCursor)
+ m_CacheLen = m_BufferOffset - playCursor;
+ else
+ m_CacheLen = m_dwBufferLen - (playCursor - m_BufferOffset);
+}
+
+unsigned int CAESinkDirectSound::GetSpace()
+{
+ CSingleLock lock (m_runLock);
+ UpdateCacheStatus();
+ unsigned int space = m_dwBufferLen - m_CacheLen;
+
+ // We can never allow the internal buffers to fill up complete
+ // as we get confused between if the buffer is full or empty
+ // so never allow the last chunk to be added
+ if (space > m_dwChunkSize)
+ return space - m_dwChunkSize;
+ else
+ return 0;
+}
+
+void CAESinkDirectSound::AEChannelsFromSpeakerMask(DWORD speakers)
+{
+ int j = 0;
+
+ m_channelLayout.Reset();
+
+ for (int i = 0; i < DS_SPEAKER_COUNT; i++)
+ {
+ if (speakers & DSChannelOrder[i])
+ m_channelLayout += AEChannelNames[i];
+ }
+}
+
+DWORD CAESinkDirectSound::SpeakerMaskFromAEChannels(const CAEChannelInfo &channels)
+{
+ DWORD mask = 0;
+
+ for (unsigned int i = 0; i < channels.Count(); i++)
+ {
+ for (unsigned int j = 0; j < DS_SPEAKER_COUNT; j++)
+ if (channels[i] == AEChannelNames[j])
+ mask |= DSChannelOrder[j];
+ }
+
+ return mask;
+}
+
+const char *CAESinkDirectSound::dserr2str(int err)
+{
+ switch (err)
+ {
+ case DS_OK: return "DS_OK";
+ case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
+ case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
+ case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
+ case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
+ case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
+ case DSERR_GENERIC: return "DSERR_GENERIC";
+ case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
+ case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
+ case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
+ case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
+ case DSERR_NODRIVER: return "DSERR_NODRIVER";
+ case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
+ case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
+ case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
+ case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
+ case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
+ case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
+ case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
+ default: return "unknown";
+ }
+}
+
+const char *CAESinkDirectSound::WASAPIErrToStr(HRESULT err)
+{
+ switch(err)
+ {
+ ERRTOSTR(AUDCLNT_E_NOT_INITIALIZED);
+ ERRTOSTR(AUDCLNT_E_ALREADY_INITIALIZED);
+ ERRTOSTR(AUDCLNT_E_WRONG_ENDPOINT_TYPE);
+ ERRTOSTR(AUDCLNT_E_DEVICE_INVALIDATED);
+ ERRTOSTR(AUDCLNT_E_NOT_STOPPED);
+ ERRTOSTR(AUDCLNT_E_BUFFER_TOO_LARGE);
+ ERRTOSTR(AUDCLNT_E_OUT_OF_ORDER);
+ ERRTOSTR(AUDCLNT_E_UNSUPPORTED_FORMAT);
+ ERRTOSTR(AUDCLNT_E_INVALID_SIZE);
+ ERRTOSTR(AUDCLNT_E_DEVICE_IN_USE);
+ ERRTOSTR(AUDCLNT_E_BUFFER_OPERATION_PENDING);
+ ERRTOSTR(AUDCLNT_E_THREAD_NOT_REGISTERED);
+ ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED);
+ ERRTOSTR(AUDCLNT_E_ENDPOINT_CREATE_FAILED);
+ ERRTOSTR(AUDCLNT_E_SERVICE_NOT_RUNNING);
+ ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED);
+ ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_ONLY);
+ ERRTOSTR(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL);
+ ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_SET);
+ ERRTOSTR(AUDCLNT_E_INCORRECT_BUFFER_SIZE);
+ ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_ERROR);
+ ERRTOSTR(AUDCLNT_E_CPUUSAGE_EXCEEDED);
+ ERRTOSTR(AUDCLNT_E_BUFFER_ERROR);
+ ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED);
+ ERRTOSTR(AUDCLNT_E_INVALID_DEVICE_PERIOD);
+ ERRTOSTR(E_POINTER);
+ ERRTOSTR(E_INVALIDARG);
+ ERRTOSTR(E_OUTOFMEMORY);
+ default: break;
+ }
+ return NULL;
+}
+
+
+
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
new file mode 100644
index 0000000000..4ee55e4072
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
@@ -0,0 +1,79 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Interfaces/AESink.h"
+#include <stdint.h>
+#include <dsound.h>
+#include "../Utils/AEDeviceInfo.h"
+
+#include "threads/CriticalSection.h"
+
+class CAESinkDirectSound : public IAESink
+{
+public:
+ virtual const char *GetName() { return "DirectSound"; }
+
+ CAESinkDirectSound();
+ virtual ~CAESinkDirectSound();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual void Stop ();
+ virtual double GetDelay ();
+ virtual double GetCacheTime ();
+ virtual double GetCacheTotal ();
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList);
+private:
+ void AEChannelsFromSpeakerMask(DWORD speakers);
+ DWORD SpeakerMaskFromAEChannels(const CAEChannelInfo &channels);
+ void CheckPlayStatus();
+ void UpdateCacheStatus();
+ unsigned int GetSpace();
+ const char *dserr2str(int err);
+
+ static const char *WASAPIErrToStr(HRESULT err);
+
+ LPDIRECTSOUNDBUFFER m_pBuffer;
+ LPDIRECTSOUND8 m_pDSound;
+
+ AEAudioFormat m_format;
+ enum AEDataFormat m_encodedFormat;
+ CAEChannelInfo m_channelLayout;
+ std::string m_device;
+
+ unsigned int m_AvgBytesPerSec;
+
+ unsigned int m_dwChunkSize;
+ unsigned int m_dwFrameSize;
+ unsigned int m_dwBufferLen;
+
+ unsigned int m_BufferOffset;
+ unsigned int m_CacheLen;
+ unsigned int m_LastCacheCheck;
+
+ bool m_running;
+ bool m_initialized;
+ CCriticalSection m_runLock;
+};
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
new file mode 100644
index 0000000000..d42d239bf1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#include "AESinkNULL.h"
+#include <stdint.h>
+#include <limits.h>
+
+#include "guilib/LocalizeStrings.h"
+#include "dialogs/GUIDialogKaiToast.h"
+
+#include "Utils/AEUtil.h"
+#include "utils/StdString.h"
+#include "utils/log.h"
+#include "utils/MathUtils.h"
+#include "utils/TimeUtils.h"
+#include "settings/GUISettings.h"
+
+CAESinkNULL::CAESinkNULL() {
+}
+
+CAESinkNULL::~CAESinkNULL()
+{
+}
+
+bool CAESinkNULL::Initialize(AEAudioFormat &format, std::string &device)
+{
+ m_msPerFrame = 1000.0f / format.m_sampleRate;
+ m_ts = 0;
+
+ format.m_dataFormat = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT;
+ format.m_frames = format.m_sampleRate / 1000 * 500; /* 500ms */
+ format.m_frameSamples = format.m_channelLayout.Count();
+ format.m_frameSize = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+
+#if 0
+ /* FIXME, CAUSES A DEADLOCK */
+ /* display failure notification */
+ CGUIDialogKaiToast::QueueNotification(
+ CGUIDialogKaiToast::Error,
+ g_localizeStrings.Get(34402),
+ g_localizeStrings.Get(34403),
+ TOAST_DISPLAY_TIME,
+ false
+ );
+#endif
+
+ return true;
+}
+
+void CAESinkNULL::Deinitialize()
+{
+}
+
+bool CAESinkNULL::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ return false;
+}
+
+double CAESinkNULL::GetDelay()
+{
+ return std::max(0.0, (double)(m_ts - CurrentHostCounter()) / 1000000.0f);
+}
+
+unsigned int CAESinkNULL::AddPackets(uint8_t *data, unsigned int frames)
+{
+ float timeout = m_msPerFrame * frames;
+ m_ts = CurrentHostCounter() + MathUtils::round_int(timeout * 1000000.0f);
+ Sleep(MathUtils::round_int(timeout));
+ return frames;
+}
+
+void CAESinkNULL::Drain()
+{
+}
+
+void CAESinkNULL::EnumerateDevices (AEDeviceList &devices, bool passthrough)
+{
+ /* we never return any devices */
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
new file mode 100644
index 0000000000..461b1b0940
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#include "Interfaces/AESink.h"
+#include <stdint.h>
+
+class CAESinkNULL : public IAESink
+{
+public:
+ virtual const char *GetName() { return "NULL"; }
+
+ CAESinkNULL();
+ virtual ~CAESinkNULL();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual double GetDelay ();
+ virtual double GetCacheTime () { return 0.0; }
+ virtual double GetCacheTotal () { return 0.0; }
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ virtual void Drain ();
+ static void EnumerateDevices(AEDeviceList &devices, bool passthrough);
+private:
+ int64_t m_ts;
+ float m_msPerFrame;
+};
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
new file mode 100644
index 0000000000..2eafb47031
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AESinkOSS.h"
+#include <stdint.h>
+#include <limits.h>
+
+#include "Utils/AEUtil.h"
+#include "utils/StdString.h"
+#include "utils/log.h"
+#include "threads/SingleLock.h"
+#include <sstream>
+
+#include <sys/ioctl.h>
+
+#if defined(OSS4) || defined(__FreeBSD__)
+ #include <sys/soundcard.h>
+#else
+ #include <linux/soundcard.h>
+#endif
+
+#define OSS_FRAMES 256
+
+static enum AEChannel OSSChannelMap[9] =
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_FC, AE_CH_LFE, AE_CH_SL, AE_CH_SR, AE_CH_NULL};
+
+#if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
+static int OSSSampleRateList[] =
+{
+ 5512,
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ 384000,
+ 0
+};
+#endif
+
+CAESinkOSS::CAESinkOSS()
+{
+}
+
+CAESinkOSS::~CAESinkOSS()
+{
+ Deinitialize();
+}
+
+std::string CAESinkOSS::GetDeviceUse(const AEAudioFormat format, const std::string device)
+{
+#ifdef OSS4
+ if (AE_IS_RAW(format.m_dataFormat))
+ {
+ if (device.find_first_of('/') != 0)
+ return "/dev/dsp_ac3";
+ return device;
+ }
+
+ if (device.find_first_of('/') != 0)
+ return "/dev/dsp_multich";
+#else
+ if (device.find_first_of('/') != 0)
+ return "/dev/dsp";
+#endif
+
+ return device;
+}
+
+bool CAESinkOSS::Initialize(AEAudioFormat &format, std::string &device)
+{
+ m_initFormat = format;
+ format.m_channelLayout = GetChannelLayout(format);
+ device = GetDeviceUse(format, device);
+
+#ifdef __linux__
+ /* try to open in exclusive mode first (no software mixing) */
+ m_fd = open(device.c_str(), O_WRONLY | O_EXCL, 0);
+ if (!m_fd)
+#endif
+ m_fd = open(device.c_str(), O_WRONLY, 0);
+ if (!m_fd)
+ {
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to open the audio device: %s", device.c_str());
+ return false;
+ }
+
+ int format_mask;
+ if (ioctl(m_fd, SNDCTL_DSP_GETFMTS, &format_mask) == -1)
+ {
+ close(m_fd);
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get supported formats, assuming AFMT_S16_NE");
+ return false;
+ }
+
+#ifdef OSS4
+ bool useCooked = true;
+#endif
+
+ int oss_fmt = 0;
+#ifdef AFMT_FLOAT
+ if ((format.m_dataFormat == AE_FMT_FLOAT) && (format_mask & AFMT_FLOAT ))
+ oss_fmt = AFMT_FLOAT;
+ else
+#endif
+#ifdef AFMT_S32_NE
+ if ((format.m_dataFormat == AE_FMT_S32NE) && (format_mask & AFMT_S32_NE))
+ oss_fmt = AFMT_S32_NE;
+ else if ((format.m_dataFormat == AE_FMT_S32BE) && (format_mask & AFMT_S32_BE))
+ oss_fmt = AFMT_S32_BE;
+ else if ((format.m_dataFormat == AE_FMT_S32LE) && (format_mask & AFMT_S32_LE))
+ oss_fmt = AFMT_S32_LE;
+ else
+#endif
+ if ((format.m_dataFormat == AE_FMT_S16NE) && (format_mask & AFMT_S16_NE))
+ oss_fmt = AFMT_S16_NE;
+ else if ((format.m_dataFormat == AE_FMT_S16BE) && (format_mask & AFMT_S16_BE))
+ oss_fmt = AFMT_S16_BE;
+ else if ((format.m_dataFormat == AE_FMT_S16LE) && (format_mask & AFMT_S16_LE))
+ oss_fmt = AFMT_S16_LE;
+ else if ((format.m_dataFormat == AE_FMT_S8 ) && (format_mask & AFMT_S8 ))
+ oss_fmt = AFMT_S8;
+ else if ((format.m_dataFormat == AE_FMT_U8 ) && (format_mask & AFMT_U8 ))
+ oss_fmt = AFMT_U8;
+ else if ((AE_IS_RAW(format.m_dataFormat) ) && (format_mask & AFMT_AC3 ))
+ oss_fmt = AFMT_AC3;
+ else if (AE_IS_RAW(format.m_dataFormat))
+ {
+ close(m_fd);
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable RAW output format");
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGINFO, "CAESinkOSS::Initialize - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));
+
+ /* fallback to the best supported format */
+#ifdef AFMT_FLOAT
+ if (format_mask & AFMT_FLOAT )
+ {
+ oss_fmt = AFMT_FLOAT;
+ format.m_dataFormat = AE_FMT_FLOAT;
+ }
+ else
+#endif
+#ifdef AFMT_S32_NE
+ if (format_mask & AFMT_S32_NE)
+ {
+ oss_fmt = AFMT_S32_NE;
+ format.m_dataFormat = AE_FMT_S32NE;
+ }
+ else if (format_mask & AFMT_S32_BE)
+ {
+ oss_fmt = AFMT_S32_BE;
+ format.m_dataFormat = AE_FMT_S32BE;
+ }
+ else if (format_mask & AFMT_S32_LE)
+ {
+ oss_fmt = AFMT_S32_LE;
+ format.m_dataFormat = AE_FMT_S32LE;
+ }
+ else
+#endif
+ if (format_mask & AFMT_S16_NE)
+ {
+ oss_fmt = AFMT_S16_NE;
+ format.m_dataFormat = AE_FMT_S16NE;
+ }
+ else if (format_mask & AFMT_S16_BE)
+ {
+ oss_fmt = AFMT_S16_BE;
+ format.m_dataFormat = AE_FMT_S16BE;
+ }
+ else if (format_mask & AFMT_S16_LE)
+ {
+ oss_fmt = AFMT_S16_LE;
+ format.m_dataFormat = AE_FMT_S16LE;
+ }
+ else if (format_mask & AFMT_S8 )
+ {
+ oss_fmt = AFMT_S8;
+ format.m_dataFormat = AE_FMT_S8;
+ }
+ else if (format_mask & AFMT_U8 )
+ {
+ oss_fmt = AFMT_U8;
+ format.m_dataFormat = AE_FMT_U8;
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable native output format, will try to use AE_FMT_S16NE anyway");
+ oss_fmt = AFMT_S16_NE;
+ format.m_dataFormat = AE_FMT_S16NE;
+#ifdef OSS4
+ /* dont use cooked if we did not find a native format, OSS might be able to convert */
+ useCooked = false;
+#endif
+ }
+ }
+
+#ifdef OSS4
+ if (useCooked)
+ {
+ int oss_cooked = 1;
+ if (ioctl(m_fd, SNDCTL_DSP_COOKEDMODE, &oss_cooked) == -1)
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set cooked mode");
+ }
+#endif
+
+ if (ioctl(m_fd, SNDCTL_DSP_SETFMT, &oss_fmt) == -1)
+ {
+ close(m_fd);
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the data format (%s)", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ return false;
+ }
+
+#ifndef OSS4
+#ifndef __FreeBSD__
+ int mask = 0;
+ for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
+ switch (format.m_channelLayout[i])
+ {
+ case AE_CH_FL:
+ case AE_CH_FR:
+ mask |= DSP_BIND_FRONT;
+ break;
+
+ case AE_CH_BL:
+ case AE_CH_BR:
+ mask |= DSP_BIND_SURR;
+ break;
+
+ case AE_CH_FC:
+ case AE_CH_LFE:
+ mask |= DSP_BIND_CENTER_LFE;
+ break;
+
+ default:
+ break;
+ }
+
+ /* try to set the channel mask, not all cards support this */
+ if (ioctl(m_fd, SNDCTL_DSP_BIND_CHANNEL, &mask) == -1)
+ {
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the channel mask");
+ /* get the configured channel mask */
+ if (ioctl(m_fd, SNDCTL_DSP_GETCHANNELMASK, &mask) == -1)
+ {
+ /* as not all cards support this so we just assume stereo if it fails */
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel mask, assuming stereo");
+ mask = DSP_BIND_FRONT;
+ }
+ }
+#endif
+#else /* OSS4 */
+ unsigned long long order = 0;
+ for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
+ switch (format.m_channelLayout[i])
+ {
+ case AE_CH_FL : order = (order << 4) | CHID_L ; break;
+ case AE_CH_FR : order = (order << 4) | CHID_R ; break;
+ case AE_CH_FC : order = (order << 4) | CHID_C ; break;
+ case AE_CH_LFE: order = (order << 4) | CHID_LFE; break;
+ case AE_CH_SL : order = (order << 4) | CHID_LS ; break;
+ case AE_CH_SR : order = (order << 4) | CHID_RS ; break;
+ case AE_CH_BL : order = (order << 4) | CHID_LR ; break;
+ case AE_CH_BR : order = (order << 4) | CHID_RR ; break;
+
+ default:
+ continue;
+ }
+
+ if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1)
+ {
+ if (ioctl(m_fd, SNDCTL_DSP_GET_CHNORDER, &order) == -1)
+ {
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel order, assuming CHNORDER_NORMAL");
+ order = CHNORDER_NORMAL;
+ }
+ }
+
+#endif
+
+ /* find the number we need to open to access the channels we need */
+ bool found = false;
+ int oss_ch = 0;
+ for (int ch = format.m_channelLayout.Count(); ch < 9; ++ch)
+ {
+ oss_ch = ch;
+ if (ioctl(m_fd, SNDCTL_DSP_CHANNELS, &oss_ch) != -1 && oss_ch >= (int)format.m_channelLayout.Count())
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to access the number of channels required, falling back");
+
+ int tmp = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count() * OSS_FRAMES;
+ int pos = 0;
+ while ((tmp & 0x1) == 0x0)
+ {
+ tmp = tmp >> 1;
+ ++pos;
+ }
+
+ int oss_frag = (4 << 16) | pos;
+ if (ioctl(m_fd, SNDCTL_DSP_SETFRAGMENT, &oss_frag) == -1)
+ CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the fragment size");
+
+ int oss_sr = format.m_sampleRate;
+ if (ioctl(m_fd, SNDCTL_DSP_SPEED, &oss_sr) == -1)
+ {
+ close(m_fd);
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the sample rate");
+ return false;
+ }
+
+ audio_buf_info bi;
+ if (ioctl(m_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
+ {
+ close(m_fd);
+ CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get the output buffer size");
+ return false;
+ }
+
+ format.m_sampleRate = oss_sr;
+ format.m_frameSize = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count();
+ format.m_frames = bi.fragsize / format.m_frameSize;
+ format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
+
+ m_device = device;
+ m_format = format;
+ return true;
+}
+
+void CAESinkOSS::Deinitialize()
+{
+ Stop();
+
+ if (m_fd)
+ close(m_fd);
+}
+
+inline CAEChannelInfo CAESinkOSS::GetChannelLayout(AEAudioFormat format)
+{
+ unsigned int count = 0;
+
+ if (format.m_dataFormat == AE_FMT_AC3 ||
+ format.m_dataFormat == AE_FMT_DTS ||
+ format.m_dataFormat == AE_FMT_EAC3)
+ count = 2;
+ else if (format.m_dataFormat == AE_FMT_TRUEHD ||
+ format.m_dataFormat == AE_FMT_DTSHD)
+ count = 8;
+ else
+ {
+ for (unsigned int c = 0; c < 8; ++c)
+ for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
+ if (format.m_channelLayout[i] == OSSChannelMap[c])
+ {
+ count = c + 1;
+ break;
+ }
+ }
+
+ CAEChannelInfo info;
+ for (unsigned int i = 0; i < count; ++i)
+ info += OSSChannelMap[i];
+
+ return info;
+}
+
+bool CAESinkOSS::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ AEAudioFormat tmp = format;
+ tmp.m_channelLayout = GetChannelLayout(format);
+
+ return (
+ tmp.m_sampleRate == m_initFormat.m_sampleRate &&
+ tmp.m_dataFormat == m_initFormat.m_dataFormat &&
+ tmp.m_channelLayout == m_initFormat.m_channelLayout &&
+ GetDeviceUse(tmp, device) == m_device
+ );
+}
+
+void CAESinkOSS::Stop()
+{
+#ifdef SNDCTL_DSP_RESET
+ if (m_fd)
+ ioctl(m_fd, SNDCTL_DSP_RESET, NULL);
+#endif
+}
+
+double CAESinkOSS::GetDelay()
+{
+ int delay;
+ if (ioctl(m_fd, SNDCTL_DSP_GETODELAY, &delay) == -1)
+ return 0.0f;
+
+ return (double)delay / (m_format.m_frameSize * m_format.m_sampleRate);
+}
+
+unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames)
+{
+ int size = frames * m_format.m_frameSize;
+ int wrote = write(m_fd, data, size);
+ if (wrote < 0)
+ {
+ CLog::Log(LOGERROR, "CAESinkOSS::AddPackets - Failed to write");
+ return frames;
+ }
+
+ return wrote / m_format.m_frameSize;
+}
+
+void CAESinkOSS::Drain()
+{
+ if (!m_fd)
+ return;
+
+ // ???
+}
+
+void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list)
+{
+ int mixerfd;
+ const char * mixerdev = "/dev/mixer";
+
+ if ((mixerfd = open(mixerdev, O_RDWR, 0)) == -1)
+ {
+ CLog::Log(LOGERROR,
+ "CAESinkOSS::EnumerateDevicesEx - Failed to open mixer: %s", mixerdev);
+ return;
+ }
+
+#if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
+ oss_sysinfo sysinfo;
+ if (ioctl(mixerfd, SNDCTL_SYSINFO, &sysinfo) == -1)
+ {
+ // hardware not supported
+ // OSSv4 required ?
+ close(mixerfd);
+ return;
+ }
+
+ for (int i = 0; i < sysinfo.numcards; ++i)
+ {
+ std::stringstream devicepath;
+ CAEDeviceInfo info;
+ oss_card_info cardinfo;
+
+ devicepath << "/dev/dsp" << i;
+ info.m_deviceName = devicepath.str();
+
+ cardinfo.card = i;
+ if (ioctl(mixerfd, SNDCTL_CARDINFO, &cardinfo) == -1)
+ break;
+
+ info.m_displayName = cardinfo.longname;
+ if (info.m_displayName.find("HDMI") != std::string::npos)
+ info.m_deviceType = AE_DEVTYPE_HDMI;
+ else if (info.m_displayName.find("Digital") != std::string::npos)
+ info.m_deviceType = AE_DEVTYPE_IEC958;
+ else
+ info.m_deviceType = AE_DEVTYPE_PCM;
+
+ oss_audioinfo ainfo;
+ memset(&ainfo, 0, sizeof(ainfo));
+ ainfo.dev = i;
+ if (ioctl(mixerfd, SNDCTL_AUDIOINFO, &ainfo) != -1) {
+#if 0
+ if (ainfo.oformats & AFMT_S32_LE)
+ info.m_dataFormats.push_back(AE_FMT_S32LE);
+ if (ainfo.oformats & AFMT_S16_LE)
+ info.m_dataFormats.push_back(AE_FMT_S16LE);
+#endif
+ for (int j = 0;
+ j < ainfo.max_channels && AE_CH_NULL != OSSChannelMap[j];
+ ++j)
+ info.m_channels += OSSChannelMap[j];
+
+ for (int *rate = OSSSampleRateList; *rate != 0; ++rate)
+ if (*rate >= ainfo.min_rate && *rate <= ainfo.max_rate)
+ info.m_sampleRates.push_back(*rate);
+ }
+ list.push_back(info);
+ }
+
+ close(mixerfd);
+#endif
+}
+
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
new file mode 100644
index 0000000000..4010d7778f
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Interfaces/AESink.h"
+#include "Utils/AEDeviceInfo.h"
+#include <stdint.h>
+
+#include "threads/CriticalSection.h"
+
+class CAESinkOSS : public IAESink
+{
+public:
+ virtual const char *GetName() { return "OSS"; }
+
+ CAESinkOSS();
+ virtual ~CAESinkOSS();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual void Stop ();
+ virtual double GetDelay ();
+ virtual double GetCacheTime () { return 0.0; } /* FIXME */
+ virtual double GetCacheTotal () { return 0.0; } /* FIXME */
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ virtual void Drain ();
+ static void EnumerateDevicesEx(AEDeviceInfoList &list);
+private:
+ int m_fd;
+ std::string m_device;
+ AEAudioFormat m_initFormat;
+ AEAudioFormat m_format;
+
+ CAEChannelInfo GetChannelLayout(AEAudioFormat format);
+ std::string GetDeviceUse(const AEAudioFormat format, const std::string device);
+};
+
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp
new file mode 100644
index 0000000000..0962aca145
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#include "AESinkProfiler.h"
+#include <stdint.h>
+#include <limits.h>
+
+#include "Utils/AEUtil.h"
+#include "utils/StdString.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "settings/GUISettings.h"
+
+CAESinkProfiler::CAESinkProfiler()
+{
+}
+
+CAESinkProfiler::~CAESinkProfiler()
+{
+}
+
+bool CAESinkProfiler::Initialize(AEAudioFormat &format, std::string &device)
+{
+ if (AE_IS_RAW(format.m_dataFormat))
+ return false;
+
+ format.m_sampleRate = 192000;
+ format.m_channelLayout = AE_CH_LAYOUT_7_1;
+ format.m_dataFormat = AE_FMT_S32LE;
+ format.m_frames = 30720;
+ format.m_frameSamples = format.m_channelLayout.Count();
+ format.m_frameSize = format.m_frameSamples * sizeof(float);
+ return true;
+}
+
+void CAESinkProfiler::Deinitialize()
+{
+}
+
+bool CAESinkProfiler::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ if (AE_IS_RAW(format.m_dataFormat))
+ return false;
+
+ if (format.m_dataFormat != AE_FMT_FLOAT)
+ return false;
+
+ return true;
+}
+
+double CAESinkProfiler::GetDelay()
+{
+ return 0.0f;
+}
+
+unsigned int CAESinkProfiler::AddPackets(uint8_t *data, unsigned int frames)
+{
+ int64_t ts = CurrentHostCounter();
+ CLog::Log(LOGDEBUG, "CAESinkProfiler::AddPackets - latency %f ms", (float)(ts - m_ts) / 1000000.0f);
+ m_ts = ts;
+ return frames;
+}
+
+void CAESinkProfiler::Drain()
+{
+}
+
+void CAESinkProfiler::EnumerateDevices (AEDeviceList &devices, bool passthrough)
+{
+ devices.push_back(AEDevice("Profiler", "Profiler"));
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h
new file mode 100644
index 0000000000..9edb6c2802
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#include "Interfaces/AESink.h"
+#include <stdint.h>
+
+class CAESinkProfiler : public IAESink
+{
+public:
+ virtual const char *GetName() { return "Profiler"; }
+
+ CAESinkProfiler();
+ virtual ~CAESinkProfiler();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual double GetDelay ();
+ virtual double GetCacheTime () { return 0.0; }
+ virtual double GetCacheTotal () { return 0.0; }
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ virtual void Drain ();
+ static void EnumerateDevices(AEDeviceList &devices, bool passthrough);
+private:
+ int64_t m_ts;
+};
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
new file mode 100644
index 0000000000..c40334a359
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
@@ -0,0 +1,1279 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AESinkWASAPI.h"
+#include <Audioclient.h>
+#include <avrt.h>
+#include <initguid.h>
+#include <Mmreg.h>
+#include <stdint.h>
+
+#include "../Utils/AEUtil.h"
+#include "settings/GUISettings.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StdString.h"
+#include "utils/log.h"
+#include "threads/SingleLock.h"
+#include "utils/CharsetConverter.h"
+#include "../Utils/AEDeviceInfo.h"
+#include <Mmreg.h>
+#include <mmdeviceapi.h>
+
+#pragma comment(lib, "Avrt.lib")
+
+const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
+const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
+const IID IID_IAudioClient = __uuidof(IAudioClient);
+const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+
+static const unsigned int WASAPISampleRateCount = 10;
+static const unsigned int WASAPISampleRates[] = {384000, 192000, 176400, 96000, 88200, 48000, 44100, 32000, 22050, 11025};
+
+#define WASAPI_SPEAKER_COUNT 21
+static const unsigned int WASAPIChannelOrder[] = {AE_CH_RAW,
+ SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER,
+ SPEAKER_LOW_FREQUENCY, SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
+ SPEAKER_FRONT_LEFT_OF_CENTER, SPEAKER_FRONT_RIGHT_OF_CENTER,
+ SPEAKER_BACK_CENTER, SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
+ SPEAKER_TOP_FRONT_LEFT, SPEAKER_TOP_FRONT_RIGHT, SPEAKER_TOP_FRONT_CENTER,
+ SPEAKER_TOP_CENTER, SPEAKER_TOP_BACK_LEFT, SPEAKER_TOP_BACK_RIGHT,
+ SPEAKER_TOP_BACK_CENTER, SPEAKER_RESERVED, SPEAKER_RESERVED};
+
+static const enum AEChannel AEChannelNames[] = {AE_CH_RAW,
+ AE_CH_FL, AE_CH_FR, AE_CH_FC,
+ AE_CH_LFE, AE_CH_BL, AE_CH_BR,
+ AE_CH_FLOC, AE_CH_FROC,
+ AE_CH_BC, AE_CH_SL, AE_CH_SR,
+ AE_CH_TFL, AE_CH_TFR, AE_CH_TFC ,
+ AE_CH_TC , AE_CH_TBL, AE_CH_TBR,
+ AE_CH_TBC, AE_CH_BLOC, AE_CH_BROC};
+
+static enum AEChannel layoutsByChCount[9][9] = {
+ {AE_CH_NULL},
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_BC, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_LFE, AE_CH_NULL}};
+
+struct sampleFormat
+{
+ GUID subFormat;
+ unsigned int bitsPerSample;
+ unsigned int validBitsPerSample;
+ AEDataFormat subFormatType;
+};
+
+//Sample formats go from float -> 32 bit int -> 24 bit int (packed in 32) -> 16 bit int
+static const sampleFormat testFormats[] = { {KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32, AE_FMT_FLOAT},
+ {KSDATAFORMAT_SUBTYPE_PCM, 32, 32, AE_FMT_S32NE},
+ {KSDATAFORMAT_SUBTYPE_PCM, 32, 24, AE_FMT_S24NE4},
+ {KSDATAFORMAT_SUBTYPE_PCM, 16, 16, AE_FMT_S16NE} };
+
+struct winEndpointsToAEDeviceType
+{
+ std::string winEndpointType;
+ AEDeviceType aeDeviceType;
+};
+
+static const winEndpointsToAEDeviceType winEndpoints[EndpointFormFactor_enum_count] =
+{
+ {"Network Device - ", AE_DEVTYPE_PCM},
+ {"Speakers - ", AE_DEVTYPE_PCM},
+ {"LineLevel - ", AE_DEVTYPE_PCM},
+ {"Headphones - ", AE_DEVTYPE_PCM},
+ {"Microphone - ", AE_DEVTYPE_PCM},
+ {"Headset - ", AE_DEVTYPE_PCM},
+ {"Handset - ", AE_DEVTYPE_PCM},
+ {"Digital Passthrough - ", AE_DEVTYPE_IEC958},
+ {"SPDIF - ", AE_DEVTYPE_IEC958},
+ {"HDMI - ", AE_DEVTYPE_HDMI},
+ {"Unknown - ", AE_DEVTYPE_PCM},
+};
+
+AEDeviceInfoList DeviceInfoList;
+
+#define EXIT_ON_FAILURE(hr, reason, ...) if(FAILED(hr)) {CLog::Log(LOGERROR, reason " - %s", __VA_ARGS__, WASAPIErrToStr(hr)); goto failed;}
+
+#define ERRTOSTR(err) case err: return #err
+
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
+
+CAESinkWASAPI::CAESinkWASAPI() :
+ m_pAudioClient(NULL),
+ m_pRenderClient(NULL),
+ m_needDataEvent(0),
+ m_pDevice(NULL),
+ m_initialized(false),
+ m_running(false),
+ m_isExclusive(false),
+ m_encodedFormat(AE_FMT_INVALID),
+ m_encodedChannels(0),
+ m_encodedSampleRate(0),
+ m_uiBufferLen(0),
+ avgTimeWaiting(50)
+{
+ m_channelLayout.Reset();
+}
+
+CAESinkWASAPI::~CAESinkWASAPI()
+{
+
+}
+
+bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device)
+{
+ if (m_initialized)
+ return false;
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Initializing WASAPI Sink Rev. 1.0.5");
+
+ m_device = device;
+
+ /* Save requested format */
+ /* Clear returned format */
+ sinkReqFormat = format.m_dataFormat;
+ sinkRetFormat = AE_FMT_INVALID;
+
+ IMMDeviceEnumerator* pEnumerator = NULL;
+ IMMDeviceCollection* pEnumDevices = NULL;
+
+ HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr)
+
+ //Get our device.
+ //First try to find the named device.
+ UINT uiCount = 0;
+
+ hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
+
+ hr = pEnumDevices->GetCount(&uiCount);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
+
+ for (UINT i = 0; i < uiCount; i++)
+ {
+ IPropertyStore *pProperty = NULL;
+ PROPVARIANT varName;
+
+ hr = pEnumDevices->Item(i, &m_pDevice);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.")
+
+ hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed.");
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+
+ std::wstring strRawDevName(varName.pwszVal);
+ std::string strDevName = std::string(strRawDevName.begin(), strRawDevName.end());
+ //g_charsetConverter.ucs2CharsetToStringCharset(strRawDevName, strDevName.c_str());
+
+ if (device == strDevName)
+ i = uiCount;
+ else
+ SAFE_RELEASE(m_pDevice);
+
+ PropVariantClear(&varName);
+ SAFE_RELEASE(pProperty);
+ }
+
+ SAFE_RELEASE(pEnumDevices);
+
+ if (!m_pDevice)
+ {
+ CLog::Log(LOGINFO, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices. Trying the default device...", device.c_str());
+ hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pDevice);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the default WASAPI audio endpoint.")
+
+ IPropertyStore *pProperty = NULL;
+ PROPVARIANT varName;
+
+ hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
+
+ std::wstring strRawDevName(varName.pwszVal);
+ std::string strDevName = std::string(strRawDevName.begin(), strRawDevName.end());
+
+ PropVariantClear(&varName);
+ SAFE_RELEASE(pProperty);
+ }
+
+ //We are done with the enumerator.
+ SAFE_RELEASE(pEnumerator);
+
+ hr = m_pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Activating the WASAPI endpoint device failed.")
+
+ if (g_guiSettings.GetBool("audiooutput.useexclusivemode") || AE_IS_RAW(format.m_dataFormat))
+ {
+ if (!InitializeExclusive(format))
+ {
+ CLog::Log(LOGINFO, __FUNCTION__": Could not Initialize Exclusive with that format");
+ goto failed;
+ }
+
+ m_isExclusive = true;
+ }
+ else
+ {
+ if (!InitializeShared(format))
+ {
+ CLog::Log(LOGINFO, __FUNCTION__": Could not Initialize Shared with that format");
+ goto failed;
+ }
+
+ m_isExclusive = false;
+ }
+
+ /* get the buffer size and calculate the frames for AE */
+ m_pAudioClient->GetBufferSize(&m_uiBufferLen);
+
+ format.m_frames = m_uiBufferLen;
+ format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
+ m_format = format;
+ sinkRetFormat = format.m_dataFormat;
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Buffer Size = %d Bytes", m_uiBufferLen * format.m_frameSize);
+
+ hr = m_pAudioClient->GetService(IID_IAudioRenderClient, (void**)&m_pRenderClient);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not initialize the WASAPI render client interface.")
+
+ m_needDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hr = m_pAudioClient->SetEventHandle(m_needDataEvent);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not set the WASAPI event handler.");
+
+ m_initialized = true;
+
+ return true;
+
+failed:
+ CLog::Log(LOGERROR, __FUNCTION__": WASAPI initialization failed.");
+ SAFE_RELEASE(pEnumDevices);
+ SAFE_RELEASE(pEnumerator);
+ SAFE_RELEASE(m_pRenderClient);
+ SAFE_RELEASE(m_pAudioClient);
+ SAFE_RELEASE(m_pDevice);
+ CloseHandle(m_needDataEvent);
+
+ return false;
+}
+
+void CAESinkWASAPI::Deinitialize()
+{
+ if (!m_initialized)
+ return;
+
+
+ if (m_running)
+ m_pAudioClient->Stop();
+ m_running = false;
+
+
+ SAFE_RELEASE(m_pRenderClient);
+ SAFE_RELEASE(m_pAudioClient);
+ SAFE_RELEASE(m_pDevice);
+ CloseHandle(m_needDataEvent);
+
+ m_initialized = false;
+}
+
+bool CAESinkWASAPI::IsCompatible(const AEAudioFormat format, const std::string device)
+{
+ if (!m_initialized)
+ return false;
+
+ bool excSetting = g_guiSettings.GetBool("audiooutput.useexclusivemode");
+
+ u_int notCompatible = 0;
+ const u_int numTests = 6;
+ std::string strDiffBecause ("");
+ static const char* compatibleParams[numTests] = {":Devices",
+ ":Channels",
+ ":Sample Rates",
+ ":Data Formats",
+ ":Exclusive Mode Settings",
+ ":Passthrough Formats"};
+
+ notCompatible = (notCompatible +!((AE_IS_RAW(format.m_dataFormat) == AE_IS_RAW(m_encodedFormat)) ||
+ (!AE_IS_RAW(format.m_dataFormat) == !AE_IS_RAW(m_encodedFormat)))) << 1;
+ notCompatible = (notCompatible +!((m_isExclusive == excSetting) ||
+ (!m_isExclusive == !excSetting))) << 1;
+ notCompatible = (notCompatible +!((sinkReqFormat == format.m_dataFormat) &&
+ (sinkRetFormat == m_format.m_dataFormat))) << 1;
+ notCompatible = (notCompatible + !(format.m_sampleRate == m_format.m_sampleRate)) << 1;
+ notCompatible = (notCompatible + !(format.m_channelLayout.Count() == m_format.m_channelLayout.Count())) << 1;
+ notCompatible = (notCompatible + !(m_device == device));
+
+ if (!notCompatible)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Formats compatible - reusing existing sink");
+ return true;
+ }
+
+ for (int i = 0; i < numTests ; i++)
+ {
+ strDiffBecause += (notCompatible & 0x01) ? (std::string) compatibleParams[i] : "";
+ notCompatible = notCompatible >> 1;
+ }
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Formats Incompatible due to different %s", strDiffBecause.c_str());
+ return false;
+}
+
+double CAESinkWASAPI::GetDelay()
+{
+ HRESULT hr;
+ if (!m_initialized)
+ return 0.0;
+
+ if (m_isExclusive)
+ {
+ hr = m_pAudioClient->GetBufferSize(&m_uiBufferLen);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetBufferSize Failed : %s", WASAPIErrToStr(hr));
+ return false;
+ }
+ return (double)m_uiBufferLen / (double)m_format.m_sampleRate;
+ }
+
+ UINT32 frames;
+ m_pAudioClient->GetCurrentPadding(&frames);
+ return (double)frames / (double)m_format.m_sampleRate;
+}
+
+double CAESinkWASAPI::GetCacheTime()
+{
+ if (!m_initialized)
+ return 0.0;
+
+ REFERENCE_TIME hnsLatency;
+ HRESULT hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
+
+ /** returns buffer duration in seconds */
+ return hnsLatency / 10.0;
+}
+
+double CAESinkWASAPI::GetCacheTotal()
+{
+ if (!m_initialized)
+ return 0.0;
+
+ REFERENCE_TIME hnsLatency;
+ HRESULT hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
+
+ /** returns buffer duration in seconds */
+ return hnsLatency / 10.0;
+}
+
+unsigned int CAESinkWASAPI::AddPackets(uint8_t *data, unsigned int frames)
+{
+ if (!m_initialized)
+ return 0;
+
+ HRESULT hr;
+ BYTE *buf;
+ DWORD flags = 0;
+ UINT32 currentPaddingFrames = 0;
+#ifndef _DEBUG
+ LARGE_INTEGER timerStart;
+ LARGE_INTEGER timerStop;
+ LARGE_INTEGER timerFreq;
+#endif
+
+ unsigned int NumFramesRequested = frames;
+
+ if (!m_running) //first time called, pre-fill buffer then start audio client
+ {
+ if (!m_isExclusive)
+ {
+ hr = m_pAudioClient->GetCurrentPadding(&currentPaddingFrames);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetCurrent Padding failed (%d)", hr);
+ return 0;
+ }
+ }
+ NumFramesRequested = NumFramesRequested - currentPaddingFrames;
+ hr = m_pRenderClient->GetBuffer(NumFramesRequested, &buf);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetBuffer failed due to %s", WASAPIErrToStr(hr));
+ return 0;
+ }
+ memcpy(buf, data, NumFramesRequested * m_format.m_frameSize); //fill buffer
+ hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, flags); //pass back to audio driver
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": ReleaseBuffer failed due to %s.", WASAPIErrToStr(hr));
+ return 0;
+ }
+ hr = m_pAudioClient->Start(); //start the audio driver running
+ if FAILED(hr)
+ CLog::Log(LOGERROR, __FUNCTION__": AudioClient Start Failed");
+ m_running = true; //signal that we're processing frames
+ return NumFramesRequested;
+ }
+
+#ifndef _DEBUG
+ /* Get clock time for latency checks */
+ QueryPerformanceFrequency(&timerFreq);
+ QueryPerformanceCounter(&timerStart);
+#endif
+
+ /* Wait for Audio Driver to tell us it's got a buffer available */
+ DWORD eventAudioCallback = WaitForSingleObject(m_needDataEvent, 1100);
+ if (eventAudioCallback != WAIT_OBJECT_0)
+ {
+ // Event handle timed out - stop audio device
+ m_pAudioClient->Stop();
+ CLog::Log(LOGERROR, __FUNCTION__": Endpoint Buffer timed out");
+ m_running = false;
+ m_pAudioClient->Stop(); //stop processing - we're done
+ return 0; //need better handling here
+ }
+
+#ifndef _DEBUG
+ QueryPerformanceCounter(&timerStop);
+ LONGLONG timerDiff = timerStop.QuadPart - timerStart.QuadPart;
+ double timerElapsed = (double) timerDiff * 1000.0 / (double) timerFreq.QuadPart;
+ avgTimeWaiting += (timerElapsed - avgTimeWaiting) * 0.5;
+
+ if (avgTimeWaiting < 3.0)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Possible AQ Loss: Avg. Time Waiting for Audio Driver callback : %dmsec", (int)avgTimeWaiting);
+ }
+#endif
+
+ if (!m_isExclusive)
+ {
+ hr = m_pAudioClient->GetCurrentPadding(&currentPaddingFrames);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetCurrentPadding failed due to %s", WASAPIErrToStr(hr));
+ return 0;
+ }
+ }
+ NumFramesRequested = NumFramesRequested - currentPaddingFrames;
+ hr = m_pRenderClient->GetBuffer(NumFramesRequested, &buf);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetBuffer failed due to %s", WASAPIErrToStr(hr));
+ return 0;
+ }
+ memcpy(buf, data, NumFramesRequested * m_format.m_frameSize); //fill buffer
+ hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, flags); //pass back to audio driver
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": ReleaseBuffer failed due to %s.", WASAPIErrToStr(hr));
+ return 0;
+ }
+
+ return NumFramesRequested;
+}
+
+void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
+{
+ IMMDeviceEnumerator* pEnumerator = NULL;
+ IMMDeviceCollection* pEnumDevices = NULL;
+ CAEDeviceInfo deviceInfo;
+ CAEChannelInfo deviceChannels;
+
+ WAVEFORMATEXTENSIBLE wfxex = {0};
+ WAVEFORMATEX* pwfxex = NULL;
+ HRESULT hr;
+
+ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr)
+
+ UINT uiCount = 0;
+
+ hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
+
+ hr = pEnumDevices->GetCount(&uiCount);
+ EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
+
+ for (UINT i = 0; i < uiCount; i++)
+ {
+ IMMDevice *pDevice = NULL;
+ IPropertyStore *pProperty = NULL;
+ PROPVARIANT varName;
+ PropVariantInit(&varName);
+
+ deviceInfo.m_channels.Reset();
+ deviceInfo.m_dataFormats.clear();
+ deviceInfo.m_sampleRates.clear();
+
+ hr = pEnumDevices->Item(i, &pDevice);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint failed.");
+ goto failed;
+ }
+
+ hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.");
+ SAFE_RELEASE(pDevice);
+ goto failed;
+ }
+
+ hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+
+ std::wstring strRawFriendlyName(varName.pwszVal);
+ std::string strFriendlyName = std::string(strRawFriendlyName.begin(), strRawFriendlyName.end());
+
+ PropVariantClear(&varName);
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
+ if(FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+
+ std::wstring strRawDevName(varName.pwszVal);
+ std::string strDevName = std::string(strRawDevName.begin(), strRawDevName.end());
+
+ PropVariantClear(&varName);
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint form factor failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+ std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType;
+ AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
+
+ PropVariantClear(&varName);
+
+ hr = pProperty->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &varName);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint speaker layout failed.");
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+ goto failed;
+ }
+ unsigned int uiChannelMask = std::max(varName.uintVal, (unsigned int) AE_CH_FL | AE_CH_FR);
+
+ deviceChannels.Reset();
+
+ for (unsigned int c = 0; c < WASAPI_SPEAKER_COUNT; c++)
+ {
+ if (uiChannelMask & WASAPIChannelOrder[c])
+ deviceChannels += AEChannelNames[c];
+ }
+
+ PropVariantClear(&varName);
+
+ if (true || g_guiSettings.GetBool("audiooutput.useexclusivemode"))
+ {
+ IAudioClient *pClient;
+ hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient);
+ if (SUCCEEDED(hr))
+ {
+ /* Test format DTS-HD */
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ wfxex.Format.nSamplesPerSec = 192000;
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD;
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.Format.nChannels = 8;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTSHD));
+
+ /* Test format Dolby TrueHD */
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_TRUEHD));
+
+ /* Test format Dolby EAC3 */
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS;
+ wfxex.Format.nChannels = 2;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_EAC3));
+
+ /* Test format DTS */
+ wfxex.Format.nSamplesPerSec = 48000;
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTS));
+
+ /* Test format Dolby AC3 */
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3));
+
+ /* Test format AAC */
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_AAC;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AAC));
+
+ /* Test format for PCM format iteration */
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ wfxex.dwChannelMask = AE_CH_FL | AE_CH_FR;
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+
+ for (int p = AE_FMT_FLOAT; p > AE_FMT_INVALID; p--)
+ {
+ if (p < AE_FMT_FLOAT)
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ wfxex.Format.wBitsPerSample = CAEUtil::DataFormatToBits((AEDataFormat) p);
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ if (p <= AE_FMT_S24NE4 && p >= AE_FMT_S24BE4)
+ {
+ wfxex.Samples.wValidBitsPerSample = 24;
+ }
+ else
+ {
+ wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
+ }
+
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_dataFormats.push_back((AEDataFormat) p);
+ }
+
+ /* Test format for sample rate iteration */
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ wfxex.dwChannelMask = AE_CH_FL | AE_CH_FR;
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.Format.nChannels = 2;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+
+ for (int j = 0; j < WASAPISampleRateCount; j++)
+ {
+ wfxex.Format.nSamplesPerSec = WASAPISampleRates[j];
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]);
+ }
+
+ /* Test format for channels iteration */
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ wfxex.dwChannelMask = AE_CH_FL | AE_CH_FR;
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ wfxex.Format.nSamplesPerSec = 48000;
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.Format.nChannels = 2;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+
+ //bool mpcmFlagged = false;
+
+ for (int k = AE_CH_LAYOUT_MAX; k > 0; k--)
+ {
+ wfxex.Format.nChannels = k;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+ if (SUCCEEDED(hr))
+ {
+ deviceChannels = layoutsByChCount[k];
+ if (k > AE_CH_LAYOUT_2_1) // && !mpcmFlagged)
+ {
+ deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM);
+ //mpcmFlagged = true;
+ }
+ break;
+ }
+ }
+ pClient->Release();
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Failed to activate device for passthrough capability testing.");
+ }
+ }
+ else
+ {
+ /* In shared mode Windows tells us what format the audio must be in. */
+ IAudioClient *pClient;
+ hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient);
+ HRESULT hr = pClient->GetMixFormat(&pwfxex);
+ if (SUCCEEDED(hr))
+ {
+ deviceInfo.m_channels = layoutsByChCount[pwfxex->nChannels];
+ deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT));
+ deviceInfo.m_sampleRates.push_back(wfxex.Format.nSamplesPerSec);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetMixFormat failed (%s)", WASAPIErrToStr(hr));
+ }
+ pClient->Release();
+ }
+
+ SAFE_RELEASE(pDevice);
+ SAFE_RELEASE(pProperty);
+
+ deviceInfo.m_deviceName = strDevName;
+ deviceInfo.m_displayName = strWinDevType.append(strFriendlyName);
+ deviceInfo.m_displayNameExtra = std::string("WASAPI: ").append(strFriendlyName);
+ deviceInfo.m_deviceType = aeDeviceType;
+ deviceInfo.m_channels = deviceChannels;
+
+ /* Now logged by AESinkFactory on startup */
+ //CLog::Log(LOGDEBUG,"Audio Device %d: %s", i, ((std::string)deviceInfo).c_str());
+
+ deviceInfoList.push_back(deviceInfo);
+ }
+ return;
+
+failed:
+
+ if (FAILED(hr))
+ CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr));
+
+ SAFE_RELEASE(pEnumDevices);
+ SAFE_RELEASE(pEnumerator);
+}
+
+//Private utility functions////////////////////////////////////////////////////
+
+void CAESinkWASAPI::BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATEXTENSIBLE &wfxex)
+{
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+
+
+ if (!AE_IS_RAW(format.m_dataFormat)) // PCM data
+ {
+ wfxex.dwChannelMask = SpeakerMaskFromAEChannels(format.m_channelLayout);
+ wfxex.Format.nChannels = (WORD)format.m_channelLayout.Count();
+ wfxex.Format.nSamplesPerSec = format.m_sampleRate;
+ wfxex.Format.wBitsPerSample = format.m_dataFormat <= AE_FMT_S16NE ? 16 : 32;
+ wfxex.SubFormat = format.m_dataFormat <= AE_FMT_FLOAT ? KSDATAFORMAT_SUBTYPE_PCM : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ }
+ else //Raw bitstream
+ {
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ if (format.m_dataFormat == AE_FMT_AC3 || format.m_dataFormat == AE_FMT_DTS)
+ {
+ wfxex.dwChannelMask = bool (format.m_channelLayout.Count() == 2) ? KSAUDIO_SPEAKER_STEREO : KSAUDIO_SPEAKER_5POINT1;
+
+ if (format.m_dataFormat == AE_FMT_AC3)
+ {
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL;
+ //wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
+ }
+ else
+ {
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL; //KSDATAFORMAT_SUBTYPE_IEC61937_DTS;
+ //wfxex.Format.wFormatTag = WAVE_FORMAT_DTS;
+ }
+
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.Format.nChannels = (WORD)format.m_channelLayout.Count();
+ wfxex.Format.nSamplesPerSec = format.m_sampleRate;
+ }
+ else if (format.m_dataFormat == AE_FMT_EAC3 || format.m_dataFormat == AE_FMT_TRUEHD || format.m_dataFormat == AE_FMT_DTSHD)
+ {
+ //IEC 61937 transmissions.
+ //Currently these formats only run over HDMI.
+ wfxex.Format.nSamplesPerSec = 192000L; // Link runs at 192 KHz.
+ wfxex.Format.wBitsPerSample = 16; // Always at 16 bits over IEC 60958.
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
+
+ switch (format.m_dataFormat)
+ {
+ case AE_FMT_EAC3:
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS;
+ wfxex.Format.nChannels = 2; // One IEC 60958 Line.
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
+ break;
+ case AE_FMT_TRUEHD:
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP;
+ wfxex.Format.nChannels = 8; // Four IEC 60958 Lines.
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
+ break;
+ case AE_FMT_DTSHD:
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD;
+ wfxex.Format.nChannels = 8; // Four IEC 60958 Lines.
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
+ break;
+ }
+
+ if (format.m_channelLayout.Count() == 8)
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
+ else
+ wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
+ }
+ }
+
+ if (wfxex.Format.wBitsPerSample == 32 && wfxex.SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ wfxex.Samples.wValidBitsPerSample = 24;
+ else
+ wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
+
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+}
+
+void CAESinkWASAPI::BuildWaveFormatExtensibleIEC61397(AEAudioFormat &format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex)
+{
+ //Fill the common structure.
+ BuildWaveFormatExtensible(format, wfxex.FormatExt);
+ /*
+ wfxex.FormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE_IEC61937)-sizeof(WAVEFORMATEX);
+ wfxex.dwEncodedChannelCount = format.m_channelLayout.Count();
+ wfxex.dwEncodedSamplesPerSec = bool(format.m_dataFormat == AE_FMT_TRUEHD ||
+ format.m_dataFormat == AE_FMT_DTSHD ||
+ format.m_dataFormat == AE_FMT_EAC3) ? 96000L : 48000L;
+ wfxex.dwAverageBytesPerSec = 0; //Ignored */
+}
+
+bool CAESinkWASAPI::InitializeShared(AEAudioFormat &format)
+{
+ WAVEFORMATEXTENSIBLE *wfxex;
+ //ZeroMemory(&wfxex_iec61937, sizeof(WAVEFORMATEXTENSIBLE_IEC61937));;
+
+ //In shared mode Windows tells us what format the audio must be in.
+ HRESULT hr = m_pAudioClient->GetMixFormat((WAVEFORMATEX **)&wfxex);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetMixFormat failed (%s)", WASAPIErrToStr(hr));
+ return false;
+ }
+
+ //The windows mixer uses floats and that should be the mix format returned.
+ if (wfxex->Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT &&
+ (wfxex->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE || wfxex->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Windows mixer format is not float (%i)", wfxex->Format.wFormatTag);
+ return false;
+ }
+
+ AEChannelsFromSpeakerMask(wfxex->dwChannelMask);
+
+ format.m_channelLayout = m_channelLayout;
+ format.m_dataFormat = AE_FMT_FLOAT;
+ format.m_frameSize = sizeof(float) * format.m_channelLayout.Count();
+ format.m_sampleRate = wfxex->Format.nSamplesPerSec;
+
+ REFERENCE_TIME audioSinkBufferDurationMsec, hnsLatency;
+
+ /* Get m_audioSinkBufferSizeMsec from advancedsettings.xml */
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)g_advancedSettings.m_audioSinkBufferDurationMsec * 10000;
+
+ //use user's advancedsetting value for buffer size as long as it's over minimum set above
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)std::max(audioSinkBufferDurationMsec, (REFERENCE_TIME)500000);
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)((audioSinkBufferDurationMsec / format.m_frameSize) * format.m_frameSize); //even number of frames
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Initializing WASAPI shared mode with the following parameters:");
+ CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex->Format.nSamplesPerSec);
+ CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex->Format.wBitsPerSample);
+ CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex->Samples.wValidBitsPerSample);
+ CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex->Format.nChannels);
+ CLog::Log(LOGDEBUG, " Block Align : %d", wfxex->Format.nBlockAlign);
+ CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex->Format.nAvgBytesPerSec);
+ CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex->Samples.wSamplesPerBlock);
+ CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex->Format.cbSize);
+ CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str());
+ CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex->dwChannelMask);
+ CLog::Log(LOGDEBUG, " Periodicty : %d", audioSinkBufferDurationMsec);
+
+ if (FAILED(hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+ audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex->Format, NULL)))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Initialize failed (%s)", WASAPIErrToStr(hr));
+ CoTaskMemFree(wfxex);
+ SAFE_RELEASE(m_pAudioClient);
+ return false;
+ }
+
+ hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
+ CLog::Log(LOGDEBUG, __FUNCTION__": Requested Duration of Buffer : %fmsec", hnsLatency / 10000.0);
+ CLog::Log(LOGNOTICE, __FUNCTION__": WASAPI Shared Mode Sink Initialized Successfully!!!");
+ CoTaskMemFree(wfxex);
+ return true;
+}
+
+bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format)
+{
+ WAVEFORMATEXTENSIBLE_IEC61937 wfxex_iec61937;
+ WAVEFORMATEXTENSIBLE &wfxex = wfxex_iec61937.FormatExt;
+ //ZeroMemory(&wfxex_iec61937, sizeof(WAVEFORMATEXTENSIBLE_IEC61937));
+
+ if (format.m_dataFormat <= AE_FMT_FLOAT) //DDDamian was AE_FMT_DTS
+ BuildWaveFormatExtensible(format, wfxex);
+ else
+ BuildWaveFormatExtensibleIEC61397(format, wfxex_iec61937);
+
+ //test for incomplete or startup format and provide default
+ if (format.m_sampleRate == 0 ||
+ format.m_channelLayout == NULL ||
+ format.m_dataFormat <= AE_FMT_INVALID ||
+ format.m_dataFormat >= AE_FMT_MAX ||
+ format.m_channelLayout.Count() == 0)
+ {
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.Format.nChannels = 2;
+ wfxex.Format.nSamplesPerSec = 44100L;
+ wfxex.Format.wBitsPerSample = 16;
+ wfxex.Format.nBlockAlign = 4;
+ wfxex.Samples.wValidBitsPerSample = 16;
+ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nBlockAlign * wfxex.Format.nSamplesPerSec;
+ wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Checking IsFormatSupported with the following parameters:");
+ CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex.Format.nSamplesPerSec);
+ CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
+ CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
+ CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex.Format.nChannels);
+ CLog::Log(LOGDEBUG, " Block Align : %d", wfxex.Format.nBlockAlign);
+ CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex.Format.nAvgBytesPerSec);
+ CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex.Samples.wSamplesPerBlock);
+ CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex.Format.cbSize);
+ CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str());
+ CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex.dwChannelMask);
+
+ if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_PCM");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DTS)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DTS");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD");
+ else
+ CLog::Log(LOGDEBUG, " SubFormat : NO SUBFORMAT SPECIFIED");
+
+ HRESULT hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Format is Supported - will attempt to Initialize");
+ goto initialize;
+ }
+ else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) //It failed for a reason unrelated to an unsupported format.
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s)", WASAPIErrToStr(hr));
+ return false;
+ }
+ else if (AE_IS_RAW(format.m_dataFormat)) //No sense in trying other formats for passthrough.
+ return false;
+
+ CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s) - trying to find a compatible format", WASAPIErrToStr(hr));
+
+ int closestMatch;
+
+ //The requested format is not supported by the device. Find something that works.
+ //Try other formats
+ for (int j = 0; j < sizeof(testFormats)/sizeof(sampleFormat); j++)
+ {
+ closestMatch = -1;
+
+ wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfxex.SubFormat = testFormats[j].subFormat;
+ wfxex.Format.wBitsPerSample = testFormats[j].bitsPerSample;
+ wfxex.Samples.wValidBitsPerSample = testFormats[j].validBitsPerSample;
+ wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
+
+ /*if (wfxex.Format.wBitsPerSample == 32 && wfxex.SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ wfxex.Samples.wValidBitsPerSample = 24;
+ else
+ wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;*/
+
+ for (int i = 0 ; i < WASAPISampleRateCount; i++)
+ {
+ wfxex.Format.nSamplesPerSec = WASAPISampleRates[i];
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+
+ /* Uncomment to trace format match iteration loop via log */
+ CLog::Log(LOGDEBUG, "WASAPI: Trying Sample Format : %s", CAEUtil::DataFormatToStr(testFormats[j].subFormatType));
+ CLog::Log(LOGDEBUG, "WASAPI: Trying Sample Rate : %d", wfxex.Format.nSamplesPerSec);
+ CLog::Log(LOGDEBUG, "WASAPI: Trying Bits/Sample : %d", wfxex.Format.wBitsPerSample);
+ CLog::Log(LOGDEBUG, "WASAPI: Trying Valid Bits/Sample: %d", wfxex.Samples.wValidBitsPerSample);
+
+ hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ //If the current sample rate matches the source then stop looking and use it.
+ if ((WASAPISampleRates[i] == format.m_sampleRate) && (testFormats[j].subFormatType <= format.m_dataFormat))
+ goto initialize;
+ //If this rate is closer to the source then the previous one, save it.
+ else if (closestMatch < 0 || abs((int)WASAPISampleRates[i] - (int)format.m_sampleRate) < abs((int)WASAPISampleRates[closestMatch] - (int)format.m_sampleRate))
+ closestMatch = i;
+ }
+ else if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
+ CLog::Log(LOGERROR, __FUNCTION__": IsFormatSupported failed (%s)", WASAPIErrToStr(hr));
+ }
+
+ if (closestMatch >= 0)
+ {
+ wfxex.Format.nSamplesPerSec = WASAPISampleRates[closestMatch];
+ wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
+ goto initialize;
+ }
+ }
+
+ CLog::Log(LOGERROR, __FUNCTION__": Unable to locate a supported output format for the device. Check the speaker settings in the control panel.");
+
+ //We couldn't find anything supported. This should never happen unless the user set the wrong
+ //speaker setting in the control panel.
+ return false;
+
+initialize:
+
+ AEChannelsFromSpeakerMask(wfxex.dwChannelMask);
+
+ //When the stream is raw, the values in the format structure are set to the link
+ //parameters, so store the encoded stream values here for the IsCompatible function.
+ m_encodedFormat = format.m_dataFormat;
+ m_encodedChannels = wfxex.Format.nChannels;
+ m_encodedSampleRate = format.m_encodedRate;// bool(format.m_dataFormat == AE_FMT_TRUEHD || format.m_dataFormat == AE_FMT_DTSHD) ? 96000L : 48000L;
+ wfxex_iec61937.dwEncodedChannelCount = wfxex.Format.nChannels;
+ wfxex_iec61937.dwEncodedSamplesPerSec = m_encodedSampleRate;
+
+ if (!AE_IS_RAW(format.m_dataFormat))
+ {
+ if (wfxex.Format.wBitsPerSample == 32)
+ {
+ if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ format.m_dataFormat = AE_FMT_FLOAT;
+ else if (wfxex.Samples.wValidBitsPerSample == 32)
+ format.m_dataFormat = AE_FMT_S32NE;
+ else // wfxex->Samples.wValidBitsPerSample == 24
+ format.m_dataFormat = AE_FMT_S24NE4;
+ }
+ else
+ {
+ format.m_dataFormat = AE_FMT_S16NE;
+ }
+ }
+
+ format.m_sampleRate = wfxex.Format.nSamplesPerSec; //PCM: Sample rate. RAW: Link speed
+ //format.m_channelLayout = m_channelLayout;
+ format.m_frameSize = (wfxex.Format.wBitsPerSample >> 3) * wfxex.Format.nChannels;
+
+ REFERENCE_TIME audioSinkBufferDurationMsec, hnsLatency;
+
+ /* Get m_audioSinkBufferSizeMsec from advancedsettings.xml */
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)g_advancedSettings.m_audioSinkBufferDurationMsec * 10000;
+
+ //use user's advancedsetting value for buffer size as long as it's over minimum set above
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)std::max(audioSinkBufferDurationMsec, (REFERENCE_TIME)500000);
+ audioSinkBufferDurationMsec = (REFERENCE_TIME)((audioSinkBufferDurationMsec / format.m_frameSize) * format.m_frameSize); //even number of frames
+
+ CLog::Log(LOGDEBUG, __FUNCTION__": Initializing WASAPI exclusive mode with the following parameters:");
+ CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex.Format.nSamplesPerSec);
+ CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
+ CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
+ CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
+ CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex.Format.nChannels);
+ CLog::Log(LOGDEBUG, " Block Align : %d", wfxex.Format.nBlockAlign);
+ CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex.Format.nAvgBytesPerSec);
+ CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex.Samples.wSamplesPerBlock);
+ CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex.Format.cbSize);
+ CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str());
+ CLog::Log(LOGDEBUG, " Enc. Channels : %d", wfxex_iec61937.dwEncodedChannelCount);
+ CLog::Log(LOGDEBUG, " Enc. Samples/Sec: %d", wfxex_iec61937.dwEncodedSamplesPerSec);
+ CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex.dwChannelMask);
+ CLog::Log(LOGDEBUG, " Periodicty : %d", audioSinkBufferDurationMsec);
+
+ if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_PCM");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DTS)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DTS");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP");
+ else if (wfxex.SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD)
+ CLog::Log(LOGDEBUG, " SubFormat : KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD");
+ else
+ CLog::Log(LOGDEBUG, " SubFormat : NO SUBFORMAT SPECIFIED");
+
+ if (AE_IS_RAW(format.m_dataFormat))
+ format.m_dataFormat = AE_FMT_S16NE;
+
+ hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
+ audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex.Format, NULL);
+
+ if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
+ {
+ CLog::Log(LOGDEBUG, __FUNCTION__": Re-aligning WASAPI sink buffer due to %s.", WASAPIErrToStr(hr));
+ // Get the next aligned frame.
+ hr = m_pAudioClient->GetBufferSize(&m_uiBufferLen);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": GetBufferSize Failed : %s", WASAPIErrToStr(hr));
+ return false;
+ }
+
+ audioSinkBufferDurationMsec = (REFERENCE_TIME) ((10000.0 * 1000 / wfxex.Format.nSamplesPerSec * m_uiBufferLen) + 0.5);
+ CLog::Log(LOGDEBUG, __FUNCTION__": Number of Frames in Buffer : %d", m_uiBufferLen);
+ CLog::Log(LOGDEBUG, __FUNCTION__": Requested Duration of Buffer : %d", audioSinkBufferDurationMsec);
+
+ // Release the previous allocations.
+ SAFE_RELEASE(m_pAudioClient);
+
+ // Create a new audio client.
+ hr = m_pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Device Activation Failed : %s", WASAPIErrToStr(hr));
+ return false;
+ }
+
+ // Open the stream and associate it with an audio session.
+ hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
+ audioSinkBufferDurationMsec, audioSinkBufferDurationMsec, &wfxex.Format, NULL);
+ }
+ if (FAILED(hr))
+ {
+ CLog::Log(LOGERROR, __FUNCTION__": Unable to initialize WASAPI in exclusive mode %d - (%s).", HRESULT(hr), WASAPIErrToStr(hr));
+ return false;
+ }
+ hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
+ CLog::Log(LOGDEBUG, __FUNCTION__": Requested Duration of Buffer : %fmsec", hnsLatency / 10000.0);
+ CLog::Log(LOGNOTICE, __FUNCTION__": WASAPI Exclusive Mode Sink Initialized Successfully!!!");
+ return true;
+}
+
+void CAESinkWASAPI::AEChannelsFromSpeakerMask(DWORD speakers)
+{
+ m_channelLayout.Reset();
+
+ for (int i = 0; i < WASAPI_SPEAKER_COUNT; i++)
+ {
+ if (speakers & WASAPIChannelOrder[i])
+ m_channelLayout += AEChannelNames[i];
+ }
+}
+
+DWORD CAESinkWASAPI::SpeakerMaskFromAEChannels(const CAEChannelInfo &channels)
+{
+ DWORD mask = 0;
+
+ for (unsigned int i = 0; i < channels.Count(); i++)
+ {
+ for (unsigned int j = 0; j < WASAPI_SPEAKER_COUNT; j++)
+ if (channels[i] == AEChannelNames[j])
+ mask |= WASAPIChannelOrder[j];
+ }
+ return mask;
+}
+
+const char *CAESinkWASAPI::WASAPIErrToStr(HRESULT err)
+{
+ switch(err)
+ {
+ ERRTOSTR(AUDCLNT_E_NOT_INITIALIZED);
+ ERRTOSTR(AUDCLNT_E_ALREADY_INITIALIZED);
+ ERRTOSTR(AUDCLNT_E_WRONG_ENDPOINT_TYPE);
+ ERRTOSTR(AUDCLNT_E_DEVICE_INVALIDATED);
+ ERRTOSTR(AUDCLNT_E_NOT_STOPPED);
+ ERRTOSTR(AUDCLNT_E_BUFFER_TOO_LARGE);
+ ERRTOSTR(AUDCLNT_E_OUT_OF_ORDER);
+ ERRTOSTR(AUDCLNT_E_UNSUPPORTED_FORMAT);
+ ERRTOSTR(AUDCLNT_E_INVALID_SIZE);
+ ERRTOSTR(AUDCLNT_E_DEVICE_IN_USE);
+ ERRTOSTR(AUDCLNT_E_BUFFER_OPERATION_PENDING);
+ ERRTOSTR(AUDCLNT_E_THREAD_NOT_REGISTERED);
+ ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED);
+ ERRTOSTR(AUDCLNT_E_ENDPOINT_CREATE_FAILED);
+ ERRTOSTR(AUDCLNT_E_SERVICE_NOT_RUNNING);
+ ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED);
+ ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_ONLY);
+ ERRTOSTR(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL);
+ ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_SET);
+ ERRTOSTR(AUDCLNT_E_INCORRECT_BUFFER_SIZE);
+ ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_ERROR);
+ ERRTOSTR(AUDCLNT_E_CPUUSAGE_EXCEEDED);
+ ERRTOSTR(AUDCLNT_E_BUFFER_ERROR);
+ ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED);
+ ERRTOSTR(AUDCLNT_E_INVALID_DEVICE_PERIOD);
+ ERRTOSTR(E_POINTER);
+ ERRTOSTR(E_INVALIDARG);
+ ERRTOSTR(E_OUTOFMEMORY);
+ default: break;
+ }
+ return NULL;
+}
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
new file mode 100644
index 0000000000..4ed0f1dc28
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
@@ -0,0 +1,79 @@
+#pragma once
+/*
+* Copyright (C) 2010-2012 Team XBMC
+* http://www.xbmc.org
+*
+* 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; either version 2, or (at your option)
+* any later version.
+*
+* 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 for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with XBMC; see the file COPYING. If not, write to
+* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+#include "../Interfaces/AESink.h"
+#include <stdint.h>
+#include <mmdeviceapi.h>
+#include <Audioclient.h>
+#include "../Utils/AEDeviceInfo.h"
+
+#include "threads/CriticalSection.h"
+
+class CAESinkWASAPI : public IAESink
+{
+public:
+ virtual const char *GetName() { return "WASAPI"; }
+
+ CAESinkWASAPI();
+ virtual ~CAESinkWASAPI();
+
+ virtual bool Initialize (AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+ virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
+
+ virtual double GetDelay ();
+ virtual double GetCacheTime ();
+ virtual double GetCacheTotal ();
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames);
+ static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList);
+private:
+ bool InitializeShared(AEAudioFormat &format);
+ bool InitializeExclusive(AEAudioFormat &format);
+ void AEChannelsFromSpeakerMask(DWORD speakers);
+ DWORD SpeakerMaskFromAEChannels(const CAEChannelInfo &channels);
+ void BuildWaveFormatExtensible(AEAudioFormat &format, WAVEFORMATEXTENSIBLE &wfxex);
+ void BuildWaveFormatExtensibleIEC61397(AEAudioFormat &format, WAVEFORMATEXTENSIBLE_IEC61937 &wfxex);
+
+ static const char *WASAPIErrToStr(HRESULT err);
+
+ HANDLE m_needDataEvent;
+ IMMDevice *m_pDevice;
+ IAudioClient *m_pAudioClient;
+ IAudioRenderClient *m_pRenderClient;
+
+ AEAudioFormat m_format;
+ enum AEDataFormat m_encodedFormat;
+ unsigned int m_encodedChannels;
+ unsigned int m_encodedSampleRate;
+ CAEChannelInfo m_channelLayout;
+ std::string m_device;
+
+ enum AEDataFormat sinkReqFormat;
+ enum AEDataFormat sinkRetFormat;
+
+ bool m_running;
+ bool m_initialized;
+ bool m_isExclusive;
+
+ unsigned int m_uiBufferLen; // wasapi endpoint buffer size, in frames
+ double avgTimeWaiting; // time between next buffer of data from SoftAE and driver call for data
+}; \ No newline at end of file
diff --git a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp
new file mode 100644
index 0000000000..f6f2731f5a
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEBitstreamPacker.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#define BURST_HEADER_SIZE 8
+#define TRUEHD_FRAME_OFFSET 2560
+#define MAT_MIDDLE_CODE_OFFSET -4
+#define MAT_FRAME_SIZE 61424
+
+CAEBitstreamPacker::CAEBitstreamPacker() :
+ m_trueHD (NULL),
+ m_trueHDPos(0),
+ m_dtsHD (NULL),
+ m_dtsHDSize(0),
+ m_dataSize (0)
+{
+}
+
+CAEBitstreamPacker::~CAEBitstreamPacker()
+{
+ delete[] m_trueHD;
+ delete[] m_dtsHD;
+}
+
+void CAEBitstreamPacker::Pack(CAEStreamInfo &info, uint8_t* data, int size)
+{
+ switch (info.GetDataType())
+ {
+ case CAEStreamInfo::STREAM_TYPE_TRUEHD:
+ PackTrueHD(info, data, size);
+ break;
+
+ case CAEStreamInfo::STREAM_TYPE_DTSHD:
+ PackDTSHD (info, data, size);
+ break;
+
+ default:
+ /* pack the data into an IEC61937 frame */
+ CAEPackIEC61937::PackFunc pack = info.GetPackFunc();
+ if (pack)
+ m_dataSize = pack(data, size, m_packedBuffer);
+ }
+}
+
+unsigned int CAEBitstreamPacker::GetSize()
+{
+ return m_dataSize;
+}
+
+uint8_t* CAEBitstreamPacker::GetBuffer()
+{
+ m_dataSize = 0;
+ return m_packedBuffer;
+}
+
+/* we need to pack 24 TrueHD audio units into the unknown MAT format before packing into IEC61937 */
+void CAEBitstreamPacker::PackTrueHD(CAEStreamInfo &info, uint8_t* data, int size)
+{
+ /* magic MAT format values, meaning is unknown at this point */
+ static const uint8_t mat_start_code [20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 };
+ static const uint8_t mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 };
+ static const uint8_t mat_end_code [16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 };
+
+ /* create the buffer if it doesnt already exist */
+ if (!m_trueHD)
+ {
+ m_trueHD = new uint8_t[MAT_FRAME_SIZE];
+ m_trueHDPos = 0;
+ }
+
+ /* setup the frame for the data */
+ if (m_trueHDPos == 0)
+ {
+ memset(m_trueHD, 0, MAT_FRAME_SIZE);
+ memcpy(m_trueHD, mat_start_code, sizeof(mat_start_code));
+ memcpy(m_trueHD + (12 * TRUEHD_FRAME_OFFSET) - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET, mat_middle_code, sizeof(mat_middle_code));
+ memcpy(m_trueHD + MAT_FRAME_SIZE - sizeof(mat_end_code), mat_end_code, sizeof(mat_end_code));
+ }
+
+ size_t offset;
+ if (m_trueHDPos == 0 )
+ offset = (m_trueHDPos * TRUEHD_FRAME_OFFSET) + sizeof(mat_start_code);
+ else if (m_trueHDPos == 12)
+ offset = (m_trueHDPos * TRUEHD_FRAME_OFFSET) + sizeof(mat_middle_code) - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET;
+ else
+ offset = (m_trueHDPos * TRUEHD_FRAME_OFFSET) - BURST_HEADER_SIZE;
+
+ memcpy(m_trueHD + offset, data, size);
+
+ /* if we have a full frame */
+ if (++m_trueHDPos == 24)
+ {
+ m_trueHDPos = 0;
+ m_dataSize = CAEPackIEC61937::PackTrueHD(m_trueHD, MAT_FRAME_SIZE, m_packedBuffer);
+ }
+}
+
+void CAEBitstreamPacker::PackDTSHD(CAEStreamInfo &info, uint8_t* data, int size)
+{
+ static const uint8_t dtshd_start_code[10] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe };
+ unsigned int dataSize = sizeof(dtshd_start_code) + 2 + size;
+
+ if (dataSize > m_dtsHDSize)
+ {
+ delete[] m_dtsHD;
+ m_dtsHDSize = dataSize;
+ m_dtsHD = new uint8_t[dataSize];
+ memcpy(m_dtsHD, dtshd_start_code, sizeof(dtshd_start_code));
+ }
+
+ m_dtsHD[sizeof(dtshd_start_code) + 0] = ((uint16_t)size & 0xFF00) >> 8;
+ m_dtsHD[sizeof(dtshd_start_code) + 1] = ((uint16_t)size & 0x00FF);
+ memcpy(m_dtsHD + sizeof(dtshd_start_code) + 2, data, size);
+
+ m_dataSize = CAEPackIEC61937::PackDTSHD(m_dtsHD, dataSize, m_packedBuffer, info.GetDTSPeriod());
+}
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h
new file mode 100644
index 0000000000..efbb80c6b0
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h
@@ -0,0 +1,52 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include <list>
+#include "AEPackIEC61937.h"
+#include "AEStreamInfo.h"
+
+class CAEBitstreamPacker
+{
+public:
+ CAEBitstreamPacker();
+ ~CAEBitstreamPacker();
+
+ void Pack(CAEStreamInfo &info, uint8_t* data, int size);
+ uint8_t* GetBuffer();
+ unsigned int GetSize ();
+
+private:
+ void PackTrueHD(CAEStreamInfo &info, uint8_t* data, int size);
+ void PackDTSHD (CAEStreamInfo &info, uint8_t* data, int size);
+
+ /* we keep the trueHD and dtsHD buffers seperate so that we can handle a fast stream switch */
+ uint8_t *m_trueHD;
+ unsigned int m_trueHDPos;
+
+ uint8_t *m_dtsHD;
+ unsigned int m_dtsHDSize;
+
+ unsigned int m_dataSize;
+ uint8_t m_packedBuffer[MAX_IEC61937_PACKET];
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp b/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp
new file mode 100644
index 0000000000..b9a8a96a4d
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEBuffer.h"
+#include "utils/StdString.h" /* needed for ASSERT */
+#include <algorithm>
+
+CAEBuffer::CAEBuffer() :
+ m_buffer (NULL),
+ m_bufferSize(0 ),
+ m_bufferPos (0 ),
+ m_cursorPos (0 )
+{
+}
+
+CAEBuffer::~CAEBuffer()
+{
+ DeAlloc();
+}
+
+void CAEBuffer::Alloc(const size_t size)
+{
+ DeAlloc();
+ m_buffer = (uint8_t*)_aligned_malloc(size, 16);
+ m_bufferSize = size;
+ m_bufferPos = 0;
+}
+
+void CAEBuffer::ReAlloc(const size_t size)
+{
+#if defined(TARGET_WINDOWS)
+ m_buffer = (uint8_t*)_aligned_realloc(m_buffer, size, 16);
+#else
+ uint8_t* tmp = (uint8_t*)_aligned_malloc(size, 16);
+ if (m_buffer)
+ {
+ size_t copy = std::min(size, m_bufferSize);
+ memcpy(tmp, m_buffer, copy);
+ _aligned_free(m_buffer);
+ }
+ m_buffer = tmp;
+#endif
+
+ m_bufferSize = size;
+ m_bufferPos = std::min(m_bufferPos, m_bufferSize);
+}
+
+void CAEBuffer::DeAlloc()
+{
+ if (m_buffer)
+ _aligned_free(m_buffer);
+ m_buffer = NULL;
+ m_bufferSize = 0;
+ m_bufferPos = 0;
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AEBuffer.h b/xbmc/cores/AudioEngine/Utils/AEBuffer.h
new file mode 100644
index 0000000000..6f22db6027
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEBuffer.h
@@ -0,0 +1,172 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+
+#ifdef _DEBUG
+#include "utils/StdString.h" /* needed for ASSERT */
+#endif
+
+/**
+ * This class wraps a block of 16 byte aligned memory for simple buffer
+ * operations, if _DEBUG is defined then size is always verified.
+ */
+class CAEBuffer
+{
+private:
+ uint8_t *m_buffer;
+ size_t m_bufferSize;
+ size_t m_bufferPos;
+ size_t m_cursorPos;
+
+public:
+ CAEBuffer();
+ ~CAEBuffer();
+
+ /* initialize methods */
+ void Alloc (const size_t size);
+ void ReAlloc(const size_t size);
+ void DeAlloc();
+
+ /* usage methods */
+ inline size_t Size () { return m_bufferSize; }
+ inline size_t Used () { return m_bufferPos ; }
+ inline size_t Free () { return m_bufferSize - m_bufferPos; }
+ inline void Empty() { m_bufferPos = 0; }
+
+ /* write methods */
+ inline void Write(const void *src, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(src);
+ ASSERT(size <= m_bufferSize);
+ #endif
+ memcpy(m_buffer, src, size);
+ m_bufferPos = 0;
+ }
+
+ inline void Push(const void *src, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(src);
+ ASSERT(size <= m_bufferSize - m_bufferPos);
+ #endif
+ memcpy(m_buffer + m_bufferPos, src, size);
+ m_bufferPos += size;
+ }
+
+ inline void UnShift(const void *src, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(src);
+ ASSERT(size < m_bufferSize - m_bufferPos);
+ #endif
+ memmove(m_buffer + size, m_buffer, m_bufferSize - size);
+ memcpy (m_buffer, src, size);
+ m_bufferPos += size;
+ }
+
+ inline void* Take(const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(size <= m_bufferSize - m_bufferPos);
+ #endif
+
+ void* ret = m_buffer + m_bufferPos;
+ m_bufferPos += size;
+ return ret;
+ }
+
+ /* raw methods */
+ inline void* Raw(const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(size <= m_bufferSize);
+ #endif
+ return m_buffer;
+ }
+
+ /* read methods */
+ inline void Read(void *dst, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(size <= m_bufferSize);
+ ASSERT(dst);
+ #endif
+ memcpy(dst, m_buffer, size);
+ }
+
+ inline void Pop(void *dst, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(size <= m_bufferPos);
+ #endif
+ m_bufferPos -= size;
+ if (dst)
+ memcpy(dst, m_buffer + m_bufferPos, size);
+ }
+
+ inline void Shift(void *dst, const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(size <= m_bufferPos);
+ #endif
+ if (dst)
+ memcpy(dst, m_buffer, size);
+ memmove(m_buffer, m_buffer + size, m_bufferSize - size);
+ m_bufferPos -= size;
+ }
+
+ /* cursor methods */
+ inline void CursorReset()
+ {
+ m_cursorPos = 0;
+ }
+
+ inline size_t CursorOffset()
+ {
+ return m_cursorPos;
+ }
+
+ inline bool CursorEnd()
+ {
+ return m_cursorPos == m_bufferPos;
+ }
+
+ inline void CursorSeek (const size_t pos )
+ {
+ #ifdef _DEBUG
+ ASSERT(pos <= m_bufferSize);
+ #endif
+ m_cursorPos = pos;
+ }
+
+ inline void* CursorRead(const size_t size)
+ {
+ #ifdef _DEBUG
+ ASSERT(m_cursorPos + size <= m_bufferPos);
+ #endif
+ uint8_t* out = m_buffer + m_cursorPos;
+ m_cursorPos += size;
+ return out;
+ }
+};
diff --git a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp
new file mode 100644
index 0000000000..8800186020
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEChannelInfo.h"
+#include <string.h>
+
+CAEChannelInfo::CAEChannelInfo()
+{
+ Reset();
+}
+
+CAEChannelInfo::CAEChannelInfo(const enum AEChannel* rhs)
+{
+ *this = rhs;
+}
+
+CAEChannelInfo::CAEChannelInfo(const AEStdChLayout rhs)
+{
+ *this = rhs;
+}
+
+CAEChannelInfo::~CAEChannelInfo()
+{
+}
+
+void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs)
+{
+ /* mono gets upmixed to dual mono */
+ if (m_channelCount == 1 && m_channels[0] == AE_CH_FC)
+ {
+ Reset();
+ *this += AE_CH_FL;
+ *this += AE_CH_FR;
+ return;
+ }
+
+ bool srcHasSL = false;
+ bool srcHasSR = false;
+ bool srcHasRL = false;
+ bool srcHasRR = false;
+
+ bool dstHasSL = false;
+ bool dstHasSR = false;
+ bool dstHasRL = false;
+ bool dstHasRR = false;
+
+ for (unsigned int c = 0; c < rhs.m_channelCount; ++c)
+ switch(rhs.m_channels[c])
+ {
+ case AE_CH_SL: dstHasSL = true; break;
+ case AE_CH_SR: dstHasSR = true; break;
+ case AE_CH_BL: dstHasRL = true; break;
+ case AE_CH_BR: dstHasRR = true; break;
+ default:
+ break;
+ }
+
+ CAEChannelInfo newInfo;
+ for (unsigned int i = 0; i < m_channelCount; ++i)
+ {
+ switch (m_channels[i])
+ {
+ case AE_CH_SL: srcHasSL = true; break;
+ case AE_CH_SR: srcHasSR = true; break;
+ case AE_CH_BL: srcHasRL = true; break;
+ case AE_CH_BR: srcHasRR = true; break;
+ default:
+ break;
+ }
+
+ bool found = false;
+ for (unsigned int c = 0; c < rhs.m_channelCount; ++c)
+ if (m_channels[i] == rhs.m_channels[c])
+ {
+ found = true;
+ break;
+ }
+
+ if (found)
+ newInfo += m_channels[i];
+ }
+
+ /* we need to ensure we end up with rear or side channels for downmix to work */
+ if (srcHasSL && !dstHasSL && dstHasRL)
+ newInfo += AE_CH_BL;
+ if (srcHasSR && !dstHasSR && dstHasRR)
+ newInfo += AE_CH_BR;
+ if (srcHasRL && !dstHasRL && dstHasSL)
+ newInfo += AE_CH_SL;
+ if (srcHasRR && !dstHasRR && dstHasSR)
+ newInfo += AE_CH_SR;
+
+ *this = newInfo;
+}
+
+void CAEChannelInfo::Reset()
+{
+ m_channelCount = 0;
+ for (unsigned int i = 0; i < AE_CH_MAX; ++i)
+ m_channels[i] = AE_CH_NULL;
+}
+
+CAEChannelInfo& CAEChannelInfo::operator=(const CAEChannelInfo& rhs)
+{
+ if (this == &rhs)
+ return *this;
+
+ /* clone the information */
+ m_channelCount = rhs.m_channelCount;
+ memcpy(m_channels, rhs.m_channels, sizeof(enum AEChannel) * rhs.m_channelCount);
+
+ return *this;
+}
+
+CAEChannelInfo& CAEChannelInfo::operator=(const enum AEChannel* rhs)
+{
+ Reset();
+ if (rhs == NULL)
+ return *this;
+
+ while (m_channelCount < AE_CH_MAX && rhs[m_channelCount] != AE_CH_NULL)
+ {
+ m_channels[m_channelCount] = rhs[m_channelCount];
+ ++m_channelCount;
+ }
+
+ /* the last entry should be NULL, if not we were passed a non null terminated list */
+ ASSERT(rhs[m_channelCount] == AE_CH_NULL);
+
+ return *this;
+}
+
+CAEChannelInfo& CAEChannelInfo::operator=(const enum AEStdChLayout rhs)
+{
+ ASSERT(rhs > AE_CH_LAYOUT_INVALID && rhs < AE_CH_LAYOUT_MAX);
+
+ static enum AEChannel layouts[AE_CH_LAYOUT_MAX][9] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL , AE_CH_BR , AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL , AE_CH_BR , AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_SL , AE_CH_SR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_SL , AE_CH_SR, AE_CH_LFE, AE_CH_NULL}
+ };
+
+ *this = layouts[rhs];
+ return *this;
+}
+
+bool CAEChannelInfo::operator==(const CAEChannelInfo& rhs)
+{
+ /* if the channel count doesnt match, no need to check further */
+ if (m_channelCount != rhs.m_channelCount)
+ return false;
+
+ /* make sure the channel order is the same */
+ for (unsigned int i = 0; i < m_channelCount; ++i)
+ if (m_channels[i] != rhs.m_channels[i])
+ return false;
+
+ return true;
+}
+
+bool CAEChannelInfo::operator!=(const CAEChannelInfo& rhs)
+{
+ return !(*this == rhs);
+}
+
+void CAEChannelInfo::operator+=(const enum AEChannel rhs)
+{
+ ASSERT(m_channelCount < AE_CH_MAX);
+ ASSERT(rhs > AE_CH_NULL && rhs < AE_CH_MAX);
+
+ m_channels[m_channelCount++] = rhs;
+}
+
+const enum AEChannel CAEChannelInfo::operator[](unsigned int i) const
+{
+ ASSERT(i < m_channelCount);
+ return m_channels[i];
+}
+
+CAEChannelInfo::operator std::string()
+{
+ if (m_channelCount == 0)
+ return "NULL";
+
+ std::string s;
+ for (unsigned int i = 0; i < m_channelCount - 1; ++i)
+ {
+ s.append(GetChName(m_channels[i]));
+ s.append(",");
+ }
+ s.append(GetChName(m_channels[m_channelCount-1]));
+
+ return s;
+}
+
+const char* CAEChannelInfo::GetChName(const enum AEChannel ch)
+{
+ ASSERT(ch >= 0 || ch < AE_CH_MAX);
+
+ static const char* channels[AE_CH_MAX] =
+ {
+ "RAW" ,
+ "FL" , "FR" , "FC" , "LFE", "BL" , "BR" , "FLOC",
+ "FROC", "BC" , "SL" , "SR" , "TFL" , "TFR" , "TFC" ,
+ "TC" , "TBL", "TBR", "TBC", "BLOC", "BROC",
+
+ /* p16v devices */
+ "UNKNOWN1",
+ "UNKNOWN2",
+ "UNKNOWN3",
+ "UNKNOWN4",
+ "UNKNOWN5",
+ "UNKNOWN6",
+ "UNKNOWN7",
+ "UNKNOWN8"
+ };
+
+ return channels[ch];
+}
+
+bool CAEChannelInfo::HasChannel(const enum AEChannel ch) const
+{
+ for (unsigned int i = 0; i < m_channelCount; ++i)
+ if (m_channels[i] == ch)
+ return true;
+ return false;
+}
+
+bool CAEChannelInfo::ContainsChannels(CAEChannelInfo& rhs) const
+{
+ for (unsigned int i = 0; i < rhs.m_channelCount; ++i)
+ {
+ if (!HasChannel(rhs.m_channels[i]))
+ return false;
+ }
+ return true;
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h
new file mode 100644
index 0000000000..37dd5134be
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h
@@ -0,0 +1,99 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include "utils/StdString.h"
+
+/**
+ * The possible channels
+ */
+enum AEChannel
+{
+ AE_CH_NULL = -1,
+ AE_CH_RAW ,
+
+ AE_CH_FL , AE_CH_FR , AE_CH_FC , AE_CH_LFE, AE_CH_BL , AE_CH_BR , AE_CH_FLOC,
+ AE_CH_FROC, AE_CH_BC , AE_CH_SL , AE_CH_SR , AE_CH_TFL , AE_CH_TFR , AE_CH_TFC ,
+ AE_CH_TC , AE_CH_TBL, AE_CH_TBR, AE_CH_TBC, AE_CH_BLOC, AE_CH_BROC,
+
+ /* p16v devices */
+ AE_CH_UNKNOWN1,
+ AE_CH_UNKNOWN2,
+ AE_CH_UNKNOWN3,
+ AE_CH_UNKNOWN4,
+ AE_CH_UNKNOWN5,
+ AE_CH_UNKNOWN6,
+ AE_CH_UNKNOWN7,
+ AE_CH_UNKNOWN8,
+
+ AE_CH_MAX
+};
+
+/**
+ * Standard channel layouts
+ */
+enum AEStdChLayout
+{
+ AE_CH_LAYOUT_INVALID = -1,
+
+ AE_CH_LAYOUT_1_0 = 0,
+ AE_CH_LAYOUT_2_0,
+ AE_CH_LAYOUT_2_1,
+ AE_CH_LAYOUT_3_0,
+ AE_CH_LAYOUT_3_1,
+ AE_CH_LAYOUT_4_0,
+ AE_CH_LAYOUT_4_1,
+ AE_CH_LAYOUT_5_0,
+ AE_CH_LAYOUT_5_1,
+ AE_CH_LAYOUT_7_0,
+ AE_CH_LAYOUT_7_1,
+
+ AE_CH_LAYOUT_MAX
+};
+
+class CAEChannelInfo {
+public:
+ CAEChannelInfo();
+ CAEChannelInfo(const enum AEChannel* rhs);
+ CAEChannelInfo(const enum AEStdChLayout rhs);
+ ~CAEChannelInfo();
+ CAEChannelInfo& operator=(const CAEChannelInfo& rhs);
+ CAEChannelInfo& operator=(const enum AEChannel* rhs);
+ CAEChannelInfo& operator=(const enum AEStdChLayout rhs);
+ bool operator==(const CAEChannelInfo& rhs);
+ bool operator!=(const CAEChannelInfo& rhs);
+ void operator+=(const enum AEChannel rhs);
+ const enum AEChannel operator[](unsigned int i) const;
+ operator std::string();
+
+ /* remove any channels that dont exist in the provided info */
+ void ResolveChannels(const CAEChannelInfo& rhs);
+ void Reset();
+ inline unsigned int Count() const { return m_channelCount; }
+ static const char* GetChName(const enum AEChannel ch);
+ bool HasChannel(const enum AEChannel ch) const;
+ bool ContainsChannels(CAEChannelInfo& rhs) const;
+private:
+ unsigned int m_channelCount;
+ enum AEChannel m_channels[AE_CH_MAX];
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEConvert.cpp b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp
new file mode 100644
index 0000000000..b523ca11f1
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp
@@ -0,0 +1,1116 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifndef __STDC_LIMIT_MACROS
+ #define __STDC_LIMIT_MACROS
+#endif
+
+#include "AEConvert.h"
+#include "AEUtil.h"
+#include "utils/MathUtils.h"
+#include "utils/EndianSwap.h"
+#include <stdint.h>
+
+#if defined(TARGET_WINDOWS)
+#include <unistd.h>
+#endif
+#include <math.h>
+#include <string.h>
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#endif
+
+#ifdef __ARM_NEON__
+#include <arm_neon.h>
+#endif
+
+#define CLAMP(x) std::min(-1.0f, std::max(1.0f, (float)(x)))
+
+#ifndef INT24_MAX
+#define INT24_MAX (0x7FFFFF)
+#endif
+
+#define INT32_SCALE (-1.0f / INT_MIN)
+
+static inline int safeRound(double f)
+{
+ /* if the value is larger then we can handle, then clamp it */
+ if (f >= INT_MAX)
+ return INT_MAX;
+ if (f <= INT_MIN)
+ return INT_MIN;
+
+ /* if the value is out of the MathUtils::round_int range, then round it normally */
+ if (f <= static_cast<double>(INT_MIN / 2) - 1.0 || f >= static_cast <double>(INT_MAX / 2) + 1.0)
+ return (int)floor(f+0.5);
+
+ return MathUtils::round_int(f);
+}
+
+CAEConvert::AEConvertToFn CAEConvert::ToFloat(enum AEDataFormat dataFormat)
+{
+ switch (dataFormat)
+ {
+ case AE_FMT_U8 : return &U8_Float;
+ case AE_FMT_S8 : return &S8_Float;
+#ifdef __BIG_ENDIAN__
+ case AE_FMT_S16NE : return &S16BE_Float;
+ case AE_FMT_S32NE : return &S32BE_Float;
+ case AE_FMT_S24NE4: return &S24BE4_Float;
+ case AE_FMT_S24NE3: return &S24BE3_Float;
+#else
+ case AE_FMT_S16NE : return &S16LE_Float;
+ case AE_FMT_S32NE : return &S32LE_Float;
+ case AE_FMT_S24NE4: return &S24LE4_Float;
+ case AE_FMT_S24NE3: return &S24LE3_Float;
+#endif
+ case AE_FMT_S16LE : return &S16LE_Float;
+ case AE_FMT_S16BE : return &S16BE_Float;
+ case AE_FMT_S24LE4: return &S24LE4_Float;
+ case AE_FMT_S24BE4: return &S24BE4_Float;
+ case AE_FMT_S24LE3: return &S24LE3_Float;
+ case AE_FMT_S24BE3: return &S24BE3_Float;
+ case AE_FMT_S32LE : return &S32LE_Float;
+ case AE_FMT_S32BE : return &S32BE_Float;
+ case AE_FMT_DOUBLE: return &DOUBLE_Float;
+ default:
+ return NULL;
+ }
+}
+
+CAEConvert::AEConvertFrFn CAEConvert::FrFloat(enum AEDataFormat dataFormat)
+{
+ switch (dataFormat)
+ {
+ case AE_FMT_U8 : return &Float_U8;
+ case AE_FMT_S8 : return &Float_S8;
+#ifdef __BIG_ENDIAN__
+ case AE_FMT_S16NE : return &Float_S16BE;
+ case AE_FMT_S32NE : return &Float_S32BE;
+#else
+ case AE_FMT_S16NE : return &Float_S16LE;
+ case AE_FMT_S32NE : return &Float_S32LE;
+#endif
+ case AE_FMT_S16LE : return &Float_S16LE;
+ case AE_FMT_S16BE : return &Float_S16BE;
+ case AE_FMT_S24NE4: return &Float_S24NE4;
+ case AE_FMT_S24NE3: return &Float_S24NE3;
+ case AE_FMT_S32LE : return &Float_S32LE;
+ case AE_FMT_S32BE : return &Float_S32BE;
+ case AE_FMT_DOUBLE: return &Float_DOUBLE;
+ default:
+ return NULL;
+ }
+}
+
+unsigned int CAEConvert::U8_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ const float mul = 2.0f / UINT8_MAX;
+
+ for (unsigned int i = 0; i < samples; ++i, ++data, ++dest)
+ *dest = *(uint8_t*)data * mul - 1.0f;
+
+ return samples;
+}
+
+unsigned int CAEConvert::S8_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ const float mul = 1.0f / (INT8_MAX + 0.5f);
+
+ for (unsigned int i = 0; i < samples; ++i, ++data, ++dest)
+ *dest = *(int8_t*)data * mul;
+
+ return samples;
+}
+
+unsigned int CAEConvert::S16LE_Float(uint8_t* data, const unsigned int samples, float *dest)
+{
+ static const float mul = 1.0f / (INT16_MAX + 0.5f);
+
+#ifdef __arm__
+ for (int i = 0; i < samples; i++)
+ {
+ __asm__ __volatile__ (
+ "ldrsh r1,[%[in]] \n\t" // Read a halfword from the source address
+#ifdef __BIG_ENDIAN__
+ "revsh r1,r1 \n\t" // Swap byte order
+#endif
+ "vmov s1,r1 \n\t" // Copy input into a fp working register
+ "fsitos s1,s1 \n\t" // Convert from signed int to float (single)
+ "vmul.F32 s1,s1,%[mul] \n\t" // Scale
+ "vstr.32 s1, [%[out]] \n\t" // Transfer the result from the coprocessor
+ : // Outputs
+ : [in] "r" (data), [out] "r" (dest), [mul] "w" (mul) // Inputs
+ : "s1","r1" // Clobbers
+ );
+ data+=2;
+ dest++;
+ }
+#else
+ for (unsigned int i = 0; i < samples; ++i, data += 2, ++dest)
+ *dest = Endian_SwapLE16(*(int16_t*)data) * mul;
+#endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::S16BE_Float(uint8_t* data, const unsigned int samples, float *dest)
+{
+ static const float mul = 1.0f / (INT16_MAX + 0.5f);
+
+#ifdef __arm__
+ for (int i = 0; i < samples; i++)
+ {
+ __asm__ __volatile__ (
+ "ldrsh r1,[%[in]] \n\t" // Read a halfword from the source address
+#ifndef __BIG_ENDIAN__
+ "revsh r1,r1 \n\t" // Swap byte order
+#endif
+ "vmov s1,r1 \n\t" // Copy input into a fp working register
+ "fsitos s1,s1 \n\t" // Convert from signed int to float (single)
+ "vmul.F32 s1,s1,%[mul] \n\t" // Scale
+ "vstr.32 s1, [%[out]] \n\t" // Transfer the result from the coprocessor
+ : // Outputs
+ : [in] "r" (data), [out] "r" (dest), [mul] "w" (mul) // Inputs
+ : "s1","r1" // Clobbers
+ );
+ data+=2;
+ dest++;
+ }
+#else
+ for (unsigned int i = 0; i < samples; ++i, data += 2, ++dest)
+ *dest = Endian_SwapBE16(*(int16_t*)data) * mul;
+#endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::S24LE4_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ for (unsigned int i = 0; i < samples; ++i, ++dest, data += 4)
+ {
+ int s = (data[2] << 24) | (data[1] << 16) | (data[0] << 8);
+ *dest = (float)s * INT32_SCALE;
+ }
+ return samples;
+}
+
+unsigned int CAEConvert::S24BE4_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ for (unsigned int i = 0; i < samples; ++i, ++dest, data += 4)
+ {
+ int s = (data[0] << 24) | (data[1] << 16) | (data[2] << 8);
+ *dest = (float)s * INT32_SCALE;
+ }
+ return samples;
+}
+
+unsigned int CAEConvert::S24LE3_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ for (unsigned int i = 0; i < samples; ++i, ++dest, data += 3)
+ {
+ int s = (data[2] << 24) | (data[1] << 16) | (data[0] << 8);
+ *dest = (float)s * INT32_SCALE;
+ }
+ return samples;
+}
+
+unsigned int CAEConvert::S24BE3_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ for (unsigned int i = 0; i < samples; ++i, ++dest, data += 3)
+ {
+ int s = (data[1] << 24) | (data[2] << 16) | (data[3] << 8);
+ *dest = (float)s * INT32_SCALE;
+ }
+ return samples;
+}
+
+unsigned int CAEConvert::S32LE_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ static const float factor = 1.0f / (float)INT32_MAX;
+ int32_t *src = (int32_t*)data;
+
+#if defined(__ARM_NEON__)
+
+ /* groups of 4 samples */
+ for (float *end = dest + (samples & ~0x3); dest < end; src += 4, dest += 4)
+ {
+ int32x4_t val = vld1q_s32(src);
+ #ifdef __BIG_ENDIAN__
+ val = vrev64q_s32(val);
+ #endif
+ float32x4_t ret = vmulq_n_f32(vcvtq_f32_s32(val), factor);
+ vst1q_f32((float32_t *)dest, ret);
+ }
+
+ /* if there are >= 2 remaining samples */
+ if (samples & 0x2)
+ {
+ int32x2_t val = vld1_s32(src);
+ #ifdef __BIG_ENDIAN__
+ val = vrev64_s32(val);
+ #endif
+ float32x2_t ret = vmul_n_f32(vcvt_f32_s32(val), factor);
+ vst1_f32((float32_t *)dest, ret);
+ src += 2;
+ dest += 2;
+ }
+
+ /* if there is one remaining sample */
+ if (samples & 0x1)
+ dest[0] = (float)src[0] * factor;
+
+#else /* !defined(__ARM_NEON__) */
+
+ /* do this in groups of 4 to give the compiler a better chance of optimizing this */
+ for (float *end = dest + (samples & ~0x3); dest < end; src += 4, dest += 4)
+ {
+ dest[0] = (float)Endian_SwapLE32(src[0]) * factor;
+ dest[1] = (float)Endian_SwapLE32(src[1]) * factor;
+ dest[2] = (float)Endian_SwapLE32(src[2]) * factor;
+ dest[3] = (float)Endian_SwapLE32(src[3]) * factor;
+ }
+
+ /* process any remaining samples */
+ for (float *end = dest + (samples & 0x3); dest < end; ++src, ++dest)
+ dest[0] = (float)Endian_SwapLE32(src[0]) * factor;
+
+#endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::S32BE_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ static const float factor = 1.0f / (float)INT32_MAX;
+ int32_t *src = (int32_t*)data;
+
+#if defined(__ARM_NEON__)
+
+ /* groups of 4 samples */
+ for (float *end = dest + (samples & ~0x3); dest < end; src += 4, dest += 4)
+ {
+ int32x4_t val = vld1q_s32(src);
+ #ifndef __BIG_ENDIAN__
+ val = vrev64q_s32(val);
+ #endif
+ float32x4_t ret = vmulq_n_f32(vcvtq_f32_s32(val), factor);
+ vst1q_f32((float32_t *)dest, ret);
+ }
+
+ /* if there are >= 2 remaining samples */
+ if (samples & 0x2)
+ {
+ int32x2_t val = vld1_s32(src);
+ #ifndef __BIG_ENDIAN__
+ val = vrev64_s32(val);
+ #endif
+ float32x2_t ret = vmul_n_f32(vcvt_f32_s32(val), factor);
+ vst1_f32((float32_t *)dest, ret);
+ src += 2;
+ dest += 2;
+ }
+
+ /* if there is one remaining sample */
+ if (samples & 0x1)
+ dest[0] = (float)src[0] * factor;
+
+#else /* !defined(__ARM_NEON__) */
+
+ /* do this in groups of 4 to give the compiler a better chance of optimizing this */
+ for (float *end = dest + (samples & ~0x3); dest < end; src += 4, dest += 4)
+ {
+ dest[0] = (float)Endian_SwapBE32(src[0]) * factor;
+ dest[1] = (float)Endian_SwapBE32(src[1]) * factor;
+ dest[2] = (float)Endian_SwapBE32(src[2]) * factor;
+ dest[3] = (float)Endian_SwapBE32(src[3]) * factor;
+ }
+
+ /* process any remaining samples */
+ for (float *end = dest + (samples & 0x3); dest < end; ++src, ++dest)
+ dest[0] = (float)Endian_SwapBE32(src[0]) * factor;
+
+#endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::DOUBLE_Float(uint8_t *data, const unsigned int samples, float *dest)
+{
+ double *src = (double*)data;
+ for (unsigned int i = 0; i < samples; ++i, ++src, ++dest)
+ *dest = CLAMP(*src / (float)INT32_MAX);
+
+ return samples;
+}
+
+unsigned int CAEConvert::Float_U8(float *data, const unsigned int samples, uint8_t *dest)
+{
+ #ifdef __SSE__
+ const __m128 mul = _mm_set_ps1((float)INT8_MAX+.5f);
+ const __m128 add = _mm_set_ps1(1.0f);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ dest[0] = safeRound((data[0] + 1.0f) * ((float)INT8_MAX+.5f));
+ ++data;
+ ++dest;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dest += 4)
+ {
+ __m128 in = _mm_mul_ps(_mm_add_ps(_mm_load_ps(data), add), mul);
+ __m64 con = _mm_cvtps_pi16(in);
+
+ int16_t temp[4];
+ memcpy(temp, &con, sizeof(temp));
+ dest[0] = (uint8_t)temp[0];
+ dest[1] = (uint8_t)temp[1];
+ dest[2] = (uint8_t)temp[2];
+ dest[3] = (uint8_t)temp[3];
+ }
+
+ if (count != even)
+ {
+ const uint32_t odd = count - even;
+ if (odd == 1)
+ {
+ _mm_empty();
+ dest[0] = safeRound((data[0] + 1.0f) * ((float)INT8_MAX+.5f));
+ }
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(_mm_add_ps(_mm_load_ps(data), add), mul);
+ __m64 con = _mm_cvtps_pi16(in);
+
+ int16_t temp[2];
+ memcpy(temp, &con, sizeof(temp));
+ dest[0] = (uint8_t)temp[0];
+ dest[1] = (uint8_t)temp[1];
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(_mm_add_ps(_mm_load_ps(data), add), mul);
+ __m64 con = _mm_cvtps_pi16(in);
+
+ int16_t temp[3];
+ memcpy(temp, &con, sizeof(temp));
+ dest[0] = (uint8_t)temp[0];
+ dest[1] = (uint8_t)temp[1];
+ dest[2] = (uint8_t)temp[2];
+ }
+ }
+ }
+ _mm_empty();
+ #else /* no SSE */
+ for (uint32_t i = 0; i < samples; ++i, ++data, ++dest)
+ dest[0] = safeRound((data[0] + 1.0f) * ((float)INT8_MAX+.5f));
+ #endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::Float_S8(float *data, const unsigned int samples, uint8_t *dest)
+{
+ #ifdef __SSE__
+ const __m128 mul = _mm_set_ps1((float)INT8_MAX+.5f);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ dest[0] = safeRound(data[0] * ((float)INT8_MAX+.5f));
+ ++data;
+ ++dest;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dest += 4)
+ {
+ __m128 in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m64 con = _mm_cvtps_pi8(in);
+ memcpy(dest, &con, 4);
+ }
+
+ if (count != even)
+ {
+ const uint32_t odd = count - even;
+ if (odd == 1)
+ {
+ _mm_empty();
+ dest[0] = safeRound(data[0] * ((float)INT8_MAX+.5f));
+ }
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m64 con = _mm_cvtps_pi8(in);
+ memcpy(dest, &con, 2);
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m64 con = _mm_cvtps_pi8(in);
+ memcpy(dest, &con, 3);
+ }
+ }
+ }
+ _mm_empty();
+ #else /* no SSE */
+ for (uint32_t i = 0; i < samples; ++i, ++data, ++dest)
+ dest[0] = safeRound(data[0] * ((float)INT8_MAX+.5f));
+ #endif
+
+ return samples;
+}
+
+unsigned int CAEConvert::Float_S16LE(float *data, const unsigned int samples, uint8_t *dest)
+{
+ int16_t *dst = (int16_t*)dest;
+ #ifdef __SSE__
+
+ unsigned int count = samples;
+ unsigned int unaligned = (0x10 - ((uintptr_t)data & 0xF)) >> 2;
+ if (unaligned == 4)
+ unaligned = 0;
+
+ /*
+ if we are only out by one, dont use SSE to correct it.
+ this must run before we do any SSE work so that the FPU
+ is in a working state without having to first call
+ _mm_empty()
+ */
+ if (unaligned == 1)
+ dst[0] = Endian_SwapLE16(safeRound(data[0] * ((float)INT16_MAX + CAEUtil::FloatRand1(-0.5f, 0.5f))));
+
+ MEMALIGN(16, static const __m128 mul) = _mm_set_ps1((float)INT16_MAX);
+ MEMALIGN(16, __m128 rand);
+ MEMALIGN(16, __m128 in );
+ MEMALIGN(16, __m128i con );
+
+ /* if unaligned is greater then one, use SSE to correct it */
+ if (unaligned > 1)
+ {
+ switch (unaligned)
+ {
+ case 1: in = _mm_setr_ps(data[0], 0 , 0 , 0); break;
+ case 2: in = _mm_setr_ps(data[0], data[1], 0 , 0); break;
+ case 3: in = _mm_setr_ps(data[0], data[1], data[2], 0); break;
+ }
+
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(in, _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifdef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ if (unaligned == 3)
+ {
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ }
+ else if (unaligned == 2)
+ dst[1] = _mm_extract_epi16(con, 2);
+ }
+
+ /* update our pointers and sample count */
+ data += unaligned;
+ dst += unaligned;
+ count -= unaligned;
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
+ {
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(_mm_load_ps(data), _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifdef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ dst[3] = _mm_extract_epi16(con, 6);
+ }
+
+ /* calculate the final unaligned samples if there is any */
+ if (samples != even)
+ {
+ unaligned = samples - even;
+ switch (unaligned)
+ {
+ case 1: in = _mm_setr_ps(data[0], 0 , 0 , 0); break;
+ case 2: in = _mm_setr_ps(data[0], data[1], 0 , 0); break;
+ case 3: in = _mm_setr_ps(data[0], data[1], data[2], 0); break;
+ }
+
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(in, _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifdef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ if (unaligned == 3)
+ {
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ }
+ else if (unaligned == 2)
+ dst[1] = _mm_extract_epi16(con, 2);
+ }
+
+ /* cleanup */
+ _mm_empty();
+
+ #else /* no SSE */
+
+ uint32_t i = 0;
+ uint32_t even = samples & ~0x3;
+
+ for(; i < even; i += 4, data += 4, dst += 4)
+ {
+ /* random round to dither */
+ float rand[4];
+ CAEUtil::FloatRand4(-0.5f, 0.5f, rand);
+
+ dst[0] = Endian_SwapLE16(safeRound(data[0] * ((float)INT16_MAX + rand[0])));
+ dst[1] = Endian_SwapLE16(safeRound(data[1] * ((float)INT16_MAX + rand[1])));
+ dst[2] = Endian_SwapLE16(safeRound(data[2] * ((float)INT16_MAX + rand[2])));
+ dst[3] = Endian_SwapLE16(safeRound(data[3] * ((float)INT16_MAX + rand[3])));
+ }
+
+ for(; i < samples; ++i, ++data, ++dst)
+ dst[0] = Endian_SwapLE16(safeRound(data[0] * ((float)INT16_MAX + CAEUtil::FloatRand1(-0.5f, 0.5f))));
+
+ #endif
+
+ return samples << 1;
+}
+
+unsigned int CAEConvert::Float_S16BE(float *data, const unsigned int samples, uint8_t *dest)
+{
+ int16_t *dst = (int16_t*)dest;
+ #ifdef __SSE__
+
+ unsigned int count = samples;
+ unsigned int unaligned = (0x10 - ((uintptr_t)data & 0xF)) >> 2;
+ if (unaligned == 4)
+ unaligned = 0;
+
+ /*
+ if we are only out by one, dont use SSE to correct it.
+ this must run before we do any SSE work so that the FPU
+ is in a working state without having to first call
+ _mm_empty()
+ */
+ if (unaligned == 1)
+ dst[0] = Endian_SwapBE16(safeRound(data[0] * ((float)INT16_MAX + CAEUtil::FloatRand1(-0.5f, 0.5f))));
+
+ MEMALIGN(16, static const __m128 mul) = _mm_set_ps1((float)INT16_MAX);
+ MEMALIGN(16, __m128 rand);
+ MEMALIGN(16, __m128 in );
+ MEMALIGN(16, __m128i con );
+
+ /* if unaligned is greater then one, use SSE to correct it */
+ if (unaligned > 1)
+ {
+ switch (unaligned)
+ {
+ case 1: in = _mm_setr_ps(data[0], 0 , 0 , 0); break;
+ case 2: in = _mm_setr_ps(data[0], data[1], 0 , 0); break;
+ case 3: in = _mm_setr_ps(data[0], data[1], data[2], 0); break;
+ }
+
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(in, _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifndef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ if (unaligned == 3)
+ {
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ }
+ else if (unaligned == 2)
+ dst[1] = _mm_extract_epi16(con, 2);
+ }
+
+ /* update our pointers and sample count */
+ data += unaligned;
+ dst += unaligned;
+ count -= unaligned;
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
+ {
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(_mm_load_ps(data), _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifndef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ dst[3] = _mm_extract_epi16(con, 6);
+ }
+
+ /* calculate the final unaligned samples if there is any */
+ if (samples != even)
+ {
+ unaligned = samples - even;
+ switch (unaligned)
+ {
+ case 1: in = _mm_setr_ps(data[0], 0 , 0 , 0); break;
+ case 2: in = _mm_setr_ps(data[0], data[1], 0 , 0); break;
+ case 3: in = _mm_setr_ps(data[0], data[1], data[2], 0); break;
+ }
+
+ /* random round to dither */
+ CAEUtil::FloatRand4(-0.5f, 0.5f, NULL, &rand);
+ in = _mm_mul_ps(in, _mm_add_ps(mul, rand));
+ con = _mm_cvtps_epi32(in);
+
+ #ifndef __BIG_ENDIAN__
+ con = _mm_or_si128(_mm_slli_epi16(con, 8), _mm_srli_epi16(con, 8));
+ #endif
+
+ dst[0] = _mm_extract_epi16(con, 0);
+ if (unaligned == 3)
+ {
+ dst[1] = _mm_extract_epi16(con, 2);
+ dst[2] = _mm_extract_epi16(con, 4);
+ }
+ else if (unaligned == 2)
+ dst[1] = _mm_extract_epi16(con, 2);
+ }
+
+ /* cleanup */
+ _mm_empty();
+
+ #else /* no SSE */
+
+ uint32_t i = 0;
+ uint32_t even = samples & ~0x3;
+
+ for(; i < even; i += 4, data += 4, dst += 4)
+ {
+ /* random round to dither */
+ float rand[4];
+ CAEUtil::FloatRand4(-0.5f, 0.5f, rand);
+
+ dst[0] = Endian_SwapBE16(safeRound(data[0] * ((float)INT16_MAX + rand[0])));
+ dst[1] = Endian_SwapBE16(safeRound(data[1] * ((float)INT16_MAX + rand[1])));
+ dst[2] = Endian_SwapBE16(safeRound(data[2] * ((float)INT16_MAX + rand[2])));
+ dst[3] = Endian_SwapBE16(safeRound(data[3] * ((float)INT16_MAX + rand[3])));
+ }
+
+ for(; i < samples; ++i, ++data, ++dst)
+ dst[0] = Endian_SwapBE16(safeRound(data[0] * ((float)INT16_MAX + CAEUtil::FloatRand1(-0.5f, 0.5f))));
+
+ #endif
+
+ return samples << 1;
+}
+
+unsigned int CAEConvert::Float_S24NE4(float *data, const unsigned int samples, uint8_t *dest)
+{
+ int32_t *dst = (int32_t*)dest;
+ #ifdef __SSE__
+
+ const __m128 mul = _mm_set_ps1((float)INT24_MAX+.5f);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ dst[0] = safeRound(data[0] * ((float)INT24_MAX+.5f));
+ ++data;
+ ++dst;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
+ {
+ __m128 in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ con = _mm_slli_epi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 4);
+ }
+
+ if (samples != even)
+ {
+ const uint32_t odd = samples - even;
+ if (odd == 1)
+ dst[0] = safeRound(data[0] * ((float)INT24_MAX+.5f));
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(in, mul);
+ __m64 con = _mm_cvtps_pi32(in);
+ con = _mm_slli_pi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 2);
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(in, mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ con = _mm_slli_epi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 3);
+ }
+ }
+ }
+ _mm_empty();
+ #else /* no SSE */
+ for (uint32_t i = 0; i < samples; ++i, ++data, ++dst)
+ *dst = (safeRound(*data * ((float)INT24_MAX+.5f)) & 0xFFFFFF) << 8;
+ #endif
+
+ return samples << 2;
+}
+
+unsigned int CAEConvert::Float_S24NE3(float *data, const unsigned int samples, uint8_t *dest)
+{
+ #ifdef __SSE__
+ int32_t *dst = (int32_t*)dest;
+
+ const __m128 mul = _mm_set_ps1((float)INT24_MAX+.5f);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ *((uint32_t*)(dest)) = (safeRound(*data * ((float)INT24_MAX+.5f)) & 0xFFFFFF) << 8;
+ ++dest;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < count; i += 4, data += 4, dest += 12)
+ {
+ __m128 in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ con = _mm_slli_epi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 4);
+ *((uint32_t*)(dest + 0)) = (dst[0] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 3)) = (dst[1] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 6)) = (dst[2] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 9)) = (dst[3] & 0xFFFFFF) << 8;
+ }
+
+ if (samples != even)
+ {
+ const uint32_t odd = samples - even;
+ if (odd == 1)
+ dst[0] = safeRound(data[0] * ((float)INT24_MAX+.5f)) & 0xFFFFFF;
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(in, mul);
+ __m64 con = _mm_cvtps_pi32(in);
+ con = _mm_slli_pi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 2);
+ *((uint32_t*)(dest + 0)) = (dst[0] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 3)) = (dst[1] & 0xFFFFFF) << 8;
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(in, mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ con = _mm_slli_epi32(con, 8);
+ memcpy(dst, &con, sizeof(int32_t) * 3);
+ *((uint32_t*)(dest + 0)) = (dst[0] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 3)) = (dst[1] & 0xFFFFFF) << 8;
+ *((uint32_t*)(dest + 6)) = (dst[2] & 0xFFFFFF) << 8;
+ }
+ }
+ }
+ _mm_empty();
+ #else /* no SSE */
+ for (uint32_t i = 0; i < samples; ++i, ++data, dest += 3)
+ *((uint32_t*)(dest)) = (safeRound(*data * ((float)INT24_MAX+.5f)) & 0xFFFFFF) << 8;
+ #endif
+
+ return samples * 3;
+}
+
+unsigned int CAEConvert::Float_S32LE(float *data, const unsigned int samples, uint8_t *dest)
+{
+ int32_t *dst = (int32_t*)dest;
+ #ifdef __SSE__
+ const __m128 mul = _mm_set_ps1((float)INT32_MAX);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ ++data;
+ ++dst;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
+ {
+ __m128 in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 4);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ dst[1] = Endian_SwapLE32(dst[1]);
+ dst[2] = Endian_SwapLE32(dst[2]);
+ dst[3] = Endian_SwapLE32(dst[3]);
+ }
+
+ if (samples != even)
+ {
+ const uint32_t odd = samples - even;
+ if (odd == 1)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ }
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(in, mul);
+ __m64 con = _mm_cvtps_pi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 2);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ dst[1] = Endian_SwapLE32(dst[1]);
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(in, mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 3);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ dst[1] = Endian_SwapLE32(dst[1]);
+ dst[2] = Endian_SwapLE32(dst[2]);
+ }
+ }
+ }
+ _mm_empty();
+
+ #elif defined(__ARM_NEON__)
+
+ for (float *end = data + (samples & ~0x3); data < end; data += 4, dst += 4)
+ {
+ float32x4_t val = vmulq_n_f32(vld1q_f32((const float32_t *)data), INT32_MAX);
+ int32x4_t ret = vcvtq_s32_f32(val);
+ #ifdef __BIG_ENDIAN__
+ ret = vrev64q_s32(ret);
+ #endif
+ vst1q_s32(dst, ret);
+ }
+
+ if (samples & 0x2)
+ {
+ float32x2_t val = vmul_n_f32(vld1_f32((const float32_t *)data), INT32_MAX);
+ int32x2_t ret = vcvt_s32_f32(val);
+ #ifdef __BIG_ENDIAN__
+ ret = vrev64_s32(ret);
+ #endif
+ vst1_s32(dst, ret);
+ data += 2;
+ dst += 2;
+ }
+
+ if (samples & 0x1)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ }
+
+ #else
+
+ /* no SIMD */
+ for (uint32_t i = 0; i < samples; ++i, ++data, ++dst)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapLE32(dst[0]);
+ }
+ #endif
+
+ return samples << 2;
+}
+
+unsigned int CAEConvert::Float_S32BE(float *data, const unsigned int samples, uint8_t *dest)
+{
+ int32_t *dst = (int32_t*)dest;
+ #ifdef __SSE__
+ const __m128 mul = _mm_set_ps1((float)INT32_MAX);
+ unsigned int count = samples;
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)dest & 0xF)) && count > 0)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ ++data;
+ ++dst;
+ --count;
+ }
+
+ const uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
+ {
+ __m128 in = _mm_mul_ps(_mm_load_ps(data), mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 4);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ dst[1] = Endian_SwapBE32(dst[1]);
+ dst[2] = Endian_SwapBE32(dst[2]);
+ dst[3] = Endian_SwapBE32(dst[3]);
+ }
+
+ if (samples != even)
+ {
+ const uint32_t odd = samples - even;
+ if (odd == 1)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ }
+ else
+ {
+ __m128 in;
+ if (odd == 2)
+ {
+ in = _mm_setr_ps(data[0], data[1], 0, 0);
+ in = _mm_mul_ps(in, mul);
+ __m64 con = _mm_cvtps_pi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 2);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ dst[1] = Endian_SwapBE32(dst[1]);
+ }
+ else
+ {
+ in = _mm_setr_ps(data[0], data[1], data[2], 0);
+ in = _mm_mul_ps(in, mul);
+ __m128i con = _mm_cvtps_epi32(in);
+ memcpy(dst, &con, sizeof(int32_t) * 3);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ dst[1] = Endian_SwapBE32(dst[1]);
+ dst[2] = Endian_SwapBE32(dst[2]);
+ }
+ }
+ }
+ _mm_empty();
+
+ #elif defined(__ARM_NEON__)
+
+ for (float *end = data + (samples & ~0x3); data < end; data += 4, dst += 4)
+ {
+ float32x4_t val = vmulq_n_f32(vld1q_f32((const float32_t *)data), INT32_MAX);
+ int32x4_t ret = vcvtq_s32_f32(val);
+ #ifndef __BIG_ENDIAN__
+ ret = vrev64q_s32(ret);
+ #endif
+ vst1q_s32(dst, ret);
+ }
+
+ if (samples & 0x2)
+ {
+ float32x2_t val = vmul_n_f32(vld1_f32((const float32_t *)data), INT32_MAX);
+ int32x2_t ret = vcvt_s32_f32(val);
+ #ifndef __BIG_ENDIAN__
+ ret = vrev64_s32(ret);
+ #endif
+ vst1_s32(dst, ret);
+ data += 2;
+ dst += 2;
+ }
+
+ if (samples & 0x1)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ }
+
+ #else
+
+ /* no SIMD */
+ for (uint32_t i = 0; i < samples; ++i, ++data, ++dst)
+ {
+ dst[0] = safeRound(data[0] * (float)INT32_MAX);
+ dst[0] = Endian_SwapBE32(dst[0]);
+ }
+ #endif
+
+ return samples << 2;
+}
+
+unsigned int CAEConvert::Float_DOUBLE(float *data, const unsigned int samples, uint8_t *dest)
+{
+ double *dst = (double*)dest;
+ for (unsigned int i = 0; i < samples; ++i, ++data, ++dst)
+ *dst = *data;
+
+ return samples * sizeof(double);
+}
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEConvert.h b/xbmc/cores/AudioEngine/Utils/AEConvert.h
new file mode 100644
index 0000000000..08d1221f82
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEConvert.h
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include "../AEAudioFormat.h"
+
+/* note: always converts to machine byte endian */
+
+class CAEConvert{
+private:
+ static unsigned int U8_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S8_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S16LE_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S16BE_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S24LE4_Float(uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S24BE4_Float(uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S24LE3_Float(uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S24BE3_Float(uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S32LE_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int S32BE_Float (uint8_t *data, const unsigned int samples, float *dest);
+ static unsigned int DOUBLE_Float(uint8_t *data, const unsigned int samples, float *dest);
+
+ static unsigned int Float_U8 (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S8 (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S16LE (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S16BE (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S24NE4(float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S24NE3(float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S32LE (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_S32BE (float *data, const unsigned int samples, uint8_t *dest);
+ static unsigned int Float_DOUBLE(float *data, const unsigned int samples, uint8_t *dest);
+public:
+ typedef unsigned int (*AEConvertToFn)(uint8_t *data, const unsigned int samples, float *dest);
+ typedef unsigned int (*AEConvertFrFn)(float *data, const unsigned int samples, uint8_t *dest);
+
+ static AEConvertToFn ToFloat(enum AEDataFormat dataFormat);
+ static AEConvertFrFn FrFloat(enum AEDataFormat dataFormat);
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp
new file mode 100644
index 0000000000..fbfed7614e
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <sstream>
+#include "AEDeviceInfo.h"
+
+CAEDeviceInfo::operator std::string()
+{
+ std::stringstream ss;
+ ss << "m_deviceName : " << m_deviceName << '\n';
+ ss << "m_displayName : " << m_displayName << '\n';
+ ss << "m_displayNameExtra: " << m_displayNameExtra << '\n';
+ ss << "m_deviceType : " << DeviceTypeToString(m_deviceType) + '\n';
+ ss << "m_channels : " << (std::string)m_channels << '\n';
+
+ ss << "m_sampleRates : ";
+ for (AESampleRateList::iterator itt = m_sampleRates.begin(); itt != m_sampleRates.end(); ++itt)
+ {
+ if (itt != m_sampleRates.begin())
+ ss << ',';
+ ss << *itt;
+ }
+ ss << '\n';
+
+ ss << "m_dataFormats : ";
+ for (AEDataFormatList::iterator itt = m_dataFormats.begin(); itt != m_dataFormats.end(); ++itt)
+ {
+ if (itt != m_dataFormats.begin())
+ ss << ',';
+ ss << CAEUtil::DataFormatToStr(*itt);
+ }
+ ss << '\n';
+
+ return ss.str();
+}
+
+std::string CAEDeviceInfo::DeviceTypeToString(enum AEDeviceType deviceType)
+{
+ switch (deviceType)
+ {
+ case AE_DEVTYPE_PCM : return "AE_DEVTYPE_PCM" ; break;
+ case AE_DEVTYPE_IEC958: return "AE_DEVTYPE_IEC958"; break;
+ case AE_DEVTYPE_HDMI : return "AE_DEVTYPE_HDMI" ; break;
+ case AE_DEVTYPE_DP : return "AE_DEVTYPE_DP" ; break;
+ }
+ return "INVALID";
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h
new file mode 100644
index 0000000000..2449ec19a9
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string>
+#include <vector>
+#include "AEAudioFormat.h"
+#include "Utils/AEChannelInfo.h"
+#include "Utils/AEUtil.h"
+
+typedef std::vector<unsigned int > AESampleRateList;
+typedef std::vector<enum AEDataFormat> AEDataFormatList;
+
+enum AEDeviceType {
+ AE_DEVTYPE_PCM,
+ AE_DEVTYPE_IEC958,
+ AE_DEVTYPE_HDMI,
+ AE_DEVTYPE_DP
+};
+
+/**
+ * This classt provides the details of what the audio output hardware is capable of
+ */
+class CAEDeviceInfo
+{
+public:
+ std::string m_deviceName; /* the driver device name */
+ std::string m_displayName; /* the friendly display name */
+ std::string m_displayNameExtra; /* additional display name info, ie, monitor name from ELD */
+ enum AEDeviceType m_deviceType; /* the device type, PCM, IEC958 or HDMI */
+ CAEChannelInfo m_channels; /* the channels the device is capable of rendering */
+ AESampleRateList m_sampleRates; /* the samplerates the device is capable of rendering */
+ AEDataFormatList m_dataFormats; /* the dataformats the device is capable of rendering */
+
+ operator std::string();
+ static std::string DeviceTypeToString(enum AEDeviceType deviceType);
+};
+
+typedef std::vector<CAEDeviceInfo> AEDeviceInfoList;
diff --git a/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp b/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp
new file mode 100644
index 0000000000..64210070fc
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEELDParser.h"
+#include "utils/EndianSwap.h"
+#include <string.h>
+
+#include <stdio.h>
+
+#define GRAB_BITS(buf, byte, lowbit, bits) ((buf[byte] >> (lowbit)) & ((1 << (bits)) - 1))
+
+typedef struct
+{
+ uint8_t eld_ver;
+ uint8_t baseline_eid_len;
+ uint8_t cea_edid_ver;
+ uint8_t monitor_name_length;
+ uint8_t sad_count;
+ uint8_t conn_type;
+ bool s_ai;
+ bool hdcp;
+ uint8_t audio_sync_delay;
+ bool rlrc; /* rear left and right of center */
+ bool flrc; /* front left and right of center */
+ bool rc; /* rear center */
+ bool rlr; /* rear left and right */
+ bool fc; /* front center */
+ bool lfe; /* LFE */
+ bool flr; /* front left and right */
+ uint64_t port_id;
+ char mfg_name[4];
+ uint16_t product_code;
+ std::string monitor_name;
+} ELDHeader;
+
+#define ELD_VER_CEA_816D 2
+#define ELD_VER_PARTIAL 31
+
+#define ELD_EDID_VER_NONE 0
+#define ELD_EDID_VER_CEA_861 1
+#define ELD_EDID_VER_CEA_861_A 2
+#define ELD_EDID_VER_CEA_861_BCD 3
+
+#define ELD_CONN_TYPE_HDMI 0
+#define ELD_CONN_TYPE_DP 1
+#define ELD_CONN_TYPE_RESERVED1 2
+#define ELD_CONN_TYPE_RESERVED2 3
+
+#define CEA_861_FORMAT_RESERVED1 0
+#define CEA_861_FORMAT_LPCM 1
+#define CEA_861_FORMAT_AC3 2
+#define CEA_861_FORMAT_MPEG1 3
+#define CEA_861_FORMAT_MP3 4
+#define CEA_861_FORMAT_MPEG2 5
+#define CEA_861_FORMAT_AAC 6
+#define CEA_861_FORMAT_DTS 7
+#define CEA_861_FORMAT_ATRAC 8
+#define CEA_861_FORMAT_SACD 9
+#define CEA_861_FORMAT_EAC3 10
+#define CEA_861_FORMAT_DTSHD 11
+#define CEA_861_FORMAT_MLP 12
+#define CEA_861_FORMAT_DST 13
+#define CEA_861_FORMAT_WMAPRO 14
+#define CEA_861_FORMAT_RESERVED2 15
+
+#define rtrim(s) s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end())
+
+void CAEELDParser::Parse(const uint8_t *data, size_t length, CAEDeviceInfo& info)
+{
+ ELDHeader header;
+ header.eld_ver = (data[0 ] & 0xF8) >> 3;
+ if (header.eld_ver != ELD_VER_CEA_816D && header.eld_ver != ELD_VER_PARTIAL)
+ return;
+
+ header.baseline_eid_len = data[2 ];
+ header.cea_edid_ver = (data[4 ] & 0xE0) >> 5;
+ header.monitor_name_length = data[4 ] & 0x1F;
+ header.sad_count = (data[5 ] & 0xF0) >> 4;
+ header.conn_type = (data[5 ] & 0x0C) >> 2;
+ header.s_ai = (data[5 ] & 0x02) == 0x02;
+ header.hdcp = (data[5 ] & 0x01) == 0x01;
+ header.audio_sync_delay = data[6 ];
+ header.rlrc = (data[7 ] & 0x40) == 0x40;
+ header.flrc = (data[7 ] & 0x20) == 0x20;
+ header.rc = (data[7 ] & 0x10) == 0x10;
+ header.rlr = (data[7 ] & 0x08) == 0x08;
+ header.fc = (data[7 ] & 0x04) == 0x04;
+ header.lfe = (data[7 ] & 0x02) == 0x02;
+ header.flr = (data[7 ] & 0x01) == 0x01;
+ header.port_id = Endian_SwapLE64(*((uint64_t*)(data + 8)));
+ header.mfg_name[0] = 'A' + ((data[16] >> 2) & 0x1F) - 1;
+ header.mfg_name[1] = 'A' + (((data[16] << 3) | (data[17] >> 5)) & 0x1F) - 1;
+ header.mfg_name[2] = 'A' + (data[17] & 0x1F) - 1;
+ header.mfg_name[3] = '\0';
+ header.product_code = Endian_SwapLE16(*((uint16_t*)(data + 18)));
+
+ switch (header.conn_type)
+ {
+ case ELD_CONN_TYPE_HDMI: info.m_deviceType = AE_DEVTYPE_HDMI; break;
+ case ELD_CONN_TYPE_DP : info.m_deviceType = AE_DEVTYPE_DP ; break;
+ }
+
+ info.m_displayNameExtra = header.mfg_name;
+ if (header.monitor_name_length <= 16)
+ {
+ header.monitor_name.assign((const char *)(data + 20), header.monitor_name_length);
+ rtrim(header.monitor_name);
+ if (header.monitor_name.length() > 0)
+ {
+ info.m_displayNameExtra.append(" ");
+ info.m_displayNameExtra.append(header.monitor_name);
+ if (header.conn_type == ELD_CONN_TYPE_HDMI)
+ info.m_displayNameExtra.append(" on HDMI" );
+ else
+ info.m_displayNameExtra.append(" on DisplayPort");
+ }
+ }
+
+ if (header.flr)
+ {
+ if (!info.m_channels.HasChannel(AE_CH_FL))
+ info.m_channels += AE_CH_FL;
+ if (!info.m_channels.HasChannel(AE_CH_FR))
+ info.m_channels += AE_CH_FR;
+ }
+
+ if (header.lfe)
+ if (!info.m_channels.HasChannel(AE_CH_LFE))
+ info.m_channels += AE_CH_LFE;
+
+ if (header.fc)
+ if (!info.m_channels.HasChannel(AE_CH_FC))
+ info.m_channels += AE_CH_FC;
+
+ if (header.rlr)
+ {
+ if (!info.m_channels.HasChannel(AE_CH_BL))
+ info.m_channels += AE_CH_BL;
+ if (!info.m_channels.HasChannel(AE_CH_BR))
+ info.m_channels += AE_CH_BR;
+ }
+
+ if (header.rc)
+ if (!info.m_channels.HasChannel(AE_CH_BC))
+ info.m_channels += AE_CH_BC;
+
+ if (header.flrc)
+ {
+ if (!info.m_channels.HasChannel(AE_CH_FLOC))
+ info.m_channels += AE_CH_FLOC;
+ if (!info.m_channels.HasChannel(AE_CH_FROC))
+ info.m_channels += AE_CH_FROC;
+ }
+
+ if (header.rlrc)
+ {
+ if (!info.m_channels.HasChannel(AE_CH_BLOC))
+ info.m_channels += AE_CH_BLOC;
+ if (!info.m_channels.HasChannel(AE_CH_BROC))
+ info.m_channels += AE_CH_BROC;
+ }
+
+ const uint8_t *sad = data + 20 + header.monitor_name_length;
+ for(uint8_t i = 0; i < header.sad_count; ++i)
+ {
+ uint8_t offset = i * 3;
+ uint8_t formatCode = (sad[offset + 0] >> 3) & 0xF;
+ //uint8_t channelCount = (sad[offset + 0] & 0x7) + 1;
+ //uint8_t sampleRates = sad[offset + 1];
+
+ AEDataFormat fmt = AE_FMT_INVALID;
+ switch (formatCode)
+ {
+ case CEA_861_FORMAT_AAC : fmt = AE_FMT_AAC ; break;
+ case CEA_861_FORMAT_AC3 : fmt = AE_FMT_AC3 ; break;
+ case CEA_861_FORMAT_DTS : fmt = AE_FMT_DTS ; break;
+ case CEA_861_FORMAT_DTSHD: fmt = AE_FMT_DTSHD ; break;
+ case CEA_861_FORMAT_EAC3 : fmt = AE_FMT_EAC3 ; break;
+ case CEA_861_FORMAT_LPCM : fmt = AE_FMT_LPCM ; break;
+ case CEA_861_FORMAT_MLP : fmt = AE_FMT_TRUEHD; break;
+ }
+
+ if (fmt == AE_FMT_INVALID)
+ continue;
+
+ if (std::find(info.m_dataFormats.begin(), info.m_dataFormats.end(), fmt) == info.m_dataFormats.end())
+ info.m_dataFormats.push_back(fmt);
+ }
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AEELDParser.h b/xbmc/cores/AudioEngine/Utils/AEELDParser.h
new file mode 100644
index 0000000000..8196e0049b
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEELDParser.h
@@ -0,0 +1,31 @@
+#pragma once
+/*
+ * Copyright (C) 2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include <cstring>
+#include "AEDeviceInfo.h"
+
+class CAEELDParser {
+public:
+ static void Parse(const uint8_t *data, size_t length, CAEDeviceInfo& info);
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp
new file mode 100644
index 0000000000..437d8443d9
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/* DTS spec shows it suppors both BE and LE, we should not need to convert */
+
+#include <cassert>
+#include "system.h"
+#include "AEPackIEC61937.h"
+
+#define IEC61937_PREAMBLE1 0xF872
+#define IEC61937_PREAMBLE2 0x4E1F
+
+inline void SwapEndian(uint16_t *dst, uint16_t *src, unsigned int size)
+{
+ for (unsigned int i = 0; i < size; ++i, ++dst, ++src)
+ *dst = ((*src & 0xFF00) >> 8) | ((*src & 0x00FF) << 8);
+}
+
+int CAEPackIEC61937::PackAC3(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ assert(size <= OUT_FRAMESTOBYTES(AC3_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_length = size << 3;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+
+ int bitstream_mode = data[5] & 0x7;
+ packet->m_type = IEC61937_TYPE_AC3 | (bitstream_mode << 8);
+
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(AC3_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(AC3_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackEAC3(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ assert(size <= OUT_FRAMESTOBYTES(EAC3_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_EAC3;
+ packet->m_length = size;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(EAC3_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(EAC3_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackDTS_512(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ assert(size <= OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_DTS1;
+ packet->m_length = size << 3;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(DTS1_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ assert(size <= OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_DTS2;
+ packet->m_length = size << 3;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(DTS2_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ assert(size <= OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_DTS3;
+ packet->m_length = size << 3;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackTrueHD(uint8_t *data, unsigned int size, uint8_t *dest)
+{
+ if (size == 0)
+ return OUT_FRAMESTOBYTES(TRUEHD_FRAME_SIZE);
+
+ assert(size <= OUT_FRAMESTOBYTES(TRUEHD_FRAME_SIZE));
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_TRUEHD;
+ packet->m_length = size;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ memset(packet->m_data + size, 0, OUT_FRAMESTOBYTES(TRUEHD_FRAME_SIZE) - IEC61937_DATA_OFFSET - size);
+ return OUT_FRAMESTOBYTES(TRUEHD_FRAME_SIZE);
+}
+
+int CAEPackIEC61937::PackDTSHD(uint8_t *data, unsigned int size, uint8_t *dest, unsigned int period)
+{
+ unsigned int subtype;
+ switch (period)
+ {
+ case 512: subtype = 0; break;
+ case 1024: subtype = 1; break;
+ case 2048: subtype = 2; break;
+ case 4096: subtype = 3; break;
+ case 8192: subtype = 4; break;
+ case 16384: subtype = 5; break;
+
+ default:
+ return 0;
+ }
+
+ struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
+ packet->m_preamble1 = IEC61937_PREAMBLE1;
+ packet->m_preamble2 = IEC61937_PREAMBLE2;
+ packet->m_type = IEC61937_TYPE_DTSHD | (subtype << 8);
+
+ /* Align so that (length_code & 0xf) == 0x8. This is reportedly needed
+ * with some receivers, but the exact requirement is unconfirmed. */
+ packet->m_length = ((size + 0x17) &~ 0x0f) - 0x08;
+
+ if (data == NULL)
+ data = packet->m_data;
+#ifdef __BIG_ENDIAN__
+ else
+ memcpy(packet->m_data, data, size);
+#else
+ size += size & 0x1;
+ SwapEndian((uint16_t*)packet->m_data, (uint16_t*)data, size >> 1);
+#endif
+
+ unsigned int burstsize = period << 2;
+ memset(packet->m_data + size, 0, burstsize - IEC61937_DATA_OFFSET - size);
+ return burstsize;
+}
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h
new file mode 100644
index 0000000000..a59bbfa5f4
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h
@@ -0,0 +1,84 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include <list>
+
+#ifdef __GNUC__
+ #define S_PACK __attribute__((__packed__))
+ #define E_PACK
+#else
+ #define S_PACK __pragma(pack(push, 1))
+ #define E_PACK __pragma(pack(pop))
+#endif
+
+#define MAX_IEC61937_PACKET 61440
+#define IEC61937_DATA_OFFSET 8
+
+#define DTS1_FRAME_SIZE 512
+#define DTS2_FRAME_SIZE 1024
+#define DTS3_FRAME_SIZE 2048
+#define AC3_FRAME_SIZE 1536
+#define EAC3_FRAME_SIZE 6144
+#define TRUEHD_FRAME_SIZE 15360
+
+#define OUT_SAMPLESIZE 16
+#define OUT_CHANNELS 2
+#define OUT_FRAMESTOBYTES(a) ((a) * OUT_CHANNELS * (OUT_SAMPLESIZE>>3))
+
+class CAEPackIEC61937
+{
+public:
+ typedef int (*PackFunc)(uint8_t *data, unsigned int size, uint8_t *dest);
+
+ static int PackAC3 (uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackEAC3 (uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackDTS_512 (uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackTrueHD (uint8_t *data, unsigned int size, uint8_t *dest);
+ static int PackDTSHD (uint8_t *data, unsigned int size, uint8_t *dest, unsigned int period);
+private:
+ enum IEC61937DataType
+ {
+ IEC61937_TYPE_NULL = 0x00,
+ IEC61937_TYPE_AC3 = 0x01,
+ IEC61937_TYPE_DTS1 = 0x0B, /* 512 samples */
+ IEC61937_TYPE_DTS2 = 0x0C, /* 1024 samples */
+ IEC61937_TYPE_DTS3 = 0x0D, /* 2048 samples */
+ IEC61937_TYPE_DTSHD = 0x11,
+ IEC61937_TYPE_EAC3 = 0x15,
+ IEC61937_TYPE_TRUEHD = 0x16
+ };
+
+ S_PACK
+ struct IEC61937Packet
+ {
+ uint16_t m_preamble1;
+ uint16_t m_preamble2;
+ uint16_t m_type;
+ uint16_t m_length;
+ uint8_t m_data[MAX_IEC61937_PACKET - IEC61937_DATA_OFFSET];
+ };
+ E_PACK
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AERemap.cpp b/xbmc/cores/AudioEngine/Utils/AERemap.cpp
new file mode 100644
index 0000000000..a13675c4b3
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AERemap.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include <math.h>
+#include <sstream>
+
+#include "AERemap.h"
+#include "AEFactory.h"
+#include "AEUtil.h"
+#include "utils/log.h"
+#include "settings/GUISettings.h"
+
+using namespace std;
+
+CAERemap::CAERemap()
+{
+}
+
+CAERemap::~CAERemap()
+{
+}
+
+bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize/* = false */, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
+{
+ if (!input.Count() || !output.Count())
+ return false;
+
+ /* build the downmix matrix */
+ memset(m_mixInfo, 0, sizeof(m_mixInfo));
+ m_output = output;
+
+ /* figure which channels we have */
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ m_mixInfo[output[o]].in_dst = true;
+
+ /* flag invalid channels for forced downmix */
+ if (stdChLayout != AE_CH_LAYOUT_INVALID)
+ {
+ CAEChannelInfo layout = stdChLayout;
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ if (!layout.HasChannel(output[o]))
+ m_mixInfo[output[o]].in_dst = false;
+ }
+
+ m_outChannels = output.Count();
+
+ /* lookup the channels that exist in the output */
+ for (unsigned int i = 0; i < input.Count(); ++i)
+ {
+ AEMixInfo *info = &m_mixInfo[input[i]];
+ AEMixLevel *lvl = &info->srcIndex[info->srcCount++];
+ info->in_src = true;
+ lvl->index = i;
+ lvl->level = 1.0f;
+ if (!info->in_dst)
+ {
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ {
+ if (input[i] == output[o])
+ {
+ info->outIndex = o;
+ break;
+ }
+ }
+ }
+
+ m_inChannels = i;
+ }
+ ++m_inChannels;
+
+ /* the final stage does not need any down/upmix */
+ if (finalStage)
+ return true;
+
+ /* downmix from the specified channel to the specified list of channels */
+ #define RM(from, ...) \
+ static AEChannel downmix_##from[] = {__VA_ARGS__, AE_CH_NULL}; \
+ ResolveMix(from, CAEChannelInfo(downmix_##from));
+
+ /*
+ the order of this is important as we can not mix channels
+ into ones that have already been resolved... eg
+
+ TBR -> BR
+ TBC -> TBL & TBR
+
+ TBR will get resolved to BR, and then TBC will get
+ resolved to TBL, but since TBR has been resolved it will
+ never make it to the output. The order has to be reversed
+ as the TBC center depends on the TBR downmix... eg
+
+ TBC -> TBL & TBR
+ TBR -> BR
+
+ if for any reason out of order mapping becomes required
+ looping this list should resolve the channels.
+ */
+ RM(AE_CH_TBC , AE_CH_TBL, AE_CH_TBR);
+ RM(AE_CH_TBR , AE_CH_BR);
+ RM(AE_CH_TBL , AE_CH_BL);
+ RM(AE_CH_TC , AE_CH_TFL, AE_CH_TFR);
+ RM(AE_CH_TFC , AE_CH_TFL, AE_CH_TFR);
+ RM(AE_CH_TFR , AE_CH_FR);
+ RM(AE_CH_TFL , AE_CH_FL);
+ RM(AE_CH_SR , AE_CH_BR, AE_CH_FR);
+ RM(AE_CH_SL , AE_CH_BL, AE_CH_FL);
+ RM(AE_CH_BC , AE_CH_BL, AE_CH_BR);
+ RM(AE_CH_FROC, AE_CH_FR, AE_CH_FC);
+ RM(AE_CH_FLOC, AE_CH_FL, AE_CH_FC);
+ RM(AE_CH_BL , AE_CH_FL);
+ RM(AE_CH_BR , AE_CH_FR);
+ RM(AE_CH_LFE , AE_CH_FL, AE_CH_FR);
+ RM(AE_CH_FL , AE_CH_FC);
+ RM(AE_CH_FR , AE_CH_FC);
+ RM(AE_CH_BROC, AE_CH_BR, AE_CH_BC);
+ RM(AE_CH_BLOC, AE_CH_BL, AE_CH_BC);
+
+ /* since everything eventually mixes down to FC we need special handling for it */
+ if (m_mixInfo[AE_CH_FC].in_src)
+ {
+ /* if there is no output FC channel, try to mix it the best possible way */
+ if (!m_mixInfo[AE_CH_FC].in_dst)
+ {
+ /* if we have TFC & FL & FR */
+ if (m_mixInfo[AE_CH_TFC].in_dst && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_TFC, AE_CH_FL, AE_CH_FR);
+ }
+ /* if we have TFC */
+ else if (m_mixInfo[AE_CH_TFC].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_TFC);
+ }
+ /* if we have FLOC & FROC */
+ else if (m_mixInfo[AE_CH_FLOC].in_dst && m_mixInfo[AE_CH_FROC].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_FLOC, AE_CH_FROC);
+ }
+ /* if we have TC & FL & FR */
+ else if (m_mixInfo[AE_CH_TC].in_dst && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_TC, AE_CH_FL, AE_CH_FR);
+ }
+ /* if we have FL & FR */
+ else if (m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
+ }
+ /* if we have TFL & TFR */
+ else if (m_mixInfo[AE_CH_TFL].in_dst && m_mixInfo[AE_CH_TFR].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_TFL, AE_CH_TFR);
+ }
+ /* we dont have enough speakers to emulate FC */
+ else
+ return false;
+ }
+ else
+ {
+ /* if there is only one channel in the source and it is the FC and we have FL & FR, upmix to dual mono */
+ if (m_inChannels == 1 && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
+ {
+ RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
+ }
+ }
+ }
+
+ #undef RM
+
+ if (g_guiSettings.GetBool("audiooutput.stereoupmix"))
+ BuildUpmixMatrix(input, output);
+
+ /* normalize the values */
+ bool normalize;
+ if (forceNormalize)
+ normalize = true;
+ else
+ {
+ normalize = !g_guiSettings.GetBool("audiooutput.dontnormalizelevels");
+ CLog::Log(LOGDEBUG, "AERemap: Downmix normalization is %s", (normalize ? "enabled" : "disabled"));
+ }
+
+ if (normalize)
+ {
+ float max = 0;
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ {
+ AEMixInfo *info = &m_mixInfo[output[o]];
+ float sum = 0;
+ for (int i = 0; i < info->srcCount; ++i)
+ sum += info->srcIndex[i].level;
+
+ if (sum > max)
+ max = sum;
+ }
+
+ float scale = 1.0f / max;
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ {
+ AEMixInfo *info = &m_mixInfo[output[o]];
+ for (int i = 0; i < info->srcCount; ++i)
+ info->srcIndex[i].level *= scale;
+ }
+ }
+
+#if 1
+ /* dump the matrix */
+ CLog::Log(LOGINFO, "==[Downmix Matrix]==");
+ for (unsigned int o = 0; o < output.Count(); ++o)
+ {
+ AEMixInfo *info = &m_mixInfo[output[o]];
+ if (info->srcCount == 0)
+ continue;
+
+ std::stringstream s;
+ s << CAEChannelInfo::GetChName(output[o]) << " =";
+ for (int i = 0; i < info->srcCount; ++i)
+ s << " " << CAEChannelInfo::GetChName(input[info->srcIndex[i].index]) << "(" << info->srcIndex[i].level << ")";
+
+ CLog::Log(LOGINFO, "%s", s.str().c_str());
+ }
+ CLog::Log(LOGINFO, "====================\n");
+#endif
+
+ return true;
+}
+
+void CAERemap::ResolveMix(const AEChannel from, CAEChannelInfo to)
+{
+ AEMixInfo *fromInfo = &m_mixInfo[from];
+ if (fromInfo->in_dst || !fromInfo->in_src)
+ return;
+
+ for (unsigned int i = 0; i < to.Count(); ++i)
+ {
+ AEMixInfo *toInfo = &m_mixInfo[to[i]];
+ toInfo->in_src = true;
+ for (int o = 0; o < fromInfo->srcCount; ++o)
+ {
+ AEMixLevel *fromLvl = &fromInfo->srcIndex[o];
+ AEMixLevel *toLvl = NULL;
+
+ /* if its already in the output, then we need to combine the levels */
+ for (int l = 0; l < toInfo->srcCount; ++l)
+ if (toInfo->srcIndex[l].index == fromLvl->index)
+ {
+ toLvl = &toInfo->srcIndex[l];
+ toLvl->level = (fromLvl->level + toLvl->level) / sqrt((float)to.Count() + 1.0f);
+ break;
+ }
+
+ if (toLvl)
+ continue;
+
+ toLvl = &toInfo->srcIndex[toInfo->srcCount++];
+ toLvl->index = fromLvl->index;
+ toLvl->level = fromLvl->level / sqrt((float)to.Count());
+ }
+ }
+
+ fromInfo->srcCount = 0;
+ fromInfo->in_src = false;
+}
+
+/* This method has unrolled loop for higher performance */
+void CAERemap::Remap(float * const in, float * const out, const unsigned int frames) const
+{
+ const unsigned int frameBlocks = frames & ~0x3;
+
+ for (int o = 0; o < m_outChannels; ++o)
+ {
+ const AEMixInfo *info = &m_mixInfo[m_output[o]];
+ if (!info->in_dst)
+ {
+ unsigned int f = 0;
+ for(; f < frameBlocks; f += 4)
+ {
+ out[((f + 0) * m_outChannels) + o] = 0.0f;
+ out[((f + 1) * m_outChannels) + o] = 0.0f;
+ out[((f + 2) * m_outChannels) + o] = 0.0f;
+ out[((f + 3) * m_outChannels) + o] = 0.0f;
+ }
+
+ switch (frames & 0x3)
+ {
+ case 3: out[(f * m_outChannels) + o] = 0.0f; ++f;
+ case 2: out[(f * m_outChannels) + o] = 0.0f; ++f;
+ case 1: out[(f * m_outChannels) + o] = 0.0f;
+ }
+ continue;
+ }
+
+ /* if there is only 1 source, just copy it so we dont break DPL */
+ if (info->srcCount == 1)
+ {
+ unsigned int f = 0;
+ /* the compiler has a better chance of optimizing this if it is done in parallel */
+ for (; f < frameBlocks; f += 4)
+ {
+ out[((f + 0) * m_outChannels) + o] = in[((f + 0) * m_inChannels) + info->srcIndex[0].index];
+ out[((f + 1) * m_outChannels) + o] = in[((f + 1) * m_inChannels) + info->srcIndex[0].index];
+ out[((f + 2) * m_outChannels) + o] = in[((f + 2) * m_inChannels) + info->srcIndex[0].index];
+ out[((f + 3) * m_outChannels) + o] = in[((f + 3) * m_inChannels) + info->srcIndex[0].index];
+ }
+
+ switch (frames & 0x3)
+ {
+ case 3: out[(f * m_outChannels) + o] = in[(f * m_inChannels) + info->srcIndex[0].index]; ++f;
+ case 2: out[(f * m_outChannels) + o] = in[(f * m_inChannels) + info->srcIndex[0].index]; ++f;
+ case 1: out[(f * m_outChannels) + o] = in[(f * m_inChannels) + info->srcIndex[0].index];
+ }
+ }
+ else
+ {
+ for (unsigned int f = 0; f < frames; ++f)
+ {
+ float *outOffset = out + (f * m_outChannels) + o;
+ float *inOffset = in + (f * m_inChannels);
+ *outOffset = 0.0f;
+
+ int blocks = info->srcCount & ~0x3;
+
+ /* the compiler has a better chance of optimizing this if it is done in parallel */
+ int i = 0;
+ for (; i < blocks; i += 4)
+ {
+ *outOffset += inOffset[info->srcIndex[i + 0].index] * info->srcIndex[i + 0].level;
+ *outOffset += inOffset[info->srcIndex[i + 1].index] * info->srcIndex[i + 1].level;
+ *outOffset += inOffset[info->srcIndex[i + 2].index] * info->srcIndex[i + 2].level;
+ *outOffset += inOffset[info->srcIndex[i + 3].index] * info->srcIndex[i + 3].level;
+ }
+
+ /* unrolled loop for higher performance */
+ switch (info->srcCount & 0x3)
+ {
+ case 3: *outOffset += inOffset[info->srcIndex[i].index] * info->srcIndex[i].level; ++i;
+ case 2: *outOffset += inOffset[info->srcIndex[i].index] * info->srcIndex[i].level; ++i;
+ case 1: *outOffset += inOffset[info->srcIndex[i].index] * info->srcIndex[i].level;
+ }
+ }
+ }
+ }
+}
+
+inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output)
+{
+ #define UM(from, to) \
+ if (!m_mixInfo[to].in_src && m_mixInfo[to].in_dst) \
+ { \
+ AEMixInfo *toInfo = &m_mixInfo[to ]; \
+ AEMixInfo *fromInfo = &m_mixInfo[from]; \
+ toInfo->srcIndex[toInfo->srcCount].level = 1.0f; \
+ toInfo->srcIndex[toInfo->srcCount].index = fromInfo->srcIndex[0].index; \
+ toInfo ->srcCount++; \
+ fromInfo->cpyCount++; \
+ }
+
+ if (m_mixInfo[AE_CH_FL].in_src)
+ {
+ UM(AE_CH_FL, AE_CH_BL );
+ UM(AE_CH_FL, AE_CH_SL );
+ UM(AE_CH_FL, AE_CH_FC );
+ UM(AE_CH_FL, AE_CH_LFE);
+ }
+
+ if (m_mixInfo[AE_CH_FR].in_src)
+ {
+ UM(AE_CH_FR, AE_CH_BR );
+ UM(AE_CH_FR, AE_CH_SR );
+ UM(AE_CH_FR, AE_CH_FC );
+ UM(AE_CH_FR, AE_CH_LFE);
+ }
+
+ /* fix the levels of anything we added */
+ for (unsigned int i = 0; i < output.Count(); ++i)
+ {
+ AEMixInfo *outputInfo = &m_mixInfo[output[i]];
+ if (!outputInfo->in_src && outputInfo->srcCount > 0)
+ for (int src = 0; src < outputInfo->srcCount; ++src)
+ {
+ AEChannel srcChannel = input[outputInfo->srcIndex[src].index];
+ AEMixInfo *inputInfo = &m_mixInfo[srcChannel];
+ outputInfo->srcIndex[src].level /= sqrt((float)(inputInfo->cpyCount + 1));
+ }
+ }
+
+ /* fix the source levels also */
+ for (unsigned int i = 0; i < input.Count(); ++i)
+ {
+ AEMixInfo *inputInfo = &m_mixInfo[input[i]];
+ if (!inputInfo->in_dst || inputInfo->cpyCount == 0)
+ continue;
+ inputInfo->srcIndex[0].level /= sqrt((float)(inputInfo->cpyCount + 1));
+ }
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AERemap.h b/xbmc/cores/AudioEngine/Utils/AERemap.h
new file mode 100644
index 0000000000..408a26aafa
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AERemap.h
@@ -0,0 +1,56 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEAudioFormat.h"
+
+class CAERemap {
+public:
+ CAERemap();
+ ~CAERemap();
+
+ bool Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize = false, enum AEStdChLayout stdChLayout = AE_CH_LAYOUT_INVALID);
+ void Remap(float * const in, float * const out, const unsigned int frames) const;
+
+private:
+ typedef struct {
+ int index;
+ float level;
+ } AEMixLevel;
+
+ typedef struct {
+ bool in_src;
+ bool in_dst;
+ int outIndex;
+ int srcCount;
+ AEMixLevel srcIndex[AE_CH_MAX];
+ int cpyCount; /* the number of times the channel has been cloned */
+ } AEMixInfo;
+
+ AEMixInfo m_mixInfo[AE_CH_MAX+1];
+ CAEChannelInfo m_output;
+ int m_inChannels;
+ int m_outChannels;
+
+ void ResolveMix(const AEChannel from, CAEChannelInfo to);
+ void BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output);
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp
new file mode 100644
index 0000000000..8a7e7f6c7b
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp
@@ -0,0 +1,717 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEStreamInfo.h"
+
+#define IEC61937_PREAMBLE1 0xF872
+#define IEC61937_PREAMBLE2 0x4E1F
+#define DTS_PREAMBLE_14BE 0x1FFFE800
+#define DTS_PREAMBLE_14LE 0xFF1F00E8
+#define DTS_PREAMBLE_16BE 0x7FFE8001
+#define DTS_PREAMBLE_16LE 0xFE7F0180
+#define DTS_PREAMBLE_HD 0x64582025
+#define DTS_SFREQ_COUNT 16
+#define MAX_EAC3_BLOCKS 6
+
+static enum AEChannel OutputMaps[2][9] = {
+ {AE_CH_RAW, AE_CH_RAW, AE_CH_NULL},
+ {AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_NULL}
+};
+
+static const uint16_t AC3Bitrates [] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640};
+static const uint16_t AC3FSCod [] = {48000, 44100, 32000, 0};
+static const uint8_t AC3BlkCod [] = {1, 2, 3, 6};
+static const uint8_t AC3Channels [] = {2, 1, 2, 3, 3, 4, 4, 5};
+static const uint8_t DTSChannels [] = {1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8};
+static const uint8_t THDChanMap [] = {2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1};
+
+static const uint32_t DTSSampleRates[DTS_SFREQ_COUNT] =
+{
+ 0 ,
+ 8000 ,
+ 16000 ,
+ 32000 ,
+ 64000 ,
+ 128000,
+ 11025 ,
+ 22050 ,
+ 44100 ,
+ 88200 ,
+ 176400,
+ 12000 ,
+ 24000 ,
+ 48000 ,
+ 96000 ,
+ 192000
+};
+
+CAEStreamInfo::CAEStreamInfo() :
+ m_bufferSize(0),
+ m_skipBytes (0),
+ m_coreOnly (false),
+ m_needBytes (0),
+ m_syncFunc (&CAEStreamInfo::DetectType),
+ m_hasSync (false),
+ m_sampleRate(0),
+ m_dtsBlocks (0),
+ m_dataType (STREAM_TYPE_NULL),
+ m_packFunc (NULL)
+{
+ m_dllAvUtil.Load();
+ m_dllAvUtil.av_crc_init(m_crcTrueHD, 0, 16, 0x2D, sizeof(m_crcTrueHD));
+}
+
+CAEStreamInfo::~CAEStreamInfo()
+{
+ m_dllAvUtil.Unload();
+}
+
+int CAEStreamInfo::AddData(uint8_t *data, unsigned int size, uint8_t **buffer/* = NULL */, unsigned int *bufferSize/* = 0 */)
+{
+ if (size == 0)
+ {
+ if (bufferSize)
+ *bufferSize = 0;
+ return 0;
+ }
+
+ unsigned int consumed = 0;
+ if (m_skipBytes)
+ {
+ unsigned int canSkip = std::min(size, m_skipBytes);
+ unsigned int room = sizeof(m_buffer) - m_bufferSize;
+ unsigned int copy = std::min(room, canSkip);
+
+ memcpy(m_buffer + m_bufferSize, data, copy);
+ m_bufferSize += copy;
+ m_skipBytes -= copy;
+
+ if (m_skipBytes)
+ {
+ if (bufferSize)
+ *bufferSize = 0;
+ return copy;
+ }
+
+ GetPacket(buffer, bufferSize);
+ return copy;
+ }
+ else
+ {
+ unsigned int offset = 0;
+ unsigned int room = sizeof(m_buffer) - m_bufferSize;
+ while(1)
+ {
+ if (!size)
+ {
+ if (bufferSize)
+ *bufferSize = 0;
+ return consumed;
+ }
+
+ unsigned int copy = std::min(room, size);
+ memcpy(m_buffer + m_bufferSize, data, copy);
+ m_bufferSize += copy;
+ consumed += copy;
+ data += copy;
+ size -= copy;
+ room -= copy;
+
+ if (m_needBytes > m_bufferSize)
+ continue;
+
+ m_needBytes = 0;
+ offset = (this->*m_syncFunc)(m_buffer, m_bufferSize);
+
+ if (m_hasSync || m_needBytes)
+ break;
+ else
+ {
+ /* lost sync */
+ m_syncFunc = &CAEStreamInfo::DetectType;
+ m_dataType = STREAM_TYPE_NULL;
+ m_packFunc = NULL;
+ m_repeat = 1;
+
+ /* if the buffer is full, or the offset < the buffer size */
+ if (m_bufferSize == sizeof(m_buffer) || offset < m_bufferSize)
+ {
+ m_bufferSize -= offset;
+ room += offset;
+ memmove(m_buffer, m_buffer + offset, m_bufferSize);
+ }
+ }
+ }
+
+ /* if we got here, we acquired sync on the buffer */
+
+ /* align the buffer */
+ if (offset)
+ {
+ m_bufferSize -= offset;
+ memmove(m_buffer, m_buffer + offset, m_bufferSize);
+ }
+
+ /* bytes to skip until the next packet */
+ m_skipBytes = std::max(0, (int)m_fsize - (int)m_bufferSize);
+ if (m_skipBytes)
+ {
+ if (bufferSize)
+ *bufferSize = 0;
+ return consumed;
+ }
+
+ if (!m_needBytes)
+ GetPacket(buffer, bufferSize);
+
+ return consumed;
+ }
+}
+
+void CAEStreamInfo::GetPacket(uint8_t **buffer, unsigned int *bufferSize)
+{
+ /* if the caller wants the packet */
+ if (buffer)
+ {
+ /* if it is dtsHD and we only want the core, just fetch that */
+ unsigned int size = m_fsize;
+ if (m_dataType == STREAM_TYPE_DTSHD_CORE)
+ size = m_coreSize;
+
+ /* make sure the buffer is allocated and big enough */
+ if (!*buffer || !bufferSize || *bufferSize < size)
+ {
+ delete[] *buffer;
+ *buffer = new uint8_t[size];
+ }
+
+ /* copy the data into the buffer and update the size */
+ memcpy(*buffer, m_buffer, size);
+ if (bufferSize)
+ *bufferSize = size;
+ }
+
+ /* remove the parsed data from the buffer */
+ m_bufferSize -= m_fsize;
+ memmove(m_buffer, m_buffer + m_fsize, m_bufferSize);
+ m_fsize = 0;
+ m_coreSize = 0;
+}
+
+/* SYNC FUNCTIONS */
+
+/*
+ This function looks for sync words across the types in paralell, and only does an exhaustive
+ test if it finds a syncword. Once sync has been established, the relevent sync function sets
+ m_syncFunc to itself. This function will only be called again if total sync is lost, which
+ allows is to switch stream types on the fly much like a real reicever does.
+*/
+unsigned int CAEStreamInfo::DetectType(uint8_t *data, unsigned int size)
+{
+ unsigned int skipped = 0;
+ unsigned int possible = 0;
+
+ while (size > 8)
+ {
+ /* if it could be DTS */
+ unsigned int header = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ if (header == DTS_PREAMBLE_14LE ||
+ header == DTS_PREAMBLE_14BE ||
+ header == DTS_PREAMBLE_16LE ||
+ header == DTS_PREAMBLE_16BE)
+ {
+ unsigned int skip = SyncDTS(data, size);
+ if (m_hasSync || m_needBytes)
+ return skipped + skip;
+ else
+ possible = skipped;
+ }
+
+ /* if it could be AC3 */
+ if (data[0] == 0x0b && data[1] == 0x77)
+ {
+ unsigned int skip = SyncAC3(data, size);
+ if (m_hasSync)
+ return skipped + skip;
+ else
+ possible = skipped;
+ }
+
+ /* if it could be TrueHD */
+ if (data[4] == 0xf8 && data[5] == 0x72 && data[6] == 0x6f && data[7] == 0xba)
+ {
+ unsigned int skip = SyncTrueHD(data, size);
+ if (m_hasSync)
+ return skipped + skip;
+ else
+ possible = skipped;
+ }
+
+ /* move along one byte */
+ --size;
+ ++skipped;
+ ++data;
+ }
+
+ return possible ? possible : skipped;
+}
+
+unsigned int CAEStreamInfo::SyncAC3(uint8_t *data, unsigned int size)
+{
+ unsigned int skip = 0;
+
+ for (; size - skip > 7; ++skip, ++data)
+ {
+ /* search for an ac3 sync word */
+ if (data[0] != 0x0b || data[1] != 0x77)
+ continue;
+
+ uint8_t bsid = data[5] >> 3;
+ uint8_t acmod = data[6] >> 5;
+ uint8_t lfeon;
+
+ int8_t pos = 4;
+ if ((acmod & 0x1) && (acmod != 0x1))
+ pos -= 2;
+ if (acmod & 0x4 )
+ pos -= 2;
+ if (acmod == 0x2)
+ pos -= 2;
+ if (pos < 0)
+ lfeon = (data[7] & 0x64) ? 1 : 0;
+ else
+ lfeon = ((data[6] >> pos) & 0x1) ? 1 : 0;
+
+ if (bsid > 0x11 || acmod > 8)
+ continue;
+
+ if (bsid <= 10)
+ {
+ /* Normal AC-3 */
+
+ uint8_t fscod = data[4] >> 6;
+ uint8_t frmsizecod = data[4] & 0x3F;
+ if (fscod == 3 || frmsizecod > 37)
+ continue;
+
+ /* get the details we need to check crc1 and framesize */
+ unsigned int bitRate = AC3Bitrates[frmsizecod >> 1];
+ unsigned int framesize = 0;
+ switch (fscod)
+ {
+ case 0: framesize = bitRate * 2; break;
+ case 1: framesize = (320 * bitRate / 147 + (frmsizecod & 1 ? 1 : 0)); break;
+ case 2: framesize = bitRate * 4; break;
+ }
+
+ m_fsize = framesize << 1;
+ m_sampleRate = AC3FSCod[fscod];
+
+ /* dont do extensive testing if we have not lost sync */
+ if (m_dataType == STREAM_TYPE_AC3 && skip == 0)
+ return 0;
+
+ unsigned int crc_size;
+ /* if we have enough data, validate the entire packet, else try to validate crc2 (5/8 of the packet) */
+ if (framesize <= size - skip)
+ crc_size = framesize - 1;
+ else
+ crc_size = (framesize >> 1) + (framesize >> 3) - 1;
+
+ if (crc_size <= size - skip)
+ if (m_dllAvUtil.av_crc(m_dllAvUtil.av_crc_get_table(AV_CRC_16_ANSI), 0, &data[2], crc_size * 2))
+ continue;
+
+ /* if we get here, we can sync */
+ m_hasSync = true;
+ m_outputRate = m_sampleRate;
+ m_outputChannels = 2;
+ m_channelMap = CAEChannelInfo(OutputMaps[0]);
+ m_channels = AC3Channels[acmod] + lfeon;
+ m_syncFunc = &CAEStreamInfo::SyncAC3;
+ m_dataType = STREAM_TYPE_AC3;
+ m_packFunc = &CAEPackIEC61937::PackAC3;
+ m_repeat = 1;
+
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncAC3 - AC3 stream detected (%d channels, %dHz)", m_channels, m_sampleRate);
+ return skip;
+ }
+ else
+ {
+ /* Enhanced AC-3 */
+ uint8_t strmtyp = data[2] >> 6;
+ if (strmtyp == 3)
+ continue;
+
+ unsigned int framesize = (((data[2] & 0x7) << 8) | data[3]) + 1;
+ uint8_t fscod = (data[4] >> 6) & 0x3;
+ uint8_t cod = (data[4] >> 4) & 0x3;
+ uint8_t blocks;
+
+ if (fscod == 0x3)
+ {
+ if (cod == 0x3)
+ continue;
+
+ blocks = 6;
+ m_sampleRate = AC3FSCod[cod] >> 1;
+ }
+ else
+ {
+ blocks = AC3BlkCod[cod ];
+ m_sampleRate = AC3FSCod [fscod];
+ }
+
+ m_fsize = framesize << 1;
+ m_repeat = MAX_EAC3_BLOCKS / blocks;
+
+ if (m_sampleRate == 48000 || m_sampleRate == 96000 || m_sampleRate == 192000)
+ m_outputRate = 192000;
+ else
+ m_outputRate = 176400;
+
+ if (m_dataType == STREAM_TYPE_EAC3 && m_hasSync && skip == 0)
+ return 0;
+
+ /* if we get here, we can sync */
+ m_hasSync = true;
+ m_outputChannels = 8;
+ m_channelMap = CAEChannelInfo(OutputMaps[1]);
+ m_channels = 8; /* FIXME: this should be read out of the stream */
+ m_syncFunc = &CAEStreamInfo::SyncAC3;
+ m_dataType = STREAM_TYPE_EAC3;
+ m_packFunc = &CAEPackIEC61937::PackEAC3;
+
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncAC3 - E-AC3 stream detected (%d channels, %dHz)", m_channels, m_sampleRate);
+ return skip;
+ }
+ }
+
+ /* if we get here, the entire packet is invalid and we have lost sync */
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncAC3 - AC3 sync lost");
+ m_hasSync = false;
+ return skip;
+}
+
+unsigned int CAEStreamInfo::SyncDTS(uint8_t *data, unsigned int size)
+{
+ if (size < 13)
+ {
+ if (m_needBytes < 13)
+ m_needBytes = 14;
+ return 0;
+ }
+
+ unsigned int skip = 0;
+ for (; size - skip > 13; ++skip, ++data)
+ {
+ unsigned int header = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ unsigned int hd_sync = 0;
+ bool match = true;
+ unsigned int dtsBlocks;
+ unsigned int amode;
+ unsigned int sfreq;
+ unsigned int lfe;
+ int bits;
+
+ switch (header)
+ {
+ /* 14bit BE */
+ case DTS_PREAMBLE_14BE:
+ if (data[4] != 0x07 || (data[5] & 0xf0) != 0xf0)
+ {
+ match = false;
+ break;
+ }
+ dtsBlocks = (((data[5] & 0x7) << 4) | ((data[6] & 0x3C) >> 2)) + 1;
+ m_fsize = ((((data[6] & 0x3 << 8) | data[7]) << 4) | ((data[8] & 0x3C) >> 2)) + 1;
+ amode = ((data[8] & 0x3) << 4) | ((data[9] & 0xF0) >> 4);
+ sfreq = data[9] & 0xF;
+ lfe = (data[12] & 0x18) >> 3;
+ m_dataIsLE = false;
+ bits = 14;
+ break;
+
+ /* 14bit LE */
+ case DTS_PREAMBLE_14LE:
+ if (data[5] != 0x07 || (data[4] & 0xf0) != 0xf0)
+ {
+ match = false;
+ break;
+ }
+ dtsBlocks = (((data[4] & 0x7) << 4) | ((data[7] & 0x3C) >> 2)) + 1;
+ m_fsize = ((((data[7] & 0x3 << 8) | data[6]) << 4) | ((data[9] & 0x3C) >> 2)) + 1;
+ amode = ((data[9] & 0x3) << 4) | ((data[8] & 0xF0) >> 4);
+ sfreq = data[8] & 0xF;
+ lfe = (data[13] & 0x18) >> 3;
+ m_dataIsLE = true;
+ bits = 14;
+ break;
+
+ /* 16bit BE */
+ case DTS_PREAMBLE_16BE:
+ dtsBlocks = (((data[4] & 0x1) << 7) | ((data[5] & 0xFC) >> 2)) + 1;
+ m_fsize = (((((data[5] & 0x3) << 8) | data[6]) << 4) | ((data[7] & 0xF0) >> 4)) + 1;
+ amode = ((data[7] & 0x0F) << 2) | ((data[8] & 0xC0) >> 6);
+ sfreq = (data[8] & 0x3C) >> 2;
+ lfe = (data[10] >> 1) & 0x3;
+ m_dataIsLE = false;
+ bits = 16;
+ break;
+
+ /* 16bit LE */
+ case DTS_PREAMBLE_16LE:
+ dtsBlocks = (((data[5] & 0x1) << 7) | ((data[4] & 0xFC) >> 2)) + 1;
+ m_fsize = (((((data[4] & 0x3) << 8) | data[7]) << 4) | ((data[6] & 0xF0) >> 4)) + 1;
+ amode = ((data[6] & 0x0F) << 2) | ((data[9] & 0xC0) >> 6);
+ sfreq = (data[9] & 0x3C) >> 2;
+ lfe = (data[11] >> 1) & 0x3;
+ m_dataIsLE = true;
+ bits = 16;
+ break;
+
+ default:
+ match = false;
+ break;
+ }
+
+ if (!match || sfreq == 0 || sfreq >= DTS_SFREQ_COUNT)
+ continue;
+
+ /* make sure the framesize is sane */
+ if (m_fsize < 96 || m_fsize > 16384)
+ continue;
+
+ bool invalid = false;
+ DataType dataType;
+ switch (dtsBlocks << 5)
+ {
+ case 512 : dataType = STREAM_TYPE_DTS_512 ; m_packFunc = &CAEPackIEC61937::PackDTS_512 ; break;
+ case 1024: dataType = STREAM_TYPE_DTS_1024; m_packFunc = &CAEPackIEC61937::PackDTS_1024; break;
+ case 2048: dataType = STREAM_TYPE_DTS_2048; m_packFunc = &CAEPackIEC61937::PackDTS_2048; break;
+ default:
+ invalid = true;
+ break;
+ }
+
+ if (invalid)
+ continue;
+
+ /* adjust the fsize for 14 bit streams */
+ if (bits == 14)
+ m_fsize = m_fsize / 14 * 16;
+
+ /* we need enough data to check for DTS-HD */
+ if (size - skip < m_fsize + 10)
+ {
+ /* we can assume DTS sync at this point */
+ m_syncFunc = &CAEStreamInfo::SyncDTS;
+ m_needBytes = m_fsize + 10;
+ m_fsize = 0;
+
+ return skip;
+ }
+
+ /* look for DTS-HD */
+ hd_sync = (data[m_fsize] << 24) | (data[m_fsize + 1] << 16) | (data[m_fsize + 2] << 8) | data[m_fsize + 3];
+ if (hd_sync == DTS_PREAMBLE_HD)
+ {
+ int hd_size;
+ bool blownup = (data[m_fsize + 5] & 0x20) != 0;
+ if (blownup)
+ hd_size = (((data[m_fsize + 6] & 0x01) << 19) | (data[m_fsize + 7] << 11) | (data[m_fsize + 8] << 3) | ((data[m_fsize + 9] & 0xe0) >> 5)) + 1;
+ else
+ hd_size = (((data[m_fsize + 6] & 0x1f) << 11) | (data[m_fsize + 7] << 3) | ((data[m_fsize + 8] & 0xe0) >> 5)) + 1;
+
+ /* set the type according to core or not */
+ if (m_coreOnly)
+ dataType = STREAM_TYPE_DTSHD_CORE;
+ else
+ dataType = STREAM_TYPE_DTSHD;
+
+ m_coreSize = m_fsize;
+ m_fsize += hd_size;
+ }
+
+ unsigned int sampleRate = DTSSampleRates[sfreq];
+ if (!m_hasSync || skip || dataType != m_dataType || sampleRate != m_sampleRate || dtsBlocks != m_dtsBlocks)
+ {
+ m_hasSync = true;
+ m_dataType = dataType;
+ m_sampleRate = sampleRate;
+ m_dtsBlocks = dtsBlocks;
+ m_channels = DTSChannels[amode] + (lfe ? 1 : 0);
+ m_syncFunc = &CAEStreamInfo::SyncDTS;
+ m_repeat = 1;
+
+ if (dataType == STREAM_TYPE_DTSHD)
+ {
+ m_outputRate = 192000;
+ m_outputChannels = 8;
+ m_channelMap = CAEChannelInfo(OutputMaps[1]);
+ m_channels += 2; /* FIXME: this needs to be read out, not sure how to do that yet */
+ }
+ else
+ {
+ m_outputRate = m_sampleRate;
+ m_outputChannels = 2;
+ m_channelMap = CAEChannelInfo(OutputMaps[0]);
+ }
+
+ std::string type;
+ switch (dataType)
+ {
+ case STREAM_TYPE_DTSHD : type = "dtsHD"; break;
+ case STREAM_TYPE_DTSHD_CORE: type = "dtsHD (core)"; break;
+ default : type = "dts"; break;
+ }
+
+ /* calculate the period size for dtsHD */
+ m_dtsPeriod = (m_outputRate * (m_outputChannels >> 1)) * (m_dtsBlocks << 5) / m_sampleRate;
+
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncDTS - %s stream detected (%d channels, %dHz, %dbit %s, period: %u)",
+ type.c_str(), m_channels, m_sampleRate,
+ bits, m_dataIsLE ? "LE" : "BE",
+ m_dtsPeriod);
+ }
+
+ return skip;
+ }
+
+ /* lost sync */
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncDTS - DTS sync lost");
+ m_hasSync = false;
+ return skip;
+}
+
+inline unsigned int CAEStreamInfo::GetTrueHDChannels(const uint16_t chanmap)
+{
+ int channels = 0;
+ for (int i = 0; i < 13; ++i)
+ channels += THDChanMap[i] * ((chanmap >> i) & 1);
+ return channels;
+}
+
+unsigned int CAEStreamInfo::SyncTrueHD(uint8_t *data, unsigned int size)
+{
+ unsigned int left = size;
+ unsigned int skip = 0;
+
+ /* if MLP */
+ for (; left; ++skip, ++data, --left)
+ {
+ /* if we dont have sync and there is less the 8 bytes, then break out */
+ if (!m_hasSync && left < 8)
+ return size;
+
+ /* if its a major audio unit */
+ uint16_t length = ((data[0] & 0x0F) << 8 | data[1]) << 1;
+ uint32_t syncword = ((((data[4] << 8 | data[5]) << 8) | data[6]) << 8) | data[7];
+ if (syncword == 0xf8726fba)
+ {
+ /* we need 32 bytes to sync on a master audio unit */
+ if (left < 32)
+ return skip;
+
+ /* get the rate and ensure its valid */
+ int rate = (data[8] & 0xf0) >> 4;
+ if (rate == 0xF)
+ continue;
+
+ /* verify the crc of the audio unit */
+ uint16_t crc = m_dllAvUtil.av_crc(m_crcTrueHD, 0, data + 4, 24);
+ crc ^= (data[29] << 8) | data[28];
+ if (((data[31] << 8) | data[30]) != crc)
+ continue;
+
+ /* get the sample rate and substreams, we have a valid master audio unit */
+ m_sampleRate = (rate & 0x8 ? 44100 : 48000) << (rate & 0x7);
+ m_substreams = (data[20] & 0xF0) >> 4;
+
+ /* get the number of encoded channels */
+ uint16_t channel_map = ((data[10] & 0x1F) << 8) | data[11];
+ if (!channel_map)
+ channel_map = (data[9] << 1) | (data[10] >> 7);
+ m_channels = CAEStreamInfo::GetTrueHDChannels(channel_map);
+
+ if (m_sampleRate == 48000 || m_sampleRate == 96000 || m_sampleRate == 192000)
+ m_outputRate = 192000;
+ else
+ m_outputRate = 176400;
+
+ if (!m_hasSync)
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncTrueHD - TrueHD stream detected (%d channels, %dHz)", m_channels, m_sampleRate);
+
+ m_hasSync = true;
+ m_fsize = length;
+ m_dataType = STREAM_TYPE_TRUEHD;
+ m_outputChannels = 8;
+ m_channelMap = CAEChannelInfo(OutputMaps[1]);
+ m_syncFunc = &CAEStreamInfo::SyncTrueHD;
+ m_packFunc = &CAEPackIEC61937::PackTrueHD;
+ m_repeat = 1;
+ return skip;
+ }
+ else
+ {
+ /* we cant sink to a subframe until we have the information from a master audio unit */
+ if (!m_hasSync)
+ continue;
+
+ /* if there is not enough data left to verify the packet, just return the skip amount */
+ if (left < (unsigned int)m_substreams * 4)
+ return skip;
+
+ /* verify the parity */
+ int p = 0;
+ uint8_t check = 0;
+ for (int i = -1; i < m_substreams; ++i)
+ {
+ check ^= data[p++];
+ check ^= data[p++];
+ if (i == -1 || data[p - 2] & 0x80)
+ {
+ check ^= data[p++];
+ check ^= data[p++];
+ }
+ }
+
+ /* if the parity nibble does not match */
+ if ((((check >> 4) ^ check) & 0xF) != 0xF)
+ {
+ /* lost sync */
+ m_hasSync = false;
+ CLog::Log(LOGINFO, "CAEStreamInfo::SyncTrueHD - Sync Lost");
+ continue;
+ }
+ else
+ {
+ m_fsize = length;
+ return skip;
+ }
+ }
+ }
+
+ /* lost sync */
+ m_hasSync = false;
+ return skip;
+}
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h
new file mode 100644
index 0000000000..73689f72ae
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h
@@ -0,0 +1,105 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEPackIEC61937.h"
+#include "AEChannelInfo.h"
+#include <stdint.h>
+#include <list>
+
+/* ffmpeg re-defines this, so undef it to squash the warning */
+#undef restrict
+#include "DllAvCodec.h"
+#include "DllAvFormat.h"
+
+class CAEStreamInfo
+{
+public:
+ enum DataType
+ {
+ STREAM_TYPE_NULL,
+ STREAM_TYPE_AC3,
+ STREAM_TYPE_DTS_512,
+ STREAM_TYPE_DTS_1024,
+ STREAM_TYPE_DTS_2048,
+ STREAM_TYPE_DTSHD,
+ STREAM_TYPE_DTSHD_CORE,
+ STREAM_TYPE_EAC3,
+ STREAM_TYPE_MLP,
+ STREAM_TYPE_TRUEHD
+ };
+
+ CAEStreamInfo();
+ ~CAEStreamInfo();
+
+ int AddData(uint8_t *data, unsigned int size, uint8_t **buffer = NULL, unsigned int *bufferSize = 0);
+
+ void SetCoreOnly (bool value) { m_coreOnly = value; }
+ unsigned int IsValid () { return m_hasSync ; }
+ unsigned int GetSampleRate () { return m_sampleRate ; }
+ unsigned int GetOutputRate () { return m_outputRate ; }
+ unsigned int GetOutputChannels() { return m_outputChannels; }
+ CAEChannelInfo GetChannelMap () { return m_channelMap ; }
+ unsigned int GetChannels () { return m_channels ; }
+ unsigned int GetFrameSize () { return m_fsize ; }
+ unsigned int GetDTSBlocks () { return m_dtsBlocks ; }
+ unsigned int GetDTSPeriod () { return m_dtsPeriod ; }
+ enum DataType GetDataType () { return m_dataType ; }
+ bool IsLittleEndian () { return m_dataIsLE ; }
+ CAEPackIEC61937::PackFunc GetPackFunc () { return m_packFunc ; }
+private:
+ DllAvUtil m_dllAvUtil;
+
+ uint8_t m_buffer[MAX_IEC61937_PACKET];
+ unsigned int m_bufferSize;
+ unsigned int m_skipBytes;
+
+ typedef unsigned int (CAEStreamInfo::*ParseFunc)(uint8_t *data, unsigned int size);
+
+ bool m_coreOnly;
+ unsigned int m_needBytes;
+ ParseFunc m_syncFunc;
+ bool m_hasSync;
+ unsigned int m_sampleRate; /* the actual sample rate */
+ unsigned int m_outputRate; /* the output sample rate */
+ unsigned int m_outputChannels; /* the output channel count */
+ CAEChannelInfo m_channelMap;
+ unsigned int m_channels; /* the actual number of channels in the stream */
+ unsigned int m_coreSize; /* core size for dtsHD */
+ unsigned int m_dtsBlocks;
+ unsigned int m_dtsPeriod; /* used for dtsHD */
+ unsigned int m_fsize;
+ unsigned int m_repeat;
+ int m_substreams; /* used for TrueHD */
+ AVCRC m_crcTrueHD[1024]; /* TrueHD crc table */
+ DataType m_dataType;
+ bool m_dataIsLE;
+ CAEPackIEC61937::PackFunc m_packFunc;
+
+ void GetPacket(uint8_t **buffer, unsigned int *bufferSize);
+ unsigned int DetectType(uint8_t *data, unsigned int size);
+ unsigned int SyncAC3 (uint8_t *data, unsigned int size);
+ unsigned int SyncDTS (uint8_t *data, unsigned int size);
+ unsigned int SyncTrueHD(uint8_t *data, unsigned int size);
+
+ static unsigned int GetTrueHDChannels(const uint16_t chanmap);
+};
+
diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
new file mode 100644
index 0000000000..0926d309c5
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifndef __STDC_LIMIT_MACROS
+ #define __STDC_LIMIT_MACROS
+#endif
+
+#include "utils/StdString.h"
+#include "AEUtil.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+
+using namespace std;
+
+/* declare the rng seed and initialize it */
+unsigned int CAEUtil::m_seed = (unsigned int)(CurrentHostCounter() / 1000.0f);
+#ifdef __SSE__
+ /* declare the SSE seed and initialize it */
+ MEMALIGN(16, __m128i CAEUtil::m_sseSeed) = _mm_set_epi32(CAEUtil::m_seed, CAEUtil::m_seed+1, CAEUtil::m_seed, CAEUtil::m_seed+1);
+#endif
+
+CAEChannelInfo CAEUtil::GuessChLayout(const unsigned int channels)
+{
+ CLog::Log(LOGWARNING, "CAEUtil::GuessChLayout - This method should really never be used, please fix the code that called this");
+
+ CAEChannelInfo result;
+ if (channels < 1 || channels > 8)
+ return result;
+
+ switch (channels)
+ {
+ case 1: result = AE_CH_LAYOUT_1_0; break;
+ case 2: result = AE_CH_LAYOUT_2_0; break;
+ case 3: result = AE_CH_LAYOUT_3_0; break;
+ case 4: result = AE_CH_LAYOUT_4_0; break;
+ case 5: result = AE_CH_LAYOUT_5_0; break;
+ case 6: result = AE_CH_LAYOUT_5_1; break;
+ case 7: result = AE_CH_LAYOUT_7_0; break;
+ case 8: result = AE_CH_LAYOUT_7_1; break;
+ }
+
+ return result;
+}
+
+const char* CAEUtil::GetStdChLayoutName(const enum AEStdChLayout layout)
+{
+ if (layout < 0 || layout >= AE_CH_LAYOUT_MAX)
+ return "UNKNOWN";
+
+ static const char* layouts[AE_CH_LAYOUT_MAX] =
+ {
+ "1.0",
+ "2.0", "2.1", "3.0", "3.1", "4.0",
+ "4.1", "5.0", "5.1", "7.0", "7.1"
+ };
+
+ return layouts[layout];
+}
+
+const unsigned int CAEUtil::DataFormatToBits(const enum AEDataFormat dataFormat)
+{
+ if (dataFormat < 0 || dataFormat >= AE_FMT_MAX)
+ return 0;
+
+ static const unsigned int formats[AE_FMT_MAX] =
+ {
+ 8, /* U8 */
+ 8, /* S8 */
+
+ 16, /* S16BE */
+ 16, /* S16LE */
+ 16, /* S16NE */
+
+ 32, /* S32BE */
+ 32, /* S32LE */
+ 32, /* S32NE */
+
+ 32, /* S24BE */
+ 32, /* S24LE */
+ 32, /* S24NE */
+
+ 24, /* S24BE3 */
+ 24, /* S24LE3 */
+ 24, /* S24NE3 */
+
+ sizeof(double) << 3, /* DOUBLE */
+ sizeof(float ) << 3, /* FLOAT */
+
+ 8, /* AAC */
+ 8, /* AC3 */
+ 8, /* DTS */
+ 8, /* EAC3 */
+ 8, /* TRUEHD */
+ 8, /* DTS-HD */
+ 32 /* LPCM */
+ };
+
+ return formats[dataFormat];
+}
+
+const char* CAEUtil::DataFormatToStr(const enum AEDataFormat dataFormat)
+{
+ if (dataFormat < 0 || dataFormat >= AE_FMT_MAX)
+ return "UNKNOWN";
+
+ static const char *formats[AE_FMT_MAX] =
+ {
+ "AE_FMT_U8",
+ "AE_FMT_S8",
+
+ "AE_FMT_S16BE",
+ "AE_FMT_S16LE",
+ "AE_FMT_S16NE",
+
+ "AE_FMT_S32BE",
+ "AE_FMT_S32LE",
+ "AE_FMT_S32NE",
+
+ "AE_FMT_S24BE4",
+ "AE_FMT_S24LE4",
+ "AE_FMT_S24NE4", /* S24 in 4 bytes */
+
+ "AE_FMT_S24BE3",
+ "AE_FMT_S24LE3",
+ "AE_FMT_S24NE3", /* S24 in 3 bytes */
+
+ "AE_FMT_DOUBLE",
+ "AE_FMT_FLOAT",
+
+ /* for passthrough streams and the like */
+ "AE_FMT_AAC",
+ "AE_FMT_AC3",
+ "AE_FMT_DTS",
+ "AE_FMT_EAC3",
+ "AE_FMT_TRUEHD",
+ "AE_FMT_DTSHD",
+ "AE_FMT_LPCM"
+ };
+
+ return formats[dataFormat];
+}
+
+#ifdef __SSE__
+void CAEUtil::SSEMulArray(float *data, const float mul, uint32_t count)
+{
+ const __m128 m = _mm_set_ps1(mul);
+
+ /* work around invalid alignment */
+ while (((uintptr_t)data & 0xF) && count > 0)
+ {
+ data[0] *= mul;
+ ++data;
+ --count;
+ }
+
+ uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i+=4, data+=4)
+ {
+ __m128 to = _mm_load_ps(data);
+ *(__m128*)data = _mm_mul_ps (to, m);
+ }
+
+ if (even != count)
+ {
+ uint32_t odd = count - even;
+ if (odd == 1)
+ data[0] *= mul;
+ else
+ {
+ __m128 to;
+ if (odd == 2)
+ {
+ to = _mm_setr_ps(data[0], data[1], 0, 0);
+ __m128 ou = _mm_mul_ps(to, m);
+ data[0] = ((float*)&ou)[0];
+ data[1] = ((float*)&ou)[1];
+ }
+ else
+ {
+ to = _mm_setr_ps(data[0], data[1], data[2], 0);
+ __m128 ou = _mm_mul_ps(to, m);
+ data[0] = ((float*)&ou)[0];
+ data[1] = ((float*)&ou)[1];
+ data[2] = ((float*)&ou)[2];
+ }
+ }
+ }
+}
+
+void CAEUtil::SSEMulAddArray(float *data, float *add, const float mul, uint32_t count)
+{
+ const __m128 m = _mm_set_ps1(mul);
+
+ /* work around invalid alignment */
+ while ((((uintptr_t)data & 0xF) || ((uintptr_t)add & 0xF)) && count > 0)
+ {
+ data[0] += add[0] * mul;
+ ++add;
+ ++data;
+ --count;
+ }
+
+ uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i+=4, data+=4, add+=4)
+ {
+ __m128 ad = _mm_load_ps(add );
+ __m128 to = _mm_load_ps(data);
+ *(__m128*)data = _mm_add_ps (to, _mm_mul_ps(ad, m));
+ }
+
+ if (even != count)
+ {
+ uint32_t odd = count - even;
+ if (odd == 1)
+ data[0] += add[0] * mul;
+ else
+ {
+ __m128 ad;
+ __m128 to;
+ if (odd == 2)
+ {
+ ad = _mm_setr_ps(add [0], add [1], 0, 0);
+ to = _mm_setr_ps(data[0], data[1], 0, 0);
+ __m128 ou = _mm_add_ps(to, _mm_mul_ps(ad, m));
+ data[0] = ((float*)&ou)[0];
+ data[1] = ((float*)&ou)[1];
+ }
+ else
+ {
+ ad = _mm_setr_ps(add [0], add [1], add [2], 0);
+ to = _mm_setr_ps(data[0], data[1], data[2], 0);
+ __m128 ou = _mm_add_ps(to, _mm_mul_ps(ad, m));
+ data[0] = ((float*)&ou)[0];
+ data[1] = ((float*)&ou)[1];
+ data[2] = ((float*)&ou)[2];
+ }
+ }
+ }
+}
+#endif
+
+inline float CAEUtil::SoftClamp(const float x)
+{
+#if 1
+ /*
+ This is a rational function to approximate a tanh-like soft clipper.
+ It is based on the pade-approximation of the tanh function with tweaked coefficients.
+ See: http://www.musicdsp.org/showone.php?id=238
+ */
+ if (x < -3.0f)
+ return -1.0f;
+ else if (x > 3.0f)
+ return 1.0f;
+ float y = x * x;
+ return x * (27.0f + y) / (27.0f + 9.0f * y);
+#else
+ /* slower method using tanh, but more accurate */
+
+ static const double k = 0.9f;
+ /* perform a soft clamp */
+ if (x > k)
+ x = (float) (tanh((x - k) / (1 - k)) * (1 - k) + k);
+ else if (x < -k)
+ x = (float) (tanh((x + k) / (1 - k)) * (1 - k) - k);
+
+ /* hard clamp anything still outside the bounds */
+ if (x > 1.0f)
+ return 1.0f;
+ if (x < -1.0f)
+ return -1.0f;
+
+ /* return the final sample */
+ return x;
+#endif
+}
+
+void CAEUtil::ClampArray(float *data, uint32_t count)
+{
+#ifndef __SSE__
+ for (uint32_t i = 0; i < count; ++i)
+ data[i] = SoftClamp(data[i]);
+
+#else
+ const __m128 c1 = _mm_set_ps1(27.0f);
+ const __m128 c2 = _mm_set_ps1(27.0f + 9.0f);
+
+ /* work around invalid alignment */
+ while (((uintptr_t)data & 0xF) && count > 0)
+ {
+ data[0] = SoftClamp(data[0]);
+ ++data;
+ --count;
+ }
+
+ uint32_t even = count & ~0x3;
+ for (uint32_t i = 0; i < even; i+=4, data+=4)
+ {
+ /* tanh approx clamp */
+ __m128 dt = _mm_load_ps(data);
+ __m128 tmp = _mm_mul_ps(dt, dt);
+ *(__m128*)data = _mm_div_ps(
+ _mm_mul_ps(
+ dt,
+ _mm_add_ps(c1, tmp)
+ ),
+ _mm_add_ps(c2, tmp)
+ );
+ }
+
+ if (even != count)
+ {
+ uint32_t odd = count - even;
+ if (odd == 1)
+ data[0] = SoftClamp(data[0]);
+ else
+ {
+ __m128 dt;
+ __m128 tmp;
+ __m128 out;
+ if (odd == 2)
+ {
+ /* tanh approx clamp */
+ dt = _mm_setr_ps(data[0], data[1], 0, 0);
+ tmp = _mm_mul_ps(dt, dt);
+ out = _mm_div_ps(
+ _mm_mul_ps(
+ dt,
+ _mm_add_ps(c1, tmp)
+ ),
+ _mm_add_ps(c2, tmp)
+ );
+
+ data[0] = ((float*)&out)[0];
+ data[1] = ((float*)&out)[1];
+ }
+ else
+ {
+ /* tanh approx clamp */
+ dt = _mm_setr_ps(data[0], data[1], data[2], 0);
+ tmp = _mm_mul_ps(dt, dt);
+ out = _mm_div_ps(
+ _mm_mul_ps(
+ dt,
+ _mm_add_ps(c1, tmp)
+ ),
+ _mm_add_ps(c2, tmp)
+ );
+
+ data[0] = ((float*)&out)[0];
+ data[1] = ((float*)&out)[1];
+ data[2] = ((float*)&out)[2];
+ }
+ }
+ }
+#endif
+}
+
+/*
+ Rand implementations based on:
+ http://software.intel.com/en-us/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor/
+ This is NOT safe for crypto work, but perfectly fine for audio usage (dithering)
+*/
+float CAEUtil::FloatRand1(const float min, const float max)
+{
+ const float delta = (max - min) / 2;
+ const float factor = delta / (float)INT32_MAX;
+ return ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
+}
+
+void CAEUtil::FloatRand4(const float min, const float max, float result[4], __m128 *sseresult/* = NULL */)
+{
+ #ifdef __SSE__
+ /*
+ this method may be called from other SSE code, we need
+ to calculate the delta & factor using SSE as the FPU
+ state is unknown and _mm_clear() is expensive.
+ */
+ MEMALIGN(16, static const __m128 point5 ) = _mm_set_ps1(0.5f);
+ MEMALIGN(16, static const __m128 int32max) = _mm_set_ps1((const float)INT32_MAX);
+ MEMALIGN(16, __m128 f) = _mm_div_ps(
+ _mm_mul_ps(
+ _mm_sub_ps(
+ _mm_set_ps1(max),
+ _mm_set_ps1(min)
+ ),
+ point5
+ ),
+ int32max
+ );
+
+ MEMALIGN(16, __m128i cur_seed_split);
+ MEMALIGN(16, __m128i multiplier);
+ MEMALIGN(16, __m128i adder);
+ MEMALIGN(16, __m128i mod_mask);
+ MEMALIGN(16, __m128 res);
+ MEMALIGN(16, static const unsigned int mult [4]) = {214013, 17405, 214013, 69069};
+ MEMALIGN(16, static const unsigned int gadd [4]) = {2531011, 10395331, 13737667, 1};
+ MEMALIGN(16, static const unsigned int mask [4]) = {0xFFFFFFFF, 0, 0xFFFFFFFF, 0};
+
+ adder = _mm_load_si128((__m128i*)gadd);
+ multiplier = _mm_load_si128((__m128i*)mult);
+ mod_mask = _mm_load_si128((__m128i*)mask);
+ cur_seed_split = _mm_shuffle_epi32(m_sseSeed, _MM_SHUFFLE(2, 3, 0, 1));
+
+ m_sseSeed = _mm_mul_epu32(m_sseSeed, multiplier);
+ multiplier = _mm_shuffle_epi32(multiplier, _MM_SHUFFLE(2, 3, 0, 1));
+ cur_seed_split = _mm_mul_epu32(cur_seed_split, multiplier);
+
+ m_sseSeed = _mm_and_si128(m_sseSeed, mod_mask);
+ cur_seed_split = _mm_and_si128(cur_seed_split, mod_mask);
+ cur_seed_split = _mm_shuffle_epi32(cur_seed_split, _MM_SHUFFLE(2, 3, 0, 1));
+ m_sseSeed = _mm_or_si128(m_sseSeed, cur_seed_split);
+ m_sseSeed = _mm_add_epi32(m_sseSeed, adder);
+
+ /* adjust the value to the range requested */
+ res = _mm_cvtepi32_ps(m_sseSeed);
+ if (sseresult)
+ *sseresult = _mm_mul_ps(res, f);
+ else
+ {
+ res = _mm_mul_ps(res, f);
+ _mm_storeu_ps(result, res);
+
+ /* returning a float array, so cleanup */
+ _mm_empty();
+ }
+
+ #else
+ const float delta = (max - min) / 2.0f;
+ const float factor = delta / (float)INT32_MAX;
+
+ /* cant return sseresult if we are not using SSE intrinsics */
+ ASSERT(result && !sseresult);
+
+ result[0] = ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
+ result[1] = ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
+ result[2] = ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
+ result[3] = ((float)(m_seed = (214013 * m_seed + 2531011)) * factor) - delta;
+ #endif
+}
diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.h b/xbmc/cores/AudioEngine/Utils/AEUtil.h
new file mode 100644
index 0000000000..dfba7e8571
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEUtil.h
@@ -0,0 +1,77 @@
+#pragma once
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "../AEAudioFormat.h"
+#include "utils/StdString.h"
+#include "PlatformDefs.h"
+#include <math.h>
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#else
+#define __m128 void
+#endif
+
+#ifdef __GNUC__
+ #define MEMALIGN(b, x) x __attribute__((aligned(b)))
+#else
+ #define MEMALIGN(b, x) __declspec(align(b)) x
+#endif
+
+class CAEUtil
+{
+private:
+ static unsigned int m_seed;
+ #ifdef __SSE__
+ static __m128i m_sseSeed;
+ #endif
+
+ static float SoftClamp(const float x);
+
+public:
+ static CAEChannelInfo GuessChLayout (const unsigned int channels);
+ static const char* GetStdChLayoutName(const enum AEStdChLayout layout);
+ static const unsigned int DataFormatToBits (const enum AEDataFormat dataFormat);
+ static const char* DataFormatToStr (const enum AEDataFormat dataFormat);
+
+ /* convert a linear value between 0.0 and 1.0 to a logrithmic value */
+ static inline const float LinToLog(const float dbrange, const float value)
+ {
+ float b = log(pow(10.0f, dbrange * 0.05f));
+ float a = 1.0f / exp(b);
+ return a * exp(b * value);
+ }
+
+ #ifdef __SSE__
+ static void SSEMulArray (float *data, const float mul, uint32_t count);
+ static void SSEMulAddArray (float *data, float *add, const float mul, uint32_t count);
+ #endif
+ static void ClampArray(float *data, uint32_t count);
+
+ /*
+ Rand implementations based on:
+ http://software.intel.com/en-us/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor/
+ This is NOT safe for crypto work, but perfectly fine for audio usage (dithering)
+ */
+ static float FloatRand1(const float min, const float max);
+ static void FloatRand4(const float min, const float max, float result[4], __m128 *sseresult = NULL);
+};
diff --git a/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp
new file mode 100644
index 0000000000..75aafcbf44
--- /dev/null
+++ b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AEWAVLoader.h"
+
+
+#include "system.h"
+#include "utils/log.h"
+#include "utils/EndianSwap.h"
+#include "filesystem/FileFactory.h"
+#include "filesystem/IFile.h"
+#include <samplerate.h>
+
+#include "AEConvert.h"
+#include "AEUtil.h"
+#include "AERemap.h"
+
+typedef struct
+{
+ char chunk_id[4];
+ uint32_t chunksize;
+} WAVE_CHUNK;
+
+CAEWAVLoader::CAEWAVLoader() :
+ m_valid (false),
+ m_sampleRate (0 ),
+ m_channelCount(0 ),
+ m_frameCount (0 ),
+ m_sampleCount (0 ),
+ m_samples (NULL )
+{
+}
+
+CAEWAVLoader::~CAEWAVLoader()
+{
+ DeInitialize();
+}
+
+bool CAEWAVLoader::Initialize(const std::string &filename, unsigned int resampleRate /* = 0 */)
+{
+ DeInitialize();
+ m_filename = filename;
+
+ XFILE::IFile *file = XFILE::CFileFactory::CreateLoader(m_filename);
+ if (!file)
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Failed to create loader: %s", m_filename.c_str());
+ return false;
+ }
+
+ struct __stat64 st;
+ if (!file->Open(CStdString(m_filename)) || file->Stat(&st) < 0)
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Failed to stat file: %s", m_filename.c_str());
+ delete file;
+ return false;
+ }
+
+ bool isRIFF = false;
+ bool isWAVE = false;
+ bool isFMT = false;
+ bool isPCM = false;
+ bool isDATA = false;
+
+ uint32_t sampleRate;
+ uint32_t byteRate;
+ uint16_t blockAlign;
+ uint16_t bitsPerSample;
+
+ WAVE_CHUNK chunk;
+ while (file->Read(&chunk, sizeof(chunk)) == sizeof(chunk))
+ {
+ chunk.chunksize = Endian_SwapLE32(chunk.chunksize);
+
+ /* if its the RIFF header */
+ if (!isRIFF && memcmp(chunk.chunk_id, "RIFF", 4) == 0)
+ {
+ isRIFF = true;
+
+ /* work around invalid chunksize, I have seen this in one file so far (shutter.wav) */
+ if (chunk.chunksize == st.st_size)
+ chunk.chunksize -= 8;
+
+ /* sanity check on the chunksize */
+ if (chunk.chunksize > st.st_size - 8)
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Corrupt WAV header: %s", m_filename.c_str());
+ file->Close();
+ delete file;
+ return false;
+ }
+
+ /* we only support WAVE files */
+ char format[4];
+ if (file->Read(&format, 4) != 4)
+ break;
+ isWAVE = memcmp(format, "WAVE", 4) == 0;
+ if (!isWAVE)
+ break;
+ }
+ /* if its the fmt section */
+ else if (!isFMT && memcmp(chunk.chunk_id, "fmt ", 4) == 0)
+ {
+ isFMT = true;
+ if (chunk.chunksize < 16)
+ break;
+ uint16_t format;
+ if (file->Read(&format, sizeof(format)) != sizeof(format))
+ break;
+ format = Endian_SwapLE16(format);
+ if (format != WAVE_FORMAT_PCM)
+ break;
+
+ uint16_t channelCount;
+ if (file->Read(&channelCount , 2) != 2)
+ break;
+ if (file->Read(&sampleRate , 4) != 4)
+ break;
+ if (file->Read(&byteRate , 4) != 4)
+ break;
+ if (file->Read(&blockAlign , 2) != 2)
+ break;
+ if (file->Read(&bitsPerSample, 2) != 2)
+ break;
+
+ m_channelCount = Endian_SwapLE16(channelCount );
+ m_sampleRate = Endian_SwapLE32(sampleRate );
+ byteRate = Endian_SwapLE32(byteRate );
+ blockAlign = Endian_SwapLE16(blockAlign );
+ bitsPerSample = Endian_SwapLE16(bitsPerSample);
+
+ if (m_channelCount > 2)
+ break;
+ isPCM = true;
+
+ if (chunk.chunksize > 16)
+ file->Seek(chunk.chunksize - 16, SEEK_CUR);
+ }
+ /* if we have the PCM info and its the DATA section */
+ else if (isPCM && !isDATA && memcmp(chunk.chunk_id, "data", 4) == 0)
+ {
+ unsigned int bytesPerSample = bitsPerSample >> 3;
+ m_sampleCount = chunk.chunksize / bytesPerSample;
+ m_frameCount = m_sampleCount / m_channelCount;
+ isDATA = m_frameCount > 0;
+
+ /* get the conversion function */
+ CAEConvert::AEConvertToFn convertFn;
+ switch (bitsPerSample)
+ {
+ case 8 : convertFn = CAEConvert::ToFloat(AE_FMT_U8 ); break;
+ case 16: convertFn = CAEConvert::ToFloat(AE_FMT_S16LE); break;
+ case 32: convertFn = CAEConvert::ToFloat(AE_FMT_S32LE); break;
+ default:
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Unsupported data format in wav: %s", m_filename.c_str());
+ file->Close();
+ delete file;
+ return false;
+ }
+
+ /* read in each sample */
+ unsigned int s;
+ m_samples = (float*)_aligned_malloc(sizeof(float) * m_sampleCount, 16);
+ uint8_t *raw = (uint8_t *)_aligned_malloc(bytesPerSample, 16);
+ for (s = 0; s < m_sampleCount; ++s)
+ {
+ if (file->Read(raw, bytesPerSample) != bytesPerSample)
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - WAV data shorter then expected: %s", m_filename.c_str());
+ _aligned_free(m_samples);
+ _aligned_free(raw);
+ m_samples = NULL;
+ file->Close();
+ delete file;
+ return false;
+ }
+
+ /* convert the sample to float */
+ convertFn(raw, 1, &m_samples[s]);
+ }
+ _aligned_free(raw);
+ }
+ else
+ {
+ /* skip any unknown sections */
+ file->Seek(chunk.chunksize, SEEK_CUR);
+ }
+ }
+
+ if (!isRIFF || !isWAVE || !isFMT || !isPCM || !isDATA || m_sampleCount == 0)
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Invalid, or un-supported WAV file: %s", m_filename.c_str());
+ file->Close();
+ delete file;
+ return false;
+ }
+
+ /* close the file as we have the samples now */
+ file->Close();
+ delete file;
+
+ /* if we got here, the file was valid and we have the data but it may need re-sampling still */
+ if (resampleRate != 0 && m_sampleRate != resampleRate)
+ {
+ unsigned int space = (unsigned int)((((float)m_sampleCount / (float)m_sampleRate) * (float)resampleRate) * 2.0f);
+ SRC_DATA data;
+ data.data_in = m_samples;
+ data.input_frames = m_frameCount;
+ data.data_out = (float*)_aligned_malloc(sizeof(float) * space, 16);
+ data.output_frames = space / m_channelCount;
+ data.src_ratio = (double)resampleRate / (double)m_sampleRate;
+#ifdef TARGET_DARWIN_IOS
+ if (src_simple(&data, SRC_SINC_FASTEST, m_channelCount) != 0)
+#else
+ if (src_simple(&data, SRC_SINC_MEDIUM_QUALITY, m_channelCount) != 0)
+#endif
+ {
+ CLog::Log(LOGERROR, "CAEWAVLoader::Initialize - Failed to resample audio: %s", m_filename.c_str());
+ _aligned_free(data.data_out);
+ _aligned_free(m_samples);
+ m_samples = NULL;
+ return false;
+ }
+
+ /* reassign m_samples */
+ _aligned_free(m_samples);
+ m_samples = data.data_out;
+ m_frameCount = data.output_frames_gen;
+ m_sampleCount = data.output_frames_gen * m_channelCount;
+ m_sampleRate = resampleRate;
+ }
+
+ CLog::Log(LOGINFO, "CAEWAVLoader::Initialize - Sound Loaded: %s", m_filename.c_str());
+ m_valid = true;
+ return true;
+}
+
+void CAEWAVLoader::DeInitialize()
+{
+ _aligned_free(m_samples);
+ m_samples = NULL;
+ m_frameCount = 0;
+ m_channelCount = 0;
+}
+
+bool CAEWAVLoader::Remap(CAEChannelInfo to, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
+{
+ /* FIXME: add support for multichannel files */
+ if (m_channelCount > 2)
+ return false;
+
+ static AEChannel layouts[][3] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL}
+ };
+
+ CAERemap remap;
+ if (!remap.Initialize(layouts[m_channelCount - 1], to, false, false, stdChLayout))
+ return false;
+
+ float *remapped = (float*)_aligned_malloc(sizeof(float) * m_frameCount * to.Count(), 16);
+ remap.Remap(m_samples, remapped, m_frameCount);
+
+ _aligned_free(m_samples);
+ m_samples = remapped;
+ m_sampleCount = m_frameCount * to.Count();
+ m_channelCount = to.Count();
+
+ return true;
+}
+
+unsigned int CAEWAVLoader::GetChannelCount()
+{
+ return m_channelCount;
+}
+
+unsigned int CAEWAVLoader::GetSampleRate()
+{
+ return m_sampleRate;
+}
+
+unsigned int CAEWAVLoader::GetSampleCount()
+{
+ return m_sampleCount;
+}
+
+unsigned int CAEWAVLoader::GetFrameCount()
+{
+ return m_frameCount;
+}
+
+float* CAEWAVLoader::GetSamples()
+{
+ if (!m_valid || m_samples == NULL) return NULL;
+ return m_samples;
+}
+
diff --git a/xbmc/guilib/GUISound.h b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h
index d756c128d5..d735917fbd 100644
--- a/xbmc/guilib/GUISound.h
+++ b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h
@@ -1,6 +1,7 @@
+#pragma once
/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://xbmc.org
*
* 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
@@ -19,38 +20,34 @@
*
*/
-#pragma once
-
#include "utils/StdString.h"
+#include "AEAudioFormat.h"
-#ifdef HAS_SDL_AUDIO
-#include <SDL/SDL_mixer.h>
-#endif
-
-class CGUISound
+class CAEWAVLoader
{
public:
- CGUISound();
- virtual ~CGUISound();
+ CAEWAVLoader();
+ ~CAEWAVLoader();
+
+ bool Initialize (const std::string &filename, unsigned int resampleRate = 0);
+ void DeInitialize();
- bool Load(const CStdString& strFile);
- void Play();
- void Stop();
- bool IsPlaying();
- void SetVolume(int level);
- void Wait(uint32_t millis = 500);
+ bool IsValid() { return m_valid; }
+ bool Remap(CAEChannelInfo to, enum AEStdChLayout stdChLayout = AE_CH_LAYOUT_INVALID);
+ unsigned int GetChannelCount();
+ unsigned int GetSampleRate();
+ unsigned int GetSampleCount();
+ unsigned int GetFrameCount();
+ float* GetSamples();
private:
-#ifdef _WIN32
- bool LoadWav(const CStdString& strFile, WAVEFORMATEX* wfx, LPBYTE* ppWavData, int* pDataSize);
- bool CreateBuffer(LPWAVEFORMATEX wfx, int iLength);
- bool FillBuffer(LPBYTE pbData, int iLength);
- void FreeBuffer();
+ std::string m_filename;
+ bool m_valid;
- LPDIRECTSOUNDBUFFER m_soundBuffer;
-#elif defined(HAS_SDL_AUDIO)
- Mix_Chunk* m_soundBuffer;
-#else
- void *m_soundBuffer;
-#endif
+ unsigned int m_sampleRate;
+ unsigned int m_channelCount;
+ unsigned int m_frameCount;
+ unsigned int m_sampleCount;
+ float *m_samples;
};
+
diff --git a/xbmc/cores/AudioRenderers/ALSADirectSound.cpp b/xbmc/cores/AudioRenderers/ALSADirectSound.cpp
deleted file mode 100644
index 3c891cff67..0000000000
--- a/xbmc/cores/AudioRenderers/ALSADirectSound.cpp
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
-* XBMC Media Center
-* Copyright (c) 2002 d7o3g4q and RUNTiME
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* 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 for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "ALSADirectSound.h"
-#include "guilib/AudioContext.h"
-#include "filesystem/SpecialProtocol.h"
-#include "settings/GUISettings.h"
-#include "settings/Settings.h"
-#include "utils/log.h"
-#include "limits.h"
-#include "guilib/LocalizeStrings.h"
-
-#define CHECK_ALSA(l,s,e) if ((e)<0) CLog::Log(l,"%s - %s, alsa error: %d - %s",__FUNCTION__,s,e,snd_strerror(e));
-#define CHECK_ALSA_RETURN(l,s,e) CHECK_ALSA((l),(s),(e)); if ((e)<0) return false;
-
-using namespace std;
-
-static CStdString QuoteDevice(const CStdString& device)
-{
- CStdString result(device);
- result.Replace("'", "\\'");
- return "'" + result + "'";
-}
-
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
-//***********************************************************************************************
-CALSADirectSound::CALSADirectSound()
-{
- m_pPlayHandle = NULL;
- m_bIsAllocated = false;
-}
-
-bool CALSADirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded encoded)
-{
- enum PCMChannels *outLayout;
-
- static enum PCMChannels ALSAChannelMap[8] =
- {
- PCM_FRONT_LEFT , PCM_FRONT_RIGHT ,
- PCM_BACK_LEFT , PCM_BACK_RIGHT ,
- PCM_FRONT_CENTER, PCM_LOW_FREQUENCY,
- PCM_SIDE_LEFT , PCM_SIDE_RIGHT
- };
-
- CStdString deviceuse;
-
- /* setup the channel mapping */
- m_uiDataChannels = iChannels;
- m_remap.Reset();
-
- if (encoded == ENCODED_NONE && channelMap)
- {
- /* set the input format, and get the channel layout so we know what we need to open */
- outLayout = m_remap.SetInputFormat (iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);
- unsigned int outChannels = 0;
- unsigned int ch = 0, map;
- while(outLayout[ch] != PCM_INVALID)
- {
- for(map = 0; map < 8; ++map)
- if (outLayout[ch] == ALSAChannelMap[map])
- {
- if (map > outChannels)
- outChannels = map;
- break;
- }
- ++ch;
- }
-
- m_remap.SetOutputFormat(++outChannels, ALSAChannelMap);
- if (m_remap.CanRemap())
- {
- iChannels = outChannels;
- if (m_uiDataChannels != (unsigned int)iChannels)
- CLog::Log(LOGDEBUG, "CALSADirectSound::CALSADirectSound - Requested channels changed from %i to %i", m_uiDataChannels, iChannels);
- }
- }
-
- bool bAudioOnAllSpeakers(false);
- g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
-
- m_pPlayHandle = NULL;
- m_bPause = false;
- m_bCanPause = false;
- m_bIsAllocated = false;
- m_uiChannels = iChannels;
- m_uiSamplesPerSec = uiSamplesPerSec;
- m_uiBitsPerSample = uiBitsPerSample;
- m_bPassthrough = encoded != ENCODED_NONE;
- m_drc = 0;
-
- m_nCurrentVolume = g_settings.m_nVolumeLevel;
- if (!m_bPassthrough)
- m_amp.SetVolume(m_nCurrentVolume);
-
- m_dwFrameCount = 512;
- m_dwNumPackets = 16;
- m_uiBufferSize = 0;
-
- snd_pcm_hw_params_t *hw_params=NULL;
- snd_pcm_sw_params_t *sw_params=NULL;
-
- /* Open the device */
- int nErr;
-
- /* if this is first access to audio, global sound config might not be loaded */
- if(!snd_config)
- snd_config_update();
-
- snd_config_t *config = snd_config;
- deviceuse = device;
-
- nErr = snd_config_copy(&config, snd_config);
- CHECK_ALSA_RETURN(LOGERROR,"config_copy",nErr);
-
- if(m_bPassthrough)
- {
- /* http://www.alsa-project.org/alsa-doc/alsa-lib/group___digital___audio___interface.html */
- deviceuse += (deviceuse.Find(':') >= 0) ? ',' : ':';
- deviceuse += "AES0=0x6";
- deviceuse += ",AES1=0x82";
- deviceuse += ",AES2=0x0";
- if(uiSamplesPerSec == 192000)
- deviceuse += ",AES3=0xe";
- else if(uiSamplesPerSec == 176400)
- deviceuse += ",AES3=0xc";
- else if(uiSamplesPerSec == 96000)
- deviceuse += ",AES3=0xa";
- else if(uiSamplesPerSec == 88200)
- deviceuse += ",AES3=0x8";
- else if(uiSamplesPerSec == 48000)
- deviceuse += ",AES3=0x2";
- else if(uiSamplesPerSec == 44100)
- deviceuse += ",AES3=0x0";
- else if(uiSamplesPerSec == 32000)
- deviceuse += ",AES3=0x3";
- else
- deviceuse += ",AES3=0x1";
- }
- else
- {
- if((deviceuse + ":").Left(5) == "hdmi:"
- || (deviceuse + ":").Left(7) == "iec958:"
- || (deviceuse + ":").Left(6) == "spdif:")
- deviceuse = "plug:" + QuoteDevice(deviceuse);
-
- if(deviceuse == "default")
- switch(iChannels)
- {
- case 8: deviceuse = "plug:surround71"; break;
- case 6: deviceuse = "plug:surround51"; break;
- case 5: deviceuse = "plug:surround50"; break;
- case 4: deviceuse = "plug:surround40"; break;
- }
-
- if(deviceuse != device)
- {
- snd_input_t* input;
- nErr = snd_input_stdio_open(&input, CSpecialProtocol::TranslatePath("special://xbmc/system/asound.conf").c_str(), "r");
- if(nErr >= 0)
- {
- nErr = snd_config_load(config, input);
- CHECK_ALSA_RETURN(LOGERROR,"config_load", nErr);
-
- snd_input_close(input);
- CHECK_ALSA_RETURN(LOGERROR,"input_close", nErr);
- }
- else
- {
- CLog::Log(LOGWARNING, "%s - Unable to load alsa configuration \"%s\" for device \"%s\" - %s", __FUNCTION__, "special://xbmc/system/asound.conf", deviceuse.c_str(), snd_strerror(nErr));
- deviceuse = device;
- }
- }
- }
-
- CLog::Log(LOGDEBUG, "%s - using alsa device %s", __FUNCTION__, deviceuse.c_str());
-
- nErr = snd_pcm_open_lconf(&m_pPlayHandle, deviceuse.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK, config);
-
- if(nErr == -EBUSY)
- {
- // this could happen if we are in the middle of a resolution switch sometimes
- CLog::Log(LOGERROR, "%s - device %s busy retrying...", __FUNCTION__, deviceuse.c_str());
- if(m_pPlayHandle)
- {
- snd_pcm_close(m_pPlayHandle);
- m_pPlayHandle = NULL;
- }
- Sleep(200);
- nErr = snd_pcm_open_lconf(&m_pPlayHandle, deviceuse.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK, config);
- }
-
- if(nErr < 0 && deviceuse != device)
- {
- CLog::Log(LOGERROR, "%s - failed to open custom device %s (error:%s), retry with default %s", __FUNCTION__, deviceuse.c_str(), snd_strerror(nErr), device.c_str());
- if(m_pPlayHandle)
- {
- snd_pcm_close(m_pPlayHandle);
- m_pPlayHandle = NULL;
- }
- nErr = snd_pcm_open_lconf(&m_pPlayHandle, device.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK, config);
-
- }
-
- CHECK_ALSA_RETURN(LOGERROR,"pcm_open_lconf",nErr);
-
- snd_config_delete(config);
-
- /* Allocate Hardware Parameters structures and fills it with config space for PCM */
- snd_pcm_hw_params_malloc(&hw_params);
-
- /* Allocate Software Parameters structures and fills it with config space for PCM */
- snd_pcm_sw_params_malloc(&sw_params);
-
- nErr = snd_pcm_hw_params_any(m_pPlayHandle, hw_params);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_any",nErr);
-
- nErr = snd_pcm_hw_params_set_access(m_pPlayHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_access",nErr);
-
- // always use 16 bit samples
- nErr = snd_pcm_hw_params_set_format(m_pPlayHandle, hw_params, SND_PCM_FORMAT_S16);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_format",nErr);
-
- nErr = snd_pcm_hw_params_set_rate_near(m_pPlayHandle, hw_params, &m_uiSamplesPerSec, NULL);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_rate",nErr);
-
- nErr = snd_pcm_hw_params_set_channels(m_pPlayHandle, hw_params, iChannels);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_channels",nErr);
-
- nErr = snd_pcm_hw_params_set_period_size_near(m_pPlayHandle, hw_params, &m_dwFrameCount, NULL);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_period_size",nErr);
-
- nErr = snd_pcm_hw_params_set_periods_near(m_pPlayHandle, hw_params, &m_dwNumPackets, NULL);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_periods",nErr);
-
- nErr = snd_pcm_hw_params_get_buffer_size(hw_params, &m_uiBufferSize);
- CHECK_ALSA_RETURN(LOGERROR,"hw_params_get_buffer_size",nErr);
-
- /* Assign them to the playback handle and free the parameters structure */
- nErr = snd_pcm_hw_params(m_pPlayHandle, hw_params);
- CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_hw_params",nErr);
-
- nErr = snd_pcm_sw_params_current(m_pPlayHandle, sw_params);
- CHECK_ALSA_RETURN(LOGERROR,"sw_params_current",nErr);
-
- nErr = snd_pcm_sw_params_set_start_threshold(m_pPlayHandle, sw_params, INT_MAX);
- CHECK_ALSA_RETURN(LOGERROR,"sw_params_set_start_threshold",nErr);
-
- snd_pcm_uframes_t boundary;
- nErr = snd_pcm_sw_params_get_boundary( sw_params, &boundary );
- CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_sw_params_get_boundary",nErr);
-
- nErr = snd_pcm_sw_params_set_silence_threshold(m_pPlayHandle, sw_params, 0 );
- CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_sw_params_set_silence_threshold",nErr);
-
- nErr = snd_pcm_sw_params_set_silence_size( m_pPlayHandle, sw_params, boundary );
- CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_sw_params_set_silence_size",nErr);
-
- nErr = snd_pcm_sw_params(m_pPlayHandle, sw_params);
- CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_sw_params",nErr);
-
- m_bCanPause = !!snd_pcm_hw_params_can_pause(hw_params);
-
- snd_pcm_hw_params_free (hw_params);
- snd_pcm_sw_params_free (sw_params);
-
-
- CLog::Log(LOGDEBUG, "CALSADirectSound::Initialize - frame count:%u, packet count:%u, buffer size:%u"
- , (unsigned int)m_dwFrameCount
- , m_dwNumPackets
- , (unsigned int)m_uiBufferSize);
-
- if(m_uiSamplesPerSec != uiSamplesPerSec)
- CLog::Log(LOGWARNING, "CALSADirectSound::CALSADirectSound - requested samplerate (%d) not supported by hardware, using %d instead", uiSamplesPerSec, m_uiSamplesPerSec);
-
-
- nErr = snd_pcm_prepare (m_pPlayHandle);
- CHECK_ALSA(LOGERROR,"snd_pcm_prepare",nErr);
-
- m_bIsAllocated = true;
- return true;
-}
-
-//***********************************************************************************************
-CALSADirectSound::~CALSADirectSound()
-{
- Deinitialize();
-}
-
-
-//***********************************************************************************************
-bool CALSADirectSound::Deinitialize()
-{
- m_bIsAllocated = false;
- if (m_pPlayHandle)
- {
- snd_pcm_drop(m_pPlayHandle);
- snd_pcm_close(m_pPlayHandle);
- }
-
- m_pPlayHandle=NULL;
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- return true;
-}
-
-void CALSADirectSound::Flush()
-{
- if (!m_bIsAllocated)
- return;
-
- int nErr = snd_pcm_drop(m_pPlayHandle);
- CHECK_ALSA(LOGERROR,"flush-drop",nErr);
- nErr = snd_pcm_prepare(m_pPlayHandle);
- CHECK_ALSA(LOGERROR,"flush-prepare",nErr);
-}
-
-//***********************************************************************************************
-bool CALSADirectSound::Pause()
-{
- if (!m_bIsAllocated)
- return -1;
-
- if (m_bPause) return true;
- m_bPause = true;
-
- snd_pcm_state_t state = snd_pcm_state(m_pPlayHandle);
-
- if(state != SND_PCM_STATE_RUNNING)
- {
- if(state != SND_PCM_STATE_PAUSED
- && state != SND_PCM_STATE_PREPARED)
- {
- CLog::Log(LOGWARNING, "CALSADirectSound::Pause - device in weird state %d", (int)state);
- Flush();
- }
- return true;
- }
-
- if(m_bCanPause)
- {
- int nErr = snd_pcm_pause(m_pPlayHandle,1); // this is not supported on all devices.
- CHECK_ALSA(LOGERROR,"pcm_pause",nErr);
- if(nErr<0)
- m_bCanPause = false;
- }
-
- if(!m_bCanPause)
- {
- snd_pcm_sframes_t avail = snd_pcm_avail_update(m_pPlayHandle);
- snd_pcm_sframes_t delay = 0;
- if(avail >= 0)
- delay = (snd_pcm_sframes_t)m_uiBufferSize - avail;
-
- CLog::Log(LOGWARNING, "CALSADirectSound::CALSADirectSound - device is not able to pause playback, will flush and prefix with %d frames", (int)delay);
- Flush();
-
- if(delay > 0)
- {
- void* silence = calloc(snd_pcm_frames_to_bytes(m_pPlayHandle, delay), 1);
- int nErr = snd_pcm_writei(m_pPlayHandle, silence, delay);
- CHECK_ALSA(LOGERROR,"snd_pcm_writei", nErr);
- free(silence);
- }
- }
-
- return true;
-}
-
-//***********************************************************************************************
-bool CALSADirectSound::Resume()
-{
- if (!m_bIsAllocated)
- return -1;
-
- snd_pcm_state_t state = snd_pcm_state(m_pPlayHandle);
- if(state == SND_PCM_STATE_PAUSED)
- snd_pcm_pause(m_pPlayHandle,0);
-
- else if(state == SND_PCM_STATE_PREPARED)
- {
- snd_pcm_sframes_t avail = snd_pcm_avail_update(m_pPlayHandle);
- if(avail >= 0 && avail < (snd_pcm_sframes_t)m_uiBufferSize)
- snd_pcm_start(m_pPlayHandle);
- }
- else if(state == SND_PCM_STATE_RUNNING)
- {}
- else
- {
- CLog::Log(LOGWARNING, "CALSADirectSound::Resume - unexpected device state %d flushing", state);
- Flush();
- snd_pcm_start(m_pPlayHandle);
- }
-
- m_bPause = false;
-
- return true;
-}
-
-//***********************************************************************************************
-bool CALSADirectSound::Stop()
-{
- if (!m_bIsAllocated)
- return -1;
-
- Flush();
-
- m_bPause = false;
-
- return true;
-}
-
-//***********************************************************************************************
-long CALSADirectSound::GetCurrentVolume() const
-{
- return m_nCurrentVolume;
-}
-
-//***********************************************************************************************
-void CALSADirectSound::Mute(bool bMute)
-{
- if (!m_bIsAllocated)
- return;
-
- if (bMute)
- SetCurrentVolume(VOLUME_MINIMUM);
- else
- SetCurrentVolume(m_nCurrentVolume);
-
-}
-
-//***********************************************************************************************
-bool CALSADirectSound::SetCurrentVolume(long nVolume)
-{
- if (!m_bIsAllocated) return -1;
- m_nCurrentVolume = nVolume;
- m_amp.SetVolume(nVolume);
- return true;
-}
-
-
-//***********************************************************************************************
-unsigned int CALSADirectSound::GetSpaceFrames()
-{
- if (!m_bIsAllocated) return 0;
-
- int nSpace = snd_pcm_avail_update(m_pPlayHandle);
- if (nSpace == 0)
- {
- snd_pcm_state_t state = snd_pcm_state(m_pPlayHandle);
- if(state != SND_PCM_STATE_RUNNING && state != SND_PCM_STATE_PREPARED && !m_bPause)
- {
- CLog::Log(LOGWARNING,"CALSADirectSound::GetSpace - buffer underun (%d)", state);
- Flush();
- return m_uiBufferSize;
- }
- }
- if (nSpace < 0)
- {
- CLog::Log(LOGWARNING,"CALSADirectSound::GetSpace - get space failed. err: %d (%s)", nSpace, snd_strerror(nSpace));
- Flush();
- return m_uiBufferSize;
- }
- return nSpace;
-}
-
-unsigned int CALSADirectSound::GetSpace()
-{
- return GetSpaceFrames() * m_uiDataChannels * m_uiBitsPerSample / 8;
-}
-
-//***********************************************************************************************
-unsigned int CALSADirectSound::AddPackets(const void* data, unsigned int len)
-{
- if (!m_bIsAllocated)
- {
- CLog::Log(LOGERROR,"CALSADirectSound::AddPackets - sanity failed. no valid play handle!");
- return len;
- }
- // if we are paused we don't accept any data as pause doesn't always
- // work, and then playback would start again
- if(m_bPause)
- return 0;
-
- int framesToWrite, bytesToWrite;
-
- framesToWrite = std::min(GetSpaceFrames(), len / ( m_uiDataChannels * m_uiBitsPerSample / 8 ) );
- framesToWrite /= m_dwFrameCount;
- framesToWrite *= m_dwFrameCount;
- bytesToWrite = snd_pcm_frames_to_bytes(m_pPlayHandle, framesToWrite);
-
- if(framesToWrite == 0)
- {
- // if we haven't started playback, do so now
- if(snd_pcm_state(m_pPlayHandle) == SND_PCM_STATE_PREPARED && !m_bPause)
- snd_pcm_start(m_pPlayHandle);
- return 0;
- }
-
- int writeResult;
- if (m_bPassthrough && m_nCurrentVolume == VOLUME_MINIMUM)
- {
- char dummy[bytesToWrite];
- memset(dummy,0,sizeof(dummy));
- writeResult = snd_pcm_writei(m_pPlayHandle, dummy, framesToWrite);
- }
- else
- {
- if (m_remap.CanRemap())
- {
- /* remap the data to the correct channels */
- uint8_t outData[bytesToWrite];
- m_remap.Remap((void *)data, outData, framesToWrite, m_drc);
- m_amp.DeAmplify((short *)outData, bytesToWrite / 2);
- writeResult = snd_pcm_writei(m_pPlayHandle, outData, framesToWrite);
- }
- else
- {
- if (!m_bPassthrough)
- m_amp.DeAmplify((short *)data, framesToWrite * m_uiDataChannels);
-
- writeResult = snd_pcm_writei(m_pPlayHandle, data, framesToWrite);
- }
- }
- if ( writeResult == -EPIPE )
- {
- CLog::Log(LOGDEBUG, "CALSADirectSound::AddPackets - buffer underun (tried to write %d frames)",
- framesToWrite);
- Flush();
- return 0;
- }
- else if (writeResult != framesToWrite)
- {
- CLog::Log(LOGERROR, "CALSADirectSound::AddPackets - failed to write %d frames. "
- "bad write (err: %d) - %s",
- framesToWrite, writeResult, snd_strerror(writeResult));
- Flush();
- }
-
- if (writeResult > 0)
- {
- if(snd_pcm_state(m_pPlayHandle) == SND_PCM_STATE_PREPARED && !m_bPause && GetSpaceFrames() <= m_dwFrameCount)
- snd_pcm_start(m_pPlayHandle);
-
- return writeResult * m_uiBitsPerSample * m_uiDataChannels / 8;
- }
-
- return 0;
-}
-
-//***********************************************************************************************
-float CALSADirectSound::GetDelay()
-{
- if (!m_bIsAllocated)
- return 0.0;
-
- snd_pcm_sframes_t frames = 0;
-
- int nErr = snd_pcm_delay(m_pPlayHandle, &frames);
- CHECK_ALSA(LOGERROR,"snd_pcm_delay",nErr);
- if (nErr < 0)
- {
- frames = 0;
- Flush();
- }
-
- if (frames < 0)
- {
-#if SND_LIB_VERSION >= 0x000901 /* snd_pcm_forward() exists since 0.9.0rc8 */
- snd_pcm_forward(m_pPlayHandle, -frames);
-#endif
- frames = 0;
- }
-
- return (double)frames / m_uiSamplesPerSec;
-}
-
-float CALSADirectSound::GetCacheTime()
-{
- return (float)(m_uiBufferSize - GetSpaceFrames()) / m_uiSamplesPerSec;
-}
-
-float CALSADirectSound::GetCacheTotal()
-{
- return (float)m_uiBufferSize / m_uiSamplesPerSec;
-}
-
-//***********************************************************************************************
-unsigned int CALSADirectSound::GetChunkLen()
-{
- return m_dwFrameCount * m_uiDataChannels * m_uiBitsPerSample / 8;
-}
-//***********************************************************************************************
-int CALSADirectSound::SetPlaySpeed(int iSpeed)
-{
- return 0;
-}
-
-void CALSADirectSound::RegisterAudioCallback(IAudioCallback *pCallback)
-{
- m_pCallback = pCallback;
-}
-
-void CALSADirectSound::UnRegisterAudioCallback()
-{
- m_pCallback = NULL;
-}
-
-void CALSADirectSound::WaitCompletion()
-{
- if (!m_bIsAllocated || m_bPause)
- return;
-
- snd_pcm_wait(m_pPlayHandle, -1);
-}
-
-void CALSADirectSound::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
-{
- return ;
-}
-
-void CALSADirectSound::EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough)
-{
- if (!passthrough)
- {
- vAudioSinks.push_back(AudioSink(g_localizeStrings.Get(409) + " (ALSA)", "alsa:default"));
- vAudioSinks.push_back(AudioSink("iec958 (ALSA)" , "alsa:plug:iec958"));
- vAudioSinks.push_back(AudioSink("hdmi (ALSA)" , "alsa:plug:hdmi"));
- }
- else
- {
- vAudioSinks.push_back(AudioSink("iec958 (ALSA)" , "alsa:iec958"));
- vAudioSinks.push_back(AudioSink("hdmi (ALSA)" , "alsa:hdmi"));
- }
-
- snd_ctl_t *handle;
- snd_ctl_card_info_t *info;
- snd_ctl_card_info_alloca( &info );
- CStdString strHwName;
- int n_cards = -1;
-
- while ( snd_card_next( &n_cards ) == 0 && n_cards >= 0 )
- {
- strHwName.Format("hw:%d", n_cards);
- if ( snd_ctl_open( &handle, strHwName.c_str(), 0 ) == 0 )
- {
- if ( snd_ctl_card_info( handle, info ) == 0 )
- {
- CStdString strReadableCardName = snd_ctl_card_info_get_name( info );
- CStdString strCardName = snd_ctl_card_info_get_id( info );
-
- int dev = -1;
- while( snd_ctl_pcm_next_device( handle, &dev ) == 0 && dev >= 0 )
- {
- if (!passthrough)
- GenSoundLabel(vAudioSinks, "default", strCardName, dev, strReadableCardName);
- GenSoundLabel(vAudioSinks, "iec958", strCardName, dev, strReadableCardName);
- GenSoundLabel(vAudioSinks, "hdmi", strCardName, dev, strReadableCardName);
- }
- }
- else
- CLog::Log(LOGERROR,"((ALSAENUM))control hardware info (%i): failed.\n", n_cards );
- snd_ctl_close( handle );
- }
- else
- CLog::Log(LOGERROR,"((ALSAENUM))control open (%i) failed.\n", n_cards );
- }
-}
-
-void CALSADirectSound::GenSoundLabel(AudioSinkList& vAudioSinks, CStdString sink, CStdString card, int dev, CStdString readableCard)
-{
- CStdString deviceString;
- deviceString.Format("%s:CARD=%s,DEV=%d", sink, card.c_str(), dev);
-
- CStdString finalSink;
- finalSink.Format("alsa:%s", deviceString.c_str());
- CStdString label;
- label.Format("%s - %s - %d (ALSA)", readableCard.c_str(), sink.c_str(), dev);
- vAudioSinks.push_back(AudioSink(label, finalSink));
-}
diff --git a/xbmc/cores/AudioRenderers/ALSADirectSound.h b/xbmc/cores/AudioRenderers/ALSADirectSound.h
deleted file mode 100644
index 21b5acb2cd..0000000000
--- a/xbmc/cores/AudioRenderers/ALSADirectSound.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-* XBMC Media Center
-* Copyright (c) 2002 d7o3g4q and RUNTiME
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* 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 for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-// AsyncAudioRenderer.h: interface for the CAsyncDirectSound class.
-//
-//////////////////////////////////////////////////////////////////////
-
-#ifndef __ALSA_DIRECT_SOUND_H__
-#define __ALSA_DIRECT_SOUND_H__
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-#include "IAudioRenderer.h"
-#include "cores/IAudioCallback.h"
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#include <alsa/asoundlib.h>
-
-#include "../../utils/PCMAmplifier.h"
-
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-class CALSADirectSound : public IAudioRenderer
-{
-public:
- virtual void UnRegisterAudioCallback();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback);
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- CALSADirectSound();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual ~CALSADirectSound();
-
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual bool Deinitialize();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void SetDynamicRangeCompression(long drc) { m_drc = drc; }
- virtual int SetPlaySpeed(int iSpeed);
- virtual void WaitCompletion();
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
-
- virtual void Flush();
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough);
-private:
- unsigned int GetSpaceFrames();
- static void GenSoundLabel(AudioSinkList& vAudioSinks, CStdString sink, CStdString card, int dev, CStdString readableCard);
- snd_pcm_t *m_pPlayHandle;
-
- IAudioCallback* m_pCallback;
- CPCMAmplifier m_amp;
- long m_nCurrentVolume;
- long m_drc;
- snd_pcm_uframes_t m_dwFrameCount;
- snd_pcm_uframes_t m_uiBufferSize;
- unsigned int m_dwNumPackets;
- bool m_bPause;
- bool m_bIsAllocated;
- bool m_bCanPause;
-
- unsigned int m_uiSamplesPerSec;
- unsigned int m_uiBitsPerSample;
- unsigned int m_uiDataChannels;
- unsigned int m_uiChannels;
-
- bool m_bPassthrough;
-};
-
-#endif
-
diff --git a/xbmc/cores/AudioRenderers/AudioRendererFactory.cpp b/xbmc/cores/AudioRenderers/AudioRendererFactory.cpp
deleted file mode 100644
index 927e8ea8f1..0000000000
--- a/xbmc/cores/AudioRenderers/AudioRendererFactory.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "system.h"
-#include "AudioRendererFactory.h"
-#include "settings/GUISettings.h"
-#include "utils/log.h"
-#include "NullDirectSound.h"
-
-#ifdef HAS_PULSEAUDIO
-#include "PulseAudioDirectSound.h"
-#endif
-
-#ifdef _WIN32
-#include "Win32WASAPI.h"
-#include "Win32DirectSound.h"
-#endif
-#if defined(__APPLE__)
-#if defined(__arm__)
- #include "IOSAudioRenderer.h"
-#else
- #include "CoreAudioRenderer.h"
-#endif
-#elif defined(USE_ALSA)
-#include "ALSADirectSound.h"
-#endif
-
-#define ReturnOnValidInitialize(rendererName) \
-{ \
- if (audioSink->Initialize(pCallback, device, iChannels, channelMap, uiSamplesPerSec, uiBitsPerSample, bResample, bIsMusic, encoded)) \
- { \
- CLog::Log(LOGDEBUG, "%s::Initialize" \
- " - Channels: %i" \
- " - SampleRate: %i" \
- " - SampleBit: %i" \
- " - Resample %s" \
- " - IsMusic %s" \
- " - IsPassthrough %d" \
- " - audioDevice: %s", \
- rendererName, \
- iChannels, \
- uiSamplesPerSec, \
- uiBitsPerSample, \
- bResample ? "true" : "false", \
- bIsMusic ? "true" : "false", \
- encoded, \
- device.c_str() \
- ); \
- return audioSink; \
- } \
- else \
- { \
- audioSink->Deinitialize(); \
- delete audioSink; \
- audioSink = NULL; \
- } \
-}
-
-#define CreateAndReturnOnValidInitialize(rendererClass) \
-{ \
- audioSink = new rendererClass(); \
- ReturnOnValidInitialize(#rendererClass); \
-}
-
-#define ReturnNewRenderer(rendererClass) \
-{ \
- renderer = #rendererClass; \
- return new rendererClass(); \
-}
-
-/* windows channel order */
-static const enum PCMChannels dsound_default_channel_layout[][8] =
-{
- {PCM_FRONT_CENTER},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_SIDE_LEFT, PCM_SIDE_RIGHT}
-};
-
-IAudioRenderer* CAudioRendererFactory::Create(IAudioCallback* pCallback, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, IAudioRenderer::EEncoded encoded)
-{
- IAudioRenderer* audioSink = NULL;
- CStdString renderer;
-
- if(channelMap == NULL)
- {
- CLog::Log(LOGINFO, "CAudioRendererFactory: no input channel map specified assume windows\n");
- channelMap = (enum PCMChannels *)dsound_default_channel_layout[iChannels - 1];
- }
-
- CStdString deviceString, device;
- if (encoded)
- {
-#if defined(_LINUX) && !defined(__APPLE__)
- deviceString = g_guiSettings.GetString("audiooutput.passthroughdevice");
- if (deviceString.Equals("custom"))
- deviceString = g_guiSettings.GetString("audiooutput.custompassthrough");
-#else
- // osx/win platforms do not have an "audiooutput.passthroughdevice" setting but can do passthrough
- deviceString = g_guiSettings.GetString("audiooutput.audiodevice");
-#endif
- }
- else
- {
- deviceString = g_guiSettings.GetString("audiooutput.audiodevice");
- if (deviceString.Equals("custom"))
- deviceString = g_guiSettings.GetString("audiooutput.customdevice");
- }
- int iPos = deviceString.Find(":");
- if (iPos > 0)
- {
- audioSink = CreateFromUri(deviceString.Left(iPos), renderer);
- if (audioSink)
- {
- device = deviceString.Right(deviceString.length() - iPos - 1);
- ReturnOnValidInitialize(renderer.c_str());
-
-#ifdef _WIN32
- //If WASAPI failed try DirectSound.
- if(deviceString.Left(iPos).Equals("wasapi"))
- {
- audioSink = CreateFromUri("directsound", renderer);
- ReturnOnValidInitialize(renderer.c_str());
- }
-#endif
-
- CreateAndReturnOnValidInitialize(CNullDirectSound);
- /* should never get here */
- assert(false);
- }
- }
- CLog::Log(LOGINFO, "AudioRendererFactory: %s not a explicit device, trying to autodetect.", device.c_str());
-
- device = deviceString;
-
-/* First pass creation */
-#ifdef HAS_PULSEAUDIO
- CreateAndReturnOnValidInitialize(CPulseAudioDirectSound);
-#endif
-
-/* incase none in the first pass was able to be created, fall back to os specific */
-#ifdef WIN32
- CreateAndReturnOnValidInitialize(CWin32DirectSound);
-#endif
-#if defined(__APPLE__)
- #if defined(__arm__)
- CreateAndReturnOnValidInitialize(CIOSAudioRenderer);
- #else
- CreateAndReturnOnValidInitialize(CCoreAudioRenderer);
- #endif
-#elif defined(USE_ALSA)
- CreateAndReturnOnValidInitialize(CALSADirectSound);
-#endif
-
- CreateAndReturnOnValidInitialize(CNullDirectSound);
- /* should never get here */
- assert(false);
- return NULL;
-}
-
-void CAudioRendererFactory::EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough)
-{
-#ifdef HAS_PULSEAUDIO
- CPulseAudioDirectSound::EnumerateAudioSinks(vAudioSinks, passthrough);
-#endif
-
-#ifdef WIN32
- CWin32DirectSound::EnumerateAudioSinks(vAudioSinks, passthrough);
- CWin32WASAPI::EnumerateAudioSinks(vAudioSinks, passthrough);
-#endif
-
-#if defined(__APPLE__)
- #if !defined(__arm__)
- CCoreAudioRenderer::EnumerateAudioSinks(vAudioSinks, passthrough);
- #endif
-#elif defined(USE_ALSA)
- CALSADirectSound::EnumerateAudioSinks(vAudioSinks, passthrough);
-#endif
-}
-
-IAudioRenderer *CAudioRendererFactory::CreateFromUri(const CStdString &soundsystem, CStdString &renderer)
-{
-#ifdef HAS_PULSEAUDIO
- if (soundsystem.Equals("pulse"))
- ReturnNewRenderer(CPulseAudioDirectSound);
-#endif
-
-#ifdef WIN32
- if (soundsystem.Equals("wasapi"))
- ReturnNewRenderer(CWin32WASAPI)
- else if (soundsystem.Equals("directsound"))
- ReturnNewRenderer(CWin32DirectSound);
-#endif
-
-#if defined(__APPLE__)
- #if defined(__arm__)
- if (soundsystem.Equals("ioscoreaudio"))
- ReturnNewRenderer(CIOSAudioRenderer);
- #else
- if (soundsystem.Equals("coreaudio"))
- ReturnNewRenderer(CCoreAudioRenderer);
- #endif
-#elif defined(USE_ALSA)
- if (soundsystem.Equals("alsa"))
- ReturnNewRenderer(CALSADirectSound);
-#endif
-
- if (soundsystem.Equals("null"))
- ReturnNewRenderer(CNullDirectSound);
-
- return NULL;
-}
diff --git a/xbmc/cores/AudioRenderers/CoreAudioRenderer.cpp b/xbmc/cores/AudioRenderers/CoreAudioRenderer.cpp
deleted file mode 100644
index e850e25313..0000000000
--- a/xbmc/cores/AudioRenderers/CoreAudioRenderer.cpp
+++ /dev/null
@@ -1,1650 +0,0 @@
-#ifdef __APPLE__
-/*
- * Copyright (C) 2005-2011 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#if !defined(__arm__)
-#include "threads/SystemClock.h"
-#include <CoreServices/CoreServices.h>
-
-#include "CoreAudioRenderer.h"
-#include "Application.h"
-#include "guilib/AudioContext.h"
-#include "osx/CocoaInterface.h"
-#include "settings/GUISettings.h"
-#include "settings/Settings.h"
-#include "settings/AdvancedSettings.h"
-#include "threads/Atomics.h"
-#include "windowing/WindowingFactory.h"
-#include "utils/log.h"
-#include "utils/SystemInfo.h"
-#include "utils/TimeUtils.h"
-
-const AudioChannelLabel g_LabelMap[] =
-{
- kAudioChannelLabel_Left, // PCM_FRONT_LEFT,
- kAudioChannelLabel_Right, // PCM_FRONT_RIGHT,
- kAudioChannelLabel_Center, // PCM_FRONT_CENTER,
- kAudioChannelLabel_LFEScreen, // PCM_LOW_FREQUENCY,
- kAudioChannelLabel_LeftSurroundDirect, // PCM_BACK_LEFT, *** This is incorrect, but has been changed to match dvdplayer
- kAudioChannelLabel_RightSurroundDirect, // PCM_BACK_RIGHT, *** This is incorrect, but has been changed to match dvdplayer
- kAudioChannelLabel_LeftCenter, // PCM_FRONT_LEFT_OF_CENTER,
- kAudioChannelLabel_RightCenter, // PCM_FRONT_RIGHT_OF_CENTER,
- kAudioChannelLabel_CenterSurround, // PCM_BACK_CENTER,
- kAudioChannelLabel_LeftSurround, // PCM_SIDE_LEFT, *** This is incorrect, but has been changed to match dvdplayer
- kAudioChannelLabel_RightSurround, // PCM_SIDE_RIGHT, *** This is incorrect, but has been changed to match dvdplayer
- kAudioChannelLabel_VerticalHeightLeft, // PCM_TOP_FRONT_LEFT,
- kAudioChannelLabel_VerticalHeightRight, // PCM_TOP_FRONT_RIGHT,
- kAudioChannelLabel_VerticalHeightCenter, // PCM_TOP_FRONT_CENTER,
- kAudioChannelLabel_TopCenterSurround, // PCM_TOP_CENTER,
- kAudioChannelLabel_TopBackLeft, // PCM_TOP_BACK_LEFT,
- kAudioChannelLabel_TopBackRight, // PCM_TOP_BACK_RIGHT,
- kAudioChannelLabel_TopBackCenter // PCM_TOP_BACK_CENTER
-};
-
-#define MAX_AUDIO_CHANNEL_LABEL kAudioChannelLabel_CenterSurroundDirect
-
-const AudioChannelLayoutTag g_LayoutMap[] =
-{
- kAudioChannelLayoutTag_Stereo, // PCM_LAYOUT_2_0 = 0,
- kAudioChannelLayoutTag_DVD_4, // PCM_LAYOUT_2_1,
- kAudioChannelLayoutTag_MPEG_3_0_A, // PCM_LAYOUT_3_0,
- kAudioChannelLayoutTag_DVD_10, // PCM_LAYOUT_3_1,
- kAudioChannelLayoutTag_DVD_3, // PCM_LAYOUT_4_0,
- kAudioChannelLayoutTag_DVD_6, // PCM_LAYOUT_4_1,
- kAudioChannelLayoutTag_MPEG_5_0_A, // PCM_LAYOUT_5_0,
- kAudioChannelLayoutTag_MPEG_5_1_A, // PCM_LAYOUT_5_1,
- kAudioChannelLayoutTag_AudioUnit_7_0, // PCM_LAYOUT_7_0, ** This layout may be incorrect...no content to test **
- kAudioChannelLayoutTag_MPEG_7_1_A, // PCM_LAYOUT_7_1
-};
-
-/////////////////////////////////////////////////////////////////////////////////
-// CAtomicAllocator: Wrapper class for lf_heap.
-////////////////////////////////////////////////////////////////////////////////
-CAtomicAllocator::CAtomicAllocator(size_t blockSize) :
- m_BlockSize(blockSize)
-{
- lf_heap_init(&m_Heap, blockSize);
-}
-
-CAtomicAllocator::~CAtomicAllocator()
-{
- lf_heap_deinit(&m_Heap);
-}
-
-void* CAtomicAllocator::Alloc()
-{
- return lf_heap_alloc(&m_Heap);
-}
-
-void CAtomicAllocator::Free(void* p)
-{
- lf_heap_free(&m_Heap, p);
-}
-
-size_t CAtomicAllocator::GetBlockSize()
-{
- return m_BlockSize;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////
-// CSliceQueue: Lock-free queue for audio_slices
-//////////////////////////////////////////////////////////////////////////////////////
-CSliceQueue::CSliceQueue(size_t sliceSize) :
- m_TotalBytes(0),
- m_pPartialSlice(NULL),
- m_RemainderSize(0)
-{
- m_pAllocator = new CAtomicAllocator(sliceSize + offsetof(audio_slice, data));
- lf_queue_init(&m_Queue);
-}
-
-CSliceQueue::~CSliceQueue()
-{
- Clear();
- lf_queue_deinit(&m_Queue);
- delete m_pAllocator;
-}
-
-// NOTE: The value of m_TotalBytes is only guaranteed to be accurate to within one slice,
-// but is sufficient. This means that it may at times be one slice too high or one slice
-// tool low, but will never be < 0.
-void CSliceQueue::Push(audio_slice* pSlice)
-{
- if (pSlice)
- {
- lf_queue_enqueue(&m_Queue, pSlice);
- AtomicAdd((long*)&m_TotalBytes, (long)pSlice->header.data_len);
- }
-}
-
-audio_slice* CSliceQueue::Pop()
-{
- audio_slice* pSlice = (audio_slice*)lf_queue_dequeue(&m_Queue);
- if (pSlice)
- AtomicSubtract((long*)&m_TotalBytes, (long)pSlice->header.data_len);
- return pSlice;
-}
-
-// *** NOTE: AddData and GetData are thread-safe for multiple writers and one reader
-size_t CSliceQueue::AddData(void* pBuf, size_t bufLen)
-{
- size_t bytesLeft = bufLen;
- unsigned char* pData = (unsigned char*)pBuf;
- if (pBuf && bufLen)
- {
- while (bytesLeft)
- {
- // TODO: find a way to ensure success...
- audio_slice* pSlice = (audio_slice*)m_pAllocator->Alloc(); // Allocation should never fail. The heap grows automatically.
- if (!pSlice)
- {
- CLog::Log(LOGDEBUG, "CSliceQueue::AddData: Failed to allocate new slice");
- return bufLen - bytesLeft;
- }
- pSlice->header.data_len = m_pAllocator->GetBlockSize() - offsetof(audio_slice, data);
- if (pSlice->header.data_len >= bytesLeft) // plenty of room. move it all in.
- {
- memcpy(pSlice->get_data(), pData, bytesLeft);
- pSlice->header.data_len = bytesLeft; // Adjust the reported size of the container
- }
- else
- {
- memcpy(pSlice->get_data(), pData, pSlice->header.data_len); // Copy all we can into this slice. More to come.
- }
- bytesLeft -= pSlice->header.data_len;
- pData += pSlice->header.data_len;
- Push(pSlice);
- }
- }
- return bufLen - bytesLeft;
-}
-
-size_t CSliceQueue::GetData(void* pBuf, size_t bufLen)
-{
- if (!pBuf || !bufLen)
- return 0;
-
- size_t remainder = 0;
- audio_slice* pNext = NULL;
- size_t bytesUsed = 0;
-
- // See if we can fill the request out of our partial slice (if there is one)
- if (m_RemainderSize >= bufLen)
- {
- memcpy(pBuf, m_pPartialSlice->get_data() + m_pPartialSlice->header.data_len - m_RemainderSize , bufLen);
- m_RemainderSize -= bufLen;
- bytesUsed = bufLen;
- }
- else // Pull what we can from the partial slice and get the rest from complete slices
- {
- // Take what we can from the partial slice (if there is one)
- if (m_RemainderSize)
- {
- memcpy(pBuf, m_pPartialSlice->get_data() + m_pPartialSlice->header.data_len - m_RemainderSize , m_RemainderSize);
- bytesUsed += m_RemainderSize;
- m_RemainderSize = 0;
- }
-
- // Pull slices from the fifo until we have enough data
- do // TODO: The efficiency of this loop can be improved (a lot I imagine)
- {
- pNext = Pop();
- if (!pNext)
- break;
- size_t nextLen = pNext->header.data_len;
- if (bytesUsed + nextLen > bufLen) // Check for a partial slice
- remainder = nextLen - (bufLen - bytesUsed);
- memcpy((BYTE*)pBuf + bytesUsed, pNext->get_data(), nextLen - remainder);
- bytesUsed += (nextLen - remainder); // Increment output size (remainder will be captured separately)
- if (!remainder)
- m_pAllocator->Free(pNext); // Free the copied slice
- } while (bytesUsed < bufLen);
- }
-
- // Clean up the previous partial slice
- if (!m_RemainderSize && m_pPartialSlice)
- {
- m_pAllocator->Free(m_pPartialSlice);
- m_pPartialSlice = NULL;
- }
-
- // Save off the new partial slice (if there is one)
- if (remainder)
- {
- m_pPartialSlice = pNext;
- m_RemainderSize = remainder;
- }
-
- return bytesUsed;
-}
-
-size_t CSliceQueue::GetTotalBytes()
-{
- return m_TotalBytes + m_RemainderSize;
-}
-
-void CSliceQueue::Clear()
-{
- while (audio_slice* pSlice = Pop())
- m_pAllocator->Free(pSlice);
- m_pAllocator->Free(m_pPartialSlice);
- m_pPartialSlice = NULL;
- m_RemainderSize = 0;
-}
-
-//***********************************************************************************************
-// Performance Monitoring Helper Class
-//***********************************************************************************************
-CCoreAudioPerformance::CCoreAudioPerformance() :
- m_TotalBytesIn(0),
- m_TotalBytesOut(0),
- m_ExpectedBytesPerSec(0),
- m_ActualBytesPerSec(0),
- m_Flags(0),
- m_WatchdogEnable(false),
- m_WatchdogInterval(0),
- m_LastWatchdogCheck(0),
- m_LastWatchdogBytesIn(0),
- m_LastWatchdogBytesOut(0),
- m_WatchdogBitrateSensitivity(0.99f),
- m_WatchdogPreroll(0)
-{
-
-}
-
-CCoreAudioPerformance::~CCoreAudioPerformance()
-{
-
-}
-
-void CCoreAudioPerformance::Init(UInt32 expectedBytesPerSec, UInt32 watchdogInterval /*= 1000*/, UInt32 flags /*=0*/)
-{
- m_ExpectedBytesPerSec = expectedBytesPerSec;
- m_WatchdogInterval = watchdogInterval;
- m_Flags = flags;
-}
-
-void CCoreAudioPerformance::ReportData(UInt32 bytesIn, UInt32 bytesOut)
-{
- m_TotalBytesIn += bytesIn;
- m_TotalBytesOut += bytesOut;
-
- if (!m_WatchdogEnable)
- return;
-
- // Perform watchdog funtions
- UInt32 time = XbmcThreads::SystemClockMillis();
- if (!m_LastWatchdogCheck)
- m_LastWatchdogCheck = time;
- UInt32 deltaTime = time - m_LastWatchdogCheck;
- m_ActualBytesPerSec = (m_TotalBytesOut - m_LastWatchdogBytesOut) / ((float)deltaTime/1000.0f);
- if (deltaTime > m_WatchdogInterval)
- {
- if (m_TotalBytesOut > m_WatchdogPreroll) // Allow m_WatchdogPreroll bytes to go by unmonitored
- {
- // Check outgoing bitrate
- if (m_ActualBytesPerSec < m_WatchdogBitrateSensitivity * m_ExpectedBytesPerSec)
- CLog::Log(LOGWARNING, "CCoreAudioPerformance: Outgoing bitrate is lagging. Target: %lu, Actual: %lu. deltaTime was %lu", m_ExpectedBytesPerSec, m_ActualBytesPerSec, deltaTime);
- }
- m_LastWatchdogCheck = time;
- m_LastWatchdogBytesIn = m_TotalBytesIn;
- m_LastWatchdogBytesOut = m_TotalBytesOut;
- }
-}
-
-void CCoreAudioPerformance::EnableWatchdog(bool enable)
-{
- if (!m_WatchdogEnable && enable)
- Reset();
- m_WatchdogEnable = enable;
-}
-
-void CCoreAudioPerformance::SetPreroll(UInt32 bytes)
-{
- m_WatchdogPreroll = bytes;
-}
-
-void CCoreAudioPerformance::SetPreroll(float seconds)
-{
- SetPreroll((UInt32)(seconds * m_ExpectedBytesPerSec));
-}
-
-void CCoreAudioPerformance::Reset()
-{
- m_TotalBytesIn = 0;
- m_TotalBytesOut = 0;
- m_ActualBytesPerSec = 0;
- m_LastWatchdogCheck = 0;
- m_LastWatchdogBytesIn = 0;
- m_LastWatchdogBytesOut = 0;
-}
-
-//***********************************************************************************************
-// Surround Up/Down Mapping Class
-//***********************************************************************************************
-struct ChannelPatch
-{
- AudioChannelLabel label; // Target Channel
- Float32 coeff; // Output level
-};
-
-// TODO: There is not a lot of logic behind these mapping coefficients
-// Routings for Explicit Mapping. These are in priority order.
-// g_ChannelRoutings[channel][routing][patch]
-// These are currently all down-mix routings
-#define MAX_CHANNELS sizeof(g_LabelMap)/sizeof(AudioChannelLabel)
-#define NO_ROUTINGS {{{kAudioChannelLabel_Unknown, 0.0f}}}
-ChannelPatch g_ChannelRoutings[MAX_AUDIO_CHANNEL_LABEL+1][MAX_CHANNELS][MAX_CHANNELS] =
-{
- NO_ROUTINGS, // kAudioChannelLabel_Unknown (0)
- // kAudioChannelLabel_Left (1)
- {
- {{kAudioChannelLabel_Center, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_Right (2)
- {
- {{kAudioChannelLabel_Center, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_Center (3)
- {
- {{kAudioChannelLabel_Left, 0.707f},{kAudioChannelLabel_Right, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_LFEScreen (4)
- {
- {{kAudioChannelLabel_Left, 0.707f},{kAudioChannelLabel_Right, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_LeftSurround (5)
- {
- {{kAudioChannelLabel_LeftSurroundDirect, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_RightSurround (6)
- {
- {{kAudioChannelLabel_RightSurroundDirect, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_LeftCenter (7)
- {
- {{kAudioChannelLabel_Center, 0.707f},{kAudioChannelLabel_Left, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_RightCenter (8)
- {
- {{kAudioChannelLabel_Center, 0.707f},{kAudioChannelLabel_Right, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_CenterSurround (9)
- {
- {{kAudioChannelLabel_LeftSurround, 0.707f},{kAudioChannelLabel_RightSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_LeftSurroundDirect, 0.707f},{kAudioChannelLabel_RightSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 0.707f},{kAudioChannelLabel_Right, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_LeftSurroundDirect (10)
- {
- {{kAudioChannelLabel_LeftSurround, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_RightSurroundDirect (11)
- {
- {{kAudioChannelLabel_RightSurround, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- NO_ROUTINGS, //kAudioChannelLabel_TopCenterSurround (12)
- NO_ROUTINGS, //kAudioChannelLabel_VerticalHeightLeft (13)
- NO_ROUTINGS, //kAudioChannelLabel_VerticalHeightCenter (14)
- NO_ROUTINGS, //kAudioChannelLabel_VerticalHeightRight (15)
- NO_ROUTINGS, //kAudioChannelLabel_TopBackLeft (16)
- NO_ROUTINGS, //kAudioChannelLabel_TopBackCenter (17)
- NO_ROUTINGS, //kAudioChannelLabel_VerticalHeightRight (18)
- NO_ROUTINGS, // INVALID LABEL (19)
- NO_ROUTINGS, // INVALID LABEL (20)
- NO_ROUTINGS, // INVALID LABEL (21)
- NO_ROUTINGS, // INVALID LABEL (22)
- NO_ROUTINGS, // INVALID LABEL (23)
- NO_ROUTINGS, // INVALID LABEL (24)
- NO_ROUTINGS, // INVALID LABEL (25)
- NO_ROUTINGS, // INVALID LABEL (26)
- NO_ROUTINGS, // INVALID LABEL (27)
- NO_ROUTINGS, // INVALID LABEL (28)
- NO_ROUTINGS, // INVALID LABEL (29)
- NO_ROUTINGS, // INVALID LABEL (30)
- NO_ROUTINGS, // INVALID LABEL (31)
- NO_ROUTINGS, // INVALID LABEL (32)
- // kAudioChannelLabel_RearSurroundLeft (33)
- {
- {{kAudioChannelLabel_LeftSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_LeftSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Left, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- // kAudioChannelLabel_RearSurroundRight (34)
- {
- {{kAudioChannelLabel_RightSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_RightSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurround, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_CenterSurroundDirect, 0.707f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Right, 1.0f},{kAudioChannelLabel_Unknown, 0.0f}},
- {{kAudioChannelLabel_Unknown, 0.0f}}
- },
- NO_ROUTINGS, // kAudioChannelLabel_LeftWide (35)
- NO_ROUTINGS, // kAudioChannelLabel_RightWide (36)
- NO_ROUTINGS, // kAudioChannelLabel_LFE2 (37)
- NO_ROUTINGS, // kAudioChannelLabel_LeftTotal (38)
- NO_ROUTINGS, // kAudioChannelLabel_RightTotal (39)
- NO_ROUTINGS, // kAudioChannelLabel_HearingImpaired (40)
- NO_ROUTINGS, // kAudioChannelLabel_Narration (41)
- NO_ROUTINGS, // kAudioChannelLabel_Mono (42)
- NO_ROUTINGS, // kAudioChannelLabel_DialogCentricMix (43)
- NO_ROUTINGS // kAudioChannelLabel_CenterSurroundDirect (44)
-};
-
-CCoreAudioMixMap::CCoreAudioMixMap() :
- m_isValid(false)
-{
- m_pMap = (Float32*)calloc(sizeof(Float32), 2);
-}
-
-CCoreAudioMixMap::CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout, bool forceExplicit/*=false*/) :
- m_isValid(false)
-{
- Rebuild(inLayout, outLayout, forceExplicit);
-}
-
-CCoreAudioMixMap::~CCoreAudioMixMap()
-{
- if (m_pMap)
- {
- free(m_pMap);
- }
-}
-
-void CCoreAudioMixMap::Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout, bool forceExplicit/*=false*/)
-{
- // map[in][out] = mix-level of input_channel[in] into output_channel[out]
-
- if (m_pMap)
- {
- free(m_pMap);
- m_pMap = NULL;
- }
-
- m_inChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(inLayout);
- m_outChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(outLayout);
-
- // If the caller allows it, try to find a 'well-known' matrix
- if (!forceExplicit)
- {
- const AudioChannelLayout* layouts[] = {&inLayout, &outLayout};
- UInt32 propSize = 0;
- OSStatus ret = AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize);
- m_pMap = (Float32*)calloc(1,propSize);
- ret = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize, m_pMap);
- if (!ret)
- {
- m_isValid = true;
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::Rebuild: Using pre-defined mixing matrix.");
- return; // Nothing else to do...a map already exists
- }
-
- // No predefined mixing matrix was available. Going to have to build it manually
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::Rebuild: Unable to locate pre-defined mixing matrix. Trying to build one explicitly...");
- }
- else
- CLog::Log(LOGINFO, "CCoreAudioMixMap::Rebuild: Building explicit mixing matrix [forceExplicit=true]");
-
- // Try to build a mixing matrix from scratch
- m_isValid = BuildExplicit(inLayout, outLayout);
-}
-
-bool CCoreAudioMixMap::BuildExplicit(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout)
-{
- // Initialize map
- // map[in][out] = mix-level of input_channel[in] into output_channel[out]
- m_pMap = (Float32*)calloc(sizeof(Float32), inLayout.mNumberChannelDescriptions * outLayout.mNumberChannelDescriptions);
- int mappedChannels = 0;
-
- // Initialize array of output channel locations
- int outPos[MAX_AUDIO_CHANNEL_LABEL + 1];
- for (UInt32 i = 0; i < (MAX_AUDIO_CHANNEL_LABEL + 1); i++)
- outPos[i] = -1;
-
- // Map output channel order to output layout
- for (UInt32 channel = 0; channel < outLayout.mNumberChannelDescriptions; channel++)
- outPos[outLayout.mChannelDescriptions[channel].mChannelLabel] = channel;
-
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::BuildExplicit: Building mixing matrix [%d input channels, %d output channels]", inLayout.mNumberChannelDescriptions, outLayout.mNumberChannelDescriptions);
-
- // For each input channel, decide how to route/mix it
- for (UInt32 channel = 0; channel < inLayout.mNumberChannelDescriptions; channel++)
- {
- AudioChannelDescription* pDesc = &inLayout.mChannelDescriptions[channel];
-
- if (pDesc->mChannelLabel > MAX_AUDIO_CHANNEL_LABEL)
- {
- CLog::Log(LOGINFO, "CCoreAudioMixMap::BuildExplicit:\tUnrecognized input channel encountered (label=%d) - skipping.", pDesc->mChannelLabel);
- continue;
- }
-
- // Does the channel exist in the output?
- int outIndex = outPos[pDesc->mChannelLabel];
- if (outIndex > -1) // If so, just pass it through
- {
- // map[in][out] = map[in * outCount + out]
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::BuildExplicit:\tin[%d] -> out[%d] @ %0.02f", channel, outIndex, 1.0f);
- m_pMap[channel * outLayout.mNumberChannelDescriptions + outIndex] = 1.0f;
- mappedChannels++;
- }
- else // The input channel does not exist in the output layout. Decide where to route it...
- {
- // Loop through the pre-defined routings strategies for this channel. Use the first compatible one we find
- for (UInt32 r = 0; r < MAX_CHANNELS; r++)
- {
- // If the first patch's label is kAudioChannelLabel_Unknown, this is the routing list terminator
- if (g_ChannelRoutings[pDesc->mChannelLabel][r][0].label == kAudioChannelLabel_Unknown)
- break; // No dice...
-
- // Check each patch in this routing to see if it is possible. If it is not, give up on this routing
- bool validRouting = false;
- for (UInt32 p = 0; ((p < MAX_CHANNELS) && !validRouting); p++)
- {
- ChannelPatch* patch = &g_ChannelRoutings[pDesc->mChannelLabel][r][p];
- if (patch->label == kAudioChannelLabel_Unknown) // End of this routing
- validRouting = true; // Success...this routing should work.
- else if (outPos[patch->label] == -1)
- break; // The specified output channel is not available. Invalidate the whole routing
- }
-
- // If this is a valid routing (all output channels are available), apply it...
- if (validRouting)
- {
- for (UInt32 p = 0; p < MAX_CHANNELS; p++)
- {
- ChannelPatch* patch = &g_ChannelRoutings[pDesc->mChannelLabel][r][p];
- if (patch->label == kAudioChannelLabel_Unknown) // List terminator
- break;
-
- int outIndex = outPos[patch->label];
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::BuildExplicit:\tin[%d] -> out[%d] @ %0.02f", channel, outIndex, patch->coeff);
- m_pMap[channel * outLayout.mNumberChannelDescriptions + outIndex] = patch->coeff;
- mappedChannels++;
- }
- break;
- }
- }
- }
- }
- if (!mappedChannels)
- {
- CLog::Log(LOGINFO, "CCoreAudioMixMap::BuildExplicit: No valid patches found.");
- return false;
- }
-
- CLog::Log(LOGINFO, "CCoreAudioMixMap::BuildExplicit: Completed explicit channel map.");
- return true;
-}
-
-//***********************************************************************************************
-// Contruction/Destruction
-//***********************************************************************************************
-CCoreAudioRenderer::CCoreAudioRenderer() :
- m_Pause(false),
- m_ChunkLen(0),
- m_MaxCacheLen(0),
- m_AvgBytesPerSec(0),
- m_CurrentVolume(0),
- m_Initialized(false),
- m_Passthrough(false),
- m_EnableVolumeControl(true),
- m_OutputBufferIndex(0),
- m_pCache(NULL),
- m_DoRunout(0)
-{
- m_init_state.reinit = false;
- m_init_state.channelMap = NULL;
-
- SInt32 major, minor;
- Gestalt(gestaltSystemVersionMajor, &major);
- Gestalt(gestaltSystemVersionMinor, &minor);
-
- // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
- // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
- // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
- // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
- if (major == 10 && minor >=6)
- {
- CFRunLoopRef theRunLoop = NULL;
- AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
- if (theError != noErr)
- {
- CLog::Log(LOGERROR, "CoreAudioRenderer::constructor: kAudioHardwarePropertyRunLoop error.");
- }
- }
-#ifdef VERBOSE_DEBUG
- AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, HardwareListenerProc, this);
- AudioHardwareAddPropertyListener(kAudioHardwarePropertyIsInitingOrExiting, HardwareListenerProc, this);
- AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, HardwareListenerProc, this);
-#endif
- g_Windowing.Register(this);
-}
-
-CCoreAudioRenderer::~CCoreAudioRenderer()
-{
-#ifdef VERBOSE_DEBUG
- AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, HardwareListenerProc);
- AudioHardwareRemovePropertyListener(kAudioHardwarePropertyIsInitingOrExiting, HardwareListenerProc);
- AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultOutputDevice, HardwareListenerProc);
-#endif
- g_Windowing.Unregister(this);
- Deinitialize();
- delete m_init_state.channelMap;
-}
-
-//***********************************************************************************************
-// Initialization
-//***********************************************************************************************
-
-// Macro to use for sanity checks in each method. 'x' is the return value if not initialized
-#define VERIFY_INIT(x) \
-if (!m_Initialized) \
-return x
-
-bool CCoreAudioRenderer::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic /*Useless Legacy Parameter*/, EEncoded bPassthrough)
-{
- CSingleLock lock(m_init_csection);
-
- // Have to clean house before we start again.
- // TODO: Should we return failure instead?
- if (m_Initialized)
- Deinitialize();
-
- m_init_state.device = device;
- m_init_state.iChannels = iChannels;
- // save the old and delete after clone incase we are reinit'ing.
- enum PCMChannels *old_channelMap = m_init_state.channelMap;
- m_init_state.channelMap = NULL;
- if (channelMap)
- {
- m_init_state.channelMap = new PCMChannels[m_init_state.iChannels];
- for (int i = 0; i < iChannels; i++)
- m_init_state.channelMap[i] = channelMap[i];
- }
- delete [] old_channelMap;
- m_init_state.uiSamplesPerSec = uiSamplesPerSec;
- m_init_state.uiBitsPerSample = uiBitsPerSample;
- m_init_state.bResample = bResample;
- m_init_state.bIsMusic = bIsMusic;
- m_init_state.bPassthrough = bPassthrough;
- m_init_state.pCallback = pCallback;
-
- // Reset all the devices to a default 'non-hog' and mixable format.
- // If we don't do this we may be unable to find the Default Output device.
- // (e.g. if we crashed last time leaving it stuck in AC3/DTS/SPDIF mode)
- Cocoa_ResetAudioDevices();
-
- if(m_init_state.bPassthrough)
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL);
- else
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
-
- // TODO: If debugging, output information about all devices/streams
-
- // Attempt to find the configured output device
- AudioDeviceID outputDevice = CCoreAudioHardware::FindAudioDevice(g_guiSettings.GetString("audiooutput.audiodevice"));
- // Fall back to the default device if no match is found
- if (!outputDevice)
- {
- CLog::Log(LOGWARNING, "CoreAudioRenderer::Initialize: "
- "Unable to locate configured device, falling-back to the system default.");
- outputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
- // Not a lot to be done with no device.
- // TODO: Should we just grab the first existing device?
- if (!outputDevice)
- return false;
- }
-
-#ifdef VERBOSE_DEBUG
- AudioDeviceAddPropertyListener(outputDevice, 0, false, kAudioDevicePropertyDeviceIsAlive, DeviceListenerProc, this);
- AudioDeviceAddPropertyListener(outputDevice, 0, false, kAudioDevicePropertyDeviceIsRunning, DeviceListenerProc, this);
- AudioDeviceAddPropertyListener(outputDevice, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc, this);
-#endif
-
- // TODO: Determine if the device is in-use/locked by another process.
-
- // Attach our output object to the device
- m_AudioDevice.Open(outputDevice);
-
- // If this is a passthrough (AC3/DTS) stream, attempt to handle it natively
- if (m_init_state.bPassthrough)
- {
- m_Passthrough = InitializeEncoded(outputDevice, m_init_state.uiSamplesPerSec);
- // TODO: wait for audio device startup
- Sleep(200);
- }
-
- // If this is a PCM stream, or we failed to handle a passthrough stream natively,
- // prepare the standard interleaved PCM interface
- if (!m_Passthrough)
- {
- // Create the Output AudioUnit Component
- if (!m_AUOutput.Open(kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple))
- return false;
-
- // Hook the Ouput AudioUnit to the selected device
- if (!m_AUOutput.SetCurrentDevice(outputDevice))
- return false;
-
- // If we are here and this is a passthrough stream, native handling failed.
- // Try to handle it as IEC61937 data over straight PCM (DD-Wav)
- bool configured = false;
- if (m_init_state.bPassthrough)
- {
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::Initialize: "
- "No suitable AC3 output format found. Attempting DD-Wav.");
- configured = InitializePCMEncoded(m_init_state.uiSamplesPerSec);
- // TODO: wait for audio device startup
- Sleep(250);
- }
- else
- {
- // Standard PCM data
- configured = InitializePCM(m_init_state.iChannels, m_init_state.uiSamplesPerSec, m_init_state.uiBitsPerSample, m_init_state.channelMap);
- // TODO: wait for audio device startup
- Sleep(250);
- }
-
- // No suitable output format was able to be configured
- if (!configured)
- return false;
-
- // Configure the maximum number of frames that the AudioUnit will ask to process at one time.
- // If this is not called, there is no guarantee that the callback will ever be called.
- // Size of the output buffer, in Frames
- UInt32 bufferFrames = m_AUOutput.GetBufferFrameSize();
- if (!m_AUOutput.SetMaxFramesPerSlice(bufferFrames))
- return false;
-
- // This is the minimum amount of data that we will accept from a client
- m_ChunkLen = bufferFrames * m_BytesPerFrame;
-
- // Setup the callback function that the AudioUnit will use to request data
- ICoreAudioSource* pSource = this;
- if (m_init_state.bIsMusic)
- {
- // A mixer is in-use
- if (m_MixerUnit.IsInitialized())
- pSource = &m_MixerUnit;
- }
- else
- {
- // A mixer+compressor are in-use
- if (m_AUCompressor.IsInitialized())
- pSource = &m_AUCompressor;
- }
- if (!m_AUOutput.SetInputSource(pSource))
- return false;
-
- // Initialize the Output AudioUnit
- if (!m_AUOutput.Initialize())
- return false;
-
- // Log some information about the stream
- AudioStreamBasicDescription inputDesc_end, outputDesc_end;
- CStdString formatString;
- m_AUOutput.GetInputFormat(&inputDesc_end);
- m_AUOutput.GetOutputFormat(&outputDesc_end);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::Initialize: Input Stream Format %s",
- StreamDescriptionToString(inputDesc_end, formatString));
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::Initialize: Output Stream Format %s",
- StreamDescriptionToString(outputDesc_end, formatString));
- }
-
- m_NumLatencyFrames = m_AudioDevice.GetNumLatencyFrames();
- // Set the max cache size to 1 second of data. TODO: Make this more intelligent
- m_MaxCacheLen = m_AvgBytesPerSec;
- // Suspend rendering. We will start once we have some data.
- m_Pause = true;
- // Initialize our incoming data cache
- if (!m_pCache)
- m_pCache = new CSliceQueue(m_ChunkLen);
-#ifdef _DEBUG
- // Set up the performance monitor
- m_PerfMon.Init(m_AvgBytesPerSec, 1000, CCoreAudioPerformance::FlagDefault);
- // Disable underrun detection for the first 2 seconds (after start and after resume)
- m_PerfMon.SetPreroll(2.0f);
-#endif
- m_init_state.reinit = false;
- m_Initialized = true;
- m_DoRunout = 0;
-
- SetCurrentVolume(g_settings.m_nVolumeLevel);
-
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::Initialize: "
- "Renderer Configuration - Chunk Len: %u, Max Cache: %lu (%0.0fms).",
- m_ChunkLen, m_MaxCacheLen, 1000.0 *(float)m_MaxCacheLen/(float)m_AvgBytesPerSec);
- CLog::Log(LOGINFO, "CoreAudioRenderer::Initialize: Successfully configured audio output.");
-
- return true;
-}
-
-bool CCoreAudioRenderer::Deinitialize()
-{
- VERIFY_INIT(true); // Not really a failure if we weren't initialized
-
-#ifdef VERBOSE_DEBUG
- AudioDeviceRemovePropertyListener(m_AudioDevice.GetId(), 0, false, kAudioDevicePropertyDeviceIsAlive, DeviceListenerProc);
- AudioDeviceRemovePropertyListener(m_AudioDevice.GetId(), 0, false, kAudioDevicePropertyDeviceIsRunning, DeviceListenerProc);
- AudioDeviceRemovePropertyListener(m_AudioDevice.GetId(), 0, false, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc);
-#endif
-
- // Stop rendering
- Stop();
- // Reset our state but do not diddle internal vars if we are re-init'ing
- if (!m_init_state.reinit)
- {
- m_ChunkLen = 0;
- m_MaxCacheLen = 0;
- m_AvgBytesPerSec = 0;
- }
- if (m_Passthrough)
- m_AudioDevice.RemoveIOProc();
- m_AUCompressor.Close();
- m_MixerUnit.Close();
- m_AUConverter.Close();
- m_AUOutput.Close();
- m_OutputStream.Close();
- Sleep(10);
- m_AudioDevice.Close();
- if (!m_init_state.reinit)
- {
- // do not blow the cache if we are just re-init'ing
- delete m_pCache;
- m_pCache = NULL;
- }
- m_Initialized = false;
- m_DoRunout = 0;
- m_EnableVolumeControl = true;
-
- // do not diddle with active device if we are re-init'ing
- if (!m_init_state.reinit)
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
-
- CLog::Log(LOGINFO, "CoreAudioRenderer::Deinitialize: Renderer has been shut down.");
-
- return true;
-}
-
-bool CCoreAudioRenderer::Reinitialize()
-{
- CSingleLock lock(m_init_csection);
-
- m_init_state.reinit = true;
- return Initialize(m_init_state.pCallback,
- m_init_state.device,
- m_init_state.iChannels,
- m_init_state.channelMap,
- m_init_state.uiSamplesPerSec,
- m_init_state.uiBitsPerSample,
- m_init_state.bResample,
- m_init_state.bIsMusic,
- m_init_state.bPassthrough);
-}
-//***********************************************************************************************
-// Transport control methods
-//***********************************************************************************************
-bool CCoreAudioRenderer::Pause()
-{
- VERIFY_INIT(false);
-
- if (!m_Pause)
- {
- //CLog::Log(LOGDEBUG, "CoreAudioRenderer::Pause: Pausing Playback.");
- if (m_Passthrough)
- m_AudioDevice.Stop();
- else
- m_AUOutput.Stop();
- m_Pause = true;
- }
-#ifdef _DEBUG
- m_PerfMon.EnableWatchdog(false); // Stop monitoring, we're paused
-#endif
- return true;
-}
-
-bool CCoreAudioRenderer::Resume()
-{
- VERIFY_INIT(false);
-
- if (m_Pause)
- {
- //CLog::Log(LOGDEBUG, "CoreAudioRenderer::Resume: Resuming Playback.");
- if (m_Passthrough)
- m_AudioDevice.Start();
- else
- m_AUOutput.Start();
- m_Pause = false;
- }
-#ifdef _DEBUG
- m_PerfMon.EnableWatchdog(true); // Resume monitoring
-#endif
- return true;
-}
-
-bool CCoreAudioRenderer::Stop()
-{
- VERIFY_INIT(false);
-
- if (m_Passthrough)
- m_AudioDevice.Stop();
- else
- m_AUOutput.Stop();
-
- m_Pause = true;
-#ifdef _DEBUG
- m_PerfMon.EnableWatchdog(false);
-#endif
- m_pCache->Clear();
-
- return true;
-}
-
-//***********************************************************************************************
-// Volume control methods
-//***********************************************************************************************
-LONG CCoreAudioRenderer::GetCurrentVolume() const
-{
- return m_CurrentVolume;
-}
-
-void CCoreAudioRenderer::Mute(bool bMute)
-{
- if (bMute)
- SetCurrentVolume(0);
- else
- SetCurrentVolume(m_CurrentVolume);
-}
-
-bool CCoreAudioRenderer::SetCurrentVolume(LONG nVolume)
-{
- VERIFY_INIT(false);
-
- if (m_EnableVolumeControl) // Don't change actual volume for encoded streams
- {
- // Convert milliBels to percent
- Float32 volPct = pow(10.0f, (float)nVolume/2000.0f);
-
- // Try to set the volume. If it fails there is not a lot to be done.
- if (!m_AUOutput.SetCurrentVolume(volPct))
- return false;
- }
- m_CurrentVolume = nVolume; // Store the volume setpoint. We need this to check for 'mute'
- return true;
-}
-
-void CCoreAudioRenderer::SetDynamicRangeCompression(long drc)
-{
- if (m_AUCompressor.IsInitialized())
- m_AUCompressor.SetMasterGain(((float)drc)/100.0f);
-}
-
-//***********************************************************************************************
-// Data management methods
-//***********************************************************************************************
-unsigned int CCoreAudioRenderer::GetSpace()
-{
- VERIFY_INIT(0);
- return m_MaxCacheLen - m_pCache->GetTotalBytes(); // This is just an estimate, since the driver is asynchonously pulling data.
-}
-
-unsigned int CCoreAudioRenderer::AddPackets(const void* data, DWORD len)
-{
- if (!m_pCache)
- return 0;
-
- // Require at least one 'chunk'. This allows us at least some measure of control over efficiency
- if (len < m_ChunkLen || m_pCache->GetTotalBytes() >= m_MaxCacheLen)
- return 0;
-
- unsigned int cacheSpace = GetSpace();
- if (len > cacheSpace)
- return 0; // Wait until we can accept all of it
-
- size_t bytesUsed = m_pCache->AddData((void*)data, len);
-
-#ifdef _DEBUG
- // Update tracking variable
- m_PerfMon.ReportData(bytesUsed, 0);
-#endif
-
- //We have some data. Attempt to resume playback only if not trying to reinit.
- if (!m_init_state.reinit)
- Resume();
-
- // Number of bytes added to cache;
- return bytesUsed;
-}
-
-float CCoreAudioRenderer::GetDelay()
-{
- VERIFY_INIT(0);
- // Calculate the duration of the data in the cache
- float delay = (float)m_pCache->GetTotalBytes()/(float)m_AvgBytesPerSec;
- // TODO: Obtain hardware/os latency for better accuracy
- delay += (float)m_NumLatencyFrames/(float)m_AvgBytesPerSec;
-
- return delay;
-}
-
-float CCoreAudioRenderer::GetCacheTime()
-{
- return GetDelay();
-}
-
-float CCoreAudioRenderer::GetCacheTotal()
-{
- return (float)m_MaxCacheLen / m_AvgBytesPerSec;
-}
-
-unsigned int CCoreAudioRenderer::GetChunkLen()
-{
- return m_ChunkLen;
-}
-
-void CCoreAudioRenderer::WaitCompletion()
-{
- VERIFY_INIT();
-
- // The cache is already empty. There is nothing to wait for.
- if (m_pCache->GetTotalBytes() == 0)
- return;
-
- // Signal that we are waiting
- AtomicIncrement(&m_DoRunout);
- // Signal that a buffer underrun is OK
- m_DoRunout = 1;
- // TODO: Should we pad the wait time to allow for preemption?
- // This is how much time 'should' be in the cache (plus 10ms for preemption hedge)
- UInt32 delay = (UInt32)(GetDelay() * 1000.0f) + 10;
- if (delay)
- {
- // Wait for the callback thread to process the whole cache,
- // but only wait as long as we expect it to take.
- m_RunoutEvent.WaitMSec(delay);
- if (!m_RunoutEvent.WaitMSec(delay))
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::WaitCompletion: "
- "Timed-out waiting for runout. Remaining data will be truncated.");
- }
- }
-
- Stop();
-}
-
-//***********************************************************************************************
-// Rendering Methods
-//***********************************************************************************************
-OSStatus CCoreAudioRenderer::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-OSStatus CCoreAudioRenderer::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- if (!m_Initialized)
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::OnRender: Callback to de/unitialized renderer.");
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = 0;
- }
-
- // Process the request
- // Data length requested, based on the input data format
- UInt32 bytesRequested = m_BytesPerFrame * inNumberFrames;
- UInt32 bytesRead = (UInt32)m_pCache->GetData(ioData->mBuffers[m_OutputBufferIndex].mData, bytesRequested);
- if (bytesRead < bytesRequested)
- {
- // Stop further requests until we have more data.
- // The AddPackets method will resume playback
- Pause();
- // Tell anyone who cares that the cache is empty
- m_RunoutEvent.Set();
- // We were waiting for a runout. This is not an error.
- if (m_DoRunout)
- {
- //CLog::Log(LOGDEBUG, "CCoreAudioRenderer::OnRender: Runout complete");
- m_DoRunout = 0;
- }
- /*
- else
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::OnRender: Buffer underrun.");
- */
- }
- // Hard mute for formats that do not allow standard volume control. Throw away any actual data to keep the stream moving.
- if (!m_EnableVolumeControl && m_CurrentVolume <= VOLUME_MINIMUM)
- memset(ioData->mBuffers[m_OutputBufferIndex].mData, 0x00, bytesRead);
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = bytesRead;
-
-#ifdef _DEBUG
- // Calculate stats and perform a sanity check
- m_PerfMon.ReportData(0, bytesRead); // TODO: Should we check the result?
-#endif
- return noErr;
-}
-
-// Static Callback from AudioDevice
-OSStatus CCoreAudioRenderer::DirectRenderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
-{
- CCoreAudioRenderer* pThis = (CCoreAudioRenderer*)inClientData;
- return pThis->OnRender(NULL, inInputTime, 0, outOutputData->mBuffers[0].mDataByteSize / pThis->m_BytesPerFrame, outOutputData);
-}
-
-//***********************************************************************************************
-// Audio Device Initialization Methods
-//***********************************************************************************************
-bool CCoreAudioRenderer::InitializePCM(UInt32 channels, UInt32 samplesPerSecond, UInt32 bitsPerSample, enum PCMChannels *channelMap, bool allowMixing /*= true*/)
-{
- // Set the input stream format for the first AudioUnit (this is what is being sent to us)
- AudioStreamBasicDescription inputFormat;
- AudioStreamBasicDescription outputFormat;
- inputFormat.mFormatID = kAudioFormatLinearPCM; // Data encoding format
- inputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian
- | kLinearPCMFormatFlagIsPacked // Samples occupy all bits (not left or right aligned)
- | kAudioFormatFlagIsSignedInteger;
- inputFormat.mChannelsPerFrame = channels; // Number of interleaved audiochannels
- inputFormat.mSampleRate = (Float64)samplesPerSecond; // the sample rate of the audio stream
- inputFormat.mBitsPerChannel = bitsPerSample; // Number of bits per sample, per channel
- inputFormat.mBytesPerFrame = (bitsPerSample>>3) * channels; // Size of a frame == 1 sample per channel
- inputFormat.mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
- inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
- inputFormat.mReserved = 0;
-
- // Configure up/down mixing if necessary, if caller allows it (and provides enough information to complete it)
- if (allowMixing && channelMap)
- {
- bool hasLFE = false;
- // Convert XBMC input channel layout format to CoreAudio layout format
- AudioChannelLayout* pInLayout = (AudioChannelLayout*)malloc(sizeof(AudioChannelLayout) + sizeof(AudioChannelDescription) * channels);
- pInLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- pInLayout->mChannelBitmap = 0;
- pInLayout->mNumberChannelDescriptions = channels;
- for (unsigned int chan=0; chan < channels; chan++)
- {
- AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
- pDesc->mChannelLabel = g_LabelMap[(unsigned int)channelMap[chan]]; // Convert from XBMC channel tag to CoreAudio channel tag
- pDesc->mChannelFlags = kAudioChannelFlags_AllOff;
- pDesc->mCoordinates[0] = 0.0f;
- pDesc->mCoordinates[1] = 0.0f;
- pDesc->mCoordinates[2] = 0.0f;
- if (pDesc->mChannelLabel == kAudioChannelLabel_LFEScreen)
- hasLFE = true;
- }
-
- // HACK: Fix broken channel layouts coming from some aac sources that include rear channels but no side channels.
- // 5.1 streams should include front and side channels. Rear channels are added by 6.1 and 7.1, so any 5.1
- // source that claims to have rear channels is wrong.
- if (inputFormat.mChannelsPerFrame == 6 && hasLFE) // Check for 5.1 configuration (as best we can without getting too silly)
- {
- for (unsigned int chan=0; chan < inputFormat.mChannelsPerFrame; chan++)
- {
- AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
- if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround || pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround)
- break; // Required condition cannot be true
-
- if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurroundDirect)
- {
- pDesc->mChannelLabel = kAudioChannelLabel_LeftSurround; // Change [Back Left] to [Side Left]
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: Detected faulty input channel map...fixing(Back Left-->Side Left)");
- }
- if (pDesc->mChannelLabel == kAudioChannelLabel_RightSurroundDirect)
- {
- pDesc->mChannelLabel = kAudioChannelLabel_RightSurround; // Change [Back Left] to [Side Left]
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: Detected faulty input channel map...fixing(Back Right-->Side Right)");
- }
- }
- }
-
- CCoreAudioChannelLayout sourceLayout(*pInLayout);
- free(pInLayout);
- pInLayout = NULL;
-
- CStdString strInLayout;
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: Source Stream Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)sourceLayout, strInLayout));
-
- // Get User-Configured (XBMC) Speaker Configuration
- AudioChannelLayout guiLayout;
- if (g_sysinfo.IsAppleTV())
- {
- // Force ATV1 to a 2.0 layout (that is all it knows), since it does not provide a usable channel layout
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: AppleTV detected - Forcing channel layout to 2.0 (max available PCM channels)");
- guiLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
- }
- else
- guiLayout.mChannelLayoutTag = g_LayoutMap[(PCMLayout)g_guiSettings.GetInt("audiooutput.channellayout")];
-
- CCoreAudioChannelLayout userLayout(guiLayout);
- CStdString strUserLayout;
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: User-Configured Speaker Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)userLayout, strUserLayout));
-
- // Get OS-Configured (Audio MIDI Setup) Speaker Configuration (Channel Layout)
- CCoreAudioChannelLayout deviceLayout;
- if (!m_AudioDevice.GetPreferredChannelLayout(deviceLayout))
- return false;
-
- // Detect devices with no Speaker Configuration
- bool undefinedLayout = true;
- for (UInt32 c = 0; c < deviceLayout.GetChannelCount(); c++)
- {
- // If this is a known channel, then we can't have an undefined layout
- if (deviceLayout.GetChannelLabel(c) != kAudioChannelLabel_Unknown)
- {
- undefinedLayout = false;
- break;
- }
- }
- if (undefinedLayout)
- {
- AudioChannelLayoutTag newLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
- if (g_sysinfo.IsAppleTV()) // AppleTV is only Stereo
- newLayoutTag = kAudioChannelLayoutTag_Stereo;
- else // AppleTV users cannot do this...
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::InitializePCM: The selected device (%s) does not have a speaker layout configured. Using the default layout.", m_AudioDevice.GetName());
- CLog::Log(LOGERROR, "CCoreAudioRenderer::InitializePCM: \tPlease go to Applications -> Utilities -> Audio MIDI Setup, and select 'Configure Speakers...'");
-
- // Pick a default layout based on the number of channels
- newLayoutTag = GetDefaultLayout(deviceLayout.GetChannelCount());
- if (newLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Undefined, give up...
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::InitializePCM: Unable to find a suitable default layout for this device.");
- return false;
- }
- }
- if (!deviceLayout.SetLayout(newLayoutTag))
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::InitializePCM: Unable to set channel layout from tag.");
- return false;
- }
- }
-
- CStdString strOutLayout;
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: Output Device Layout: %s", CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)deviceLayout, strOutLayout));
-
- // TODO:
- // Reconcile the OS and GUI layout configurations. Clamp to the minimum number of speakers
- // For each OS-defined output, see if it exists in the GUI configuration
- // If it does, add it to the 'union' layout (bitmap?)
- // User may have configured 5.1 in GUI, but only 2.0 in OS
- // Resulting layout would be {FL, FR}
- // User may have configured 2.0 in GUI, and 5.1 in OS
- // Resulting layout would be {FL, FR}
-
- // Correct any configuration incompatibilities
- // if (CCoreAudioChannelLayout::GetChannelCountForLayout(guiLayout) < CCoreAudioChannelLayout::GetChannelCountForLayout(deviceLayout))
- // deviceLayout.CopyLayout(guiLayout);
-
- // TODO: Skip matrix mixer if input/output are compatible -> Add a IsPurePassthrough() method to the CCoreAudioMixMap class
-
- AudioChannelLayout* layoutCandidates[] = {(AudioChannelLayout*)deviceLayout, (AudioChannelLayout*)userLayout, NULL};
-
- // Try to construct a mapping matrix for the mixer. Work through the layout candidates and see if any will work
- CCoreAudioMixMap mixMap;
- for(AudioChannelLayout** pLayout = layoutCandidates; *pLayout != NULL; pLayout++)
- {
- mixMap.Rebuild(*sourceLayout, **pLayout);
- if (mixMap.IsValid())
- break;
- }
-
- if (mixMap.IsValid())
- {
- if (!m_AUConverter.Open(kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple) ||
- !m_AUConverter.SetInputFormat(&inputFormat))
- return false;
-
- // Audio units use noninterleaved 32-bit floating point linear PCM data for input and output,
- // ...except in the case of an audio unit that is a data format converter, which converts to or from this format.
- AudioStreamBasicDescription fmt;
- fmt.mFormatID = kAudioFormatLinearPCM;
- fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
- fmt.mBitsPerChannel = sizeof(Float32)<<3;
- fmt.mSampleRate = (Float64)samplesPerSecond;
- fmt.mFramesPerPacket = 1;
- fmt.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
- fmt.mBytesPerFrame = sizeof(Float32);
- fmt.mBytesPerPacket = sizeof(Float32);
-
- // Set-up the format converter
- if (!m_AUConverter.SetOutputFormat(&fmt) ||
- !m_AUConverter.SetInputSource(this) || // Converter's data comes from the renderer
- !m_AUConverter.Initialize())
- return false;
-
- // Set-up the mixer input
- if (!m_MixerUnit.Open() || // Create the MatrixMixer AudioUnit Component to handle up/down mix)
- !m_MixerUnit.SetInputBusCount(1) || // Configure the mixer
- !m_MixerUnit.SetOutputBusCount(1) ||
- !m_MixerUnit.SetInputFormat(&fmt)) // Same input format as the outpur from the converter
- return false;
-
- // Update format structure to reflect the desired format from the mixer
- fmt.mChannelsPerFrame = mixMap.GetOutputChannels(); // The output format of the mixer is identical to the input format, except for the channel count
-
- // Set-up the mixer output
- if (!m_MixerUnit.SetOutputFormat(&fmt) ||
- !m_MixerUnit.SetInputSource(&m_AUConverter) || // The mixer gets its data from the converter
- !m_MixerUnit.Initialize())
- return false;
-
- // Configure the mixing matrix
- Float32* val = (Float32*)mixMap;
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: Loading matrix mixer configuration");
- for (UInt32 i = 0; i < inputFormat.mChannelsPerFrame; ++i)
- {
- for (UInt32 j = 0; j < fmt.mChannelsPerFrame; ++j)
- {
- AudioUnitSetParameter(m_MixerUnit.GetComponent(),
- kMatrixMixerParam_Volume, kAudioUnitScope_Global, (i<<16) | j, *val++, 0);
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: \t[%d][%d][%0.1f]",
- (int)i, (int)j, *(val-1));
- }
- }
-
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::InitializePCM: "
- "Mixer Output Format: %d channels, %0.1f kHz, %d bits, %d bytes per frame",
- (int)fmt.mChannelsPerFrame, fmt.mSampleRate / 1000.0f, (int)fmt.mBitsPerChannel, (int)fmt.mBytesPerFrame);
-
- // only enable the compressor if not playing music
- if (!m_init_state.bIsMusic)
- {
- // Set-up the compander
- if (!m_AUCompressor.Open() ||
- !m_AUCompressor.SetOutputFormat(&fmt) ||
- !m_AUCompressor.SetInputFormat(&fmt) ||
- !m_AUCompressor.SetInputSource(&m_MixerUnit) || // The compressor gets its data from the mixer
- !m_AUCompressor.Initialize())
- return false;
-
- // Configure compander parameters
- m_AUCompressor.SetAttackTime(g_advancedSettings.m_limiterHold);
- m_AUCompressor.SetReleaseTime(g_advancedSettings.m_limiterRelease);
- }
-
- // Copy format for the Output AU
- outputFormat = fmt;
- }
- else
- {
- CLog::Log(LOGERROR, "CCoreAudioRenderer::InitializePCM: No matrix mixer configuration available - unmapped channels will be dropped");
- outputFormat = inputFormat; // We don't know how to map this...let CoreAudio handle it
- }
- }
- else
- {
- if (allowMixing && !channelMap)
- CLog::Log(LOGINFO, "CCoreAudioRenderer::InitializePCM: No channel map provided - extra channels will be dropped");
- outputFormat = inputFormat;
- }
-
- if (!m_AUOutput.SetInputFormat(&outputFormat))
- return false;
-
- m_BytesPerFrame = inputFormat.mBytesPerFrame;
- m_AvgBytesPerSec = inputFormat.mSampleRate * inputFormat.mBytesPerFrame; // 1 sample per channel per frame
- m_EnableVolumeControl = true;
-
- return true;
-}
-
-bool CCoreAudioRenderer::InitializePCMEncoded(UInt32 sampleRate)
-{
- m_AudioDevice.SetHogStatus(true); // Prevent any other application from using this device.
- m_AudioDevice.SetMixingSupport(false); // Try to disable mixing support. Effectiveness depends on the device.
-
- // Set the Sample Rate as defined by the spec.
- m_AudioDevice.SetNominalSampleRate((float)sampleRate);
-
- if (!InitializePCM(2, sampleRate, 16, NULL, false))
- return false;
-
- m_EnableVolumeControl = false; // Prevent attempts to change the output volume. It is not possible with encoded audio
- return true;
-}
-
-bool CCoreAudioRenderer::InitializeEncoded(AudioDeviceID outputDevice, UInt32 sampleRate)
-{
- //return false; // un-comment to force PCM Spoofing (DD-Wav). For testing use only.
-
- CStdString formatString;
- AudioStreamBasicDescription outputFormat = {0};
- AudioStreamID outputStream = 0;
-
- // Fetch a list of the streams defined by the output device
- AudioStreamIdList streams;
- UInt32 streamIndex = 0;
- m_AudioDevice.GetStreams(&streams);
-
- while (!streams.empty())
- {
- // Get the next stream
- CCoreAudioStream stream;
- stream.Open(streams.front());
- streams.pop_front(); // We copied it, now we are done with it
-
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: "
- "Found %s stream - id: 0x%04X, Terminal Type: 0x%04lX",
- stream.GetDirection() ? "Input" : "Output",
- (int)stream.GetId(),
- stream.GetTerminalType());
-
- // Probe physical formats
- StreamFormatList physicalFormats;
- stream.GetAvailablePhysicalFormats(&physicalFormats);
- while (!physicalFormats.empty())
- {
- AudioStreamRangedDescription& desc = physicalFormats.front();
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: Considering Physical Format: %s", StreamDescriptionToString(desc.mFormat, formatString));
- if (desc.mFormat.mFormatID == kAudioFormat60958AC3 && desc.mFormat.mSampleRate == sampleRate)
- {
- outputFormat = desc.mFormat; // Select this format
- m_OutputBufferIndex = streamIndex; // TODO: Is this technically correct? Will each stream have it's own IOProc buffer?
- outputStream = stream.GetId();
- break;
- }
- physicalFormats.pop_front();
- }
-
- // TODO: How do we determine if this is the right stream (not just the right format) to use?
- if (outputFormat.mFormatID)
- break; // We found a suitable format. No need to continue.
- streamIndex++;
- }
-
- if (!outputFormat.mFormatID) // No match found
- {
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: Unable to identify suitable output format.");
- return false;
- }
-
- m_ChunkLen = outputFormat.mBytesPerPacket; // 1 Chunk == 1 Packet
- m_AvgBytesPerSec = outputFormat.mChannelsPerFrame * (outputFormat.mBitsPerChannel>>3) * outputFormat.mSampleRate; // mBytesPerFrame is 0 for a cac3 stream
- m_BytesPerFrame = outputFormat.mChannelsPerFrame * (outputFormat.mBitsPerChannel>>3);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: Selected stream[%lu] - id: 0x%04lX, Physical Format: %s (%lu Bytes/sec.)", streamIndex, outputStream, StreamDescriptionToString(outputFormat, formatString), m_AvgBytesPerSec);
-
- // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
- // It appears that leaving this set will aslo restore the previous stream format when the
- // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
- // From the SDK docs: "If the AudioDevice is in a non-mixable mode, the HAL will automatically take hog mode on behalf of the first process to start an IOProc."
-
- // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
- // If it is attempted after the format change, there is a high likelihood of a deadlock
- // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
-
- CCoreAudioHardware::SetAutoHogMode(false); // Auto-Hog does not always un-hog the device when changing back to a mixable mode. Handle this on our own until it is fixed.
- bool autoHog = CCoreAudioHardware::GetAutoHogMode();
- CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
- if (!autoHog) // Try to handle this ourselves
- {
- m_AudioDevice.SetHogStatus(true); // Hog the device if it is not set to be done automatically
- m_AudioDevice.SetMixingSupport(false); // Try to disable mixing. If we cannot, it may not be a problem
- }
- m_NumLatencyFrames = m_AudioDevice.GetNumLatencyFrames();
-
- // Configure the output stream object
- m_OutputStream.Open(outputStream); // This is the one we will keep
- AudioStreamBasicDescription virtualFormat;
- m_OutputStream.GetVirtualFormat(&virtualFormat);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: Previous Virtual Format: %s (%lu Bytes/sec.)", StreamDescriptionToString(virtualFormat, formatString), m_AvgBytesPerSec);
- AudioStreamBasicDescription previousPhysicalFormat;
- m_OutputStream.GetPhysicalFormat(&previousPhysicalFormat);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: Previous Physical Format: %s (%lu Bytes/sec.)", StreamDescriptionToString(previousPhysicalFormat, formatString), m_AvgBytesPerSec);
- m_OutputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
- m_NumLatencyFrames += m_OutputStream.GetNumLatencyFrames();
- m_OutputStream.GetVirtualFormat(&virtualFormat);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: New Virtual Format: %s (%lu Bytes/sec.)", StreamDescriptionToString(virtualFormat, formatString), m_AvgBytesPerSec);
- CLog::Log(LOGDEBUG, "CoreAudioRenderer::InitializeEncoded: New Physical Format: %s (%lu Bytes/sec.)", StreamDescriptionToString(outputFormat, formatString), m_AvgBytesPerSec);
-
- // Register for data request callbacks from the driver
- m_AudioDevice.AddIOProc(DirectRenderCallback, this);
-
- m_EnableVolumeControl = false; // Prevent attempts to change the output volume. It is not possible with encoded audio
- return true;
-}
-
-OSStatus CCoreAudioRenderer::HardwareListenerProc(AudioHardwarePropertyID property, void *clientref)
-{
- //CCoreAudioRenderer *m = (CCoreAudioRenderer*)clientref;
- switch(property)
- {
- case kAudioHardwarePropertyDevices:
- // An audio device has been added/removed to the system
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::HardwareListenerProc:kAudioHardwarePropertyDevices");
- break;
- case kAudioHardwarePropertyIsInitingOrExiting:
- // HAL is either initializing or exiting the process.
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::HardwareListenerProc:kAudioHardwarePropertyIsInitingOrExiting");
- break;
- case kAudioHardwarePropertyDefaultOutputDevice:
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::HardwareListenerProc:kAudioHardwarePropertyDefaultOutputDevice");
- break;
- default:
- break;
- }
-
- return noErr;
-}
-
-OSStatus CCoreAudioRenderer::DeviceListenerProc(AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *clientref)
-{
- //CCoreAudioRenderer *m = (CCoreAudioRenderer*)clientref;
- switch(inPropertyID)
- {
- case kAudioDevicePropertyDeviceIsAlive:
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::DeviceListenerProc:kAudioDevicePropertyDeviceIsAlive");
- break;
- case kAudioDevicePropertyDeviceIsRunning:
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::DeviceListenerProc:kAudioDevicePropertyDeviceIsRunning, "
- "inDevice(0x%x), inChannel(%d), inDevice(%d)", inDevice, inChannel, isInput);
- break;
- case kAudioDevicePropertyStreamConfiguration:
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::DeviceListenerProc:kAudioDevicePropertyStreamConfiguration, "
- "inDevice(0x%x), inChannel(%d), inDevice(%d)", inDevice, inChannel, isInput);
- break;
- default:
- break;
- }
- return noErr;
-}
-
-void CCoreAudioRenderer::OnLostDevice()
-{
- if (g_guiSettings.GetBool("videoplayer.adjustrefreshrate"))
- {
- CStdString deviceName;
- m_AudioDevice.GetName(deviceName);
- if (deviceName.Equals("HDMI"))
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::OnLostDevice");
- }
-}
-
-void CCoreAudioRenderer::OnResetDevice()
-{
- if (g_guiSettings.GetBool("videoplayer.adjustrefreshrate"))
- {
- CStdString deviceName;
- m_AudioDevice.GetName(deviceName);
- if (deviceName.Equals("HDMI"))
- {
- Reinitialize();
- CLog::Log(LOGDEBUG, "CCoreAudioRenderer::OnResetDevice");
- }
- }
-}
-
-AudioChannelLayoutTag CCoreAudioRenderer::GetDefaultLayout(UInt32 channelCount)
-{
- switch(channelCount)
- {
- case 1:
- return kAudioChannelLayoutTag_Mono; // 1.0
- case 2:
- return kAudioChannelLayoutTag_Stereo; // 2.0
- case 3:
- return kAudioChannelLayoutTag_DVD_4; // 2.1
- case 4:
- return kAudioChannelLayoutTag_DVD_10; // 3.1
- case 5:
- return kAudioChannelLayoutTag_DVD_6; // 4.1
- case 6:
- return kAudioChannelLayoutTag_MPEG_5_1_A; // 5.1
- case 7:
- return kAudioChannelLayoutTag_AudioUnit_7_0; // 7.0
- case 8:
- return kAudioChannelLayoutTag_MPEG_7_1_A; // 7.1
- case 0:
- default:
- return kAudioChannelLayoutTag_UseChannelBitmap; // Basically 'Undefined'
- }
-}
-
-#endif
-#endif
-
diff --git a/xbmc/cores/AudioRenderers/CoreAudioRenderer.h b/xbmc/cores/AudioRenderers/CoreAudioRenderer.h
deleted file mode 100644
index 3b06a518fb..0000000000
--- a/xbmc/cores/AudioRenderers/CoreAudioRenderer.h
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2005-2011 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-#ifndef __COREAUDIO_RENDERER_H__
-#define __COREAUDIO_RENDERER_H__
-
-#if !defined(__arm__)
-#include <osx/CoreAudio.h>
-#include "IAudioRenderer.h"
-#include "threads/Event.h"
-#include "threads/LockFree.h"
-#include "guilib/DispResource.h"
-
-struct audio_slice
-{
- struct _tag_header{
- uint64_t timestamp; // Currently not used
- size_t data_len;
- } header;
- unsigned int data[1];
- unsigned char* get_data() {return (unsigned char*)&data;}
-};
-
-class CAtomicAllocator
-{
-public:
- CAtomicAllocator(size_t blockSize);
- ~CAtomicAllocator();
- void* Alloc();
- void Free(void* p);
- size_t GetBlockSize();
-private:
- lf_heap m_Heap ;
- size_t m_BlockSize;
-};
-
-class CSliceQueue
-{
-public:
- CSliceQueue(size_t sliceSize);
- virtual ~CSliceQueue();
- size_t AddData(void* pBuf, size_t bufLen);
- size_t GetData(void* pBuf, size_t bufLen);
- size_t GetTotalBytes();
- void Clear();
-protected:
- void Push(audio_slice* pSlice);
- audio_slice* Pop(); // Does not respect remainder, so it must be private
- CAtomicAllocator* m_pAllocator;
- lf_queue m_Queue;
- size_t m_TotalBytes;
- audio_slice* m_pPartialSlice;
- size_t m_RemainderSize;
-};
-
-class CCoreAudioPerformance
-{
-public:
- CCoreAudioPerformance();
- ~CCoreAudioPerformance();
- void Init(UInt32 expectedBytesPerSec, UInt32 watchdogInterval = 1000, UInt32 flags = 0);
- void ReportData(UInt32 bytesIn, UInt32 bytesOut);
- void EnableWatchdog(bool enable);
- void SetPreroll(UInt32 bytes); // Fixed bytes
- void SetPreroll(float seconds); // Calculated for time (seconds)
- void Reset();
- enum
- {
- FlagDefault = 0
- };
-protected:
- UInt64 m_TotalBytesIn;
- UInt64 m_TotalBytesOut;
- UInt32 m_ExpectedBytesPerSec;
- UInt32 m_ActualBytesPerSec;
- UInt32 m_Flags;
- bool m_WatchdogEnable;
- UInt32 m_WatchdogInterval;
- UInt32 m_LastWatchdogCheck;
- UInt32 m_LastWatchdogBytesIn;
- UInt32 m_LastWatchdogBytesOut;
- float m_WatchdogBitrateSensitivity;
- UInt32 m_WatchdogPreroll;
-};
-
-class CCoreAudioMixMap
-{
-public:
- CCoreAudioMixMap();
- CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout, bool forceExplicit=false);
- virtual ~CCoreAudioMixMap();
- operator Float32*() const {return m_pMap;}
- const Float32* GetBuffer() {return m_pMap;}
- UInt32 GetInputChannels() {return m_inChannels;}
- UInt32 GetOutputChannels() {return m_outChannels;}
- bool IsValid() {return m_isValid;}
- void Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout, bool forceExplicit=false);
-private:
- bool BuildExplicit(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout);
- Float32* m_pMap;
- UInt32 m_inChannels;
- UInt32 m_outChannels;
- bool m_isValid;
-};
-
-class CCoreAudioRenderer : public IAudioRenderer, public ICoreAudioSource, public IDispResource
-{
-public:
- CCoreAudioRenderer();
- virtual ~CCoreAudioRenderer();
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual bool Deinitialize();
- bool Reinitialize();
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void WaitCompletion();
-
- virtual void SetDynamicRangeCompression(long drc);
-
- // Unimplemented IAudioRenderer methods
- virtual int SetPlaySpeed(int iSpeed) {return 0;};
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) {};
- virtual void UnRegisterAudioCallback() {};
- virtual void RegisterAudioCallback(IAudioCallback* pCallback) {};
-
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough) {};
-
- // AudioUnit Rendering Connection Point (called by down-stream sinks)
- virtual OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList);
-
- virtual void OnLostDevice();
- virtual void OnResetDevice();
-private:
- OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- static OSStatus DirectRenderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData);
- bool InitializeEncoded(AudioDeviceID outputDevice, UInt32 sampleRate);
- bool InitializePCM(UInt32 channels, UInt32 samplesPerSecond, UInt32 bitsPerSample, enum PCMChannels *channelMap, bool allowMixing = true);
- bool InitializePCMEncoded(UInt32 sampleRate);
-
- AudioChannelLayoutTag GetDefaultLayout(UInt32 channelCount);
- static OSStatus HardwareListenerProc(AudioHardwarePropertyID property, void *clientref);
- static OSStatus DeviceListenerProc(AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *clientref);
-
- bool m_Pause;
- bool m_Initialized; // Prevent multiple init/deinit
-
- long m_CurrentVolume; // Courtesy of the jerk that made GetCurrentVolume a const...
- unsigned int m_ChunkLen; // Minimum amount of data accepted by AddPackets
- // char* m_RemapBuffer; // Temporary buffer for channel remapping
- CSliceQueue* m_pCache;
- size_t m_MaxCacheLen; // Maximum number of bytes to be cached by the renderer.
-
- CAUOutputDevice m_AUOutput;
- CAUMatrixMixer m_MixerUnit;
- CCoreAudioDevice m_AudioDevice;
- CCoreAudioStream m_OutputStream;
- UInt32 m_OutputBufferIndex;
-
- CAUGenericSource m_AUConverter;
- CAUDynamicsProcessor m_AUCompressor;
-
- bool m_Passthrough;
- bool m_EnableVolumeControl;
-
- // Stream format
- size_t m_AvgBytesPerSec;
- size_t m_BytesPerFrame; // Input frame size
- UInt32 m_NumLatencyFrames;
-
-#ifdef _DEBUG
- // Performace Monitoring
- CCoreAudioPerformance m_PerfMon;
-#endif
- // Thread synchronization
- CEvent m_RunoutEvent;
- long m_DoRunout;
- // saved Initialize vars
- struct init_state
- {
- bool reinit;
- CStdString device;
- int iChannels;
- enum PCMChannels *channelMap;
- unsigned int uiSamplesPerSec;
- unsigned int uiBitsPerSample;
- bool bResample;
- bool bIsMusic;
- EEncoded bPassthrough;
- IAudioCallback *pCallback;
- };
- CCriticalSection m_init_csection;
- init_state m_init_state;
-
-};
-
-#endif
-#endif
diff --git a/xbmc/cores/AudioRenderers/IAudioRenderer.h b/xbmc/cores/AudioRenderers/IAudioRenderer.h
deleted file mode 100644
index e1342fd336..0000000000
--- a/xbmc/cores/AudioRenderers/IAudioRenderer.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-* XBMC Media Center
-* Copyright (c) 2002 d7o3g4q and RUNTiME
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* 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 for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-// AsyncAudioRenderer.h: interface for the CAsyncDirectSound class.
-//
-//////////////////////////////////////////////////////////////////////
-
-#if !defined(AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_)
-#define AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-#include "utils/StdString.h"
-#include "cores/IAudioCallback.h"
-#include "utils/PCMRemap.h"
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-typedef std::pair<CStdString, CStdString> AudioSink;
-typedef std::vector<AudioSink> AudioSinkList;
-
-class IAudioRenderer
-{
-public:
- enum EEncoded {
- ENCODED_NONE = 0,
- ENCODED_IEC61937_AC3,
- ENCODED_IEC61937_EAC3,
- ENCODED_IEC61937_DTS,
- ENCODED_IEC61937_MPEG,
- ENCODED_IEC61937_UNKNOWN,
- };
-
- IAudioRenderer() {};
- virtual ~IAudioRenderer() {};
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = ENCODED_NONE) = 0;
- virtual void UnRegisterAudioCallback() = 0;
- virtual void RegisterAudioCallback(IAudioCallback* pCallback) = 0;
- virtual float GetDelay() = 0;
- virtual float GetCacheTime() = 0;
- virtual float GetCacheTotal() { return 1.0f; }
-
- virtual unsigned int AddPackets(const void* data, unsigned int len) = 0;
- virtual bool IsResampling() { return false;};
- virtual unsigned int GetSpace() = 0;
- virtual bool Deinitialize() = 0;
- virtual bool Pause() = 0;
- virtual bool Stop() = 0;
- virtual bool Resume() = 0;
- virtual unsigned int GetChunkLen() = 0;
-
- virtual long GetCurrentVolume() const = 0;
- virtual void Mute(bool bMute) = 0;
- virtual bool SetCurrentVolume(long nVolume) = 0;
- virtual void SetDynamicRangeCompression(long drc) {};
- virtual float GetCurrentAttenuation() { return m_remap.GetCurrentAttenuation(); }
- virtual int SetPlaySpeed(int iSpeed) = 0;
- virtual void WaitCompletion() = 0;
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) = 0;
-
-protected:
- CPCMRemap m_remap;
-
-private:
-};
-
-#endif // !defined(AFX_IAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_)
diff --git a/xbmc/cores/AudioRenderers/IOSAudioRenderer.cpp b/xbmc/cores/AudioRenderers/IOSAudioRenderer.cpp
deleted file mode 100644
index cb72e67c5e..0000000000
--- a/xbmc/cores/AudioRenderers/IOSAudioRenderer.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-#ifdef __APPLE__
-/*
- * Copyright (C) 2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "IOSAudioRenderer.h"
-#include "IOSAudioRingBuffer.h"
-#include "AudioContext.h"
-#include "GUISettings.h"
-#include "Settings.h"
-#include "utils/log.h"
-
-//***********************************************************************************************
-// Contruction/Destruction
-//***********************************************************************************************
-CIOSAudioRenderer::CIOSAudioRenderer() :
- m_Pause(false),
- m_Initialized(false),
- m_CurrentVolume(0),
- m_OutputBufferIndex(0),
- m_BytesPerSec(0),
- m_NumChunks(0),
- m_PacketSize(0),
- m_Passthrough(false),
- m_SamplesPerSec(0),
- m_DoRunout(0)
-{
- m_Buffer = new IOSAudioRingBuffer();
-}
-
-CIOSAudioRenderer::~CIOSAudioRenderer()
-{
- Deinitialize();
- delete m_Buffer;
-}
-
-//***********************************************************************************************
-// Initialization
-//***********************************************************************************************
-
-bool CIOSAudioRenderer::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic /*Useless Legacy Parameter*/, EEncoded bPassthrough)
-{
- // Limit to 2.0. It is only used for anloge audio.
- static enum PCMChannels IOSChannelMap[2] =
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT};
-
- m_Passthrough = bPassthrough;
-
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
-
- bool bAudioOnAllSpeakers(false);
- g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
-
- if(bPassthrough)
- {
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL);
- } else {
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
- }
-
- m_DataChannels = iChannels;
- m_remap.Reset();
-
- if (!m_Passthrough && channelMap)
- {
- enum PCMChannels *outLayout;
-
- /* set the input format, and get the channel layout so we know what we need to open */
- outLayout = m_remap.SetInputFormat (iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);
- unsigned int outChannels = 0;
- unsigned int ch = 0, map;
- while(outLayout[ch] != PCM_INVALID)
- {
- for(map = 0; map < 8; ++map)
- {
- if (outLayout[ch] == IOSChannelMap[map])
- {
- if (map > outChannels)
- outChannels = map;
- break;
- }
- }
- ++ch;
- }
-
- m_remap.SetOutputFormat(++outChannels, IOSChannelMap);
- if (m_remap.CanRemap())
- {
- iChannels = outChannels;
- if (m_DataChannels != (unsigned int)iChannels)
- CLog::Log(LOGDEBUG, "CIOSAudioRenderer::InitializePCM: Requested channels changed from %i to %i", m_DataChannels, iChannels);
- }
-
- }
-
- m_Channels = iChannels;
-
- // Set the input stream format for the AudioUnit
- // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
- // The autput format is automaticaly set to the input format.
- AudioStreamBasicDescription audioFormat;
- audioFormat.mFormatID = kAudioFormatLinearPCM; // Data encoding format
-
- audioFormat.mFormatFlags = kAudioFormatFlagsCanonical;
- audioFormat.mChannelsPerFrame = iChannels; // Number of interleaved audiochannels
- audioFormat.mSampleRate = (Float64)uiSamplesPerSec; // the sample rate of the audio stream
- audioFormat.mBitsPerChannel = uiBitsPerSample; // Number of bits per sample, per channel
- audioFormat.mBytesPerFrame = (uiBitsPerSample>>3) * iChannels; // Size of a frame == 1 sample per channel
- audioFormat.mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
- audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
- audioFormat.mReserved = 0;
-
- // Attach our output object to the device
- if(!m_AudioDevice.Init(/*m_Passthrough*/ true, &audioFormat, RenderCallback, this))
- {
- CLog::Log(LOGDEBUG, "CIOSAudioRenderer::Init failed");
- return false;
- }
-
- m_PacketSize = iChannels * (uiBitsPerSample / 8) * 512;
-
- m_BufferFrames = m_AudioDevice.FramesPerSlice(m_PacketSize);
- if(!m_BufferFrames)
- {
- CLog::Log(LOGDEBUG, "CIOSAudioRenderer::FramesPerSlice bufferFrames == 0\n");
- //return false;
- }
-
- m_BytesPerFrame = audioFormat.mBytesPerFrame;
- m_BitsPerChannel = audioFormat.mBitsPerChannel;
- m_BytesPerSec = uiSamplesPerSec * (uiBitsPerSample / 8) * iChannels;
- m_SamplesPerSec = uiSamplesPerSec;
- m_BufferLen = m_PacketSize * 96;
- if(m_BufferLen < m_PacketSize || m_BufferLen == 0)
- m_BufferLen = m_PacketSize;
-
- bool success = m_Buffer->Create(m_BufferLen);
- if(!success || !m_BufferLen)
- {
- CLog::Log(LOGDEBUG, "CIOSAudioRenderer::Initialize: Error allocation audio buffer size %d.", m_BufferLen);
- return false;
- }
-
- m_EnableVolumeControl = true;
-
- /*
- if (!m_AudioDevice.SetSessionListener(kAudioSessionProperty_AudioRouteChange, PropertyChangeCallback, this))
- return false;
- */
-
- // Start the audio device
- if (!m_AudioDevice.Open())
- return false;
-
- // Suspend rendering. We will start once we have some data.
- m_Pause = true;
- m_Initialized = true;
-
- CLog::Log(LOGDEBUG, "CIOSAudioRenderer::Initialize: Renderer Configuration - Chunk Len: %u, Max Cache: %u (%0.0fms).", m_PacketSize, m_BufferLen, 1000.0 *(float)m_BufferLen/(float)m_BytesPerSec);
- CLog::Log(LOGINFO, "CIOSAudioRenderer::Initialize: Successfully configured audio output.");
-
- m_DoRunout = 0;
-
- m_drc = 0;
-
- return true;
-}
-
-bool CIOSAudioRenderer::Deinitialize()
-{
-
- if(m_Initialized)
- WaitCompletion();
-
- // Stop rendering
- Stop();
-
- Sleep(10);
- m_AudioDevice.Close();
- m_Initialized = false;
- m_BytesPerSec = 0;
- m_BufferLen = 0;
- m_NumChunks = 0;
- m_PacketSize = 0;
- m_SamplesPerSec = 0;
- m_DoRunout = 0;
-
- CLog::Log(LOGINFO, "CIOSAudioRenderer::Deinitialize: Renderer has been shut down.");
-
- return true;
-}
-
-void CIOSAudioRenderer::Flush()
-{
- Pause();
-
- // IOSAudioRingBuffer::Reset is not threadsafe but we have
- // paused here so renderer is not reading from m_Buffer and
- // we can reset with confidence.
- m_Buffer->Reset();
-}
-
-//***********************************************************************************************
-// Transport control methods
-//***********************************************************************************************
-bool CIOSAudioRenderer::Pause()
-{
- if (!m_Pause)
- {
- m_AudioDevice.Stop();
- m_Pause = true;
- }
- return true;
-}
-
-bool CIOSAudioRenderer::Resume()
-{
- if (m_Pause)
- {
- m_AudioDevice.Start();
- m_Pause = false;
- }
- return true;
-}
-
-bool CIOSAudioRenderer::Stop()
-{
- m_AudioDevice.Stop();
-
- m_Pause = true;
-
- Flush();
- return true;
-}
-
-//***********************************************************************************************
-// Volume control methods
-//***********************************************************************************************
-LONG CIOSAudioRenderer::GetCurrentVolume() const
-{
- return m_CurrentVolume;
-}
-
-void CIOSAudioRenderer::Mute(bool bMute)
-{
-}
-
-bool CIOSAudioRenderer::SetCurrentVolume(LONG nVolume)
-{
- return true;
-}
-
-//***********************************************************************************************
-// Data management methods
-//***********************************************************************************************
-unsigned int CIOSAudioRenderer::GetSpace()
-{
- int free = m_Buffer->GetWriteSize();
- return (free / m_Channels) * m_DataChannels;
-}
-
-unsigned int CIOSAudioRenderer::AddPackets(const void* data, DWORD len)
-{
- int status;
-
- // call channel remapping routine if available and required
- if (m_remap.CanRemap() && !m_Passthrough)
- {
- int length, frames;
-
- // we might be up or down converting, so convert to number of bytes
- // that we will get out of remapping the channels and see if that fits.
- length = (len / m_DataChannels) * m_Channels;
- if (length > GetSpace())
- return 0;
-
- // check buffer fit, we can only accept unit frames, so if less than
- // a complete frame, we punt.
- frames = length / m_Channels / (m_BitsPerChannel >> 3);
- if (frames == 0)
- {
- CLog::Log(LOGINFO, "IOSAudioRenderer::AddPackets() - Need complete frame.");
- return 0;
- }
-
- uint8_t outData[length];
- // remap the audio channels using the frame count
- m_remap.Remap((void*)data, outData, frames, m_drc);
-
- status = m_Buffer->Write(outData, length);
- // return the number of input bytes we accepted
- len = (length / m_Channels) * m_DataChannels;
- }
- else
- {
- // simple case, not remaping or passthough, only have to check
- // that we have free space in our buffer.
- status = m_Buffer->Write((unsigned char *)data, len);
- }
-
- Resume();
- //only return the length if buffer accepted the data
- return status == 0 ? len : 0;
-}
-
-float CIOSAudioRenderer::GetDelay()
-{
- return (float)m_Buffer->GetReadSize() / (float)m_BytesPerSec;
-}
-
-float CIOSAudioRenderer::GetCacheTime()
-{
- unsigned int nBufferLenFull = (m_BufferLen / m_Channels) * m_DataChannels;
- return (float)(nBufferLenFull - GetSpace()) / (float)m_BytesPerSec;
-}
-
-float CIOSAudioRenderer::GetCacheTotal()
-{
- return (float)m_BufferLen / (float)m_BytesPerSec;
-}
-
-unsigned int CIOSAudioRenderer::GetChunkLen()
-{
- return (m_PacketSize / m_Channels) * m_DataChannels;
-}
-
-void CIOSAudioRenderer::WaitCompletion()
-{
- // we don't lock here as we are just checking for zero or non-zero.
-
- // The cache is already empty. There is nothing to wait for.
- if (m_Buffer->GetReadSize() == 0)
- return;
-
- m_DoRunout = 1;
-
- UInt32 delay = (UInt32)(GetDelay() * 1000.0f) + 10;
- if (!delay)
- {
- bool ret = m_RunoutEvent.WaitMSec(delay);
- if (!ret && m_Buffer->GetReadSize() )
- {
- //See if there is still some data left in the cache that didn't get played
- CLog::Log(LOGERROR, "CIOSAudioRenderer::WaitCompletion: Timed-out waiting for runout. Remaining data will be truncated.");
- }
- }
-
- Stop();
-}
-
-//***********************************************************************************************
-// Rendering Methods
-//***********************************************************************************************
-OSStatus CIOSAudioRenderer::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- if (!m_Initialized)
- {
- CLog::Log(LOGERROR, "CIOSAudioRenderer::OnRender: Callback to de/unitialized renderer.");
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = 0;
- return noErr;
- }
-
- if(m_Pause)
- {
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = 0;
- return noErr;
- }
-
- UInt32 bytesRead = m_Buffer->GetReadSize();
- UInt32 bytesRequested = inNumberFrames * m_BytesPerFrame;
-
- if (bytesRead < bytesRequested)
- {
- m_RunoutEvent.Set(); // Tell anyone who cares that the cache is empty
- if (m_DoRunout) // We were waiting for a runout. This is not an error.
- {
- m_DoRunout = 0;
- }
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = 0;
- return noErr;
- }
-
- m_Buffer->Read((unsigned char *)ioData->mBuffers[m_OutputBufferIndex].mData, bytesRequested);
-
- if (!m_EnableVolumeControl && m_CurrentVolume <= VOLUME_MINIMUM)
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = 0;
- else
- ioData->mBuffers[m_OutputBufferIndex].mDataByteSize = bytesRequested;
-
- return noErr;
-}
-
-OSStatus CIOSAudioRenderer::RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return ((CIOSAudioRenderer*)inRefCon)->OnRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
-}
-
-// Static Callback from AudioUnit
-void CIOSAudioRenderer::PropertyChanged(AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue)
-{
- CLog::Log(LOGERROR, "CIOSAudioRenderer::PropertyChanged: inID %d.", (int)inID);
-}
-
-void CIOSAudioRenderer::PropertyChangeCallback(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue)
-{
- ((CIOSAudioRenderer*)inClientData)->PropertyChanged(inID, inDataSize, inPropertyValue);
-}
-
-#endif
diff --git a/xbmc/cores/AudioRenderers/IOSAudioRenderer.h b/xbmc/cores/AudioRenderers/IOSAudioRenderer.h
deleted file mode 100644
index 5b4b0729f0..0000000000
--- a/xbmc/cores/AudioRenderers/IOSAudioRenderer.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __IOSAUDIO_RENDERER_H__
-#define __IOSAUDIO_RENDERER_H__
-
-#include "IOSCoreAudio.h"
-#include "PlatformDefs.h"
-#include "IAudioRenderer.h"
-#include "threads/Event.h"
-
-class IOSAudioRingBuffer;
-class CIOSAudioRenderer : public IAudioRenderer
- {
- public:
- CIOSAudioRenderer();
- virtual ~CIOSAudioRenderer();
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual bool Deinitialize();
- virtual void Flush();
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void SetDynamicRangeCompression(long drc) { m_drc = drc; }
- virtual void WaitCompletion();
-
- // Unimplemented IAudioRenderer methods
- virtual int SetPlaySpeed(int iSpeed) {return 0;};
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) {};
- virtual void UnRegisterAudioCallback() {};
- virtual void RegisterAudioCallback(IAudioCallback* pCallback) {};
-
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough) {};
- private:
- OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- static OSStatus RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- static void PropertyChanged(AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue);
- static void PropertyChangeCallback(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inPropertyValue);
- bool InitializePCM(UInt32 channels, UInt32 samplesPerSecond, UInt32 bitsPerSample, enum PCMChannels *channelMap);
-
- bool m_Pause;
- bool m_Initialized; // Prevent multiple init/deinit
-
- long m_CurrentVolume; // Courtesy of the jerk that made GetCurrentVolume a const...
- bool m_EnableVolumeControl;
-
- CIOSCoreAudioDevice m_AudioDevice;
- UInt32 m_OutputBufferIndex;
-
- // Stream format
- int m_BitsPerChannel;
- int m_ChannelsPerFrame;
-
- IOSAudioRingBuffer *m_Buffer;
- unsigned int m_BytesPerSec;
- unsigned int m_BufferLen; ///< must always be num_chunks * chunk_size
- unsigned int m_NumChunks;
- unsigned int m_PacketSize;
- unsigned int m_BytesPerFrame;
- unsigned int m_BufferFrames;
- unsigned int m_SamplesPerSec;
-
- CEvent m_RunoutEvent;
- long m_DoRunout;
- unsigned int m_DataChannels;
- unsigned int m_Channels;
- bool m_Passthrough;
-
- long m_drc;
-
- };
-
-#endif
diff --git a/xbmc/cores/AudioRenderers/Makefile.in b/xbmc/cores/AudioRenderers/Makefile.in
deleted file mode 100644
index 61adeb69f6..0000000000
--- a/xbmc/cores/AudioRenderers/Makefile.in
+++ /dev/null
@@ -1,28 +0,0 @@
-ARCH=@ARCH@
-
-ifeq ($(findstring osx,$(ARCH)), osx)
-SRCS = \
- NullDirectSound.cpp \
- AudioRendererFactory.cpp \
- CoreAudioRenderer.cpp \
-
-else
-SRCS = \
- NullDirectSound.cpp \
- AudioRendererFactory.cpp \
-
-endif
-
-ifeq (@USE_ALSA@,1)
-SRCS+= ALSADirectSound.cpp
-endif
-
-ifeq (@USE_PULSE@,1)
-SRCS+= PulseAudioDirectSound.cpp \
-
-endif
-
-LIB=audiorenderers.a
-
-include @abs_top_srcdir@/Makefile.include
--include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/cores/AudioRenderers/Makefile.include b/xbmc/cores/AudioRenderers/Makefile.include
deleted file mode 100644
index e69de29bb2..0000000000
--- a/xbmc/cores/AudioRenderers/Makefile.include
+++ /dev/null
diff --git a/xbmc/cores/AudioRenderers/NullDirectSound.cpp b/xbmc/cores/AudioRenderers/NullDirectSound.cpp
deleted file mode 100644
index 20e6125b6d..0000000000
--- a/xbmc/cores/AudioRenderers/NullDirectSound.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "threads/SystemClock.h"
-#include "NullDirectSound.h"
-#include "guilib/AudioContext.h"
-#include "guilib/LocalizeStrings.h"
-#include "utils/log.h"
-#include "utils/TimeUtils.h"
-#include "dialogs/GUIDialogKaiToast.h"
-
-#define BUFFER CHUNKLEN * 20
-#define CHUNKLEN 512
-
-
-void CNullDirectSound::DoWork()
-{
-
-}
-
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
-//***********************************************************************************************
-CNullDirectSound::CNullDirectSound()
-{
-}
-bool CNullDirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded encoded)
-{
- CLog::Log(LOGERROR,"Creating a Null Audio Renderer, Check your audio settings as this should not happen");
- if (iChannels == 0)
- iChannels = 2;
-
- bool bAudioOnAllSpeakers(false);
- g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
-
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(34402), g_localizeStrings.Get(34403), TOAST_DISPLAY_TIME, false);
- m_timePerPacket = 1.0f / (float)(iChannels*(uiBitsPerSample/8) * uiSamplesPerSec);
- m_packetsSent = 0;
- m_paused = 0;
- m_lastUpdate = XbmcThreads::SystemClockMillis();
- return true;
-}
-
-//***********************************************************************************************
-CNullDirectSound::~CNullDirectSound()
-{
- Deinitialize();
-}
-
-
-//***********************************************************************************************
-bool CNullDirectSound::Deinitialize()
-{
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- return true;
-}
-
-void CNullDirectSound::Flush()
-{
- m_lastUpdate = XbmcThreads::SystemClockMillis();
- m_packetsSent = 0;
- Pause();
-}
-
-//***********************************************************************************************
-bool CNullDirectSound::Pause()
-{
- m_paused = true;
- return true;
-}
-
-//***********************************************************************************************
-bool CNullDirectSound::Resume()
-{
- m_paused = false;
- return true;
-}
-
-//***********************************************************************************************
-bool CNullDirectSound::Stop()
-{
- Flush();
- return true;
-}
-
-//***********************************************************************************************
-long CNullDirectSound::GetCurrentVolume() const
-{
- return m_nCurrentVolume;
-}
-
-//***********************************************************************************************
-void CNullDirectSound::Mute(bool bMute)
-{
-}
-
-//***********************************************************************************************
-bool CNullDirectSound::SetCurrentVolume(long nVolume)
-{
- m_nCurrentVolume = nVolume;
- return true;
-}
-
-
-//***********************************************************************************************
-unsigned int CNullDirectSound::GetSpace()
-{
- Update();
-
- if(BUFFER > m_packetsSent)
- return (int)BUFFER - m_packetsSent;
- else
- return 0;
-}
-
-//***********************************************************************************************
-unsigned int CNullDirectSound::AddPackets(const void* data, unsigned int len)
-{
- if (m_paused || GetSpace() == 0)
- return 0;
-
- int add = ( len / GetChunkLen() ) * GetChunkLen();
- m_packetsSent += add;
-
- return add;
-}
-
-//***********************************************************************************************
-float CNullDirectSound::GetDelay()
-{
- Update();
-
- return m_timePerPacket * (float)m_packetsSent;
-}
-
-float CNullDirectSound::GetCacheTime()
-{
- return GetDelay();
-}
-
-//***********************************************************************************************
-unsigned int CNullDirectSound::GetChunkLen()
-{
- return (int)CHUNKLEN;
-}
-//***********************************************************************************************
-int CNullDirectSound::SetPlaySpeed(int iSpeed)
-{
- return 0;
-}
-
-void CNullDirectSound::RegisterAudioCallback(IAudioCallback *pCallback)
-{
-}
-
-void CNullDirectSound::UnRegisterAudioCallback()
-{
-}
-
-void CNullDirectSound::WaitCompletion()
-{
- while(m_packetsSent > 0)
- Update();
-}
-
-void CNullDirectSound::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
-{
- return ;
-}
-
-void CNullDirectSound::Update()
-{
- unsigned int currentTime = XbmcThreads::SystemClockMillis();
- // because of the if clause below it's possible that m_lastUpdate is larger
- // than currentTime. We need to handle this.
- long deltaTime = (currentTime - m_lastUpdate); // the diff shouldn't overflow
-
- if (m_paused)
- {
- m_lastUpdate += deltaTime;
- return;
- }
-
- double d = (double)deltaTime / 1000.0f;
-
- if (currentTime != m_lastUpdate)
- {
- double i = (d / (double)m_timePerPacket);
- m_packetsSent -= (long)i;
- if (m_packetsSent < 0)
- m_packetsSent = 0;
- m_lastUpdate = currentTime;
- }
-}
diff --git a/xbmc/cores/AudioRenderers/NullDirectSound.h b/xbmc/cores/AudioRenderers/NullDirectSound.h
deleted file mode 100644
index 7ba91f290e..0000000000
--- a/xbmc/cores/AudioRenderers/NullDirectSound.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __NULL_DIRECT_SOUND_H__
-#define __NULL_DIRECT_SOUND_H__
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-#include "IAudioRenderer.h"
-#include "cores/IAudioCallback.h"
-
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-class CNullDirectSound : public IAudioRenderer
-{
-public:
- virtual void UnRegisterAudioCallback();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback);
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual float GetCacheTime();
- CNullDirectSound();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual ~CNullDirectSound();
-
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual bool Deinitialize();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual int SetPlaySpeed(int iSpeed);
- virtual void WaitCompletion();
- virtual void DoWork();
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
-
- virtual void Flush();
-private:
- long m_nCurrentVolume;
-
- float m_timePerPacket;
- int m_packetsSent;
- bool m_paused;
- unsigned int m_lastUpdate;
-
- void Update();
-};
-
-#endif
diff --git a/xbmc/cores/AudioRenderers/PulseAudioDirectSound.cpp b/xbmc/cores/AudioRenderers/PulseAudioDirectSound.cpp
deleted file mode 100644
index 861270d91c..0000000000
--- a/xbmc/cores/AudioRenderers/PulseAudioDirectSound.cpp
+++ /dev/null
@@ -1,754 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "system.h"
-#ifdef HAS_PULSEAUDIO
-#include "PulseAudioDirectSound.h"
-#include "guilib/AudioContext.h"
-#include "settings/AdvancedSettings.h"
-#include "settings/Settings.h"
-#include "utils/log.h"
-#include "Util.h"
-#include "guilib/LocalizeStrings.h"
-
-static const char *ContextStateToString(pa_context_state s)
-{
- switch (s)
- {
- case PA_CONTEXT_UNCONNECTED:
- return "unconnected";
- case PA_CONTEXT_CONNECTING:
- return "connecting";
- case PA_CONTEXT_AUTHORIZING:
- return "authorizing";
- case PA_CONTEXT_SETTING_NAME:
- return "setting name";
- case PA_CONTEXT_READY:
- return "ready";
- case PA_CONTEXT_FAILED:
- return "failed";
- case PA_CONTEXT_TERMINATED:
- return "terminated";
- default:
- return "none";
- }
-}
-
-static const char *StreamStateToString(pa_stream_state s)
-{
- switch(s)
- {
- case PA_STREAM_UNCONNECTED:
- return "unconnected";
- case PA_STREAM_CREATING:
- return "creating";
- case PA_STREAM_READY:
- return "ready";
- case PA_STREAM_FAILED:
- return "failed";
- case PA_STREAM_TERMINATED:
- return "terminated";
- default:
- return "none";
- }
-}
-
-/* Static callback functions */
-
-static void ContextStateCallback(pa_context *c, void *userdata)
-{
- pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- switch (pa_context_get_state(c))
- {
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal(m, 0);
- break;
- }
-}
-
-static void StreamStateCallback(pa_stream *s, void *userdata)
-{
- pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- switch (pa_stream_get_state(s))
- {
- case PA_STREAM_UNCONNECTED:
- case PA_STREAM_CREATING:
- case PA_STREAM_READY:
- case PA_STREAM_FAILED:
- case PA_STREAM_TERMINATED:
- pa_threaded_mainloop_signal(m, 0);
- break;
- }
-}
-
-static void StreamRequestCallback(pa_stream *s, size_t length, void *userdata)
-{
- pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- pa_threaded_mainloop_signal(m, 0);
-}
-
-static void StreamLatencyUpdateCallback(pa_stream *s, void *userdata)
-{
- pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- pa_threaded_mainloop_signal(m, 0);
-}
-
-struct SinkInfoStruct
-{
- bool passthrough;
- AudioSinkList *list;
- pa_threaded_mainloop *mainloop;
-};
-
-static void SinkInfo(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
-{
- SinkInfoStruct *sinkStruct = (SinkInfoStruct *)userdata;
-
- if (i && i->name)
- {
- bool add = false;
- if(sinkStruct->passthrough)
- {
-#if PA_CHECK_VERSION(1,0,0)
- for(int idx = 0; idx < i->n_formats; ++idx)
- {
- if(!pa_format_info_is_pcm(i->formats[idx]))
- {
- add = true;
- break;
- }
- }
-#endif
- }
- else
- add = true;
-
- if(add)
- {
- CStdString desc, sink;
- if(sinkStruct->list->size() == 0)
- sinkStruct->list->push_back(AudioSink(g_localizeStrings.Get(409) + " (PulseAudio)", "pulse:default@default"));
- desc.Format("%s (PulseAudio)", i->description);
- sink.Format("pulse:%s@default", i->name);
- sinkStruct->list->push_back(AudioSink(desc, sink));
- CLog::Log(LOGDEBUG, "PulseAudio: Found %s with devicestring %s", desc.c_str(), sink.c_str());
- }
- }
-
- pa_threaded_mainloop_signal(sinkStruct->mainloop, 0);
-}
-
-/* PulseAudio class memberfunctions*/
-
-CPulseAudioDirectSound::CPulseAudioDirectSound()
-{
-}
-
-bool CPulseAudioDirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels* channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded encoded)
-{
- m_remap.Reset();
- m_uiDataChannels = iChannels;
- enum PCMChannels* outLayout = NULL;
-
- if (encoded == ENCODED_NONE && channelMap)
- {
- /* set the input format, and get the channel layout so we know what we need to open */
- outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);
- enum PCMChannels *channel;
- iChannels = 0;
- for(channel = outLayout; *channel != PCM_INVALID; ++channel)
- ++iChannels;
-
- m_remap.SetOutputFormat(iChannels, outLayout);
- if (m_uiDataChannels != (unsigned int)iChannels)
- CLog::Log(LOGDEBUG, "CPulseAudioDirectSound::CPulseAudioDirectSound - Requested channels changed from %i to %i", m_uiDataChannels, iChannels);
- }
-
- bool bAudioOnAllSpeakers(false);
- g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
-
- m_Context = NULL;
- m_Stream = NULL;
- m_MainLoop = NULL;
- m_bPause = false;
- m_bRecentlyFlushed = true;
- m_bAutoResume = false;
- m_bIsAllocated = false;
- m_uiChannels = iChannels;
- m_uiSamplesPerSec = uiSamplesPerSec;
- m_uiBufferSize = 0;
- m_uiBitsPerSample = uiBitsPerSample;
- m_bPassthrough = encoded == ENCODED_NONE ? false : true;
- m_uiBytesPerSecond = uiSamplesPerSec * (uiBitsPerSample / 8) * iChannels;
- m_drc = 0;
-
- m_nCurrentVolume = g_settings.m_nVolumeLevel;
-
- m_dwPacketSize = iChannels*(uiBitsPerSample/8)*512;
- m_dwNumPackets = 16;
-
-#if !PA_CHECK_VERSION(1,0,0)
- /* Open the device */
- if (m_bPassthrough)
- {
- CLog::Log(LOGWARNING, "PulseAudio: Does not support passthrough");
- return false;
- }
-#endif
-
- std::vector<CStdString> hostdevice;
- CUtil::Tokenize(device, hostdevice, "@");
-
- const char *host = (hostdevice.size() < 2 || hostdevice[1].Equals("default") ? NULL : hostdevice[1].c_str());
- if (!SetupContext(host, &m_Context, &m_MainLoop))
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to create context");
- Deinitialize();
- return false;
- }
-
- pa_threaded_mainloop_lock(m_MainLoop);
-
-
- struct pa_channel_map map;
-
- // Build the channel map, we dont need to remap, but we still need PCMRemap to handle mono to dual mono stereo
- map.channels = iChannels;
- if (outLayout)
- {
- for(int ch = 0; ch < iChannels; ++ch)
- {
- switch(outLayout[ch])
- {
- case PCM_INVALID : break;
- case PCM_FRONT_LEFT : map.map[ch] = PA_CHANNEL_POSITION_FRONT_LEFT ; break;
- case PCM_FRONT_RIGHT : map.map[ch] = PA_CHANNEL_POSITION_FRONT_RIGHT ; break;
- case PCM_FRONT_CENTER : map.map[ch] = PA_CHANNEL_POSITION_FRONT_CENTER ; break;
- case PCM_BACK_CENTER : map.map[ch] = PA_CHANNEL_POSITION_REAR_CENTER ; break;
- case PCM_BACK_LEFT : map.map[ch] = PA_CHANNEL_POSITION_REAR_LEFT ; break;
- case PCM_BACK_RIGHT : map.map[ch] = PA_CHANNEL_POSITION_REAR_RIGHT ; break;
- case PCM_LOW_FREQUENCY : map.map[ch] = PA_CHANNEL_POSITION_LFE ; break;
- case PCM_FRONT_LEFT_OF_CENTER : map.map[ch] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ; break;
- case PCM_FRONT_RIGHT_OF_CENTER: map.map[ch] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; break;
- case PCM_SIDE_LEFT : map.map[ch] = PA_CHANNEL_POSITION_SIDE_LEFT ; break;
- case PCM_SIDE_RIGHT : map.map[ch] = PA_CHANNEL_POSITION_SIDE_RIGHT ; break;
- case PCM_TOP_CENTER : map.map[ch] = PA_CHANNEL_POSITION_TOP_CENTER ; break;
- case PCM_TOP_FRONT_LEFT : map.map[ch] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT ; break;
- case PCM_TOP_FRONT_RIGHT : map.map[ch] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ; break;
- case PCM_TOP_FRONT_CENTER : map.map[ch] = PA_CHANNEL_POSITION_TOP_CENTER ; break;
- case PCM_TOP_BACK_LEFT : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_LEFT ; break;
- case PCM_TOP_BACK_RIGHT : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT ; break;
- case PCM_TOP_BACK_CENTER : map.map[ch] = PA_CHANNEL_POSITION_TOP_REAR_CENTER ; break;
- }
- }
- }
- else
- pa_channel_map_init_auto(&map, m_uiChannels, PA_CHANNEL_MAP_ALSA);
-
- pa_cvolume_reset(&m_Volume, m_uiChannels);
-
- if(m_bPassthrough)
- {
-#if PA_CHECK_VERSION(1,0,0)
- pa_format_info *info[1];
- info[0] = pa_format_info_new();
- switch(encoded)
- {
- case ENCODED_IEC61937_AC3 : info[0]->encoding = PA_ENCODING_AC3_IEC61937 ; break;
- case ENCODED_IEC61937_DTS : info[0]->encoding = PA_ENCODING_DTS_IEC61937 ; break;
- case ENCODED_IEC61937_EAC3: info[0]->encoding = PA_ENCODING_EAC3_IEC61937; break;
- case ENCODED_IEC61937_MPEG: info[0]->encoding = PA_ENCODING_MPEG_IEC61937; break;
- default: info[0]->encoding = PA_ENCODING_INVALID ; break;
- }
- pa_format_info_set_rate(info[0], m_uiSamplesPerSec);
- pa_format_info_set_channels(info[0], m_uiChannels);
- pa_format_info_set_sample_format(info[0], PA_SAMPLE_S16NE);
- m_Stream = pa_stream_new_extended(m_Context, "audio stream", info, 1, NULL);
- pa_format_info_free(info[0]);
-#endif
- }
- else
- {
-
- pa_sample_spec spec;
- spec.channels = iChannels;
- spec.rate = uiSamplesPerSec;
- spec.format = PA_SAMPLE_S16NE;
-
- if (!pa_sample_spec_valid(&spec))
- {
- CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec");
- Deinitialize();
- return false;
- }
-
- m_Stream = pa_stream_new(m_Context, "audio stream", &spec, &map);
- }
-
- if (m_Stream == NULL)
- {
- CLog::Log(LOGERROR, "PulseAudio: Could not create a stream");
- pa_threaded_mainloop_unlock(m_MainLoop);
- Deinitialize();
- return false;
- }
-
- pa_stream_set_state_callback(m_Stream, StreamStateCallback, m_MainLoop);
- pa_stream_set_write_callback(m_Stream, StreamRequestCallback, m_MainLoop);
- pa_stream_set_latency_update_callback(m_Stream, StreamLatencyUpdateCallback, m_MainLoop);
-
- const char *sink = hostdevice.size() < 1 || hostdevice[0].Equals("default") ? NULL : hostdevice[0].c_str();
- if (pa_stream_connect_playback(m_Stream, sink, NULL, ((pa_stream_flags)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE)), &m_Volume, NULL) < 0)
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output");
- pa_threaded_mainloop_unlock(m_MainLoop);
- Deinitialize();
- return false;
- }
-
- /* Wait until the stream is ready */
- do
- {
- pa_threaded_mainloop_wait(m_MainLoop);
- CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream)));
- }
- while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED);
-
- if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED)
- {
- CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed");
- pa_threaded_mainloop_unlock(m_MainLoop);
- Deinitialize();
- return false;
- }
-
- const pa_buffer_attr *a;
-
- if (!(a = pa_stream_get_buffer_attr(m_Stream)))
- CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context)));
- else
- {
- m_dwPacketSize = a->minreq;
- CLog::Log(LOGDEBUG, "PulseAudio: Default buffer attributes, maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", a->maxlength, a->tlength, a->prebuf, a->minreq);
- pa_buffer_attr b;
- b.prebuf = (uint32_t)-1;
- b.minreq = a->minreq;
- b.tlength = m_uiBufferSize = a->tlength;
- b.maxlength = a->maxlength;
- b.fragsize = a->fragsize;
-
- WaitForOperation(pa_stream_set_buffer_attr(m_Stream, &b, NULL, NULL), m_MainLoop, "SetBuffer");
-
- if (!(a = pa_stream_get_buffer_attr(m_Stream)))
- CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context)));
- else
- {
- m_dwPacketSize = a->minreq;
- m_uiBufferSize = a->tlength;
- CLog::Log(LOGDEBUG, "PulseAudio: Choosen buffer attributes, maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", a->maxlength, a->tlength, a->prebuf, a->minreq);
- }
- }
-
- pa_threaded_mainloop_unlock(m_MainLoop);
-
- m_bIsAllocated = true;
-
- SetCurrentVolume(m_nCurrentVolume);
- Resume();
- return true;
-}
-
-CPulseAudioDirectSound::~CPulseAudioDirectSound()
-{
- Deinitialize();
-}
-
-bool CPulseAudioDirectSound::Deinitialize()
-{
- m_bIsAllocated = false;
-
- if (m_Stream)
- WaitCompletion();
-
- if (m_MainLoop)
- pa_threaded_mainloop_stop(m_MainLoop);
-
- if (m_Stream)
- {
- pa_stream_disconnect(m_Stream);
- pa_stream_unref(m_Stream);
- m_Stream = NULL;
- }
-
- if (m_Context)
- {
- pa_context_disconnect(m_Context);
- pa_context_unref(m_Context);
- m_Context = NULL;
- }
-
- if (m_MainLoop)
- {
- pa_threaded_mainloop_free(m_MainLoop);
- m_MainLoop = NULL;
- }
-
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- return true;
-}
-
-inline bool CPulseAudioDirectSound::WaitForOperation(pa_operation *op, pa_threaded_mainloop *mainloop, const char *LogEntry = "")
-{
- if (op == NULL)
- return false;
-
- bool sucess = true;
-
- while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(mainloop);
-
- if (pa_operation_get_state(op) != PA_OPERATION_DONE)
- {
- CLog::Log(LOGERROR, "PulseAudio: %s Operation failed", LogEntry);
- sucess = false;
- }
-
- pa_operation_unref(op);
- return sucess;
-}
-
-void CPulseAudioDirectSound::Flush()
-{
- if (!m_bIsAllocated)
- return;
-
- Pause();
-
- pa_threaded_mainloop_lock(m_MainLoop);
- WaitForOperation(pa_stream_flush(m_Stream, NULL, NULL), m_MainLoop, "Flush");
- m_bRecentlyFlushed = true;
- pa_threaded_mainloop_unlock(m_MainLoop);
-}
-
-bool CPulseAudioDirectSound::Cork(bool cork)
-{
- pa_threaded_mainloop_lock(m_MainLoop);
-
- if (!WaitForOperation(pa_stream_cork(m_Stream, cork ? 1 : 0, NULL, NULL), m_MainLoop, cork ? "Pause" : "Resume"))
- cork = !cork;
-
- pa_threaded_mainloop_unlock(m_MainLoop);
-
- return cork;
-}
-
-bool CPulseAudioDirectSound::Pause()
-{
- if (!m_bIsAllocated)
- return -1;
-
- if (m_bPause)
- return true;
-
- m_bPause = Cork(true);
-
- return m_bPause;
-}
-
-bool CPulseAudioDirectSound::Resume()
-{
- if (!m_bIsAllocated)
- return false;
-
- bool result = false;
-
- if(m_bPause && !m_bRecentlyFlushed)
- {
- m_bPause = Cork(false);
- result = !m_bPause;
- }
- else if (m_bPause)
- result = m_bAutoResume = true;
-
- return result;
-}
-
-bool CPulseAudioDirectSound::Stop()
-{
- if (!m_bIsAllocated)
- return false;
-
- Flush();
-
- return true;
-}
-
-long CPulseAudioDirectSound::GetCurrentVolume() const
-{
- return m_nCurrentVolume;
-}
-
-void CPulseAudioDirectSound::Mute(bool bMute)
-{
- if (!m_bIsAllocated)
- return;
-
- if (bMute)
- SetCurrentVolume(VOLUME_MINIMUM);
- else
- SetCurrentVolume(m_nCurrentVolume);
-}
-
-bool CPulseAudioDirectSound::SetCurrentVolume(long nVolume)
-{
- if (!m_bIsAllocated || m_bPassthrough)
- return -1;
-
- pa_threaded_mainloop_lock(m_MainLoop);
- pa_volume_t volume = pa_sw_volume_from_dB((float)nVolume*1.5f / 200.0f);
- if ( nVolume <= VOLUME_MINIMUM )
- pa_cvolume_mute(&m_Volume, m_uiChannels);
- else
- pa_cvolume_set(&m_Volume, m_uiChannels, volume);
- pa_operation *op = pa_context_set_sink_input_volume(m_Context, pa_stream_get_index(m_Stream), &m_Volume, NULL, NULL);
- if (op == NULL)
- CLog::Log(LOGERROR, "PulseAudio: Failed to set volume");
- else
- pa_operation_unref(op);
-
- pa_threaded_mainloop_unlock(m_MainLoop);
-
- return true;
-}
-
-unsigned int CPulseAudioDirectSound::GetSpace()
-{
- if (!m_bIsAllocated)
- return 0;
-
- size_t l;
- pa_threaded_mainloop_lock(m_MainLoop);
- l = pa_stream_writable_size(m_Stream);
- pa_threaded_mainloop_unlock(m_MainLoop);
- return (l / m_uiChannels) * m_uiDataChannels;
-}
-
-unsigned int CPulseAudioDirectSound::AddPackets(const void* data, unsigned int len)
-{
- if (!m_bIsAllocated)
- return len;
-
- pa_threaded_mainloop_lock(m_MainLoop);
-
- len = (len / m_uiDataChannels) * m_uiChannels;
- int length = std::min((int)pa_stream_writable_size(m_Stream), (int)len);
- int frames = length / m_uiChannels / (m_uiBitsPerSample >> 3);
- if (frames == 0)
- {
- pa_threaded_mainloop_unlock(m_MainLoop);
- return 0;
- }
-
- if (m_remap.CanRemap())
- {
- /* remap the data to the correct channels */
- uint8_t outData[length];
- m_remap.Remap((void *)data, outData, frames, m_drc);
- if (pa_stream_write(m_Stream, outData, length, NULL, 0, PA_SEEK_RELATIVE) < 0)
- CLog::Log(LOGERROR, "CPulseAudioDirectSound::AddPackets - pa_stream_write failed\n");
-
- }
- else
- if (pa_stream_write(m_Stream, data, length, NULL, 0, PA_SEEK_RELATIVE) < 0)
- CLog::Log(LOGERROR, "CPulseAudioDirectSound::AddPackets - pa_stream_write failed\n");
-
-
- if (m_bRecentlyFlushed)
- m_bRecentlyFlushed = false;
-
- pa_threaded_mainloop_unlock(m_MainLoop);
-
- if (m_bAutoResume)
- m_bAutoResume = !Resume();
-
- return (length / m_uiChannels) * m_uiDataChannels;
-}
-
-float CPulseAudioDirectSound::GetCacheTime()
-{
- return (float)(m_uiBufferSize - GetSpace()) / (float)m_uiBytesPerSecond;
-}
-
-float CPulseAudioDirectSound::GetCacheTotal()
-{
- return (float)m_uiBufferSize / (float)m_uiBytesPerSecond;
-}
-
-float CPulseAudioDirectSound::GetDelay()
-{
- if (!m_bIsAllocated)
- return 0;
-
- pa_usec_t latency = (pa_usec_t) -1;
- pa_threaded_mainloop_lock(m_MainLoop);
- while (pa_stream_get_latency(m_Stream, &latency, NULL) < 0)
- {
- if (pa_context_errno(m_Context) != PA_ERR_NODATA)
- {
- CLog::Log(LOGERROR, "PulseAudio: pa_stream_get_latency() failed");
- break;
- }
- /* Wait until latency data is available again */
- pa_threaded_mainloop_wait(m_MainLoop);
- }
- pa_threaded_mainloop_unlock(m_MainLoop);
- return latency / 1000000.0;
-}
-
-unsigned int CPulseAudioDirectSound::GetChunkLen()
-{
- return (m_dwPacketSize / m_uiChannels) * m_uiDataChannels;
-}
-
-int CPulseAudioDirectSound::SetPlaySpeed(int iSpeed)
-{
- return 0;
-}
-
-void CPulseAudioDirectSound::RegisterAudioCallback(IAudioCallback *pCallback)
-{
- m_pCallback = pCallback;
-}
-
-void CPulseAudioDirectSound::UnRegisterAudioCallback()
-{
- m_pCallback = NULL;
-}
-
-void CPulseAudioDirectSound::WaitCompletion()
-{
- if (!m_bIsAllocated || m_bRecentlyFlushed)
- return;
-
- pa_threaded_mainloop_lock(m_MainLoop);
- WaitForOperation(pa_stream_drain(m_Stream, NULL, NULL), m_MainLoop, "Drain");
- pa_threaded_mainloop_unlock(m_MainLoop);
-}
-
-void CPulseAudioDirectSound::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
-{
-}
-
-void CPulseAudioDirectSound::EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough)
-{
- pa_context *context;
- pa_threaded_mainloop *mainloop;
-
- if (!SetupContext(NULL, &context, &mainloop))
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to create context");
- return;
- }
-
- pa_threaded_mainloop_lock(mainloop);
-
- SinkInfoStruct sinkStruct;
- sinkStruct.passthrough = passthrough;
- sinkStruct.mainloop = mainloop;
- sinkStruct.list = &vAudioSinks;
- WaitForOperation(pa_context_get_sink_info_list(context, SinkInfo, &sinkStruct), mainloop, "EnumerateAudioSinks");
-
- pa_threaded_mainloop_unlock(mainloop);
-
- if (mainloop)
- pa_threaded_mainloop_stop(mainloop);
-
- if (context)
- {
- pa_context_disconnect(context);
- pa_context_unref(context);
- context = NULL;
- }
-
- if (mainloop)
- {
- pa_threaded_mainloop_free(mainloop);
- mainloop = NULL;
- }
-}
-
-bool CPulseAudioDirectSound::SetupContext(const char *host, pa_context **context, pa_threaded_mainloop **mainloop)
-{
- if ((*mainloop = pa_threaded_mainloop_new()) == NULL)
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to allocate main loop");
- return false;
- }
-
- if (((*context) = pa_context_new(pa_threaded_mainloop_get_api(*mainloop), "XBMC")) == NULL)
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to allocate context");
- return false;
- }
-
- pa_context_set_state_callback(*context, ContextStateCallback, *mainloop);
-
- if (pa_context_connect(*context, host, (pa_context_flags_t)0, NULL) < 0)
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to connect context");
- return false;
- }
- pa_threaded_mainloop_lock(*mainloop);
-
- if (pa_threaded_mainloop_start(*mainloop) < 0)
- {
- CLog::Log(LOGERROR, "PulseAudio: Failed to start MainLoop");
- pa_threaded_mainloop_unlock(*mainloop);
- return false;
- }
-
- /* Wait until the context is ready */
- do
- {
- pa_threaded_mainloop_wait(*mainloop);
- CLog::Log(LOGDEBUG, "PulseAudio: Context %s", ContextStateToString(pa_context_get_state(*context)));
- }
- while (pa_context_get_state(*context) != PA_CONTEXT_READY && pa_context_get_state(*context) != PA_CONTEXT_FAILED);
-
- if (pa_context_get_state(*context) == PA_CONTEXT_FAILED)
- {
- CLog::Log(LOGERROR, "PulseAudio: Waited for the Context but it failed");
- pa_threaded_mainloop_unlock(*mainloop);
- return false;
- }
-
- pa_threaded_mainloop_unlock(*mainloop);
- return true;
-}
-#endif
-
diff --git a/xbmc/cores/AudioRenderers/PulseAudioDirectSound.h b/xbmc/cores/AudioRenderers/PulseAudioDirectSound.h
deleted file mode 100644
index 440bd6f580..0000000000
--- a/xbmc/cores/AudioRenderers/PulseAudioDirectSound.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __PULSE_AUDIO_DIRECT_SOUND_H__
-#define __PULSE_AUDIO_DIRECT_SOUND_H__
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-#include "IAudioRenderer.h"
-#include "cores/IAudioCallback.h"
-
-#include <pulse/pulseaudio.h>
-
-#include "../../utils/PCMAmplifier.h"
-
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-class CPulseAudioDirectSound : public IAudioRenderer
-{
-public:
- virtual void UnRegisterAudioCallback();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback);
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- CPulseAudioDirectSound();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual ~CPulseAudioDirectSound();
-
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual bool Deinitialize();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void SetDynamicRangeCompression(long drc) { m_drc = drc; }
- virtual int SetPlaySpeed(int iSpeed);
- virtual void WaitCompletion();
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
-
- virtual void Flush();
-
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough);
-private:
- static bool SetupContext(const char *host, pa_context **context, pa_threaded_mainloop **mainloop);
- bool Cork(bool cork);
- static inline bool WaitForOperation(pa_operation *op, pa_threaded_mainloop *mainloop, const char *LogEntry);
-
- IAudioCallback* m_pCallback;
-
- long m_nCurrentVolume;
- long m_drc;
- unsigned int m_dwPacketSize;
- unsigned int m_dwNumPackets;
-
- bool m_bIsAllocated;
-
- unsigned int m_uiBytesPerSecond;
- unsigned int m_uiBufferSize;
- unsigned int m_uiSamplesPerSec;
- unsigned int m_uiBitsPerSample;
- unsigned int m_uiDataChannels;
- unsigned int m_uiChannels;
- bool m_bPause, m_bRecentlyFlushed, m_bAutoResume;
- bool m_bPassthrough;
-
- pa_stream *m_Stream;
- pa_cvolume m_Volume;
-
- pa_context *m_Context;
- pa_threaded_mainloop *m_MainLoop;
-};
-
-#endif
-
diff --git a/xbmc/cores/AudioRenderers/Win32DirectSound.cpp b/xbmc/cores/AudioRenderers/Win32DirectSound.cpp
deleted file mode 100644
index 1ac873287a..0000000000
--- a/xbmc/cores/AudioRenderers/Win32DirectSound.cpp
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
-* XBMC Media Center
-* Copyright (c) 2002 d7o3g4q and RUNTiME
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* 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 for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "threads/SystemClock.h"
-#include "system.h" // WIN32INCLUDES needed for the directsound stuff below
-#include "Win32DirectSound.h"
-#include "guilib/AudioContext.h"
-#include "settings/Settings.h"
-#include <initguid.h>
-#include <Mmreg.h>
-#include "threads/SingleLock.h"
-#include "utils/SystemInfo.h"
-#include "utils/log.h"
-#include "utils/TimeUtils.h"
-#include "utils/CharsetConverter.h"
-
-#ifdef HAS_DX
-#pragma comment(lib, "dxguid.lib")
-#endif
-
-DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-
-const enum PCMChannels dsound_default_channel_layout[][8] =
-{
- {PCM_FRONT_CENTER},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_SIDE_LEFT, PCM_SIDE_RIGHT}
-};
-
-const enum PCMChannels dsound_channel_order[] = {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_FRONT_LEFT_OF_CENTER, PCM_FRONT_RIGHT_OF_CENTER, PCM_BACK_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT};
-
-#define DSOUND_TOTAL_CHANNELS 11
-
-static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
-{
- AudioSinkList& enumerator = *static_cast<AudioSinkList*>(lpContext);
-
- CStdString device(lpcstrDescription);
- g_charsetConverter.unknownToUTF8(device);
-
- enumerator.push_back(AudioSink(CStdString("DirectSound: ").append(device), CStdString("directsound:").append(device)));
-
- return TRUE;
-}
-
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
-//***********************************************************************************************
-CWin32DirectSound::CWin32DirectSound() :
- m_Passthrough(false),
- m_AvgBytesPerSec(0),
- m_CacheLen(0),
- m_dwChunkSize(0),
- m_dwDataChunkSize(0),
- m_dwBufferLen(0),
- m_PreCacheSize(0),
- m_LastCacheCheck(0)
-{
-}
-
-bool CWin32DirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels* channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded bAudioPassthrough)
-{
- m_uiDataChannels = iChannels;
-
- if(!bAudioPassthrough && channelMap)
- {
- PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);
-
- for(iChannels = 0; outLayout[iChannels] != PCM_INVALID;) ++iChannels;
-
- BuildChannelMapping(iChannels, outLayout);
- m_remap.SetOutputFormat(iChannels, m_SpeakerOrder, false);
- }
-
- bool bAudioOnAllSpeakers(false);
- g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
- if(bAudioPassthrough)
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL);
- else
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
- m_pDSound=g_audioContext.GetDirectSoundDevice();
-
- m_bPause = false;
- m_bIsAllocated = false;
- m_pBuffer = NULL;
- m_uiChannels = iChannels;
- m_uiSamplesPerSec = uiSamplesPerSec;
- m_uiBitsPerSample = uiBitsPerSample;
- m_Passthrough = (bAudioPassthrough != ENCODED_NONE);
-
- m_nCurrentVolume = g_settings.m_nVolumeLevel;
- m_drc = 0;
-
- WAVEFORMATEXTENSIBLE wfxex = {0};
-
- //fill waveformatex
- ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
- wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- wfxex.Format.nChannels = iChannels;
- wfxex.Format.nSamplesPerSec = uiSamplesPerSec;
- if (bAudioPassthrough)
- {
- wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
- wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
- wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
- wfxex.Format.wBitsPerSample = 16;
- wfxex.Format.nChannels = 2;
- }
- else
- {
- wfxex.dwChannelMask = m_uiSpeakerMask;
-
- if (iChannels > 2)
- wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- else
- wfxex.Format.wFormatTag = WAVE_FORMAT_PCM;
-
- wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
- wfxex.Format.wBitsPerSample = uiBitsPerSample;
- }
-
- wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
- wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
- wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
-
- m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;
-
- m_uiBytesPerFrame = wfxex.Format.nBlockAlign;
- m_uiDataBytesPerFrame = (wfxex.Format.nBlockAlign / iChannels) * m_uiDataChannels;
-
- // unsure if these are the right values
- m_dwChunkSize = wfxex.Format.nBlockAlign * 3096;
- m_dwDataChunkSize = (m_dwChunkSize / iChannels) * m_uiDataChannels;
- m_dwBufferLen = m_dwChunkSize * 16;
- m_PreCacheSize = m_dwBufferLen - 2*m_dwChunkSize;
-
- CLog::Log(LOGDEBUG, __FUNCTION__": Packet Size = %d. Avg Bytes Per Second = %d.", m_dwChunkSize, m_AvgBytesPerSec);
-
- // fill in the secondary sound buffer descriptor
- DSBUFFERDESC dsbdesc;
- memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
- | DSBCAPS_GLOBALFOCUS /** Allows background playing */
- | DSBCAPS_CTRLVOLUME; /** volume control enabled */
-
- if (!g_sysinfo.IsVistaOrHigher())
- dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; /** Needed for 5.1 on emu101k, always fails on Vista, by design */
-
- dsbdesc.dwBufferBytes = m_dwBufferLen;
- dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;
-
- // now create the stream buffer
- HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
- if (res != DS_OK)
- {
- if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
- {
- SAFE_RELEASE(m_pBuffer);
- CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res));
- // Try without DSBCAPS_LOCHARDWARE
- dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
- res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
- }
- if (res != DS_OK && dsbdesc.dwFlags & DSBCAPS_CTRLVOLUME)
- {
- SAFE_RELEASE(m_pBuffer);
- CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without CTRLVOLUME.", dserr2str(res));
- // Try without DSBCAPS_CTRLVOLUME
- dsbdesc.dwFlags &= ~DSBCAPS_CTRLVOLUME;
- res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
- }
- if (res != DS_OK)
- {
- SAFE_RELEASE(m_pBuffer);
- CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
- return false;
- }
- }
- CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");
-
- m_pBuffer->Stop();
-
- if (DSERR_CONTROLUNAVAIL == m_pBuffer->SetVolume(g_settings.m_nVolumeLevel))
- CLog::Log(LOGINFO, __FUNCTION__": Volume control is unavailable in the current configuration");
-
- m_bIsAllocated = true;
- m_BufferOffset = 0;
- m_CacheLen = 0;
- m_LastCacheCheck = XbmcThreads::SystemClockMillis();
-
- return m_bIsAllocated;
-}
-
-//***********************************************************************************************
-CWin32DirectSound::~CWin32DirectSound()
-{
- Deinitialize();
-}
-
-//***********************************************************************************************
-bool CWin32DirectSound::Deinitialize()
-{
- if (m_bIsAllocated)
- {
- CLog::Log(LOGDEBUG, __FUNCTION__": Cleaning up");
- m_bIsAllocated = false;
- if (m_pBuffer)
- {
- m_pBuffer->Stop();
- SAFE_RELEASE(m_pBuffer);
- }
-
- m_pBuffer = NULL;
- m_pDSound = NULL;
- m_BufferOffset = 0;
- m_CacheLen = 0;
- m_dwChunkSize = 0;
- m_dwBufferLen = 0;
-
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- }
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32DirectSound::Pause()
-{
- CSingleLock lock (m_critSection);
- if (m_bPause) // Already paused
- return true;
- m_bPause = true;
- m_pBuffer->Stop();
-
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32DirectSound::Resume()
-{
- CSingleLock lock (m_critSection);
- if (!m_bPause) // Already playing
- return true;
- m_bPause = false;
- if (m_CacheLen >= m_PreCacheSize) // Make sure we have some data to play (if not, playback will start when we add some)
- m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
-
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32DirectSound::Stop()
-{
- CSingleLock lock (m_critSection);
- // Stop and reset DirectSound buffer
- m_pBuffer->Stop();
- m_pBuffer->SetCurrentPosition(0);
-
- // Reset buffer management members
- m_BufferOffset = 0;
- m_CacheLen = 0;
- m_bPause = false;
-
- return true;
-}
-
-//***********************************************************************************************
-long CWin32DirectSound::GetCurrentVolume() const
-{
- return m_nCurrentVolume;
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::Mute(bool bMute)
-{
- CSingleLock lock (m_critSection);
- if (!m_bIsAllocated) return;
- if (bMute)
- m_pBuffer->SetVolume(VOLUME_MINIMUM);
- else
- m_pBuffer->SetVolume(m_nCurrentVolume);
-}
-
-//***********************************************************************************************
-bool CWin32DirectSound::SetCurrentVolume(long nVolume)
-{
- CSingleLock lock (m_critSection);
- if (!m_bIsAllocated) return false;
- m_nCurrentVolume = nVolume;
- return m_pBuffer->SetVolume( m_nCurrentVolume ) == S_OK;
-}
-
-//***********************************************************************************************
-unsigned int CWin32DirectSound::AddPackets(const void* data, unsigned int len)
-{
- CSingleLock lock (m_critSection);
- DWORD total = len;
- unsigned char* pBuffer = (unsigned char*)data;
-
- DWORD bufferStatus = 0;
- m_pBuffer->GetStatus(&bufferStatus);
- if (bufferStatus & DSBSTATUS_BUFFERLOST)
- {
- CLog::Log(LOGDEBUG, __FUNCTION__ ": Buffer allocation was lost. Restoring buffer.");
- m_pBuffer->Restore();
- }
-
- while (len >= m_dwDataChunkSize && GetSpace() >= m_dwDataChunkSize) // We want to write at least one chunk at a time
- {
- LPVOID start = NULL, startWrap = NULL;
- DWORD size = 0, sizeWrap = 0;
- if (m_BufferOffset >= m_dwBufferLen) // Wrap-around manually
- m_BufferOffset = 0;
- HRESULT res = m_pBuffer->Lock(m_BufferOffset, m_dwChunkSize, &start, &size, &startWrap, &sizeWrap, 0);
- if (DS_OK != res)
- {
- CLog::Log(LOGERROR, __FUNCTION__ ": Unable to lock buffer at offset %u. HRESULT: 0x%08x", m_BufferOffset, res);
- break;
- }
-
- // Remap the data to the correct channels into the buffer
- if (m_remap.CanRemap())
- m_remap.Remap((void*)pBuffer, start, size / m_uiBytesPerFrame, m_drc);
- else
- memcpy(start, pBuffer, size);
-
- pBuffer += size * m_uiDataBytesPerFrame / m_uiBytesPerFrame;
- len -= size * m_uiDataBytesPerFrame / m_uiBytesPerFrame;
-
- m_BufferOffset += size;
- if (startWrap) // Write-region wraps to beginning of buffer
- {
- // Remap the data to the correct channels into the buffer
- if (m_remap.CanRemap())
- m_remap.Remap((void*)pBuffer, startWrap, sizeWrap / m_uiBytesPerFrame, m_drc);
- else
- memcpy(startWrap, pBuffer, sizeWrap);
- m_BufferOffset = sizeWrap;
-
- pBuffer += sizeWrap * m_uiDataBytesPerFrame / m_uiBytesPerFrame;
- len -= sizeWrap * m_uiDataBytesPerFrame / m_uiBytesPerFrame;
- }
-
- m_CacheLen += size + sizeWrap; // This data is now in the cache
- m_pBuffer->Unlock(start, size, startWrap, sizeWrap);
- }
-
- CheckPlayStatus();
-
- return total - len; // Bytes used
-}
-
-void CWin32DirectSound::UpdateCacheStatus()
-{
- CSingleLock lock (m_critSection);
- // TODO: Check to see if we may have cycled around since last time
- unsigned int time = XbmcThreads::SystemClockMillis();
- if (time == m_LastCacheCheck)
- return; // Don't recalc more frequently than once/ms (that is our max resolution anyway)
-
- DWORD playCursor = 0, writeCursor = 0;
- HRESULT res = m_pBuffer->GetCurrentPosition(&playCursor, &writeCursor); // Get the current playback and safe write positions
- if (DS_OK != res)
- {
- CLog::Log(LOGERROR,__FUNCTION__ ": GetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
- return;
- }
-
- m_LastCacheCheck = time;
- // Check the state of the ring buffer (P->O->W == underrun)
- // These are the logical situations that can occur
- // O: CurrentOffset W: WriteCursor P: PlayCursor
- // | | | | | | | | | |
- // ***O----W----P***** < underrun P > W && O < W (1)
- // | | | | | | | | | |
- // ---P****O----W----- < underrun O > P && O < W (2)
- // | | | | | | | | | |
- // ---W----P****O----- < underrun P > W && P < O (3)
- // | | | | | | | | | |
- // ***W****O----P***** P > W && P > O (4)
- // | | | | | | | | | |
- // ---P****W****O----- P < W && O > W (5)
- // | | | | | | | | | |
- // ***O----P****W***** P < W && O < P (6)
-
- // Check for underruns
- if ((playCursor > writeCursor && m_BufferOffset < writeCursor) || // (1)
- (playCursor < m_BufferOffset && m_BufferOffset < writeCursor) || // (2)
- (playCursor > writeCursor && playCursor < m_BufferOffset)) // (3)
- {
- CLog::Log(LOGWARNING, "CWin32DirectSound::GetSpace - buffer underrun - W:%u, P:%u, O:%u.", writeCursor, playCursor, m_BufferOffset);
- m_BufferOffset = writeCursor; // Catch up
- m_pBuffer->Stop(); // Wait until someone gives us some data to restart playback (prevents glitches)
- }
-
- // Calculate available space in the ring buffer
- if (playCursor == m_BufferOffset && m_BufferOffset == writeCursor) // Playback is stopped and we are all at the same place
- m_CacheLen = 0;
- else if (m_BufferOffset > playCursor)
- m_CacheLen = m_BufferOffset - playCursor;
- else
- m_CacheLen = m_dwBufferLen - (playCursor - m_BufferOffset);
-}
-
-void CWin32DirectSound::CheckPlayStatus()
-{
- DWORD status = 0;
- m_pBuffer->GetStatus(&status);
-
- if(!m_bPause && !(status & DSBSTATUS_PLAYING) && m_CacheLen >= m_PreCacheSize) // If we have some data, see if we can start playback
- {
- m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
- CLog::Log(LOGDEBUG,__FUNCTION__ ": Resuming Playback");
- }
-}
-
-unsigned int CWin32DirectSound::GetSpace()
-{
- CSingleLock lock (m_critSection);
- UpdateCacheStatus();
- unsigned int space = ((m_dwBufferLen - m_CacheLen) / m_uiChannels) * m_uiDataChannels;
-
- // We can never allow the internal buffers to fill up complete
- // as we get confused between if the buffer is full or empty
- // so never allow the last chunk to be added
- if(space > m_dwDataChunkSize)
- return space - m_dwDataChunkSize;
- else
- return 0;
-}
-
-//***********************************************************************************************
-float CWin32DirectSound::GetDelay()
-{
- // Make sure we know how much data is in the cache
- UpdateCacheStatus();
-
- CSingleLock lock (m_critSection);
- float delay = 0.008f; // WTF?
- delay += (float)m_CacheLen / (float)m_AvgBytesPerSec;
- return delay;
-}
-
-//***********************************************************************************************
-float CWin32DirectSound::GetCacheTime()
-{
- CSingleLock lock (m_critSection);
- // Make sure we know how much data is in the cache
- UpdateCacheStatus();
-
- return (float)m_CacheLen / (float)m_AvgBytesPerSec;
-}
-
-float CWin32DirectSound::GetCacheTotal()
-{
- CSingleLock lock (m_critSection);
- return (float)(m_dwBufferLen - m_dwDataChunkSize) / (float)m_AvgBytesPerSec;
-}
-
-//***********************************************************************************************
-unsigned int CWin32DirectSound::GetChunkLen()
-{
- return m_dwDataChunkSize;
-}
-
-//***********************************************************************************************
-int CWin32DirectSound::SetPlaySpeed(int iSpeed)
-{
- return 0;
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::RegisterAudioCallback(IAudioCallback *pCallback)
-{
- m_pCallback = pCallback;
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::UnRegisterAudioCallback()
-{
- m_pCallback = NULL;
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::WaitCompletion()
-{
- CSingleLock lock (m_critSection);
- DWORD status;
- unsigned int timeout;
- unsigned char* silence;
-
- if (!m_pBuffer)
- return ;
-
- if(FAILED(m_pBuffer->GetStatus(&status)) || (status & DSBSTATUS_PLAYING) == 0)
- return; // We weren't playing anyway
-
- // The drain should complete in the time occupied by the cache
- timeout = (unsigned int)(1000 * GetDelay());
- unsigned int startTime = XbmcThreads::SystemClockMillis();
- silence = (unsigned char*)calloc(1,m_dwChunkSize); // Initialize 'silence' to zero...
-
- while(AddPackets(silence, m_dwChunkSize) == 0)
- {
- if(FAILED(m_pBuffer->GetStatus(&status)) || (status & DSBSTATUS_PLAYING) == 0)
- break;
-
- if((XbmcThreads::SystemClockMillis() - startTime) > timeout)
- {
- CLog::Log(LOGWARNING, __FUNCTION__ ": timeout adding silence to buffer");
- break;
- }
- }
- free(silence);
-
- while(m_CacheLen)
- {
- if(FAILED(m_pBuffer->GetStatus(&status)) || (status & DSBSTATUS_PLAYING) == 0)
- break;
-
- if((XbmcThreads::SystemClockMillis() - startTime) > timeout)
- {
- CLog::Log(LOGDEBUG, "CWin32DirectSound::WaitCompletion - timeout waiting for silence");
- break;
- }
- else
- Sleep(1);
- GetSpace();
- }
-
- m_pBuffer->Stop();
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
-{
- return;
-}
-
-//***********************************************************************************************
-
-void CWin32DirectSound::EnumerateAudioSinks(AudioSinkList &vAudioSinks, bool passthrough)
-{
- if (FAILED(DirectSoundEnumerate(DSEnumCallback, &vAudioSinks)))
- CLog::Log(LOGERROR, "%s - failed to enumerate output devices", __FUNCTION__);
-}
-
-//***********************************************************************************************
-char * CWin32DirectSound::dserr2str(int err)
-{
- switch (err)
- {
- case DS_OK: return "DS_OK";
- case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
- case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
- case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
- case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
- case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
- case DSERR_GENERIC: return "DSERR_GENERIC";
- case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
- case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
- case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
- case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
- case DSERR_NODRIVER: return "DSERR_NODRIVER";
- case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
- case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
- case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
- case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
- case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
- case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
- case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
- default: return "unknown";
- }
-}
-
-//***********************************************************************************************
-void CWin32DirectSound::BuildChannelMapping(int channels, enum PCMChannels* map)
-{
- bool usedChannels[DSOUND_TOTAL_CHANNELS];
-
- memset(usedChannels, false, sizeof(usedChannels));
-
- m_uiSpeakerMask = 0;
-
- if(!map)
- map = (PCMChannels *)dsound_default_channel_layout[channels - 1];
-
- //Build the speaker mask and note which are used.
- for(int i = 0; i < channels; i++)
- {
- switch(map[i])
- {
- case PCM_FRONT_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_LEFT;
- break;
- case PCM_FRONT_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_RIGHT;
- break;
- case PCM_FRONT_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_CENTER;
- break;
- case PCM_LOW_FREQUENCY:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_LOW_FREQUENCY;
- break;
- case PCM_BACK_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_LEFT;
- break;
- case PCM_BACK_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_RIGHT;
- break;
- case PCM_FRONT_LEFT_OF_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_LEFT_OF_CENTER;
- break;
- case PCM_FRONT_RIGHT_OF_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_RIGHT_OF_CENTER;
- break;
- case PCM_BACK_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_CENTER;
- break;
- case PCM_SIDE_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_SIDE_LEFT;
- break;
- case PCM_SIDE_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_SIDE_RIGHT;
- break;
- }
- }
-
- //Assemble a compacted channel set.
- for(int i = 0, j = 0; i < DSOUND_TOTAL_CHANNELS; i++)
- {
- if(usedChannels[i])
- {
- m_SpeakerOrder[j] = dsound_channel_order[i];
- j++;
- }
- }
-}
diff --git a/xbmc/cores/AudioRenderers/Win32DirectSound.h b/xbmc/cores/AudioRenderers/Win32DirectSound.h
deleted file mode 100644
index b8dece37a9..0000000000
--- a/xbmc/cores/AudioRenderers/Win32DirectSound.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-* XBMC Media Center
-* Copyright (c) 2002 d7o3g4q and RUNTiME
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* 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; either version 2 of the License, or
-* (at your option) any later version.
-*
-* 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 for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-// AsyncAudioRenderer.h: interface for the CAsyncDirectSound class.
-//
-//////////////////////////////////////////////////////////////////////
-
-#if !defined(AFX_ASYNCAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_)
-#define AFX_ASYNCAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-#include "IAudioRenderer.h"
-#include "threads/CriticalSection.h"
-
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-class CWin32DirectSound : public IAudioRenderer
-{
-public:
- virtual void UnRegisterAudioCallback();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback);
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- CWin32DirectSound();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels* channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
- virtual ~CWin32DirectSound();
-
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual bool Deinitialize();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void SetDynamicRangeCompression(long drc) { m_drc = drc; }
- virtual int SetPlaySpeed(int iSpeed);
- virtual void WaitCompletion();
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
-
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough);
-
-private:
- void UpdateCacheStatus();
- void CheckPlayStatus();
- void BuildChannelMapping(int channels, enum PCMChannels* map);
-
- LPDIRECTSOUNDBUFFER m_pBuffer;
- LPDIRECTSOUND8 m_pDSound;
-
- IAudioCallback* m_pCallback;
-
- long m_nCurrentVolume;
- long m_drc;
- unsigned int m_dwChunkSize;
- unsigned int m_dwDataChunkSize;
- unsigned int m_dwBufferLen;
- bool m_bPause;
- bool m_bIsAllocated;
-
- bool m_Passthrough;
- unsigned int m_uiSamplesPerSec;
- unsigned int m_uiBitsPerSample;
- unsigned int m_uiChannels;
- unsigned int m_uiDataChannels;
- unsigned int m_AvgBytesPerSec;
- unsigned int m_uiBytesPerFrame;
- unsigned int m_uiDataBytesPerFrame;
- unsigned int m_uiSpeakerMask;
- enum PCMChannels m_SpeakerOrder[8];
-
- char * dserr2str(int err);
-
- unsigned int m_BufferOffset;
- unsigned int m_CacheLen;
-
- unsigned int m_LastCacheCheck;
- size_t m_PreCacheSize;
-
- CCriticalSection m_critSection;
-};
-
-#endif // !defined(AFX_ASYNCAUDIORENDERER_H__B590A94D_D15E_43A6_A41D_527BD441B5F5__INCLUDED_)
diff --git a/xbmc/cores/AudioRenderers/Win32WASAPI.cpp b/xbmc/cores/AudioRenderers/Win32WASAPI.cpp
deleted file mode 100644
index d5b383af5e..0000000000
--- a/xbmc/cores/AudioRenderers/Win32WASAPI.cpp
+++ /dev/null
@@ -1,754 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "threads/SystemClock.h"
-#include "system.h" // WIN32INCLUDES needed for the WASAPI stuff below
-
-#include <mmdeviceapi.h>
-#include <Audioclient.h>
-#include <Functiondiscoverykeys_devpkey.h>
-#include <avrt.h>
-#include <initguid.h>
-#include <Mmreg.h>
-#include "Win32WASAPI.h"
-#include "guilib/AudioContext.h"
-#include "settings/Settings.h"
-#include "threads/SingleLock.h"
-#include "utils/SystemInfo.h"
-#include "utils/log.h"
-#include "utils/TimeUtils.h"
-#include "utils/CharsetConverter.h"
-
-#pragma comment(lib, "Avrt.lib")
-
-const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
-const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
-const IID IID_IAudioClient = __uuidof(IAudioClient);
-const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
-
-DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-
-const enum PCMChannels wasapi_default_channel_layout[][8] =
-{
- {PCM_FRONT_CENTER},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_SIDE_LEFT, PCM_SIDE_RIGHT}
-};
-
-const enum PCMChannels wasapi_channel_order[] = {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_FRONT_LEFT_OF_CENTER, PCM_FRONT_RIGHT_OF_CENTER, PCM_BACK_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT};
-
-#define WASAPI_TOTAL_CHANNELS 11
-
-#define EXIT_ON_FAILURE(hr, reason, ...) if(FAILED(hr)) {CLog::Log(LOGERROR, reason, __VA_ARGS__); goto failed;}
-
-//This needs to be static since only one exclusive stream can exist at one time.
-bool CWin32WASAPI::m_bIsAllocated = false;
-
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
-//***********************************************************************************************
-CWin32WASAPI::CWin32WASAPI() :
- m_bPassthrough(false),
- m_uiAvgBytesPerSec(0),
- m_CacheLen(0),
- m_uiChunkSize(0),
- m_uiSrcChunkSize(0),
- m_uiBufferLen(0),
- m_PreCacheSize(0),
- m_LastCacheCheck(0),
- m_pAudioClient(NULL),
- m_pRenderClient(NULL),
- m_pDevice(NULL)
-{
-}
-
-bool CWin32WASAPI::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded bAudioPassthrough)
-{
- CLog::Log(LOGDEBUG, __FUNCTION__": endpoint device %s", device.c_str());
-
- //First check if the version of Windows we are running on even supports WASAPI.
- if (!g_sysinfo.IsVistaOrHigher())
- {
- CLog::Log(LOGERROR, __FUNCTION__": WASAPI output requires Vista or higher.");
- return false;
- }
-
- //Only one exclusive stream may be initialized at one time.
- if(m_bIsAllocated)
- {
- CLog::Log(LOGERROR, __FUNCTION__": Cannot create more then one WASAPI stream at one time.");
- return false;
- }
-
- int layoutChannels = 0;
-
- if(!bAudioPassthrough && channelMap)
- {
- PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);
-
- for(PCMChannels *channel = outLayout; *channel != PCM_INVALID; channel++)
- ++layoutChannels;
-
- //Expand monural to stereo as most devices don't seem to like 1 channel PCM streams.
- //Stereo sources should be sent explicitly as two channels so that the external hardware
- //can apply ProLogic/5CH Stereo/etc processing on it.
- if(iChannels <= 2)
- {
- BuildChannelMapping(2, (PCMChannels *)wasapi_default_channel_layout[1]);
-
- layoutChannels = 2;
- m_remap.SetOutputFormat(2, m_SpeakerOrder, false);
- }
- else //Do the standard remapping.
- {
- BuildChannelMapping(layoutChannels, outLayout);
- m_remap.SetOutputFormat(layoutChannels, m_SpeakerOrder, false);
- }
- }
-
- m_bPlaying = false;
- m_bPause = false;
- m_bMuting = false;
- m_uiChannels = iChannels;
- m_uiBitsPerSample = uiBitsPerSample;
- m_bPassthrough = (bAudioPassthrough != ENCODED_NONE);
-
- m_nCurrentVolume = g_settings.m_nVolumeLevel;
- m_pcmAmplifier.SetVolume(m_nCurrentVolume);
- m_drc = 0;
-
- WAVEFORMATEXTENSIBLE wfxex = {0};
-
- //fill waveformatex
- ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
- wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- wfxex.Format.nChannels = layoutChannels;
- wfxex.Format.nSamplesPerSec = uiSamplesPerSec;
- if (bAudioPassthrough)
- {
- wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
- wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
- wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
- wfxex.Format.wBitsPerSample = 16;
- wfxex.Format.nChannels = 2;
- }
- else
- {
- wfxex.dwChannelMask = m_uiSpeakerMask;
- wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- wfxex.Format.wBitsPerSample = uiBitsPerSample;
- }
-
- wfxex.Samples.wValidBitsPerSample = uiBitsPerSample == 32 ? 24 : uiBitsPerSample;
- wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
- wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
-
- m_uiAvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;
-
- m_uiBytesPerFrame = wfxex.Format.nBlockAlign;
- m_uiBytesPerSrcFrame = bAudioPassthrough ? m_uiBytesPerFrame : iChannels * wfxex.Format.wBitsPerSample >> 3;
-
- IMMDeviceEnumerator* pEnumerator = NULL;
- IMMDeviceCollection* pEnumDevices = NULL;
-
- //Shut down Directsound.
- g_audioContext.SetActiveDevice(CAudioContext::NONE);
-
- HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %i", hr)
-
- //Get our device.
- //First try to find the named device.
- UINT uiCount = 0;
-
- hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
-
- hr = pEnumDevices->GetCount(&uiCount);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
-
- for(UINT i = 0; i < uiCount; i++)
- {
- IPropertyStore *pProperty = NULL;
- PROPVARIANT varName;
-
- hr = pEnumDevices->Item(i, &m_pDevice);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.")
-
- hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")
-
- hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
- if(FAILED(hr))
- {
- CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed.");
- SAFE_RELEASE(pProperty);
- goto failed;
- }
-
- CStdStringW strRawDevName(varName.pwszVal);
- CStdString strDevName;
- g_charsetConverter.wToUTF8(strRawDevName, strDevName);
-
- if(device == strDevName)
- i = uiCount;
- else
- SAFE_RELEASE(m_pDevice);
-
- PropVariantClear(&varName);
- SAFE_RELEASE(pProperty);
- }
-
- SAFE_RELEASE(pEnumDevices);
-
- if(!m_pDevice)
- {
- CLog::Log(LOGDEBUG, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices. Trying the default device...", device.c_str());
- hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pDevice);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the default WASAPI audio endpoint.")
- }
-
- //We are done with the enumerator.
- SAFE_RELEASE(pEnumerator);
-
- hr = m_pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&m_pAudioClient);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Activating the WASAPI endpoint device failed.")
-
- hr = m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Audio format not supported by the WASAPI device. Channels: %i, Rate: %i, Bits/sample: %i.", iChannels, uiSamplesPerSec, uiBitsPerSample)
-
- REFERENCE_TIME hnsRequestedDuration, hnsPeriodicity;
- hr = m_pAudioClient->GetDevicePeriod(&hnsPeriodicity, NULL);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the WASAPI endpoint device period.");
-
- //The default periods of some devices are VERY low (less than 3ms).
- //For audio stability make sure we have at least an 8ms buffer.
- if(hnsPeriodicity < 80000) hnsPeriodicity = 80000;
-
- hnsRequestedDuration = hnsPeriodicity * 16;
-
- // now create the stream buffer
- hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, 0, hnsRequestedDuration, hnsPeriodicity, &wfxex.Format, NULL);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not initialize the WASAPI endpoint device. %i", hr)
-
- hr = m_pAudioClient->GetBufferSize(&m_uiBufferLen);
- m_uiBufferLen *= m_uiBytesPerFrame;
-
- //Chunk sizes are 1/16 the buffer size.
- //WASAPI chunk sizes need to be evenly divisable into the buffer size or pops and clicks will result.
- m_uiChunkSize = m_uiBufferLen / 16;
- m_uiSrcChunkSize = (m_uiChunkSize / m_uiBytesPerFrame) * m_uiBytesPerSrcFrame;
-
- m_PreCacheSize = m_uiBufferLen - m_uiChunkSize;
-
- CLog::Log(LOGDEBUG, __FUNCTION__": Packet Size = %d. Avg Bytes Per Second = %d.", m_uiChunkSize, m_uiAvgBytesPerSec);
-
- hr = m_pAudioClient->GetService(IID_IAudioRenderClient, (void**)&m_pRenderClient);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not initialize the WASAPI render client interface.")
-
- m_bIsAllocated = true;
- m_CacheLen = 0;
- m_LastCacheCheck = XbmcThreads::SystemClockMillis();
-
- return m_bIsAllocated;
-
-failed:
- CLog::Log(LOGERROR, __FUNCTION__": WASAPI initialization failed.");
- SAFE_RELEASE(pEnumDevices);
- SAFE_RELEASE(pEnumerator);
- SAFE_RELEASE(m_pRenderClient);
- SAFE_RELEASE(m_pAudioClient);
- SAFE_RELEASE(m_pDevice);
-
- //Restart Directsound
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
-
- return false;
-}
-
-//***********************************************************************************************
-CWin32WASAPI::~CWin32WASAPI()
-{
- Deinitialize();
-}
-
-//***********************************************************************************************
-bool CWin32WASAPI::Deinitialize()
-{
- if (m_bIsAllocated)
- {
- CLog::Log(LOGDEBUG, __FUNCTION__": Cleaning up");
-
- m_pAudioClient->Stop();
-
- SAFE_RELEASE(m_pRenderClient);
- SAFE_RELEASE(m_pAudioClient);
- SAFE_RELEASE(m_pDevice);
-
- m_CacheLen = 0;
- m_uiChunkSize = 0;
- m_uiBufferLen = 0;
-
- m_bIsAllocated = false;
-
- //Restart Directsound for the interface sounds.
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- }
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32WASAPI::Pause()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return false;
-
- if (m_bPause) // Already paused
- return true;
-
- m_bPause = true;
- m_pAudioClient->Stop();
-
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32WASAPI::Resume()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return false;
-
- if(!m_bPause) // Already playing
- return true;
-
- m_bPause = false;
-
- UpdateCacheStatus();
- if(m_CacheLen >= m_PreCacheSize) // Make sure we have some data to play (if not, playback will start when we add some)
- m_pAudioClient->Start();
- else
- m_bPlaying = false; // Trigger playback restart the next time data is added to the buffer.
-
- return true;
-}
-
-//***********************************************************************************************
-bool CWin32WASAPI::Stop()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return false;
-
- // Stop and reset WASAPI buffer
- m_pAudioClient->Stop();
- m_pAudioClient->Reset();
-
- // Reset buffer management members
- m_CacheLen = 0;
- m_bPause = false;
- m_bPlaying = false;
-
- return true;
-}
-
-//***********************************************************************************************
-long CWin32WASAPI::GetCurrentVolume() const
-{
- return m_nCurrentVolume;
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::Mute(bool bMute)
-{
- CSingleLock lock (m_critSection);
-
- m_bMuting = bMute;
-}
-
-//***********************************************************************************************
-bool CWin32WASAPI::SetCurrentVolume(long nVolume)
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return false;
-
- m_nCurrentVolume = nVolume;
- m_pcmAmplifier.SetVolume(m_nCurrentVolume);
- return true;
-}
-
-//***********************************************************************************************
-unsigned int CWin32WASAPI::AddPackets(const void* data, unsigned int len)
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated || m_bPause)
- return 0;
-
- DWORD dwFlags = m_bMuting || m_nCurrentVolume == VOLUME_MINIMUM ? AUDCLNT_BUFFERFLAGS_SILENT : 0;
-
- unsigned int uiBytesToWrite, uiSrcBytesToWrite;
- BYTE* pBuffer = NULL;
- HRESULT hr;
-
- UpdateCacheStatus();
-
- uiBytesToWrite = std::min(m_uiBufferLen - m_CacheLen, (len / m_uiBytesPerSrcFrame) * m_uiBytesPerFrame);
- uiBytesToWrite /= m_uiChunkSize;
-
- uiSrcBytesToWrite = uiBytesToWrite * m_uiSrcChunkSize;
-
- uiBytesToWrite *= m_uiChunkSize;
-
- if(uiBytesToWrite == 0)
- return 0;
-
- // Get the buffer
- if (SUCCEEDED(hr = m_pRenderClient->GetBuffer(uiBytesToWrite/m_uiBytesPerFrame, &pBuffer)))
- {
- // Write data into the buffer
- AddDataToBuffer((unsigned char*)data, uiSrcBytesToWrite, pBuffer);
-
- //Adjust the volume if necessary.
- if(!m_bPassthrough)
- m_pcmAmplifier.DeAmplify((short*)pBuffer, uiBytesToWrite / 2);
-
- // Release the buffer
- if (FAILED(hr=m_pRenderClient->ReleaseBuffer(uiBytesToWrite/m_uiBytesPerFrame, dwFlags)))
- CLog::Log(LOGERROR, __FUNCTION__": ReleaseBuffer failed (%i)", hr);
- }
- else
- {
- CLog::Log(LOGERROR, __FUNCTION__": GetBuffer failed (%i)", hr);
- }
- m_CacheLen += uiBytesToWrite;
-
- CheckPlayStatus();
-
- return uiSrcBytesToWrite; // Bytes used
-}
-
-void CWin32WASAPI::UpdateCacheStatus()
-{
- unsigned int time = XbmcThreads::SystemClockMillis();
- if (time == m_LastCacheCheck)
- return; // Don't recalc more frequently than once/ms (that is our max resolution anyway)
-
- m_LastCacheCheck = time;
-
- m_pAudioClient->GetCurrentPadding(&m_CacheLen);
- m_CacheLen *= m_uiBytesPerFrame;
-}
-
-void CWin32WASAPI::CheckPlayStatus()
-{
- if(!m_bPause && !m_bPlaying && m_CacheLen >= m_PreCacheSize) // If we have some data, see if we can start playback
- {
- m_pAudioClient->Start();
- m_bPlaying = true;
- }
-}
-
-unsigned int CWin32WASAPI::GetSpace()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return 0;
-
- // Make sure we know how much data is in the cache
- UpdateCacheStatus();
-
- return ((m_uiBufferLen - m_CacheLen) / m_uiBytesPerFrame) * m_uiBytesPerSrcFrame;
-}
-
-//***********************************************************************************************
-float CWin32WASAPI::GetDelay()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return 0.0f;
-
- // Make sure we know how much data is in the cache
- UpdateCacheStatus();
-
- return (float)m_CacheLen / (float)m_uiAvgBytesPerSec;
-}
-
-//***********************************************************************************************
-float CWin32WASAPI::GetCacheTime()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return 0.0f;
-
- // Make sure we know how much data is in the cache
- UpdateCacheStatus();
-
- return (float)m_CacheLen / (float)m_uiAvgBytesPerSec;
-}
-
-//***********************************************************************************************
-float CWin32WASAPI::GetCacheTotal()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return 0.0f;
-
- return (float)m_uiBufferLen / (float)m_uiAvgBytesPerSec;
-}
-
-//***********************************************************************************************
-unsigned int CWin32WASAPI::GetChunkLen()
-{
- return m_uiSrcChunkSize;
-}
-
-//***********************************************************************************************
-int CWin32WASAPI::SetPlaySpeed(int iSpeed)
-{
- return 0;
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::RegisterAudioCallback(IAudioCallback *pCallback)
-{
- m_pCallback = pCallback;
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::UnRegisterAudioCallback()
-{
- m_pCallback = NULL;
-}
-
-//***********************************************************************************************
-
-void CWin32WASAPI::EnumerateAudioSinks(AudioSinkList &vAudioSinks, bool passthrough)
-{
- //First check if the version of Windows we are running on even supports WASAPI.
- if (!g_sysinfo.IsVistaOrHigher())
- {
- CLog::Log(LOGDEBUG, __FUNCTION__": WASAPI enumeration requires Vista or higher.");
- return;
- }
-
- IMMDeviceEnumerator* pEnumerator = NULL;
- IMMDeviceCollection* pEnumDevices = NULL;
-
- HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %i", hr)
-
- UINT uiCount = 0;
-
- hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
-
- hr = pEnumDevices->GetCount(&uiCount);
- EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
-
- for(UINT i = 0; i < uiCount; i++)
- {
- IMMDevice *pDevice = NULL;
- IPropertyStore *pProperty = NULL;
- PROPVARIANT varName;
-
- pEnumDevices->Item(i, &pDevice);
- if(FAILED(hr))
- {
- CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint failed.");
-
- goto failed;
- }
-
- hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
- if(FAILED(hr))
- {
- CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.");
- SAFE_RELEASE(pDevice);
-
- goto failed;
- }
-
- hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
- if(FAILED(hr))
- {
- CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed.");
- SAFE_RELEASE(pDevice);
- SAFE_RELEASE(pProperty);
-
- goto failed;
- }
-
- CStdStringW strRawDevName(varName.pwszVal);
- CStdString strDevName;
- g_charsetConverter.wToUTF8(strRawDevName, strDevName);
-
- CLog::Log(LOGDEBUG, __FUNCTION__": found endpoint device: %s", strDevName.c_str());
- vAudioSinks.push_back(AudioSink(CStdString("WASAPI: ").append(strDevName), CStdString("wasapi:").append(strDevName)));
-
- SAFE_RELEASE(pDevice);
-
- PropVariantClear(&varName);
- SAFE_RELEASE(pProperty);
- }
-
-failed:
-
- if(FAILED(hr))
- CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices.");
-
- SAFE_RELEASE(pEnumDevices);
- SAFE_RELEASE(pEnumerator);
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::WaitCompletion()
-{
- CSingleLock lock (m_critSection);
-
- if (!m_bIsAllocated)
- return;
-
- DWORD dwTimeRemaining;
-
- if(!m_bPlaying)
- return; // We weren't playing anyway
-
- //Calculate the remaining cache time and wait for it to finish.
- dwTimeRemaining = (DWORD)(1000 * GetDelay());
- Sleep(dwTimeRemaining);
-
- m_pAudioClient->Stop();
- m_pAudioClient->Reset();
-
- m_CacheLen = 0;
- m_bPause = false;
- m_bPlaying = false;
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::AddDataToBuffer(unsigned char* pData, unsigned int len, unsigned char* pOut)
-{
- // Remap the data to the correct channels
- if(m_remap.CanRemap() && !m_bPassthrough)
- m_remap.Remap((void*)pData, pOut, len / m_uiBytesPerSrcFrame, m_drc);
- else
- memcpy(pOut, pData, len);
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers)
-{
- return;
-}
-
-//***********************************************************************************************
-void CWin32WASAPI::BuildChannelMapping(int channels, enum PCMChannels* map)
-{
- bool usedChannels[WASAPI_TOTAL_CHANNELS];
-
- memset(usedChannels, false, sizeof(usedChannels));
-
- m_uiSpeakerMask = 0;
-
- if(!map)
- map = (PCMChannels *)wasapi_default_channel_layout[channels - 1];
-
- //Build the speaker mask and note which are used.
- for(int i = 0; i < channels; i++)
- {
- switch(map[i])
- {
- case PCM_FRONT_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_LEFT;
- break;
- case PCM_FRONT_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_RIGHT;
- break;
- case PCM_FRONT_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_CENTER;
- break;
- case PCM_LOW_FREQUENCY:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_LOW_FREQUENCY;
- break;
- case PCM_BACK_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_LEFT;
- break;
- case PCM_BACK_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_RIGHT;
- break;
- case PCM_FRONT_LEFT_OF_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_LEFT_OF_CENTER;
- break;
- case PCM_FRONT_RIGHT_OF_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_FRONT_RIGHT_OF_CENTER;
- break;
- case PCM_BACK_CENTER:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_BACK_CENTER;
- break;
- case PCM_SIDE_LEFT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_SIDE_LEFT;
- break;
- case PCM_SIDE_RIGHT:
- usedChannels[map[i]] = true;
- m_uiSpeakerMask |= SPEAKER_SIDE_RIGHT;
- break;
- }
- }
-
- //Assemble a compacted channel set.
- for(int i = 0, j = 0; i < WASAPI_TOTAL_CHANNELS; i++)
- {
- if(usedChannels[i])
- {
- m_SpeakerOrder[j] = wasapi_channel_order[i];
- j++;
- }
- }
-}
diff --git a/xbmc/cores/AudioRenderers/Win32WASAPI.h b/xbmc/cores/AudioRenderers/Win32WASAPI.h
deleted file mode 100644
index 7fd8287c85..0000000000
--- a/xbmc/cores/AudioRenderers/Win32WASAPI.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __WIN32WASAPI_H__
-#define __WIN32WASAPI_H__
-
-#if _MSC_VER > 1000
-#pragma once
-#endif // _MSC_VER > 1000
-
-
-#include "IAudioRenderer.h"
-#include "threads/CriticalSection.h"
-#include "utils/PCMAmplifier.h"
-#include <mmdeviceapi.h>
-#include <Audioclient.h>
-
-extern void RegisterAudioCallback(IAudioCallback* pCallback);
-extern void UnRegisterAudioCallback();
-
-class CWin32WASAPI : public IAudioRenderer
-{
-public:
- CWin32WASAPI();
- virtual ~CWin32WASAPI();
- virtual void UnRegisterAudioCallback();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback);
- virtual unsigned int GetChunkLen();
- virtual float GetDelay();
- virtual float GetCacheTime();
- virtual float GetCacheTotal();
- virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic=false, EEncoded encoded = IAudioRenderer::ENCODED_NONE);
-
- virtual unsigned int AddPackets(const void* data, unsigned int len);
- virtual unsigned int GetSpace();
- virtual bool Deinitialize();
- virtual bool Pause();
- virtual bool Stop();
- virtual bool Resume();
-
- virtual long GetCurrentVolume() const;
- virtual void Mute(bool bMute);
- virtual bool SetCurrentVolume(long nVolume);
- virtual void SetDynamicRangeCompression(long drc) { m_drc = drc; }
- virtual int SetPlaySpeed(int iSpeed);
- virtual void WaitCompletion();
- virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers);
-
- static void EnumerateAudioSinks(AudioSinkList& vAudioSinks, bool passthrough);
-
-private:
- void AddDataToBuffer(unsigned char* pData, unsigned int len, unsigned char* pOut);
- void UpdateCacheStatus();
- void CheckPlayStatus();
- void BuildChannelMapping(int channels, enum PCMChannels* map);
-
- IMMDevice* m_pDevice;
- IAudioClient* m_pAudioClient;
- IAudioRenderClient* m_pRenderClient;
-
- IAudioCallback* m_pCallback;
-
- long m_nCurrentVolume;
- long m_drc;
- float m_fVolAdjustFactor;
-
- unsigned int m_uiChunkSize;
- unsigned int m_uiSrcChunkSize;
- unsigned int m_uiBufferLen;
- unsigned int m_uiBytesPerFrame;
- unsigned int m_uiBytesPerSrcFrame;
- unsigned int m_uiBitsPerSample;
- unsigned int m_uiChannels;
- unsigned int m_uiAvgBytesPerSec;
- unsigned int m_uiSpeakerMask;
- enum PCMChannels m_SpeakerOrder[8];
-
- static bool m_bIsAllocated;
- bool m_bPlaying;
- bool m_bPause;
- bool m_bMuting;
- bool m_bPassthrough;
-
- unsigned int m_CacheLen;
- unsigned int m_LastCacheCheck;
- size_t m_PreCacheSize;
-
- CPCMAmplifier m_pcmAmplifier;
- CCriticalSection m_critSection;
-};
-
-#endif //__WIN32WASAPI_H__
diff --git a/xbmc/cores/DummyVideoPlayer.h b/xbmc/cores/DummyVideoPlayer.h
index 21708d59f8..82dd244b8d 100644
--- a/xbmc/cores/DummyVideoPlayer.h
+++ b/xbmc/cores/DummyVideoPlayer.h
@@ -45,7 +45,7 @@ public:
virtual void Seek(bool bPlus, bool bLargeStep);
virtual void SeekPercentage(float iPercent);
virtual float GetPercentage();
- virtual void SetVolume(long nVolume) {}
+ virtual void SetVolume(float volume) {}
virtual void SetDynamicRangeCompression(long drc) {}
virtual void SetContrast(bool bPlus) {}
virtual void SetBrightness(bool bPlus) {}
diff --git a/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp b/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp
index 20046b44e7..62c08923af 100644
--- a/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp
+++ b/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp
@@ -24,7 +24,6 @@
#include "signal.h"
#include "limits.h"
#include "threads/SingleLock.h"
-#include "guilib/AudioContext.h"
#include "ExternalPlayer.h"
#include "windowing/WindowingFactory.h"
#include "dialogs/GUIDialogOK.h"
@@ -243,13 +242,6 @@ void CExternalPlayer::Process()
strFArgs.append("\"");
}
- int iActiveDevice = g_audioContext.GetActiveDevice();
- if (iActiveDevice != CAudioContext::NONE)
- {
- CLog::Log(LOGNOTICE, "%s: Releasing audio device %d", __FUNCTION__, iActiveDevice);
- g_audioContext.SetActiveDevice(CAudioContext::NONE);
- }
-
#if defined(_WIN32)
if (m_warpcursor)
{
@@ -361,12 +353,6 @@ void CExternalPlayer::Process()
g_application.ResetScreenSaver();
g_application.WakeUpScreenSaverAndDPMS();
- if (iActiveDevice != CAudioContext::NONE)
- {
- CLog::Log(LOGNOTICE, "%s: Reclaiming audio device %d", __FUNCTION__, iActiveDevice);
- g_audioContext.SetActiveDevice(iActiveDevice);
- }
-
if (!ret || (m_playOneStackItem && g_application.CurrentFileItem().IsStack()))
m_callback.OnPlayBackStopped();
else
diff --git a/xbmc/cores/ExternalPlayer/ExternalPlayer.h b/xbmc/cores/ExternalPlayer/ExternalPlayer.h
index 5323947aa6..fc38b9a84b 100644
--- a/xbmc/cores/ExternalPlayer/ExternalPlayer.h
+++ b/xbmc/cores/ExternalPlayer/ExternalPlayer.h
@@ -50,7 +50,7 @@ public:
virtual void Seek(bool bPlus, bool bLargeStep);
virtual void SeekPercentage(float iPercent);
virtual float GetPercentage();
- virtual void SetVolume(long nVolume) {}
+ virtual void SetVolume(float volume) {}
virtual void SetDynamicRangeCompression(long drc) {}
virtual void SetContrast(bool bPlus) {}
virtual void SetBrightness(bool bPlus) {}
diff --git a/xbmc/cores/IAudioCallback.h b/xbmc/cores/IAudioCallback.h
index e402aa0334..4478a63701 100644
--- a/xbmc/cores/IAudioCallback.h
+++ b/xbmc/cores/IAudioCallback.h
@@ -36,7 +36,7 @@ public:
IAudioCallback() {};
virtual ~IAudioCallback() {};
virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) = 0;
- virtual void OnAudioData(const unsigned char* pAudioData, int iAudioDataLength) = 0;
+ virtual void OnAudioData(const float* pAudioData, int iAudioDataLength) = 0;
};
diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h
index 60bf69abfb..719e5e4634 100644
--- a/xbmc/cores/IPlayer.h
+++ b/xbmc/cores/IPlayer.h
@@ -91,7 +91,7 @@ public:
virtual void SeekPercentage(float fPercent = 0){}
virtual float GetPercentage(){ return 0;}
virtual float GetCachePercentage(){ return 0;}
- virtual void SetVolume(long nVolume){}
+ virtual void SetVolume(float volume){}
virtual void SetDynamicRangeCompression(long drc){}
virtual void GetAudioInfo( CStdString& strAudioInfo) = 0;
virtual void GetVideoInfo( CStdString& strVideoInfo) = 0;
diff --git a/xbmc/cores/dvdplayer/DVDAudio.cpp b/xbmc/cores/dvdplayer/DVDAudio.cpp
index c74ffd6e3d..6393deb1c5 100644
--- a/xbmc/cores/dvdplayer/DVDAudio.cpp
+++ b/xbmc/cores/dvdplayer/DVDAudio.cpp
@@ -26,7 +26,7 @@
#include "DVDClock.h"
#include "DVDCodecs/DVDCodecs.h"
#include "DVDPlayerAudio.h"
-#include "../AudioRenderers/AudioRendererFactory.h"
+#include "cores/AudioEngine/AEFactory.h"
#include "settings/Settings.h"
using namespace std;
@@ -34,15 +34,13 @@ using namespace std;
CDVDAudio::CDVDAudio(volatile bool &bStop)
: m_bStop(bStop)
{
- m_pAudioDecoder = NULL;
- m_pCallback = NULL;
+ m_pAudioStream = NULL;
m_iBufferSize = 0;
m_dwPacketSize = 0;
m_pBuffer = NULL;
m_bPassthrough = false;
m_iBitsPerSample = 0;
m_iBitrate = 0;
- m_iChannels = 0;
m_SecondsPerByte = 0.0;
m_bPaused = true;
}
@@ -50,71 +48,48 @@ CDVDAudio::CDVDAudio(volatile bool &bStop)
CDVDAudio::~CDVDAudio()
{
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder)
- {
- m_pAudioDecoder->Deinitialize();
- delete m_pAudioDecoder;
- }
- free(m_pBuffer);
-}
-
-void CDVDAudio::RegisterAudioCallback(IAudioCallback* pCallback)
-{
- CSingleLock lock (m_critSection);
- m_pCallback = pCallback;
- if (m_pCallback && m_pAudioDecoder && !m_bPassthrough)
- m_pCallback->OnInitialize(m_iChannels, m_iBitrate, m_iBitsPerSample);
-}
+ if (m_pAudioStream)
+ CAEFactory::AE->FreeStream(m_pAudioStream);
-void CDVDAudio::UnRegisterAudioCallback()
-{
- CSingleLock lock (m_critSection);
- m_pCallback = NULL;
+ free(m_pBuffer);
}
-bool CDVDAudio::Create(const DVDAudioFrame &audioframe, CodecID codec)
+bool CDVDAudio::Create(const DVDAudioFrame &audioframe, CodecID codec, bool needresampler)
{
- CLog::Log(LOGNOTICE, "Creating audio device with codec id: %i, channels: %i, sample rate: %i, %s", codec, audioframe.channels, audioframe.sample_rate, audioframe.passthrough ? "pass-through" : "no pass-through");
+ CLog::Log(LOGNOTICE,
+ "Creating audio stream (codec id: %i, channels: %i, sample rate: %i, %s)",
+ codec,
+ audioframe.channel_count,
+ audioframe.sample_rate,
+ audioframe.passthrough ? "pass-through" : "no pass-through"
+ );
// if passthrough isset do something else
- CSingleLock lock (m_critSection);
-
- IAudioRenderer::EEncoded encoded = IAudioRenderer::ENCODED_NONE;
- if(audioframe.passthrough)
- {
- switch(codec) {
- case CODEC_ID_AC3 : encoded = IAudioRenderer::ENCODED_IEC61937_AC3; break;
- case CODEC_ID_EAC3: encoded = IAudioRenderer::ENCODED_IEC61937_EAC3; break;
- case CODEC_ID_DTS : encoded = IAudioRenderer::ENCODED_IEC61937_DTS; break;
- case CODEC_ID_MP1 :
- case CODEC_ID_MP2 :
- case CODEC_ID_MP3 : encoded = IAudioRenderer::ENCODED_IEC61937_MPEG; break;
- default: encoded = IAudioRenderer::ENCODED_IEC61937_UNKNOWN; break;
- }
- }
-
- m_pAudioDecoder = CAudioRendererFactory::Create(m_pCallback, audioframe.channels, audioframe.channel_map, audioframe.sample_rate, audioframe.bits_per_sample, false, false, encoded);
-
- if (!m_pAudioDecoder) return false;
-
- m_iChannels = audioframe.channels;
- m_iBitrate = audioframe.sample_rate;
+ CSingleLock lock(m_critSection);
+ unsigned int options = needresampler && !audioframe.passthrough ? AESTREAM_FORCE_RESAMPLE : 0;
+ options |= AESTREAM_AUTOSTART;
+
+ m_pAudioStream = CAEFactory::AE->MakeStream(
+ audioframe.data_format,
+ audioframe.sample_rate,
+ audioframe.encoded_sample_rate,
+ audioframe.channel_layout,
+ options
+ );
+ if (!m_pAudioStream) return false;
+
+ m_iBitrate = audioframe.sample_rate;
m_iBitsPerSample = audioframe.bits_per_sample;
- m_bPassthrough = audioframe.passthrough;
- if(m_iChannels && m_iBitrate && m_iBitsPerSample)
- m_SecondsPerByte = 1.0 / (m_iChannels * m_iBitrate * (m_iBitsPerSample>>3));
+ m_bPassthrough = audioframe.passthrough;
+ m_channelLayout = audioframe.channel_layout;
+ m_dwPacketSize = m_pAudioStream->GetFrameSize();
+
+ if(m_channelLayout.Count() && m_iBitrate && m_iBitsPerSample)
+ m_SecondsPerByte = 1.0 / (m_channelLayout.Count() * m_iBitrate * (m_iBitsPerSample>>3));
else
m_SecondsPerByte = 0.0;
- m_dwPacketSize = m_pAudioDecoder->GetChunkLen();
- if(m_bPaused)
- m_pAudioDecoder->Pause();
-
m_iBufferSize = 0;
-
- if(m_pCallback && !m_bPassthrough)
- m_pCallback->OnInitialize(m_iChannels, m_iBitrate, m_iBitsPerSample);
-
SetDynamicRangeCompression((long)(g_settings.m_currentVideoSettings.m_VolumeAmplification * 100));
return true;
@@ -124,18 +99,14 @@ void CDVDAudio::Destroy()
{
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder)
- {
- m_pAudioDecoder->Stop();
- m_pAudioDecoder->Deinitialize();
- delete m_pAudioDecoder;
- }
+ if (m_pAudioStream)
+ CAEFactory::AE->FreeStream(m_pAudioStream);
+
free(m_pBuffer);
m_pBuffer = NULL;
m_dwPacketSize = 0;
- m_pAudioDecoder = NULL;
+ m_pAudioStream = NULL;
m_iBufferSize = 0;
- m_iChannels = 0;
m_iBitrate = 0;
m_iBitsPerSample = 0;
m_bPassthrough = false;
@@ -144,16 +115,16 @@ void CDVDAudio::Destroy()
DWORD CDVDAudio::AddPacketsRenderer(unsigned char* data, DWORD len, CSingleLock &lock)
{
- if(!m_pAudioDecoder)
+ if(!m_pAudioStream)
return 0;
- DWORD bps = m_iChannels * m_iBitrate * (m_iBitsPerSample>>3);
+ DWORD bps = m_channelLayout.Count() * m_iBitrate * (m_iBitsPerSample>>3);
if(!bps)
return 0;
//Calculate a timeout when this definitely should be done
double timeout;
- timeout = DVD_SEC_TO_TIME(m_pAudioDecoder->GetDelay() + len * m_SecondsPerByte);
+ timeout = DVD_SEC_TO_TIME(m_pAudioStream->GetDelay() + len * m_SecondsPerByte);
timeout += DVD_SEC_TO_TIME(1.0);
timeout += CDVDClock::GetAbsoluteClock();
@@ -161,7 +132,7 @@ DWORD CDVDAudio::AddPacketsRenderer(unsigned char* data, DWORD len, CSingleLock
DWORD copied;
do
{
- copied = m_pAudioDecoder->AddPackets(data, len);
+ copied = m_pAudioStream->AddData(data, len);
data += copied;
len -= copied;
if (len < m_dwPacketSize)
@@ -191,19 +162,6 @@ DWORD CDVDAudio::AddPackets(const DVDAudioFrame &audioframe)
DWORD total = len;
DWORD copied;
- //Feed audio to the visualizer if necessary.
- if(m_pCallback && !m_bPassthrough)
- m_pCallback->OnAudioData(data, len);
-
- // When paused, we need to buffer all data as renderers don't need to accept it
- if (m_bPaused)
- {
- m_pBuffer = (BYTE*)realloc(m_pBuffer, m_iBufferSize + len);
- memcpy(m_pBuffer+m_iBufferSize, data, len);
- m_iBufferSize += len;
- return len;
- }
-
if (m_iBufferSize > 0) // See if there are carryover bytes from the last call. need to add them 1st.
{
copied = std::min(m_dwPacketSize - m_iBufferSize % m_dwPacketSize, len); // Smaller of either the data provided or the leftover data
@@ -245,35 +203,10 @@ DWORD CDVDAudio::AddPackets(const DVDAudioFrame &audioframe)
return total;
}
-double CDVDAudio::AddSilence(double delay)
-{
- CLog::Log(LOGDEBUG, "CDVDAudio::AddSilence - %f seconds", delay);
- DVDAudioFrame audioframe;
- audioframe.passthrough = m_bPassthrough;
- audioframe.channels = m_iChannels;
- audioframe.sample_rate = m_iBitrate;
- audioframe.bits_per_sample = m_iBitsPerSample;
- audioframe.size = m_iChannels * (m_iBitsPerSample>>3);
- audioframe.data = (BYTE*)calloc(1, audioframe.size);
- if(audioframe.data == NULL)
- return 0.0;
- unsigned samples = m_iBitrate * delay;
- unsigned added = 0;
- for(; added < samples; added++)
- {
- if(AddPackets(audioframe) != audioframe.size)
- break;
- }
- if(added < samples)
- CLog::Log(LOGDEBUG, "CDVDAudio::AddSilence - failed to %d silence samples of %u", samples - added, samples);
- free(audioframe.data);
- return (double)added / m_iBitrate;
-}
-
void CDVDAudio::Finish()
{
CSingleLock lock (m_critSection);
- if (!m_pAudioDecoder)
+ if (!m_pAudioStream)
return;
DWORD silence = m_dwPacketSize - m_iBufferSize % m_dwPacketSize;
@@ -296,27 +229,26 @@ void CDVDAudio::Drain()
{
Finish();
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder)
- m_pAudioDecoder->WaitCompletion();
+ if (m_pAudioStream)
+ m_pAudioStream->Drain();
}
-void CDVDAudio::SetVolume(int iVolume)
+void CDVDAudio::SetVolume(float volume)
{
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder) m_pAudioDecoder->SetCurrentVolume(iVolume);
+ if (m_pAudioStream) m_pAudioStream->SetVolume(volume);
}
void CDVDAudio::SetDynamicRangeCompression(long drc)
{
- CSingleLock lock (m_critSection);
- if (m_pAudioDecoder) m_pAudioDecoder->SetDynamicRangeCompression(drc);
+
}
float CDVDAudio::GetCurrentAttenuation()
{
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder)
- return m_pAudioDecoder->GetCurrentAttenuation();
+ if (m_pAudioStream)
+ return m_pAudioStream->GetVolume();
else
return 1.0f;
}
@@ -324,15 +256,13 @@ float CDVDAudio::GetCurrentAttenuation()
void CDVDAudio::Pause()
{
CSingleLock lock (m_critSection);
- m_bPaused = true;
- if (m_pAudioDecoder) m_pAudioDecoder->Pause();
+ if (m_pAudioStream) m_pAudioStream->Pause();
}
void CDVDAudio::Resume()
{
CSingleLock lock (m_critSection);
- m_bPaused = false;
- if (m_pAudioDecoder) m_pAudioDecoder->Resume();
+ if (m_pAudioStream) m_pAudioStream->Resume();
}
double CDVDAudio::GetDelay()
@@ -340,8 +270,8 @@ double CDVDAudio::GetDelay()
CSingleLock lock (m_critSection);
double delay = 0.0;
- if(m_pAudioDecoder)
- delay = m_pAudioDecoder->GetDelay();
+ if(m_pAudioStream)
+ delay = m_pAudioStream->GetDelay();
delay += m_SecondsPerByte * m_iBufferSize;
@@ -352,39 +282,46 @@ void CDVDAudio::Flush()
{
CSingleLock lock (m_critSection);
- if (m_pAudioDecoder)
+ if (m_pAudioStream)
{
- m_pAudioDecoder->Stop();
- m_pAudioDecoder->Resume();
+ m_pAudioStream->Flush();
}
m_iBufferSize = 0;
}
bool CDVDAudio::IsValidFormat(const DVDAudioFrame &audioframe)
{
- if(!m_pAudioDecoder)
+ if(!m_pAudioStream)
return false;
if(audioframe.passthrough != m_bPassthrough)
return false;
- if(audioframe.channels != m_iChannels
- || audioframe.sample_rate != m_iBitrate
- || audioframe.bits_per_sample != m_iBitsPerSample)
+ if(m_iBitrate != audioframe.sample_rate
+ || m_iBitsPerSample != audioframe.bits_per_sample
+ || m_channelLayout != audioframe.channel_layout)
return false;
return true;
}
+void CDVDAudio::SetResampleRatio(double ratio)
+{
+ CSingleLock lock (m_critSection);
+
+ if(m_pAudioStream)
+ m_pAudioStream->SetResampleRatio(ratio);
+}
+
double CDVDAudio::GetCacheTime()
{
CSingleLock lock (m_critSection);
- if(!m_pAudioDecoder)
+ if(!m_pAudioStream)
return 0.0;
double delay = 0.0;
- if(m_pAudioDecoder)
- delay = m_pAudioDecoder->GetCacheTime();
+ if(m_pAudioStream)
+ delay = m_pAudioStream->GetCacheTime();
delay += m_SecondsPerByte * m_iBufferSize;
@@ -394,7 +331,7 @@ double CDVDAudio::GetCacheTime()
double CDVDAudio::GetCacheTotal()
{
CSingleLock lock (m_critSection);
- if(!m_pAudioDecoder)
+ if(!m_pAudioStream)
return 0.0;
- return m_pAudioDecoder->GetCacheTotal();
+ return m_pAudioStream->GetCacheTotal();
}
diff --git a/xbmc/cores/dvdplayer/DVDAudio.h b/xbmc/cores/dvdplayer/DVDAudio.h
index 1f1393d72b..35529f0a48 100644
--- a/xbmc/cores/dvdplayer/DVDAudio.h
+++ b/xbmc/cores/dvdplayer/DVDAudio.h
@@ -24,11 +24,11 @@
#if (defined HAVE_CONFIG_H) && (!defined WIN32)
#include "config.h"
#endif
-#include "cores/AudioRenderers/IAudioRenderer.h"
-#include "cores/IAudioCallback.h"
#include "threads/CriticalSection.h"
#include "PlatformDefs.h"
+#include "cores/AudioEngine/Interfaces/AEStream.h"
+
#ifndef _LINUX
enum CodecID;
#else
@@ -54,19 +54,15 @@ public:
CDVDAudio(volatile bool& bStop);
~CDVDAudio();
- void RegisterAudioCallback(IAudioCallback* pCallback);
- void UnRegisterAudioCallback();
-
- void SetVolume(int iVolume);
+ void SetVolume(float fVolume);
void SetDynamicRangeCompression(long drc);
float GetCurrentAttenuation();
void Pause();
void Resume();
- bool Create(const DVDAudioFrame &audioframe, CodecID codec);
+ bool Create(const DVDAudioFrame &audioframe, CodecID codec, bool needresampler);
bool IsValidFormat(const DVDAudioFrame &audioframe);
void Destroy();
DWORD AddPackets(const DVDAudioFrame &audioframe);
- double AddSilence(double delay);
double GetDelay(); // returns the time it takes to play a packet if we add one at this time
double GetCacheTime(); // returns total amount of data cached in audio output at this time
double GetCacheTotal(); // returns total amount the audio device can buffer
@@ -74,20 +70,22 @@ public:
void Finish();
void Drain();
- IAudioRenderer* m_pAudioDecoder;
+ void SetSpeed(int iSpeed);
+ void SetResampleRatio(double ratio);
+
+ IAEStream *m_pAudioStream;
protected:
DWORD AddPacketsRenderer(unsigned char* data, DWORD len, CSingleLock &lock);
- IAudioCallback* m_pCallback;
BYTE* m_pBuffer; // should be [m_dwPacketSize]
DWORD m_iBufferSize;
DWORD m_dwPacketSize;
CCriticalSection m_critSection;
- int m_iChannels;
int m_iBitrate;
int m_iBitsPerSample;
double m_SecondsPerByte;
bool m_bPassthrough;
+ CAEChannelInfo m_channelLayout;
bool m_bPaused;
volatile bool& m_bStop;
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h
index 25df2b5d2b..06bbc1ca72 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h
@@ -22,7 +22,7 @@
*/
#include "system.h"
-#include "utils/PCMRemap.h"
+#include "cores/AudioEngine/AEAudioFormat.h"
#if (defined HAVE_CONFIG_H) && (!defined WIN32)
#include "config.h"
@@ -76,9 +76,14 @@ public:
virtual int GetChannels() = 0;
/*
+ * returns the nr of channels for the encoded audio stream
+ */
+ virtual int GetEncodedChannels() { return 0; }
+
+ /*
* returns the channel mapping
*/
- virtual enum PCMChannels* GetChannelMap() = 0;
+ virtual CAEChannelInfo GetChannelMap() = 0;
/*
* returns the samplerate for the decoded audio stream
@@ -86,9 +91,14 @@ public:
virtual int GetSampleRate() = 0;
/*
- * returns the bitspersample for the decoded audio stream (eg 16 bits)
+ * returns the samplerate for the encoded audio stream
+ */
+ virtual int GetEncodedSampleRate() { return 0; }
+
+ /*
+ * returns the data format for the decoded audio stream (eg AE_FMT_S16LE)
*/
- virtual int GetBitsPerSample() = 0;
+ virtual enum AEDataFormat GetDataFormat() = 0;
/*
* should return the average input bit rate
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
index c55c3923c2..dc90af7636 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
@@ -29,6 +29,7 @@
CDVDAudioCodecFFmpeg::CDVDAudioCodecFFmpeg() : CDVDAudioCodec()
{
+ m_iBufferSize1 = 0;
m_iBufferSize2 = 0;
m_pBuffer2 = (BYTE*)_aligned_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16);
memset(m_pBuffer2, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
@@ -38,10 +39,10 @@ CDVDAudioCodecFFmpeg::CDVDAudioCodecFFmpeg() : CDVDAudioCodec()
m_pConvert = NULL;
m_bOpenedCodec = false;
- m_channelMap[0] = PCM_INVALID;
m_channels = 0;
m_layout = 0;
- m_pFrame1 = NULL;
+
+ m_bLpcmMode = false;
}
CDVDAudioCodecFFmpeg::~CDVDAudioCodecFFmpeg()
@@ -67,6 +68,12 @@ bool CDVDAudioCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
return false;
}
+#if defined(TARGET_DARWIN)
+ int audioMode = g_guiSettings.GetInt("audiooutput.mode");
+ if (audioMode == AUDIO_HDMI)
+ m_bLpcmMode = g_guiSettings.GetBool("audiooutput.multichannellpcm");
+#endif
+
m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
m_pCodecContext->debug_mv = 0;
m_pCodecContext->debug = 0;
@@ -151,7 +158,6 @@ int CDVDAudioCodecFFmpeg::Decode(BYTE* pData, int iSize)
if (iBytesUsed < 0 || !got_frame)
{
m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
return iBytesUsed;
}
m_iBufferSize1 = m_dllAvUtil.av_samples_get_buffer_size(NULL, m_pCodecContext->channels, m_pFrame1->nb_samples, m_pCodecContext->sample_fmt, 1);
@@ -167,8 +173,16 @@ int CDVDAudioCodecFFmpeg::Decode(BYTE* pData, int iSize)
m_iBuffered += iBytesUsed;
else
m_iBuffered = 0;
+
+ if(m_bLpcmMode)
+ ConvertToFloat();
- if(m_pCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 && m_iBufferSize1 > 0)
+ return iBytesUsed;
+}
+
+void CDVDAudioCodecFFmpeg::ConvertToFloat()
+{
+ if(m_pCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT && m_iBufferSize1 > 0)
{
if(m_pConvert && m_pCodecContext->sample_fmt != m_iSampleFormat)
{
@@ -179,35 +193,33 @@ int CDVDAudioCodecFFmpeg::Decode(BYTE* pData, int iSize)
if(!m_pConvert)
{
m_iSampleFormat = m_pCodecContext->sample_fmt;
- m_pConvert = m_dllAvCodec.av_audio_convert_alloc(AV_SAMPLE_FMT_S16, 1, m_pCodecContext->sample_fmt, 1, NULL, 0);
+ m_pConvert = m_dllAvCodec.av_audio_convert_alloc(AV_SAMPLE_FMT_FLT, 1, m_pCodecContext->sample_fmt, 1, NULL, 0);
}
if(!m_pConvert)
{
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", m_pCodecContext->sample_fmt);
+ CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_FLT", m_pCodecContext->sample_fmt);
m_iBufferSize1 = 0;
m_iBufferSize2 = 0;
- return iBytesUsed;
+ return;
}
const void *ibuf[6] = { m_pFrame1->data[0] };
void *obuf[6] = { m_pBuffer2 };
int istr[6] = { m_dllAvUtil.av_get_bytes_per_sample(m_pCodecContext->sample_fmt) };
- int ostr[6] = { 2 };
+ int ostr[6] = { m_dllAvUtil.av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) };
int len = m_iBufferSize1 / istr[0];
if(m_dllAvCodec.av_audio_convert(m_pConvert, obuf, ostr, ibuf, istr, len) < 0)
{
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_S16", (int)m_pCodecContext->sample_fmt);
+ CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_FLT", (int)m_pCodecContext->sample_fmt);
m_iBufferSize1 = 0;
m_iBufferSize2 = 0;
- return iBytesUsed;
+ return;
}
m_iBufferSize1 = 0;
m_iBufferSize2 = len * ostr[0];
}
-
- return iBytesUsed;
}
int CDVDAudioCodecFFmpeg::GetData(BYTE** dst)
@@ -217,11 +229,13 @@ int CDVDAudioCodecFFmpeg::GetData(BYTE** dst)
*dst = m_pFrame1->data[0];
return m_iBufferSize1;
}
+
if(m_iBufferSize2)
{
*dst = m_pBuffer2;
return m_iBufferSize2;
}
+
return 0;
}
@@ -240,13 +254,38 @@ int CDVDAudioCodecFFmpeg::GetChannels()
int CDVDAudioCodecFFmpeg::GetSampleRate()
{
- if (m_pCodecContext) return m_pCodecContext->sample_rate;
+ if (m_pCodecContext)
+ return m_pCodecContext->sample_rate;
return 0;
}
-int CDVDAudioCodecFFmpeg::GetBitsPerSample()
+int CDVDAudioCodecFFmpeg::GetEncodedSampleRate()
{
- return 16;
+ if (m_pCodecContext)
+ return m_pCodecContext->sample_rate;
+ return 0;
+}
+
+enum AEDataFormat CDVDAudioCodecFFmpeg::GetDataFormat()
+{
+ if(m_bLpcmMode)
+ {
+ return AE_FMT_LPCM;
+ }
+ else
+ {
+ switch(m_pCodecContext->sample_fmt)
+ {
+ case SAMPLE_FMT_U8 : return AE_FMT_U8;
+ case SAMPLE_FMT_S16: return AE_FMT_S16NE;
+ case SAMPLE_FMT_S32: return AE_FMT_S32NE;
+ case SAMPLE_FMT_FLT: return AE_FMT_FLOAT;
+ case SAMPLE_FMT_DBL: return AE_FMT_DOUBLE;
+ default:
+ assert(false);
+ return AE_FMT_INVALID;
+ }
+ }
}
int CDVDAudioCodecFFmpeg::GetBitRate()
@@ -265,7 +304,7 @@ static unsigned count_bits(int64_t value)
void CDVDAudioCodecFFmpeg::BuildChannelMap()
{
- if (m_channels == m_pCodecContext->channels && m_layout == (int64_t)m_pCodecContext->channel_layout)
+ if (m_channels == m_pCodecContext->channels && m_layout == m_pCodecContext->channel_layout)
return; //nothing to do here
m_channels = m_pCodecContext->channels;
@@ -282,36 +321,32 @@ void CDVDAudioCodecFFmpeg::BuildChannelMap()
layout = m_dllAvUtil.av_get_default_channel_layout(m_pCodecContext->channels);
}
- int index = 0;
- if (layout & AV_CH_FRONT_LEFT ) m_channelMap[index++] = PCM_FRONT_LEFT ;
- if (layout & AV_CH_FRONT_RIGHT ) m_channelMap[index++] = PCM_FRONT_RIGHT ;
- if (layout & AV_CH_FRONT_CENTER ) m_channelMap[index++] = PCM_FRONT_CENTER ;
- if (layout & AV_CH_LOW_FREQUENCY ) m_channelMap[index++] = PCM_LOW_FREQUENCY ;
- if (layout & AV_CH_BACK_LEFT ) m_channelMap[index++] = PCM_BACK_LEFT ;
- if (layout & AV_CH_BACK_RIGHT ) m_channelMap[index++] = PCM_BACK_RIGHT ;
- if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelMap[index++] = PCM_FRONT_LEFT_OF_CENTER ;
- if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelMap[index++] = PCM_FRONT_RIGHT_OF_CENTER;
- if (layout & AV_CH_BACK_CENTER ) m_channelMap[index++] = PCM_BACK_CENTER ;
- if (layout & AV_CH_SIDE_LEFT ) m_channelMap[index++] = PCM_SIDE_LEFT ;
- if (layout & AV_CH_SIDE_RIGHT ) m_channelMap[index++] = PCM_SIDE_RIGHT ;
- if (layout & AV_CH_TOP_CENTER ) m_channelMap[index++] = PCM_TOP_CENTER ;
- if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelMap[index++] = PCM_TOP_FRONT_LEFT ;
- if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelMap[index++] = PCM_TOP_FRONT_CENTER ;
- if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelMap[index++] = PCM_TOP_FRONT_RIGHT ;
- if (layout & AV_CH_TOP_BACK_LEFT ) m_channelMap[index++] = PCM_TOP_BACK_LEFT ;
- if (layout & AV_CH_TOP_BACK_CENTER ) m_channelMap[index++] = PCM_TOP_BACK_CENTER ;
- if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelMap[index++] = PCM_TOP_BACK_RIGHT ;
-
- //terminate the channel map
- m_channelMap[index] = PCM_INVALID;
+ m_channelLayout.Reset();
+
+ if (layout & AV_CH_FRONT_LEFT ) m_channelLayout += AE_CH_FL ;
+ if (layout & AV_CH_FRONT_RIGHT ) m_channelLayout += AE_CH_FR ;
+ if (layout & AV_CH_FRONT_CENTER ) m_channelLayout += AE_CH_FC ;
+ if (layout & AV_CH_LOW_FREQUENCY ) m_channelLayout += AE_CH_LFE ;
+ if (layout & AV_CH_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+ if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelLayout += AE_CH_FLOC;
+ if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelLayout += AE_CH_FROC;
+ if (layout & AV_CH_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_SIDE_LEFT ) m_channelLayout += AE_CH_SL ;
+ if (layout & AV_CH_SIDE_RIGHT ) m_channelLayout += AE_CH_SR ;
+ if (layout & AV_CH_TOP_CENTER ) m_channelLayout += AE_CH_TC ;
+ if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelLayout += AE_CH_TFL ;
+ if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelLayout += AE_CH_TFC ;
+ if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelLayout += AE_CH_TFR ;
+ if (layout & AV_CH_TOP_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+
+ m_channels = m_pCodecContext->channels;
}
-enum PCMChannels* CDVDAudioCodecFFmpeg::GetChannelMap()
+CAEChannelInfo CDVDAudioCodecFFmpeg::GetChannelMap()
{
BuildChannelMap();
-
- if (m_channelMap[0] == PCM_INVALID)
- return NULL;
-
- return m_channelMap;
+ return m_channelLayout;
}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h
index 4c01499ca1..749e687836 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h
@@ -37,34 +37,37 @@ public:
virtual int GetData(BYTE** dst);
virtual void Reset();
virtual int GetChannels();
- virtual enum PCMChannels *GetChannelMap();
+ virtual CAEChannelInfo GetChannelMap();
virtual int GetSampleRate();
- virtual int GetBitsPerSample();
+ virtual int GetEncodedSampleRate();
+ virtual enum AEDataFormat GetDataFormat();
virtual const char* GetName() { return "FFmpeg"; }
virtual int GetBufferSize() { return m_iBuffered; }
virtual int GetBitRate();
protected:
- AVCodecContext* m_pCodecContext;
- AVAudioConvert* m_pConvert;;
- enum AVSampleFormat m_iSampleFormat;
- enum PCMChannels m_channelMap[PCM_MAX_CH + 1];
+ AVCodecContext* m_pCodecContext;
+ AVAudioConvert* m_pConvert;
+ enum AVSampleFormat m_iSampleFormat;
+ CAEChannelInfo m_channelLayout;
+ int m_iMapChannels;
+ bool m_bLpcmMode;
AVFrame* m_pFrame1;
- int m_iBufferSize1;
-
- BYTE *m_pBuffer2;
- int m_iBufferSize2;
+ int m_iBufferSize1;
+ BYTE* m_pBuffer2;
+ int m_iBufferSize2;
bool m_bOpenedCodec;
int m_iBuffered;
- int m_channels;
- int64_t m_layout;
+ int m_channels;
+ uint64_t m_layout;
DllAvCodec m_dllAvCodec;
DllAvUtil m_dllAvUtil;
void BuildChannelMap();
+ void ConvertToFloat();
};
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp
index 7aef7e4be1..40a91360fe 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp
@@ -22,21 +22,6 @@
#include "DVDAudioCodecLibMad.h"
#include "DVDStreamInfo.h"
-static inline signed int scale(mad_fixed_t sample)
-{
- /* round */
- sample += (1L << (MAD_F_FRACBITS - 16));
-
- /* clip */
- if (sample >= MAD_F_ONE)
- sample = MAD_F_ONE - 1;
- else if (sample < -MAD_F_ONE)
- sample = -MAD_F_ONE;
-
- /* quantize */
- return sample >> (MAD_F_FRACBITS + 1 - 16);
-}
-
CDVDAudioCodecLibMad::CDVDAudioCodecLibMad() : CDVDAudioCodec()
{
m_bInitialized = false;
@@ -96,15 +81,12 @@ int CDVDAudioCodecLibMad::Decode(BYTE* pData, int iSize)
// m_inputBuffer should always have room here for extra bytes
int iBytesFree = MAD_INPUT_SIZE - m_iInputBufferSize;
- int iBytesUsed = iBytesFree;
- if (iBytesUsed > iSize) iBytesUsed = iSize;
+ int iBytesUsed = std::min(iSize, iBytesFree);
// copy data into our buffer for decoding
memcpy(m_inputBuffer + m_iInputBufferSize, pData, iBytesUsed);
m_iInputBufferSize += iBytesUsed;
-
-
if (m_bInitialized)
{
m_dll.mad_stream_buffer(&m_stream, pBuffer, m_iInputBufferSize);
@@ -134,7 +116,7 @@ int CDVDAudioCodecLibMad::Decode(BYTE* pData, int iSize)
if (m_stream.next_frame)
{
m_iInputBufferSize = m_stream.bufend - m_stream.next_frame;
- pBuffer = (BYTE*)m_stream.next_frame;
+ pBuffer = (BYTE*)m_stream.next_frame;
}
if (m_iInputBufferSize <= 0)
@@ -142,74 +124,50 @@ int CDVDAudioCodecLibMad::Decode(BYTE* pData, int iSize)
return iBytesUsed;
}
- // buffer again after a sync
- m_dll.mad_stream_buffer(&m_stream, pBuffer, m_iInputBufferSize);
+ // buffer again after a sync
+ m_dll.mad_stream_buffer(&m_stream, pBuffer, m_iInputBufferSize);
}
else
{
// decoded some data
m_iSourceSampleRate = m_frame.header.samplerate;
- m_iSourceChannels = (m_frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1 : 2;
- m_iSourceBitrate = m_frame.header.bitrate;
-
- /*
- switch (this->frame.header.layer) {
- case MAD_LAYER_I:
- _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC,
- "MPEG audio layer 1 (lib: MAD)");
- break;
- case MAD_LAYER_II:
- _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC,
- "MPEG audio layer 2 (lib: MAD)");
- break;
- case MAD_LAYER_III:
- _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC,
- "MPEG audio layer 3 (lib: MAD)");
- break;
- default:
- _x_meta_info_set_utf8(this->xstream, XINE_META_INFO_AUDIOCODEC,
- "MPEG audio (lib: MAD)");
- }
- */
+ m_iSourceChannels = (m_frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1 : 2;
+ m_iSourceBitrate = m_frame.header.bitrate;
m_dll.mad_synth_frame(&m_synth, &m_frame);
{
unsigned int nchannels, nsamples;
- mad_fixed_t const* left_ch, *right_ch;
- struct mad_pcm* pcm = &m_synth.pcm;
- unsigned __int16* output = (unsigned __int16*)(m_decodedData + m_iDecodedDataSize);
+ mad_fixed_t const* left_ch, *right_ch;
+ struct mad_pcm* pcm = &m_synth.pcm;
+ float* output = (float*)(m_decodedData + m_iDecodedDataSize);
nchannels = pcm->channels;
nsamples = pcm->length;
- left_ch = pcm->samples[0];
- right_ch = pcm->samples[1];
-
- int iSize = nsamples * 2;
- if (nchannels == 2) iSize += nsamples * 2;
+ left_ch = pcm->samples[0];
+ right_ch = pcm->samples[1];
+ unsigned int iSize = nsamples * sizeof(float) * nchannels;
if (iSize < (MAD_DECODED_SIZE - m_iDecodedDataSize))
{
- while (nsamples--)
- {
- // output sample(s) in 16-bit signed little-endian PCM
- *output++ = scale(*left_ch++);
-
- if (nchannels == 2)
- {
- *output++ = scale(*right_ch++);
- }
- }
-
- m_iDecodedDataSize += iSize;
- }
+ while (nsamples--)
+ {
+ // output sample(s) in float
+ *output++ = mad_f_todouble(*left_ch++);
+ if (nchannels == 2)
+ {
+ *output++ = mad_f_todouble(*right_ch++);
+ }
+ }
+ m_iDecodedDataSize += iSize;
+ }
- if (iSize > (MAD_DECODED_SIZE - m_iDecodedDataSize))
+ if (iSize > (MAD_DECODED_SIZE - m_iDecodedDataSize))
{
// next audio frame is not going to fit
bFullOutputBuffer = true;
}
- }
+ }
}
}
}
@@ -244,13 +202,15 @@ void CDVDAudioCodecLibMad::Reset()
}
}
-enum PCMChannels* CDVDAudioCodecLibMad::GetChannelMap()
+CAEChannelInfo CDVDAudioCodecLibMad::GetChannelMap()
{
- static enum PCMChannels map[2][2] = {
- {PCM_FRONT_CENTER},
- {PCM_FRONT_LEFT, PCM_FRONT_RIGHT}
+ assert(m_iSourceChannels > 0 && m_iSourceChannels < 3);
+
+ static enum AEChannel map[2][3] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL}
};
- return map[m_iSourceChannels - 1];
+ return CAEChannelInfo(map[m_iSourceChannels -1]);
}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h
index 314246eb79..d61ae24df9 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h
@@ -25,7 +25,7 @@
#include "DllLibMad.h"
#define MAD_INPUT_SIZE (8 * 1024)
-#define MAD_DECODED_SIZE (16 * MAD_INPUT_SIZE)
+#define MAD_DECODED_SIZE (sizeof(float) * MAD_INPUT_SIZE)
class CDVDAudioCodecLibMad : public CDVDAudioCodec
{
@@ -37,12 +37,12 @@ public:
virtual int Decode(BYTE* pData, int iSize);
virtual int GetData(BYTE** dst);
virtual void Reset();
- virtual enum PCMChannels* GetChannelMap();
- virtual int GetChannels() { return m_iSourceChannels; }
- virtual int GetSampleRate() { return m_iSourceSampleRate; }
- virtual int GetBitsPerSample() { return 16; }
- virtual const char* GetName() { return "libmad"; }
- virtual int GetBufferSize() { return m_iInputBufferSize; }
+ virtual CAEChannelInfo GetChannelMap();
+ virtual int GetChannels() { return m_iSourceChannels; }
+ virtual int GetSampleRate() { return m_iSourceSampleRate; }
+ virtual enum AEDataFormat GetDataFormat() { return AE_FMT_FLOAT; }
+ virtual const char* GetName() { return "libmad"; }
+ virtual int GetBufferSize() { return m_iInputBufferSize; }
private:
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp
new file mode 100644
index 0000000000..2fd96627e7
--- /dev/null
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DVDAudioCodecPassthrough.h"
+#include "DVDCodecs/DVDCodecs.h"
+#include "DVDStreamInfo.h"
+#include "settings/GUISettings.h"
+#include "settings/Settings.h"
+#include "utils/log.h"
+
+#include "cores/AudioEngine/AEFactory.h"
+
+CDVDAudioCodecPassthrough::CDVDAudioCodecPassthrough(void) :
+ m_buffer (NULL),
+ m_bufferSize(0)
+{
+}
+
+CDVDAudioCodecPassthrough::~CDVDAudioCodecPassthrough(void)
+{
+ Dispose();
+}
+
+bool CDVDAudioCodecPassthrough::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+{
+ /* dont open if AE doesnt support RAW */
+ if (!CAEFactory::AE->SupportsRaw())
+ return false;
+
+ bool bSupportsAC3Out = false;
+ bool bSupportsDTSOut = false;
+ bool bSupportsTrueHDOut = false;
+ bool bSupportsDTSHDOut = false;
+
+ int audioMode = g_guiSettings.GetInt("audiooutput.mode");
+ if (AUDIO_IS_BITSTREAM(audioMode))
+ {
+ bSupportsAC3Out = g_guiSettings.GetBool("audiooutput.ac3passthrough");
+ bSupportsDTSOut = g_guiSettings.GetBool("audiooutput.dtspassthrough");
+ }
+
+ if (audioMode == AUDIO_HDMI)
+ {
+ bSupportsTrueHDOut = g_guiSettings.GetBool("audiooutput.truehdpassthrough");
+ bSupportsDTSHDOut = g_guiSettings.GetBool("audiooutput.dtshdpassthrough" ) && bSupportsDTSOut;
+ }
+
+ /* only get the dts core from the parser if we don't support dtsHD */
+ m_info.SetCoreOnly(!bSupportsDTSHDOut);
+ m_bufferSize = 0;
+
+ if (
+ (hints.codec == CODEC_ID_AC3 && bSupportsAC3Out) ||
+ (hints.codec == CODEC_ID_DTS && bSupportsDTSOut) ||
+ (audioMode == AUDIO_HDMI &&
+ (
+ (hints.codec == CODEC_ID_EAC3 && bSupportsAC3Out ) ||
+ (hints.codec == CODEC_ID_TRUEHD && bSupportsTrueHDOut)
+ )
+ )
+ )
+ return true;
+
+ return false;
+}
+
+int CDVDAudioCodecPassthrough::GetSampleRate()
+{
+ return m_info.GetOutputRate();
+}
+
+int CDVDAudioCodecPassthrough::GetEncodedSampleRate()
+{
+ return m_info.GetSampleRate();
+}
+
+enum AEDataFormat CDVDAudioCodecPassthrough::GetDataFormat()
+{
+ switch(m_info.GetDataType())
+ {
+ case CAEStreamInfo::STREAM_TYPE_AC3:
+ return AE_FMT_AC3;
+
+ case CAEStreamInfo::STREAM_TYPE_DTS_512:
+ case CAEStreamInfo::STREAM_TYPE_DTS_1024:
+ case CAEStreamInfo::STREAM_TYPE_DTS_2048:
+ case CAEStreamInfo::STREAM_TYPE_DTSHD_CORE:
+ return AE_FMT_DTS;
+
+ case CAEStreamInfo::STREAM_TYPE_EAC3:
+ return AE_FMT_EAC3;
+
+ case CAEStreamInfo::STREAM_TYPE_TRUEHD:
+ return AE_FMT_TRUEHD;
+
+ case CAEStreamInfo::STREAM_TYPE_DTSHD:
+ return AE_FMT_DTSHD;
+
+ default:
+ return AE_FMT_INVALID; //Unknown stream type
+ }
+}
+
+int CDVDAudioCodecPassthrough::GetChannels()
+{
+ return m_info.GetOutputChannels();
+}
+
+int CDVDAudioCodecPassthrough::GetEncodedChannels()
+{
+ return m_info.GetChannels();
+}
+
+CAEChannelInfo CDVDAudioCodecPassthrough::GetChannelMap()
+{
+ return m_info.GetChannelMap();
+}
+
+void CDVDAudioCodecPassthrough::Dispose()
+{
+ if (m_buffer)
+ {
+ delete[] m_buffer;
+ m_buffer = NULL;
+ }
+
+ m_bufferSize = 0;
+}
+
+int CDVDAudioCodecPassthrough::Decode(BYTE* pData, int iSize)
+{
+ if (iSize <= 0) return 0;
+
+ unsigned int size = m_bufferSize;
+ unsigned int used = m_info.AddData(pData, iSize, &m_buffer, &size);
+ m_bufferSize = std::max(m_bufferSize, size);
+
+ /* if we have a frame */
+ if (size)
+ m_packer.Pack(m_info, m_buffer, size);
+
+ return used;
+}
+
+int CDVDAudioCodecPassthrough::GetData(BYTE** dst)
+{
+ int size = m_packer.GetSize();
+ *dst = m_packer.GetBuffer();
+ return size;
+}
+
+void CDVDAudioCodecPassthrough::Reset()
+{
+}
+
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h
new file mode 100644
index 0000000000..f624231a44
--- /dev/null
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h
@@ -0,0 +1,57 @@
+#pragma once
+
+/*
+ * Copyright (C) 2010-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <list>
+
+#include "system.h"
+#include "DVDAudioCodec.h"
+#include "cores/AudioEngine/AEAudioFormat.h"
+#include "cores/AudioEngine/Utils/AEStreamInfo.h"
+#include "cores/AudioEngine/Utils/AEBitstreamPacker.h"
+
+class CDVDAudioCodecPassthrough : public CDVDAudioCodec
+{
+public:
+ CDVDAudioCodecPassthrough();
+ virtual ~CDVDAudioCodecPassthrough();
+
+ virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
+ virtual void Dispose();
+ virtual int Decode(BYTE* pData, int iSize);
+ virtual int GetData(BYTE** dst);
+ virtual void Reset();
+ virtual int GetChannels ();
+ virtual int GetEncodedChannels ();
+ virtual CAEChannelInfo GetChannelMap ();
+ virtual int GetSampleRate ();
+ virtual int GetEncodedSampleRate ();
+ virtual enum AEDataFormat GetDataFormat();
+ virtual bool NeedPassthrough () { return true; }
+ virtual const char* GetName () { return "passthrough"; }
+private:
+ CAEStreamInfo m_info;
+ CAEBitstreamPacker m_packer;
+ uint8_t* m_buffer;
+ unsigned int m_bufferSize;
+};
+
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp
index c0c3c060af..1de73c213c 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp
@@ -26,8 +26,6 @@
#include "settings/Settings.h"
#include "utils/log.h"
-#include "Encoders/DVDAudioEncoderFFmpeg.h"
-
//These values are forced to allow spdif out
#define OUT_SAMPLESIZE 16
#define OUT_CHANNELS 2
@@ -58,9 +56,7 @@ CDVDAudioCodecPassthroughFFmpeg::CDVDAudioCodecPassthroughFFmpeg(void)
m_SampleRate = 0;
m_Codec = NULL;
- m_Encoder = NULL;
- m_InitEncoder = true;
-
+
/* make enough room for at-least two audio frames */
m_DecodeSize = 0;
m_DecodeBuffer = NULL;
@@ -285,44 +281,11 @@ bool CDVDAudioCodecPassthroughFFmpeg::SupportsFormat(CDVDStreamInfo &hints)
if (m_bSupportsAC3Out && hints.codec == CODEC_ID_AC3) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAC3;
else if (m_bSupportsDTSOut && hints.codec == CODEC_ID_DTS) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncDTS;
else if (m_bSupportsAACOut && hints.codec == CODEC_ID_AAC) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAAC;
- else if (m_bSupportsMP1Out && hints.codec == CODEC_ID_MP1);
- else if (m_bSupportsMP2Out && hints.codec == CODEC_ID_MP2);
- else if (m_bSupportsMP3Out && hints.codec == CODEC_ID_MP3);
else return false;
return true;
}
-bool CDVDAudioCodecPassthroughFFmpeg::SetupEncoder(CDVDStreamInfo &hints)
-{
- /* there is no point encoding <= 2 channel sources, and we dont support anything but AC3 at the moment */
- if (hints.channels <= 2 || !m_bSupportsAC3Out)
- return false;
-
- CLog::Log(LOGDEBUG, "CDVDAudioCodecPassthroughFFmpeg::SetupEncoder - Setting up encoder for on the fly transcode");
-
- /* we need to decode the incoming audio data, so we can re-encode it as we need it */
- CDVDStreamInfo h(hints);
- m_Codec = CDVDFactoryCodec::CreateAudioCodec(h, false);
- if (!m_Codec)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupEncoder - Unable to create a decoder for transcode");
- return false;
- }
-
- /* create and setup the encoder */
- m_Encoder = new CDVDAudioEncoderFFmpeg();
- m_InitEncoder = true;
-
- /* adjust the hints according to the encorders output */
- hints.codec = m_Encoder->GetCodecID();
- hints.bitrate = m_Encoder->GetBitRate();
- hints.channels = OUT_CHANNELS;
-
- CLog::Log(LOGDEBUG, "CDVDAudioCodecPassthroughFFmpeg::SetupEncoder - Ready to transcode");
- return true;
-}
-
bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
{
int audioMode = g_guiSettings.GetInt("audiooutput.mode");
@@ -333,9 +296,6 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio
m_bSupportsAC3Out = g_guiSettings.GetBool("audiooutput.ac3passthrough");
m_bSupportsDTSOut = g_guiSettings.GetBool("audiooutput.dtspassthrough");
m_bSupportsAACOut = g_guiSettings.GetBool("audiooutput.passthroughaac");
- m_bSupportsMP1Out = g_guiSettings.GetBool("audiooutput.passthroughmp1");
- m_bSupportsMP2Out = g_guiSettings.GetBool("audiooutput.passthroughmp2");
- m_bSupportsMP3Out = g_guiSettings.GetBool("audiooutput.passthroughmp3");
}
else
return false;
@@ -362,20 +322,9 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio
/* see if the muxer supports our codec (see spdif.c for supported formats) */
if (!SupportsFormat(hints))
{
- /* HDMI can do multichannel LPCM, transcoding would just be silly */
- if (audioMode == AUDIO_HDMI)
- {
- CLog::Log(LOGINFO, "CDVDAudioCodecPassthroughFFmpeg::Open - Won't transcode for HDMI");
- Dispose();
- return false;
- }
-
- if (!SetupEncoder(hints) || !SupportsFormat(hints))
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - FFmpeg SPDIF muxer does not support this codec");
- Dispose();
- return false;
- }
+ CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - FFmpeg SPDIF muxer does not support this codec");
+ Dispose();
+ return false;
}
else
{
@@ -389,7 +338,6 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio
}
m_Codec = NULL;
- m_Encoder = NULL;
}
if (!SetupMuxer(hints, "spdif", m_SPDIF))
@@ -399,6 +347,9 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio
/* we will check the first packet's crc */
m_LostSync = true;
+
+ m_codec = hints.codec;
+
return true;
}
@@ -415,9 +366,6 @@ void CDVDAudioCodecPassthroughFFmpeg::Dispose()
m_DecodeBuffer = NULL;
}
- delete m_Encoder;
- m_Encoder = NULL;
-
delete m_Codec;
m_Codec = NULL;
}
@@ -427,76 +375,6 @@ int CDVDAudioCodecPassthroughFFmpeg::Decode(BYTE* pData, int iSize)
unsigned int used, fSize;
fSize = iSize;
- /* if we are transcoding */
- if (m_Encoder)
- {
- uint8_t *decData;
- uint8_t *encData;
-
- /* if we are decoding */
- used = m_Codec->Decode (pData, iSize);
- fSize = m_Codec->GetData(&decData);
-
- /* we may not get any data for a few frames, this is expected */
- if (fSize == 0)
- return used;
-
- /* now we have data, it is safe to initialize the encoder, as we should now have a channel map */
- if (m_InitEncoder)
- {
- if (m_Encoder->Initialize(m_Codec->GetChannels(), m_Codec->GetChannelMap(), m_Codec->GetBitsPerSample(), m_Codec->GetSampleRate()))
- {
- m_InitEncoder = false;
- m_EncPacketSize = m_Encoder->GetPacketSize();
- /* allocate enough room for two packets of data */
- m_DecodeBuffer = (uint8_t*)_aligned_malloc(m_EncPacketSize * 2, 16);
- m_DecodeSize = 0;
- }
- else
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Encode - Unable to initialize the encoder for transcode");
- return -1;
- }
- }
-
- unsigned int avail, eUsed, eCoded = 0;
- avail = fSize + m_DecodeSize;
-
- while(avail >= m_EncPacketSize)
- {
- /* append up to one packet of data to the buffer */
- if (m_DecodeSize < m_EncPacketSize)
- {
- unsigned int copy = (fSize > m_EncPacketSize) ? m_EncPacketSize : fSize;
- if (copy)
- {
- memcpy(m_DecodeBuffer + m_DecodeSize, decData, copy);
- m_DecodeSize += copy;
- decData += copy;
- fSize -= copy;
- }
- }
-
- /* encode the data and advance our data pointer */
- eUsed = m_Encoder->Encode(m_DecodeBuffer, m_EncPacketSize);
- avail -= eUsed;
-
- /* shift buffered data along with memmove as the data can overlap */
- m_DecodeSize -= eUsed;
- memmove(m_DecodeBuffer, m_DecodeBuffer + eUsed, m_DecodeSize);
-
- /* output the frame of data */
- while((eCoded = m_Encoder->GetData(&encData)))
- WriteFrame(m_SPDIF, encData, eCoded);
- }
-
- /* append any leftover data to the buffer */
- memcpy(m_DecodeBuffer + m_DecodeSize, decData, fSize);
- m_DecodeSize += fSize;
-
- return used;
- }
-
/* if we are muxing into ADTS (AAC) */
int adts_used = 0;
if (m_ADTS.m_pFormat)
@@ -581,16 +459,17 @@ void CDVDAudioCodecPassthroughFFmpeg::Reset()
ResetMuxer(m_SPDIF);
ResetMuxer(m_ADTS );
-
- if (m_Encoder)
- m_Encoder->Reset();
}
int CDVDAudioCodecPassthroughFFmpeg::GetChannels()
{
//Can't return correct channels here as this is used to keep sync.
//should probably have some other way to find out this
- return OUT_CHANNELS;
+ switch(m_codec)
+ {
+ default:
+ return 2;
+ }
}
int CDVDAudioCodecPassthroughFFmpeg::GetSampleRate()
@@ -598,6 +477,23 @@ int CDVDAudioCodecPassthroughFFmpeg::GetSampleRate()
return m_SPDIF.m_pStream->codec->sample_rate;
}
+int CDVDAudioCodecPassthroughFFmpeg::GetEncodedSampleRate()
+{
+ return m_SPDIF.m_pStream->codec->sample_rate;
+}
+
+enum AEDataFormat CDVDAudioCodecPassthroughFFmpeg::GetDataFormat()
+{
+ switch(m_codec)
+ {
+ case CODEC_ID_AC3: return AE_FMT_AC3;
+ case CODEC_ID_DTS: return AE_FMT_DTS;
+ default:
+ return AE_FMT_INVALID; //Unknown stream type
+ }
+}
+
+
int CDVDAudioCodecPassthroughFFmpeg::GetBitsPerSample()
{
return OUT_SAMPLESIZE;
@@ -737,3 +633,18 @@ unsigned int CDVDAudioCodecPassthroughFFmpeg::SyncAAC(BYTE* pData, unsigned int
return iSize;
}
/* ========================== END SYNC FUNCTIONS ========================== */
+
+
+CAEChannelInfo CDVDAudioCodecPassthroughFFmpeg::GetChannelMap()
+{
+ static enum AEChannel map[2][9] = {
+ {AE_CH_RAW, AE_CH_RAW, AE_CH_NULL},
+ {AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_RAW, AE_CH_NULL}
+ };
+
+ switch(m_codec)
+ {
+ default:
+ return map[0];
+ }
+}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h
index c05faab1c3..acef62247d 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h
@@ -27,11 +27,8 @@
#include "DllAvCodec.h"
#include "DllAvUtil.h"
-#include "../DVDFactoryCodec.h"
#include "DVDAudioCodec.h"
-class IDVDAudioEncoder;
-
class CDVDAudioCodecPassthroughFFmpeg : public CDVDAudioCodec
{
public:
@@ -44,8 +41,10 @@ public:
virtual int GetData(BYTE** dst);
virtual void Reset();
virtual int GetChannels();
- virtual enum PCMChannels *GetChannelMap() { static enum PCMChannels map[2] = {PCM_FRONT_LEFT, PCM_FRONT_RIGHT}; return map; }
- virtual int GetSampleRate();
+ virtual CAEChannelInfo GetChannelMap();
+ virtual int GetSampleRate();
+ virtual int GetEncodedSampleRate();
+ virtual enum AEDataFormat GetDataFormat();
virtual int GetBitsPerSample();
virtual bool NeedPassthrough() { return true; }
virtual const char* GetName() { return "PassthroughFFmpeg"; }
@@ -85,24 +84,18 @@ private:
bool m_bSupportsAC3Out;
bool m_bSupportsDTSOut;
bool m_bSupportsAACOut;
- bool m_bSupportsMP1Out;
- bool m_bSupportsMP2Out;
- bool m_bSupportsMP3Out;
CDVDAudioCodec *m_Codec;
- IDVDAudioEncoder *m_Encoder;
- bool m_InitEncoder;
- unsigned int m_EncPacketSize;
BYTE *m_DecodeBuffer;
unsigned int m_DecodeSize;
bool SupportsFormat(CDVDStreamInfo &hints);
- bool SetupEncoder (CDVDStreamInfo &hints);
uint8_t m_NeededBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
unsigned int m_NeededUsed;
unsigned int m_Needed;
bool m_LostSync;
int m_SampleRate;
+ CodecID m_codec;
unsigned int (CDVDAudioCodecPassthroughFFmpeg::*m_pSyncFrame)(BYTE* pData, unsigned int iSize, unsigned int *fSize);
unsigned int SyncAC3(BYTE* pData, unsigned int iSize, unsigned int *fSize);
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp
index ca3d7db544..0f7f605df7 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp
@@ -293,18 +293,19 @@ int CDVDAudioCodecPcm::GetChannels()
return m_iOutputChannels;
}
-enum PCMChannels* CDVDAudioCodecPcm::GetChannelMap()
+CAEChannelInfo CDVDAudioCodecPcm::GetChannelMap()
{
- static enum PCMChannels map[8][8] =
+ assert(m_iOutputChannels > 0 && m_iOutputChannels <= 8);
+ static enum AEChannel map[8][9] =
{
- /* MONO */ {PCM_FRONT_CENTER },
- /* STEREO */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT },
- /* 3.0 ? */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER },
- /* 4.0 ? */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT , PCM_BACK_RIGHT },
- /* 5.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT , PCM_BACK_RIGHT },
- /* 5.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT , PCM_BACK_RIGHT },
- /* 7.0 ? */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT , PCM_BACK_RIGHT, PCM_SIDE_LEFT , PCM_SIDE_RIGHT },
- /* 7.1 ? */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_BACK_LEFT , PCM_BACK_RIGHT, PCM_SIDE_LEFT , PCM_SIDE_RIGHT }
+ /* MONO */ {AE_CH_FC, AE_CH_NULL, },
+ /* STEREO */ {AE_CH_FL, AE_CH_FR, AE_CH_NULL, },
+ /* 3.0 ? */ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_NULL, },
+ /* 4.0 ? */ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR , AE_CH_NULL, },
+ /* 5.0 */ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL , AE_CH_BR, AE_CH_NULL },
+ /* 5.1 */ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_BL, AE_CH_BR, AE_CH_NULL, },
+ /* 7.0 ? */ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL , AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_NULL },
+ /* 7.1 ? */ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_NULL}
};
return map[m_iOutputChannels - 1];
@@ -315,7 +316,7 @@ int CDVDAudioCodecPcm::GetSampleRate()
return m_iSourceSampleRate;
}
-int CDVDAudioCodecPcm::GetBitsPerSample()
+enum AEDataFormat CDVDAudioCodecPcm::GetDataFormat()
{
- return 16;
+ return AE_FMT_S16NE;
}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h
index b7d8f74d73..7ca2d0666b 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h
@@ -34,9 +34,9 @@ public:
virtual int GetData(BYTE** dst);
virtual void Reset();
virtual int GetChannels();
- virtual enum PCMChannels* GetChannelMap();
+ virtual CAEChannelInfo GetChannelMap();
virtual int GetSampleRate();
- virtual int GetBitsPerSample();
+ virtual enum AEDataFormat GetDataFormat();
virtual const char* GetName() { return "pcm"; }
protected:
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.cpp
deleted file mode 100644
index 0189e659d8..0000000000
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#define AC3_ENCODE_BITRATE 640000
-
-#include "DVDAudioEncoderFFmpeg.h"
-#include "utils/log.h"
-#include <string.h>
-
-CDVDAudioEncoderFFmpeg::CDVDAudioEncoderFFmpeg():
- m_CodecCtx(NULL),
- m_AudioConvert(NULL),
- m_Buffer(NULL),
- m_TmpBuffer(NULL),
- m_TmpBuffer2(NULL)
-{
-}
-
-CDVDAudioEncoderFFmpeg::~CDVDAudioEncoderFFmpeg()
-{
- Reset();
- if(m_dllAvUtil.IsLoaded())
- {
- m_dllAvUtil.av_freep(&m_CodecCtx);
- }
-
- if(m_dllAvCodec.IsLoaded())
- {
- if (m_AudioConvert)
- m_dllAvCodec.av_audio_convert_free(m_AudioConvert);
- }
- delete[] m_Buffer;
- delete[] m_TmpBuffer;
- delete[] m_TmpBuffer2;
-}
-
-bool CDVDAudioEncoderFFmpeg::Initialize(unsigned int channels, enum PCMChannels *channelMap, unsigned int bitsPerSample, unsigned int sampleRate)
-{
- Reset();
- if (!channelMap || !m_dllAvUtil.Load() || !m_dllAvCodec.Load())
- return false;
-
- m_dllAvCodec.avcodec_register_all();
- AVCodec *codec;
- codec = m_dllAvCodec.avcodec_find_encoder(CODEC_ID_AC3);
- if (!codec)
- return false;
-
- /* always assume 6 channels, 5.1... m_remap will give us what we want */
- m_CodecCtx = m_dllAvCodec.avcodec_alloc_context3(codec);
- m_CodecCtx->bit_rate = AC3_ENCODE_BITRATE;
- m_CodecCtx->sample_rate = sampleRate;
- m_CodecCtx->channels = 6;
- m_CodecCtx->channel_layout = AV_CH_LAYOUT_5POINT1_BACK;
-
- switch(bitsPerSample)
- {
- case 8: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8 ; break;
- case 16: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; break;
- case 32: m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S32; break;
- default:
- m_dllAvUtil.av_freep(&m_CodecCtx);
- return false;
- }
-
- /* check if the sample format is supported by the encoder */
- if (codec->sample_fmts)
- {
- int i;
- for (i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++)
- if (m_CodecCtx->sample_fmt == codec->sample_fmts[i])
- break;
- if (codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE)
- {
- /* the input format was not supported, initiate conversion to the first
- * supported format */
- CLog::Log(LOGDEBUG, "CDVDAudioEncoderFFmpeg: Initializing audio conversion for encoding");
- m_AudioConvert = m_dllAvCodec.av_audio_convert_alloc(codec->sample_fmts[0], 1,
- m_CodecCtx->sample_fmt, 1, NULL, 0);
- if (!m_AudioConvert)
- {
- m_dllAvUtil.av_freep(&m_CodecCtx);
- return false;
- }
- m_CodecCtx->sample_fmt = codec->sample_fmts[0];
- }
- }
-
- if (m_dllAvCodec.avcodec_open2(m_CodecCtx, codec, NULL))
- {
- m_dllAvUtil.av_freep(&m_CodecCtx);
- return false;
- }
-
- /* remap the channels according to the specified channel layout */
- int index = 0;
- if (m_CodecCtx->channel_layout & AV_CH_FRONT_LEFT ) m_ChannelMap[index++] = PCM_FRONT_LEFT ;
- if (m_CodecCtx->channel_layout & AV_CH_FRONT_RIGHT ) m_ChannelMap[index++] = PCM_FRONT_RIGHT ;
- if (m_CodecCtx->channel_layout & AV_CH_FRONT_CENTER ) m_ChannelMap[index++] = PCM_FRONT_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_LOW_FREQUENCY ) m_ChannelMap[index++] = PCM_LOW_FREQUENCY ;
- if (m_CodecCtx->channel_layout & AV_CH_BACK_LEFT ) m_ChannelMap[index++] = PCM_BACK_LEFT ;
- if (m_CodecCtx->channel_layout & AV_CH_BACK_RIGHT ) m_ChannelMap[index++] = PCM_BACK_RIGHT ;
- if (m_CodecCtx->channel_layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_ChannelMap[index++] = PCM_FRONT_LEFT_OF_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_ChannelMap[index++] = PCM_FRONT_RIGHT_OF_CENTER;
- if (m_CodecCtx->channel_layout & AV_CH_BACK_CENTER ) m_ChannelMap[index++] = PCM_BACK_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_SIDE_LEFT ) m_ChannelMap[index++] = PCM_SIDE_LEFT ;
- if (m_CodecCtx->channel_layout & AV_CH_SIDE_RIGHT ) m_ChannelMap[index++] = PCM_SIDE_RIGHT ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_CENTER ) m_ChannelMap[index++] = PCM_TOP_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_FRONT_LEFT ) m_ChannelMap[index++] = PCM_TOP_FRONT_LEFT ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_FRONT_CENTER ) m_ChannelMap[index++] = PCM_TOP_FRONT_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_FRONT_RIGHT ) m_ChannelMap[index++] = PCM_TOP_FRONT_RIGHT ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_BACK_LEFT ) m_ChannelMap[index++] = PCM_TOP_BACK_LEFT ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_BACK_CENTER ) m_ChannelMap[index++] = PCM_TOP_BACK_CENTER ;
- if (m_CodecCtx->channel_layout & AV_CH_TOP_BACK_RIGHT ) m_ChannelMap[index++] = PCM_TOP_BACK_RIGHT ;
-
- m_Remap.SetInputFormat (channels, channelMap, bitsPerSample / 8, sampleRate);
- m_Remap.SetOutputFormat(index, m_ChannelMap, true);
-
- if (!m_Remap.CanRemap())
- {
- m_dllAvUtil.av_freep(&m_CodecCtx);
- return false;
- }
-
- m_BitsPerSample = bitsPerSample;
- m_NeededFrames = m_CodecCtx->frame_size;
- m_NeededBytes = m_Remap.FramesToInBytes (m_NeededFrames);
- m_OutputBytes = m_Remap.FramesToOutBytes(m_NeededFrames);
- m_Buffer = new uint8_t[FF_MIN_BUFFER_SIZE];
- m_TmpBuffer = new uint8_t[m_OutputBytes];
-
- if (m_AudioConvert)
- m_TmpBuffer2 = new uint8_t[m_NeededFrames * m_CodecCtx->channels *
- m_dllAvUtil.av_get_bytes_per_sample(m_CodecCtx->sample_fmt)];
-
- return true;
-}
-
-void CDVDAudioEncoderFFmpeg::Reset()
-{
- m_BufferSize = 0;
-}
-
-unsigned int CDVDAudioEncoderFFmpeg::GetBitRate()
-{
- return AC3_ENCODE_BITRATE;
-}
-
-CodecID CDVDAudioEncoderFFmpeg::GetCodecID()
-{
- return CODEC_ID_AC3;
-}
-
-unsigned int CDVDAudioEncoderFFmpeg::GetPacketSize()
-{
- return m_NeededBytes;
-}
-
-int CDVDAudioEncoderFFmpeg::Encode(uint8_t *data, int size)
-{
- /* remap the data and encode it in blocks */
- if (size < (int)m_NeededBytes)
- return 0;
-
- m_Remap.Remap(data, m_TmpBuffer, m_NeededFrames);
-
- if (m_AudioConvert) {
- void *convInBuf[] = { m_TmpBuffer };
- int convInStr[] = { m_BitsPerSample / 8 };
- void *convOutBuf[] = { m_TmpBuffer2 };
- int convOutStr[] = { m_dllAvUtil.av_get_bytes_per_sample(m_CodecCtx->sample_fmt) };
- if (m_dllAvCodec.av_audio_convert(m_AudioConvert, convOutBuf, convOutStr,
- convInBuf, convInStr, m_NeededFrames * m_CodecCtx->channels) < 0) {
- CLog::Log(LOGERROR, "CDVDAudioEncoderFFmpeg: Audio conversion failed");
- m_BufferSize = 0;
- return m_NeededBytes;
- }
- }
-
- short *inBuf = (short *)(m_AudioConvert ? m_TmpBuffer2 : m_TmpBuffer);
- m_BufferSize = m_dllAvCodec.avcodec_encode_audio(m_CodecCtx, m_Buffer, FF_MIN_BUFFER_SIZE, inBuf);
- return m_NeededBytes;
-}
-
-int CDVDAudioEncoderFFmpeg::GetData(uint8_t **data)
-{
- int size;
- *data = m_Buffer;
- size = m_BufferSize;
- m_BufferSize = 0;
- return size;
-}
-
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.h
deleted file mode 100644
index 40be42bc42..0000000000
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Encoders/DVDAudioEncoderFFmpeg.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-/*
- * Copyright (C) 2005-2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "IDVDAudioEncoder.h"
-#include "DllAvCodec.h"
-#include "DllAvFormat.h"
-#include "DllAvUtil.h"
-
-class CDVDAudioEncoderFFmpeg: public IDVDAudioEncoder
-{
-public:
- CDVDAudioEncoderFFmpeg();
- virtual ~CDVDAudioEncoderFFmpeg();
- virtual bool Initialize(unsigned int channels, enum PCMChannels *channelMap, unsigned int bitsPerSample, unsigned int sampleRate);
- virtual void Reset();
-
- /* returns this DSPs output format */
- virtual unsigned int GetBitRate ();
- virtual CodecID GetCodecID ();
- virtual unsigned int GetPacketSize();
-
- /* add/get packets to/from the DSP */
- virtual int Encode (uint8_t *data, int size);
- virtual int GetData(uint8_t **data);
-private:
- DllAvCodec m_dllAvCodec;
- DllAvUtil m_dllAvUtil;
-
- AVCodecContext *m_CodecCtx;
- AVAudioConvert *m_AudioConvert;
- enum PCMChannels m_ChannelMap[PCM_MAX_CH];
- CPCMRemap m_Remap;
- uint8_t *m_Buffer;
- uint8_t *m_TmpBuffer;
- uint8_t *m_TmpBuffer2;
- int m_BufferSize;
-
- unsigned int m_NeededFrames;
- unsigned int m_NeededBytes;
- unsigned int m_OutputBytes;
- unsigned int m_BitsPerSample;
-};
-
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in
index c40ee33bd3..5423f9da8b 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in
@@ -5,9 +5,8 @@ CXXFLAGS+=-DHAVE_MMX
SRCS= DVDAudioCodecFFmpeg.cpp \
DVDAudioCodecLibMad.cpp \
DVDAudioCodecLPcm.cpp \
- DVDAudioCodecPassthroughFFmpeg.cpp \
- DVDAudioCodecPcm.cpp \
- Encoders/DVDAudioEncoderFFmpeg.cpp
+ DVDAudioCodecPassthrough.cpp \
+ DVDAudioCodecPcm.cpp
LIB=Audio.a
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
index 983d9bfa62..78b34aad1e 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
@@ -41,7 +41,10 @@
#include "Audio/DVDAudioCodecLibMad.h"
#include "Audio/DVDAudioCodecPcm.h"
#include "Audio/DVDAudioCodecLPcm.h"
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_DARWIN_IOS)
#include "Audio/DVDAudioCodecPassthroughFFmpeg.h"
+#endif
+#include "Audio/DVDAudioCodecPassthrough.h"
#include "Overlay/DVDOverlayCodecSSA.h"
#include "Overlay/DVDOverlayCodecText.h"
#include "Overlay/DVDOverlayCodecTX3G.h"
@@ -270,8 +273,18 @@ CDVDAudioCodec* CDVDFactoryCodec::CreateAudioCodec( CDVDStreamInfo &hint, bool p
if (passthrough)
{
- pCodec = OpenCodec( new CDVDAudioCodecPassthroughFFmpeg(), hint, options);
- if ( pCodec ) return pCodec;
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_DARWIN_IOS)
+ switch(hint.codec)
+ {
+ case CODEC_ID_AC3:
+ case CODEC_ID_DTS:
+ pCodec = OpenCodec( new CDVDAudioCodecPassthroughFFmpeg(), hint, options );
+ if( pCodec ) return pCodec;
+ break;
+ }
+#endif
+ pCodec = OpenCodec( new CDVDAudioCodecPassthrough(), hint, options );
+ if( pCodec ) return pCodec;
}
switch (hint.codec)
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h
index 1299d23817..c5afa70ecf 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.h
+++ b/xbmc/cores/dvdplayer/DVDPlayer.h
@@ -167,8 +167,6 @@ class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer
public:
CDVDPlayer(IPlayerCallback& callback);
virtual ~CDVDPlayer();
- virtual void RegisterAudioCallback(IAudioCallback* pCallback) { m_dvdPlayerAudio.RegisterAudioCallback(pCallback); }
- virtual void UnRegisterAudioCallback() { m_dvdPlayerAudio.UnRegisterAudioCallback(); }
virtual bool OpenFile(const CFileItem& file, const CPlayerOptions &options);
virtual bool CloseFile();
virtual bool IsPlaying() const;
@@ -184,7 +182,7 @@ public:
virtual float GetPercentage();
virtual float GetCachePercentage();
- virtual void SetVolume(long nVolume) { m_dvdPlayerAudio.SetVolume(nVolume); }
+ virtual void SetVolume(float nVolume) { m_dvdPlayerAudio.SetVolume(nVolume); }
virtual void SetDynamicRangeCompression(long drc) { m_dvdPlayerAudio.SetDynamicRangeCompression(drc); }
virtual void GetAudioInfo(CStdString& strAudioInfo);
virtual void GetVideoInfo(CStdString& strVideoInfo);
diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
index b0cc80e847..97c402b9f9 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
@@ -31,10 +31,18 @@
#include "utils/log.h"
#include "utils/TimeUtils.h"
#include "utils/MathUtils.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
#include <sstream>
#include <iomanip>
+/* for sync-based resampling */
+#define PROPORTIONAL 20.0
+#define PROPREF 0.01
+#define PROPDIVMIN 2.0
+#define PROPDIVMAX 40.0
+#define INTEGRAL 200.0
+
using namespace std;
CPTSOutputQueue::CPTSOutputQueue()
@@ -226,7 +234,6 @@ void CDVDPlayerAudio::OpenStream( CDVDStreamInfo &hints, CDVDAudioCodec* codec )
m_synctype = SYNC_DISCON;
m_setsynctype = g_guiSettings.GetInt("videoplayer.synctype");
m_prevsynctype = -1;
- m_resampler.SetQuality(g_guiSettings.GetInt("videoplayer.resamplequality"));
m_error = 0;
m_errorbuff = 0;
@@ -277,7 +284,6 @@ void CDVDPlayerAudio::CloseStream(bool bWaitForBuffers)
// flush any remaining pts values
m_ptsOutput.Flush();
- m_resampler.Flush();
}
// decode one audio frame and returns its uncompressed size
@@ -328,16 +334,20 @@ int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe, bool bDropPacket)
// get decoded data and the size of it
audioframe.size = m_pAudioCodec->GetData(&audioframe.data);
- audioframe.pts = m_audioClock;
- audioframe.channel_map = m_pAudioCodec->GetChannelMap();
- audioframe.channels = m_pAudioCodec->GetChannels(); /* get channels AFTER map so that it can be corrected if bad */
- audioframe.bits_per_sample = m_pAudioCodec->GetBitsPerSample();
- audioframe.sample_rate = m_pAudioCodec->GetSampleRate();
- audioframe.passthrough = m_pAudioCodec->NeedPassthrough();
+ audioframe.pts = m_audioClock;
if (audioframe.size <= 0)
continue;
+ audioframe.channel_layout = m_pAudioCodec->GetChannelMap();
+ audioframe.channel_count = m_pAudioCodec->GetChannels();
+ audioframe.encoded_channel_count = m_pAudioCodec->GetEncodedChannels();
+ audioframe.data_format = m_pAudioCodec->GetDataFormat();
+ audioframe.bits_per_sample = CAEUtil::DataFormatToBits(audioframe.data_format);
+ audioframe.sample_rate = m_pAudioCodec->GetSampleRate();
+ audioframe.encoded_sample_rate = m_pAudioCodec->GetEncodedSampleRate();
+ audioframe.passthrough = m_pAudioCodec->NeedPassthrough();
+
if (m_streaminfo.samplerate != audioframe.sample_rate)
{
// The sample rate has changed or we just got it for the first time
@@ -354,7 +364,7 @@ int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe, bool bDropPacket)
}
// compute duration.
- int n = (audioframe.channels * audioframe.bits_per_sample * audioframe.sample_rate)>>3;
+ int n = (audioframe.channel_count * audioframe.bits_per_sample * audioframe.sample_rate)>>3;
if (n > 0)
{
// safety check, if channels == 0, n will result in 0, and that will result in a nice devide exception
@@ -440,7 +450,6 @@ int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe, bool bDropPacket)
m_dvdAudio.Flush();
m_ptsOutput.Flush();
m_ptsInput.Flush();
- m_resampler.Flush();
m_syncclock = true;
m_stalled = true;
m_started = false;
@@ -486,7 +495,6 @@ int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe, bool bDropPacket)
else
{
m_ptsOutput.Flush();
- m_resampler.Flush();
m_syncclock = true;
if (m_speed != DVD_PLAYSPEED_PAUSE)
m_dvdAudio.Flush();
@@ -589,7 +597,7 @@ void CDVDPlayerAudio::Process()
else
m_dvdAudio.Pause();
- if(!m_dvdAudio.Create(audioframe, m_streaminfo.codec))
+ if(!m_dvdAudio.Create(audioframe, m_streaminfo.codec, m_setsynctype == SYNC_RESAMPLE))
CLog::Log(LOGERROR, "%s - failed to create audio renderer", __FUNCTION__);
}
@@ -709,7 +717,6 @@ void CDVDPlayerAudio::HandleSyncError(double duration)
m_integral = 0;
m_skipdupcount = 0;
m_error = 0;
- m_resampler.Flush();
m_errortime = CurrentHostCounter();
return;
}
@@ -822,19 +829,10 @@ bool CDVDPlayerAudio::OutputPacket(DVDAudioFrame &audioframe)
proportional = m_error / DVD_TIME_BASE / proportionaldiv;
}
- m_resampleratio = 1.0 / g_VideoReferenceClock.GetSpeed() + proportional + m_integral;
- m_resampler.SetRatio(m_resampleratio);
- //add to the resampler
- m_resampler.Add(audioframe, audioframe.pts);
- //give any packets from the resampler to the audiorenderer
- bool packetadded = false;
- while(m_resampler.Retrieve(audioframe, audioframe.pts))
- {
- m_dvdAudio.AddPackets(audioframe);
- packetadded = true;
- }
- return packetadded;
+ m_resampleratio = 1.0 / g_VideoReferenceClock.GetSpeed() + proportional + m_integral;
+ m_dvdAudio.SetResampleRatio(m_resampleratio);
+ m_dvdAudio.AddPackets(audioframe);
}
return true;
diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.h b/xbmc/cores/dvdplayer/DVDPlayerAudio.h
index 739e8a106f..e71bd51f07 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerAudio.h
+++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.h
@@ -28,7 +28,8 @@
#include "DVDDemuxers/DVDDemuxUtils.h"
#include "DVDStreamInfo.h"
#include "utils/BitstreamStats.h"
-#include "DVDPlayerAudioResampler.h"
+
+#include "cores/AudioEngine/AEAudioFormat.h"
#include <list>
#include <queue>
@@ -53,11 +54,14 @@ typedef struct stDVDAudioFrame
double duration;
unsigned int size;
- int channels;
- enum PCMChannels *channel_map;
- int bits_per_sample;
- int sample_rate;
- bool passthrough;
+ int channel_count;
+ int encoded_channel_count;
+ CAEChannelInfo channel_layout;
+ enum AEDataFormat data_format;
+ int bits_per_sample;
+ int sample_rate;
+ int encoded_sample_rate;
+ bool passthrough;
} DVDAudioFrame;
class CPTSOutputQueue
@@ -93,9 +97,6 @@ public:
CDVDPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent);
virtual ~CDVDPlayerAudio();
- void RegisterAudioCallback(IAudioCallback* pCallback) { m_dvdAudio.RegisterAudioCallback(pCallback); }
- void UnRegisterAudioCallback() { m_dvdAudio.UnRegisterAudioCallback(); }
-
bool OpenStream(CDVDStreamInfo &hints);
void OpenStream(CDVDStreamInfo &hints, CDVDAudioCodec* codec);
void CloseStream(bool bWaitForBuffers);
@@ -115,7 +116,7 @@ public:
//! codec changes, in which case we may want to switch passthrough on/off.
bool SwitchCodecIfNeeded();
- void SetVolume(long nVolume) { m_dvdAudio.SetVolume(nVolume); }
+ void SetVolume(float fVolume) { m_dvdAudio.SetVolume(fVolume); }
void SetDynamicRangeCompression(long drc) { m_dvdAudio.SetDynamicRangeCompression(drc); }
float GetCurrentAttenuation() { return m_dvdAudio.GetCurrentAttenuation(); }
@@ -185,8 +186,6 @@ protected:
double m_duration; // last packets duration
bool m_silence;
- CDVDPlayerResampler m_resampler;
-
bool OutputPacket(DVDAudioFrame &audioframe);
//SYNC_DISCON, SYNC_SKIPDUP, SYNC_RESAMPLE
diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudioResampler.cpp b/xbmc/cores/dvdplayer/DVDPlayerAudioResampler.cpp
index 33606c7166..ea334a0dbd 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerAudioResampler.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerAudioResampler.cpp
@@ -51,11 +51,11 @@ CDVDPlayerResampler::~CDVDPlayerResampler()
void CDVDPlayerResampler::Add(DVDAudioFrame &audioframe, double pts)
{
//check if nr of channels changed so we can allocate new buffers if necessary
- CheckResampleBuffers(audioframe.channels);
+ CheckResampleBuffers(audioframe.channel_count);
//value to divide samples by to get them into -1.0:1.0 range
float scale = (float)(1 << (audioframe.bits_per_sample - 1));
- int nrframes = audioframe.size / audioframe.channels / (audioframe.bits_per_sample / 8);
+ int nrframes = audioframe.size / audioframe.channel_count / (audioframe.bits_per_sample / 8);
//resize sample buffer if necessary
//we want the buffer to be large enough to hold the current frames in it,
@@ -94,11 +94,11 @@ void CDVDPlayerResampler::Add(DVDAudioFrame &audioframe, double pts)
bool CDVDPlayerResampler::Retrieve(DVDAudioFrame &audioframe, double &pts)
{
//check if nr of channels changed so we can allocate new buffers if necessary
- CheckResampleBuffers(audioframe.channels);
+ CheckResampleBuffers(audioframe.channel_count);
//value to divide samples by to get them into -1.0:1.0 range
float scale = (float)(1 << (audioframe.bits_per_sample - 1));
- int nrframes = audioframe.size / audioframe.channels / (audioframe.bits_per_sample / 8);
+ int nrframes = audioframe.size / audioframe.channel_count / (audioframe.bits_per_sample / 8);
//if we don't have enough in the samplebuffer, return false
if (nrframes > m_bufferfill)
diff --git a/xbmc/cores/paplayer/ADPCMCodec.cpp b/xbmc/cores/paplayer/ADPCMCodec.cpp
index 6425f0fb3d..028a88f1cf 100644
--- a/xbmc/cores/paplayer/ADPCMCodec.cpp
+++ b/xbmc/cores/paplayer/ADPCMCodec.cpp
@@ -51,6 +51,7 @@ bool ADPCMCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = m_dll.GetNumberOfChannels(m_adpcm);
m_SampleRate = m_dll.GetPlaybackRate(m_adpcm);
m_BitsPerSample = 16;//m_dll.GetSampleSize(m_adpcm);
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = m_dll.GetLength(m_adpcm); // fixme?
m_iDataPos = 0;
diff --git a/xbmc/cores/paplayer/ASAPCodec.cpp b/xbmc/cores/paplayer/ASAPCodec.cpp
index 64dab26eca..98d0543464 100644
--- a/xbmc/cores/paplayer/ASAPCodec.cpp
+++ b/xbmc/cores/paplayer/ASAPCodec.cpp
@@ -58,6 +58,7 @@ bool ASAPCodec::Init(const CStdString &strFile, unsigned int filecache)
m_TotalTime = duration;
m_SampleRate = 44100;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
return true;
}
diff --git a/xbmc/cores/paplayer/AudioDecoder.cpp b/xbmc/cores/paplayer/AudioDecoder.cpp
index e5a98430d9..6ca6d3d2ad 100644
--- a/xbmc/cores/paplayer/AudioDecoder.cpp
+++ b/xbmc/cores/paplayer/AudioDecoder.cpp
@@ -28,8 +28,6 @@
#include "utils/log.h"
#include <math.h>
-#define INTERNAL_BUFFER_LENGTH sizeof(float)*2*44100 // float samples, 2 channels, 44100 samples per sec = 1 second
-
CAudioDecoder::CAudioDecoder()
{
m_codec = NULL;
@@ -38,9 +36,6 @@ CAudioDecoder::CAudioDecoder()
m_status = STATUS_NO_FILE;
m_canPlay = false;
-
- m_gaplessBufferSize = 0;
- m_blockSize = 4;
}
CAudioDecoder::~CAudioDecoder()
@@ -54,7 +49,6 @@ void CAudioDecoder::Destroy()
m_status = STATUS_NO_FILE;
m_pcmBuffer.Destroy();
- m_gaplessBufferSize = 0;
if ( m_codec )
delete m_codec;
@@ -63,14 +57,11 @@ void CAudioDecoder::Destroy()
m_canPlay = false;
}
-bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset, unsigned int nBufferSize)
+bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset)
{
Destroy();
CSingleLock lock(m_critSection);
- // create our pcm buffer
- m_pcmBuffer.Create((int)std::max<unsigned int>(2, nBufferSize) *
- INTERNAL_BUFFER_LENGTH);
// reset our playback timing variables
m_eof = false;
@@ -93,8 +84,11 @@ bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset, unsigned i
Destroy();
return false;
}
- m_blockSize = m_codec->m_Channels * m_codec->m_BitsPerSample / 8;
-
+ unsigned int blockSize = (m_codec->m_BitsPerSample >> 3) * m_codec->GetChannelInfo().Count();
+
+ /* allocate the pcmBuffer for 2 seconds of audio */
+ m_pcmBuffer.Create(2 * blockSize * m_codec->m_SampleRate);
+
// set total time from the given tag
if (file.HasMusicInfoTag() && file.GetMusicInfoTag()->GetDuration())
m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration());
@@ -107,14 +101,15 @@ bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset, unsigned i
return true;
}
-void CAudioDecoder::GetDataFormat(unsigned int *channels, unsigned int *samplerate, unsigned int *bitspersample)
+void CAudioDecoder::GetDataFormat(CAEChannelInfo *channelInfo, unsigned int *samplerate, unsigned int *encodedSampleRate, enum AEDataFormat *dataFormat)
{
if (!m_codec)
return;
- if (channels) *channels = m_codec->m_Channels;
- if (samplerate) *samplerate = m_codec->m_SampleRate;
- if (bitspersample) *bitspersample = m_codec->m_BitsPerSample;
+ if (channelInfo ) *channelInfo = m_codec->GetChannelInfo();
+ if (samplerate ) *samplerate = m_codec->m_SampleRate;
+ if (encodedSampleRate) *encodedSampleRate = m_codec->m_EncodedSampleRate;
+ if (dataFormat ) *dataFormat = m_codec->m_DataFormat;
}
int64_t CAudioDecoder::Seek(int64_t time)
@@ -141,55 +136,36 @@ unsigned int CAudioDecoder::GetDataSize()
// check for end of file and end of buffer
if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE)
m_status = STATUS_ENDED;
- return m_pcmBuffer.getMaxReadSize() / sizeof(float);
+ return std::min(m_pcmBuffer.getMaxReadSize() / (m_codec->m_BitsPerSample >> 3), (unsigned int)OUTPUT_SAMPLES);
}
-void *CAudioDecoder::GetData(unsigned int size)
+void *CAudioDecoder::GetData(unsigned int samples)
{
- if (size > OUTPUT_SAMPLES)
+ unsigned int size = samples * (m_codec->m_BitsPerSample >> 3);
+ if (size > sizeof(m_outputBuffer))
{
- CLog::Log(LOGWARNING, "CAudioDecoder::GetData() more bytes/samples (%i) requested than we have to give (%i)!", size, OUTPUT_SAMPLES);
- size = OUTPUT_SAMPLES;
+ CLog::Log(LOGERROR, "CAudioDecoder::GetData - More data was requested then we have space to buffer!");
+ return NULL;
}
- // first copy anything from our gapless buffer
- if (m_gaplessBufferSize > size)
+
+ if (size > m_pcmBuffer.getMaxReadSize())
{
- memcpy(m_outputBuffer, m_gaplessBuffer, size*sizeof(float));
- memmove(m_gaplessBuffer, m_gaplessBuffer + size, (m_gaplessBufferSize - size)*sizeof(float));
- m_gaplessBufferSize -= size;
- return m_outputBuffer;
+ CLog::Log(LOGWARNING, "CAudioDecoder::GetData() more bytes/samples (%i) requested than we have to give (%i)!", size, m_pcmBuffer.getMaxReadSize());
+ size = m_pcmBuffer.getMaxReadSize();
}
- if (m_gaplessBufferSize)
- memcpy(m_outputBuffer, m_gaplessBuffer, m_gaplessBufferSize*sizeof(float));
- if (m_pcmBuffer.ReadData( (char *)(m_outputBuffer + m_gaplessBufferSize), (size - m_gaplessBufferSize) * sizeof(float)))
+ if (m_pcmBuffer.ReadData((char *)m_outputBuffer, size))
{
- m_gaplessBufferSize = 0;
- // check for end of file + end of buffer
- if ( m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() < (int) (OUTPUT_SAMPLES * sizeof(float)))
- {
- CLog::Log(LOGINFO, "CAudioDecoder::GetData() ending track - only have %lu samples left", (unsigned long)(m_pcmBuffer.getMaxReadSize() / sizeof(float)));
+ if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() == 0)
m_status = STATUS_ENDED;
- }
+
return m_outputBuffer;
}
- CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with %i samples", size - m_gaplessBufferSize);
+
+ CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with %i samples", samples);
return NULL;
}
-void CAudioDecoder::PrefixData(void *data, unsigned int size)
-{
- if (!data)
- {
- CLog::Log(LOGERROR, "CAudioDecoder::PrefixData() failed - null data pointer");
- return;
- }
- m_gaplessBufferSize = std::min<unsigned int>(PACKET_SIZE, size);
- memcpy(m_gaplessBuffer, data, m_gaplessBufferSize*sizeof(float));
- if (m_gaplessBufferSize != size)
- CLog::Log(LOGWARNING, "CAudioDecoder::PrefixData - losing %i bytes of audio data in track transistion", size - m_gaplessBufferSize);
-}
-
int CAudioDecoder::ReadSamples(int numsamples)
{
if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED)
@@ -203,27 +179,18 @@ int CAudioDecoder::ReadSamples(int numsamples)
CSingleLock lock(m_critSection);
// Read in more data
- int maxsize = std::min<int>(INPUT_SAMPLES,
- (m_pcmBuffer.getMaxWriteSize() / (int)(sizeof (float))));
+ int maxsize = std::min<int>(INPUT_SAMPLES, m_pcmBuffer.getMaxWriteSize() / (m_codec->m_BitsPerSample >> 3));
numsamples = std::min<int>(numsamples, maxsize);
- numsamples -= (numsamples % m_codec->m_Channels); // make sure it's divisible by our number of channels
+ numsamples -= (numsamples % m_codec->GetChannelInfo().Count()); // make sure it's divisible by our number of channels
if ( numsamples )
{
- int actualsamples = 0;
- // if our codec sends floating point, then read it
- int result = READ_ERROR;
- if (m_codec->HasFloatData())
- result = m_codec->ReadSamples(m_inputBuffer, numsamples, &actualsamples);
- else
- result = ReadPCMSamples(m_inputBuffer, numsamples, &actualsamples);
-
- if ( result != READ_ERROR && actualsamples )
- {
- // do any post processing of the audio (eg replaygain etc.)
- ProcessAudio(m_inputBuffer, actualsamples);
+ int readSize = 0;
+ int result = m_codec->ReadPCM(m_pcmInputBuffer, numsamples * (m_codec->m_BitsPerSample >> 3), &readSize);
+ if (result != READ_ERROR && readSize)
+ {
// move it into our buffer
- m_pcmBuffer.WriteData((char *)m_inputBuffer, actualsamples * sizeof(float));
+ m_pcmBuffer.WriteData((char *)m_pcmInputBuffer, readSize);
// update status
if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9)
@@ -259,24 +226,12 @@ int CAudioDecoder::ReadSamples(int numsamples)
return RET_SLEEP; // nothing to do
}
-void CAudioDecoder::ProcessAudio(float *data, int numsamples)
-{
- if (g_guiSettings.m_replayGain.iType != REPLAY_GAIN_NONE)
- {
- float gainFactor = GetReplayGain();
- for (int i = 0; i < numsamples; i++)
- {
- data[i] *= gainFactor;
- // check the range (is this needed here?)
- if (data[i] > 1.0f) data[i] = 1.0f;
- if (data[i] < -1.0f) data[i] = -1.0f;
- }
- }
-}
-
float CAudioDecoder::GetReplayGain()
{
#define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
+ if (g_guiSettings.m_replayGain.iType == REPLAY_GAIN_NONE)
+ return 0.0f;
+
// Compute amount of gain
float replaydB = (float)g_guiSettings.m_replayGain.iNoGainPreAmp;
float peak = 0.0f;
@@ -314,36 +269,7 @@ float CAudioDecoder::GetReplayGain()
if (fabs(peak * replaygain) > 1.0f)
replaygain = 1.0f / fabs(peak);
}
- return replaygain;
-}
-
-int CAudioDecoder::ReadPCMSamples(float *buffer, int numsamples, int *actualsamples)
-{
- // convert samples to bytes
- numsamples *= (m_codec->m_BitsPerSample / 8);
- // read in our PCM data
- int result = m_codec->ReadPCM(m_pcmInputBuffer, numsamples, actualsamples);
-
- // convert to floats (-1 ... 1) range
- int i;
- switch (m_codec->m_BitsPerSample)
- {
- case 8:
- for (i = 0; i < *actualsamples; i++)
- m_inputBuffer[i] = 1.0f / 0x7f * (m_pcmInputBuffer[i] - 128);
- break;
- case 16:
- *actualsamples /= 2;
- for (i = 0; i < *actualsamples; i++)
- m_inputBuffer[i] = 1.0f / 0x7fff * ((short *)m_pcmInputBuffer)[i];
- break;
- case 24:
- *actualsamples /= 3;
- for (i = 0; i < *actualsamples; i++)
- m_inputBuffer[i] = 1.0f / 0x7fffff * (((int)m_pcmInputBuffer[3*i] << 0) | ((int)m_pcmInputBuffer[3*i+1] << 8) | (((int)((char *)m_pcmInputBuffer)[3*i+2]) << 16));
- break;
- }
- return result;
+ return replaygain;
}
diff --git a/xbmc/cores/paplayer/AudioDecoder.h b/xbmc/cores/paplayer/AudioDecoder.h
index 3a5f1de619..3ad03fb7f2 100644
--- a/xbmc/cores/paplayer/AudioDecoder.h
+++ b/xbmc/cores/paplayer/AudioDecoder.h
@@ -25,6 +25,7 @@
#include "ICodec.h"
#include "threads/CriticalSection.h"
#include "utils/RingBuffer.h"
+#include "cores/AudioEngine/Utils/AEChannelInfo.h"
class CFileItem;
@@ -56,7 +57,7 @@ public:
CAudioDecoder();
~CAudioDecoder();
- bool Create(const CFileItem &file, int64_t seekOffset, unsigned int nBufferSize);
+ bool Create(const CFileItem &file, int64_t seekOffset);
void Destroy();
int ReadSamples(int numsamples);
@@ -68,32 +69,21 @@ public:
int GetStatus() { return m_status; };
void SetStatus(int status) { m_status = status; };
- void GetDataFormat(unsigned int *channels, unsigned int *samplerate, unsigned int *bitspersample);
- unsigned int GetChannels() { if (m_codec) return m_codec->m_Channels; else return 0; };
+ void GetDataFormat(CAEChannelInfo *channelInfo, unsigned int *samplerate, unsigned int *encodedSampleRate, enum AEDataFormat *dataFormat);
+ unsigned int GetChannels() { if (m_codec) return m_codec->GetChannelInfo().Count(); else return 0; };
// Data management
unsigned int GetDataSize();
- void *GetData(unsigned int size);
- void PrefixData(void *data, unsigned int size);
+ void *GetData(unsigned int samples);
ICodec *GetCodec() const { return m_codec; }
-
-private:
- void ProcessAudio(float *data, int numsamples);
- // ReadPCMSamples() - helper to convert PCM (short/byte) to float
- int ReadPCMSamples(float *buffer, int numsamples, int *actualsamples);
float GetReplayGain();
- // block size (number of bytes per sample * number of channels)
- int m_blockSize;
+private:
// pcm buffer
CRingBuffer m_pcmBuffer;
// output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain)
float m_outputBuffer[OUTPUT_SAMPLES];
- // gapless buffer (left over samples from the previous audio decoder)
- float m_gaplessBuffer[OUTPUT_SAMPLES];
- unsigned int m_gaplessBufferSize;
-
// input buffer (for transferring data from the Codecs to our Pcm Ringbuffer
BYTE m_pcmInputBuffer[INPUT_SIZE];
float m_inputBuffer[INPUT_SAMPLES];
diff --git a/xbmc/cores/paplayer/BXAcodec.cpp b/xbmc/cores/paplayer/BXAcodec.cpp
index 94ba3dabe3..18d024cb27 100644
--- a/xbmc/cores/paplayer/BXAcodec.cpp
+++ b/xbmc/cores/paplayer/BXAcodec.cpp
@@ -57,6 +57,7 @@ bool BXACodec::Init(const CStdString &strFile, unsigned int filecache)
m_BitsPerSample = bxah.bitsPerSample;
m_TotalTime = bxah.durationMs;
m_Bitrate = bxah.sampleRate * bxah.channels * bxah.bitsPerSample;
+ m_DataFormat = AE_FMT_S16LE;
if (m_SampleRate == 0 || m_Channels == 0 || m_BitsPerSample == 0)
{
diff --git a/xbmc/cores/paplayer/CDDAcodec.cpp b/xbmc/cores/paplayer/CDDAcodec.cpp
index 5389378fdc..9ac727a900 100644
--- a/xbmc/cores/paplayer/CDDAcodec.cpp
+++ b/xbmc/cores/paplayer/CDDAcodec.cpp
@@ -36,6 +36,7 @@ CDDACodec::CDDACodec()
m_SampleRate = 44100;
m_Channels = 2;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = 0;
m_Bitrate = 0;
m_CodecName = "CDDA";
diff --git a/xbmc/cores/paplayer/DVDPlayerCodec.cpp b/xbmc/cores/paplayer/DVDPlayerCodec.cpp
index c6b09188c7..9cac9cf5c5 100644
--- a/xbmc/cores/paplayer/DVDPlayerCodec.cpp
+++ b/xbmc/cores/paplayer/DVDPlayerCodec.cpp
@@ -21,6 +21,7 @@
#include "DVDPlayerCodec.h"
#include "Util.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
#include "cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.h"
#include "cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h"
@@ -28,6 +29,7 @@
#include "cores/dvdplayer/DVDStreamInfo.h"
#include "cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h"
#include "utils/log.h"
+#include "settings/GUISettings.h"
#include "AudioDecoder.h"
@@ -130,7 +132,8 @@ bool DVDPlayerCodec::Init(const CStdString &strFile, unsigned int filecache)
CDVDStreamInfo hint(*pStream, true);
- m_pAudioCodec = CDVDFactoryCodec::CreateAudioCodec(hint, false);
+ bool passthrough = AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode"));
+ m_pAudioCodec = CDVDFactoryCodec::CreateAudioCodec(hint, passthrough);
if (!m_pAudioCodec)
{
CLog::Log(LOGERROR, "%s: Could not create audio codec", __FUNCTION__);
@@ -151,10 +154,12 @@ bool DVDPlayerCodec::Init(const CStdString &strFile, unsigned int filecache)
if (ReadPCM(dummy, nSize, &nSize) == READ_ERROR)
++nErrors;
- // We always ask ffmpeg to return s16le
- m_BitsPerSample = m_pAudioCodec->GetBitsPerSample();
+ m_DataFormat = m_pAudioCodec->GetDataFormat();
+ m_BitsPerSample = CAEUtil::DataFormatToBits(m_DataFormat);
m_SampleRate = m_pAudioCodec->GetSampleRate();
+ m_EncodedSampleRate = m_pAudioCodec->GetEncodedSampleRate();
m_Channels = m_pAudioCodec->GetChannels();
+ m_ChannelInfo = m_pAudioCodec->GetChannelMap();
}
if (nErrors >= 10)
diff --git a/xbmc/cores/paplayer/DVDPlayerCodec.h b/xbmc/cores/paplayer/DVDPlayerCodec.h
index 4f0b85dedd..d610960469 100644
--- a/xbmc/cores/paplayer/DVDPlayerCodec.h
+++ b/xbmc/cores/paplayer/DVDPlayerCodec.h
@@ -40,6 +40,7 @@ public:
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize);
virtual bool CanInit();
virtual bool CanSeek();
+ virtual CAEChannelInfo GetChannelInfo() {return m_ChannelInfo;}
void SetContentType(const CStdString &strContent);
@@ -57,6 +58,8 @@ private:
BYTE *m_decoded;
int m_nDecodedLen;
+
+ CAEChannelInfo m_ChannelInfo;
};
#endif
diff --git a/xbmc/cores/paplayer/FLACcodec.cpp b/xbmc/cores/paplayer/FLACcodec.cpp
index 5808c2fb00..ffe9b05195 100644
--- a/xbmc/cores/paplayer/FLACcodec.cpp
+++ b/xbmc/cores/paplayer/FLACcodec.cpp
@@ -30,6 +30,7 @@ FLACCodec::FLACCodec()
m_SampleRate = 0;
m_Channels = 0;
m_BitsPerSample = 0;
+ m_DataFormat = AE_FMT_INVALID;
m_TotalTime=0;
m_Bitrate = 0;
m_CodecName = "FLAC";
@@ -87,7 +88,7 @@ bool FLACCodec::Init(const CStdString &strFile, unsigned int filecache)
}
// These are filled by the metadata callback
- if (m_SampleRate==0 || m_Channels==0 || m_BitsPerSample==0 || m_TotalTime==0 || m_MaxFrameSize==0)
+ if (m_SampleRate==0 || m_Channels==0 || m_BitsPerSample==0 || m_TotalTime==0 || m_MaxFrameSize==0 || m_DataFormat == AE_FMT_INVALID)
{
CLog::Log(LOGERROR, "FLACCodec: Can't get stream info, SampleRate=%i, Channels=%i, BitsPerSample=%i, TotalTime=%"PRIu64", MaxFrameSize=%i", m_SampleRate, m_Channels, m_BitsPerSample, m_TotalTime, m_MaxFrameSize);
FreeDecoder();
@@ -313,9 +314,31 @@ void FLACCodec::DecoderMetadataCallback(const FLAC__StreamDecoder *decoder, cons
if (metadata->type==FLAC__METADATA_TYPE_STREAMINFO)
{
+ static enum AEChannel map[6][7] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_BL, AE_CH_BR, AE_CH_NULL}
+ };
+
+ /* channel counts greater then 6 are undefined */
+ if (metadata->data.stream_info.channels > 6)
+ pThis->m_ChannelInfo = CAEUtil::GuessChLayout(metadata->data.stream_info.channels);
+ else
+ pThis->m_ChannelInfo = CAEChannelInfo(map[metadata->data.stream_info.channels - 1]);
+
pThis->m_SampleRate = metadata->data.stream_info.sample_rate;
pThis->m_Channels = metadata->data.stream_info.channels;
pThis->m_BitsPerSample = metadata->data.stream_info.bits_per_sample;
+ switch(pThis->m_BitsPerSample)
+ {
+ case 8: pThis->m_DataFormat = AE_FMT_U8; break;
+ case 16: pThis->m_DataFormat = AE_FMT_S16NE; break;
+ case 24: pThis->m_DataFormat = AE_FMT_S24NE3; break;
+ case 32: pThis->m_DataFormat = AE_FMT_FLOAT; break;
+ }
pThis->m_TotalTime = (int64_t)metadata->data.stream_info.total_samples * 1000 / metadata->data.stream_info.sample_rate;
pThis->m_MaxFrameSize = metadata->data.stream_info.max_blocksize*(pThis->m_BitsPerSample/8)*pThis->m_Channels;
}
diff --git a/xbmc/cores/paplayer/FLACcodec.h b/xbmc/cores/paplayer/FLACcodec.h
index 5c7998032d..d5be6b6216 100644
--- a/xbmc/cores/paplayer/FLACcodec.h
+++ b/xbmc/cores/paplayer/FLACcodec.h
@@ -35,6 +35,7 @@ public:
virtual int64_t Seek(int64_t iSeekTime);
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize);
virtual bool CanInit();
+ virtual CAEChannelInfo GetChannelInfo() {return m_ChannelInfo;}
private:
// I/O callbacks for the flac decoder
@@ -54,4 +55,5 @@ private:
int m_BufferSize; // size of buffer is filled with decoded audio data
int m_MaxFrameSize; // size of a single decoded frame
FLAC__StreamDecoder* m_pFlacDecoder;
+ CAEChannelInfo m_ChannelInfo;
};
diff --git a/xbmc/cores/paplayer/ICodec.h b/xbmc/cores/paplayer/ICodec.h
index 0629fbe52a..c1bfecc99c 100644
--- a/xbmc/cores/paplayer/ICodec.h
+++ b/xbmc/cores/paplayer/ICodec.h
@@ -25,6 +25,9 @@
#include "utils/StdString.h"
#include "filesystem/File.h"
+#include "cores/AudioEngine/AEAudioFormat.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+
#define READ_EOF -1
#define READ_SUCCESS 0
#define READ_ERROR 1
@@ -74,14 +77,6 @@ public:
// the data has been exhausted, and READ_ERROR on error.
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize)=0;
- // ReadSamples()
- // Decodes audio into floats (normalized to 1) into pBuffer up to numsamples samples.
- // The actual amount of returned samples is given in actualsamples. Samples are
- // total samples (ie distributed over channels).
- // Returns READ_SUCCESS on success. Returns READ_EOF when the data has been exhausted,
- // and READ_ERROR on error.
- virtual int ReadSamples(float *pBuffer, int numsamples, int *actualsamples) { return READ_ERROR; };
-
// CanInit()
// Should return true if the codec can be initialized
// eg. check if a dll needed for the codec exists
@@ -97,16 +92,22 @@ public:
virtual bool IsCaching() const {return false;}
virtual int GetCacheLevel() const {return -1;}
- // true if we can retrieve normalized float data immediately
- virtual bool HasFloatData() const { return false; }
+ // GetChannelInfo()
+ // Return the channel layout and count information in an CAEChannelInfo object
+ virtual CAEChannelInfo GetChannelInfo() {return CAEUtil::GuessChLayout(m_Channels);}
int64_t m_TotalTime; // time in ms
int m_SampleRate;
+ int m_EncodedSampleRate;
int m_BitsPerSample;
- int m_Channels;
+ enum AEDataFormat m_DataFormat;
int m_Bitrate;
CStdString m_CodecName;
CReplayGain m_replayGain;
XFILE::CFile m_file;
+
+protected:
+ int m_Channels; /* remove this soon, its being deprecated */
+
};
diff --git a/xbmc/cores/paplayer/MP3codec.cpp b/xbmc/cores/paplayer/MP3codec.cpp
index 02f191bcc2..bd1cc4c286 100644
--- a/xbmc/cores/paplayer/MP3codec.cpp
+++ b/xbmc/cores/paplayer/MP3codec.cpp
@@ -33,15 +33,16 @@ using namespace MUSIC_INFO;
#define DECODING_SUCCESS 0
#define DECODING_CALLAGAIN 1
-#define BITSPERSAMPLE 32
-#define mad_scale_float(sample) ((float)(sample/(float)(1L << MAD_F_FRACBITS)))
+#define SAMPLESPERFRAME 1152
+#define CHANNELSPERSAMPLE 2
+#define BITSPERSAMPLE 32
+#define OUTPUTFRAMESIZE (SAMPLESPERFRAME * CHANNELSPERSAMPLE * (BITSPERSAMPLE >> 3))
MP3Codec::MP3Codec()
{
m_SampleRate = 0;
m_Channels = 0;
m_BitsPerSample = 0;
- m_BitsPerSampleInternal = 0;
m_TotalTime = 0;
m_Bitrate = 0;
m_CodecName = "MP3";
@@ -55,9 +56,10 @@ MP3Codec::MP3Codec()
m_InputBufferPos = 0;
memset(&m_Formatdata,0,sizeof(m_Formatdata));
+ m_DataFormat = AE_FMT_S32NE;
// create our output buffer
- m_OutputBufferSize = 1152*4*8; // enough for 4 frames
+ m_OutputBufferSize = OUTPUTFRAMESIZE * 4; // enough for 4 frames
m_OutputBuffer = new BYTE[m_OutputBufferSize];
m_OutputBufferPos = 0;
m_Decoding = false;
@@ -158,13 +160,13 @@ bool MP3Codec::Init(const CStdString &strFile, unsigned int filecache)
}
}
- if ( m_TotalTime && (length-id3v2Size > 0) )
+ if ( m_TotalTime && (length - id3v2Size > 0) )
{
- m_Bitrate = (int)(((length-id3v2Size) / m_seekInfo.GetDuration()) * 8); // average bitrate
+ m_Bitrate = (int)(((length - id3v2Size) / m_seekInfo.GetDuration()) * 8); // average bitrate
}
m_eof = false;
- while ((result != DECODING_SUCCESS) && !m_eof && (m_OutputBufferPos < 1152*8)) // eof can be set from outside (when stopping playback)
+ while ((result != DECODING_SUCCESS) && !m_eof && (m_OutputBufferPos < OUTPUTFRAMESIZE)) // eof can be set from outside (when stopping playback)
{
result = Read(8192, true);
if (result == DECODING_ERROR)
@@ -175,6 +177,7 @@ bool MP3Codec::Init(const CStdString &strFile, unsigned int filecache)
if (bTags && !m_Bitrate) //use tag bitrate if average bitrate is not available
m_Bitrate = m_Formatdata[4];
} ;
+
return true;
error:
@@ -206,13 +209,6 @@ int64_t MP3Codec::Seek(int64_t iSeekTime)
return iSeekTime;
}
-int MP3Codec::ReadSamples(float *pBuffer, int numsamples, int *actualsamples)
-{
- int result = ReadPCM((BYTE *)pBuffer, numsamples * sizeof(float), actualsamples);
- *actualsamples /= sizeof(float);
- return result;
-}
-
int MP3Codec::Read(int size, bool init)
{
int inputBufferToRead = (int)(m_InputBufferSize - m_InputBufferPos);
@@ -281,16 +277,15 @@ int MP3Codec::Read(int size, bool init)
m_Channels = m_Formatdata[2];
m_SampleRate = m_Formatdata[1];
- m_BitsPerSampleInternal = m_Formatdata[3];
- //m_BitsPerSample holds display value when using 32-bits floats (source is 24 bits), real value otherwise
- m_BitsPerSample = m_BitsPerSampleInternal>16?24:m_BitsPerSampleInternal;
+ m_BitsPerSample = m_Formatdata[3];
}
+
// let's check if we need to ignore the decoded data.
if ( m_IgnoreFirst && outputsize && m_seekInfo.GetFirstSample() )
{
// starting up - lets ignore the first (typically 576) samples
int iDelay = DECODER_DELAY + m_seekInfo.GetFirstSample(); // decoder delay + encoder delay
- iDelay *= m_Channels * m_BitsPerSampleInternal / 8; // sample size
+ iDelay *= m_Channels * (m_BitsPerSample >> 3); // sample size
if (outputsize + m_IgnoredBytes >= iDelay)
{
// have enough data to ignore - let's move the valid data to the start
@@ -306,6 +301,7 @@ int MP3Codec::Read(int size, bool init)
outputsize = 0;
}
}
+
// Do we still have data in the buffer to decode?
if ( result == DECODING_CALLAGAIN )
m_CallAgainWithSameBuffer = true;
@@ -320,7 +316,7 @@ int MP3Codec::Read(int size, bool init)
if (m_IgnoreLast && m_seekInfo.GetLastSample())
{
unsigned int samplestoremove = (m_seekInfo.GetLastSample() - DECODER_DELAY);
- samplestoremove *= m_Channels * m_BitsPerSampleInternal / 8;
+ samplestoremove *= m_Channels * (m_BitsPerSample >> 3);
if (samplestoremove > m_OutputBufferPos)
samplestoremove = m_OutputBufferPos;
m_OutputBufferPos -= samplestoremove;
@@ -347,22 +343,22 @@ int MP3Codec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
// check whether we can move data out of our output buffer
// we leave some data in our output buffer to allow us to remove samples
// at the end of the track for gapless playback
- int amounttomove = 0;
- if (m_OutputBufferPos > 1152 * 4 * 2)
- amounttomove = m_OutputBufferPos - 1152 * 4 * 2;
- if (m_eof && !m_Decoding)
- amounttomove = m_OutputBufferPos;
- if (amounttomove > size) amounttomove = size;
- if (amounttomove)
- {
- memcpy(pBuffer, m_OutputBuffer, amounttomove);
- m_OutputBufferPos -= amounttomove;
- memmove(m_OutputBuffer, m_OutputBuffer + amounttomove, m_OutputBufferPos);
- *actualsize = amounttomove;
- }
+ int move;
+ if ((m_eof && !m_Decoding) || m_OutputBufferPos <= OUTPUTFRAMESIZE)
+ move = m_OutputBufferPos;
+ else
+ move = m_OutputBufferPos - OUTPUTFRAMESIZE;
+ move = std::min(move, size);
+
+ memcpy(pBuffer, m_OutputBuffer, move);
+ m_OutputBufferPos -= move;
+ memmove(m_OutputBuffer, m_OutputBuffer + move, m_OutputBufferPos);
+ *actualsize = move;
+
// only return READ_EOF when we've reached the end of the mp3 file, we've finished decoding, and our output buffer is depleated.
if (m_eof && !m_Decoding && !m_OutputBufferPos)
return READ_EOF;
+
return READ_SUCCESS;
}
@@ -381,9 +377,8 @@ bool MP3Codec::CanSeek()
return true;
}
-int MP3Codec::Decode(
- int *out_len // out_len is read and written to
-) {
+int MP3Codec::Decode(int *out_len)
+{
if (!m_HaveData)
{
if (!m_dll.IsLoaded())
@@ -407,9 +402,9 @@ int MP3Codec::Decode(
int skip;
skip = 2;
do
- {
- if (m_dll.mad_frame_decode(&mxhouse.frame, &mxhouse.stream) == 0)
- {
+ {
+ if (m_dll.mad_frame_decode(&mxhouse.frame, &mxhouse.stream) == 0)
+ {
if (--skip == 0)
m_dll.mad_synth_frame(&mxhouse.synth, &mxhouse.frame);
}
@@ -490,7 +485,7 @@ int MP3Codec::madx_init (madx_house *mxhouse )
return(1);
}
-madx_sig MP3Codec::madx_read(madx_house *mxhouse, madx_stat *mxstat, int maxwrite, bool discard)
+madx_sig MP3Codec::madx_read(madx_house *mxhouse, madx_stat *mxstat, int maxwrite)
{
if (!m_dll.IsLoaded())
m_dll.Load();
@@ -518,27 +513,25 @@ madx_sig MP3Codec::madx_read(madx_house *mxhouse, madx_stat *mxstat, int maxwrit
m_dll.mad_synth_frame( &mxhouse->synth, &mxhouse->frame );
- mxstat->framepcmsize = mxhouse->synth.pcm.length * mxhouse->synth.pcm.channels * (int)BITSPERSAMPLE/8;
+ mxstat->framepcmsize = mxhouse->synth.pcm.length * mxhouse->synth.pcm.channels * (int)(BITSPERSAMPLE >> 3);
mxhouse->frame_cnt++;
m_dll.mad_timer_add( &mxhouse->timer, mxhouse->frame.header.duration );
- float *data_f = (float *)mxhouse->output_ptr;
- if (!discard)
+
+ int32_t *dest = (int32_t*)mxhouse->output_ptr;
+ for(int i=0; i < mxhouse->synth.pcm.length; i++)
{
- for( int i=0; i < mxhouse->synth.pcm.length; i++ )
- {
- // Left channel
- *data_f++ = mad_scale_float(mxhouse->synth.pcm.samples[0][i]);
- mxhouse->output_ptr += sizeof(float);
- // Right channel
- if(MAD_NCHANNELS(&mxhouse->frame.header)==2)
- {
- *data_f++ = mad_scale_float(mxhouse->synth.pcm.samples[1][i]);
- mxhouse->output_ptr += sizeof(float);
- }
- }
- // Tell calling code buffer size
- mxstat->write_size = mxhouse->output_ptr - (m_OutputBuffer + m_OutputBufferPos);
+ // Left channel
+ *dest++ = (int32_t)(mxhouse->synth.pcm.samples[0][i] << 2);
+
+ // Right channel
+ if(MAD_NCHANNELS(&mxhouse->frame.header) == 2)
+ *dest++ = (int32_t)(mxhouse->synth.pcm.samples[1][i] << 2);
}
+
+ // Tell calling code buffer size
+ mxhouse->output_ptr = (unsigned char*)dest;
+ mxstat->write_size = mxhouse->output_ptr - (m_OutputBuffer + m_OutputBufferPos);
+
return(FLUSH_BUFFER);
}
@@ -550,3 +543,16 @@ void MP3Codec::madx_deinit( madx_house *mxhouse )
m_dll.mad_frame_finish(&mxhouse->frame);
m_dll.mad_stream_finish(&mxhouse->stream);
}
+
+CAEChannelInfo MP3Codec::GetChannelInfo()
+{
+ static enum AEChannel map[2][3] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR , AE_CH_NULL}
+ };
+
+ if (m_Channels > 2)
+ return CAEUtil::GuessChLayout(m_Channels);
+
+ return CAEChannelInfo(map[m_Channels - 1]);
+}
diff --git a/xbmc/cores/paplayer/MP3codec.h b/xbmc/cores/paplayer/MP3codec.h
index ad5ef6f5c4..0f7089ca69 100644
--- a/xbmc/cores/paplayer/MP3codec.h
+++ b/xbmc/cores/paplayer/MP3codec.h
@@ -61,17 +61,17 @@ public:
virtual bool CanSeek();
virtual int64_t Seek(int64_t iSeekTime);
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize);
- virtual int ReadSamples(float *pBuffer, int numsamples, int *actualsamples);
virtual bool CanInit();
virtual bool SkipNext();
- virtual bool HasFloatData() const { return m_BitsPerSampleInternal == 32; };
+ virtual CAEChannelInfo GetChannelInfo();
+
private:
/* TODO decoder functions */
virtual int Decode(int *out_len);
virtual void Flush();
int madx_init(madx_house* mxhouse);
- madx_sig madx_read(madx_house *mxhouse, madx_stat* mxstat, int maxwrite, bool discard = false);
+ madx_sig madx_read(madx_house *mxhouse, madx_stat* mxstat, int maxwrite);
void madx_deinit(madx_house* mxhouse);
/* END decoder functions */
@@ -118,8 +118,6 @@ private:
bool m_IgnoreLast; // Ignore first samples if this is true (for gapless playback)
int m_IgnoredBytes; // amount of samples ignored thus far
- int m_BitsPerSampleInternal;
-
DllLibMad m_dll;
};
diff --git a/xbmc/cores/paplayer/ModplugCodec.cpp b/xbmc/cores/paplayer/ModplugCodec.cpp
index 1ddd147ff4..b8420b598f 100644
--- a/xbmc/cores/paplayer/ModplugCodec.cpp
+++ b/xbmc/cores/paplayer/ModplugCodec.cpp
@@ -73,6 +73,7 @@ bool ModplugCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = 2;
m_SampleRate = 44100;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = (int64_t)(m_dll.ModPlug_GetLength(m_module));
return true;
diff --git a/xbmc/cores/paplayer/NSFCodec.cpp b/xbmc/cores/paplayer/NSFCodec.cpp
index 6816b2eaac..88e4c3b8c3 100644
--- a/xbmc/cores/paplayer/NSFCodec.cpp
+++ b/xbmc/cores/paplayer/NSFCodec.cpp
@@ -73,6 +73,7 @@ bool NSFCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = 1;
m_SampleRate = 48000;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = 4*60*1000; // fixme?
m_iDataPos = 0;
diff --git a/xbmc/cores/paplayer/OGGcodec.cpp b/xbmc/cores/paplayer/OGGcodec.cpp
index f08819ebde..120eb49ebb 100644
--- a/xbmc/cores/paplayer/OGGcodec.cpp
+++ b/xbmc/cores/paplayer/OGGcodec.cpp
@@ -32,6 +32,7 @@ OGGCodec::OGGCodec() : m_callback(m_file)
m_SampleRate = 0;
m_Channels = 0;
m_BitsPerSample = 0;
+ m_DataFormat = AE_FMT_INVALID;
m_Bitrate = 0;
m_CodecName = "OGG";
m_TimeOffset = 0.0;
@@ -115,6 +116,7 @@ bool OGGCodec::Init(const CStdString &strFile1, unsigned int filecache)
m_SampleRate = pInfo->rate;
m_Channels = pInfo->channels;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
if (item.IsInternetStream())
m_TotalTime = -1;
else
@@ -196,9 +198,6 @@ int OGGCodec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
else
*actualsize=lRead;
- if (m_Channels==6) // Only 6 channel files need remapping
- RemapChannels((short*)pBuffer, size/2); // size/2 = 16 bit samples
-
return READ_SUCCESS;
}
@@ -207,25 +206,21 @@ bool OGGCodec::CanInit()
return m_dll.CanLoad();
}
-// OGG order : L, C, R, L", R", LFE
-// Output order : L, R, L", R", C, LFE
-void OGGCodec::RemapChannels(short *SampleBuffer, int samples)
+CAEChannelInfo OGGCodec::GetChannelInfo()
{
- short r1, r2, r3, r4, r5, r6;
- for (int i = 0; i < samples; i += 6)
- {
- r1 = SampleBuffer[i];
- r2 = SampleBuffer[i+1];
- r3 = SampleBuffer[i+2];
- r4 = SampleBuffer[i+3];
- r5 = SampleBuffer[i+4];
- r6 = SampleBuffer[i+5];
- SampleBuffer[i] = r1;
- SampleBuffer[i+1] = r3;
- SampleBuffer[i+2] = r4;
- SampleBuffer[i+3] = r5;
- SampleBuffer[i+4] = r2;
- SampleBuffer[i+5] = r6;
- }
+ static enum AEChannel map[8][9] = {
+ {AE_CH_FC, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
+ {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL}
+ };
+
+ if (m_Channels > 8)
+ return CAEUtil::GuessChLayout(m_Channels);
+
+ return CAEChannelInfo(map[m_Channels - 1]);
}
-
diff --git a/xbmc/cores/paplayer/OGGcodec.h b/xbmc/cores/paplayer/OGGcodec.h
index 541a859608..5fa1ba312f 100644
--- a/xbmc/cores/paplayer/OGGcodec.h
+++ b/xbmc/cores/paplayer/OGGcodec.h
@@ -36,10 +36,10 @@ public:
virtual int64_t Seek(int64_t iSeekTime);
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize);
virtual bool CanInit();
+ virtual CAEChannelInfo GetChannelInfo();
private:
COggCallback m_callback;
- void RemapChannels(short *SampleBuffer, int samples);
DllVorbisfile m_dll;
OggVorbis_File m_VorbisFile;
diff --git a/xbmc/cores/paplayer/PAPlayer.cpp b/xbmc/cores/paplayer/PAPlayer.cpp
index 4e1c808f6f..1c86950ac6 100644
--- a/xbmc/cores/paplayer/PAPlayer.cpp
+++ b/xbmc/cores/paplayer/PAPlayer.cpp
@@ -19,1081 +19,793 @@
*
*/
-#include "threads/SystemClock.h"
#include "PAPlayer.h"
#include "CodecFactory.h"
#include "GUIInfoManager.h"
-#include "guilib/AudioContext.h"
#include "Application.h"
#include "FileItem.h"
#include "settings/AdvancedSettings.h"
#include "settings/GUISettings.h"
#include "settings/Settings.h"
#include "music/tags/MusicInfoTag.h"
-#include "../AudioRenderers/AudioRendererFactory.h"
#include "utils/TimeUtils.h"
#include "utils/log.h"
#include "utils/MathUtils.h"
-#ifdef _LINUX
-#define XBMC_SAMPLE_RATE 44100
-#else
-#define XBMC_SAMPLE_RATE 48000
-#endif
+#include "threads/SingleLock.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
-#define VOLUME_FFWD_MUTE 900 // 9dB
-
-#define FADE_TIME 2 * 2048.0f / XBMC_SAMPLE_RATE.0f // 2 packets
-
-#define TIME_TO_CACHE_NEXT_FILE 5000L // 5 seconds
-#define TIME_TO_CROSS_FADE 10000L // 10 seconds
+#define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
+#define FAST_XFADE_TIME 80 /* 80 milliseconds */
// PAP: Psycho-acoustic Audio Player
// Supporting all open audio codec standards.
// First one being nullsoft's nsv audio decoder format
-PAPlayer::PAPlayer(IPlayerCallback& callback) : IPlayer(callback), CThread("PAPlayer")
+PAPlayer::PAPlayer(IPlayerCallback& callback) :
+ IPlayer (callback),
+ CThread ("PAPlayer"),
+ m_signalSpeedChange(false),
+ m_playbackSpeed (1 ),
+ m_isPlaying (false),
+ m_isPaused (false),
+ m_isFinished (false),
+ m_currentStream (NULL ),
+ m_audioCallback (NULL )
{
- m_bIsPlaying = false;
- m_bPaused = false;
- m_cachingNextFile = false;
- m_currentlyCrossFading = false;
- m_bQueueFailed = false;
-
- m_currentDecoder = 0;
-
- m_iSpeed = 1;
- m_SeekTime=-1;
- m_IsFFwdRewding = false;
- m_timeOffset = 0;
-
- for (int i=0; i<2; i++)
- {
- m_channelCount[i] = 0;
- m_channelMap[i] = NULL;
- m_sampleRate[i] = 0;
- m_bitsPerSample[i] = 0;
-
- m_pAudioDecoder[i] = NULL;
- m_pcmBuffer[i] = NULL;
- m_bufferPos[i] = 0;
- m_Chunklen[i] = PACKET_SIZE;
- }
-
- m_currentStream = 0;
- m_packet[0][0].packet = NULL;
- m_packet[1][0].packet = NULL;
-
- m_bytesSentOut = 0;
- m_BytesPerSecond = 0;
-
- m_resampleAudio = false;
-
- m_visBufferLength = 0;
- m_pCallback = NULL;
-
- m_forceFadeToNext = false;
- m_CacheLevel = 0;
- m_LastCacheLevelCheck = 0;
-
- m_currentFile = new CFileItem;
- m_nextFile = new CFileItem;
}
PAPlayer::~PAPlayer()
{
- CloseFileInternal(true);
- delete m_currentFile;
- delete m_nextFile;
-}
+ if (!m_isPaused)
+ SoftStop(true, true);
+ CloseAllStreams(false);
+ /* wait for the thread to terminate */
+ StopThread(true);//true - wait for end of thread
+}
-void PAPlayer::OnExit()
+bool PAPlayer::HandlesType(const CStdString &type)
{
+ ICodec* codec = CodecFactory::CreateCodec(type);
+ if (codec && codec->CanInit())
+ {
+ delete codec;
+ return true;
+ }
+ return false;
}
-bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
+void PAPlayer::SoftStart(bool wait/* = false */)
{
- if (m_currentlyCrossFading) CloseFileInternal(false); //user seems to be in a hurry
+ CSharedLock lock(m_streamsLock);
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ StreamInfo* si = *itt;
+ if (si->m_fadeOutTriggered)
+ continue;
- m_crossFading = g_guiSettings.GetInt("musicplayer.crossfade");
- //WASAPI doesn't support multiple streams, no crossfading for cdda, cd-reading goes mad and no crossfading for last.fm doesn't like two connections
- if (file.IsCDDA() || file.IsLastFM() || g_guiSettings.GetString("audiooutput.audiodevice").find("wasapi:") != CStdString::npos) m_crossFading = 0;
- if (m_crossFading && IsPlaying())
+ si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
+ si->m_stream->Resume();
+ }
+
+ if (wait)
{
- //do a short crossfade on trackskip
- //set to max 2 seconds for these prev/next transitions
- if (m_crossFading > 2) m_crossFading = 2;
- //queue for crossfading
- bool result = QueueNextFile(file, false);
- if (result)
+ /* wait for them to fade in */
+ lock.Leave();
+ Sleep(FAST_XFADE_TIME);
+ lock.Enter();
+
+ /* be sure they have faded in */
+ while(wait)
{
- //crossfading value may be update by QueueNextFile when nr of channels changed
- if (!m_crossFading) // swap to next track
- m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
- else //force to fade to next track immediately
- m_forceFadeToNext = true;
+ wait = false;
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ StreamInfo* si = *itt;
+ if (si->m_stream->IsFading())
+ {
+ lock.Leave();
+ wait = true;
+ Sleep(1);
+ lock.Enter();
+ break;
+ }
+ }
}
- return result;
- }
-
- // normal opening of file, nothing playing or crossfading not enabled
- // however no need to return to gui audio device
- CloseFileInternal(false);
-
- // always open the file using the current decoder
- m_currentDecoder = 0;
-
- if (!m_decoder[m_currentDecoder].Create(file, (int64_t)(options.starttime * 1000), m_crossFading))
- return false;
-
- m_iSpeed = 1;
- m_bPaused = false;
- m_bStopPlaying = false;
- m_bytesSentOut = 0;
-
- CLog::Log(LOGINFO, "PAPlayer: Playing %s", file.GetPath().c_str());
-
- m_timeOffset = (int64_t)(options.starttime * 1000);
-
- unsigned int channel, sampleRate, bitsPerSample;
- m_decoder[m_currentDecoder].GetDataFormat(&channel, &sampleRate, &bitsPerSample);
-
- if (!CreateStream(m_currentStream, channel, sampleRate, bitsPerSample))
- {
- m_decoder[m_currentDecoder].Destroy();
- CLog::Log(LOGERROR, "PAPlayer::Unable to create audio stream");
}
-
- *m_currentFile = file;
-
- if (!IsRunning())
- Create();
-
- m_startEvent.Set();
-
- m_bIsPlaying = true;
- m_cachingNextFile = false;
- m_currentlyCrossFading = false;
- m_forceFadeToNext = false;
- m_bQueueFailed = false;
-
- m_decoder[m_currentDecoder].Start(); // start playback
-
- return true;
}
-void PAPlayer::UpdateCrossFadingTime(const CFileItem& file)
+void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
{
- if ((m_crossFading = g_guiSettings.GetInt("musicplayer.crossfade")))
+ /* fade all the streams out fast for a nice soft stop */
+ CSharedLock lock(m_streamsLock);
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
{
- if (
- m_crossFading &&
- (
- file.IsCDDA() ||
- file.IsLastFM() ||
- (
- file.HasMusicInfoTag() && !g_guiSettings.GetBool("musicplayer.crossfadealbumtracks") &&
- (m_currentFile->GetMusicInfoTag()->GetAlbum() != "") &&
- (m_currentFile->GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
- (m_currentFile->GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
- (m_currentFile->GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)
- )
- || g_guiSettings.GetString("audiooutput.audiodevice").find("wasapi:") != CStdString::npos
- )
- )
+ StreamInfo* si = *itt;
+ if (si->m_stream)
+ si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
+
+ if (close)
{
- m_crossFading = 0;
+ si->m_prepareTriggered = true;
+ si->m_playNextTriggered = true;
+ si->m_fadeOutTriggered = true;
}
}
-}
-void PAPlayer::OnNothingToQueueNotify()
-{
- //nothing to queue, stop playing
- m_bQueueFailed = true;
-}
+ /* if we are going to wait for them to finish fading */
+ if(wait)
+ {
+ /* wait for them to fade out */
+ lock.Leave();
+ Sleep(FAST_XFADE_TIME);
+ lock.Enter();
-bool PAPlayer::QueueNextFile(const CFileItem &file)
-{
- return QueueNextFile(file, true);
-}
+ /* be sure they have faded out */
+ while(wait)
+ {
+ wait = false;
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ StreamInfo* si = *itt;
+ if (si->m_stream && si->m_stream->IsFading())
+ {
+ lock.Leave();
+ wait = true;
+ Sleep(1);
+ lock.Enter();
+ break;
+ }
+ }
+ }
-bool PAPlayer::QueueNextFile(const CFileItem &file, bool checkCrossFading)
-{
- if (IsPaused())
- Pause();
-
- if (file.GetPath() == m_currentFile->GetPath() &&
- file.m_lStartOffset > 0 &&
- file.m_lStartOffset == m_currentFile->m_lEndOffset)
- { // continuing on a .cue sheet item - return true to say we'll handle the transistion
- *m_nextFile = file;
- return true;
+ /* if we are not closing the streams, pause them */
+ if (!close)
+ {
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ StreamInfo* si = *itt;
+ si->m_stream->Pause();
+ }
+ }
}
+}
- // check if we can handle this file at all
- int decoder = 1 - m_currentDecoder;
- int64_t seekOffset = (file.m_lStartOffset * 1000) / 75;
- if (!m_decoder[decoder].Create(file, seekOffset, m_crossFading))
+void PAPlayer::CloseAllStreams(bool fade/* = true */)
+{
+ if (!fade)
{
- m_bQueueFailed = true;
- return false;
- }
+ CExclusiveLock lock(m_streamsLock);
+ while(!m_streams.empty())
+ {
+ StreamInfo* si = m_streams.front();
+ m_streams.pop_front();
+
+ if (si->m_stream)
+ {
+ CAEFactory::AE->FreeStream(si->m_stream);
+ si->m_stream = NULL;
+ }
- // ok, we're good to go on queuing this one up
- CLog::Log(LOGINFO, "PAPlayer: Queuing next file %s", file.GetPath().c_str());
+ si->m_decoder.Destroy();
+ delete si;
+ }
- m_bQueueFailed = false;
- if (checkCrossFading)
- {
- UpdateCrossFadingTime(file);
- }
+ while(!m_finishing.empty())
+ {
+ StreamInfo* si = m_finishing.front();
+ m_finishing.pop_front();
- unsigned int channels, samplerate, bitspersample;
- m_decoder[decoder].GetDataFormat(&channels, &samplerate, &bitspersample);
+ if (si->m_stream)
+ {
+ CAEFactory::AE->FreeStream(si->m_stream);
+ si->m_stream = NULL;
+ }
- // check the number of channels isn't changing (else we can't do crossfading)
- if (m_crossFading && m_decoder[m_currentDecoder].GetChannels() == channels)
- { // crossfading - need to create a new stream
- if (!CreateStream(1 - m_currentStream, channels, samplerate, bitspersample))
- {
- m_decoder[decoder].Destroy();
- CLog::Log(LOGERROR, "PAPlayer::Unable to create audio stream");
+ si->m_decoder.Destroy();
+ delete si;
}
+ m_currentStream = NULL;
}
else
- { // no crossfading if nr of channels is not the same
- m_crossFading = 0;
- }
-
- *m_nextFile = file;
-
- return true;
+ {
+ SoftStop(false, true);
+ CExclusiveLock lock(m_streamsLock);
+ m_currentStream = NULL;
+ }
}
-
-
-bool PAPlayer::CloseFileInternal(bool bAudioDevice /*= true*/)
+bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
{
- if (IsPaused())
- Pause();
-
- m_bStopPlaying = true;
- m_bStop = true;
+ CloseAllStreams();
+ m_crossFadeTime = g_guiSettings.GetInt("musicplayer.crossfade") * 1000;
- m_visBufferLength = 0;
- StopThread();
-
- // kill both our streams if we need to
- for (int i = 0; i < 2; i++)
- {
- m_decoder[i].Destroy();
- if (bAudioDevice)
- FreeStream(i);
- }
-
- m_currentFile->Reset();
- m_nextFile->Reset();
+ if (!QueueNextFileEx(file, false))
+ return false;
- if(bAudioDevice)
- g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
- else
- FlushStreams();
+ if (!IsRunning())
+ Create();
+ /* trigger playback start */
+ m_isPlaying = true;
+ m_startEvent.Set();
return true;
}
-void PAPlayer::FreeStream(int stream)
+bool PAPlayer::QueueNextFile(const CFileItem &file)
{
- if (m_pAudioDecoder[stream])
- {
- DrainStream(stream);
-
- delete m_pAudioDecoder[stream];
- free(m_pcmBuffer[stream]);
- }
- m_pAudioDecoder[stream] = NULL;
- m_pcmBuffer[stream] = NULL;
-
- if (m_packet[stream][0].packet)
- free(m_packet[stream][0].packet);
-
- for (int i = 0; i < PACKET_COUNT; i++)
- {
- m_packet[stream][i].packet = NULL;
- }
-
- m_resampler[stream].DeInitialize();
+ return QueueNextFileEx(file);
}
-void PAPlayer::DrainStream(int stream)
+bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */)
{
- if(m_bStopPlaying || m_pAudioDecoder[1 - stream])
- {
- m_pAudioDecoder[stream]->Stop();
- return;
- }
-
- DWORD silence = m_pAudioDecoder[stream]->GetChunkLen() - m_bufferPos[stream] % m_pAudioDecoder[stream]->GetChunkLen();
+ StreamInfo *si = new StreamInfo();
- if(silence > 0 && m_bufferPos[stream] > 0)
+ if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75))
{
- CLog::Log(LOGDEBUG, "PAPlayer: Drain - adding %d bytes of silence, real pcmdata size: %d, chunk size: %d", silence, m_bufferPos[stream], m_pAudioDecoder[stream]->GetChunkLen());
- memset(m_pcmBuffer[stream] + m_bufferPos[stream], 0, silence);
- m_bufferPos[stream] += silence;
+ CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
+
+ delete si;
+ m_callback.OnQueueNextItem();
+ return false;
}
- DWORD added = 0;
- while(m_bufferPos[stream] - added >= m_pAudioDecoder[stream]->GetChunkLen())
+ /* decode until there is data-available */
+ si->m_decoder.Start();
+ while(si->m_decoder.GetDataSize() == 0)
{
- added += m_pAudioDecoder[stream]->AddPackets(m_pcmBuffer[stream] + added, m_bufferPos[stream] - added);
- Sleep(1);
- }
- m_bufferPos[stream] = 0;
+ int status = si->m_decoder.GetStatus();
+ if (status == STATUS_ENDED ||
+ status == STATUS_NO_FILE ||
+ si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
+ {
+ CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
- m_pAudioDecoder[stream]->WaitCompletion();
-}
+ si->m_decoder.Destroy();
+ delete si;
+ m_callback.OnQueueNextItem();
+ return false;
+ }
-bool PAPlayer::CreateStream(int num, unsigned int channels, unsigned int samplerate, unsigned int bitspersample, CStdString codec)
-{
- unsigned int outputSampleRate = (channels <= 2 && g_advancedSettings.m_musicResample) ? g_advancedSettings.m_musicResample : samplerate;
+ /* yield our time so that the main PAP thread doesnt stall */
+ CThread::Sleep(1);
+ }
+
+ /* init the streaminfo struct */
+ si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat);
+ si->m_startOffset = file.m_lStartOffset * 1000 / 75;
+ si->m_endOffset = file.m_lEndOffset * 1000 / 75;
+ si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3;
+ si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count();
+ si->m_started = false;
+ si->m_finishing = false;
+ si->m_framesSent = 0;
+ si->m_seekNextAtFrame = 0;
+ si->m_seekFrame = -1;
+ si->m_stream = NULL;
+ si->m_volume = (fadeIn && m_crossFadeTime) ? 0.0f : 1.0f;
+ si->m_fadeOutTriggered = false;
+ si->m_isSlaved = false;
+
+ if (si->m_decoder.TotalTime() < TIME_TO_CACHE_NEXT_FILE + m_crossFadeTime)
+ si->m_prepareNextAtFrame = 0;
+ else
+ si->m_prepareNextAtFrame = (int)((si->m_decoder.TotalTime() - TIME_TO_CACHE_NEXT_FILE - m_crossFadeTime) * si->m_sampleRate / 1000.0f);
+ si->m_prepareTriggered = false;
- if (m_pAudioDecoder[num] != NULL && m_channelCount[num] == channels && m_sampleRate[num] == outputSampleRate /* && m_bitsPerSample[num] == bitspersample */)
- {
- CLog::Log(LOGDEBUG, "PAPlayer: Using existing audio renderer");
- }
+ if (si->m_decoder.TotalTime() < m_crossFadeTime)
+ si->m_playNextAtFrame = (int)((si->m_decoder.TotalTime() / 2) * si->m_sampleRate / 1000.0f);
else
- {
- FreeStream(num);
- CLog::Log(LOGDEBUG, "PAPlayer: Creating new audio renderer");
- m_bitsPerSample[num] = 16;
- m_sampleRate[num] = outputSampleRate;
- m_channelCount[num] = channels;
- m_channelMap[num] = NULL;
- m_BytesPerSecond = (m_bitsPerSample[num] / 8)* outputSampleRate * channels;
-
- /* Open the device */
- m_pAudioDecoder[num] = CAudioRendererFactory::Create(
- m_pCallback , //pCallback
- m_channelCount [num], //iChannels
- m_channelMap [num], //channelMap
- m_sampleRate [num], //uiSamplesPerSec
- m_bitsPerSample[num], //uiBitsPerSample
- false , //bResample
- true , //bIsMusic
- IAudioRenderer::ENCODED_NONE //bPassthrough
- );
-
- if (!m_pAudioDecoder[num]) return false;
-
- m_pcmBuffer[num] = (unsigned char*)malloc((m_pAudioDecoder[num]->GetChunkLen() + PACKET_SIZE));
- m_bufferPos[num] = 0;
- m_latency[num] = m_pAudioDecoder[num]->GetDelay();
- m_Chunklen[num] = std::max(PACKET_SIZE, (int)m_pAudioDecoder[num]->GetChunkLen());
- m_packet[num][0].packet = (BYTE*)malloc(PACKET_SIZE * PACKET_COUNT);
- for (int i = 1; i < PACKET_COUNT ; i++)
- m_packet[num][i].packet = m_packet[num][i - 1].packet + PACKET_SIZE;
- }
-
- // set initial volume
- SetStreamVolume(num, g_settings.m_nVolumeLevel);
+ si->m_playNextAtFrame = (int)((si->m_decoder.TotalTime() - m_crossFadeTime) * si->m_sampleRate / 1000.0f);
+ si->m_playNextTriggered = false;
- m_resampler[num].InitConverter(samplerate, bitspersample, channels, outputSampleRate, m_bitsPerSample[num], PACKET_SIZE);
+ PrepareStream(si);
- // TODO: How do we best handle the callback, given that our samplerate etc. may be
- // changing at this point?
+ /* add the stream to the list */
+ CExclusiveLock lock(m_streamsLock);
+ m_streams.push_back(si);
- // fire off our init to our callback
- if (m_pCallback)
- m_pCallback->OnInitialize(channels, outputSampleRate, m_bitsPerSample[num]);
return true;
}
-void PAPlayer::Pause()
+inline bool PAPlayer::PrepareStream(StreamInfo *si)
{
- CLog::Log(LOGDEBUG,"PAPlayer: pause m_bplaying: %d", m_bIsPlaying);
- if (!m_bIsPlaying || !m_pAudioDecoder)
- return ;
+ /* if we have a stream we are already prepared */
+ if (si->m_stream)
+ return true;
- m_bPaused = !m_bPaused;
+ /* get a paused stream */
+ si->m_stream = CAEFactory::AE->MakeStream(
+ si->m_dataFormat,
+ si->m_sampleRate,
+ si->m_encodedSampleRate,
+ si->m_channelInfo,
+ AESTREAM_PAUSED
+ );
- if (m_bPaused)
+ if (!si->m_stream)
{
- if (m_pAudioDecoder[m_currentStream])
- m_pAudioDecoder[m_currentStream]->Pause();
+ CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
+ return false;
+ }
- if (m_currentlyCrossFading && m_pAudioDecoder[1 - m_currentStream])
- m_pAudioDecoder[1 - m_currentStream]->Pause();
+ si->m_stream->SetVolume (si->m_volume);
+ si->m_stream->SetReplayGain(si->m_decoder.GetReplayGain());
- m_callback.OnPlayBackPaused();
- CLog::Log(LOGDEBUG, "PAPlayer: Playback paused");
+ /* if its not the first stream and crossfade is not enabled */
+ if (m_currentStream && m_currentStream != si && !m_crossFadeTime)
+ {
+ /* slave the stream for gapless */
+ si->m_isSlaved = true;
+ m_currentStream->m_stream->RegisterSlave(si->m_stream);
}
- else
+
+ /* fill the stream's buffer */
+ while(si->m_stream->IsBuffering())
{
- if (m_pAudioDecoder[m_currentStream])
- m_pAudioDecoder[m_currentStream]->Resume();
+ int status = si->m_decoder.GetStatus();
+ if (status == STATUS_ENDED ||
+ status == STATUS_NO_FILE ||
+ si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
+ {
+ CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
+ break;
+ }
- if (m_currentlyCrossFading && m_pAudioDecoder[1 - m_currentStream])
- m_pAudioDecoder[1 - m_currentStream]->Resume();
+ if (!QueueData(si))
+ break;
- m_callback.OnPlayBackResumed();
- CLog::Log(LOGDEBUG, "PAPlayer: Playback resumed");
+ /* yield our time so that the main PAP thread doesnt stall */
+ CThread::Sleep(1);
}
-}
-void PAPlayer::SetVolume(long nVolume)
-{
- if (m_pAudioDecoder[m_currentStream])
- m_pAudioDecoder[m_currentStream]->SetCurrentVolume(nVolume);
+ CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
+
+ return true;
}
-void PAPlayer::SetDynamicRangeCompression(long drc)
+bool PAPlayer::CloseFile()
{
- // TODO: Add volume amplification
- CLog::Log(LOGDEBUG,"PAPlayer::SetDynamicRangeCompression - drc: %lu", drc);
+ m_callback.OnPlayBackStopped();
+ return true;
}
void PAPlayer::Process()
{
- CLog::Log(LOGDEBUG, "PAPlayer: Thread started");
- if (m_startEvent.WaitMSec(100))
+ if (!m_startEvent.WaitMSec(100))
{
- m_startEvent.Reset();
+ CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
+ return;
+ }
- do
+ CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
+ while(m_isPlaying && !m_bStop)
+ {
+ /* this needs to happen outside of any locks to prevent deadlocks */
+ if (m_signalSpeedChange)
{
- if (!m_bPaused)
- {
- if (!ProcessPAP())
- break;
- }
- else
- {
- Sleep(100);
- }
+ m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
+ m_signalSpeedChange = false;
}
- while (!m_bStopPlaying && m_bIsPlaying && !m_bStop);
- CLog::Log(LOGINFO, "PAPlayer: End of playback reached");
- m_bIsPlaying = false;
- if (!m_bStopPlaying && !m_bStop)
- m_callback.OnPlayBackEnded();
- else
- m_callback.OnPlayBackStopped();
- }
- CLog::Log(LOGDEBUG, "PAPlayer: Thread end");
-}
+ double delay = 100.0;
+ double buffer = 100.0;
+ ProcessStreams(delay, buffer);
-void PAPlayer::ToFFRW(int iSpeed)
-{
- m_iSpeed = iSpeed;
- m_callback.OnPlayBackSpeedChanged(iSpeed);
+ if (delay < buffer && delay > 0.75 * buffer)
+ CThread::Sleep(MathUtils::round_int((buffer - delay) * 1000.0));
+ }
}
-void PAPlayer::UpdateCacheLevel()
+inline void PAPlayer::ProcessStreams(double &delay, double &buffer)
{
- //check cachelevel every .5 seconds
- if ((XbmcThreads::SystemClockMillis() - m_LastCacheLevelCheck) > 500)
+ CSharedLock sharedLock(m_streamsLock);
+ if (m_isFinished && m_streams.empty() && m_finishing.empty())
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- {
- m_CacheLevel = codec->GetCacheLevel();
- m_LastCacheLevelCheck = XbmcThreads::SystemClockMillis();
- //CLog::Log(LOGDEBUG,"Cachelevel: %i%%", m_CacheLevel);
- }
+ m_isPlaying = false;
+ delay = 0;
+ m_callback.OnPlayBackEnded();
+ return;
}
-}
-bool PAPlayer::ProcessPAP()
-{
- /*
- * Here's what we should be doing in each player loop:
- *
- * 1. Run DoWork() on our audio device to actually output audio.
- *
- * 2. Pass our current buffer to the audio device to see if it wants anything,
- * and if so, reduce our buffer size accordingly.
- *
- * 3. Check whether we have space in our buffer for more data, and if so,
- * read some more in.
- *
- * 4. Check for end of file and return false if we reach it.
- *
- * 5. Perform any seeking and ffwd/rewding as necessary.
- *
- * 6. If we don't do anything in 2...5, we can take a breather and break out for sleeping.
- */
- while (true)
+ /* destroy any drained streams */
+ for(StreamList::iterator itt = m_finishing.begin(); itt != m_finishing.end();)
{
- if (m_bStop) return false;
-
- // Check for .cue sheet item end
- if (m_currentFile->m_lEndOffset && GetTime() >= GetTotalTime64())
- {
- CLog::Log(LOGINFO, "PAPlayer: Passed end of track in a .cue sheet item");
- m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
+ StreamInfo* si = *itt;
+ if (si->m_stream->IsDrained())
+ {
+ itt = m_finishing.erase(itt);
+ CAEFactory::AE->FreeStream(si->m_stream);
+ delete si;
+ CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
}
+ else
+ ++itt;
+ }
- // check whether we need to send off our callbacks etc.
- int status = m_decoder[m_currentDecoder].GetStatus();
- if (status == STATUS_NO_FILE)
- return false;
-
- UpdateCacheLevel();
-
- // check whether we should queue the next file up
- if ((GetTotalTime64() > 0) && GetTotalTime64() - GetTime() < TIME_TO_CACHE_NEXT_FILE + m_crossFading * 1000L && !m_cachingNextFile)
- { // request the next file from our application
- m_callback.OnQueueNextItem();
- m_cachingNextFile = true;
- }
+ sharedLock.Leave();
+ CExclusiveLock lock(m_streamsLock);
- if (m_crossFading && m_decoder[0].GetChannels() == m_decoder[1].GetChannels())
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ {
+ StreamInfo* si = *itt;
+ if (!m_currentStream && !si->m_started)
+ m_currentStream = si;
+ /* if the stream is finishing */
+ if ((si->m_fadeOutTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, delay, buffer))
{
- if (((GetTotalTime64() - GetTime() < m_crossFading * 1000L) || (m_forceFadeToNext)) && !m_currentlyCrossFading)
- { // request the next file from our application
- if (m_decoder[1 - m_currentDecoder].GetStatus() == STATUS_QUEUED && m_pAudioDecoder[1 - m_currentStream])
- {
- m_currentlyCrossFading = true;
- if (m_forceFadeToNext)
- {
- m_forceFadeToNext = false;
- m_crossFadeLength = m_crossFading * 1000L;
- }
- else
- {
- m_crossFadeLength = GetTotalTime64() - GetTime();
- }
- m_currentDecoder = 1 - m_currentDecoder;
- m_decoder[m_currentDecoder].Start();
- m_currentStream = 1 - m_currentStream;
- CLog::Log(LOGDEBUG, "Starting Crossfade - resuming stream %i", m_currentStream);
-
- m_pAudioDecoder[m_currentStream]->Resume();
-
- m_callback.OnPlayBackStarted();
- m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
- m_bytesSentOut = 0;
- *m_currentFile = *m_nextFile;
- m_nextFile->Reset();
- m_cachingNextFile = false;
- }
+ if (!si->m_prepareTriggered)
+ {
+ si->m_prepareTriggered = true;
+ m_callback.OnQueueNextItem();
}
- }
- // Check for EOF and queue the next track if applicable
- if (m_decoder[m_currentDecoder].GetStatus() == STATUS_ENDED)
- { // time to swap tracks
- if (m_nextFile->GetPath() != m_currentFile->GetPath() ||
- !m_nextFile->m_lStartOffset ||
- m_nextFile->m_lStartOffset != m_currentFile->m_lEndOffset)
- { // don't have a .cue sheet item
- int nextstatus = m_decoder[1 - m_currentDecoder].GetStatus();
- if (nextstatus == STATUS_QUEUED || nextstatus == STATUS_QUEUING || nextstatus == STATUS_PLAYING)
- { // swap streams
- CLog::Log(LOGDEBUG, "PAPlayer: Swapping tracks %i to %i", m_currentDecoder, 1-m_currentDecoder);
- if (!m_crossFading || m_decoder[0].GetChannels() != m_decoder[1].GetChannels())
- { // playing gapless (we use only the 1 output stream in this case)
- int prefixAmount = m_decoder[m_currentDecoder].GetDataSize();
- CLog::Log(LOGDEBUG, "PAPlayer::Prefixing %i samples of old data to new track for gapless playback", prefixAmount);
- m_decoder[1 - m_currentDecoder].PrefixData(m_decoder[m_currentDecoder].GetData(prefixAmount), prefixAmount);
- // check if we need to change the resampler (due to format change)
- unsigned int channels, samplerate, bitspersample;
- m_decoder[m_currentDecoder].GetDataFormat(&channels, &samplerate, &bitspersample);
- unsigned int channels2, samplerate2, bitspersample2;
- m_decoder[1 - m_currentDecoder].GetDataFormat(&channels2, &samplerate2, &bitspersample2);
- // change of channels - reinitialize our speaker configuration
- if (channels != channels2 || (g_advancedSettings.m_musicResample == 0 && (samplerate != samplerate2 || bitspersample != bitspersample2)))
- {
- CLog::Log(LOGINFO, "PAPlayer: Stream properties have changed, restarting stream");
- FreeStream(m_currentStream);
- if (!CreateStream(m_currentStream, channels2, samplerate2, bitspersample2))
- {
- CLog::Log(LOGERROR, "PAPlayer: Error creating stream!");
- return false;
- }
- m_pAudioDecoder[m_currentStream]->Resume();
- }
- else if (samplerate != samplerate2 || bitspersample != bitspersample2)
- {
- CLog::Log(LOGINFO, "PAPlayer: Restarting resampler due to a change in data format");
- m_resampler[m_currentStream].DeInitialize();
- if (!m_resampler[m_currentStream].InitConverter(samplerate2, bitspersample2, channels2, g_advancedSettings.m_musicResample, 16, PACKET_SIZE))
- {
- CLog::Log(LOGERROR, "PAPlayer: Error initializing resampler!");
- return false;
- }
- }
- CLog::Log(LOGINFO, "PAPlayer: Starting new track");
-
- m_decoder[m_currentDecoder].Destroy();
- m_decoder[1 - m_currentDecoder].Start();
- m_callback.OnPlayBackStarted();
- m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
- m_bytesSentOut = 0;
- *m_currentFile = *m_nextFile;
- m_nextFile->Reset();
- m_cachingNextFile = false;
- m_currentDecoder = 1 - m_currentDecoder;
- }
- else
- { // cross fading - shouldn't ever get here - if we do, return false
- if (!m_currentlyCrossFading)
- {
- CLog::Log(LOGERROR, "End of file Reached before crossfading kicked in!");
- return false;
- }
- else
- {
- CLog::Log(LOGINFO, "End of file reached before crossfading finished!");
- return false;
- }
+ /* remove the stream */
+ itt = m_streams.erase(itt);
+ /* if its the current stream */
+ if (si == m_currentStream)
+ {
+ /* if it was the last stream */
+ if (itt == m_streams.end())
+ {
+ /* if it didnt trigger the next queue item */
+ if (!si->m_prepareTriggered)
+ {
+ m_callback.OnQueueNextItem();
+ si->m_prepareTriggered = true;
}
+ m_currentStream = NULL;
}
else
{
- if (GetTotalTime64() <= 0 && !m_bQueueFailed)
- { //we did not know the duration so didn't queue the next song, try queueing it now
- if (!m_cachingNextFile)
- {// request the next file from our application
- m_callback.OnQueueNextItem();
- m_cachingNextFile = true;
- }
- }
- else
- {
- // no track queued - return and get another one once we are finished
- // with the current stream
- WaitForStream();
- return false;
- }
+ m_currentStream = *itt;
}
}
- else
- {
- // set the next track playing (.cue sheet)
- m_decoder[m_currentDecoder].SetStatus(STATUS_PLAYING);
- m_callback.OnPlayBackStarted();
- m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
- m_bytesSentOut = 0;
- *m_currentFile = *m_nextFile;
- m_nextFile->Reset();
- m_cachingNextFile = false;
- }
+
+ /* unregister the audio callback */
+ si->m_stream->UnRegisterAudioCallback();
+ si->m_decoder.Destroy();
+ si->m_stream->Drain();
+ m_finishing.push_back(si);
+ return;
}
- // handle seeking and ffwd/rewding.
- HandleSeeking();
- if (!HandleFFwdRewd())
+ if (!si->m_started)
+ continue;
+
+ /* is it time to prepare the next stream? */
+ if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
{
- // need to skip to the next track - let's see if we already have another one
- m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
- continue; // loop around to start the next track
+ si->m_prepareTriggered = true;
+ m_callback.OnQueueNextItem();
}
- if (!m_bPaused)
+ /* it is time to start playing the next stream? */
+ if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && si->m_framesSent >= si->m_playNextAtFrame)
{
-
- // Let our decoding stream(s) do their thing
- int retVal = m_decoder[m_currentDecoder].ReadSamples(PACKET_SIZE);
- if (retVal == RET_ERROR)
+ if (!si->m_prepareTriggered)
{
- m_decoder[m_currentDecoder].Destroy();
- return false;
+ si->m_prepareTriggered = true;
+ m_callback.OnQueueNextItem();
}
- int retVal2 = m_decoder[1 - m_currentDecoder].ReadSamples(PACKET_SIZE);
- if (retVal2 == RET_ERROR)
+ if (!m_isFinished)
{
- m_decoder[1 - m_currentDecoder].Destroy();
- }
+ if (m_crossFadeTime)
+ si->m_stream->FadeVolume(1.0f, 0.0f, m_crossFadeTime);
+ m_currentStream = NULL;
- // if we're cross-fading, then we do this for both streams, otherwise
- // we do it just for the one stream.
- if (m_currentlyCrossFading)
- {
- if (GetTime() >= m_crossFadeLength) // finished
- {
- CLog::Log(LOGDEBUG, "Finished Crossfading");
- m_currentlyCrossFading = false;
- SetStreamVolume(m_currentStream, g_settings.m_nVolumeLevel);
- FreeStream(1 - m_currentStream);
- m_decoder[1 - m_currentDecoder].Destroy();
- }
- else
- {
- float fraction = (float)(m_crossFadeLength - GetTime()) / (float)m_crossFadeLength - 0.5f;
- // make sure we can take valid logs.
- if (fraction > 0.499f) fraction = 0.499f;
- if (fraction < -0.499f) fraction = -0.499f;
- float volumeCurrent = 2000.0f * log10(0.5f - fraction);
- float volumeNext = 2000.0f * log10(0.5f + fraction);
- SetStreamVolume(m_currentStream, g_settings.m_nVolumeLevel + (int)volumeCurrent);
- SetStreamVolume(1 - m_currentStream, g_settings.m_nVolumeLevel + (int)volumeNext);
- if (AddPacketsToStream(1 - m_currentStream, m_decoder[1 - m_currentDecoder]))
- retVal2 = RET_SUCCESS;
- }
+ /* unregister the audio callback */
+ si->m_stream->UnRegisterAudioCallback();
}
- // add packets as necessary
- if (AddPacketsToStream(m_currentStream, m_decoder[m_currentDecoder]))
- retVal = RET_SUCCESS;
+ si->m_playNextTriggered = true;
+ }
+ }
+}
- if (retVal == RET_SLEEP && retVal2 == RET_SLEEP)
- {
- float maximumSleepTime = m_pAudioDecoder[m_currentStream]->GetCacheTime();
-
- if (m_pAudioDecoder[1 - m_currentStream])
- maximumSleepTime = std::min(maximumSleepTime, m_pAudioDecoder[1 - m_currentStream]->GetCacheTime());
+inline bool PAPlayer::ProcessStream(StreamInfo *si, double &delay, double &buffer)
+{
+ /* if playback needs to start on this stream, do it */
+ if (si == m_currentStream && !si->m_started)
+ {
+ si->m_started = true;
+ si->m_stream->RegisterAudioCallback(m_audioCallback);
+ if (!si->m_isSlaved)
+ si->m_stream->Resume();
+ si->m_stream->FadeVolume(0.0f, 1.0f, m_crossFadeTime);
+ m_callback.OnPlayBackStarted();
+ }
- int sleep = std::max((int)((maximumSleepTime / 2.0f) * 1000.0f), 1);
+ /* if we have not started yet and the stream has been primed */
+ unsigned int space = si->m_stream->GetSpace();
+ if (!si->m_started && !space)
+ return true;
- Sleep(std::min(sleep, 15));
- }
+ /* see if it is time yet to FF/RW or a direct seek */
+ if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
+ {
+ /* if its a direct seek */
+ if (si->m_seekFrame > -1)
+ {
+ si->m_framesSent = si->m_seekFrame;
+ si->m_seekFrame = -1;
}
+ /* if its FF/RW */
else
- Sleep(100);
+ {
+ si->m_framesSent += si->m_sampleRate * (m_playbackSpeed - 1);
+ si->m_seekNextAtFrame = si->m_framesSent + si->m_sampleRate / 2;
+ }
+
+ int64_t time = (int64_t)(si->m_startOffset + ((float)si->m_framesSent / (float)si->m_sampleRate * 1000.0f));
+
+ /* if we are seeking back before the start of the track start normal playback */
+ if (time < si->m_startOffset || si->m_framesSent < 0)
+ {
+ time = si->m_startOffset;
+ si->m_framesSent = 0;
+ si->m_seekNextAtFrame = 0;
+ ToFFRW(1);
+ }
+
+ si->m_decoder.Seek(time);
}
- return true;
-}
-int64_t PAPlayer::GetTime()
-{
- int64_t timeplus = m_BytesPerSecond ? (int64_t)(((float) m_bytesSentOut / (float) m_BytesPerSecond ) * 1000.0) : 0;
- return m_timeOffset + timeplus - m_currentFile->m_lStartOffset * 1000 / 75;
-}
+ int status = si->m_decoder.GetStatus();
+ if (status == STATUS_ENDED ||
+ status == STATUS_NO_FILE ||
+ si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
+ {
+ CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
+ return false;
+ }
-int64_t PAPlayer::GetTotalTime64()
-{
- int64_t total = m_decoder[m_currentDecoder].TotalTime();
- if (m_currentFile->m_lEndOffset)
- total = m_currentFile->m_lEndOffset * 1000 / 75;
- if (m_currentFile->m_lStartOffset)
- total -= m_currentFile->m_lStartOffset * 1000 / 75;
- return total;
-}
+ if (!QueueData(si))
+ return false;
-int PAPlayer::GetTotalTime()
-{
- return (int)(GetTotalTime64()/1000);
+ /* update the delay time if we are running */
+ if (si->m_started)
+ {
+ if (si->m_stream->IsBuffering())
+ delay = 0.0;
+ else
+ delay = std::min(delay , si->m_stream->GetDelay());
+ buffer = std::min(buffer, si->m_stream->GetCacheTotal());
+ }
+
+ return true;
}
-int PAPlayer::GetCacheLevel() const
+bool PAPlayer::QueueData(StreamInfo *si)
{
- const ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return codec->GetCacheLevel();
+ unsigned int space = si->m_stream->GetSpace();
+ unsigned int samples = std::min(si->m_decoder.GetDataSize(), space / si->m_bytesPerSample);
+ if (!samples)
+ return true;
- return -1;
+ void* data = si->m_decoder.GetData(samples);
+ if (!data)
+ {
+ CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
+ return false;
+ }
+
+ unsigned int added = si->m_stream->AddData(data, samples * si->m_bytesPerSample);
+ si->m_framesSent += added / si->m_bytesPerFrame;
+
+ return true;
}
-int PAPlayer::GetChannels()
+void PAPlayer::OnExit()
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return codec->m_Channels;
- return 0;
+
}
-int PAPlayer::GetBitsPerSample()
+void PAPlayer::RegisterAudioCallback(IAudioCallback* pCallback)
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return codec->m_BitsPerSample;
- return 0;
+ CSharedLock lock(m_streamsLock);
+ m_audioCallback = pCallback;
+ if (m_currentStream && m_currentStream->m_stream)
+ m_currentStream->m_stream->RegisterAudioCallback(pCallback);
}
-int PAPlayer::GetSampleRate()
+void PAPlayer::UnRegisterAudioCallback()
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return (int)((codec->m_SampleRate / 1000) + 0.5);
- return 0;
+ CSharedLock lock(m_streamsLock);
+ /* only one stream should have the callback, but we do it to all just incase */
+ for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
+ if ((*itt)->m_stream)
+ (*itt)->m_stream->UnRegisterAudioCallback();
+ m_audioCallback = NULL;
}
-CStdString PAPlayer::GetAudioCodecName()
+void PAPlayer::OnNothingToQueueNotify()
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return codec->m_CodecName;
- return "";
+ m_isFinished = true;
}
-int PAPlayer::GetAudioBitrate()
+bool PAPlayer::IsPlaying() const
{
- ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
- if (codec)
- return codec->m_Bitrate;
- return 0;
+ return m_isPlaying;
}
-bool PAPlayer::CanSeek()
+bool PAPlayer::IsPaused() const
{
- return ((m_decoder[m_currentDecoder].TotalTime() > 0) && m_decoder[m_currentDecoder].CanSeek());
+ return m_isPaused;
}
-void PAPlayer::Seek(bool bPlus, bool bLargeStep)
+void PAPlayer::Pause()
{
- int64_t seek;
- if (g_advancedSettings.m_musicUseTimeSeeking && GetTotalTime() > 2*g_advancedSettings.m_musicTimeSeekForwardBig)
+ if (m_isPaused)
{
- if (bLargeStep)
- seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig;
- else
- seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward;
- seek *= 1000;
- seek += GetTime();
+ m_isPaused = false;
+ SoftStart();
}
else
{
- float percent;
- if (bLargeStep)
- percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig;
- else
- percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward;
- seek = (int64_t)(GetTotalTime64()*(GetPercentage()+percent)/100);
+ m_isPaused = true;
+ SoftStop(true, false);
}
-
- SeekTime(seek);
}
-void PAPlayer::SeekTime(int64_t iTime /*=0*/)
+void PAPlayer::SetVolume(float volume)
{
- if (!CanSeek()) return;
- int seekOffset = (int)(iTime - GetTime());
- if (m_currentFile->m_lStartOffset)
- iTime += m_currentFile->m_lStartOffset * 1000 / 75;
- m_SeekTime = iTime;
- m_callback.OnPlayBackSeek((int)m_SeekTime, seekOffset);
- CLog::Log(LOGDEBUG, "PAPlayer::Seeking to time %f", 0.001f * m_SeekTime);
-}
-void PAPlayer::SeekPercentage(float fPercent /*=0*/)
-{
- if (fPercent < 0.0f) fPercent = 0.0f;
- if (fPercent > 100.0f) fPercent = 100.0f;
- SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
}
-float PAPlayer::GetPercentage()
+void PAPlayer::SetDynamicRangeCompression(long drc)
{
- float percent = (float)GetTime() * 100.0f / GetTotalTime64();
- return percent;
+
}
-void PAPlayer::HandleSeeking()
+void PAPlayer::ToFFRW(int iSpeed)
{
- if (m_SeekTime != -1)
- {
- unsigned int time = XbmcThreads::SystemClockMillis();
- m_timeOffset = m_decoder[m_currentDecoder].Seek(m_SeekTime);
- CLog::Log(LOGDEBUG, "Seek to time %f took %i ms", 0.001f * m_SeekTime, (int)(XbmcThreads::SystemClockMillis() - time));
- FlushStreams();
- m_SeekTime = -1;
- }
- g_infoManager.m_performingSeek = false;
+ m_playbackSpeed = iSpeed;
+ m_signalSpeedChange = true;
}
-void PAPlayer::FlushStreams()
+int64_t PAPlayer::GetTime()
{
- m_bytesSentOut = 0;
- for (int stream = 0; stream < 2; stream++)
- {
- if (m_pAudioDecoder[stream] && m_packet[stream])
- {
- m_pAudioDecoder[stream]->Stop();
- m_pAudioDecoder[stream]->Resume();
- m_bufferPos[stream] = 0;
- }
- }
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
+
+ double time = (double)m_currentStream->m_framesSent / (double)m_currentStream->m_sampleRate;
+ if (m_currentStream->m_stream)
+ time -= m_currentStream->m_stream->GetDelay();
+
+ return (int64_t)(time * 1000.0);
}
-bool PAPlayer::HandleFFwdRewd()
+int64_t PAPlayer::GetTotalTime64()
{
- if (!m_IsFFwdRewding && m_iSpeed == 1)
- return true; // nothing to do
- if (m_IsFFwdRewding && m_iSpeed == 1)
- { // stop ffwd/rewd
- m_IsFFwdRewding = false;
- SetVolume(g_settings.m_nVolumeLevel);
- FlushStreams();
- return true;
- }
- // we're definitely fastforwarding or rewinding
- int snippet = m_BytesPerSecond / 2;
- if ( m_bytesSentOut >= snippet )
- {
- // Calculate offset to seek if we do FF/RW
- int64_t time = GetTime();
- if (m_IsFFwdRewding) snippet = (int)m_bytesSentOut;
- time += (int64_t)((double)snippet * (m_iSpeed - 1.0) / m_BytesPerSecond * 1000.0);
-
- // Is our offset inside the track range?
- if (time >= 0 && time <= m_decoder[m_currentDecoder].TotalTime())
- { // just set next position to read
- m_IsFFwdRewding = true;
- time += m_currentFile->m_lStartOffset * 1000 / 75;
- m_timeOffset = m_decoder[m_currentDecoder].Seek(time);
- FlushStreams();
- SetVolume(g_settings.m_nVolumeLevel - VOLUME_FFWD_MUTE); // override xbmc mute
- }
- else if (time < 0)
- { // ...disable seeking and start the track again
- time = m_currentFile->m_lStartOffset * 1000 / 75;
- m_timeOffset = m_decoder[m_currentDecoder].Seek(time);
- FlushStreams();
- m_iSpeed = 1;
- SetVolume(g_settings.m_nVolumeLevel); // override xbmc mute
- } // is our next position greater then the end sector...
- else //if (time > m_codec->m_TotalTime)
- {
- // restore volume level so the next track isn't muted
- SetVolume(g_settings.m_nVolumeLevel);
- CLog::Log(LOGDEBUG, "PAPlayer: End of track reached while seeking");
- return false;
- }
- }
- return true;
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
+
+ int64_t total = m_currentStream->m_decoder.TotalTime();
+ if (m_currentStream->m_endOffset)
+ total = m_currentStream->m_endOffset;
+ total -= m_currentStream->m_startOffset;
+ return total;
}
-void PAPlayer::SetStreamVolume(int stream, long nVolume)
+int PAPlayer::GetTotalTime()
{
- m_pAudioDecoder[stream]->SetCurrentVolume(nVolume);
+ return (int)(GetTotalTime64() / 1000);
}
-bool PAPlayer::AddPacketsToStream(int stream, CAudioDecoder &dec)
+int PAPlayer::GetCacheLevel() const
{
- if (!m_pAudioDecoder[stream] || dec.GetStatus() == STATUS_NO_FILE)
- return false;
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return -1;
- bool ret = false;
- int amount = m_resampler[stream].GetInputSamples();
- if (amount > 0 && amount <= (int)dec.GetDataSize())
- { // resampler wants more data - let's feed it
- m_resampler[stream].PutFloatData((float *)dec.GetData(amount), amount);
- ret = true;
- }
- else if (m_resampler[stream].GetData(m_packet[stream][0].packet))
- {
- // got some data from our resampler - construct audio packet
- m_packet[stream][0].length = PACKET_SIZE;
- m_packet[stream][0].stream = stream;
+ const ICodec* codec = m_currentStream->m_decoder.GetCodec();
+ if (codec)
+ return codec->GetCacheLevel();
+ return -1;
+}
- unsigned char *pcmPtr = m_packet[stream][0].packet;
- int len = m_packet[stream][0].length;
- StreamCallback(&m_packet[stream][0]);
+int PAPlayer::GetChannels()
+{
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
- memcpy(m_pcmBuffer[stream]+m_bufferPos[stream], pcmPtr, len);
- m_bufferPos[stream] += len;
+ return m_currentStream->m_channelInfo.Count();
+}
- while (m_bufferPos[stream] >= (int)m_pAudioDecoder[stream]->GetChunkLen())
- {
- int rtn = m_pAudioDecoder[stream]->AddPackets(m_pcmBuffer[stream], m_bufferPos[stream]);
+int PAPlayer::GetBitsPerSample()
+{
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
- if (rtn > 0)
- {
- m_bufferPos[stream] -= rtn;
- memmove(m_pcmBuffer[stream], m_pcmBuffer[stream] + rtn, m_bufferPos[stream]);
- }
- else //no pcm data added
- {
- int sleepTime = MathUtils::round_int(m_pAudioDecoder[stream]->GetCacheTime() * 200.0);
- Sleep(std::max(sleepTime, 1));
- }
- }
+ return m_currentStream->m_bytesPerSample >> 3;
+}
- // something done
- ret = true;
- }
+int PAPlayer::GetSampleRate()
+{
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
- return ret;
+ return m_currentStream->m_sampleRate;
}
-bool PAPlayer::FindFreePacket( int stream, DWORD* pdwPacket )
+CStdString PAPlayer::GetAudioCodecName()
{
- return true;
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return "";
+
+ const ICodec* codec = m_currentStream->m_decoder.GetCodec();
+ if (codec)
+ return codec->m_CodecName;
+ return "";
}
-void PAPlayer::RegisterAudioCallback(IAudioCallback *pCallback)
+int PAPlayer::GetAudioBitrate()
{
- m_pCallback = pCallback;
- if (m_pCallback)
- m_pCallback->OnInitialize(m_channelCount[m_currentStream], m_sampleRate[m_currentStream], m_bitsPerSample[m_currentStream]);
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return 0;
+
+ const ICodec* codec = m_currentStream->m_decoder.GetCodec();
+ if (codec)
+ return codec->m_Bitrate;
+ return 0;
}
-void PAPlayer::UnRegisterAudioCallback()
+bool PAPlayer::CanSeek()
{
- m_pCallback = NULL;
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
+ return false;
+
+ return m_currentStream->m_decoder.CanSeek();
}
-void PAPlayer::DoAudioWork()
+void PAPlayer::Seek(bool bPlus, bool bLargeStep)
{
- if (m_pCallback && m_visBufferLength)
- {
- m_pCallback->OnAudioData((BYTE*)m_visBuffer, m_visBufferLength);
- m_visBufferLength = 0;
- }
}
-void PAPlayer::StreamCallback( LPVOID pPacketContext )
+void PAPlayer::SeekTime(int64_t iTime /*=0*/)
{
- AudioPacket *pkt = (AudioPacket *)pPacketContext;
-
+ if (!CanSeek()) return;
- // only process from the current stream (if we're crossfading for instance)
- if (pkt->stream != m_currentStream)
+ CSharedLock lock(m_streamsLock);
+ if (!m_currentStream)
return;
- m_bytesSentOut += pkt->length;
+ int seekOffset = (int)(iTime - GetTime());
+ if (m_currentStream->m_startOffset)
+ iTime += m_currentStream->m_startOffset;
- if (m_pCallback)
- { // copy into our visualisation buffer.
- // can't use a memcpy() here due to the context (will crash otherwise)
- memcpy((short*)m_visBuffer, pkt->packet, pkt->length);
- m_visBufferLength = pkt->length;
- }
+ if (m_playbackSpeed != 1)
+ ToFFRW(1);
+
+ m_currentStream->m_seekFrame = (int)(m_currentStream->m_sampleRate * (iTime / 1000));
+ m_callback.OnPlayBackSeek((int)iTime, seekOffset);
}
-void CALLBACK StaticStreamCallback( VOID* pStreamContext, VOID* pPacketContext, DWORD dwStatus )
+void PAPlayer::SeekPercentage(float fPercent /*=0*/)
{
- PAPlayer* pPlayer = (PAPlayer*)pStreamContext;
- pPlayer->StreamCallback(pPacketContext);
+ if (fPercent < 0.0f ) fPercent = 0.0f;
+ if (fPercent > 100.0f) fPercent = 100.0f;
+ SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
}
-bool PAPlayer::HandlesType(const CStdString &type)
+float PAPlayer::GetPercentage()
{
- ICodec* codec=CodecFactory::CreateCodec(type);
-
- if (codec && codec->CanInit())
- {
- delete codec;
- return true;
- }
- if (codec)
- delete codec;
-
- return false;
+ return GetTime() * 100.0f / GetTotalTime64();
}
-// Skip to next track/item inside the current media (if supported).
bool PAPlayer::SkipNext()
{
- if (m_decoder[m_currentDecoder].GetCodec() && m_decoder[m_currentDecoder].GetCodec()->SkipNext())
- {
- return true;
- }
return false;
}
-
-void PAPlayer::WaitForStream()
-{
- // should we wait for our other stream as well?
- // currently we don't.
- if (m_pAudioDecoder[m_currentStream])
- {
- m_pAudioDecoder[m_currentStream]->WaitCompletion();
- }
-}
diff --git a/xbmc/cores/paplayer/PAPlayer.h b/xbmc/cores/paplayer/PAPlayer.h
index 75ad1046ce..2b40a4521e 100644
--- a/xbmc/cores/paplayer/PAPlayer.h
+++ b/xbmc/cores/paplayer/PAPlayer.h
@@ -21,55 +21,40 @@
*
*/
+#include <list>
+
#include "cores/IPlayer.h"
#include "threads/Thread.h"
#include "AudioDecoder.h"
-#include "utils/ssrc.h"
-#include "cores/AudioRenderers/IAudioRenderer.h"
+#include "threads/SharedSection.h"
-class CFileItem;
-#ifndef _LINUX
-#define PACKET_COUNT 20 // number of packets of size PACKET_SIZE (defined in AudioDecoder.h)
-#else
-#define PACKET_COUNT 1
-#endif
-
-#define STATUS_NO_FILE 0
-#define STATUS_QUEUING 1
-#define STATUS_QUEUED 2
-#define STATUS_PLAYING 3
-#define STATUS_ENDING 4
-#define STATUS_ENDED 5
-
-struct AudioPacket
-{
- BYTE *packet;
- DWORD length;
- DWORD status;
- int stream;
-};
+#include "cores/IAudioCallback.h"
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/Interfaces/AEStream.h"
+class CFileItem;
class PAPlayer : public IPlayer, public CThread
{
public:
PAPlayer(IPlayerCallback& callback);
virtual ~PAPlayer();
+ virtual void RegisterAudioCallback(IAudioCallback* pCallback);
+ virtual void UnRegisterAudioCallback();
virtual bool OpenFile(const CFileItem& file, const CPlayerOptions &options);
virtual bool QueueNextFile(const CFileItem &file);
virtual void OnNothingToQueueNotify();
- virtual bool CloseFile() { return CloseFileInternal(true); }
- virtual bool CloseFileInternal(bool bAudioDevice = true);
- virtual bool IsPlaying() const { return m_bIsPlaying; }
+ virtual bool CloseFile();
+ virtual bool IsPlaying() const;
virtual void Pause();
- virtual bool IsPaused() const { return m_bPaused; }
+ virtual bool IsPaused() const;
virtual bool HasVideo() const { return false; }
virtual bool HasAudio() const { return true; }
virtual bool CanSeek();
virtual void Seek(bool bPlus = true, bool bLargeStep = false);
virtual void SeekPercentage(float fPercent = 0.0f);
virtual float GetPercentage();
- virtual void SetVolume(long nVolume);
+ virtual void SetVolume(float volume);
virtual void SetDynamicRangeCompression(long drc);
virtual void GetAudioInfo( CStdString& strAudioInfo) {}
virtual void GetVideoInfo( CStdString& strVideoInfo) {}
@@ -78,7 +63,6 @@ public:
virtual void ToFFRW(int iSpeed = 0);
virtual int GetCacheLevel() const;
virtual int GetTotalTime();
- int64_t GetTotalTime64();
virtual int GetAudioBitrate();
virtual int GetChannels();
virtual int GetBitsPerSample();
@@ -86,114 +70,67 @@ public:
virtual CStdString GetAudioCodecName();
virtual int64_t GetTime();
virtual void SeekTime(int64_t iTime = 0);
- // Skip to next track/item inside the current media (if supported).
virtual bool SkipNext();
- void StreamCallback( LPVOID pPacketContext );
-
- virtual void RegisterAudioCallback(IAudioCallback *pCallback);
- virtual void UnRegisterAudioCallback();
-
static bool HandlesType(const CStdString &type);
- virtual void DoAudioWork();
-
protected:
-
virtual void OnStartup() {}
virtual void Process();
virtual void OnExit();
- void HandleSeeking();
- bool HandleFFwdRewd();
-
- bool m_bPaused;
- bool m_bIsPlaying;
- bool m_bQueueFailed;
- bool m_bStopPlaying;
- bool m_cachingNextFile;
- int m_crossFading;
- bool m_currentlyCrossFading;
- int64_t m_crossFadeLength;
-
- CEvent m_startEvent;
-
- int m_iSpeed; // current playing speed
-
private:
-
- bool ProcessPAP(); // does the actual reading and decode from our PAP dll
-
- int64_t m_SeekTime;
- int m_IsFFwdRewding;
- int64_t m_timeOffset;
- bool m_forceFadeToNext;
-
- int m_currentDecoder;
- CAudioDecoder m_decoder[2]; // our 2 audiodecoders (for crossfading + precaching)
-
-#ifndef _LINUX
- void SetupDirectSound(int channels);
-#endif
-
- // Our directsoundstream
- friend void CALLBACK StaticStreamCallback( LPVOID pStreamContext, LPVOID pPacketContext, DWORD dwStatus );
- bool AddPacketsToStream(int stream, CAudioDecoder &dec);
- bool FindFreePacket(int stream, DWORD *pdwPacket ); // Looks for a free packet
- void FreeStream(int stream);
-#if defined(_LINUX) || defined(_WIN32)
- void DrainStream(int stream);
-#endif
- bool CreateStream(int stream, unsigned int channels, unsigned int samplerate, unsigned int bitspersample, CStdString codec = "");
- void FlushStreams();
- void WaitForStream();
- void SetStreamVolume(int stream, long nVolume);
-
- void UpdateCrossFadingTime(const CFileItem& file);
- bool QueueNextFile(const CFileItem &file, bool checkCrossFading);
- void UpdateCacheLevel();
-
- int m_currentStream;
-
- IAudioRenderer* m_pAudioDecoder[2];
- float m_latency[2];
- unsigned char* m_pcmBuffer[2];
- int m_bufferPos[2];
- unsigned int m_Chunklen[2];
-
- unsigned int m_SampleRate;
- unsigned int m_Channels;
- unsigned int m_BitsPerSample;
-
- unsigned int m_SampleRateOutput;
- unsigned int m_BitsPerSampleOutput;
-
- AudioPacket m_packet[2][PACKET_COUNT];
-
- IAudioCallback* m_pCallback;
-
- int64_t m_bytesSentOut;
-
- // format (this should be stored/retrieved from the audio device object probably)
- unsigned int m_channelCount[2];
- enum PCMChannels*m_channelMap[2];
- unsigned int m_sampleRate[2];
- unsigned int m_bitsPerSample[2];
- unsigned int m_BytesPerSecond;
-
- unsigned int m_CacheLevel;
- unsigned int m_LastCacheLevelCheck;
-
- // resampler
- Cssrc m_resampler[2];
- bool m_resampleAudio;
-
- // our file
- CFileItem* m_currentFile;
- CFileItem* m_nextFile;
-
- // stuff for visualisation
- unsigned int m_visBufferLength;
- short m_visBuffer[PACKET_SIZE+2];
-
+ typedef struct {
+ CAudioDecoder m_decoder; /* the stream decoder */
+ int64_t m_startOffset; /* the stream start offset */
+ int64_t m_endOffset; /* the stream end offset */
+ CAEChannelInfo m_channelInfo; /* channel layout information */
+ unsigned int m_sampleRate; /* sample rate of the stream */
+ unsigned int m_encodedSampleRate; /* the encoded sample rate of raw streams */
+ enum AEDataFormat m_dataFormat; /* data format of the samples */
+ unsigned int m_bytesPerSample; /* number of bytes per audio sample */
+ unsigned int m_bytesPerFrame; /* number of bytes per audio frame */
+
+ bool m_started; /* if playback of this stream has been started */
+ bool m_finishing; /* if this stream is finishing */
+ int m_framesSent; /* number of frames sent to the stream */
+ int m_prepareNextAtFrame; /* when to prepare the next stream */
+ bool m_prepareTriggered; /* if the next stream has been prepared */
+ int m_playNextAtFrame; /* when to start playing the next stream */
+ bool m_playNextTriggered; /* if this stream has started the next one */
+ bool m_fadeOutTriggered; /* if the stream has been told to fade out */
+ int m_seekNextAtFrame; /* the FF/RR sample to seek at */
+ int m_seekFrame; /* the exact position to seek too, -1 for none */
+
+ IAEStream* m_stream; /* the playback stream */
+ float m_volume; /* the initial volume level to set the stream to on creation */
+
+ bool m_isSlaved; /* true if the stream has been slaved to another */
+ } StreamInfo;
+
+ typedef std::list<StreamInfo*> StreamList;
+
+ bool m_signalSpeedChange; /* true if OnPlaybackSpeedChange needs to be called */
+ int m_playbackSpeed; /* the playback speed (1 = normal) */
+ bool m_isPlaying;
+ bool m_isPaused;
+ bool m_isFinished; /* if there are no more songs in the queue */
+ unsigned int m_crossFadeTime; /* how long the crossfade is */
+ CEvent m_startEvent; /* event for playback start */
+ StreamInfo* m_currentStream; /* the current playing stream */
+ IAudioCallback* m_audioCallback; /* the viz audio callback */
+
+ CSharedSection m_streamsLock; /* lock for the stream list */
+ StreamList m_streams; /* playing streams */
+ StreamList m_finishing; /* finishing streams */
+
+ bool QueueNextFileEx(const CFileItem &file, bool fadeIn = true);
+ void SoftStart(bool wait = false);
+ void SoftStop(bool wait = false, bool close = true);
+ void CloseAllStreams(bool fade = true);
+ void ProcessStreams(double &delay, double &buffer);
+ bool PrepareStream(StreamInfo *si);
+ bool ProcessStream(StreamInfo *si, double &delay, double &buffer);
+ bool QueueData(StreamInfo *si);
+ int64_t GetTotalTime64();
};
diff --git a/xbmc/cores/paplayer/SPCCodec.cpp b/xbmc/cores/paplayer/SPCCodec.cpp
index ec247a70f7..86bd7e456b 100644
--- a/xbmc/cores/paplayer/SPCCodec.cpp
+++ b/xbmc/cores/paplayer/SPCCodec.cpp
@@ -125,6 +125,7 @@ bool SPCCodec::Init(const CStdString &strFile, unsigned int filecache)
m_SampleRate = 32000;
m_Channels = 2;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
CMusicInfoTagLoaderSPC tagLoader;
CMusicInfoTag tag;
tagLoader.Load(strFile,tag);
diff --git a/xbmc/cores/paplayer/TimidityCodec.cpp b/xbmc/cores/paplayer/TimidityCodec.cpp
index 01b7b09889..b99a22e584 100644
--- a/xbmc/cores/paplayer/TimidityCodec.cpp
+++ b/xbmc/cores/paplayer/TimidityCodec.cpp
@@ -124,6 +124,7 @@ bool TimidityCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = 2;
m_SampleRate = 48000;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = (int64_t)m_dll.GetLength(m_mid);
return true;
diff --git a/xbmc/cores/paplayer/VGMCodec.cpp b/xbmc/cores/paplayer/VGMCodec.cpp
index 71ff0e346e..ee018ef6de 100644
--- a/xbmc/cores/paplayer/VGMCodec.cpp
+++ b/xbmc/cores/paplayer/VGMCodec.cpp
@@ -28,6 +28,7 @@ VGMCodec::VGMCodec()
m_CodecName = "VGM";
m_vgm = 0;
m_iDataPos = -1;
+ m_DataFormat = AE_FMT_INVALID;
}
VGMCodec::~VGMCodec()
@@ -53,6 +54,13 @@ bool VGMCodec::Init(const CStdString &strFile, unsigned int filecache)
return false;
}
+ switch (m_BitsPerSample)
+ {
+ case 8: m_DataFormat = AE_FMT_U8 ; break;
+ case 16: m_DataFormat = AE_FMT_S16NE; break;
+ case 32: m_DataFormat = AE_FMT_FLOAT; break;
+ }
+
m_TotalTime = (int64_t)m_dll.GetLength(m_vgm);
return true;
diff --git a/xbmc/cores/paplayer/WAVcodec.cpp b/xbmc/cores/paplayer/WAVcodec.cpp
index d164b5f0ed..40b58a02e7 100644
--- a/xbmc/cores/paplayer/WAVcodec.cpp
+++ b/xbmc/cores/paplayer/WAVcodec.cpp
@@ -48,7 +48,7 @@ WAVCodec::WAVCodec()
m_SampleRate = 0;
m_Channels = 0;
m_BitsPerSample = 0;
- m_bHasFloat = false;
+ m_DataFormat = AE_FMT_INVALID;
m_iDataStart=0;
m_iDataLen=0;
m_Bitrate = 0;
@@ -99,8 +99,16 @@ bool WAVCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = Endian_SwapLE16(wfx.Format.nChannels );
m_BitsPerSample = Endian_SwapLE16(wfx.Format.wBitsPerSample);
+ switch(m_BitsPerSample)
+ {
+ case 8 : m_DataFormat = AE_FMT_U8 ; break;
+ case 16: m_DataFormat = AE_FMT_S16LE; break;
+ case 24: m_DataFormat = AE_FMT_S24NE3; break;
+ case 32: m_DataFormat = AE_FMT_FLOAT; break;
+ }
+
CLog::Log(LOGINFO, "WAVCodec::Init - Sample Rate: %d, Bits Per Sample: %d, Channels: %d", m_SampleRate, m_BitsPerSample, m_Channels);
- if ((m_SampleRate == 0) || (m_Channels == 0) || (m_BitsPerSample == 0))
+ if ((m_SampleRate == 0) || (m_Channels == 0) || (m_BitsPerSample == 0) || (m_DataFormat == AE_FMT_INVALID))
{
CLog::Log(LOGERROR, "WAVCodec::Init - Invalid data in WAVE header");
return false;
@@ -115,7 +123,6 @@ bool WAVCodec::Init(const CStdString &strFile, unsigned int filecache)
case WAVE_FORMAT_PCM:
CLog::Log(LOGINFO, "WAVCodec::Init - WAVE_FORMAT_PCM detected");
m_ChannelMask = 0;
- m_bHasFloat = false;
break;
case WAVE_FORMAT_IEEE_FLOAT:
@@ -127,7 +134,6 @@ bool WAVCodec::Init(const CStdString &strFile, unsigned int filecache)
}
m_ChannelMask = 0;
- m_bHasFloat = true;
break;
case WAVE_FORMAT_EXTENSIBLE:
@@ -145,7 +151,6 @@ bool WAVCodec::Init(const CStdString &strFile, unsigned int filecache)
if (memcmp(&wfx.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0)
{
CLog::Log(LOGINFO, "WAVCodec::Init - SubFormat KSDATAFORMAT_SUBTYPE_PCM Detected");
- m_bHasFloat = false;
}
else if (memcmp(&wfx.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0)
{
@@ -155,7 +160,6 @@ bool WAVCodec::Init(const CStdString &strFile, unsigned int filecache)
CLog::Log(LOGERROR, "WAVCodec::Init - Only 32bit Float is supported");
return false;
}
- m_bHasFloat = true;
}
else
{
@@ -263,18 +267,6 @@ int WAVCodec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
return READ_ERROR;
}
-int WAVCodec::ReadSamples(float *pBuffer, int numsamples, int *actualsamples)
-{
- int ret = READ_ERROR;
- *actualsamples = 0;
- if (m_bHasFloat)
- {
- ret = ReadPCM((BYTE*)pBuffer, numsamples * (m_BitsPerSample >> 3), actualsamples);
- *actualsamples /= (m_BitsPerSample >> 3);
- }
- return ret;
-}
-
bool WAVCodec::CanInit()
{
return true;
diff --git a/xbmc/cores/paplayer/WAVcodec.h b/xbmc/cores/paplayer/WAVcodec.h
index e8e239070b..6c2e165d9b 100644
--- a/xbmc/cores/paplayer/WAVcodec.h
+++ b/xbmc/cores/paplayer/WAVcodec.h
@@ -34,12 +34,9 @@ public:
virtual void DeInit();
virtual int64_t Seek(int64_t iSeekTime);
virtual int ReadPCM(BYTE *pBuffer, int size, int *actualsize);
- virtual int ReadSamples(float *pBuffer, int numsamples, int *actualsamples);
virtual bool CanInit();
- virtual bool HasFloatData() const { return m_bHasFloat; }
private:
- bool m_bHasFloat;
uint32_t m_iDataStart;
uint32_t m_iDataLen;
DWORD m_ChannelMask;
diff --git a/xbmc/cores/paplayer/YMCodec.cpp b/xbmc/cores/paplayer/YMCodec.cpp
index 4dbae66216..ab1ff9482e 100644
--- a/xbmc/cores/paplayer/YMCodec.cpp
+++ b/xbmc/cores/paplayer/YMCodec.cpp
@@ -50,6 +50,7 @@ bool YMCodec::Init(const CStdString &strFile, unsigned int filecache)
m_Channels = 1;
m_SampleRate = 44100;
m_BitsPerSample = 16;
+ m_DataFormat = AE_FMT_S16NE;
m_TotalTime = m_dll.GetLength(m_ym)*1000;
return true;
diff --git a/xbmc/dialogs/GUIDialogMuteBug.cpp b/xbmc/dialogs/GUIDialogMuteBug.cpp
index a54ec0a5f3..df4fde9305 100644
--- a/xbmc/dialogs/GUIDialogMuteBug.cpp
+++ b/xbmc/dialogs/GUIDialogMuteBug.cpp
@@ -36,7 +36,7 @@ CGUIDialogMuteBug::~CGUIDialogMuteBug(void)
void CGUIDialogMuteBug::UpdateVisibility()
{
- if (g_settings.m_bMute || g_settings.m_nVolumeLevel == VOLUME_MINIMUM)
+ if (g_settings.m_bMute || g_settings.m_fVolumeLevel == VOLUME_MINIMUM)
Show();
else
Close();
diff --git a/xbmc/guilib/AudioContext.cpp b/xbmc/guilib/AudioContext.cpp
deleted file mode 100644
index 1ae0ccfdcb..0000000000
--- a/xbmc/guilib/AudioContext.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "system.h"
-#include "AudioContext.h"
-#include "GUIAudioManager.h"
-#include "settings/Settings.h"
-#include "settings/GUISettings.h"
-#ifdef _WIN32
-#include "WINDirectSound.h"
-#endif
-extern HWND g_hWnd;
-
-#ifdef _WIN32
-static GUID g_digitaldevice;
-BOOL CALLBACK DSEnumCallback(
- LPGUID lpGuid,
- LPCSTR lpcstrDescription,
- LPCSTR lpcstrModule,
- LPVOID lpContext
-)
-{
- if(strstr(lpcstrDescription, "Digital Output") != NULL)
- {
- g_digitaldevice = *lpGuid;
- return false;
- }
- return true;
-}
-#endif
-
-CAudioContext::CAudioContext()
-{
- m_bAC3EncoderActive=false;
- m_iDevice=DEFAULT_DEVICE;
- m_strDevice.clear();
-#ifdef HAS_AUDIO
-#ifdef HAS_AUDIO_PASS_THROUGH
- m_pAC97Device=NULL;
-#endif
- m_pDirectSoundDevice=NULL;
-#endif
-}
-
-CAudioContext::~CAudioContext()
-{
-}
-
-// \brief Create a new device by type (DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE)
-void CAudioContext::SetActiveDevice(int iDevice)
-{
- CStdString strAudioDev = g_guiSettings.GetString("audiooutput.audiodevice");
-
- /* if device is the same, no need to bother */
-#ifdef _WIN32
-
- HRESULT hr;
- int iPos = strAudioDev.Find(':');
- if(iPos != CStdString::npos)
- strAudioDev.erase(0, iPos+1);
-
- if (iDevice == DEFAULT_DEVICE)
- iDevice = DIRECTSOUND_DEVICE; // default device on win32 is directsound device
- if(m_iDevice == iDevice && strAudioDev.Equals(m_strDevice))
- {
- if (iDevice != NONE && m_pDirectSoundDevice)
- {
- DSCAPS devCaps = {0};
- devCaps.dwSize = sizeof(devCaps);
- HRESULT hr = m_pDirectSoundDevice->GetCaps(&devCaps);
- if (SUCCEEDED(hr)) // Make sure the DirectSound interface is still valid.
- return;
- CLog::Log(LOGDEBUG, "%s - DirectSoundDevice no longer valid and is going to be recreated (0x%08x)", __FUNCTION__, hr);
- }
- }
-
-#else
- if(m_iDevice == iDevice)
- return;
-#endif
-
- CLog::Log(LOGDEBUG, "%s - SetActiveDevice from %i to %i", __FUNCTION__, m_iDevice, iDevice);
- /* deinit current device */
- RemoveActiveDevice();
-
- m_iDevice=iDevice;
- m_strDevice=strAudioDev;
-
-#ifdef HAS_AUDIO
- memset(&g_digitaldevice, 0, sizeof(GUID));
- hr = DirectSoundEnumerate(DSEnumCallback, this);
- if (FAILED(hr))
- CLog::Log(LOGERROR, "%s - failed to enumerate output devices (0x%08X)", __FUNCTION__, hr);
-
- if (iDevice==DIRECTSOUND_DEVICE
- || iDevice==DIRECTSOUND_DEVICE_DIGITAL)
- {
- LPGUID guid = NULL;
-#ifdef _WIN32
- CWDSound p_dsound;
- std::vector<DSDeviceInfo > deviceList = p_dsound.GetSoundDevices();
- if (deviceList.size() == 0)
- CLog::Log(LOGDEBUG, "%s - no output devices found.", __FUNCTION__);
-
- std::vector<DSDeviceInfo >::const_iterator iter = deviceList.begin();
- for (int i=0; iter != deviceList.end(); i++)
- {
- DSDeviceInfo dev = *iter;
-
- if (strAudioDev.Equals(dev.strDescription))
- {
- guid = dev.lpGuid;
- CLog::Log(LOGDEBUG, "%s - selecting %s as output devices", __FUNCTION__, dev.strDescription.c_str());
- break;
- }
-
- ++iter;
- }
- if (guid == NULL)
- CLog::Log(LOGDEBUG, "%s - (default playback device).", __FUNCTION__);
-#else
- if(iDevice == DIRECTSOUND_DEVICE_DIGITAL
- && ( g_digitaldevice.Data1 || g_digitaldevice.Data2
- || g_digitaldevice.Data3 || g_digitaldevice.Data4 ))
- guid = &g_digitaldevice;
-#endif
-
- // Create DirectSound
- hr = DirectSoundCreate( guid, &m_pDirectSoundDevice, NULL );
- if (FAILED(hr))
- {
- CLog::Log(LOGERROR, "DirectSoundCreate() Failed (0x%08X)", hr);
- return;
- }
- hr = m_pDirectSoundDevice->SetCooperativeLevel(g_hWnd, DSSCL_PRIORITY);
- if (FAILED(hr))
- {
- CLog::Log(LOGERROR, "DirectSoundDevice::SetCooperativeLevel() Failed (0x%08X)", hr);
- return;
- }
- }
- else if (iDevice == DIRECTSOUND_DEVICE_DIGITAL)
- {
-
- }
-#ifdef HAS_AUDIO_PASS_THROUGH
- else if (iDevice==AC97_DEVICE)
- {
- // Create AC97 Device
- if (FAILED(Ac97CreateMediaObject(DSAC97_CHANNEL_DIGITAL, NULL, NULL, &m_pAC97Device)))
- {
- CLog::Log(LOGERROR, "Failed to create digital Ac97CreateMediaObject()");
- return;
- }
- }
-#endif
- // Don't log an error if the caller specifically asked for no device
- // externalplayer does this to ensure all audio devices are closed
- // during external playback
- else if (iDevice != NONE)
- {
- CLog::Log(LOGERROR, "Failed to create audio device");
- return;
- }
-#endif
- g_audioManager.Initialize(m_iDevice);
-}
-
-// \brief Return the active device type (NONE, DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE)
-int CAudioContext::GetActiveDevice()
-{
- return m_iDevice;
-}
-
-// \brief Remove the current sound device, eg. to setup new speaker config
-void CAudioContext::RemoveActiveDevice()
-{
- CLog::Log(LOGDEBUG, "%s - Removing device %i", __FUNCTION__, m_iDevice);
- g_audioManager.DeInitialize(m_iDevice);
- m_iDevice=NONE;
-
-#ifdef HAS_AUDIO
-#ifdef HAS_AUDIO_PASS_THROUGH
- SAFE_RELEASE(m_pAC97Device);
-#endif
- SAFE_RELEASE(m_pDirectSoundDevice);
-#endif
-}
-
-// \brief set a new speaker config
-void CAudioContext::SetupSpeakerConfig(int iChannels, bool& bAudioOnAllSpeakers, bool bIsMusic)
-{
- m_bAC3EncoderActive = false;
- bAudioOnAllSpeakers = false;
-
-#ifdef HAS_AUDIO
- return; //not implemented
- DWORD spconfig = DSSPEAKER_USE_DEFAULT;
- if (AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode")))
- {
- if (g_settings.m_currentVideoSettings.m_OutputToAllSpeakers && !bIsMusic)
- {
- bAudioOnAllSpeakers = true;
- m_bAC3EncoderActive = true;
- spconfig = DSSPEAKER_USE_DEFAULT; //Allows ac3 encoder should it be enabled
- }
- else
- {
- if (iChannels == 1)
- spconfig = DSSPEAKER_MONO;
- else if (iChannels == 2)
- spconfig = DSSPEAKER_STEREO;
- else
- spconfig = DSSPEAKER_USE_DEFAULT; //Allows ac3 encoder should it be enabled
- }
- }
- else // We don't want to use the Dolby Digital Encoder output. Downmix to surround instead.
- {
- if (iChannels == 1)
- spconfig = DSSPEAKER_MONO;
- else
- {
- // check if surround mode is allowed, if not then use normal stereo
- // don't always set it to default as that enabled ac3 encoder if that is allowed in dash
- // ruining quality
- spconfig = DSSPEAKER_STEREO;
- }
- }
-
- DWORD spconfig_old = DSSPEAKER_USE_DEFAULT;
- if(m_pDirectSoundDevice)
- {
- m_pDirectSoundDevice->GetSpeakerConfig(&spconfig_old);
- spconfig_old = DSSPEAKER_CONFIG(spconfig_old);
- }
-
- /* speaker config identical, no need to do anything */
- if(spconfig == spconfig_old) return;
- CLog::Log(LOGDEBUG, "%s - Speakerconfig changed from %i to %i", __FUNCTION__, spconfig_old, spconfig);
-#endif
-
- /* speaker config has changed, caller need to recreate it */
- RemoveActiveDevice();
-}
-
-bool CAudioContext::IsAC3EncoderActive() const
-{
- return m_bAC3EncoderActive;
-}
-
-bool CAudioContext::IsPassthroughActive() const
-{
- return (m_iDevice == DIRECTSOUND_DEVICE_DIGITAL);
-}
-
diff --git a/xbmc/guilib/AudioContext.h b/xbmc/guilib/AudioContext.h
deleted file mode 100644
index b46d4f61b8..0000000000
--- a/xbmc/guilib/AudioContext.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*!
-\file AudioContext.h
-\brief
-*/
-
-#pragma once
-
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "utils/StdString.h"
-
-#define DSMIXBINTYPE_STANDARD 1
-#define DSMIXBINTYPE_DMO 2
-#define DSMIXBINTYPE_AAC 3
-#define DSMIXBINTYPE_OGG 4
-#define DSMIXBINTYPE_CUSTOM 5
-#define DSMIXBINTYPE_STEREOALL 6
-#define DSMIXBINTYPE_STEREOLEFT 7
-#define DSMIXBINTYPE_STEREORIGHT 8
-
-class CAudioContext
-{
-public:
- CAudioContext();
- virtual ~CAudioContext();
-
- void SetActiveDevice(int iDevice);
- int GetActiveDevice();
-
-#ifdef HAS_AUDIO
- LPDIRECTSOUND8 GetDirectSoundDevice() { return m_pDirectSoundDevice; }
-#ifdef HAS_AUDIO_PASS_THROUGH
- LPAC97MEDIAOBJECT GetAc97Device() { return m_pAC97Device; }
-#endif
-#endif
-
- void SetupSpeakerConfig(int iChannels, bool& bAudioOnAllSpeakers, bool bIsMusic=true);
- bool IsAC3EncoderActive() const;
- bool IsPassthroughActive() const;
-
- enum AUDIO_DEVICE {NONE=0, DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE, DIRECTSOUND_DEVICE_DIGITAL };
-protected:
- void RemoveActiveDevice();
-
-#ifdef HAS_AUDIO
-#ifdef HAS_AUDIO_PASS_THROUGH
- LPAC97MEDIAOBJECT m_pAC97Device;
-#endif
- LPDIRECTSOUND8 m_pDirectSoundDevice;
-#endif
-
- int m_iDevice;
- CStdString m_strDevice;
- bool m_bAC3EncoderActive;
-};
-
-extern CAudioContext g_audioContext;
diff --git a/xbmc/guilib/GUIAudioManager.cpp b/xbmc/guilib/GUIAudioManager.cpp
index 96a159f014..e197c2ffa4 100644
--- a/xbmc/guilib/GUIAudioManager.cpp
+++ b/xbmc/guilib/GUIAudioManager.cpp
@@ -22,18 +22,13 @@
#include "system.h"
#include "GUIAudioManager.h"
#include "Key.h"
-#include "AudioContext.h"
-#include "GUISound.h"
-#include "settings/Settings.h"
#include "settings/GUISettings.h"
#include "input/ButtonTranslator.h"
#include "threads/SingleLock.h"
#include "utils/URIUtils.h"
#include "utils/XBMCTinyXML.h"
#include "addons/Skin.h"
-#ifdef HAS_SDL_AUDIO
-#include <SDL/SDL_mixer.h>
-#endif
+#include "cores/AudioEngine/AEFactory.h"
using namespace std;
using namespace XFILE;
@@ -42,133 +37,36 @@ CGUIAudioManager g_audioManager;
CGUIAudioManager::CGUIAudioManager()
{
- m_bInitialized = false;
m_bEnabled = false;
- m_actionSound=NULL;
}
CGUIAudioManager::~CGUIAudioManager()
{
-
}
-void CGUIAudioManager::Initialize(int iDevice)
+void CGUIAudioManager::Initialize()
{
- if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
- return;
-
- if (iDevice==CAudioContext::DEFAULT_DEVICE)
- {
- CSingleLock lock(m_cs);
-
- if (m_bInitialized)
- return;
-
- CLog::Log(LOGDEBUG, "CGUIAudioManager::Initialize");
-#ifdef _WIN32
- bool bAudioOnAllSpeakers=false;
- g_audioContext.SetupSpeakerConfig(2, bAudioOnAllSpeakers);
- g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
- m_bInitialized = true;
-#elif defined(HAS_SDL_AUDIO)
- Mix_CloseAudio();
- if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 4096))
- CLog::Log(LOGERROR, "Unable to open audio mixer");
- else
- Mix_Volume(0, (int)(128.f * (g_settings.m_nVolumeLevel - VOLUME_MINIMUM) / (float)(VOLUME_MAXIMUM - VOLUME_MINIMUM)));
-
- m_bInitialized = true;
-#endif
- }
}
-void CGUIAudioManager::DeInitialize(int iDevice)
+void CGUIAudioManager::DeInitialize()
{
- if (!(iDevice == CAudioContext::DIRECTSOUND_DEVICE || iDevice == CAudioContext::DEFAULT_DEVICE)) return;
-
CSingleLock lock(m_cs);
-
- if (!m_bInitialized)
- return;
-
- CLog::Log(LOGDEBUG, "CGUIAudioManager::DeInitialize");
-
- if (m_actionSound)
- m_actionSound->Wait();
-
- Stop();
-#ifdef HAS_SDL_AUDIO
- Mix_CloseAudio();
-#endif
- m_bInitialized = false;
+ UnLoad();
}
void CGUIAudioManager::Stop()
{
CSingleLock lock(m_cs);
- if (m_actionSound)
+ for (windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
{
- delete m_actionSound;
- m_actionSound=NULL;
+ if (it->second.initSound ) it->second.initSound ->Stop();
+ if (it->second.deInitSound) it->second.deInitSound->Stop();
}
- for (windowSoundsMap::iterator it=m_windowSounds.begin();it!=m_windowSounds.end();it++)
+ for (pythonSoundsMap::iterator it = m_pythonSounds.begin(); it != m_pythonSounds.end(); ++it)
{
- CGUISound* sound=it->second;
- if (sound->IsPlaying())
- sound->Stop();
-
- delete sound;
- }
- m_windowSounds.clear();
-
- for (pythonSoundsMap::iterator it1=m_pythonSounds.begin();it1!=m_pythonSounds.end();it1++)
- {
- CGUISound* sound=it1->second;
- if (sound->IsPlaying())
- sound->Stop();
-
- delete sound;
- }
- m_pythonSounds.clear();
-}
-
-// \brief Clear any unused audio buffers
-void CGUIAudioManager::FreeUnused()
-{
- CSingleLock lock(m_cs);
-
- // Free the sound from the last action
- if (m_actionSound && !m_actionSound->IsPlaying())
- {
- delete m_actionSound;
- m_actionSound=NULL;
- }
-
- // Free sounds from windows
- windowSoundsMap::iterator it=m_windowSounds.begin();
- while (it!=m_windowSounds.end())
- {
- CGUISound* sound=it->second;
- if (!sound->IsPlaying())
- {
- delete sound;
- m_windowSounds.erase(it++);
- }
- else ++it;
- }
-
- // Free sounds from python
- pythonSoundsMap::iterator it1=m_pythonSounds.begin();
- while (it1!=m_pythonSounds.end())
- {
- CGUISound* sound=it1->second;
- if (!sound->IsPlaying())
- {
- delete sound;
- m_pythonSounds.erase(it1++);
- }
- else ++it1;
+ IAESound* sound = it->second;
+ sound->Stop();
}
}
@@ -178,28 +76,15 @@ void CGUIAudioManager::PlayActionSound(const CAction& action)
CSingleLock lock(m_cs);
// it's not possible to play gui sounds when passthrough is active
- if (!m_bEnabled || !m_bInitialized || g_audioContext.IsPassthroughActive())
- return;
-
- actionSoundMap::iterator it=m_actionSoundMap.find(action.GetID());
- if (it==m_actionSoundMap.end())
+ if (!m_bEnabled)
return;
- if (m_actionSound)
- {
- delete m_actionSound;
- m_actionSound=NULL;
- }
-
- m_actionSound=new CGUISound();
- if (!m_actionSound->Load(URIUtils::AddFileToFolder(m_strMediaDir, it->second)))
- {
- delete m_actionSound;
- m_actionSound=NULL;
+ actionSoundMap::iterator it = m_actionSoundMap.find(action.GetID());
+ if (it == m_actionSoundMap.end())
return;
- }
- m_actionSound->Play();
+ if (it->second)
+ it->second->Play();;
}
// \brief Play a sound associated with a window and its event
@@ -209,7 +94,7 @@ void CGUIAudioManager::PlayWindowSound(int id, WINDOW_SOUND event)
CSingleLock lock(m_cs);
// it's not possible to play gui sounds when passthrough is active
- if (!m_bEnabled || !m_bInitialized || g_audioContext.IsPassthroughActive())
+ if (!m_bEnabled)
return;
windowSoundMap::iterator it=m_windowSoundMap.find(id);
@@ -217,39 +102,20 @@ void CGUIAudioManager::PlayWindowSound(int id, WINDOW_SOUND event)
return;
CWindowSounds sounds=it->second;
- CStdString strFile;
+ IAESound *sound = NULL;
switch (event)
{
case SOUND_INIT:
- strFile=sounds.strInitFile;
+ sound = sounds.initSound;
break;
case SOUND_DEINIT:
- strFile=sounds.strDeInitFile;
+ sound = sounds.deInitSound;
break;
}
- if (strFile.IsEmpty())
+ if (!sound)
return;
- // One sound buffer for each window
- windowSoundsMap::iterator itsb=m_windowSounds.find(id);
- if (itsb!=m_windowSounds.end())
- {
- CGUISound* sound=itsb->second;
- if (sound->IsPlaying())
- sound->Stop();
- delete sound;
- m_windowSounds.erase(itsb++);
- }
-
- CGUISound* sound=new CGUISound();
- if (!sound->Load(URIUtils::AddFileToFolder(m_strMediaDir, strFile)))
- {
- delete sound;
- return;
- }
-
- m_windowSounds.insert(pair<int, CGUISound*>(id, sound));
sound->Play();
}
@@ -259,31 +125,60 @@ void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName)
CSingleLock lock(m_cs);
// it's not possible to play gui sounds when passthrough is active
- if (!m_bEnabled || !m_bInitialized || g_audioContext.IsPassthroughActive())
+ if (!m_bEnabled)
return;
// If we already loaded the sound, just play it
pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName);
- if (itsb!=m_pythonSounds.end())
+ if (itsb != m_pythonSounds.end())
{
- CGUISound* sound=itsb->second;
- if (sound->IsPlaying())
- sound->Stop();
-
+ IAESound* sound = itsb->second;
sound->Play();
+ return;
+ }
+ IAESound *sound = LoadSound(strFileName);
+ if (!sound)
return;
+
+ m_pythonSounds.insert(pair<const CStdString, IAESound*>(strFileName, sound));
+ sound->Play();
+}
+
+void CGUIAudioManager::UnLoad()
+{
+ // Free sounds from windows
+ {
+ windowSoundMap::iterator it = m_windowSoundMap.begin();
+ while (it != m_windowSoundMap.end())
+ {
+ if (it->second.initSound ) FreeSound(it->second.initSound );
+ if (it->second.deInitSound) FreeSound(it->second.deInitSound);
+ m_windowSoundMap.erase(it++);
+ }
}
- CGUISound* sound=new CGUISound();
- if (!sound->Load(strFileName))
+ // Free sounds from python
{
- delete sound;
- return;
+ pythonSoundsMap::iterator it = m_pythonSounds.begin();
+ while (it != m_pythonSounds.end())
+ {
+ IAESound* sound = it->second;
+ FreeSound(sound);
+ m_pythonSounds.erase(it++);
+ }
}
- m_pythonSounds.insert(pair<CStdString, CGUISound*>(strFileName, sound));
- sound->Play();
+ // free action sounds
+ {
+ actionSoundMap::iterator it = m_actionSoundMap.begin();
+ while (it != m_actionSoundMap.end())
+ {
+ IAESound* sound = it->second;
+ FreeSound(sound);
+ m_actionSoundMap.erase(it++);
+ }
+ }
}
// \brief Load the config file (sounds.xml) for nav sounds
@@ -294,8 +189,7 @@ bool CGUIAudioManager::Load()
{
CSingleLock lock(m_cs);
- m_actionSoundMap.clear();
- m_windowSoundMap.clear();
+ UnLoad();
if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
return true;
@@ -349,10 +243,15 @@ bool CGUIAudioManager::Load()
TiXmlNode* pFileNode = pAction->FirstChild("file");
CStdString strFile;
if (pFileNode && pFileNode->FirstChild())
- strFile+=pFileNode->FirstChild()->Value();
+ strFile += pFileNode->FirstChild()->Value();
if (id > 0 && !strFile.IsEmpty())
- m_actionSoundMap.insert(pair<int, CStdString>(id, strFile));
+ {
+ CStdString filename = URIUtils::AddFileToFolder(m_strMediaDir, strFile);
+ IAESound *sound = LoadSound(filename);
+ if (sound)
+ m_actionSoundMap.insert(pair<int, IAESound *>(id, sound));
+ }
pAction = pAction->NextSibling();
}
@@ -376,8 +275,8 @@ bool CGUIAudioManager::Load()
}
CWindowSounds sounds;
- LoadWindowSound(pWindow, "activate", sounds.strInitFile);
- LoadWindowSound(pWindow, "deactivate", sounds.strDeInitFile);
+ sounds.initSound = LoadWindowSound(pWindow, "activate" );
+ sounds.deInitSound = LoadWindowSound(pWindow, "deactivate");
if (id > 0)
m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds));
@@ -389,20 +288,53 @@ bool CGUIAudioManager::Load()
return true;
}
+IAESound* CGUIAudioManager::LoadSound(const CStdString &filename)
+{
+ CSingleLock lock(m_cs);
+ soundCache::iterator it = m_soundCache.find(filename);
+ if (it != m_soundCache.end())
+ {
+ ++it->second.usage;
+ return it->second.sound;
+ }
+
+ IAESound *sound = CAEFactory::AE->MakeSound(filename);
+ if (!sound)
+ return NULL;
+
+ CSoundInfo info;
+ info.usage = 1;
+ info.sound = sound;
+ m_soundCache[filename] = info;
+
+ return info.sound;
+}
+
+void CGUIAudioManager::FreeSound(IAESound *sound)
+{
+ CSingleLock lock(m_cs);
+ for(soundCache::iterator it = m_soundCache.begin(); it != m_soundCache.end(); ++it) {
+ if (it->second.sound == sound) {
+ if (--it->second.usage == 0) {
+ CAEFactory::AE->FreeSound(sound);
+ m_soundCache.erase(it);
+ }
+ return;
+ }
+ }
+}
+
// \brief Load a window node of the config file (sounds.xml)
-bool CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier, CStdString& strFile)
+IAESound* CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier)
{
if (!pWindowNode)
- return false;
+ return NULL;
TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier);
if (pFileNode && pFileNode->FirstChild())
- {
- strFile = pFileNode->FirstChild()->Value();
- return true;
- }
+ return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir, pFileNode->FirstChild()->Value()));
- return false;
+ return NULL;
}
// \brief Enable/Disable nav sounds
@@ -413,38 +345,38 @@ void CGUIAudioManager::Enable(bool bEnable)
bEnable = false;
CSingleLock lock(m_cs);
-
m_bEnabled = bEnable;
-
- if (bEnable)
- Initialize(CAudioContext::DEFAULT_DEVICE);
- else
- DeInitialize(CAudioContext::DEFAULT_DEVICE);
}
// \brief Sets the volume of all playing sounds
-void CGUIAudioManager::SetVolume(int iLevel)
+void CGUIAudioManager::SetVolume(float level)
{
CSingleLock lock(m_cs);
- if (m_actionSound)
- m_actionSound->SetVolume(iLevel);
-
- windowSoundsMap::iterator it=m_windowSounds.begin();
- while (it!=m_windowSounds.end())
{
- if (it->second)
- it->second->SetVolume(iLevel);
+ actionSoundMap::iterator it = m_actionSoundMap.begin();
+ while (it!=m_actionSoundMap.end())
+ {
+ if (it->second)
+ it->second->SetVolume(level);
+ ++it;
+ }
+ }
- ++it;
+ for(windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
+ {
+ if (it->second.initSound ) it->second.initSound ->SetVolume(level);
+ if (it->second.deInitSound) it->second.deInitSound->SetVolume(level);
}
- pythonSoundsMap::iterator it1=m_pythonSounds.begin();
- while (it1!=m_pythonSounds.end())
{
- if (it1->second)
- it1->second->SetVolume(iLevel);
+ pythonSoundsMap::iterator it = m_pythonSounds.begin();
+ while (it != m_pythonSounds.end())
+ {
+ if (it->second)
+ it->second->SetVolume(level);
- ++it1;
+ ++it;
+ }
}
}
diff --git a/xbmc/guilib/GUIAudioManager.h b/xbmc/guilib/GUIAudioManager.h
index 77c34af02d..7c6e6158b4 100644
--- a/xbmc/guilib/GUIAudioManager.h
+++ b/xbmc/guilib/GUIAudioManager.h
@@ -24,13 +24,14 @@
#include "threads/CriticalSection.h"
#include "utils/log.h"
#include "utils/StdString.h"
+#include "cores/AudioEngine/Interfaces/AESound.h"
#include <map>
// forward definitions
class CAction;
-class CGUISound;
class TiXmlNode;
+class IAESound;
enum WINDOW_SOUND { SOUND_INIT = 0, SOUND_DEINIT };
@@ -39,42 +40,44 @@ class CGUIAudioManager
class CWindowSounds
{
public:
- CStdString strInitFile;
- CStdString strDeInitFile;
+ IAESound *initSound;
+ IAESound *deInitSound;
+ };
+
+ class CSoundInfo
+ {
+ public:
+ int usage;
+ IAESound *sound;
};
public:
CGUIAudioManager();
- ~CGUIAudioManager();
+ ~CGUIAudioManager();
- void Initialize(int iDevice);
- void DeInitialize(int iDevice);
+ void Initialize();
+ void DeInitialize();
- bool Load();
+ bool Load();
+ void UnLoad();
- void PlayActionSound(const CAction& action);
- void PlayWindowSound(int id, WINDOW_SOUND event);
- void PlayPythonSound(const CStdString& strFileName);
- void FreeUnused();
+ void PlayActionSound(const CAction& action);
+ void PlayWindowSound(int id, WINDOW_SOUND event);
+ void PlayPythonSound(const CStdString& strFileName);
- void Enable(bool bEnable);
- void SetVolume(int iLevel);
- void Stop();
+ void Enable(bool bEnable);
+ void SetVolume(float level);
+ void Stop();
private:
- bool LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier, CStdString& strFile);
-
- typedef std::map<int, CStdString> actionSoundMap;
- typedef std::map<int, CWindowSounds> windowSoundMap;
-
- typedef std::map<CStdString, CGUISound*> pythonSoundsMap;
- typedef std::map<int, CGUISound*> windowSoundsMap;
+ typedef std::map<const CStdString, CSoundInfo> soundCache;
+ typedef std::map<int, IAESound* > actionSoundMap;
+ typedef std::map<int, CWindowSounds > windowSoundMap;
+ typedef std::map<const CStdString, IAESound* > pythonSoundsMap;
+ soundCache m_soundCache;
actionSoundMap m_actionSoundMap;
windowSoundMap m_windowSoundMap;
-
- CGUISound* m_actionSound;
- windowSoundsMap m_windowSounds;
pythonSoundsMap m_pythonSounds;
CStdString m_strMediaDir;
@@ -82,6 +85,10 @@ private:
bool m_bEnabled;
CCriticalSection m_cs;
+
+ IAESound* LoadSound(const CStdString &filename);
+ void FreeSound(IAESound *sound);
+ IAESound* LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier);
};
extern CGUIAudioManager g_audioManager;
diff --git a/xbmc/guilib/GUISound.cpp b/xbmc/guilib/GUISound.cpp
deleted file mode 100644
index 316792ba5a..0000000000
--- a/xbmc/guilib/GUISound.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "system.h"
-#include "GUISound.h"
-#include "AudioContext.h"
-#include "settings/Settings.h"
-#include "filesystem/File.h"
-#include "utils/log.h"
-#ifdef HAS_SDL_AUDIO
-#include <SDL/SDL_mixer.h>
-#endif
-#include "filesystem/SpecialProtocol.h"
-#ifndef HAS_SDL_AUDIO
-
-typedef struct
-{
- char chunk_id[4];
- long chunksize;
-} WAVE_CHUNK;
-
-typedef struct
-{
- char riff[4];
- long filesize;
- char rifftype[4];
-} WAVE_RIFFHEADER;
-#else
-#define GUI_SOUND_CHANNEL 0
-#endif
-
-CGUISound::CGUISound()
-{
- m_soundBuffer=NULL;
-}
-
-CGUISound::~CGUISound()
-{
-#ifdef _WIN32
- FreeBuffer();
-#elif defined(HAS_SDL_AUDIO)
- Mix_FreeChunk(m_soundBuffer);
-#endif
-}
-
-// \brief Loads a wav file by filename
-bool CGUISound::Load(const CStdString& strFile)
-{
-#ifdef _WIN32
- LPBYTE pbData=NULL;
- WAVEFORMATEX wfx;
- int size=0;
- if (!LoadWav(strFile, &wfx, &pbData, &size))
- return false;
-
- bool bReady=(CreateBuffer(&wfx, size) && FillBuffer(pbData, size));
-
- if (!bReady)
- FreeBuffer();
-
- delete[] pbData;
-
- return bReady;
-#elif defined(HAS_SDL_AUDIO)
- m_soundBuffer = Mix_LoadWAV(CSpecialProtocol::TranslatePath(strFile));
- if (!m_soundBuffer)
- return false;
-
- return true;
-#else
- return false;
-#endif
-}
-
-// \brief Starts playback of the sound
-void CGUISound::Play()
-{
- if (m_soundBuffer)
- {
-#ifdef _WIN32
- m_soundBuffer->Play(0, 0, 0);
-#elif defined(HAS_SDL_AUDIO)
- Mix_PlayChannel(GUI_SOUND_CHANNEL, m_soundBuffer, 0);
-#endif
- }
-}
-
-// \brief returns true if the sound is playing
-bool CGUISound::IsPlaying()
-{
-#ifdef _WIN32
- if (m_soundBuffer)
- {
- DWORD dwStatus;
- m_soundBuffer->GetStatus(&dwStatus);
- return (dwStatus & DSBSTATUS_PLAYING);
- }
-
- return false;
-#elif defined(HAS_SDL_AUDIO)
- return Mix_Playing(GUI_SOUND_CHANNEL) != 0;
-#else
- return false;
-#endif
-}
-
-// \brief Stops playback if the sound
-void CGUISound::Stop()
-{
- if (m_soundBuffer)
- {
-#ifdef _WIN32
- m_soundBuffer->Stop();
-#elif defined(HAS_SDL_AUDIO)
- Mix_HaltChannel(GUI_SOUND_CHANNEL);
-#endif
- Wait();
- }
-}
-
-// \brief Sets the volume of the sound
-void CGUISound::SetVolume(int level)
-{
- if (m_soundBuffer)
- {
-#ifdef _WIN32
- m_soundBuffer->SetVolume(level);
-#elif defined(HAS_SDL_AUDIO)
- Mix_Volume(GUI_SOUND_CHANNEL, level);
-#endif
- }
-}
-
-void CGUISound::Wait(uint32_t millis/*=500*/)
-{
- millis /= 10;
- while (IsPlaying() && millis--)
- Sleep(10);
-}
-
-#ifdef _WIN32
-bool CGUISound::CreateBuffer(LPWAVEFORMATEX wfx, int iLength)
-{
- // Set up DSBUFFERDESC structure
- DSBUFFERDESC dsbdesc;
- memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize=sizeof(DSBUFFERDESC);
- // directsound requires ctrlvolume to be set
- dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME;
- dsbdesc.dwBufferBytes=iLength;
- dsbdesc.lpwfxFormat=wfx;
-
- LPDIRECTSOUND directSound=g_audioContext.GetDirectSoundDevice();
- if (!directSound)
- return false;
-
- // Create buffer
- if (FAILED(directSound->CreateSoundBuffer(&dsbdesc, &m_soundBuffer, NULL)))
- {
- m_soundBuffer = NULL;
- CLog::Log(LOGERROR, __FUNCTION__" Creating sound buffer failed!");
- return false;
- }
-
- // Make effects as loud as possible
- m_soundBuffer->SetVolume(g_settings.m_nVolumeLevel);
-
- return true;
-}
-
-bool CGUISound::FillBuffer(LPBYTE pbData, int iLength)
-{
- if (!m_soundBuffer)
- return false;
-
- LPVOID lpvWrite;
- DWORD dwLength;
-
- if (SUCCEEDED(m_soundBuffer->Lock(0, 0, &lpvWrite, &dwLength, NULL, NULL, DSBLOCK_ENTIREBUFFER)))
- {
- memcpy(lpvWrite, pbData, iLength);
- m_soundBuffer->Unlock(lpvWrite, dwLength, NULL, 0);
- return true;
- }
-
- CLog::Log(LOGERROR, __FUNCTION__" Filling sound buffer failed!");
-
- return false;
-}
-
-void CGUISound::FreeBuffer()
-{
- if (IsPlaying())
- Stop();
-
- SAFE_RELEASE(m_soundBuffer);
-}
-
-bool CGUISound::LoadWav(const CStdString& strFile, WAVEFORMATEX* wfx, LPBYTE* ppWavData, int* pDataSize)
-{
- XFILE::CFile file;
- if (!file.Open(strFile))
- return false;
-
- // read header
- WAVE_RIFFHEADER riffh;
- file.Read(&riffh, sizeof(WAVE_RIFFHEADER));
-
- // file valid?
- if (strncmp(riffh.riff, "RIFF", 4)!=0 && strncmp(riffh.rifftype, "WAVE", 4)!=0)
- {
- file.Close();
- return false;
- }
-
- long offset=0;
- offset += sizeof(WAVE_RIFFHEADER);
- offset -= sizeof(WAVE_CHUNK);
-
- // parse chunks
- do
- {
- WAVE_CHUNK chunk;
-
- // always seeking to the start of a chunk
- file.Seek(offset + sizeof(WAVE_CHUNK), SEEK_SET);
- file.Read(&chunk, sizeof(WAVE_CHUNK));
-
- if (!strncmp(chunk.chunk_id, "fmt ", 4))
- { // format chunk
- memset(wfx, 0, sizeof(WAVEFORMATEX));
- file.Read(wfx, 16);
- // we only need 16 bytes of the fmt chunk
- if (chunk.chunksize-16>0)
- file.Seek(chunk.chunksize-16, SEEK_CUR);
- }
- else if (!strncmp(chunk.chunk_id, "data", 4))
- { // data chunk
- *ppWavData=new BYTE[chunk.chunksize+1];
- file.Read(*ppWavData, chunk.chunksize);
- *pDataSize=chunk.chunksize;
-
- if (chunk.chunksize & 1)
- offset++;
- }
- else
- { // other chunk - unused, just skip
- file.Seek(chunk.chunksize, SEEK_CUR);
- }
-
- offset+=(chunk.chunksize+sizeof(WAVE_CHUNK));
-
- if (offset & 1)
- offset++;
-
- } while (offset+(int)sizeof(WAVE_CHUNK) < riffh.filesize);
-
- file.Close();
- return (*ppWavData!=NULL);
-}
-
-#endif
diff --git a/xbmc/guilib/Makefile.in b/xbmc/guilib/Makefile.in
index 8bbbd39bed..d824caa797 100644
--- a/xbmc/guilib/Makefile.in
+++ b/xbmc/guilib/Makefile.in
@@ -1,5 +1,4 @@
SRCS=AnimatedGif.cpp \
- AudioContext.cpp \
DDSImage.cpp \
DirectXGraphics.cpp \
DirtyRegionSolvers.cpp \
@@ -49,7 +48,6 @@ SRCS=AnimatedGif.cpp \
GUISelectButtonControl.cpp \
GUISettingsSliderControl.cpp \
GUISliderControl.cpp \
- GUISound.cpp \
GUISpinControl.cpp \
GUISpinControlEx.cpp \
GUIStandardWindow.cpp \
diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp
index 409fdf585f..fa49de71ef 100644
--- a/xbmc/interfaces/Builtins.cpp
+++ b/xbmc/interfaces/Builtins.cpp
@@ -827,7 +827,7 @@ int CBuiltins::Execute(const CStdString& execString)
int oldVolume = g_application.GetVolume();
int volume = atoi(parameter.c_str());
- g_application.SetVolume(volume);
+ g_application.SetVolume((float)volume);
if(oldVolume != volume)
{
if(params.size() > 1 && params[1].Equals("showVolumeBar"))
diff --git a/xbmc/interfaces/http-api/XBMChttp.cpp b/xbmc/interfaces/http-api/XBMChttp.cpp
index b1b9503d67..c3db3f2d51 100644
--- a/xbmc/interfaces/http-api/XBMChttp.cpp
+++ b/xbmc/interfaces/http-api/XBMChttp.cpp
@@ -1597,7 +1597,7 @@ int CXbmcHttp::xbmcSetVolume(int numParas, CStdString paras[])
else
{
int iPercent = atoi(paras[0].c_str());
- g_application.SetVolume(iPercent);
+ g_application.SetVolume((float)iPercent, true);
return SetResponse(openTag+"OK");
}
}
@@ -2612,11 +2612,7 @@ int CXbmcHttp::xbmcSTSetting(int numParas, CStdString paras[])
else if (paras[i]=="httpapibroadcastlevel")
tmp.Format("%i",g_settings.m_HttpApiBroadcastLevel);
else if (paras[i]=="volumelevel")
- tmp.Format("%i",g_settings.m_nVolumeLevel);
- else if (paras[i]=="dynamicrangecompressionlevel")
- tmp.Format("%i",g_settings.m_dynamicRangeCompressionLevel);
- else if (paras[i]=="premutevolumelevel")
- tmp.Format("%i",g_settings.m_iPreMuteVolumeLevel);
+ tmp.Format("%i",g_application.GetVolume());
else if (paras[i]=="systemtimetotalup")
tmp.Format("%i",g_settings.m_iSystemTimeTotalUp);
else if (paras[i]=="mute")
diff --git a/xbmc/interfaces/json-rpc/ApplicationOperations.cpp b/xbmc/interfaces/json-rpc/ApplicationOperations.cpp
index 8b17ef8b10..3f8110f592 100644
--- a/xbmc/interfaces/json-rpc/ApplicationOperations.cpp
+++ b/xbmc/interfaces/json-rpc/ApplicationOperations.cpp
@@ -58,7 +58,7 @@ JSONRPC_STATUS CApplicationOperations::SetVolume(const CStdString &method, ITran
int oldVolume = g_application.GetVolume();
int volume = (int)parameterObject["volume"].asInteger();
- g_application.SetVolume(volume);
+ g_application.SetVolume((float)volume, true);
up = oldVolume < volume;
}
diff --git a/xbmc/network/AirPlayServer.cpp b/xbmc/network/AirPlayServer.cpp
index e8da0e51f0..5cf254b27c 100644
--- a/xbmc/network/AirPlayServer.cpp
+++ b/xbmc/network/AirPlayServer.cpp
@@ -688,7 +688,7 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
else if (uri == "/volume")
{
const char* found = strstr(queryString.c_str(), "volume=");
- double volume = found ? (double)(atof(found + strlen("volume="))) : 0;
+ float volume = found ? (float)strtod(found + strlen("volume="), NULL) : 0;
CLog::Log(LOGDEBUG, "AIRPLAY: got request %s with volume %f", uri.c_str(), volume);
diff --git a/xbmc/network/UPnP.cpp b/xbmc/network/UPnP.cpp
index 85a8bdc790..912aa75f4b 100644
--- a/xbmc/network/UPnP.cpp
+++ b/xbmc/network/UPnP.cpp
@@ -1672,11 +1672,10 @@ CUPnPRenderer::UpdateState()
int volume;
if (g_settings.m_bMute) {
rct->SetStateVariable("Mute", "1");
- volume = g_settings.m_iPreMuteVolumeLevel;
} else {
rct->SetStateVariable("Mute", "0");
- volume = g_application.GetVolume();
}
+ volume = g_application.GetVolume();
buffer.Format("%d", volume);
rct->SetStateVariable("Volume", buffer.c_str());
@@ -1991,7 +1990,7 @@ CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
{
NPT_String volume;
NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
- g_application.SetVolume(atoi((const char*)volume));
+ g_application.SetVolume((float)strtod((const char*)volume, NULL));
return NPT_SUCCESS;
}
diff --git a/xbmc/osx/CocoaInterface.h b/xbmc/osx/CocoaInterface.h
index 0376cbe620..2d7358e1af 100644
--- a/xbmc/osx/CocoaInterface.h
+++ b/xbmc/osx/CocoaInterface.h
@@ -73,8 +73,6 @@ extern "C"
const char *Cocoa_Paste() ;
- void Cocoa_ResetAudioDevices();
-
#ifdef __cplusplus
}
#endif
diff --git a/xbmc/osx/CocoaInterface.mm b/xbmc/osx/CocoaInterface.mm
index 21f312de02..e1a63b4745 100644
--- a/xbmc/osx/CocoaInterface.mm
+++ b/xbmc/osx/CocoaInterface.mm
@@ -40,7 +40,6 @@
#import "AutoPool.h"
-#import "CoreAudio.h"
// hack for Cocoa_GL_ResizeWindow
//extern "C" void SDL_SetWidthHeight(int w, int h);
@@ -626,50 +625,4 @@ const char *Cocoa_Paste()
return NULL;
}
-void Cocoa_ResetAudioDevices()
-{
- // Reset any devices with an AC3/DTS/SPDIF stream back to a Linear PCM
- // so that they can become a default output device
- CoreAudioDeviceList deviceList;
- CCoreAudioHardware::GetOutputDevices(&deviceList);
- for (CoreAudioDeviceList::iterator d = deviceList.begin(); d != deviceList.end(); d++)
- {
- CCoreAudioDevice device(*d);
- AudioStreamIdList streamList;
- if (device.GetStreams(&streamList))
- {
- for (AudioStreamIdList::iterator s = streamList.begin(); s != streamList.end(); s++)
- {
- CCoreAudioStream stream;
- if (stream.Open(*s))
- {
- AudioStreamBasicDescription currentFormat;
- if (stream.GetPhysicalFormat(&currentFormat))
- {
- if (currentFormat.mFormatID == 'IAC3' || currentFormat.mFormatID == kAudioFormat60958AC3)
- {
- StreamFormatList formatList;
- if (stream.GetAvailablePhysicalFormats(&formatList))
- {
- for (StreamFormatList::iterator f = formatList.begin(); f != formatList.end(); f++)
- {
- if ((*f).mFormat.mFormatID == kAudioFormatLinearPCM)
- {
- if (stream.SetPhysicalFormat(&(*f).mFormat))
- {
- sleep(1);
- break;
- }
- }
- }
- }
- }
- }
- stream.Close();
- }
- }
- }
- }
-}
-
#endif
diff --git a/xbmc/osx/CoreAudio.cpp b/xbmc/osx/CoreAudio.cpp
deleted file mode 100644
index 0a45c2903d..0000000000
--- a/xbmc/osx/CoreAudio.cpp
+++ /dev/null
@@ -1,1971 +0,0 @@
-#ifdef __APPLE__
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#if !defined(__arm__)
-#include <CoreServices/CoreServices.h>
-
-#include "CoreAudio.h"
-#include "PlatformDefs.h"
-#include "utils/log.h"
-#include "math.h"
-
-#define MAX_CHANNEL_LABEL 15
-const char* g_ChannelLabels[] =
-{
- "Unused", // kAudioChannelLabel_Unused
- "Left", // kAudioChannelLabel_Left
- "Right", // kAudioChannelLabel_Right
- "Center", // kAudioChannelLabel_Center
- "LFE", // kAudioChannelLabel_LFEScreen
- "Side Left", // kAudioChannelLabel_LeftSurround
- "Side Right", // kAudioChannelLabel_RightSurround
- "Left Center", // kAudioChannelLabel_LeftCenter
- "Right Center", // kAudioChannelLabel_RightCenter
- "Back Center", // kAudioChannelLabel_CenterSurround
- "Back Left", // kAudioChannelLabel_LeftSurroundDirect
- "Back Right", // kAudioChannelLabel_RightSurroundDirect
- "Top Center", // kAudioChannelLabel_TopCenterSurround
- "Top Back Left", // kAudioChannelLabel_VerticalHeightLeft
- "Top Back Center", // kAudioChannelLabel_VerticalHeightCenter
- "Top Back Right", // kAudioChannelLabel_VerticalHeightRight
-};
-
-char* UInt32ToFourCC(UInt32* pVal) // NOT NULL TERMINATED! Modifies input value.
-{
- UInt32 inVal = *pVal;
- char* pIn = (char*)&inVal;
- char* fourCC = (char*)pVal;
- fourCC[3] = pIn[0];
- fourCC[2] = pIn[1];
- fourCC[1] = pIn[2];
- fourCC[0] = pIn[3];
- return fourCC;
-}
-
-const char* StreamDescriptionToString(AudioStreamBasicDescription desc, CStdString& str)
-{
- UInt32 formatId = desc.mFormatID;
- char* fourCC = UInt32ToFourCC(&formatId);
-
- switch (desc.mFormatID)
- {
- case kAudioFormatLinearPCM:
- str.Format("[%4.4s] %s%sInterleaved %u Channel %u-bit %s %s(%uHz)",
- fourCC,
- (desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "Mixable ",
- (desc.mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? "Non-" : "",
- desc.mChannelsPerFrame,
- desc.mBitsPerChannel,
- (desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Floating Point" : "Signed Integer",
- (desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE",
- (UInt32)desc.mSampleRate);
- break;
- case kAudioFormatAC3:
- str.Format("[%4.4s] AC-3/DTS (%uHz)",
- fourCC,
- (desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE",
- (UInt32)desc.mSampleRate);
- break;
- case kAudioFormat60958AC3:
- str.Format("[%4.4s] AC-3/DTS for S/PDIF (%uHz)", fourCC, (UInt32)desc.mSampleRate);
- break;
- default:
- str.Format("[%4.4s]", fourCC);
- break;
- }
- return str.c_str();
-}
-
-CCoreAudioChannelLayout::CCoreAudioChannelLayout() :
- m_pLayout(NULL)
-{
-
-}
-
-CCoreAudioChannelLayout::CCoreAudioChannelLayout(AudioChannelLayout& layout) :
- m_pLayout(NULL)
-{
- CopyLayout(layout);
-}
-
-CCoreAudioChannelLayout::~CCoreAudioChannelLayout()
-{
- if (m_pLayout)
- free(m_pLayout);
-}
-
-bool CCoreAudioChannelLayout::CopyLayout(AudioChannelLayout& layout)
-{
- if (m_pLayout)
- free(m_pLayout);
- m_pLayout = NULL;
-
- // This method always produces a layout with a ChannelDescriptions structure
-
- OSStatus ret = 0;
- UInt32 channels = GetChannelCountForLayout(layout);
- UInt32 size = sizeof(AudioChannelLayout) + (channels - kVariableLengthArray) * sizeof(AudioChannelDescription);
-
- if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) // We can copy the whole layout
- {
- m_pLayout = (AudioChannelLayout*)malloc(size);
- memcpy(m_pLayout, &layout, size);
- }
- else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Deconstruct the bitmap to get the layout
- {
- UInt32 propSize = 0;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize);
- m_pLayout = (AudioChannelLayout*)malloc(propSize);
- ret = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize, m_pLayout);
- m_pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- }
- else // Convert the known layout to a custom layout
- {
- UInt32 propSize = 0;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize);
- m_pLayout = (AudioChannelLayout*)malloc(propSize);
- ret = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize, m_pLayout);
- m_pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- }
-
- return (ret == noErr);
-}
-
-bool CCoreAudioChannelLayout::SetLayout(AudioChannelLayoutTag layoutTag)
-{
- UInt32 propSize = 0;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layoutTag), &layoutTag, &propSize);
- m_pLayout = (AudioChannelLayout*)malloc(propSize);
- OSStatus ret = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layoutTag), &layoutTag, &propSize, m_pLayout);
- m_pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- return (ret == noErr);
-}
-
-AudioChannelLabel CCoreAudioChannelLayout::GetChannelLabel(UInt32 index)
-{
- if (!m_pLayout || (index >= m_pLayout->mNumberChannelDescriptions))
- return kAudioChannelLabel_Unknown;
-
- return m_pLayout->mChannelDescriptions[index].mChannelLabel;
-}
-
-UInt32 CCoreAudioChannelLayout::GetChannelCountForLayout(AudioChannelLayout& layout)
-{
- UInt32 channels = 0;
- if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Channels are in fixed-order('USB Order'), any combination
- {
- UInt32 bitmap = layout.mChannelBitmap;
- for (UInt32 c = 0; c < (sizeof(layout.mChannelBitmap) << 3); c++)
- {
- if (bitmap & 0x1)
- channels++;
- bitmap >>= 1;
- }
- }
- else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) // Channels are in any order, any combination
- channels = layout.mNumberChannelDescriptions;
- else // Channels are in a predefined order and combination
- channels = AudioChannelLayoutTag_GetNumberOfChannels(layout.mChannelLayoutTag);
-
- return channels;
-}
-
-UInt32 CCoreAudioChannelLayout::GetChannelCount()
-{
- if (m_pLayout)
- return GetChannelCountForLayout(*m_pLayout);
- return 0;
-}
-
-const char* CCoreAudioChannelLayout::ChannelLabelToString(UInt32 label)
-{
- if (label > MAX_CHANNEL_LABEL)
- return "Unknown";
- return g_ChannelLabels[label];
-}
-
-const char* CCoreAudioChannelLayout::ChannelLayoutToString(AudioChannelLayout& layout, CStdString& str)
-{
- AudioChannelLayout* pLayout = NULL;
-
- if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions)
- {
- pLayout = &layout;
- }
- else if (layout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) // Deconstruct the bitmap to get the layout
- {
- UInt32 propSize = 0;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize);
- pLayout = (AudioChannelLayout*)calloc(propSize, 1);
- AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap, sizeof(layout.mChannelBitmap), &layout.mChannelBitmap, &propSize, pLayout);
- }
- else // Predefinied layout 'tag'
- {
- UInt32 propSize = 0;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize);
- pLayout = (AudioChannelLayout*)calloc(propSize, 1);
- AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag, sizeof(layout.mChannelLayoutTag), &layout.mChannelLayoutTag, &propSize, pLayout);
- }
-
- for (UInt32 c = 0; c < pLayout->mNumberChannelDescriptions; c++)
- {
- str += "[";
- str += ChannelLabelToString(pLayout->mChannelDescriptions[c].mChannelLabel);
- str += "] ";
- }
-
- if (layout.mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
- free(pLayout);
-
- return str.c_str();
-}
-
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CCoreAudioHardware
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-AudioDeviceID CCoreAudioHardware::FindAudioDevice(CStdString searchName)
-{
- if (!searchName.length())
- return 0;
-
- UInt32 size = 0;
- AudioDeviceID deviceId = 0;
- OSStatus ret;
-
- if (searchName.Equals("Default Output Device"))
- {
- AudioDeviceID defaultDevice = GetDefaultOutputDevice();
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
- "Returning default device [0x%04x].", (unsigned int)defaultDevice);
- return defaultDevice;
- }
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
- "Searching for device - %s.", searchName.c_str());
-
- // Obtain a list of all available audio devices
- AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
- UInt32 deviceCount = size / sizeof(AudioDeviceID);
- AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
- ret = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, pDevices);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
- "Unable to retrieve the list of available devices. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- delete[] pDevices;
- return 0;
- }
-
- // Attempt to locate the requested device
- CStdString deviceName;
- for (UInt32 dev = 0; dev < deviceCount; dev++)
- {
- CCoreAudioDevice device;
- device.Open((pDevices[dev]));
- device.GetName(deviceName);
- UInt32 totalChannels = device.GetTotalOutputChannels();
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
- "Device[0x%04x] - Name: '%s', Total Ouput Channels: %u. ",
- (unsigned int)pDevices[dev], deviceName.c_str(), (unsigned int)totalChannels);
- if (searchName.Equals(deviceName))
- deviceId = pDevices[dev];
- if (deviceId)
- break;
- }
- delete[] pDevices;
-
- return deviceId;
-}
-
-AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
-{
- UInt32 size = sizeof(AudioDeviceID);
- AudioDeviceID deviceId = 0;
- OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &deviceId);
- if (ret || !deviceId) // outputDevice is set to 0 if there is no audio device available, or if the default device is set to an encoded format
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice: "
- "Unable to identify default output device. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0;
- }
- return deviceId;
-}
-
-UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList* pList)
-{
- if (!pList)
- return 0;
-
- // Obtain a list of all available audio devices
- UInt32 found = 0;
- UInt32 size = 0;
- AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
- UInt32 deviceCount = size / sizeof(AudioDeviceID);
- AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
- OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, pDevices);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices: "
- "Unable to retrieve the list of available devices. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- else
- {
- for (UInt32 dev = 0; dev < deviceCount; dev++)
- {
- CCoreAudioDevice device(pDevices[dev]);
- if (device.GetTotalOutputChannels() == 0)
- continue;
- found++;
- pList->push_back(pDevices[dev]);
- }
- }
- delete[] pDevices;
- return found;
-}
-
-bool CCoreAudioHardware::GetAutoHogMode()
-{
- UInt32 val = 0;
- UInt32 size = sizeof(val);
- OSStatus ret = AudioHardwareGetProperty(kAudioHardwarePropertyHogModeIsAllowed, &size, &val);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetAutoHogMode: "
- "Unable to get auto 'hog' mode. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return (val == 1);
-}
-
-void CCoreAudioHardware::SetAutoHogMode(bool enable)
-{
- UInt32 val = enable ? 1 : 0;
- OSStatus ret = AudioHardwareSetProperty(kAudioHardwarePropertyHogModeIsAllowed, sizeof(val), &val);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: "
- "Unable to set auto 'hog' mode. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CCoreAudioDevice
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CCoreAudioDevice::CCoreAudioDevice() :
- m_DeviceId(0),
- m_Started(false),
- m_Hog(-1),
- m_MixerRestore(-1),
- m_IoProc(NULL),
- m_SampleRateRestore(0.0f),
- m_BufferSizeRestore(0)
-{
-
-}
-
-CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
- m_DeviceId(deviceId),
- m_Started(false),
- m_Hog(-1),
- m_MixerRestore(-1),
- m_IoProc(NULL),
- m_SampleRateRestore(0.0f),
- m_BufferSizeRestore(0)
-{
- Open(m_DeviceId);
-}
-
-CCoreAudioDevice::~CCoreAudioDevice()
-{
- Close();
-}
-
-bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
-{
- m_DeviceId = deviceId;
- m_BufferSizeRestore = GetBufferSize();
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::Open: "
- "Opened device 0x%04x. Buffer size is %d",
- (unsigned int)m_DeviceId, (int)m_BufferSizeRestore);
- return true;
-}
-
-void CCoreAudioDevice::Close()
-{
- if (!m_DeviceId)
- return;
-
- Stop(); // Stop the device if it was started
-
- RemoveIOProc(); // Unregister the IOProc if we have one
-
- SetHogStatus(false);
- if (m_MixerRestore > -1) // We changed the mixer status
- SetMixingSupport((m_MixerRestore ? true : false));
- m_MixerRestore = -1;
-
- if (m_SampleRateRestore != 0.0f)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::Close: Restoring original nominal samplerate.");
- SetNominalSampleRate(m_SampleRateRestore);
- m_SampleRateRestore =0.0f;
- }
-
- if (m_BufferSizeRestore != GetBufferSize()) // Put this back the way we found it...
- {
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::Close: Restoring original buffer size.");
- SetBufferSize(m_BufferSizeRestore);
- m_BufferSizeRestore = 0;
- }
-
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::Close: Closed device 0x%04x", (unsigned int)m_DeviceId);
- m_DeviceId = 0;
- m_IoProc = NULL; // Probably uneccessary since this is reset in RemoveIOProc
-
-}
-
-void CCoreAudioDevice::Start()
-{
- if (!m_DeviceId || m_Started)
- return;
-
- OSStatus ret = AudioDeviceStart(m_DeviceId, m_IoProc);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::Start: "
- "Unable to start device. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- else
- m_Started = true;
-}
-
-void CCoreAudioDevice::Stop()
-{
- if (!m_DeviceId || !m_Started)
- return;
-
- OSStatus ret = AudioDeviceStop(m_DeviceId, m_IoProc);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: "
- "Unable to stop device. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- m_Started = false;
-}
-
-bool CCoreAudioDevice::AddIOProc(AudioDeviceIOProc ioProc, void* pCallbackData)
-{
- if (!m_DeviceId || m_IoProc) // Only one IOProc at a time
- return false;
-
- OSStatus ret = AudioDeviceAddIOProc(m_DeviceId, ioProc, pCallbackData);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::Stop: "
- "Unable to add IOProc. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- m_IoProc = ioProc;
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::AddIOProc: "
- "IOProc set for device 0x%04x", (unsigned int)m_DeviceId);
- return true;
-}
-
-void CCoreAudioDevice::RemoveIOProc()
-{
- if (!m_DeviceId || !m_IoProc)
- return;
-
- Stop();
-
- OSStatus ret = AudioDeviceRemoveIOProc(m_DeviceId, m_IoProc);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::RemoveIOProc: "
- "Unable to remove IOProc. Error = 0x%08x (%4.4s).",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- else
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::AddIOProc: "
- "IOProc removed for device 0x%04x", (unsigned int)m_DeviceId);
- m_IoProc = NULL; // Clear the reference no matter what
-}
-
-const char* CCoreAudioDevice::GetName(CStdString& name)
-{
- if (!m_DeviceId)
- return NULL;
-
- UInt32 size = 0;
- AudioDeviceGetPropertyInfo(m_DeviceId,0, false, kAudioDevicePropertyDeviceName, &size, NULL); // TODO: Change to kAudioObjectPropertyObjectName
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyDeviceName, &size, name.GetBufferSetLength(size));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
- "Unable to get device name - id: 0x%04x Error = 0x%08x (%4.4s)",
- (unsigned int)m_DeviceId, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return NULL;
- }
- return name.c_str();
-}
-
-const char* CCoreAudioDevice::GetName()
-{
- // Use internal storage
- return GetName(m_Name);
-}
-
-
-UInt32 CCoreAudioDevice::GetTotalOutputChannels()
-{
- if (!m_DeviceId)
- return 0;
- UInt32 channels = 0;
- UInt32 size = 0;
- AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyStreamConfiguration, &size, NULL);
- AudioBufferList* pList = (AudioBufferList*)malloc(size);
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyStreamConfiguration, &size, pList);
- if (!ret)
- for(UInt32 buffer = 0; buffer < pList->mNumberBuffers; ++buffer)
- channels += pList->mBuffers[buffer].mNumberChannels;
- else
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetTotalOutputChannels: "
- "Unable to get total device output channels - id: 0x%04x Error = 0x%08x (%4.4s)",
- (unsigned int)m_DeviceId, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::GetTotalOutputChannels: "
- "Found %u channels in %u buffers", (unsigned int)channels, (unsigned int)pList->mNumberBuffers);
- free(pList);
- return channels;
-}
-
-bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
-{
- if (!pList || !m_DeviceId)
- return false;
-
- UInt32 propertySize = 0;
- Boolean writable = false;
- OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false, kAudioDevicePropertyStreams, &propertySize, &writable);
- if (ret)
- return false;
- UInt32 streamCount = propertySize / sizeof(AudioStreamID);
- AudioStreamID* pStreamList = new AudioStreamID[streamCount];
- ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyStreams, &propertySize, pStreamList);
- if (!ret)
- {
- for (UInt32 stream = 0; stream < streamCount; stream++)
- pList->push_back(pStreamList[stream]);
- }
- delete[] pStreamList;
- return (ret == noErr);
-}
-
-
-bool CCoreAudioDevice::IsRunning()
-{
- UInt32 isRunning = false;
- UInt32 size = sizeof(isRunning);
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyDeviceIsRunning, &size, &isRunning);
- if (ret)
- return false;
- return (isRunning != 0);
-}
-
-bool CCoreAudioDevice::SetHogStatus(bool hog)
-{
- // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
- // is a toggle and the only way to tell if you do get hog mode is to compare
- // the returned pid against getpid, if the match, you have hog mode, if not you don't.
- if (!m_DeviceId)
- return false;
-
- if (hog)
- {
- if (m_Hog == -1) // Not already set
- {
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: "
- "Setting 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
- OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyHogMode, sizeof(m_Hog), &m_Hog);
- if (ret || m_Hog != getpid())
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
- "Unable to set 'hog' status. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: "
- "Successfully set 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
- }
- }
- else
- {
- if (m_Hog > -1) // Currently Set
- {
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetHogStatus: "
- "Releasing 'hog' status on device 0x%04x", (unsigned int)m_DeviceId);
- pid_t hogPid = -1;
- OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyHogMode, sizeof(hogPid), &hogPid);
- if (ret || hogPid == getpid())
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
- "Unable to release 'hog' status. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- m_Hog = hogPid; // Reset internal state
- }
- }
- return true;
-}
-
-pid_t CCoreAudioDevice::GetHogStatus()
-{
- if (!m_DeviceId)
- return false;
-
- pid_t hogPid = -1;
- UInt32 size = sizeof(hogPid);
- AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertyHogMode, &size, &hogPid);
-
- return hogPid;
-}
-
-bool CCoreAudioDevice::SetMixingSupport(bool mix)
-{
- if (!m_DeviceId)
- return false;
- int restore = -1;
- if (m_MixerRestore == -1) // This is our first change to this setting. Store the original setting for restore
- restore = (GetMixingSupport() ? 1 : 0);
- UInt32 mixEnable = mix ? 1 : 0;
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetMixingSupport: "
- "%sabling mixing for device 0x%04x", mix ? "En" : "Dis", (unsigned int)m_DeviceId);
- OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertySupportsMixing, sizeof(mixEnable), &mixEnable);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetMixingSupport: "
- "Unable to set MixingSupport to %s. Error = 0x%08x (%4.4s)",
- mix ? "'On'" : "'Off'", (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- if (m_MixerRestore == -1)
- m_MixerRestore = restore;
- return true;
-}
-
-bool CCoreAudioDevice::GetMixingSupport()
-{
- if (!m_DeviceId)
- return false;
- UInt32 val = 0;
- UInt32 size = sizeof(val);
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false, kAudioDevicePropertySupportsMixing, &size, &val);
- if (ret)
- return false;
- return (val > 0);
-}
-
-bool CCoreAudioDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
-{
- if (!m_DeviceId)
- return false;
-
- UInt32 propertySize = 0;
- Boolean writable = false;
- OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false,
- kAudioDevicePropertyPreferredChannelLayout, &propertySize, &writable);
- if (ret)
- return false;
-
- void* pBuf = malloc(propertySize);
- ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyPreferredChannelLayout, &propertySize, pBuf);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
- "Unable to retrieve preferred channel layout. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- else
- layout.CopyLayout(*((AudioChannelLayout*)pBuf)); // Copy the result into the caller's instance
- free(pBuf);
- return (ret == noErr);
-}
-
-bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
-{
- if (!pList || !m_DeviceId)
- return false;
-
- UInt32 propertySize = 0;
- Boolean writable = false;
- OSStatus ret = AudioDeviceGetPropertyInfo(m_DeviceId, 0, false,
- kAudioDevicePropertyDataSources, &propertySize, &writable);
- if (ret)
- return false;
- UInt32 sources = propertySize / sizeof(UInt32);
- UInt32* pSources = new UInt32[sources];
- ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyDataSources, &propertySize, pSources);
- if (!ret)
- for (UInt32 i = 0; i < sources; i++)
- pList->push_back(pSources[i]);;
- delete[] pSources;
- return (!ret);
-}
-
-Float64 CCoreAudioDevice::GetNominalSampleRate()
-{
- if (!m_DeviceId)
- return 0.0f;
-
- Float64 sampleRate = 0.0f;
- UInt32 size = sizeof(Float64);
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyNominalSampleRate, &size, &sampleRate);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetNominalSampleRate: "
- "Unable to retrieve current device sample rate. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return sampleRate;
-}
-
-bool CCoreAudioDevice::SetNominalSampleRate(Float64 sampleRate)
-{
- if (!m_DeviceId || sampleRate == 0.0f)
- return false;
-
- Float64 currentRate = GetNominalSampleRate();
- if (currentRate == sampleRate)
- return true; //No need to change
-
- UInt32 size = sizeof(Float64);
- OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sampleRate);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
- "Unable to set current device sample rate to %0.0f. Error = 0x%08x (%4.4s)",
- (float)sampleRate, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetNominalSampleRate: Changed device sample rate from %0.0f to %0.0f.", (float)currentRate, (float)sampleRate);
- if (m_SampleRateRestore == 0.0f)
- m_SampleRateRestore = currentRate;
-
- return true;
-}
-
-UInt32 CCoreAudioDevice::GetNumLatencyFrames()
-{
- UInt32 i_param, i_param_size, num_latency_frames = 0;
- if (!m_DeviceId)
- return 0;
-
- i_param_size = sizeof(uint32_t);
-
- // number of frames of latency in the AudioDevice
- if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyLatency, &i_param_size, &i_param))
- {
- num_latency_frames += i_param;
- }
-
- // number of frames in the IO buffers
- if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyBufferFrameSize, &i_param_size, &i_param))
- {
- num_latency_frames += i_param;
- }
-
- // number for frames in ahead the current hardware position that is safe to do IO
- if (noErr == AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertySafetyOffset, &i_param_size, &i_param))
- {
- num_latency_frames += i_param;
- }
-
- return(num_latency_frames);
-}
-
-UInt32 CCoreAudioDevice::GetBufferSize()
-{
- if (!m_DeviceId)
- return false;
-
- UInt32 size = 0;
- UInt32 propertySize = sizeof(size);
- OSStatus ret = AudioDeviceGetProperty(m_DeviceId, 0, false,
- kAudioDevicePropertyBufferFrameSize, &propertySize, &size);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetBufferSize: "
- "Unable to retrieve buffer size. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return size;
-}
-
-bool CCoreAudioDevice::SetBufferSize(UInt32 size)
-{
- if (!m_DeviceId)
- return false;
-
- UInt32 propertySize = sizeof(size);
- OSStatus ret = AudioDeviceSetProperty(m_DeviceId, NULL, 0, false,
- kAudioDevicePropertyBufferFrameSize, propertySize, &size);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: "
- "Unable to set buffer size. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
-
- if (GetBufferSize() != size)
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetBufferSize: Buffer size change not applied.");
- else
- CLog::Log(LOGDEBUG, "CCoreAudioDevice::SetBufferSize: Set buffer size to %d", (int)size);
-
- return (ret == noErr);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CCoreAudioStream
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CCoreAudioStream::CCoreAudioStream() :
-m_StreamId(0)
-{
- m_OriginalVirtualFormat.mFormatID = 0;
- m_OriginalPhysicalFormat.mFormatID = 0;
-}
-
-CCoreAudioStream::~CCoreAudioStream()
-{
- Close();
-}
-
-bool CCoreAudioStream::Open(AudioStreamID streamId)
-{
- m_StreamId = streamId;
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Open: Opened stream 0x%04x.", (unsigned int)m_StreamId);
- return true;
-}
-
-// TODO: Should it even be possible to change both the physical and virtual formats, since the devices do it themselves?
-void CCoreAudioStream::Close()
-{
- if (!m_StreamId)
- return;
-
- CStdString formatString;
-
- // Revert any format changes we made
- if (m_OriginalVirtualFormat.mFormatID && m_StreamId)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
- "Restoring original virtual format for stream 0x%04x. (%s)",
- (unsigned int)m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
- SetVirtualFormat(&m_OriginalVirtualFormat);
- }
- if (m_OriginalPhysicalFormat.mFormatID && m_StreamId)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
- "Restoring original physical format for stream 0x%04x. (%s)",
- (unsigned int)m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
- SetPhysicalFormat(&m_OriginalPhysicalFormat);
- }
-
- m_OriginalPhysicalFormat.mFormatID = 0;
- m_OriginalVirtualFormat.mFormatID = 0;
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", (unsigned int)m_StreamId);
- m_StreamId = 0;
-}
-
-UInt32 CCoreAudioStream::GetDirection()
-{
- if (!m_StreamId)
- return 0;
- UInt32 size = sizeof(UInt32);
- UInt32 val = 0;
- OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyDirection, &size, &val);
- if (ret)
- return 0;
- return val;
-}
-
-UInt32 CCoreAudioStream::GetTerminalType()
-{
- if (!m_StreamId)
- return 0;
- UInt32 size = sizeof(UInt32);
- UInt32 val = 0;
- OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyTerminalType, &size, &val);
- if (ret)
- return 0;
- return val;
-}
-
-UInt32 CCoreAudioStream::GetNumLatencyFrames()
-{
- UInt32 i_param, i_param_size, num_latency_frames = 0;
- if (!m_StreamId)
- return 0;
-
- i_param_size = sizeof(uint32_t);
-
- // number of frames of latency in the AudioStream
- if (noErr == AudioStreamGetProperty(m_StreamId, 0,
- kAudioStreamPropertyLatency, &i_param_size, &i_param))
- {
- num_latency_frames += i_param;
- }
-
- return(num_latency_frames);
-}
-
-bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioStreamGetProperty(m_StreamId, 0,
- kAudioStreamPropertyVirtualFormat, &size, pDesc);
- if (ret)
- return false;
- return true;
-}
-
-bool CCoreAudioStream::SetVirtualFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
- if (!m_OriginalVirtualFormat.mFormatID)
- {
- // Store the original format (as we found it) so that it can be restored later
- if (!GetVirtualFormat(&m_OriginalVirtualFormat))
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
- "Unable to retrieve current virtual format for stream 0x%04x.", (unsigned int)m_StreamId);
- return false;
- }
- }
- OSStatus ret = AudioStreamSetProperty(m_StreamId, NULL, 0,
- kAudioStreamPropertyVirtualFormat, sizeof(AudioStreamBasicDescription), pDesc);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
- "Unable to set virtual format for stream 0x%04x. Error = 0x%08x (%4.4s)",
- (unsigned int)m_StreamId, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioStreamGetProperty(m_StreamId, 0, kAudioStreamPropertyPhysicalFormat, &size, pDesc);
- if (ret)
- return false;
- return true;
-}
-
-bool CCoreAudioStream::SetPhysicalFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
- if (!m_OriginalPhysicalFormat.mFormatID)
- {
- // Store the original format (as we found it) so that it can be restored later
- if (!GetPhysicalFormat(&m_OriginalPhysicalFormat))
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
- "Unable to retrieve current physical format for stream 0x%04x.",
- (unsigned int)m_StreamId);
- return false;
- }
- }
- OSStatus ret = AudioStreamSetProperty(m_StreamId, NULL, 0,
- kAudioStreamPropertyPhysicalFormat, sizeof(AudioStreamBasicDescription), pDesc);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
- "Unable to set physical format for stream 0x%04x. Error = 0x%08x (%4.4s)",
- (unsigned int)m_StreamId, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- sleep(1); // For the change to take effect
- return true;
-}
-
-bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
-{
- if (!pList || !m_StreamId)
- return false;
-
- UInt32 propertySize = 0;
- Boolean writable = false;
- OSStatus ret = AudioStreamGetPropertyInfo(m_StreamId, 0,
- kAudioStreamPropertyAvailableVirtualFormats, &propertySize, &writable);
- if (ret)
- return false;
- UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription* pFormatList = new AudioStreamRangedDescription[formatCount];
- ret = AudioStreamGetProperty(m_StreamId, 0,
- kAudioStreamPropertyAvailableVirtualFormats, &propertySize, pFormatList);
- if (!ret)
- {
- for (UInt32 format = 0; format < formatCount; format++)
- pList->push_back(pFormatList[format]);
- }
- delete[] pFormatList;
- return (ret == noErr);
-}
-
-bool CCoreAudioStream::GetAvailablePhysicalFormats(StreamFormatList* pList)
-{
- if (!pList || !m_StreamId)
- return false;
-
- UInt32 propertySize = 0;
- Boolean writable = false;
- OSStatus ret = AudioStreamGetPropertyInfo(m_StreamId, 0,
- kAudioStreamPropertyAvailablePhysicalFormats, &propertySize, &writable);
- if (ret)
- return false;
- UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription* pFormatList = new AudioStreamRangedDescription[formatCount];
- ret = AudioStreamGetProperty(m_StreamId, 0,
- kAudioStreamPropertyAvailablePhysicalFormats, &propertySize, pFormatList);
- if (!ret)
- {
- for (UInt32 format = 0; format < formatCount; format++)
- pList->push_back(pFormatList[format]);
- }
- delete[] pFormatList;
- return (ret == noErr);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CCoreAudioUnit
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CCoreAudioUnit::CCoreAudioUnit() :
-m_Initialized(false),
-m_Component(NULL),
-m_pSource(NULL)
-{
-}
-
-CCoreAudioUnit::~CCoreAudioUnit()
-{
- Close();
-}
-
-bool CCoreAudioUnit::Open(ComponentDescription desc)
-{
- if (m_Component)
- Close();
-
- // Find the required Component
- Component outputComp = FindNextComponent(NULL, &desc);
- if (outputComp == NULL) // Unable to find the AudioUnit we requested
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::Open: Unable to locate AudioUnit Component.");
- return false;
- }
-
- // Create an instance of the AudioUnit Component
- OSStatus ret = OpenAComponent(outputComp, &m_Component);
- if (ret) // Unable to open AudioUnit
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::Open: "
- "Unable to open AudioUnit Component. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::Open(OSType type, OSType subType, OSType manufacturer)
-{
- ComponentDescription desc;
- desc.componentType = type;
- desc.componentSubType = subType;
- desc.componentManufacturer = manufacturer;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
- return Open(desc);
-}
-
-
-void CCoreAudioUnit::Close()
-{
- if (m_Initialized)
- AudioUnitUninitialize(m_Component);
- if (m_Component)
- CloseComponent(m_Component);
- m_Initialized = false;
- m_Component = 0;
- m_pSource = NULL;
-}
-
-bool CCoreAudioUnit::Initialize()
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitInitialize(m_Component);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::Initialize: "
- "Unable to Initialize AudioUnit. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- m_Initialized = true;
- return true;
-}
-
-
-bool CCoreAudioUnit::GetInputFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!m_Component || !pDesc)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioUnitGetProperty(m_Component,
- kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, pDesc, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputFormat: "
- "Unable to get AudioUnit input format. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::GetOutputFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!m_Component || !pDesc)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioUnitGetProperty(m_Component,
- kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, pDesc, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputFormat: "
- "Unable to get AudioUnit output format. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::SetInputFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!m_Component || !pDesc)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input, 0, pDesc, sizeof(AudioStreamBasicDescription));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetInputFormat: "
- "Unable to set AudioUnit input format. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::SetOutputFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!m_Component || !pDesc)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output, 0, pDesc, sizeof(AudioStreamBasicDescription));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetInputFormat: "
- "Unable to set AudioUnit output format. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_MaximumFramesPerSlice,
- kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: "
- "Unable to set AudioUnit max frames per slice. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CCoreAudioUnit::GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts)
-{
- if (!m_Component)
- return false;
- if (!pLayouts)
- return false;
-
- UInt32 propSize = 0;
- Boolean writable = false;
- OSStatus ret = AudioUnitGetPropertyInfo(m_Component, kAudioUnitProperty_SupportedChannelLayoutTags,
- kAudioUnitScope_Input, 0, &propSize, &writable);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
- "Unable to retrieve supported channel layout property info. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- UInt32 layoutCount = propSize / sizeof(AudioChannelLayoutTag);
- AudioChannelLayoutTag* pSuppLayouts = new AudioChannelLayoutTag[layoutCount];
- ret = AudioUnitGetProperty(m_Component, kAudioUnitProperty_SupportedChannelLayoutTags,
- kAudioUnitScope_Output, 0, pSuppLayouts, &propSize);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
- "Unable to retrieve supported channel layouts. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- for (UInt32 layout = 0; layout < layoutCount; layout++)
- pLayouts->push_back(pSuppLayouts[layout]);
- delete[] pSuppLayouts;
- return true;
-}
-
-// Data Source Management Routines
-bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
-{
- m_pSource = pSource;
- if (pSource)
- return SetRenderProc(RenderCallback, this);
- else
- return SetRenderProc(NULL, NULL); // TODO: Is this correct, or is there another way to clear the render proc?
-}
-
-bool CCoreAudioUnit::SetRenderProc(AURenderCallback callback, void* pClientData)
-{
- if (!m_Component)
- return false;
-
- AURenderCallbackStruct callbackInfo;
- callbackInfo.inputProc = callback; // Function to be called each time the AudioUnit needs data
- callbackInfo.inputProcRefCon = pClientData; // Pointer to be returned in the callback proc
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: "
- "Unable to set AudioUnit render callback. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-OSStatus CCoreAudioUnit::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- if (m_pSource)
- return m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
- return siInputDeviceErr; // TODO: Should we do something else here instead?
-}
-
-OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return ((CCoreAudioUnit*)inRefCon)->OnRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CAUGenericSource
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CAUGenericSource::CAUGenericSource()
-{
-
-}
-
-CAUGenericSource::~CAUGenericSource()
-{
-
-}
-
-OSStatus CAUGenericSource::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = AudioUnitRender(m_Component, actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CAUOutputDevice
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// TODO: The channel map setter/getter are inefficient
-
-CAUOutputDevice::CAUOutputDevice()
-{
-
-}
-
-CAUOutputDevice::~CAUOutputDevice()
-{
-
-}
-
-bool CAUOutputDevice::SetCurrentDevice(AudioDeviceID deviceId)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentDevice: "
- "Unable to set current device. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CAUOutputDevice::GetChannelMap(CoreAudioChannelList* pChannelMap)
-{
- if (!m_Component)
- return false;
-
- UInt32 size = 0;
- Boolean writable = false;
- AudioUnitGetPropertyInfo(m_Component, kAudioOutputUnitProperty_ChannelMap,
- kAudioUnitScope_Input, 0, &size, &writable);
- UInt32 channels = size/sizeof(SInt32);
- SInt32* pMap = new SInt32[channels];
- OSStatus ret = AudioUnitGetProperty(m_Component, kAudioOutputUnitProperty_ChannelMap,
- kAudioUnitScope_Input, 0, pMap, &size);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputChannelMap: "
- "Unable to retrieve AudioUnit input channel map. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- else
- for (UInt32 i = 0; i < channels; i++)
- pChannelMap->push_back(pMap[i]);
- delete[] pMap;
- return (!ret);
-}
-
-bool CAUOutputDevice::SetChannelMap(CoreAudioChannelList* pChannelMap)
-{
- // The number of array elements must match the number of output channels provided by the device
- if (!m_Component || !pChannelMap)
- return false;
- UInt32 channels = pChannelMap->size();
- UInt32 size = sizeof(SInt32) * channels;
- SInt32* pMap = new SInt32[channels];
- for (UInt32 i = 0; i < channels; i++)
- pMap[i] = (*pChannelMap)[i];
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioOutputUnitProperty_ChannelMap,
- kAudioUnitScope_Input, 0, pMap, size);
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
- "Unable to get current device's buffer size. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- delete[] pMap;
- return (!ret);
-}
-
-void CAUOutputDevice::Start()
-{
- // TODO: Check component status
- if (m_Component && m_Initialized)
- AudioOutputUnitStart(m_Component);
-}
-
-void CAUOutputDevice::Stop()
-{
- // TODO: Check component status
- if (m_Component && m_Initialized)
- AudioOutputUnitStop(m_Component);
-}
-
-Float32 CAUOutputDevice::GetCurrentVolume()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 volPct = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kHALOutputParam_Volume,
- kAudioUnitScope_Global, 0, &volPct);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: "
- "Unable to get AudioUnit volume. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return volPct;
-}
-
-bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kHALOutputParam_Volume,
- kAudioUnitScope_Global, 0, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: "
- "Unable to set AudioUnit volume. Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CAUOutputDevice::IsRunning()
-{
- if (!m_Component)
- return false;
-
- UInt32 isRunning = 0;
- UInt32 size = sizeof(isRunning);
- AudioUnitGetProperty(m_Component, kAudioOutputUnitProperty_IsRunning,
- kAudioUnitScope_Global, 0, &isRunning, &size);
- return (isRunning != 0);
-}
-
-UInt32 CAUOutputDevice::GetBufferFrameSize()
-{
- if (!m_Component)
- return 0;
-
- UInt32 size = sizeof(UInt32);
- UInt32 bufferSize = 0;
- OSStatus ret = AudioUnitGetProperty(m_Component, kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Input, 0, &bufferSize, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
- "Unable to get current device's buffer size. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0;
- }
- return bufferSize;
-}
-
-OSStatus CAUOutputDevice::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- OSStatus ret = CCoreAudioUnit::OnRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
- return ret;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CAUMatrixMixer
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-CAUMatrixMixer::CAUMatrixMixer()
-{
-
-}
-
-CAUMatrixMixer::~CAUMatrixMixer()
-{
-
-}
-
-bool CAUMatrixMixer::Open()
-{
- return CCoreAudioUnit::Open(kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple);
-}
-
-bool CAUMatrixMixer::Open(OSType type, OSType subType, OSType manufacturer)
-{
- return Open();
-}
-
-OSStatus CAUMatrixMixer::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = CAUGenericSource::Render(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-bool CAUMatrixMixer::Initialize()
-{
- bool ret = CCoreAudioUnit::Initialize();
- if (ret)
- {
- // Fetch the channel configuration
- UInt32 dims[2];
- UInt32 size = sizeof(dims);
- if (noErr != AudioUnitGetProperty(m_Component, kAudioUnitProperty_MatrixDimensions,
- kAudioUnitScope_Global, 0, dims, &size))
- return false;
- // Initialize global, input, and output levels
- if (!SetGlobalVolume(1.0f))
- return false;
- for (UInt32 i = 0; i < dims[0]; i++)
- if (!SetInputVolume(i, 1.0f))
- return false;
- for (UInt32 i = 0; i < dims[1]; i++)
- if (!SetOutputVolume(i, 1.0f))
- return false;
- }
- return ret;
-}
-
-UInt32 CAUMatrixMixer::GetInputBusCount()
-{
- if (!m_Component)
- return 0;
-
- UInt32 busCount = 0;
- UInt32 size = sizeof(busCount);
- OSStatus ret = AudioUnitGetProperty(m_Component, kAudioUnitProperty_BusCount,
- kAudioUnitScope_Input, 0, &busCount, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputBusCount: "
- "Unable to get input bus count. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0;
- }
- return busCount;
-}
-
-bool CAUMatrixMixer::SetInputBusCount(UInt32 busCount)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_BusCount,
- kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputBusCount: "
- "Unable to set input bus count. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-UInt32 CAUMatrixMixer::GetOutputBusCount()
-{
- if (!m_Component)
- return 0;
-
- UInt32 busCount = 0;
- UInt32 size = sizeof(busCount);
- OSStatus ret = AudioUnitGetProperty(m_Component, kAudioUnitProperty_BusCount,
- kAudioUnitScope_Output, 0, &busCount, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputBusCount: "
- "Unable to get output bus count. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0;
- }
- return busCount;
-}
-
-bool CAUMatrixMixer::SetOutputBusCount(UInt32 busCount)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetProperty(m_Component, kAudioUnitProperty_BusCount,
- kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputBusCount: "
- "Unable to set output bus count. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMatrixMixer::GetGlobalVolume()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 vol = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Global, 0xFFFFFFFF, &vol);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::GetGlobalVolume: "
- "Unable to get global volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return vol;
-}
-
-bool CAUMatrixMixer::SetGlobalVolume(Float32 vol)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Global, 0xFFFFFFFF, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::SetGlobalVolume: "
- "Unable to set global volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMatrixMixer::GetInputVolume(UInt32 element)
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 vol = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Input, element, &vol);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputVolume: "
- "Unable to get input volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return vol;
-}
-
-bool CAUMatrixMixer::SetInputVolume(UInt32 element, Float32 vol)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Input, element, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputVolume: "
- "Unable to set input volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMatrixMixer::GetOutputVolume(UInt32 element)
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 vol = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Output, element, &vol);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputVolume: "
- "Unable to get output volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return vol;
-}
-
-bool CAUMatrixMixer::SetOutputVolume(UInt32 element, Float32 vol)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMatrixMixerParam_Volume,
- kAudioUnitScope_Output, element, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputVolume: "
- "Unable to set output volume. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-CAUMultibandCompressor::CAUMultibandCompressor()
-{
-
-}
-
-CAUMultibandCompressor::~CAUMultibandCompressor()
-{
-
-}
-
-bool CAUMultibandCompressor::Open()
-{
- return CCoreAudioUnit::Open(kAudioUnitType_Effect, kAudioUnitSubType_MultiBandCompressor, kAudioUnitManufacturer_Apple);
-}
-
-bool CAUMultibandCompressor::Open(OSType type, OSType subType, OSType manufacturer)
-{
- return Open();
-}
-
-OSStatus CAUMultibandCompressor::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = CAUGenericSource::Render(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-bool CAUMultibandCompressor::Initialize()
-{
- bool ret = CCoreAudioUnit::Initialize();
- if (ret)
- {
- if (!SetPreGain(-30.0) ||
- !SetAttackTime(0.02) ||
- !SetReleaseTime(0.04))
- return false;
- }
- return ret;
-}
-
-bool CAUMultibandCompressor::SetPostGain(Float32 gain)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMultibandCompressorParam_Postgain, kAudioUnitScope_Global, 0, gain, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::SetPostGain: "
- "Unable to set post-gain. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMultibandCompressor::GetPostGain()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 gain = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMultibandCompressorParam_Postgain,
- kAudioUnitScope_Output, 0, &gain);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::GetPostGain: "
- "Unable to get post-gain. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return gain;
-}
-
-bool CAUMultibandCompressor::SetPreGain(Float32 gain)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMultibandCompressorParam_Pregain, kAudioUnitScope_Global, 0, gain, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::SetPreGain: "
- "Unable to set pre-gain. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMultibandCompressor::GetPreGain()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 gain = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMultibandCompressorParam_Pregain,
- kAudioUnitScope_Output, 0, &gain);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::GetPreGain: "
- "Unable to get pre-gain. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return gain;
-}
-
-bool CAUMultibandCompressor::SetAttackTime(Float32 time)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMultibandCompressorParam_AttackTime, kAudioUnitScope_Global, 0, time, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::SetAttackTime: "
- "Unable to set attack time. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMultibandCompressor::GetAttackTime()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 time = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMultibandCompressorParam_AttackTime, kAudioUnitScope_Global, 0, &time);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::GetAttackTime: "
- "Unable to get attack time. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return time;
-}
-
-bool CAUMultibandCompressor::SetReleaseTime(Float32 time)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, kMultibandCompressorParam_ReleaseTime, kAudioUnitScope_Global, 0, time, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::SetReleaseTime: "
- "Unable to set attack time. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUMultibandCompressor::GetReleaseTime()
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 time = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, kMultibandCompressorParam_ReleaseTime, kAudioUnitScope_Global, 0, &time);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUMultibandCompressor::GetReleaseTime: "
- "Unable to get attack time. ErrCode = Error = 0x%08x (%4.4s)",
- (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return time;
-}
-
-CAUDynamicsProcessor::CAUDynamicsProcessor()
-{
-
-}
-
-CAUDynamicsProcessor::~CAUDynamicsProcessor()
-{
-
-}
-
-bool CAUDynamicsProcessor::Open()
-{
- return CCoreAudioUnit::Open(kAudioUnitType_Effect, kAudioUnitSubType_DynamicsProcessor, kAudioUnitManufacturer_Apple);
-}
-
-bool CAUDynamicsProcessor::Open(OSType type, OSType subType, OSType manufacturer)
-{
- return Open();
-}
-
-OSStatus CAUDynamicsProcessor::Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = CAUGenericSource::Render(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-bool CAUDynamicsProcessor::Initialize()
-{
- bool ret = CCoreAudioUnit::Initialize();
- if (ret)
- {
- if (!SetMasterGain(0.0) ||
- !SetCompressionThreshold(-35.0) ||
- !SetHeadroom(30.0) ||
- !SetExpansionRatio(1.0) ||
- !SetExpansionThreshold(-100.0) ||
- !SetAttackTime(0.03) ||
- !SetReleaseTime(0.03))
- return false;
- }
- return ret;
-}
-
-bool CAUDynamicsProcessor::SetFloatParam(UInt32 param, UInt32 element, Float32 val)
-{
- if (!m_Component)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_Component, param, kAudioUnitScope_Global, element, val, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUDynamicsProcessor::SetFloatParam: "
- "Unable to set parameter (id: %d). ErrCode = Error = 0x%08x (%4.4s)",
- param, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CAUDynamicsProcessor::GetFloatParam(UInt32 param, UInt32 element)
-{
- if (!m_Component)
- return 0.0f;
-
- Float32 val = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_Component, param, kAudioUnitScope_Global, element, &val);
- if (ret)
- {
- CLog::Log(LOGERROR, "CAUDynamicsProcessor::GetFloatParam: "
- "Unable to get parameter (id: %d). ErrCode = Error = 0x%08x (%4.4s)",
- param, (unsigned int)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return val;
-}
-
-#endif
-#endif
diff --git a/xbmc/osx/CoreAudio.h b/xbmc/osx/CoreAudio.h
deleted file mode 100644
index 73764a06d9..0000000000
--- a/xbmc/osx/CoreAudio.h
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __COREAUDIO_H__
-#define __COREAUDIO_H__
-
-#if !defined(__arm__)
-#include "utils/StdString.h"
-
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-#include <list>
-#include <vector>
-
-// Forward declarations
-class CCoreAudioHardware;
-class CCoreAudioDevice;
-class CCoreAudioStream;
-class CCoreAudioUnit;
-
-typedef std::list<AudioDeviceID> CoreAudioDeviceList;
-
-// Not yet implemented
-// kAudioHardwarePropertyDevices
-// kAudioHardwarePropertyDefaultInputDevice
-// kAudioHardwarePropertyDefaultSystemOutputDevice
-// kAudioHardwarePropertyDeviceForUID
-
-// There is only one AudioSystemObject instance system-side.
-// Therefore, all CCoreAudioHardware methods are static
-class CCoreAudioHardware
-{
-public:
- static AudioDeviceID FindAudioDevice(CStdString deviceName);
- static AudioDeviceID GetDefaultOutputDevice();
- static UInt32 GetOutputDevices(CoreAudioDeviceList* pList);
- static bool GetAutoHogMode();
- static void SetAutoHogMode(bool enable);
-};
-
-// Not yet implemented
-// kAudioDevicePropertyDeviceIsRunning, kAudioDevicePropertyDeviceIsRunningSomewhere, kAudioDevicePropertyLatency,
-// kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyUsesVariableBufferFrameSizes,
-// kAudioDevicePropertySafetyOffset, kAudioDevicePropertyIOCycleUsage, kAudioDevicePropertyStreamConfiguration
-// kAudioDevicePropertyIOProcStreamUsage, kAudioDevicePropertyPreferredChannelsForStereo, kAudioDevicePropertyPreferredChannelLayout,
-// kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyActualSampleRate,
-// kAudioDevicePropertyTransportType
-
-typedef std::list<AudioStreamID> AudioStreamIdList;
-typedef std::list<UInt32> CoreAudioDataSourceList;
-typedef std::vector<SInt32> CoreAudioChannelList;
-
-class CCoreAudioChannelLayout
-{
-public:
- CCoreAudioChannelLayout();
- CCoreAudioChannelLayout(AudioChannelLayout& layout);
- virtual ~CCoreAudioChannelLayout();
- operator AudioChannelLayout*() {return m_pLayout;}
- bool CopyLayout(AudioChannelLayout& layout);
- bool SetLayout(AudioChannelLayoutTag layoutTag);
- UInt32 GetChannelCount();
- AudioChannelLabel GetChannelLabel(UInt32 index);
- static UInt32 GetChannelCountForLayout(AudioChannelLayout& layout);
- static const char* ChannelLabelToString(UInt32 label);
- static const char* ChannelLayoutToString(AudioChannelLayout& layout, CStdString& str);
-protected:
- AudioChannelLayout* m_pLayout;
-};
-
-class CCoreAudioDevice
-{
-public:
- CCoreAudioDevice();
- CCoreAudioDevice(AudioDeviceID deviceId);
- virtual ~CCoreAudioDevice();
-
- bool Open(AudioDeviceID deviceId);
- void Close();
-
- void Start();
- void Stop();
- bool AddIOProc(AudioDeviceIOProc ioProc, void* pCallbackData);
- void RemoveIOProc();
-
- AudioDeviceID GetId() {return m_DeviceId;}
- const char* GetName(CStdString& name);
- const char* GetName();
- UInt32 GetTotalOutputChannels();
- bool GetStreams(AudioStreamIdList* pList);
- bool IsRunning();
- bool SetHogStatus(bool hog);
- pid_t GetHogStatus();
- bool SetMixingSupport(bool mix);
- bool GetMixingSupport();
- bool GetPreferredChannelLayout(CCoreAudioChannelLayout& layout);
- bool GetDataSources(CoreAudioDataSourceList* pList);
- Float64 GetNominalSampleRate();
- bool SetNominalSampleRate(Float64 sampleRate);
- UInt32 GetNumLatencyFrames();
- UInt32 GetBufferSize();
- bool SetBufferSize(UInt32 size);
-protected:
- AudioDeviceID m_DeviceId;
- bool m_Started;
- pid_t m_Hog;
- int m_MixerRestore;
- AudioDeviceIOProc m_IoProc;
- Float64 m_SampleRateRestore;
- UInt32 m_BufferSizeRestore;
- CStdString m_Name;
-};
-
-typedef std::list<AudioStreamRangedDescription> StreamFormatList;
-
-class CCoreAudioStream
-{
-public:
- CCoreAudioStream();
- virtual ~CCoreAudioStream();
-
- bool Open(AudioStreamID streamId);
- void Close();
-
- AudioStreamID GetId() {return m_StreamId;}
- UInt32 GetDirection();
- UInt32 GetTerminalType();
- UInt32 GetNumLatencyFrames();
- bool GetVirtualFormat(AudioStreamBasicDescription* pDesc);
- bool GetPhysicalFormat(AudioStreamBasicDescription* pDesc);
- bool SetVirtualFormat(AudioStreamBasicDescription* pDesc);
- bool SetPhysicalFormat(AudioStreamBasicDescription* pDesc);
- bool GetAvailableVirtualFormats(StreamFormatList* pList);
- bool GetAvailablePhysicalFormats(StreamFormatList* pList);
-
-protected:
- AudioStreamID m_StreamId;
- AudioStreamBasicDescription m_OriginalVirtualFormat;
- AudioStreamBasicDescription m_OriginalPhysicalFormat;
-};
-
-class ICoreAudioSource
-{
-public:
- virtual ~ICoreAudioSource() {};
- // Function to request rendered data from a data source
- virtual OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList) = 0;
-};
-
-typedef std::list<AudioChannelLayoutTag> AudioChannelLayoutList;
-
-class CCoreAudioUnit
-{
-public:
- CCoreAudioUnit();
- virtual ~CCoreAudioUnit();
-
- virtual bool Open(ComponentDescription desc);
- virtual bool Open(OSType type, OSType subType, OSType manufacturer);
- virtual void Attach(AudioUnit audioUnit) {m_Component = audioUnit;}
- virtual AudioUnit GetComponent(){return m_Component;}
- virtual void Close();
- virtual bool Initialize();
- virtual bool IsInitialized() {return m_Initialized;}
- virtual bool SetInputSource(ICoreAudioSource* pSource);
- virtual bool GetInputFormat(AudioStreamBasicDescription* pDesc);
- virtual bool GetOutputFormat(AudioStreamBasicDescription* pDesc);
- virtual bool SetInputFormat(AudioStreamBasicDescription* pDesc);
- virtual bool SetOutputFormat(AudioStreamBasicDescription* pDesc);
- virtual bool SetMaxFramesPerSlice(UInt32 maxFrames);
- virtual bool GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts);
-protected:
- bool SetRenderProc(AURenderCallback callback, void* pClientData);
- static OSStatus RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- virtual OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
-
- ICoreAudioSource* m_pSource;
- AudioUnit m_Component;
- bool m_Initialized;
-};
-
-class CAUGenericSource : public CCoreAudioUnit, public ICoreAudioSource
-{
-public:
- CAUGenericSource();
- virtual ~CAUGenericSource();
- virtual OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList);
-};
-
-class CAUOutputDevice : public CCoreAudioUnit
-{
-public:
- CAUOutputDevice();
- virtual ~CAUOutputDevice();
- bool SetCurrentDevice(AudioDeviceID deviceId);
- bool GetChannelMap(CoreAudioChannelList* pChannelMap);
- bool SetChannelMap(CoreAudioChannelList* pChannelMap);
- UInt32 GetBufferFrameSize();
-
- void Start();
- void Stop();
- bool IsRunning();
-
- Float32 GetCurrentVolume();
- bool SetCurrentVolume(Float32 vol);
-protected:
- virtual OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
-};
-
-class CAUMatrixMixer : public CAUGenericSource
-{
-public:
- CAUMatrixMixer();
- virtual ~CAUMatrixMixer();
- bool Open(OSType type, OSType subType, OSType manufacturer);
- bool Open();
- OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList);
- bool Initialize();
-
- UInt32 GetInputBusCount();
- bool SetInputBusCount(UInt32 busCount);
- UInt32 GetOutputBusCount();
- bool SetOutputBusCount(UInt32 busCount);
-
- Float32 GetGlobalVolume();
- bool SetGlobalVolume(Float32 vol);
- Float32 GetInputVolume(UInt32 element);
- bool SetInputVolume(UInt32 element, Float32 vol);
- Float32 GetOutputVolume(UInt32 element);
- bool SetOutputVolume(UInt32 element, Float32 vol);
-protected:
-};
-
-class CAUMultibandCompressor : public CAUGenericSource
-{
-public:
- CAUMultibandCompressor();
- virtual ~CAUMultibandCompressor();
-
- bool Open(OSType type, OSType subType, OSType manufacturer);
- bool Open();
-
- OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList);
- bool Initialize();
-
- bool SetPostGain(Float32 gain);
- Float32 GetPostGain();
- bool SetPreGain(Float32 gain);
- Float32 GetPreGain();
- bool SetAttackTime(Float32 time);
- Float32 GetAttackTime();
- bool SetReleaseTime(Float32 time);
- Float32 GetReleaseTime();
-
-protected:
-};
-
-class CAUDynamicsProcessor : public CAUGenericSource
-{
-public:
- CAUDynamicsProcessor();
- virtual ~CAUDynamicsProcessor();
-
- bool Open(OSType type, OSType subType, OSType manufacturer);
- bool Open();
-
- OSStatus Render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList);
- bool Initialize();
-
- bool SetCompressionThreshold(Float32 db) {return SetFloatParam(kDynamicsProcessorParam_Threshold, 0, db);}
- Float32 SetCompressionThreshold() {return GetFloatParam(kDynamicsProcessorParam_Threshold, 0);}
- bool SetHeadroom(Float32 db) {return SetFloatParam(kDynamicsProcessorParam_HeadRoom, 0, db);}
- Float32 GetHeadroom() {return GetFloatParam(kDynamicsProcessorParam_HeadRoom, 0);}
- bool SetExpansionRatio(Float32 ratio) {return SetFloatParam(kDynamicsProcessorParam_ExpansionRatio, 0, ratio);}
- Float32 GetExpansionRatio() {return GetFloatParam(kDynamicsProcessorParam_ExpansionRatio, 0);}
- bool SetExpansionThreshold(Float32 ratio) {return SetFloatParam(kDynamicsProcessorParam_ExpansionThreshold, 0, ratio);}
- Float32 GetExpansionThreshold() {return GetFloatParam(kDynamicsProcessorParam_ExpansionThreshold, 0);}
- bool SetAttackTime(Float32 time) {return SetFloatParam(kDynamicsProcessorParam_AttackTime, 0, time);}
- Float32 GetAttackTime() {return GetFloatParam(kDynamicsProcessorParam_AttackTime, 0);}
- bool SetReleaseTime(Float32 time) {return SetFloatParam(kDynamicsProcessorParam_ReleaseTime, 0, time);}
- Float32 GetReleaseTime() {return GetFloatParam(kDynamicsProcessorParam_ReleaseTime, 0);}
- bool SetMasterGain(Float32 gain) {return SetFloatParam(kDynamicsProcessorParam_MasterGain, 0, gain);}
- Float32 GetMasterGain() {return GetFloatParam(kDynamicsProcessorParam_MasterGain, 0);}
-
-protected:
- bool SetFloatParam(UInt32 param, UInt32 element, Float32 val);
- Float32 GetFloatParam(UInt32 param, UInt32 element);
-};
-
-// Helper Functions
-char* UInt32ToFourCC(UInt32* val);
-const char* StreamDescriptionToString(AudioStreamBasicDescription desc, CStdString& str);
-
-#define CONVERT_OSSTATUS(x) UInt32ToFourCC((UInt32*)&ret)
-
-#endif // __arm__
-#endif // __COREAUDIO_H__
diff --git a/xbmc/osx/CoreAudioSoundManager.cpp b/xbmc/osx/CoreAudioSoundManager.cpp
deleted file mode 100644
index 64a312e8a2..0000000000
--- a/xbmc/osx/CoreAudioSoundManager.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifdef __APPLE__
-
-#include "CoreAudioSoundManager.h"
-#include "PlatformDefs.h"
-#include "Log.h"
-
-struct core_audio_sound
-{
- UInt32 ref_count;
- UInt32 play_count;
- UInt32 total_frames;
- AudioBufferList* buffer_list;
-};
-
-typedef clock_t core_audio_timestamp;
-
-struct core_audio_sound_event
-{
- core_audio_sound* sound;
- core_audio_timestamp play_time; // Don't play before
- core_audio_timestamp expire_time; // Stop playing no later than
- UInt32 offset;
-};
-
-CCoreAudioSoundManager::CCoreAudioSoundManager() :
- m_pCurrentEvent(NULL),
- m_RestartOutputUnit(false)
-{
-
-}
-
-CCoreAudioSoundManager::~CCoreAudioSoundManager()
-{
- Stop();
-}
-
-bool CCoreAudioSoundManager::Initialize(CStdString deviceName)
-{
- // Attempt to find the configured output device
- AudioDeviceID outputDevice = CCoreAudioHardware::FindAudioDevice(deviceName);
- if (!outputDevice) // Fall back to the default device if no match is found
- {
- CLog::Log(LOGWARNING, "CCoreAudioSoundManager::Initialize: Unable to locate configured device, falling-back to the system default.");
- outputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
- if (!outputDevice) // Not a lot to be done with no device.
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::Initialize: No default device found. Unable to initialize.");
- return false;
- }
- }
-
- // Attach our output object to the device
- if (!m_OutputDevice.Open(outputDevice))
- return false;
-
- // Create the Output AudioUnit Component
- if (!m_OutputUnit.Open(kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple))
- return false;
-
- // Hook the Ouput AudioUnit to the selected device
- if (!m_OutputUnit.SetCurrentDevice(outputDevice))
- return false;
-
- // Set up output format (32-bit float, 2-channel, non-interleaved)
- m_OutputFormat.mSampleRate = 44100.0;
- m_OutputFormat.mChannelsPerFrame = 2;
- m_OutputFormat.mFormatID = kAudioFormatLinearPCM;
- m_OutputFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
- m_OutputFormat.mBitsPerChannel = 8 * sizeof(float);
- m_OutputFormat.mBytesPerFrame = sizeof(float);
- m_OutputFormat.mFramesPerPacket = 1;
- m_OutputFormat.mBytesPerPacket = m_OutputFormat.mBytesPerFrame * m_OutputFormat.mFramesPerPacket;
- if (!m_OutputUnit.SetInputFormat(&m_OutputFormat))
- return false;
-
- // Configure the maximum number of frames that the AudioUnit will ask to process at one time.
- // If this is not called, there is no guarantee that the callback will ever be called.
- UInt32 bufferFrames = m_OutputUnit.GetBufferFrameSize(); // Size of the output buffer, in Frames
- if (!m_OutputUnit.SetMaxFramesPerSlice(bufferFrames))
- return false;
-
- // Setup the callback function that the AudioUnit will use to request data
- if (!m_OutputUnit.SetRenderProc(RenderCallback, this))
- return false;
-
- // Initialize the Output AudioUnit
- if (!m_OutputUnit.Initialize())
- return false;
-
- // All went as planned
- return true;
-}
-
-void CCoreAudioSoundManager::Run()
-{
- AudioDeviceAddPropertyListener(m_OutputDevice.GetId(), 0, false, kAudioDevicePropertyHogMode, PropertyChangeCallback, this); // If this fails, there is not a whole lot to be done
- m_OutputUnit.Start();
- CLog::Log(LOGDEBUG, "CCoreAudioSoundManager::Run: SoundManager is now running.");
-}
-
-void CCoreAudioSoundManager::Stop()
-{
- if (!m_OutputUnit.IsRunning())
- return;
-
- m_OutputUnit.Stop();
- AudioDeviceRemovePropertyListener(m_OutputDevice.GetId(), 0, false, kAudioDevicePropertyHogMode, PropertyChangeCallback); // No longer need to know if the device is hogged
- // TODO: Clean up leftover event(s)
- CLog::Log(LOGDEBUG, "CCoreAudioSoundManager::Stop: SoundManager has been stopped.");
-}
-
-CoreAudioSoundRef CCoreAudioSoundManager::RegisterSound(const CStdString& fileName)
-{
- return LoadSoundFromFile(fileName);
-}
-
-void CCoreAudioSoundManager::UnregisterSound(CoreAudioSoundRef soundRef)
-{
- if (!soundRef)
- return;
-
- core_audio_sound* pSound = soundRef;
- if (!--pSound->ref_count && !pSound->play_count) // Release the caller's reference and see if the object is ready to be freed
- {
- delete pSound->buffer_list;
- delete pSound;
- }
-}
-
-void CCoreAudioSoundManager::PlaySound(CoreAudioSoundRef soundRef)
-{
- if (!soundRef)
- return;
-
- // Restart the output AudioUnit if necessary
- if (m_RestartOutputUnit) // This flag is set by the property change notification handler to work around an OSX 10.4 quirk/bug.
- { // It will re-init the callback thread for the AudioUnit after hog-mode has been released.
- m_OutputUnit.Stop();
- m_OutputUnit.Start();
- m_RestartOutputUnit = false;
- }
-
- if (!m_pCurrentEvent)
- {
- // TODO: Build queue and/or mix parallel sounds. For now just boot the currently playing sound and replace it.
- // TODO: Not thread-safe. Going to cause trouble.
-
- // Create a new 'event' object to hold playback info
- core_audio_sound_event* pEvent = new core_audio_sound_event;
- pEvent->sound = soundRef;
- pEvent->offset = 0; // Start at the beginning
-
- pEvent->sound->play_count++;
- m_pCurrentEvent = pEvent; // TODO: This can create a memory leak if we already had one
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-// Private Methods
-//////////////////////////////////////////////////////////////////////////////////////////////
-OSStatus CCoreAudioSoundManager::OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- if (m_pCurrentEvent) // Do we have a sound event queued to play?
- {
- core_audio_sound_event* pEvent = m_pCurrentEvent; // Grab reference in case the event gets changed while we work
- UInt32 framesAvailable = pEvent->sound->total_frames - pEvent->offset;
- // TODO: Find a better way to manage time references
- if (pEvent->offset < pEvent->sound->total_frames) // Do we have any (unexpired) data left to give.
- {
- if (framesAvailable < inNumberFrames) // Do we have enough data to fill the complete request
- inNumberFrames = framesAvailable; // Truncate request to available data length
- for (int i = 0; i < ioData->mNumberBuffers; i++)
- {
- UInt32 frameLen = m_OutputFormat.mBytesPerFrame;
- unsigned char* pIn = (unsigned char*)pEvent->sound->buffer_list->mBuffers[i].mData;
- memcpy(ioData->mBuffers[i].mData, &pIn[pEvent->offset * frameLen], inNumberFrames * frameLen); // Copy out the requested number of frames
- ioData->mBuffers[i].mDataByteSize = inNumberFrames * frameLen; // Update to reflect actual date
- }
- pEvent->offset += inNumberFrames; // Update position in current buffer
- if (m_pCurrentEvent != pEvent) // This sound has been canceled
- delete pEvent;
- }
- else // No (unexpired) data left in this buffer. Free it.
- {
- // TODO: reference counting needs work
- pEvent->sound->play_count--; // Allow the sound to be deleted. We are done with it.
- if (!pEvent->sound->play_count && !m_pCurrentEvent->sound->ref_count) // Try and help out if all references have been released
- UnregisterSound(pEvent->sound);
- delete pEvent;
- if (pEvent == m_pCurrentEvent)
- m_pCurrentEvent = NULL;
- }
- }
- else // No current buffer. notify caller and return.
- {
- ioData->mBuffers[0].mDataByteSize = ioData->mBuffers[0].mDataByteSize = 0;
- }
- return noErr;
-}
-
-OSStatus CCoreAudioSoundManager::RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return ((CCoreAudioSoundManager*)inRefCon)->OnRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); // Hand over to instance memeber
-}
-
-OSStatus CCoreAudioSoundManager::PropertyChangeCallback(AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void* inClientData)
-{
- CCoreAudioSoundManager* pThis = (CCoreAudioSoundManager*)inClientData;
- pid_t hogPid = pThis->m_OutputDevice.GetHogStatus();
- if (hogPid > -1) // Device is hogged.
- {
- CLog::Log(LOGWARNING, "CCoreAudioSoundManager: Someone has hogged the output device. Stopping until it is released.");
- pThis->m_OutputUnit.Stop();
- }
- else // Device has been released.
- {
- CLog::Log(LOGWARNING, "CCoreAudioSoundManager: The output device has been released. Resuming.");
- pThis->m_RestartOutputUnit = true; // This will cause the PlaySound method to restart the AudioUnit before queuing up the next sound.
- // It is needed to work around a bug in the OSX 10.4 AudioUnit code that incorrectly reports a successful start.
- }
- return noErr;
-}
-
-core_audio_sound* CCoreAudioSoundManager::LoadSoundFromFile(const CStdString& fileName)
-{
- FSRef fileRef;
- UInt32 size = 0;
- ExtAudioFileRef audioFile;
- OSStatus ret = FSPathMakeRef((const UInt8*) fileName.c_str(), &fileRef, false);
- ret = ExtAudioFileOpen(&fileRef, &audioFile);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to open source file (%s). Error = 0x%08x (%4.4s)", fileName.c_str(), ret, CONVERT_OSSTATUS(ret));
- return NULL;
- }
-
- core_audio_sound* pSound = new core_audio_sound;
-
- // Retrieve the format of the source file
- AudioStreamBasicDescription inputFormat;
- size = sizeof(inputFormat);
- ret = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileDataFormat, &size, &inputFormat);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to fetch source file format. Error = 0x%08x (%4.4s)", ret, CONVERT_OSSTATUS(ret));
- delete pSound;
- return NULL;
- }
-
- // Set up format conversion. This is the format that will be produced by Read/Write calls.
- // Here we use the same format provided to the output AudioUnit
- ret = ExtAudioFileSetProperty(audioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_OutputFormat);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to set conversion format. Error = 0x%08x (%4.4s)", ret, CONVERT_OSSTATUS(ret));
- delete pSound;
- return NULL;
- }
-
- // Retrieve the file size (in terms of the file's sample-rate, not the output sample-rate)
- UInt64 totalFrames;
- size = sizeof(totalFrames);
- ret = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileLengthFrames, &size, &totalFrames);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to fetch source file size. Error = 0x%08x (%4.4s)", ret, CONVERT_OSSTATUS(ret));
- delete pSound;
- return NULL;
- }
-
- // Calculate the total number of converted frames to be read
- totalFrames *= (float)m_OutputFormat.mSampleRate / (float)inputFormat.mSampleRate; // TODO: Verify the accuracy of this
-
- // Allocate AudioBuffers
- UInt32 channelCount = m_OutputFormat.mChannelsPerFrame;
- pSound->buffer_list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer) * (channelCount - kVariableLengthArray));
- pSound->buffer_list->mNumberBuffers = channelCount; // One buffer per channel for deinterlaced pcm
- float* buffers = (float*)calloc(1, sizeof(float) * totalFrames * channelCount);
- for(int i = 0; i < channelCount; i++)
- {
- pSound->buffer_list->mBuffers[i].mNumberChannels = 1; // One channel per buffer for deinterlaced pcm
- pSound->buffer_list->mBuffers[i].mData = buffers + (totalFrames * i);
- pSound->buffer_list->mBuffers[i].mDataByteSize = totalFrames * sizeof(float);
- }
-
- // Read the entire file
- // TODO: Should we limit the total file length?
- UInt32 readFrames = totalFrames;
- ret = ExtAudioFileRead(audioFile, &readFrames, pSound->buffer_list);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to read from file (%s). Error = 0x%08x (%4.4s)", fileName.c_str(), ret, CONVERT_OSSTATUS(ret));
- delete pSound;
- return NULL;
- }
- pSound->total_frames = readFrames; // Store the actual number of frames read from the file. Rounding errors in calcuating the converted number of frames can truncate the read.
-
- // TODO: What do we do with files with more than 2 channels. Currently we just copy the first two and dump the rest.
- if (inputFormat.mChannelsPerFrame == 1) // Copy Left channel into Right if the source file is Mono
- memcpy(pSound->buffer_list->mBuffers[1].mData, pSound->buffer_list->mBuffers[0].mData, pSound->buffer_list->mBuffers[0].mDataByteSize);
-
- ret = ExtAudioFileDispose(audioFile); // Close the file. We have what we need. Not a lot to be done on failure.
- if (ret)
- CLog::Log(LOGERROR, "CCoreAudioSoundManager::LoadSoundFromFile: Unable to close file (%s). Error = 0x%08x (%4.4s)", fileName.c_str(), ret, CONVERT_OSSTATUS(ret));
-
- pSound->ref_count = 1; // The caller holds a reference to this object now
- pSound->play_count = 0;
-
- return pSound;
-}
-
-#endif \ No newline at end of file
diff --git a/xbmc/osx/CoreAudioSoundManager.h b/xbmc/osx/CoreAudioSoundManager.h
deleted file mode 100644
index 8455b10495..0000000000
--- a/xbmc/osx/CoreAudioSoundManager.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __CORE_AUDIO_SOUND_MANAGER_H__
-#define __CORE_AUDIO_SOUND_MANAGER_H__
-
-#include "CoreAudio.h"
-
-struct core_audio_sound;
-struct core_audio_sound_event;
-
-typedef core_audio_sound* CoreAudioSoundRef; // Opaque reference for clients
-
-class CCoreAudioSoundManager
-{
-public:
- CCoreAudioSoundManager();
- ~CCoreAudioSoundManager();
- bool Initialize(CStdString deviceName);
- void Run();
- void Stop();
- CoreAudioSoundRef RegisterSound(const CStdString& fileName);
- void UnregisterSound(CoreAudioSoundRef soundRef);
- void PlaySound(CoreAudioSoundRef soundRef);
-protected:
- core_audio_sound_event* m_pCurrentEvent;
-
- CCoreAudioDevice m_OutputDevice;
- CCoreAudioUnit m_OutputUnit;
- AudioStreamBasicDescription m_OutputFormat;
- bool m_RestartOutputUnit;
-
- core_audio_sound* LoadSoundFromFile(const CStdString& fileName);
- static OSStatus RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
- static OSStatus PropertyChangeCallback(AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void* inClientData);
-};
-
-#endif // __CORE_AUDIO_SOUND_MANAGER_H__
diff --git a/xbmc/osx/IOSCoreAudio.cpp b/xbmc/osx/IOSCoreAudio.cpp
deleted file mode 100644
index 7106a9e82f..0000000000
--- a/xbmc/osx/IOSCoreAudio.cpp
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#if defined(__APPLE__) && defined(__arm__)
-#include <math.h>
-
-#include "IOSCoreAudio.h"
-#include "PlatformDefs.h"
-#include "utils/log.h"
-
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-char* IOSUInt32ToFourCC(UInt32* pVal) // NOT NULL TERMINATED! Modifies input value.
-{
- UInt32 inVal = *pVal;
- char* pIn = (char*)&inVal;
- char* fourCC = (char*)pVal;
- fourCC[3] = pIn[0];
- fourCC[2] = pIn[1];
- fourCC[1] = pIn[2];
- fourCC[0] = pIn[3];
- return fourCC;
-}
-
-const char* IOSStreamDescriptionToString(AudioStreamBasicDescription desc, CStdString& str)
-{
- UInt32 formatId = desc.mFormatID;
- char* fourCC = IOSUInt32ToFourCC(&formatId);
-
- switch (desc.mFormatID)
- {
- case kAudioFormatLinearPCM:
- str.Format("[%4.4s] %s%u Channel %u-bit %s (%uHz)",
- fourCC,
- (desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "Mixable ",
- desc.mChannelsPerFrame,
- desc.mBitsPerChannel,
- (desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Floating Point" : "Signed Integer",
- (UInt32)desc.mSampleRate);
- break;
- case kAudioFormatAC3:
- str.Format("[%4.4s] AC-3/DTS (%uHz)", fourCC, (UInt32)desc.mSampleRate);
- break;
- case kAudioFormat60958AC3:
- str.Format("[%4.4s] AC-3/DTS for S/PDIF (%uHz)", fourCC, (UInt32)desc.mSampleRate);
- break;
- default:
- str.Format("[%4.4s]", fourCC);
- break;
- }
- return str.c_str();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CIOSCoreAudioHardware
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-AudioComponentInstance CIOSCoreAudioHardware::FindAudioDevice(CStdString searchName)
-{
- if (!searchName.length())
- return 0;
-
- AudioComponentInstance defaultDevice = GetDefaultOutputDevice();
- CLog::Log(LOGDEBUG, "CIOSCoreAudioHardware::FindAudioDevice: Returning default device [0x%04x].", (uint32_t)defaultDevice);
-
- return defaultDevice;
-}
-
-AudioComponentInstance CIOSCoreAudioHardware::GetDefaultOutputDevice()
-{
- AudioComponentInstance ret = (AudioComponentInstance)1;
-
- return ret;
-
- /*
- OSStatus ret;
- AudioComponentInstance audioUnit;
-
- // Describe audio component
- AudioComponentDescription desc;
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
-
- // Get component
- AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
-
- // Get audio units
- ret = AudioComponentInstanceNew(inputComponent, &audioUnit);
-
- if (ret) {
- CLog::Log(LOGERROR, "CIOSCoreAudioHardware::GetDefaultOutputDevice: Unable to identify default output device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return 0;
- }
-
- return audioUnit;
- */
-}
-
-UInt32 CIOSCoreAudioHardware::GetOutputDevices(IOSCoreAudioDeviceList* pList)
-{
- if (!pList)
- return 0;
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CIOSCoreAudioDevice
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CIOSCoreAudioDevice::CIOSCoreAudioDevice() :
- m_AudioUnit(0),
- m_MixerUnit(0)
-{
-
-}
-
-CIOSCoreAudioDevice::CIOSCoreAudioDevice(AudioComponentInstance deviceId)
-{
-}
-
-CIOSCoreAudioDevice::~CIOSCoreAudioDevice()
-{
- Stop();
-}
-
-void CIOSCoreAudioDevice::SetupInfo()
-{
- AudioStreamBasicDescription pDesc;
- CStdString formatString;
-
- if(m_AudioUnit)
- {
- if(!GetFormat(m_AudioUnit, kAudioUnitScope_Output, kInputBus, &pDesc))
- return;
-
- CLog::Log(LOGDEBUG, "CIOSCoreAudioDevice::SetupInfo: Remote/IO Output Stream Bus %d Format %s",
- kInputBus, (char*)IOSStreamDescriptionToString(pDesc, formatString));
-
- if(!GetFormat(m_AudioUnit, kAudioUnitScope_Input, kOutputBus, &pDesc))
- return;
-
- CLog::Log(LOGDEBUG, "CIOSCoreAudioDevice::SetupInfo: Remote/IO Input Stream Bus %d Format %s",
- kInputBus, (char*)IOSStreamDescriptionToString(pDesc, formatString));
- }
-
- if(m_MixerUnit)
- {
- if(!GetFormat(m_AudioUnit, kAudioUnitScope_Input, kOutputBus, &pDesc))
- return;
-
- CLog::Log(LOGDEBUG, "CIOSCoreAudioDevice::SetupInfo: Remote/IO Output Stream Bus %d Format %s",
- kInputBus, (char*)IOSStreamDescriptionToString(pDesc, formatString));
-
- if(!GetFormat(m_MixerUnit, kAudioUnitScope_Input, kOutputBus, &pDesc))
- return;
-
- CLog::Log(LOGDEBUG, "CIOSCoreAudioDevice::SetupInfo: Mixer Input Stream Bus %d Format %s",
- kOutputBus, (char*)IOSStreamDescriptionToString(pDesc, formatString));
-
- if(!GetFormat(m_MixerUnit, kAudioUnitScope_Input, kInputBus, &pDesc))
- return;
-
- CLog::Log(LOGDEBUG, "CIOSCoreAudioDevice::SetupInfo: Mixer Input Stream Bus %d Format %s",
- kInputBus, (char*)IOSStreamDescriptionToString(pDesc, formatString));
- }
-
-}
-
-bool CIOSCoreAudioDevice::Init(bool bPassthrough, AudioStreamBasicDescription* pDesc, AURenderCallback renderCallback, void *pClientData)
-{
- OSStatus ret;
- AudioComponent audioComponent;
- AudioComponentDescription desc;
-
- m_AudioUnit = 0;
- m_MixerUnit = 0;
- m_Passthrough = bPassthrough;
-
- /*
- ret = AudioSessionInitialize(NULL, NULL, NULL, NULL);
- if (ret) {
- CLog::Log(LOGERROR, "CIOSCoreAudioHardware::Init: Unable to initialize session. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- ret = AudioSessionSetActive(true);
- if (ret) {
- CLog::Log(LOGERROR, "CIOSCoreAudioHardware::Init: Unable to set session active. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- */
-
- // Describe audio component
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
-
- // Get component
- audioComponent = AudioComponentFindNext(NULL, &desc);
-
- // Get audio unit
- ret = AudioComponentInstanceNew(audioComponent, &m_AudioUnit);
-
- if (ret) {
- CLog::Log(LOGERROR, "CIOSCoreAudioHardware::Init: Unable to open Remote/IO device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- if(!EnableOutput(m_AudioUnit, kOutputBus))
- return false;
-
- if(!SetFormat(m_AudioUnit, kAudioUnitScope_Input, kOutputBus, pDesc))
- return false;
-
- if(!SetFormat(m_AudioUnit, kAudioUnitScope_Output, kInputBus, pDesc))
- return false;
-
- if(!m_Passthrough) {
- // Describe audio component
- desc.componentType = kAudioUnitType_Mixer;
- desc.componentSubType = kAudioUnitSubType_AU3DMixerEmbedded;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
-
- // Get component
- audioComponent = AudioComponentFindNext(NULL, &desc);
-
- // Get mixer unit
- ret = AudioComponentInstanceNew(audioComponent, &m_MixerUnit);
- if (ret) {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Init: Unable to open Mixer device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- if(!SetFormat(m_MixerUnit, kAudioUnitScope_Input, kOutputBus, pDesc))
- return false;
-
- if(!SetFormat(m_MixerUnit, kAudioUnitScope_Input, kInputBus, pDesc))
- return false;
-
- if(!SetRenderProc(m_MixerUnit, kOutputBus, renderCallback, pClientData))
- return false;
-
- // Connect mixer to output
- AudioUnitConnection connection;
- connection.sourceAudioUnit = m_MixerUnit;
- connection.sourceOutputNumber = kOutputBus;
- connection.destInputNumber = kOutputBus;
-
- ret = AudioUnitSetProperty(m_AudioUnit, kAudioUnitProperty_MakeConnection,
- kAudioUnitScope_Input, kOutputBus, &connection, sizeof(connection));
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Init: Unable to make IO connections. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- } else {
- if(!SetRenderProc(m_AudioUnit, kOutputBus, renderCallback, pClientData))
- return false;
- }
-
- SetupInfo();
-
- return true;
-}
-
-bool CIOSCoreAudioDevice::Open()
-{
- if(!m_AudioUnit)
- return false;
-
- OSStatus ret;
-
- if(!m_Passthrough) {
- ret = AudioUnitInitialize(m_MixerUnit);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::Open: Unable to Open Mixer device. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- }
-
- ret = AudioUnitInitialize(m_AudioUnit);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::Open: Unable to Open AudioUnit. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- return true;
-}
-
-void CIOSCoreAudioDevice::Close()
-{
- if (!m_AudioUnit)
- return;
-
- Stop();
-
- OSStatus ret = AudioUnitUninitialize(m_AudioUnit);
-
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Close: Unable to close Audio device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
- ret = AudioComponentInstanceDispose(m_AudioUnit);
-
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Close: Unable to dispose Audio device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
- m_AudioUnit = 0;
-
- if(!m_Passthrough) {
- ret = AudioUnitUninitialize(m_MixerUnit);
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Close: Unable to close Mixer device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
- ret = AudioComponentInstanceDispose(m_MixerUnit);
-
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Close: Unable to dispose Mixer device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
- m_MixerUnit = 0;
- }
-}
-
-void CIOSCoreAudioDevice::Start()
-{
- if (!m_AudioUnit)
- return;
-
- OSStatus ret ;
-
- ret = AudioOutputUnitStart(m_AudioUnit);
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Start: Unable to start device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
-}
-
-void CIOSCoreAudioDevice::Stop()
-{
- if (!m_AudioUnit)
- return;
-
- OSStatus ret = AudioOutputUnitStop(m_AudioUnit);
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::Stop: Unable to stop device. Error = 0x%08x (%4.4s).", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-
-}
-
-const char* CIOSCoreAudioDevice::GetName(CStdString& name)
-{
- if (!m_AudioUnit)
- return NULL;
-
- return name.c_str();
-}
-
-bool CIOSCoreAudioDevice::EnableInput(AudioComponentInstance componentInstance, AudioUnitElement bus)
-{
- if (!componentInstance)
- return false;
-
- UInt32 flag = 0;
- OSStatus ret = AudioUnitSetProperty(componentInstance, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Input, bus, &flag, sizeof(flag));
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::EnableInput: Failed to enable input. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CIOSCoreAudioDevice::EnableOutput(AudioComponentInstance componentInstance, AudioUnitElement bus)
-{
- if (!componentInstance)
- return false;
-
- UInt32 flag = 1;
- OSStatus ret = AudioUnitSetProperty(componentInstance, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Output, bus, &flag, sizeof(flag));
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::EnableInput: Failed to enable output. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-Float32 CIOSCoreAudioDevice::GetCurrentVolume()
-{
-
- if (!m_MixerUnit)
- return false;
-
- Float32 volPct = 0.0f;
- OSStatus ret = AudioUnitGetParameter(m_MixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, &volPct);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::GetCurrentVolume: Unable to get Mixer volume. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return 0.0f;
- }
- return volPct;
-
-}
-
-bool CIOSCoreAudioDevice::SetCurrentVolume(Float32 vol)
-{
-
- if (!m_MixerUnit && m_Passthrough)
- return false;
-
- OSStatus ret = AudioUnitSetParameter(m_MixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::SetCurrentVolume: Unable to set Mixer volume. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- ret = AudioUnitSetParameter(m_MixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, kInputBus, vol, 0);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::SetCurrentVolume: Unable to set Mixer volume. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CIOSCoreAudioDevice::GetFormat(AudioComponentInstance componentInstance, AudioUnitScope scope,
- AudioUnitElement bus, AudioStreamBasicDescription* pDesc)
-{
- if (!componentInstance || !pDesc)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioUnitGetProperty(componentInstance, kAudioUnitProperty_StreamFormat,
- scope, bus, pDesc, &size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::GetFormat: Unable to get Audio Unit format bus %d. Error = 0x%08x (%4.4s)",
- (int)bus, (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-bool CIOSCoreAudioDevice::SetFormat(AudioComponentInstance componentInstance, AudioUnitScope scope,
- AudioUnitElement bus, AudioStreamBasicDescription* pDesc)
-{
- if (!componentInstance || !pDesc)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioUnitSetProperty(componentInstance, kAudioUnitProperty_StreamFormat,
- scope, bus, pDesc, size);
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioDevice::SetFormat: Unable to set Audio Unit format bus %d. Error = 0x%08x (%4.4s)",
- (int)bus, (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return true;
-}
-
-int CIOSCoreAudioDevice::FramesPerSlice(int nSlices)
-{
- if (!m_AudioUnit)
- return false;
-
- UInt32 maximumFramesPerSlice = nSlices;
- OSStatus ret = AudioUnitSetProperty(m_AudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
- kAudioUnitScope_Global, kOutputBus, &maximumFramesPerSlice, sizeof (maximumFramesPerSlice));
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::FramesPerSlice: Unable to setFramesPerSlice. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
- return maximumFramesPerSlice;
-
-
-}
-
-void CIOSCoreAudioDevice::AudioChannelLayout(int layoutTag)
-{
- if (!m_AudioUnit)
- return;
-
- struct AudioChannelLayout layout;
- layout.mChannelBitmap = 0;
- layout.mNumberChannelDescriptions = 0;
- layout.mChannelLayoutTag = layoutTag;
-
- OSStatus ret = AudioUnitSetProperty(m_AudioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, kOutputBus, &layout, sizeof (layout));
- if (ret)
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::AudioUnitSetProperty: Unable to set property kAudioUnitProperty_AudioChannelLayout. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
-}
-
-bool CIOSCoreAudioDevice::SetRenderProc(AudioComponentInstance componentInstance, AudioUnitElement bus,
- AURenderCallback callback, void* pClientData)
-{
- if (!componentInstance)
- return false;
-
- AURenderCallbackStruct callbackInfo;
- callbackInfo.inputProc = callback; // Function to be called each time the AudioUnit needs data
- callbackInfo.inputProcRefCon = pClientData; // Pointer to be returned in the callback proc
- OSStatus ret = AudioUnitSetProperty(componentInstance, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Global, bus, &callbackInfo,
- sizeof(AURenderCallbackStruct));
-
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::SetRenderProc: Unable to set AudioUnit render callback. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- return true;
-}
-
-bool CIOSCoreAudioDevice::SetSessionListener(AudioSessionPropertyID inID,
- AudioSessionPropertyListener inProc, void* pClientData)
-{
- OSStatus ret = AudioSessionAddPropertyListener(inID, inProc, pClientData);
-
- if (ret)
- {
- CLog::Log(LOGERROR, "CIOSCoreAudioUnit::SetSessionListener: Unable to set Session Listener Callback. Error = 0x%08x (%4.4s)", (uint32_t)ret, CONVERT_OSSTATUS(ret));
- return false;
- }
-
- return true;
-}
-
-#endif
diff --git a/xbmc/osx/IOSCoreAudio.h b/xbmc/osx/IOSCoreAudio.h
deleted file mode 100644
index 1c6f6b4775..0000000000
--- a/xbmc/osx/IOSCoreAudio.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#ifndef __COREAUDIO_H__
-#define __COREAUDIO_H__
-
-#if defined(__APPLE__)
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-#include <AudioToolbox/AudioServices.h>
-#include <CoreAudio/CoreAudioTypes.h>
-#include <list>
-#include <vector>
-
-#include "utils/StdString.h"
-
-#define kOutputBus 0
-#define kInputBus 1
-
-// Forward declarations
-class CIOSCoreAudioHardware;
-class CIOSCoreAudioDevice;
-class CIOSCoreAudioUnit;
-
-typedef std::list<AudioComponentInstance> IOSCoreAudioDeviceList;
-
-// There is only one AudioSystemObject instance system-side.
-// Therefore, all CIOSCoreAudioHardware methods are static
-class CIOSCoreAudioHardware
-{
-public:
- static AudioComponentInstance FindAudioDevice(CStdString deviceName);
- static AudioComponentInstance GetDefaultOutputDevice();
- static UInt32 GetOutputDevices(IOSCoreAudioDeviceList* pList);
-};
-
-class CIOSCoreAudioDevice
-{
-public:
- CIOSCoreAudioDevice();
- virtual ~CIOSCoreAudioDevice();
-
- AudioComponentInstance GetId() {return m_AudioUnit;}
- const char* GetName(CStdString& name);
- CIOSCoreAudioDevice(AudioComponentInstance deviceId);
- UInt32 GetTotalOutputChannels();
-
- void Attach(AudioUnit audioUnit) {m_AudioUnit = audioUnit;}
- AudioComponentInstance GetComponent(){return m_AudioUnit;}
-
- void SetupInfo();
- bool Init(bool bPassthrough, AudioStreamBasicDescription* pDesc, AURenderCallback renderCallback, void *pClientData);
- bool Open();
- void Close();
- void Start();
- void Stop();
-
- bool EnableInput(AudioComponentInstance componentInstance, AudioUnitElement bus);
- bool EnableOutput(AudioComponentInstance componentInstance, AudioUnitElement bus);
- bool GetFormat(AudioComponentInstance componentInstance, AudioUnitScope scope,
- AudioUnitElement bus, AudioStreamBasicDescription* pDesc);
- bool SetFormat(AudioComponentInstance componentInstance, AudioUnitScope scope,
- AudioUnitElement bus, AudioStreamBasicDescription* pDesc);
- Float32 GetCurrentVolume();
- bool SetCurrentVolume(Float32 vol);
- int FramesPerSlice(int nSlices);
- void AudioChannelLayout(int layoutTag);
- bool SetRenderProc(AudioComponentInstance componentInstance, AudioUnitElement bus,
- AURenderCallback callback, void* pClientData);
- bool SetSessionListener(AudioSessionPropertyID inID,
- AudioSessionPropertyListener inProc, void* pClientData);
-
- AudioComponentInstance m_AudioUnit;
- AudioComponentInstance m_MixerUnit;
- bool m_Passthrough;
-};
-
-// Helper Functions
-char* IOSUInt32ToFourCC(UInt32* val);
-const char* IOSStreamDescriptionToString(AudioStreamBasicDescription desc, CStdString& str);
-
-#define CONVERT_OSSTATUS(x) IOSUInt32ToFourCC((UInt32*)&ret)
-
-#endif
-#endif // __COREAUDIO_H__
diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
index b1a3416bea..f8db18bcac 100644
--- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
+++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
@@ -1188,7 +1188,7 @@ bool CPeripheralCecAdapterUpdateThread::SetInitialConfiguration(void)
// set amp present
m_adapter->SetAudioSystemConnected(true);
g_settings.m_bMute = false;
- g_settings.m_nVolumeLevel = VOLUME_MAXIMUM;
+ g_settings.m_fVolumeLevel = VOLUME_MAXIMUM;
}
m_adapter->m_bIsReady = true;
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index 71fa0ff322..37eef938f6 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -49,6 +49,11 @@ void CAdvancedSettings::Initialize()
m_ac3Gain = 12.0f;
m_audioApplyDrc = true;
m_dvdplayerIgnoreDTSinWAV = false;
+ m_audioResample = 0;
+ m_audioForceDirectSound = false;
+ m_audioAudiophile = false;
+ m_allChannelStereo = false;
+ m_audioSinkBufferDurationMsec = 50;
//default hold time of 25 ms, this allows a 20 hertz sine to pass undistorted
m_limiterHold = 0.025f;
@@ -112,7 +117,6 @@ void CAdvancedSettings::Initialize()
m_musicPercentSeekBackward = -1;
m_musicPercentSeekForwardBig = 10;
m_musicPercentSeekBackwardBig = -10;
- m_musicResample = 0;
m_slideshowPanAmount = 2.5f;
m_slideshowZoomAmount = 5.0f;
@@ -357,7 +361,12 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file)
XMLUtils::GetInt(pElement, "percentseekforwardbig", m_musicPercentSeekForwardBig, 0, 100);
XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_musicPercentSeekBackwardBig, -100, 0);
- XMLUtils::GetInt(pElement, "resample", m_musicResample, 0, 192000);
+ XMLUtils::GetInt(pElement, "resample", m_audioResample, 0, 192000);
+ XMLUtils::GetBoolean(pElement, "forceDirectSound", m_audioForceDirectSound);
+ XMLUtils::GetBoolean(pElement, "audiophile", m_audioAudiophile);
+ XMLUtils::GetBoolean(pElement, "allchannelstereo", m_allChannelStereo);
+ XMLUtils::GetString(pElement, "transcodeto", m_audioTranscodeTo);
+ XMLUtils::GetInt(pElement, "audiosinkbufferdurationmsec", m_audioSinkBufferDurationMsec);
TiXmlElement* pAudioExcludes = pElement->FirstChildElement("excludefromlisting");
if (pAudioExcludes)
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
index 7a9d0dcc4a..81a87723c6 100644
--- a/xbmc/settings/AdvancedSettings.h
+++ b/xbmc/settings/AdvancedSettings.h
@@ -94,6 +94,12 @@ class CAdvancedSettings
CStdString m_audioDefaultPlayer;
float m_audioPlayCountMinimumPercent;
bool m_dvdplayerIgnoreDTSinWAV;
+ int m_audioResample;
+ bool m_audioForceDirectSound;
+ bool m_audioAudiophile;
+ bool m_allChannelStereo;
+ int m_audioSinkBufferDurationMsec;
+ CStdString m_audioTranscodeTo;
float m_limiterHold;
float m_limiterRelease;
@@ -122,7 +128,6 @@ class CAdvancedSettings
int m_musicPercentSeekBackward;
int m_musicPercentSeekForwardBig;
int m_musicPercentSeekBackwardBig;
- int m_musicResample;
int m_videoBlackBarColour;
int m_videoIgnoreSecondsAtStart;
float m_videoIgnorePercentAtEnd;
diff --git a/xbmc/settings/GUISettings.cpp b/xbmc/settings/GUISettings.cpp
index 29ad1fa620..c1402ec49f 100644
--- a/xbmc/settings/GUISettings.cpp
+++ b/xbmc/settings/GUISettings.cpp
@@ -38,13 +38,17 @@
#include "windowing/WindowingFactory.h"
#include "powermanagement/PowerManager.h"
#include "cores/dvdplayer/DVDCodecs/Video/CrystalHD.h"
-#include "utils/PCMRemap.h"
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/AEAudioFormat.h"
#include "guilib/GUIFont.h" // for FONT_STYLE_* definitions
+#if defined(TARGET_DARWIN_OSX)
+ #include "CoreAudioAEHALOSX.h"
+#endif
#include "guilib/GUIFontManager.h"
#include "utils/Weather.h"
#include "LangInfo.h"
#include "utils/XMLUtils.h"
-#if defined(__APPLE__)
+#if defined(TARGET_DARWIN)
#include "osx/DarwinUtils.h"
#endif
@@ -446,32 +450,55 @@ void CGUISettings::Initialize()
AddInt(ao, "audiooutput.mode", 337, AUDIO_ANALOG, audiomode, SPIN_CONTROL_TEXT);
map<int,int> channelLayout;
- for(int layout = 0; layout < PCM_MAX_LAYOUT; ++layout)
- channelLayout.insert(make_pair(34101+layout, layout));
- AddInt(ao, "audiooutput.channellayout", 34100, PCM_LAYOUT_2_0, channelLayout, SPIN_CONTROL_TEXT);
+ for(int layout = AE_CH_LAYOUT_2_0; layout < AE_CH_LAYOUT_MAX; ++layout)
+ channelLayout.insert(make_pair(34100+layout, layout));
+ AddInt(ao, "audiooutput.channellayout", 34100, AE_CH_LAYOUT_2_0, channelLayout, SPIN_CONTROL_TEXT);
AddBool(ao, "audiooutput.dontnormalizelevels", 346, true);
+ AddBool(ao, "audiooutput.stereoupmix", 252, false);
+
+#if defined(TARGET_DARWIN_IOS)
+ CSettingsCategory* aocat = g_sysinfo.IsAppleTV2() ? ao : NULL;
+#else
+ CSettingsCategory* aocat = ao;
+#endif
- AddBool(ao, "audiooutput.ac3passthrough", 364, true);
- AddBool(ao, "audiooutput.dtspassthrough", 254, true);
- AddBool(NULL, "audiooutput.passthroughaac", 299, false);
- AddBool(NULL, "audiooutput.passthroughmp1", 300, false);
- AddBool(NULL, "audiooutput.passthroughmp2", 301, false);
- AddBool(NULL, "audiooutput.passthroughmp3", 302, false);
+ AddBool(aocat, "audiooutput.ac3passthrough" , 364, true);
+ AddBool(aocat, "audiooutput.dtspassthrough" , 254, true);
-#ifdef __APPLE__
- AddString(ao, "audiooutput.audiodevice", 545, "Default", SPIN_CONTROL_TEXT);
-#elif defined(_LINUX)
+
+#if !defined(TARGET_DARWIN)
+ AddBool(aocat, "audiooutput.passthroughaac" , 299, false);
+#endif
+#if !defined(TARGET_DARWIN_IOS)
+ AddBool(aocat, "audiooutput.multichannellpcm" , 348, true );
+#endif
+#if !defined(TARGET_DARWIN)
+ AddBool(aocat, "audiooutput.truehdpassthrough", 349, true );
+ AddBool(aocat, "audiooutput.dtshdpassthrough" , 407, true );
+#endif
+
+#if defined(TARGET_DARWIN)
+ #if defined(TARGET_DARWIN_IOS)
+ CStdString defaultDeviceName = "Default";
+ #else
+ CStdString defaultDeviceName;
+ CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
+ #endif
+ AddString(ao, "audiooutput.audiodevice", 545, defaultDeviceName.c_str(), SPIN_CONTROL_TEXT);
+ AddString(NULL, "audiooutput.passthroughdevice", 546, defaultDeviceName.c_str(), SPIN_CONTROL_TEXT);
+#else
AddSeparator(ao, "audiooutput.sep1");
- AddString(ao, "audiooutput.audiodevice", 545, "default", SPIN_CONTROL_TEXT);
- AddString(ao, "audiooutput.customdevice", 1300, "", EDIT_CONTROL_INPUT);
+ AddString (ao, "audiooutput.audiodevice" , 545, CStdString(CAEFactory::AE->GetDefaultDevice(false)), SPIN_CONTROL_TEXT);
+ AddString (ao, "audiooutput.passthroughdevice", 546, CStdString(CAEFactory::AE->GetDefaultDevice(true )), SPIN_CONTROL_TEXT);
AddSeparator(ao, "audiooutput.sep2");
- AddString(ao, "audiooutput.passthroughdevice", 546, "iec958", SPIN_CONTROL_TEXT);
- AddString(ao, "audiooutput.custompassthrough", 1301, "", EDIT_CONTROL_INPUT);
- AddSeparator(ao, "audiooutput.sep3");
-#elif defined(_WIN32)
- AddString(ao, "audiooutput.audiodevice", 545, "Default", SPIN_CONTROL_TEXT);
#endif
+ map<int,int> guimode;
+ guimode.insert(make_pair(34121, AE_SOUND_IDLE ));
+ guimode.insert(make_pair(34122, AE_SOUND_ALWAYS));
+ guimode.insert(make_pair(34123, AE_SOUND_OFF ));
+ AddInt(ao, "audiooutput.guisoundmode", 34120, AE_SOUND_IDLE, guimode, SPIN_CONTROL_TEXT);
+
CSettingsCategory* in = AddCategory(4, "input", 14094);
AddString(in, "input.peripherals", 35000, "", BUTTON_CONTROL_STANDARD);
#if defined(__APPLE__)
@@ -1220,9 +1247,6 @@ void CGUISettings::LoadXML(TiXmlElement *pRootElement, bool hideSettings /* = fa
CLog::Log(LOGINFO, "AC3 pass through is %s", GetBool("audiooutput.ac3passthrough") ? "enabled" : "disabled");
CLog::Log(LOGINFO, "DTS pass through is %s", GetBool("audiooutput.dtspassthrough") ? "enabled" : "disabled");
CLog::Log(LOGINFO, "AAC pass through is %s", GetBool("audiooutput.passthroughaac") ? "enabled" : "disabled");
- CLog::Log(LOGINFO, "MP1 pass through is %s", GetBool("audiooutput.passthroughmp1") ? "enabled" : "disabled");
- CLog::Log(LOGINFO, "MP2 pass through is %s", GetBool("audiooutput.passthroughmp2") ? "enabled" : "disabled");
- CLog::Log(LOGINFO, "MP3 pass through is %s", GetBool("audiooutput.passthroughmp3") ? "enabled" : "disabled");
g_guiSettings.m_LookAndFeelResolution = GetResolution();
#ifdef __APPLE__
diff --git a/xbmc/settings/GUIWindowSettingsCategory.cpp b/xbmc/settings/GUIWindowSettingsCategory.cpp
index 469a88fadc..f31113e63c 100644
--- a/xbmc/settings/GUIWindowSettingsCategory.cpp
+++ b/xbmc/settings/GUIWindowSettingsCategory.cpp
@@ -41,7 +41,6 @@
#include "PlayListPlayer.h"
#include "addons/Skin.h"
#include "guilib/GUIAudioManager.h"
-#include "guilib/AudioContext.h"
#include "network/libscrobbler/lastfmscrobbler.h"
#include "network/libscrobbler/librefmscrobbler.h"
#include "GUIPassword.h"
@@ -64,25 +63,17 @@
#include "guilib/GUIControlGroupList.h"
#include "guilib/GUIWindowManager.h"
#include "guilib/GUIFontManager.h"
+#include "cores/AudioEngine/AEFactory.h"
#ifdef _LINUX
#include "LinuxTimezone.h"
#include <dlfcn.h>
-#include "cores/AudioRenderers/AudioRendererFactory.h"
-#if defined(USE_ALSA)
-#include "cores/AudioRenderers/ALSADirectSound.h"
-#endif
#ifdef HAS_HAL
#include "HALManager.h"
#endif
#endif
-#if defined(__APPLE__)
-#if defined(__arm__)
-#include "IOSCoreAudio.h"
-#else
-#include "CoreAudio.h"
+#if defined(TARGET_DARWIN_OSX)
#include "XBMCHelper.h"
#endif
-#endif
#include "network/GUIDialogAccessPoints.h"
#include "filesystem/Directory.h"
@@ -96,7 +87,6 @@
#ifdef _WIN32
#include "WIN32Util.h"
-#include "cores/AudioRenderers/AudioRendererFactory.h"
#endif
#include <map>
#include "Settings.h"
@@ -106,6 +96,7 @@
#include "LangInfo.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
+#include "utils/SystemInfo.h"
#include "windowing/WindowingFactory.h"
#if defined(HAVE_LIBCRYSTALHD)
@@ -709,14 +700,29 @@ void CGUIWindowSettingsCategory::UpdateSettings()
strSetting.Equals("audiooutput.passthroughdevice") ||
strSetting.Equals("audiooutput.ac3passthrough") ||
strSetting.Equals("audiooutput.dtspassthrough") ||
- strSetting.Equals("audiooutput.passthroughaac") ||
- strSetting.Equals("audiooutput.passthroughmp1") ||
- strSetting.Equals("audiooutput.passthroughmp2") ||
- strSetting.Equals("audiooutput.passthroughmp3"))
+ strSetting.Equals("audiooutput.passthroughaac"))
{ // only visible if we are in digital mode
CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
if (pControl) pControl->SetEnabled(AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode")));
}
+ else if (
+ strSetting.Equals("audiooutput.multichannellpcm" ) ||
+ strSetting.Equals("audiooutput.truehdpassthrough") ||
+ strSetting.Equals("audiooutput.dtshdpassthrough" ))
+ {
+ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
+ if (pControl)
+ {
+ if (strSetting.Equals("audiooutput.dtshdpassthrough") && !g_guiSettings.GetBool("audiooutput.dtspassthrough"))
+ pControl->SetEnabled(false);
+ else
+ pControl->SetEnabled(g_guiSettings.GetInt("audiooutput.mode") == AUDIO_HDMI);
+ }
+ }
+ else if (strSetting.Equals("audiooutput.guisoundmode"))
+ {
+ CAEFactory::AE->SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode"));
+ }
else if (strSetting.Equals("musicplayer.crossfade"))
{
CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
@@ -950,25 +956,6 @@ void CGUIWindowSettingsCategory::UpdateSettings()
if (pControl)
pControl->SetEnabled(g_peripherals.GetNumberOfPeripherals() > 0);
}
-#if defined(_LINUX) && !defined(__APPLE__)
- else if (strSetting.Equals("audiooutput.custompassthrough"))
- {
- CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
- if (AUDIO_IS_BITSTREAM(g_guiSettings.GetInt("audiooutput.mode")))
- {
- if (pControl) pControl->SetEnabled(g_guiSettings.GetString("audiooutput.passthroughdevice").Equals("custom"));
- }
- else
- {
- if (pControl) pControl->SetEnabled(false);
- }
- }
- else if (strSetting.Equals("audiooutput.customdevice"))
- {
- CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
- if (pControl) pControl->SetEnabled(g_guiSettings.GetString("audiooutput.audiodevice").Equals("custom"));
- }
-#endif
}
}
@@ -1219,26 +1206,6 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
g_guiSettings.m_replayGain.iNoGainPreAmp = g_guiSettings.GetInt("musicplayer.replaygainnogainpreamp");
g_guiSettings.m_replayGain.bAvoidClipping = g_guiSettings.GetBool("musicplayer.replaygainavoidclipping");
}
- else if (strSetting.Equals("audiooutput.audiodevice"))
- {
- CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(pSettingControl->GetID());
-#if !defined(__APPLE__)
- g_guiSettings.SetString("audiooutput.audiodevice", m_AnalogAudioSinkMap[pControl->GetCurrentLabel()]);
-#else
- g_guiSettings.SetString("audiooutput.audiodevice", pControl->GetCurrentLabel());
-#endif
- }
-#if defined(_LINUX)
- else if (strSetting.Equals("audiooutput.passthroughdevice"))
- {
- CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(pSettingControl->GetID());
-#if defined(_LINUX) && !defined(__APPLE__)
- g_guiSettings.SetString("audiooutput.passthroughdevice", m_DigitalAudioSinkMap[pControl->GetCurrentLabel()]);
-#elif !defined(__arm__)
- g_guiSettings.SetString("audiooutput.passthroughdevice", pControl->GetCurrentLabel());
-#endif
- }
-#endif
#ifdef HAS_LCD
else if (strSetting.Equals("videoscreen.haslcd"))
{
@@ -1871,6 +1838,27 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
{
CUtil::DeleteVideoDatabaseDirectoryCache();
}
+ else if (strSetting.compare(0, 12, "audiooutput.") == 0)
+ {
+ if (strSetting.Equals("audiooutput.audiodevice"))
+ {
+ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(pSettingControl->GetID());
+#if defined(TARGET_DARWIN)
+ g_guiSettings.SetString("audiooutput.audiodevice", pControl->GetCurrentLabel());
+#else
+ g_guiSettings.SetString("audiooutput.audiodevice", m_AnalogAudioSinkMap[pControl->GetCurrentLabel()]);
+#endif
+ }
+#if !defined(TARGET_DARWIN)
+ else if (strSetting.Equals("audiooutput.passthroughdevice"))
+ {
+ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(pSettingControl->GetID());
+ g_guiSettings.SetString("audiooutput.passthroughdevice", m_DigitalAudioSinkMap[pControl->GetCurrentLabel()]);
+ }
+#endif
+
+ CAEFactory::AE->OnSettingsChange(strSetting);
+ }
UpdateSettings();
}
@@ -2701,62 +2689,6 @@ void CGUIWindowSettingsCategory::FillInNetworkInterfaces(CSetting *pSetting, flo
void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Passthrough)
{
-#if defined(__APPLE__)
- #if defined(__arm__)
- if (Passthrough)
- return;
- CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID());
- pControl->Clear();
-
- IOSCoreAudioDeviceList deviceList;
- CIOSCoreAudioHardware::GetOutputDevices(&deviceList);
-
- // This will cause FindAudioDevice to fall back to the system default as configured in 'System Preferences'
- if (CIOSCoreAudioHardware::GetDefaultOutputDevice())
- pControl->AddLabel("Default Output Device", 0);
-
- int activeDevice = 0;
- CStdString deviceName;
- for (int i = pControl->GetMaximum(); !deviceList.empty(); i++)
- {
- CIOSCoreAudioDevice device(deviceList.front());
- pControl->AddLabel(device.GetName(deviceName), i);
-
- // Tag this one
- if (g_guiSettings.GetString("audiooutput.audiodevice").Equals(deviceName))
- activeDevice = i;
-
- deviceList.pop_front();
- }
- pControl->SetValue(activeDevice);
- #else
- if (Passthrough)
- return;
- CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID());
- pControl->Clear();
-
- CoreAudioDeviceList deviceList;
- CCoreAudioHardware::GetOutputDevices(&deviceList);
-
- // This will cause FindAudioDevice to fall back to the system default as configured in 'System Preferences'
- if (CCoreAudioHardware::GetDefaultOutputDevice())
- pControl->AddLabel("Default Output Device", 0);
-
- int activeDevice = 0;
- CStdString deviceName;
- for (int i = pControl->GetMaximum(); !deviceList.empty(); i++)
- {
- CCoreAudioDevice device(deviceList.front());
- pControl->AddLabel(device.GetName(deviceName), i);
-
- if (g_guiSettings.GetString("audiooutput.audiodevice").Equals(deviceName))
- activeDevice = i; // Tag this one
-
- deviceList.pop_front();
- }
- pControl->SetValue(activeDevice);
- #endif
-#else
CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID());
pControl->Clear();
@@ -2766,20 +2698,19 @@ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Pas
{
m_DigitalAudioSinkMap.clear();
m_DigitalAudioSinkMap["Error - no devices found"] = "null:";
- m_DigitalAudioSinkMap[g_localizeStrings.Get(636)] = "custom";
}
else
{
m_AnalogAudioSinkMap.clear();
m_AnalogAudioSinkMap["Error - no devices found"] = "null:";
- m_AnalogAudioSinkMap[g_localizeStrings.Get(636)] = "custom";
}
int numberSinks = 0;
int selectedValue = -1;
- AudioSinkList sinkList;
- CAudioRendererFactory::EnumerateAudioSinks(sinkList, Passthrough);
+ AEDeviceList sinkList;
+ CAEFactory::AE->EnumerateOutputDevices(sinkList, Passthrough);
+#if !defined(TARGET_DARWIN)
if (sinkList.size()==0)
{
pControl->AddLabel("Error - no devices found", 0);
@@ -2788,7 +2719,8 @@ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Pas
}
else
{
- AudioSinkList::const_iterator iter = sinkList.begin();
+#endif
+ AEDeviceList::const_iterator iter = sinkList.begin();
for (int i=0; iter != sinkList.end(); iter++)
{
CStdString label = (*iter).first;
@@ -2807,13 +2739,8 @@ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Pas
}
numberSinks = sinkList.size();
+#if !defined(TARGET_DARWIN)
}
-
-#ifdef _LINUX
- if (currentDevice.Equals("custom"))
- selectedValue = numberSinks;
-
- pControl->AddLabel(g_localizeStrings.Get(636), numberSinks++);
#endif
if (selectedValue < 0)
@@ -2824,7 +2751,6 @@ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Pas
}
else
pControl->SetValue(selectedValue);
-#endif
}
void CGUIWindowSettingsCategory::NetworkInterfaceChanged(void)
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index 8230cda1b1..e1263101c6 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -32,8 +32,6 @@
#include "PasswordManager.h"
#include "utils/RegExp.h"
#include "GUIPassword.h"
-#include "guilib/GUIAudioManager.h"
-#include "guilib/AudioContext.h"
#include "GUIInfoManager.h"
#include "filesystem/MultiPathDirectory.h"
#include "filesystem/SpecialProtocol.h"
@@ -95,9 +93,7 @@ void CSettings::Initialize()
m_bAddonNotifications = true;
m_bAddonForeignFilter = false;
- m_nVolumeLevel = 0;
- m_dynamicRangeCompressionLevel = 0;
- m_iPreMuteVolumeLevel = 0;
+ m_fVolumeLevel = 1.0f;
m_bMute = false;
m_fZoomAmount = 1.0f;
m_fPixelRatio = 1.0f;
@@ -726,9 +722,7 @@ bool CSettings::LoadSettings(const CStdString& strSettingsFile)
if (pElement)
{
XMLUtils::GetBoolean(pElement, "mute", m_bMute);
- GetInteger(pElement, "volumelevel", m_nVolumeLevel, VOLUME_MAXIMUM, VOLUME_MINIMUM, VOLUME_MAXIMUM);
- GetInteger(pElement, "premutevolumelevel", m_iPreMuteVolumeLevel, 0, 0, 100);
- GetInteger(pElement, "dynamicrangecompression", m_dynamicRangeCompressionLevel, VOLUME_DRC_MINIMUM, VOLUME_DRC_MINIMUM, VOLUME_DRC_MAXIMUM);
+ GetFloat(pElement, "fvolumelevel", m_fVolumeLevel, VOLUME_MAXIMUM, VOLUME_MINIMUM, VOLUME_MAXIMUM);
}
LoadCalibration(pRootElement, strSettingsFile);
@@ -905,9 +899,7 @@ bool CSettings::SaveSettings(const CStdString& strSettingsFile, CGUISettings *lo
pNode = pRoot->InsertEndChild(volumeNode);
if (!pNode) return false;
XMLUtils::SetBoolean(pNode, "mute", m_bMute);
- XMLUtils::SetInt(pNode, "volumelevel", m_nVolumeLevel);
- XMLUtils::SetInt(pNode, "premutevolumelevel", m_iPreMuteVolumeLevel);
- XMLUtils::SetInt(pNode, "dynamicrangecompression", m_dynamicRangeCompressionLevel);
+ XMLUtils::SetFloat(pNode, "fvolumelevel", m_fVolumeLevel);
SaveCalibration(pRoot);
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index 8ca148324b..38c168e185 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -60,8 +60,10 @@
#define CACHE_VIDEO 1
#define CACHE_VOB 2
-#define VOLUME_MINIMUM -6000 // -60dB
-#define VOLUME_MAXIMUM 0 // 0dB
+#define VOLUME_MINIMUM 0.0f // -60dB
+#define VOLUME_MAXIMUM 1.0f // 0dB
+#define VOLUME_DYNAMIC_RANGE 90.0f // 60dB
+#define VOLUME_CONTROL_STEPS 90 // 90 steps
#define VOLUME_DRC_MINIMUM 0 // 0dB
#define VOLUME_DRC_MAXIMUM 6000 // 60dB
@@ -218,9 +220,7 @@ public:
int m_HttpApiBroadcastPort;
int m_HttpApiBroadcastLevel;
- int m_nVolumeLevel; // measured in milliBels -60dB -> 0dB range.
- int m_dynamicRangeCompressionLevel; // measured in milliBels 0dB -> 30dB range.
- int m_iPreMuteVolumeLevel; // save the m_nVolumeLevel for proper restore
+ float m_fVolumeLevel; // float 0.0 - 1.0 range
bool m_bMute;
int m_iSystemTimeTotalUp; // Uptime in minutes!
diff --git a/xbmc/system.h b/xbmc/system.h
index 287282cd7d..db525f7ac9 100644
--- a/xbmc/system.h
+++ b/xbmc/system.h
@@ -130,7 +130,6 @@
#if defined(TARGET_DARWIN_OSX)
#define HAS_GL
#define HAS_SDL
- #define HAS_SDL_AUDIO
#define HAS_SDL_OPENGL
#define HAS_SDL_WIN_EVENTS
#endif
@@ -160,7 +159,6 @@
#ifndef HAS_SDL_OPENGL
#define HAS_SDL_OPENGL
#endif
-#define HAS_SDL_AUDIO
#define HAS_SDL_WIN_EVENTS
#endif
#define HAS_LINUX_NETWORK
@@ -171,6 +169,9 @@
#ifdef HAVE_LIBXRANDR
#define HAS_XRANDR
#endif
+#ifdef HAVE_ALSA
+#define HAS_ALSA
+#endif
#endif
#ifdef HAVE_LIBSSH
diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile
index e707a03ca1..79da051ef3 100644
--- a/xbmc/utils/Makefile
+++ b/xbmc/utils/Makefile
@@ -35,8 +35,6 @@ SRCS=AlarmClock.cpp \
log.cpp \
md5.cpp \
Mime.cpp \
- PCMAmplifier.cpp \
- PCMRemap.cpp \
PerformanceSample.cpp \
PerformanceStats.cpp \
POUtils.cpp \
diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h
index d914e43cb4..e97566bfde 100644
--- a/xbmc/utils/MathUtils.h
+++ b/xbmc/utils/MathUtils.h
@@ -25,6 +25,10 @@
#include <climits>
#include <cmath>
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+
/*! \brief Math utility class.
Note that the test() routine should return true for all implementations
@@ -52,8 +56,11 @@ namespace MathUtils
assert(x < static_cast <double>(INT_MAX / 2) + 1.0);
const float round_to_nearest = 0.5f;
int i;
-
-#ifndef _LINUX
+
+#if defined(__SSE2__)
+ const float round_dn_to_nearest = 0.4999999f;
+ i = (x > 0) ? _mm_cvttsd_si32(_mm_set_sd(x + round_to_nearest)) : _mm_cvttsd_si32(_mm_set_sd(x - round_dn_to_nearest));
+#elif !defined(_LINUX)
__asm
{
fld x
diff --git a/xbmc/utils/PCMAmplifier.cpp b/xbmc/utils/PCMAmplifier.cpp
deleted file mode 100644
index e8fe832715..0000000000
--- a/xbmc/utils/PCMAmplifier.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "PCMAmplifier.h"
-#include "settings/Settings.h"
-
-#include <math.h>
-
-CPCMAmplifier::CPCMAmplifier() : m_nVolume(VOLUME_MAXIMUM), m_dFactor(0)
-{
-}
-
-CPCMAmplifier::~CPCMAmplifier()
-{
-}
-
-void CPCMAmplifier::SetVolume(int nVolume)
-{
- m_nVolume = nVolume;
- if (nVolume > VOLUME_MAXIMUM)
- nVolume = VOLUME_MAXIMUM;
-
- if (nVolume < VOLUME_MINIMUM)
- nVolume = VOLUME_MINIMUM;
-
- if( nVolume == VOLUME_MINIMUM)
- m_dFactor = 0;
- else
- m_dFactor = pow(10,nVolume/2000.f);
-}
-
-int CPCMAmplifier::GetVolume()
-{
- return m_nVolume;
-}
-
- // only works on 16bit samples
-void CPCMAmplifier::DeAmplify(short *pcm, int nSamples)
-{
- if (m_dFactor >= 1.0)
- {
- // no process required. using >= to make sure no amp is ever done (only de-amp)
- return;
- }
-
- for (int nSample=0; nSample<nSamples; nSample++)
- {
- int nSampleValue = pcm[nSample]; // must be int. so that we can check over/under flow
- nSampleValue = (int)((double)nSampleValue * m_dFactor);
-
- pcm[nSample] = (short)nSampleValue;
- }
-}
diff --git a/xbmc/utils/PCMRemap.cpp b/xbmc/utils/PCMRemap.cpp
deleted file mode 100644
index bd1884bf0b..0000000000
--- a/xbmc/utils/PCMRemap.cpp
+++ /dev/null
@@ -1,804 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#define __STDC_LIMIT_MACROS
-
-#include <cstdlib>
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-
-#include "MathUtils.h"
-#include "PCMRemap.h"
-#include "utils/log.h"
-#include "settings/GUISettings.h"
-#include "settings/AdvancedSettings.h"
-#ifdef _WIN32
-#include "../win32/PlatformDefs.h"
-#endif
-
-static enum PCMChannels PCMLayoutMap[PCM_MAX_LAYOUT][PCM_MAX_CH + 1] =
-{
- /* 2.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_INVALID},
- /* 2.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
- /* 3.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_INVALID},
- /* 3.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_INVALID},
- /* 4.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
- /* 4.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
- /* 5.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
- /* 5.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
- /* 7.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
- /* 7.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID}
-};
-
-/*
- map missing output into channel @ volume level
- the order of this table is important, mix tables can not depend on channels that have not been defined yet
- eg, FC can only be mixed into FL, FR as they are the only channels that have been defined
-*/
-#define PCM_MAX_MIX 3
-static struct PCMMapInfo PCMDownmixTable[PCM_MAX_CH][PCM_MAX_MIX] =
-{
- /* PCM_FRONT_LEFT */
- {
- {PCM_INVALID}
- },
- /* PCM_FRONT_RIGHT */
- {
- {PCM_INVALID}
- },
- /* PCM_FRONT_CENTER */
- {
- {PCM_FRONT_LEFT_OF_CENTER , 1.0},
- {PCM_FRONT_RIGHT_OF_CENTER, 1.0},
- {PCM_INVALID}
- },
- /* PCM_LOW_FREQUENCY */
- {
- /*
- A/52B 7.8 paragraph 2 recomends +10db
- but due to horrible clipping when normalize
- is disabled we set this to 1.0
- */
- {PCM_FRONT_LEFT , 1.0},//3.5},
- {PCM_FRONT_RIGHT , 1.0},//3.5},
- {PCM_INVALID}
- },
- /* PCM_BACK_LEFT */
- {
- {PCM_FRONT_LEFT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_BACK_RIGHT */
- {
- {PCM_FRONT_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_FRONT_LEFT_OF_CENTER */
- {
- {PCM_FRONT_LEFT , 1.0},
- {PCM_FRONT_CENTER , 1.0, true},
- {PCM_INVALID}
- },
- /* PCM_FRONT_RIGHT_OF_CENTER */
- {
- {PCM_FRONT_RIGHT , 1.0},
- {PCM_FRONT_CENTER , 1.0, true},
- {PCM_INVALID}
- },
- /* PCM_BACK_CENTER */
- {
- {PCM_BACK_LEFT , 1.0},
- {PCM_BACK_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_SIDE_LEFT */
- {
- {PCM_FRONT_LEFT , 1.0},
- {PCM_BACK_LEFT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_SIDE_RIGHT */
- {
- {PCM_FRONT_RIGHT , 1.0},
- {PCM_BACK_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_FRONT_LEFT */
- {
- {PCM_FRONT_LEFT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_FRONT_RIGHT */
- {
- {PCM_FRONT_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_FRONT_CENTER */
- {
- {PCM_TOP_FRONT_LEFT , 1.0},
- {PCM_TOP_FRONT_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_CENTER */
- {
- {PCM_TOP_FRONT_LEFT , 1.0},
- {PCM_TOP_FRONT_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_BACK_LEFT */
- {
- {PCM_BACK_LEFT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_BACK_RIGHT */
- {
- {PCM_BACK_RIGHT , 1.0},
- {PCM_INVALID}
- },
- /* PCM_TOP_BACK_CENTER */
- {
- {PCM_TOP_BACK_LEFT , 1.0},
- {PCM_TOP_BACK_RIGHT , 1.0},
- {PCM_INVALID}
- }
-};
-
-CPCMRemap::CPCMRemap() :
- m_inSet (false),
- m_outSet (false),
- m_inChannels (0),
- m_outChannels (0),
- m_inSampleSize(0),
- m_ignoreLayout(false),
- m_buf(NULL),
- m_bufsize(0),
- m_attenuation (1.0),
- m_attenuationInc(0.0),
- m_attenuationMin(1.0),
- m_sampleRate (48000.0), //safe default
- m_holdCounter (0),
- m_limiterEnabled(false)
-{
- Dispose();
-}
-
-CPCMRemap::~CPCMRemap()
-{
- Dispose();
-}
-
-void CPCMRemap::Dispose()
-{
- free(m_buf);
- m_buf = NULL;
- m_bufsize = 0;
-}
-
-/* resolves the channels recursively and returns the new index of tablePtr */
-struct PCMMapInfo* CPCMRemap::ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr)
-{
- if (channel == PCM_INVALID) return tablePtr;
-
- /* if its a 1 to 1 mapping, return */
- if (m_useable[channel])
- {
- tablePtr->channel = channel;
- tablePtr->level = level;
-
- ++tablePtr;
- tablePtr->channel = PCM_INVALID;
- return tablePtr;
- } else
- if (ifExists)
- level /= 2;
-
- struct PCMMapInfo *info;
- std::vector<enum PCMChannels>::iterator itt;
-
- for(info = PCMDownmixTable[channel]; info->channel != PCM_INVALID; ++info)
- {
- /* make sure we are not about to recurse into ourself */
- bool found = false;
- for(itt = path.begin(); itt != path.end(); ++itt)
- if (*itt == info->channel)
- {
- found = true;
- break;
- }
-
- if (found)
- continue;
-
- path.push_back(channel);
- float l = (info->level * (level / 100)) * 100;
- tablePtr = ResolveChannel(info->channel, l, info->ifExists, path, tablePtr);
- path.pop_back();
- }
-
- return tablePtr;
-}
-
-/*
- Builds a lookup table without extra adjustments, useful if we simply
- want to find out which channels are active.
- For final adjustments, BuildMap() is used.
-*/
-void CPCMRemap::ResolveChannels()
-{
- unsigned int in_ch, out_ch;
- bool hasSide = false;
- bool hasBack = false;
-
- memset(m_useable, 0, sizeof(m_useable));
-
- if (!m_outSet)
- {
- /* Output format is not known yet, assume the full configured map.
- * Note that m_ignoreLayout-using callers normally ignore the result of
- * this function when !m_outSet, when it is called only for an advice for
- * the caller of SetInputFormat about the best possible output map, and
- * they can still set their output format arbitrarily in their call to
- * SetOutputFormat. */
- for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
- m_useable[*chan] = true;
- }
- else if (m_ignoreLayout)
- {
- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
- m_useable[m_outMap[out_ch]] = true;
- }
- else
- {
- /* figure out what channels we have and can use */
- for(enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
- {
- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
- if (m_outMap[out_ch] == *chan)
- {
- m_useable[*chan] = true;
- break;
- }
- }
- }
-
- /* force mono audio to front left and front right */
- if (!m_ignoreLayout && m_inChannels == 1 && m_inMap[0] == PCM_FRONT_CENTER
- && m_useable[PCM_FRONT_LEFT] && m_useable[PCM_FRONT_RIGHT])
- {
- CLog::Log(LOGDEBUG, "CPCMRemap: Mapping mono audio to front left and front right");
- m_useable[PCM_FRONT_CENTER] = false;
- m_useable[PCM_FRONT_LEFT_OF_CENTER] = false;
- m_useable[PCM_FRONT_RIGHT_OF_CENTER] = false;
- }
-
- /* see if our input has side/back channels */
- for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
- switch(m_inMap[in_ch])
- {
- case PCM_SIDE_LEFT:
- case PCM_SIDE_RIGHT:
- hasSide = true;
- break;
-
- case PCM_BACK_LEFT:
- case PCM_BACK_RIGHT:
- hasBack = true;
- break;
-
- default:;
- }
-
- /* if our input has side, and not back channels, and our output doesnt have side channels */
- if (hasSide && !hasBack && (!m_useable[PCM_SIDE_LEFT] || !m_useable[PCM_SIDE_RIGHT]))
- {
- CLog::Log(LOGDEBUG, "CPCMRemap: Forcing side channel map to back channels");
- for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
- if (m_inMap[in_ch] == PCM_SIDE_LEFT ) m_inMap[in_ch] = PCM_BACK_LEFT;
- else if (m_inMap[in_ch] == PCM_SIDE_RIGHT) m_inMap[in_ch] = PCM_BACK_RIGHT;
- }
-
- /* resolve all the channels */
- struct PCMMapInfo table[PCM_MAX_CH + 1], *info, *dst;
- std::vector<enum PCMChannels> path;
-
- for (int i = 0; i < PCM_MAX_CH + 1; i++)
- {
- for (int j = 0; j < PCM_MAX_CH + 1; j++)
- m_lookupMap[i][j].channel = PCM_INVALID;
- }
-
- memset(m_counts, 0, sizeof(m_counts));
- for(in_ch = 0; in_ch < m_inChannels; ++in_ch) {
-
- for (int i = 0; i < PCM_MAX_CH + 1; i++)
- table[i].channel = PCM_INVALID;
-
- ResolveChannel(m_inMap[in_ch], 1.0f, false, path, table);
- for(info = table; info->channel != PCM_INVALID; ++info)
- {
- /* find the end of the table */
- for(dst = m_lookupMap[info->channel]; dst->channel != PCM_INVALID; ++dst);
-
- /* append it to the table and set its input offset */
- dst->channel = m_inMap[in_ch];
- dst->in_offset = in_ch * 2;
- dst->level = info->level;
- m_counts[dst->channel]++;
- }
- }
-}
-
-/*
- builds a lookup table to convert from the input mapping to the output
- mapping, this decreases the amount of work per sample to remap it.
-*/
-void CPCMRemap::BuildMap()
-{
- struct PCMMapInfo *dst;
- unsigned int out_ch;
-
- if (!m_inSet || !m_outSet) return;
-
- m_inStride = m_inSampleSize * m_inChannels ;
- m_outStride = m_inSampleSize * m_outChannels;
-
- /* see if we need to normalize the levels */
- bool dontnormalize = g_guiSettings.GetBool("audiooutput.dontnormalizelevels");
- CLog::Log(LOGDEBUG, "CPCMRemap: Downmix normalization is %s", (dontnormalize ? "disabled" : "enabled"));
-
- ResolveChannels();
-
- /* convert the levels into RMS values */
- float loudest = 0.0;
- bool hasLoudest = false;
-
- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
- {
- float scale = 0;
- int count = 0;
- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
- {
- dst->copy = false;
- dst->level = dst->level / sqrt((float)m_counts[dst->channel]);
- scale += dst->level;
- ++count;
- }
-
- /* if there is only 1 channel to mix, and the level is 1.0, then just copy the channel */
- dst = m_lookupMap[m_outMap[out_ch]];
- if (count == 1 && dst->level > 0.99 && dst->level < 1.01)
- dst->copy = true;
-
- /* normalize the levels if it is turned on */
- if (!dontnormalize)
- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
- {
- dst->level /= scale;
- /* find the loudest output level we have that is not 1-1 */
- if (dst->level < 1.0 && loudest < dst->level)
- {
- loudest = dst->level;
- hasLoudest = true;
- }
- }
- }
-
- /* adjust the channels that are too loud */
- for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
- {
- CStdString s = "", f;
- for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
- {
- if (hasLoudest && dst->copy)
- {
- dst->level = loudest;
- dst->copy = false;
- }
-
- f.Format("%s(%f%s) ", PCMChannelStr(dst->channel).c_str(), dst->level, dst->copy ? "*" : "");
- s += f;
- }
- CLog::Log(LOGDEBUG, "CPCMRemap: %s = %s\n", PCMChannelStr(m_outMap[out_ch]).c_str(), s.c_str());
- }
-}
-
-void CPCMRemap::DumpMap(CStdString info, unsigned int channels, enum PCMChannels *channelMap)
-{
- if (channelMap == NULL)
- {
- CLog::Log(LOGINFO, "CPCMRemap: %s channel map: NULL", info.c_str());
- return;
- }
-
- CStdString mapping;
- for(unsigned int i = 0; i < channels; ++i)
- mapping += ((i == 0) ? "" : ",") + PCMChannelStr(channelMap[i]);
-
- CLog::Log(LOGINFO, "CPCMRemap: %s channel map: %s\n", info.c_str(), mapping.c_str());
-}
-
-void CPCMRemap::Reset()
-{
- m_inSet = false;
- m_outSet = false;
- Dispose();
-}
-
-/* sets the input format, and returns the requested channel layout */
-enum PCMChannels *CPCMRemap::SetInputFormat(unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate)
-{
- m_inChannels = channels;
- m_inSampleSize = sampleSize;
- m_sampleRate = (float)sampleRate;
- m_inSet = channelMap != NULL;
- if (channelMap)
- memcpy(m_inMap, channelMap, sizeof(enum PCMChannels) * channels);
-
- /* fix me later */
- assert(sampleSize == 2);
-
- /* get the audio layout, and count the channels in it */
- m_channelLayout = (enum PCMLayout)g_guiSettings.GetInt("audiooutput.channellayout");
- if (m_channelLayout >= PCM_MAX_LAYOUT) m_channelLayout = PCM_LAYOUT_2_0;
-
- //spdif only has 2 pcm channels, so don't try to use more
- if (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_IEC958)
- {
- CLog::Log(LOGINFO, "CPCMRemap: Configured speaker layout: %s (iec958)\n", PCMLayoutStr(m_channelLayout).c_str());
- m_channelLayout = PCM_LAYOUT_2_0;
- }
- else
- CLog::Log(LOGINFO, "CPCMRemap: Configured speaker layout: %s\n", PCMLayoutStr(m_channelLayout).c_str());
-
-
- DumpMap("I", channels, channelMap);
- BuildMap();
-
- /* now remove the empty channels from PCMLayoutMap;
- * we don't perform upmixing so we want the minimum amount of those */
- if (channelMap) {
- if (!m_outSet)
- ResolveChannels(); /* Do basic channel resolving to find out the empty channels;
- * If m_outSet == true, this was done already by BuildMap() above */
- int i = 0;
- for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
- if (m_lookupMap[*chan][0].channel != PCM_INVALID) {
- /* something is mapped here, so add the channel */
- m_layoutMap[i++] = *chan;
- }
- m_layoutMap[i] = PCM_INVALID;
- } else
- memcpy(m_layoutMap, PCMLayoutMap[m_channelLayout], sizeof(PCMLayoutMap[m_channelLayout]));
-
- m_attenuation = 1.0;
- m_attenuationInc = 1.0;
- m_holdCounter = 0;
-
- return m_layoutMap;
-}
-
-/* sets the output format supported by the audio renderer */
-void CPCMRemap::SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout/* = false */)
-{
- m_outChannels = channels;
- m_outSet = channelMap != NULL;
- m_ignoreLayout = ignoreLayout;
- if (channelMap)
- memcpy(m_outMap, channelMap, sizeof(enum PCMChannels) * channels);
-
- DumpMap("O", channels, channelMap);
- BuildMap();
-
- m_attenuation = 1.0;
- m_attenuationInc = 1.0;
- m_holdCounter = 0;
-}
-
-void CPCMRemap::Remap(void *data, void *out, unsigned int samples, long drc)
-{
- float gain = 1.0f;
- if (drc > 0)
- gain = pow(10.0f, (float)drc / 2000.0f);
-
- Remap(data, out, samples, gain);
-}
-
-/* remap the supplied data into out, which must be pre-allocated */
-void CPCMRemap::Remap(void *data, void *out, unsigned int samples, float gain /*= 1.0f*/)
-{
- CheckBufferSize(samples * m_outChannels * sizeof(float));
-
- //set output buffer to 0
- memset(out, 0, samples * m_outChannels * m_inSampleSize);
-
- //set intermediate buffer to 0
- memset(m_buf, 0, m_bufsize);
-
- ProcessInput(data, out, samples, gain);
- AddGain(m_buf, samples * m_outChannels, gain);
- ProcessLimiter(samples, gain);
- ProcessOutput(out, samples, gain);
-}
-
-void CPCMRemap::CheckBufferSize(int size)
-{
- if (m_bufsize < size)
- {
- m_bufsize = size;
- m_buf = (float*)realloc(m_buf, m_bufsize);
- }
-}
-
-void CPCMRemap::ProcessInput(void* data, void* out, unsigned int samples, float gain)
-{
- for (unsigned int ch = 0; ch < m_outChannels; ch++)
- {
- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
- if (info->channel == PCM_INVALID)
- continue;
-
- if (info->copy && gain == 1.0f) //do direct copy
- {
- uint8_t* src = (uint8_t*)data + info->in_offset;
- uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
- uint8_t* dstend = dst + samples * m_outStride;
- while (dst != dstend)
- {
- *(int16_t*)dst = *(int16_t*)src;
- src += m_inStride;
- dst += m_outStride;
- }
- }
- else //needs some volume change or mixing, put into intermediate buffer
- {
- for(; info->channel != PCM_INVALID; info++)
- {
- uint8_t* src = (uint8_t*)data + info->in_offset;
- float* dst = m_buf + ch;
- float* dstend = dst + samples * m_outChannels;
- while (dst != dstend)
- {
- *dst += (float)(*(int16_t*)src) * info->level;
- src += m_inStride;
- dst += m_outChannels;
- }
- }
- }
- }
-}
-
-void CPCMRemap::AddGain(float* buf, unsigned int samples, float gain)
-{
- if (gain != 1.0f) //needs a gain change
- {
- float* ptr = m_buf;
- float* end = m_buf + samples;
- while (ptr != end)
- *(ptr++) *= gain;
- }
-}
-
-void CPCMRemap::ProcessLimiter(unsigned int samples, float gain)
-{
- //check total gain for each output channel
- float highestgain = 1.0f;
- for (unsigned int ch = 0; ch < m_outChannels; ch++)
- {
- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
- if (info->channel == PCM_INVALID)
- continue;
-
- float chgain = 0.0f;
- for(; info->channel != PCM_INVALID; info++)
- chgain += info->level * gain;
-
- if (chgain > highestgain)
- highestgain = chgain;
- }
-
- m_attenuationMin = 1.0f;
-
- //if one of the channels can clip, enable a limiter
- if (highestgain > 1.0001f)
- {
- m_attenuationMin = m_attenuation;
-
- if (!m_limiterEnabled)
- {
- CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, enabling limiter", highestgain);
- m_limiterEnabled = true;
- }
-
- for (unsigned int i = 0; i < samples; i++)
- {
- //for each collection of samples, get the highest absolute value
- float maxAbs = 0.0f;
- for (unsigned int outch = 0; outch < m_outChannels; outch++)
- {
- float absval = fabs(m_buf[i * m_outChannels + outch]) / 32768.0f;
- if (maxAbs < absval)
- maxAbs = absval;
- }
-
- //if attenuatedAbs is higher than 1.0f, audio is clipping
- float attenuatedAbs = maxAbs * m_attenuation;
- if (attenuatedAbs > 1.0f)
- {
- //set m_attenuation so that m_attenuation * sample is the maximum output value
- m_attenuation = 1.0f / maxAbs;
- if (m_attenuation < m_attenuationMin)
- m_attenuationMin = m_attenuation;
- //value to add to m_attenuation to make it 1.0f
- m_attenuationInc = 1.0f - m_attenuation;
- //amount of samples to hold m_attenuation
- m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
- }
- else if (m_attenuation < 1.0f && attenuatedAbs > 0.95f)
- {
- //if we're attenuating and we get within 5% of clipping, hold m_attenuation
- m_attenuationInc = 1.0f - m_attenuation;
- m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
- }
-
- //apply attenuation
- for (unsigned int outch = 0; outch < m_outChannels; outch++)
- m_buf[i * m_outChannels + outch] *= m_attenuation;
-
- if (m_holdCounter)
- {
- //hold m_attenuation
- m_holdCounter--;
- }
- else if (m_attenuationInc > 0.0f)
- {
- //move m_attenuation to 1.0 in g_advancedSettings.m_limiterRelease seconds
- m_attenuation += m_attenuationInc / m_sampleRate / g_advancedSettings.m_limiterRelease;
- if (m_attenuation > 1.0f)
- {
- m_attenuation = 1.0f;
- m_attenuationInc = 0.0f;
- }
- }
- }
- }
- else
- {
- if (m_limiterEnabled)
- {
- CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, disabling limiter", highestgain);
- m_limiterEnabled = false;
- }
-
- //reset the limiter
- m_attenuation = 1.0f;
- m_attenuationInc = 0.0f;
- m_holdCounter = 0;
- }
-}
-
-void CPCMRemap::ProcessOutput(void* out, unsigned int samples, float gain)
-{
- //copy from intermediate buffer to output
- for (unsigned int ch = 0; ch < m_outChannels; ch++)
- {
- struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
- if (info->channel == PCM_INVALID)
- continue;
-
- if (!info->copy || gain != 1.0f)
- {
- float* src = m_buf + ch;
- uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
- uint8_t* dstend = dst + samples * m_outStride;
-
- while(dst != dstend)
- {
- *(int16_t*)dst = MathUtils::round_int(std::min(std::max(*src, (float)INT16_MIN), (float)INT16_MAX));
- src += m_outChannels;
- dst += m_outStride;
- }
- }
- }
-}
-
-bool CPCMRemap::CanRemap()
-{
- return (m_inSet && m_outSet);
-}
-
-int CPCMRemap::InBytesToFrames(int bytes)
-{
- return bytes / m_inSampleSize / m_inChannels;
-}
-
-int CPCMRemap::FramesToOutBytes(int frames)
-{
- return frames * m_inSampleSize * m_outChannels;
-}
-
-int CPCMRemap::FramesToInBytes(int frames)
-{
- return frames * m_inSampleSize * m_inChannels;
-}
-
-CStdString CPCMRemap::PCMChannelStr(enum PCMChannels ename)
-{
- const char* PCMChannelName[] =
- {
- "FL",
- "FR",
- "CE",
- "LFE",
- "BL",
- "BR",
- "FLOC",
- "FROC",
- "BC",
- "SL",
- "SR",
- "TFL",
- "TFR",
- "TFC",
- "TC",
- "TBL",
- "TBR",
- "TBC"
- };
-
- int namepos = (int)ename;
- CStdString namestr;
-
- if (namepos < 0 || namepos >= (int)(sizeof(PCMChannelName) / sizeof(const char*)))
- namestr.Format("UNKNOWN CHANNEL:%i", namepos);
- else
- namestr = PCMChannelName[namepos];
-
- return namestr;
-}
-
-CStdString CPCMRemap::PCMLayoutStr(enum PCMLayout ename)
-{
- const char* PCMLayoutName[] =
- {
- "2.0",
- "2.1",
- "3.0",
- "3.1",
- "4.0",
- "4.1",
- "5.0",
- "5.1",
- "7.0",
- "7.1"
- };
-
- int namepos = (int)ename;
- CStdString namestr;
-
- if (namepos < 0 || namepos >= (int)(sizeof(PCMLayoutName) / sizeof(const char*)))
- namestr.Format("UNKNOWN LAYOUT:%i", namepos);
- else
- namestr = PCMLayoutName[namepos];
-
- return namestr;
-}
-
diff --git a/xbmc/utils/PCMRemap.h b/xbmc/utils/PCMRemap.h
deleted file mode 100644
index a70292e596..0000000000
--- a/xbmc/utils/PCMRemap.h
+++ /dev/null
@@ -1,147 +0,0 @@
-#ifndef __PCM_REMAP__H__
-#define __PCM_REMAP__H__
-
-/*
- * Copyright (C) 2005-2010 Team XBMC
- * http://www.xbmc.org
- *
- * 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; either version 2, or (at your option)
- * any later version.
- *
- * 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <stdint.h>
-#include <vector>
-#include "StdString.h"
-
-#define PCM_MAX_CH 18
-enum PCMChannels
-{
- PCM_INVALID = -1,
- PCM_FRONT_LEFT,
- PCM_FRONT_RIGHT,
- PCM_FRONT_CENTER,
- PCM_LOW_FREQUENCY,
- PCM_BACK_LEFT,
- PCM_BACK_RIGHT,
- PCM_FRONT_LEFT_OF_CENTER,
- PCM_FRONT_RIGHT_OF_CENTER,
- PCM_BACK_CENTER,
- PCM_SIDE_LEFT,
- PCM_SIDE_RIGHT,
- PCM_TOP_FRONT_LEFT,
- PCM_TOP_FRONT_RIGHT,
- PCM_TOP_FRONT_CENTER,
- PCM_TOP_CENTER,
- PCM_TOP_BACK_LEFT,
- PCM_TOP_BACK_RIGHT,
- PCM_TOP_BACK_CENTER
-};
-
-#define PCM_MAX_LAYOUT 10
-enum PCMLayout
-{
- PCM_LAYOUT_2_0 = 0,
- PCM_LAYOUT_2_1,
- PCM_LAYOUT_3_0,
- PCM_LAYOUT_3_1,
- PCM_LAYOUT_4_0,
- PCM_LAYOUT_4_1,
- PCM_LAYOUT_5_0,
- PCM_LAYOUT_5_1,
- PCM_LAYOUT_7_0,
- PCM_LAYOUT_7_1
-};
-
-struct PCMMapInfo
-{
- enum PCMChannels channel;
- float level;
- bool ifExists;
- int in_offset;
- bool copy;
-};
-
-//! Channels remapper class
-/*!
- The usual set-up process:
- - user calls SetInputFormat with the input channels information
- - SetInputFormat responds with a channelmap corresponding to the speaker
- layout that the user has configured, with empty (according to information
- calculated from the input channelmap) channels removed
- - user uses this information to create the desired output channelmap,
- and calls SetOutputFormat to set it (if the channelmap contains channels
- that do not exist in the configured speaker layout, they will contain
- only silence unless ignoreLayout is true)
- */
-
-class CPCMRemap
-{
-protected:
- bool m_inSet, m_outSet;
- enum PCMLayout m_channelLayout;
- unsigned int m_inChannels, m_outChannels;
- unsigned int m_inSampleSize;
- enum PCMChannels m_inMap [PCM_MAX_CH];
- enum PCMChannels m_outMap[PCM_MAX_CH];
- enum PCMChannels m_layoutMap[PCM_MAX_CH + 1];
-
- bool m_ignoreLayout;
- bool m_useable [PCM_MAX_CH];
- int m_inStride, m_outStride;
- struct PCMMapInfo m_lookupMap[PCM_MAX_CH + 1][PCM_MAX_CH + 1];
- int m_counts[PCM_MAX_CH];
-
- float* m_buf;
- int m_bufsize;
- float m_attenuation;
- float m_attenuationInc;
- float m_attenuationMin; //lowest attenuation value during a call of Remap(), used for the codec info
- float m_sampleRate;
- unsigned int m_holdCounter;
- bool m_limiterEnabled;
-
- struct PCMMapInfo* ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr);
- void ResolveChannels(); //!< Partial BuildMap(), just enough to see which output channels are active
- void BuildMap();
- void DumpMap(CStdString info, int unsigned channels, enum PCMChannels *channelMap);
- void Dispose();
- CStdString PCMChannelStr(enum PCMChannels ename);
- CStdString PCMLayoutStr(enum PCMLayout ename);
-
- void CheckBufferSize(int size);
- void ProcessInput(void* data, void* out, unsigned int samples, float gain);
- void AddGain(float* buf, unsigned int samples, float gain);
- void ProcessLimiter(unsigned int samples, float gain);
- void ProcessOutput(void* out, unsigned int samples, float gain);
-
-public:
-
- CPCMRemap();
- ~CPCMRemap();
-
- void Reset();
- enum PCMChannels *SetInputFormat (unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate);
- void SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout = false);
- void Remap(void *data, void *out, unsigned int samples, long drc);
- void Remap(void *data, void *out, unsigned int samples, float gain = 1.0f);
- bool CanRemap();
- int InBytesToFrames (int bytes );
- int FramesToOutBytes(int frames);
- int FramesToInBytes (int frames);
- float GetCurrentAttenuation() { return m_attenuationMin; }
-};
-
-#endif
diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp
index b7ec409128..59d97286ee 100644
--- a/xbmc/video/VideoDatabase.cpp
+++ b/xbmc/video/VideoDatabase.cpp
@@ -3941,11 +3941,6 @@ void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE ty
try
{
m_pDS->exec(exec.c_str());
-
- if (type == VIDEODB_CONTENT_TVSHOWS)
- AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
- else if (type == VIDEODB_CONTENT_MOVIES)
- AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
}
catch (...)
{
diff --git a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
index 66eee83ead..3d707a7d88 100644
--- a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp
@@ -75,8 +75,8 @@ void CGUIDialogAudioSubtitleSettings::CreateSettings()
// clear out any old settings
m_settings.clear();
// create our settings
- m_volume = g_settings.m_nVolumeLevel * 0.01f;
- AddSlider(AUDIO_SETTINGS_VOLUME, 13376, &m_volume, VOLUME_MINIMUM * 0.01f, (VOLUME_MAXIMUM - VOLUME_MINIMUM) * 0.0001f, VOLUME_MAXIMUM * 0.01f, FormatDecibel, false);
+ m_volume = g_settings.m_fVolumeLevel;
+ AddSlider(AUDIO_SETTINGS_VOLUME, 13376, &m_volume, VOLUME_MINIMUM, VOLUME_MAXIMUM / 100.0f, VOLUME_MAXIMUM, FormatDecibel, false);
AddSlider(AUDIO_SETTINGS_VOLUME_AMPLIFICATION, 660, &g_settings.m_currentVideoSettings.m_VolumeAmplification, VOLUME_DRC_MINIMUM * 0.01f, (VOLUME_DRC_MAXIMUM - VOLUME_DRC_MINIMUM) / 6000.0f, VOLUME_DRC_MAXIMUM * 0.01f, FormatDecibel, false);
if (g_application.m_pPlayer && g_application.m_pPlayer->IsPassthrough())
{
@@ -214,8 +214,8 @@ void CGUIDialogAudioSubtitleSettings::OnSettingChanged(SettingInfo &setting)
// check and update anything that needs it
if (setting.id == AUDIO_SETTINGS_VOLUME)
{
- g_settings.m_nVolumeLevel = (long)(m_volume * 100.0f);
- g_application.SetVolume(int(((float)(g_settings.m_nVolumeLevel - VOLUME_MINIMUM)) / (VOLUME_MAXIMUM - VOLUME_MINIMUM)*100.0f + 0.5f));
+ g_settings.m_fVolumeLevel = m_volume;
+ g_application.SetVolume(m_volume, false);//false - value is not in percent
}
else if (setting.id == AUDIO_SETTINGS_VOLUME_AMPLIFICATION)
{
@@ -352,7 +352,7 @@ void CGUIDialogAudioSubtitleSettings::OnSettingChanged(SettingInfo &setting)
void CGUIDialogAudioSubtitleSettings::FrameMove()
{
- m_volume = g_settings.m_nVolumeLevel * 0.01f;
+ m_volume = g_settings.m_fVolumeLevel;
UpdateSetting(AUDIO_SETTINGS_VOLUME);
if (g_application.m_pPlayer)
{
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
index ab4d709a21..aeacde3266 100644
--- a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp
@@ -46,6 +46,7 @@
#include "guilib/LocalizeStrings.h"
#include "GUIUserMessages.h"
#include "TextureCache.h"
+#include "interfaces/AnnouncementManager.h"
using namespace std;
using namespace XFILE;
@@ -669,6 +670,7 @@ void CGUIDialogVideoInfo::OnGetThumb()
VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
}
m_hasUpdatedThumb = true;
+ AnnounceUpdate("thumb");
// Update our screen
Update();
@@ -768,11 +770,19 @@ void CGUIDialogVideoInfo::OnGetFanart()
else
m_movieItem->ClearProperty("fanart_image");
m_hasUpdatedThumb = true;
+ AnnounceUpdate("fanart");
// Update our screen
Update();
}
+void CGUIDialogVideoInfo::AnnounceUpdate(const std::string &type)
+{
+ CVariant data;
+ data["art"] = type;
+ ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(*m_movieItem)), data);
+}
+
void CGUIDialogVideoInfo::PlayTrailer()
{
CFileItem item;
diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.h b/xbmc/video/dialogs/GUIDialogVideoInfo.h
index d5f2584fc6..1aa8339924 100644
--- a/xbmc/video/dialogs/GUIDialogVideoInfo.h
+++ b/xbmc/video/dialogs/GUIDialogVideoInfo.h
@@ -47,6 +47,7 @@ public:
protected:
void Update();
void SetLabel(int iControl, const CStdString& strLabel);
+ void AnnounceUpdate(const std::string &type);
// link cast to movies
void ClearCastList();
diff --git a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.cpp b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.cpp
index 04f3e9eb60..6ff14ba6f4 100644
--- a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.cpp
+++ b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.cpp
@@ -239,7 +239,7 @@ extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, con
z_angle = 0.0f;
}
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
int i,c;
int y=0;
@@ -261,8 +261,8 @@ extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *
{
if (c<iAudioDataLength)
{
- if(pAudioData[c] > y)
- y = (int)pAudioData[c];
+ if((int)(pAudioData[c] * (0x07fff+.5f) > y))
+ y = (int)(pAudioData[c] * (0x07fff+.5f));
}
else
continue;
diff --git a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.sln b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.sln
index f9feff4a66..7d25211f1c 100644
--- a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.sln
+++ b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.sln
@@ -1,7 +1,7 @@

-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual C++ Express 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx_spectrum", "directx_spectrum.vcproj", "{0D91724A-E6F6-4708-AF47-9F88BBE2114C}"
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "visDirectxSpectrum", "directx_spectrum.vcxproj", "{0D91724A-E6F6-4708-AF47-9F88BBE2114C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.vcxproj b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.vcxproj
index d5135f56f9..9c62c55284 100644
--- a/xbmc/visualizations/DirectXSpectrum/directx_spectrum.vcxproj
+++ b/xbmc/visualizations/DirectXSpectrum/directx_spectrum.vcxproj
@@ -55,6 +55,7 @@
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">.vis</TargetExt>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">$(DXSDK_DIR)Include;$(IncludePath)</IncludePath>
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">$(DXSDK_DIR)Lib\x86;$(LibraryPath)</LibraryPath>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">false</PostBuildEventUseInBuild>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">
<ClCompile>
diff --git a/xbmc/visualizations/Goom/Main.cpp b/xbmc/visualizations/Goom/Main.cpp
index 1eec610cba..81090e73db 100644
--- a/xbmc/visualizations/Goom/Main.cpp
+++ b/xbmc/visualizations/Goom/Main.cpp
@@ -136,10 +136,15 @@ extern "C" void ADDON_Stop()
//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
- int copysize = iAudioDataLength < (int)sizeof( g_audio_data ) ? iAudioDataLength : (int)sizeof( g_audio_data );
- memcpy( g_audio_data, pAudioData, copysize );
+ int copysize = iAudioDataLength < (int)sizeof( g_audio_data ) >> 1 ? iAudioDataLength : (int)sizeof( g_audio_data ) >> 1;
+ int ipos, i;
+ for(ipos = 0; i = 0; i < copysize; i += 2, ++ipos)
+ {
+ g_audio_data[0][ipos] = (int)(pAudioData[i ] * (INT16_MAX+.5f));
+ g_audio_data[1][ipos] = (int)(pAudioData[i+1] * (INT16_MAX+.5f));
+ }
}
diff --git a/xbmc/visualizations/Milkdrop/MilkdropXBMC.cpp b/xbmc/visualizations/Milkdrop/MilkdropXBMC.cpp
index 8af7b5eaf7..0199185c88 100644
--- a/xbmc/visualizations/Milkdrop/MilkdropXBMC.cpp
+++ b/xbmc/visualizations/Milkdrop/MilkdropXBMC.cpp
@@ -117,7 +117,7 @@ unsigned char waves[2][576];
//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
int ipos=0;
while (ipos < 576)
diff --git a/xbmc/visualizations/OpenGLSpectrum/opengl_spectrum.cpp b/xbmc/visualizations/OpenGLSpectrum/opengl_spectrum.cpp
index eaa51d046b..fcfebf25b5 100644
--- a/xbmc/visualizations/OpenGLSpectrum/opengl_spectrum.cpp
+++ b/xbmc/visualizations/OpenGLSpectrum/opengl_spectrum.cpp
@@ -29,9 +29,12 @@
* Ported to GLES 2.0 by Gimli
*/
+#define __STDC_LIMIT_MACROS
+
#include "addons/include/xbmc_vis_dll.h"
#include <string.h>
#include <math.h>
+#include <stdint.h>
#if defined(HAS_GLES)
#include "VisGUIShader.h"
@@ -342,7 +345,7 @@ extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, con
z_angle = 0.0;
}
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
int i,c;
int y=0;
@@ -365,7 +368,7 @@ extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *
if (c<iAudioDataLength)
{
if(pAudioData[c] > y)
- y = (int)pAudioData[c];
+ y = (int)(pAudioData[c] * (INT16_MAX+.5f));
}
else
continue;
diff --git a/xbmc/visualizations/WaveForm/Main.cpp b/xbmc/visualizations/WaveForm/Main.cpp
index 34ea2a0e30..5b7fb74357 100644
--- a/xbmc/visualizations/WaveForm/Main.cpp
+++ b/xbmc/visualizations/WaveForm/Main.cpp
@@ -100,19 +100,18 @@ extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, con
//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
- // Convert the audio data into a floating -1 to +1 range
- int ipos=0;
- while (ipos < 512)
- {
- for (int i=0; i < iAudioDataLength; i+=2)
- {
- g_fWaveform[0][ipos] = pAudioData[i] / 32768.0f; // left channel
- g_fWaveform[1][ipos] = pAudioData[i+1] / 32768.0f; // right channel
- ipos++;
- if (ipos >= 512) break;
- }
+ int ipos=0;
+ while (ipos < 512)
+ {
+ for (int i=0; i < iAudioDataLength; i+=2)
+ {
+ g_fWaveform[0][ipos] = pAudioData[i ]; // left channel
+ g_fWaveform[1][ipos] = pAudioData[i+1]; // right channel
+ ipos++;
+ if (ipos >= 512) break;
+ }
}
}
diff --git a/xbmc/visualizations/XBMCProjectM/Main.cpp b/xbmc/visualizations/XBMCProjectM/Main.cpp
index 8b2299b07a..63dc8ac806 100644
--- a/xbmc/visualizations/XBMCProjectM/Main.cpp
+++ b/xbmc/visualizations/XBMCProjectM/Main.cpp
@@ -117,10 +117,10 @@ extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, con
//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
-extern "C" void AudioData(const short* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
+extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
if (globalPM)
- globalPM->pcm()->addPCM16Data(pAudioData, iAudioDataLength);
+ globalPM->pcm()->addPCMfloat(pAudioData, iAudioDataLength);
}
//-- Render -------------------------------------------------------------------
diff --git a/xbmc/windows/GUIWindowHome.cpp b/xbmc/windows/GUIWindowHome.cpp
index a139371c1a..06554f62f4 100644
--- a/xbmc/windows/GUIWindowHome.cpp
+++ b/xbmc/windows/GUIWindowHome.cpp
@@ -69,6 +69,8 @@ void CGUIWindowHome::Announce(AnnouncementFlag flag, const char *sender, const c
{
if (data.isMember("playcount"))
ra_flag |= Totals;
+ if (data.isMember("art"))
+ ra_flag |= Video;
}
else if (strcmp(message, "OnScanFinished") == 0)
{