diff options
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, + ¶mSize, ¤tFormat); + + // 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(¤tPaddingFrames); + 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(¤tPaddingFrames); + 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(¤tFormat)) - { - 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) { |