7C89619213B6A16F003631FE /* GUIWindowScreensaverDim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C89619013B6A16F003631FE /* GUIWindowScreensaverDim.cpp */; };
7C8A14571154CB2600E5FCFA /* TextureCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8A14541154CB2600E5FCFA /* TextureCache.cpp */; };
7C8A187D115B2A8200E5FCFA /* TextureDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8A187A115B2A8200E5FCFA /* TextureDatabase.cpp */; };
+ 7C8AE84E189DE3CD00C33786 /* CoreAudioChannelLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE849189DE3CD00C33786 /* CoreAudioChannelLayout.cpp */; };
+ 7C8AE84F189DE3CD00C33786 /* CoreAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE84A189DE3CD00C33786 /* CoreAudioDevice.cpp */; };
+ 7C8AE850189DE3CD00C33786 /* CoreAudioHardware.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE84B189DE3CD00C33786 /* CoreAudioHardware.cpp */; };
+ 7C8AE851189DE3CD00C33786 /* CoreAudioStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE84C189DE3CD00C33786 /* CoreAudioStream.cpp */; };
+ 7C8AE854189DE47F00C33786 /* CoreAudioHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE852189DE47400C33786 /* CoreAudioHelpers.cpp */; };
7C8FC6EE1829A4580045153D /* DirectoryProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8FC6EC1829A4580045153D /* DirectoryProvider.cpp */; };
7C8FC6EF1829A4580045153D /* DirectoryProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8FC6EC1829A4580045153D /* DirectoryProvider.cpp */; };
7C8FC6F01829A4580045153D /* DirectoryProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8FC6EC1829A4580045153D /* DirectoryProvider.cpp */; };
DF3488E713FD958F0026A711 /* GUIAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF3488E513FD958F0026A711 /* GUIAction.cpp */; };
DF34892A13FD9C780026A711 /* AirPlayServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF34892813FD9C780026A711 /* AirPlayServer.cpp */; };
DF34898213FDAAF60026A711 /* HttpParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF34898113FDAAF60026A711 /* HttpParser.cpp */; };
+ DF374B2418AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE852189DE47400C33786 /* CoreAudioHelpers.cpp */; };
+ DF374B2518AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C8AE852189DE47400C33786 /* CoreAudioHelpers.cpp */; };
DF3C3C0E1752A7EE000989C3 /* IOSEAGLView.mm in Sources */ = {isa = PBXBuildFile; fileRef = E49910D7174E4A6400741B6D /* IOSEAGLView.mm */; };
DF3C3C0F1752A7EE000989C3 /* IOSExternalTouchController.mm in Sources */ = {isa = PBXBuildFile; fileRef = E49910D9174E4A6400741B6D /* IOSExternalTouchController.mm */; };
DF3C3C101752A7EE000989C3 /* IOSScreenManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = E49910DB174E4A6400741B6D /* IOSScreenManager.mm */; };
DFB25D48163D4743006C4A48 /* WindowXML.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF1AD1EC15FCE77900E10810 /* WindowXML.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 */; };
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 */; };
DFF0F13617528350002DA3A4 /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; };
DFF0F13717528350002DA3A4 /* ilog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC720A8D155091BB00FFD782 /* ilog.cpp */; };
DFF0F13817528350002DA3A4 /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */; };
- DFF0F13917528350002DA3A4 /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */; };
- DFF0F13A17528350002DA3A4 /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */; };
- DFF0F13B17528350002DA3A4 /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
- DFF0F13C17528350002DA3A4 /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */; };
- DFF0F13D17528350002DA3A4 /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */; };
DFF0F13E17528350002DA3A4 /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */; };
DFF0F13F17528350002DA3A4 /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */; };
DFF0F14017528350002DA3A4 /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */; };
DFF0F15917528350002DA3A4 /* DVDAudioCodecLibMad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E150F0D25F9F900618676 /* DVDAudioCodecLibMad.cpp */; };
DFF0F15A17528350002DA3A4 /* DVDAudioCodecLPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E15110D25F9F900618676 /* DVDAudioCodecLPcm.cpp */; };
DFF0F15B17528350002DA3A4 /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
- DFF0F15C17528350002DA3A4 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */; };
DFF0F15D17528350002DA3A4 /* DVDAudioCodecPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E15150D25F9F900618676 /* DVDAudioCodecPcm.cpp */; };
DFF0F15E17528350002DA3A4 /* DVDOverlayCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFDA3152160E34230047A626 /* DVDOverlayCodec.cpp */; };
DFF0F15F17528350002DA3A4 /* DVDOverlayCodecCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E152B0D25F9F900618676 /* DVDOverlayCodecCC.cpp */; };
E4991196174E5CEB00741B6D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; };
E4991197174E5CEB00741B6D /* ilog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC720A8D155091BB00FFD782 /* ilog.cpp */; };
E4991198174E5CF600741B6D /* AEEncoderFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6A15373AE7006B8FF1 /* AEEncoderFFmpeg.cpp */; };
- E4991199174E5CFA00741B6D /* CoreAudioAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6D15373AE7006B8FF1 /* CoreAudioAE.cpp */; };
- E499119A174E5CFA00741B6D /* CoreAudioAEHAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */; };
- E499119B174E5CFA00741B6D /* CoreAudioAEHALIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */; };
- E499119D174E5CFA00741B6D /* CoreAudioAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */; };
- E499119E174E5CFA00741B6D /* CoreAudioAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */; };
E49911A6174E5CFE00741B6D /* AEBitstreamPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA315373AE7006B8FF1 /* AEBitstreamPacker.cpp */; };
E49911A7174E5CFE00741B6D /* AEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA515373AE7006B8FF1 /* AEBuffer.cpp */; };
E49911A8174E5CFE00741B6D /* AEChannelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB65FA715373AE7006B8FF1 /* AEChannelInfo.cpp */; };
E49911C1174E5D2500741B6D /* DVDAudioCodecLibMad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E150F0D25F9F900618676 /* DVDAudioCodecLibMad.cpp */; };
E49911C2174E5D2500741B6D /* DVDAudioCodecLPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E15110D25F9F900618676 /* DVDAudioCodecLPcm.cpp */; };
E49911C3174E5D2500741B6D /* DVDAudioCodecPassthrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */; };
- E49911C4174E5D2500741B6D /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */; };
E49911C5174E5D2500741B6D /* DVDAudioCodecPcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E15150D25F9F900618676 /* DVDAudioCodecPcm.cpp */; };
E49911C6174E5D2500741B6D /* DVDOverlayCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFDA3152160E34230047A626 /* DVDOverlayCodec.cpp */; };
E49911C7174E5D2500741B6D /* DVDOverlayCodecCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38E152B0D25F9F900618676 /* DVDOverlayCodecCC.cpp */; };
F5BDB80C120202F400F0B710 /* DVDSubtitleTagSami.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5BDB80B120202F400F0B710 /* DVDSubtitleTagSami.cpp */; };
F5BDB81A1202032400F0B710 /* DVDSubtitleTagMicroDVD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5BDB8191202032400F0B710 /* DVDSubtitleTagMicroDVD.cpp */; };
F5BDB820120203C200F0B710 /* AutoPtrHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5BDB81F120203C200F0B710 /* AutoPtrHandle.cpp */; };
+ F5CC228B1814F7E9006B5E91 /* AESinkDARWINOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22891814F7E9006B5E91 /* AESinkDARWINOSX.cpp */; };
+ F5CC228E1814F7F7006B5E91 /* AESinkDARWINIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC228C1814F7F7006B5E91 /* AESinkDARWINIOS.cpp */; };
+ F5CC228F1814F7F7006B5E91 /* AESinkDARWINIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC228C1814F7F7006B5E91 /* AESinkDARWINIOS.cpp */; };
+ F5CC22DF1814FF3B006B5E91 /* ActiveAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D31814FF3B006B5E91 /* ActiveAE.cpp */; };
+ F5CC22E01814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D51814FF3B006B5E91 /* ActiveAEBuffer.cpp */; };
+ F5CC22E11814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D71814FF3B006B5E91 /* ActiveAEResample.cpp */; };
+ F5CC22E21814FF3B006B5E91 /* ActiveAESink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D91814FF3B006B5E91 /* ActiveAESink.cpp */; };
+ F5CC22E31814FF3B006B5E91 /* ActiveAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DB1814FF3B006B5E91 /* ActiveAESound.cpp */; };
+ F5CC22E41814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DD1814FF3B006B5E91 /* ActiveAEStream.cpp */; };
+ F5CC22E51814FF3B006B5E91 /* ActiveAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D31814FF3B006B5E91 /* ActiveAE.cpp */; };
+ F5CC22E61814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D51814FF3B006B5E91 /* ActiveAEBuffer.cpp */; };
+ F5CC22E71814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D71814FF3B006B5E91 /* ActiveAEResample.cpp */; };
+ F5CC22E81814FF3B006B5E91 /* ActiveAESink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D91814FF3B006B5E91 /* ActiveAESink.cpp */; };
+ F5CC22E91814FF3B006B5E91 /* ActiveAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DB1814FF3B006B5E91 /* ActiveAESound.cpp */; };
+ F5CC22EA1814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DD1814FF3B006B5E91 /* ActiveAEStream.cpp */; };
+ F5CC22EB1814FF3B006B5E91 /* ActiveAE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D31814FF3B006B5E91 /* ActiveAE.cpp */; };
+ F5CC22EC1814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D51814FF3B006B5E91 /* ActiveAEBuffer.cpp */; };
+ F5CC22ED1814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D71814FF3B006B5E91 /* ActiveAEResample.cpp */; };
+ F5CC22EE1814FF3B006B5E91 /* ActiveAESink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22D91814FF3B006B5E91 /* ActiveAESink.cpp */; };
+ F5CC22EF1814FF3B006B5E91 /* ActiveAESound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DB1814FF3B006B5E91 /* ActiveAESound.cpp */; };
+ F5CC22F01814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22DD1814FF3B006B5E91 /* ActiveAEStream.cpp */; };
+ F5CC22FD18150065006B5E91 /* ActorProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22FB18150065006B5E91 /* ActorProtocol.cpp */; };
+ F5CC22FE18150065006B5E91 /* ActorProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22FB18150065006B5E91 /* ActorProtocol.cpp */; };
+ F5CC22FF18150065006B5E91 /* ActorProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC22FB18150065006B5E91 /* ActorProtocol.cpp */; };
+ F5CC2303181500B1006B5E91 /* EndianSwap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC2302181500B1006B5E91 /* EndianSwap.cpp */; };
+ F5CC2304181500B1006B5E91 /* EndianSwap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC2302181500B1006B5E91 /* EndianSwap.cpp */; };
+ F5CC2305181500B1006B5E91 /* EndianSwap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC2302181500B1006B5E91 /* EndianSwap.cpp */; };
+ F5CC230C18150118006B5E91 /* AESinkFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC230A18150118006B5E91 /* AESinkFactory.cpp */; };
+ F5CC230D18150118006B5E91 /* AESinkFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC230A18150118006B5E91 /* AESinkFactory.cpp */; };
+ F5CC230E18150118006B5E91 /* AESinkFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC230A18150118006B5E91 /* AESinkFactory.cpp */; };
+ F5CC234718150277006B5E91 /* AESinkNULL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC234518150277006B5E91 /* AESinkNULL.cpp */; };
+ F5CC234818150277006B5E91 /* AESinkNULL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC234518150277006B5E91 /* AESinkNULL.cpp */; };
+ F5CC234918150277006B5E91 /* AESinkNULL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC234518150277006B5E91 /* AESinkNULL.cpp */; };
+ F5CC238818150768006B5E91 /* AESinkProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC238618150768006B5E91 /* AESinkProfiler.cpp */; };
+ F5CC238918150768006B5E91 /* AESinkProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC238618150768006B5E91 /* AESinkProfiler.cpp */; };
+ F5CC238A18150768006B5E91 /* AESinkProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CC238618150768006B5E91 /* AESinkProfiler.cpp */; };
F5CEE60913D3C89700225F72 /* DVDOverlayCodecTX3G.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5CEE60713D3C89700225F72 /* DVDOverlayCodecTX3G.cpp */; };
F5D8D732102BB3B1004A11AB /* OverlayRendererGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5D8D72F102BB3B1004A11AB /* OverlayRendererGL.cpp */; };
F5D8D733102BB3B1004A11AB /* OverlayRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5D8D731102BB3B1004A11AB /* OverlayRenderer.cpp */; };
F5ED8D6C1551F91400842059 /* BlurayDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED8D6A1551F91400842059 /* BlurayDirectory.cpp */; };
F5ED908815538DCE00842059 /* XBMCTinyXML.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED908615538DCE00842059 /* XBMCTinyXML.cpp */; };
F5ED908E15538E2300842059 /* POUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED908C15538E2300842059 /* POUtils.cpp */; };
- F5ED942E155D729500842059 /* CoreAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED942A155D729500842059 /* CoreAudioDevice.cpp */; };
- F5ED942F155D729500842059 /* CoreAudioHardware.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED942C155D729500842059 /* CoreAudioHardware.cpp */; };
- F5ED943E155D743700842059 /* CoreAudioStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED943C155D743700842059 /* CoreAudioStream.cpp */; };
- F5ED9462155D777B00842059 /* CoreAudioChannelLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED9460155D777B00842059 /* CoreAudioChannelLayout.cpp */; };
- F5ED9496155D7B9900842059 /* CoreAudioMixMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED9494155D7B9900842059 /* CoreAudioMixMap.cpp */; };
- F5ED94AB155D7F8000842059 /* CoreAudioUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED94A9155D7F8000842059 /* CoreAudioUnit.cpp */; };
- F5ED9509155D855200842059 /* CoreAudioGraph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5ED9507155D855200842059 /* CoreAudioGraph.cpp */; };
F5ED9A0C155EBDC000842059 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E38E238E0D2626E600618676 /* CoreAudio.framework */; };
F5ED9A15155EBE0000842059 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88ECB6580DE013C4003396A7 /* DiskArbitration.framework */; };
F5ED9BFB155EC77400842059 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E38E25340D26365C00618676 /* ApplicationServices.framework */; };
F5EDC48C1651A6F900B852D8 /* GroupUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5EDC48A1651A6F900B852D8 /* GroupUtils.cpp */; };
F5F240EF110A4F76009126C6 /* CrystalHD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F240EB110A4F76009126C6 /* CrystalHD.cpp */; };
F5F244651110DC6B009126C6 /* FileOperationJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F244641110DC6B009126C6 /* FileOperationJob.cpp */; };
- F5F245DA1112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */; };
F5F245EE1112C9AB009126C6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F245EC1112C9AB009126C6 /* FileUtils.cpp */; };
F5F2EF4B0E593E0D0092C37F /* DVDFileInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F2EF4A0E593E0D0092C37F /* DVDFileInfo.cpp */; };
F5F8E1DA0E427E8000A8E96F /* VGMCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5F8E1D90E427E8000A8E96F /* VGMCodec.cpp */; };
7C8A14551154CB2600E5FCFA /* TextureCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureCache.h; sourceTree = "<group>"; };
7C8A187A115B2A8200E5FCFA /* TextureDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureDatabase.cpp; sourceTree = "<group>"; };
7C8A187B115B2A8200E5FCFA /* TextureDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureDatabase.h; sourceTree = "<group>"; };
+ 7C8AE844189DE3CD00C33786 /* CoreAudioChannelLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioChannelLayout.h; path = Sinks/osx/CoreAudioChannelLayout.h; sourceTree = "<group>"; };
+ 7C8AE845189DE3CD00C33786 /* CoreAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioDevice.h; path = Sinks/osx/CoreAudioDevice.h; sourceTree = "<group>"; };
+ 7C8AE846189DE3CD00C33786 /* CoreAudioHardware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioHardware.h; path = Sinks/osx/CoreAudioHardware.h; sourceTree = "<group>"; };
+ 7C8AE847189DE3CD00C33786 /* CoreAudioStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioStream.h; path = Sinks/osx/CoreAudioStream.h; sourceTree = "<group>"; };
+ 7C8AE849189DE3CD00C33786 /* CoreAudioChannelLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioChannelLayout.cpp; path = Sinks/osx/CoreAudioChannelLayout.cpp; sourceTree = "<group>"; };
+ 7C8AE84A189DE3CD00C33786 /* CoreAudioDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioDevice.cpp; path = Sinks/osx/CoreAudioDevice.cpp; sourceTree = "<group>"; };
+ 7C8AE84B189DE3CD00C33786 /* CoreAudioHardware.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioHardware.cpp; path = Sinks/osx/CoreAudioHardware.cpp; sourceTree = "<group>"; };
+ 7C8AE84C189DE3CD00C33786 /* CoreAudioStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioStream.cpp; path = Sinks/osx/CoreAudioStream.cpp; sourceTree = "<group>"; };
+ 7C8AE852189DE47400C33786 /* CoreAudioHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioHelpers.cpp; path = Sinks/osx/CoreAudioHelpers.cpp; sourceTree = "<group>"; };
+ 7C8AE853189DE47700C33786 /* CoreAudioHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioHelpers.h; path = Sinks/osx/CoreAudioHelpers.h; sourceTree = "<group>"; };
7C8FC6EC1829A4580045153D /* DirectoryProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DirectoryProvider.cpp; path = xbmc/listproviders/DirectoryProvider.cpp; sourceTree = SOURCE_ROOT; };
7C8FC6ED1829A4580045153D /* DirectoryProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryProvider.h; path = xbmc/listproviders/DirectoryProvider.h; sourceTree = SOURCE_ROOT; };
7C920CF7181669FF00DA1477 /* TextureOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureOperations.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; name = CoreAudioAE.cpp; path = CoreAudio/CoreAudioAE.cpp; sourceTree = "<group>"; };
- DFB65F6E15373AE7006B8FF1 /* CoreAudioAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAE.h; path = CoreAudio/CoreAudioAE.h; sourceTree = "<group>"; };
- DFB65F6F15373AE7006B8FF1 /* CoreAudioAEHAL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioAEHAL.cpp; path = CoreAudio/CoreAudioAEHAL.cpp; sourceTree = "<group>"; };
- DFB65F7015373AE7006B8FF1 /* CoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAEHAL.h; path = CoreAudio/CoreAudioAEHAL.h; sourceTree = "<group>"; };
- DFB65F7115373AE7006B8FF1 /* CoreAudioAEHALIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioAEHALIOS.cpp; path = CoreAudio/CoreAudioAEHALIOS.cpp; sourceTree = "<group>"; };
- DFB65F7215373AE7006B8FF1 /* CoreAudioAEHALIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAEHALIOS.h; path = CoreAudio/CoreAudioAEHALIOS.h; sourceTree = "<group>"; };
- DFB65F7315373AE7006B8FF1 /* CoreAudioAEHALOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioAEHALOSX.cpp; path = CoreAudio/CoreAudioAEHALOSX.cpp; sourceTree = "<group>"; };
- DFB65F7415373AE7006B8FF1 /* CoreAudioAEHALOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAEHALOSX.h; path = CoreAudio/CoreAudioAEHALOSX.h; sourceTree = "<group>"; };
- DFB65F7515373AE7006B8FF1 /* CoreAudioAESound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioAESound.cpp; path = CoreAudio/CoreAudioAESound.cpp; sourceTree = "<group>"; };
- DFB65F7615373AE7006B8FF1 /* CoreAudioAESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAESound.h; path = CoreAudio/CoreAudioAESound.h; sourceTree = "<group>"; };
- DFB65F7715373AE7006B8FF1 /* CoreAudioAEStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioAEStream.cpp; path = CoreAudio/CoreAudioAEStream.cpp; sourceTree = "<group>"; };
- DFB65F7815373AE7006B8FF1 /* CoreAudioAEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioAEStream.h; path = CoreAudio/CoreAudioAEStream.h; sourceTree = "<group>"; };
- DFB65F7A15373AE7006B8FF1 /* ICoreAudioAEHAL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ICoreAudioAEHAL.h; path = CoreAudio/ICoreAudioAEHAL.h; sourceTree = "<group>"; };
- DFB65F7B15373AE7006B8FF1 /* ICoreAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ICoreAudioSource.h; path = CoreAudio/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>"; };
DFB65F8C15373AE7006B8FF1 /* AESink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESink.h; sourceTree = "<group>"; };
F5BDB8191202032400F0B710 /* DVDSubtitleTagMicroDVD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDSubtitleTagMicroDVD.cpp; sourceTree = "<group>"; };
F5BDB81E120203C200F0B710 /* AutoPtrHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoPtrHandle.h; sourceTree = "<group>"; };
F5BDB81F120203C200F0B710 /* AutoPtrHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutoPtrHandle.cpp; sourceTree = "<group>"; };
+ F5CC22891814F7E9006B5E91 /* AESinkDARWINOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AESinkDARWINOSX.cpp; path = Sinks/AESinkDARWINOSX.cpp; sourceTree = "<group>"; };
+ F5CC228A1814F7E9006B5E91 /* AESinkDARWINOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESinkDARWINOSX.h; path = Sinks/AESinkDARWINOSX.h; sourceTree = "<group>"; };
+ F5CC228C1814F7F7006B5E91 /* AESinkDARWINIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AESinkDARWINIOS.cpp; path = Sinks/AESinkDARWINIOS.cpp; sourceTree = "<group>"; };
+ F5CC228D1814F7F7006B5E91 /* AESinkDARWINIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESinkDARWINIOS.h; path = Sinks/AESinkDARWINIOS.h; sourceTree = "<group>"; };
+ F5CC22D31814FF3B006B5E91 /* ActiveAE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAE.cpp; sourceTree = "<group>"; };
+ F5CC22D41814FF3B006B5E91 /* ActiveAE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAE.h; sourceTree = "<group>"; };
+ F5CC22D51814FF3B006B5E91 /* ActiveAEBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAEBuffer.cpp; sourceTree = "<group>"; };
+ F5CC22D61814FF3B006B5E91 /* ActiveAEBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAEBuffer.h; sourceTree = "<group>"; };
+ F5CC22D71814FF3B006B5E91 /* ActiveAEResample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAEResample.cpp; sourceTree = "<group>"; };
+ F5CC22D81814FF3B006B5E91 /* ActiveAEResample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAEResample.h; sourceTree = "<group>"; };
+ F5CC22D91814FF3B006B5E91 /* ActiveAESink.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAESink.cpp; sourceTree = "<group>"; };
+ F5CC22DA1814FF3B006B5E91 /* ActiveAESink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAESink.h; sourceTree = "<group>"; };
+ F5CC22DB1814FF3B006B5E91 /* ActiveAESound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAESound.cpp; sourceTree = "<group>"; };
+ F5CC22DC1814FF3B006B5E91 /* ActiveAESound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAESound.h; sourceTree = "<group>"; };
+ F5CC22DD1814FF3B006B5E91 /* ActiveAEStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActiveAEStream.cpp; sourceTree = "<group>"; };
+ F5CC22DE1814FF3B006B5E91 /* ActiveAEStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActiveAEStream.h; sourceTree = "<group>"; };
+ F5CC22FB18150065006B5E91 /* ActorProtocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActorProtocol.cpp; sourceTree = "<group>"; };
+ F5CC22FC18150065006B5E91 /* ActorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActorProtocol.h; sourceTree = "<group>"; };
+ F5CC2302181500B1006B5E91 /* EndianSwap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EndianSwap.cpp; sourceTree = "<group>"; };
+ F5CC230A18150118006B5E91 /* AESinkFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AESinkFactory.cpp; sourceTree = "<group>"; };
+ F5CC230B18150118006B5E91 /* AESinkFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESinkFactory.h; sourceTree = "<group>"; };
+ F5CC234518150277006B5E91 /* AESinkNULL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AESinkNULL.cpp; path = Sinks/AESinkNULL.cpp; sourceTree = "<group>"; };
+ F5CC234618150277006B5E91 /* AESinkNULL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESinkNULL.h; path = Sinks/AESinkNULL.h; sourceTree = "<group>"; };
+ F5CC238618150768006B5E91 /* AESinkProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AESinkProfiler.cpp; path = Sinks/AESinkProfiler.cpp; sourceTree = "<group>"; };
+ F5CC238718150768006B5E91 /* AESinkProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AESinkProfiler.h; path = Sinks/AESinkProfiler.h; sourceTree = "<group>"; };
F5CEE60713D3C89700225F72 /* DVDOverlayCodecTX3G.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDOverlayCodecTX3G.cpp; sourceTree = "<group>"; };
F5CEE60813D3C89700225F72 /* DVDOverlayCodecTX3G.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDOverlayCodecTX3G.h; sourceTree = "<group>"; };
F5D8D72E102BB3B1004A11AB /* OverlayRendererGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OverlayRendererGL.h; sourceTree = "<group>"; };
F5ED908715538DCE00842059 /* XBMCTinyXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCTinyXML.h; sourceTree = "<group>"; };
F5ED908C15538E2300842059 /* POUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = POUtils.cpp; sourceTree = "<group>"; };
F5ED908D15538E2300842059 /* POUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POUtils.h; sourceTree = "<group>"; };
- F5ED942A155D729500842059 /* CoreAudioDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioDevice.cpp; path = CoreAudio/CoreAudioDevice.cpp; sourceTree = "<group>"; };
- F5ED942B155D729500842059 /* CoreAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioDevice.h; path = CoreAudio/CoreAudioDevice.h; sourceTree = "<group>"; };
- F5ED942C155D729500842059 /* CoreAudioHardware.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioHardware.cpp; path = CoreAudio/CoreAudioHardware.cpp; sourceTree = "<group>"; };
- F5ED942D155D729500842059 /* CoreAudioHardware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioHardware.h; path = CoreAudio/CoreAudioHardware.h; sourceTree = "<group>"; };
- F5ED943C155D743700842059 /* CoreAudioStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioStream.cpp; path = CoreAudio/CoreAudioStream.cpp; sourceTree = "<group>"; };
- F5ED943D155D743700842059 /* CoreAudioStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioStream.h; path = CoreAudio/CoreAudioStream.h; sourceTree = "<group>"; };
- F5ED9460155D777B00842059 /* CoreAudioChannelLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioChannelLayout.cpp; path = CoreAudio/CoreAudioChannelLayout.cpp; sourceTree = "<group>"; };
- F5ED9461155D777B00842059 /* CoreAudioChannelLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioChannelLayout.h; path = CoreAudio/CoreAudioChannelLayout.h; sourceTree = "<group>"; };
- F5ED9494155D7B9900842059 /* CoreAudioMixMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioMixMap.cpp; path = CoreAudio/CoreAudioMixMap.cpp; sourceTree = "<group>"; };
- F5ED9495155D7B9900842059 /* CoreAudioMixMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioMixMap.h; path = CoreAudio/CoreAudioMixMap.h; sourceTree = "<group>"; };
- F5ED94A9155D7F8000842059 /* CoreAudioUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioUnit.cpp; path = CoreAudio/CoreAudioUnit.cpp; sourceTree = "<group>"; };
- F5ED94AA155D7F8000842059 /* CoreAudioUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioUnit.h; path = CoreAudio/CoreAudioUnit.h; sourceTree = "<group>"; };
- F5ED9507155D855200842059 /* CoreAudioGraph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioGraph.cpp; path = CoreAudio/CoreAudioGraph.cpp; sourceTree = "<group>"; };
- F5ED9508155D855200842059 /* CoreAudioGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioGraph.h; path = CoreAudio/CoreAudioGraph.h; sourceTree = "<group>"; };
F5EDC48A1651A6F900B852D8 /* GroupUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GroupUtils.cpp; sourceTree = "<group>"; };
F5EDC48B1651A6F900B852D8 /* GroupUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupUtils.h; sourceTree = "<group>"; };
F5F240EB110A4F76009126C6 /* CrystalHD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CrystalHD.cpp; sourceTree = "<group>"; };
F5F240EC110A4F76009126C6 /* CrystalHD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrystalHD.h; sourceTree = "<group>"; };
F5F244631110DC6B009126C6 /* FileOperationJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileOperationJob.h; sourceTree = "<group>"; };
F5F244641110DC6B009126C6 /* FileOperationJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileOperationJob.cpp; sourceTree = "<group>"; };
- F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDAudioCodecPassthroughFFmpeg.cpp; sourceTree = "<group>"; };
- 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>"; };
F5F2EF490E593E0D0092C37F /* DVDFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DVDFileInfo.h; sourceTree = "<group>"; };
path = info;
sourceTree = "<group>";
};
+ 7C8AE7FE189DE3A700C33786 /* osx */ = {
+ isa = PBXGroup;
+ children = (
+ 7C8AE849189DE3CD00C33786 /* CoreAudioChannelLayout.cpp */,
+ 7C8AE844189DE3CD00C33786 /* CoreAudioChannelLayout.h */,
+ 7C8AE84A189DE3CD00C33786 /* CoreAudioDevice.cpp */,
+ 7C8AE845189DE3CD00C33786 /* CoreAudioDevice.h */,
+ 7C8AE84B189DE3CD00C33786 /* CoreAudioHardware.cpp */,
+ 7C8AE846189DE3CD00C33786 /* CoreAudioHardware.h */,
+ 7C8AE852189DE47400C33786 /* CoreAudioHelpers.cpp */,
+ 7C8AE853189DE47700C33786 /* CoreAudioHelpers.h */,
+ 7C8AE84C189DE3CD00C33786 /* CoreAudioStream.cpp */,
+ 7C8AE847189DE3CD00C33786 /* CoreAudioStream.h */,
+ );
+ name = osx;
+ sourceTree = "<group>";
+ };
810C9F5F0D67BCC80095F5DD /* MediaConnect */ = {
isa = PBXGroup;
children = (
DFB65F6C15373AE7006B8FF1 /* Engines */,
DFB65F8815373AE7006B8FF1 /* Interfaces */,
DFB65FA215373AE7006B8FF1 /* Utils */,
+ F5CC22851814F7B5006B5E91 /* Sinks */,
DFB65F6515373AE7006B8FF1 /* AEFactory.cpp */,
DFB65F6615373AE7006B8FF1 /* AEFactory.h */,
+ F5CC230A18150118006B5E91 /* AESinkFactory.cpp */,
+ F5CC230B18150118006B5E91 /* AESinkFactory.h */,
);
path = AudioEngine;
sourceTree = "<group>";
DFB65F6C15373AE7006B8FF1 /* Engines */ = {
isa = PBXGroup;
children = (
- F5ED9362155CECC800842059 /* CoreAudio */,
+ F5CC22D21814FF3B006B5E91 /* ActiveAE */,
);
path = Engines;
sourceTree = "<group>";
E38E15120D25F9F900618676 /* DVDAudioCodecLPcm.h */,
DFB6610615374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp */,
DFB6610715374E80006B8FF1 /* DVDAudioCodecPassthrough.h */,
- F5F245D81112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp */,
- F5F245D91112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.h */,
E38E15150D25F9F900618676 /* DVDAudioCodecPcm.cpp */,
E38E15160D25F9F900618676 /* DVDAudioCodecPcm.h */,
);
E38E1E220D25F9FD00618676 /* utils */ = {
isa = PBXGroup;
children = (
+ F5CC22FB18150065006B5E91 /* ActorProtocol.cpp */,
+ F5CC22FC18150065006B5E91 /* ActorProtocol.h */,
E38E1E230D25F9FD00618676 /* AlarmClock.cpp */,
E38E1E240D25F9FD00618676 /* AlarmClock.h */,
F5A9D3081097C9370050490F /* AliasShortcutUtils.cpp */,
36A9443C15821E2800727135 /* DatabaseUtils.h */,
7CC30E8816296078003E7579 /* EdenVideoArtUpdater.cpp */,
7CC30E8916296078003E7579 /* EdenVideoArtUpdater.h */,
+ F5CC2302181500B1006B5E91 /* EndianSwap.cpp */,
436B38F3106628850049AB3B /* EndianSwap.h */,
DF529BAC1741697B00523FB4 /* Environment.cpp */,
DF529BAD1741697B00523FB4 /* Environment.h */,
path = "json-rpc";
sourceTree = "<group>";
};
+ F5CC22851814F7B5006B5E91 /* Sinks */ = {
+ isa = PBXGroup;
+ children = (
+ 7C8AE7FE189DE3A700C33786 /* osx */,
+ F5CC228C1814F7F7006B5E91 /* AESinkDARWINIOS.cpp */,
+ F5CC228D1814F7F7006B5E91 /* AESinkDARWINIOS.h */,
+ F5CC22891814F7E9006B5E91 /* AESinkDARWINOSX.cpp */,
+ F5CC228A1814F7E9006B5E91 /* AESinkDARWINOSX.h */,
+ F5CC234518150277006B5E91 /* AESinkNULL.cpp */,
+ F5CC234618150277006B5E91 /* AESinkNULL.h */,
+ F5CC238618150768006B5E91 /* AESinkProfiler.cpp */,
+ F5CC238718150768006B5E91 /* AESinkProfiler.h */,
+ );
+ name = Sinks;
+ sourceTree = "<group>";
+ };
+ F5CC22D21814FF3B006B5E91 /* ActiveAE */ = {
+ isa = PBXGroup;
+ children = (
+ F5CC22D31814FF3B006B5E91 /* ActiveAE.cpp */,
+ F5CC22D41814FF3B006B5E91 /* ActiveAE.h */,
+ F5CC22D51814FF3B006B5E91 /* ActiveAEBuffer.cpp */,
+ F5CC22D61814FF3B006B5E91 /* ActiveAEBuffer.h */,
+ F5CC22D71814FF3B006B5E91 /* ActiveAEResample.cpp */,
+ F5CC22D81814FF3B006B5E91 /* ActiveAEResample.h */,
+ F5CC22D91814FF3B006B5E91 /* ActiveAESink.cpp */,
+ F5CC22DA1814FF3B006B5E91 /* ActiveAESink.h */,
+ F5CC22DB1814FF3B006B5E91 /* ActiveAESound.cpp */,
+ F5CC22DC1814FF3B006B5E91 /* ActiveAESound.h */,
+ F5CC22DD1814FF3B006B5E91 /* ActiveAEStream.cpp */,
+ F5CC22DE1814FF3B006B5E91 /* ActiveAEStream.h */,
+ );
+ path = ActiveAE;
+ sourceTree = "<group>";
+ };
F5E1050C140AA38000175026 /* peripherals */ = {
isa = PBXGroup;
children = (
name = playercorefactory;
sourceTree = "<group>";
};
- F5ED9362155CECC800842059 /* CoreAudio */ = {
- 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 */,
- F5ED9460155D777B00842059 /* CoreAudioChannelLayout.cpp */,
- F5ED9461155D777B00842059 /* CoreAudioChannelLayout.h */,
- F5ED942A155D729500842059 /* CoreAudioDevice.cpp */,
- F5ED942B155D729500842059 /* CoreAudioDevice.h */,
- F5ED9507155D855200842059 /* CoreAudioGraph.cpp */,
- F5ED9508155D855200842059 /* CoreAudioGraph.h */,
- F5ED942C155D729500842059 /* CoreAudioHardware.cpp */,
- F5ED942D155D729500842059 /* CoreAudioHardware.h */,
- F5ED9494155D7B9900842059 /* CoreAudioMixMap.cpp */,
- F5ED9495155D7B9900842059 /* CoreAudioMixMap.h */,
- F5ED943C155D743700842059 /* CoreAudioStream.cpp */,
- F5ED943D155D743700842059 /* CoreAudioStream.h */,
- F5ED94A9155D7F8000842059 /* CoreAudioUnit.cpp */,
- F5ED94AA155D7F8000842059 /* CoreAudioUnit.h */,
- DFB65F7A15373AE7006B8FF1 /* ICoreAudioAEHAL.h */,
- DFB65F7B15373AE7006B8FF1 /* ICoreAudioSource.h */,
- );
- name = CoreAudio;
- sourceTree = "<group>";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
F5DC8801110A46C700EE1B15 /* ModplugCodec.cpp in Sources */,
F5F240EF110A4F76009126C6 /* CrystalHD.cpp in Sources */,
F5F244651110DC6B009126C6 /* FileOperationJob.cpp in Sources */,
- F5F245DA1112C6AC009126C6 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
F5F245EE1112C9AB009126C6 /* FileUtils.cpp in Sources */,
F5A7A702112893E50059D6AA /* AnnouncementManager.cpp in Sources */,
F5A7A85B112908F00059D6AA /* WebServer.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 */,
DFB65FCC15373AE7006B8FF1 /* AEBitstreamPacker.cpp in Sources */,
DFB65FCD15373AE7006B8FF1 /* AEBuffer.cpp in Sources */,
DFB65FCE15373AE7006B8FF1 /* AEChannelInfo.cpp in Sources */,
DFB65FD415373AE7006B8FF1 /* AEWAVLoader.cpp in Sources */,
DFB6610915374E80006B8FF1 /* DVDAudioCodecPassthrough.cpp in Sources */,
7C0B98A4154B79C30065A238 /* AEDeviceInfo.cpp in Sources */,
- F5ED942E155D729500842059 /* CoreAudioDevice.cpp in Sources */,
- F5ED942F155D729500842059 /* CoreAudioHardware.cpp in Sources */,
- F5ED943E155D743700842059 /* CoreAudioStream.cpp in Sources */,
- F5ED9462155D777B00842059 /* CoreAudioChannelLayout.cpp in Sources */,
- F5ED9496155D7B9900842059 /* CoreAudioMixMap.cpp in Sources */,
- F5ED94AB155D7F8000842059 /* CoreAudioUnit.cpp in Sources */,
- F5ED9509155D855200842059 /* CoreAudioGraph.cpp in Sources */,
7C6EB330155BD1D40080368A /* ImageFile.cpp in Sources */,
7C6EB6FA155F32C30080368A /* HTTPImageHandler.cpp in Sources */,
C84828C0156CFCD8005A996F /* PVRClient.cpp in Sources */,
7C1409A9184015C9009F9411 /* InfoExpression.cpp in Sources */,
AE32174218313ADF0003FAFC /* XSLTUtils.cpp in Sources */,
7C15DCBC1892481400FCE564 /* InfoBool.cpp in Sources */,
+ F5CC228B1814F7E9006B5E91 /* AESinkDARWINOSX.cpp in Sources */,
+ F5CC22EB1814FF3B006B5E91 /* ActiveAE.cpp in Sources */,
+ F5CC22EC1814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */,
+ F5CC22ED1814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */,
+ F5CC22EE1814FF3B006B5E91 /* ActiveAESink.cpp in Sources */,
+ F5CC22EF1814FF3B006B5E91 /* ActiveAESound.cpp in Sources */,
+ F5CC22F01814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */,
+ F5CC22FF18150065006B5E91 /* ActorProtocol.cpp in Sources */,
+ F5CC2305181500B1006B5E91 /* EndianSwap.cpp in Sources */,
+ F5CC230E18150118006B5E91 /* AESinkFactory.cpp in Sources */,
+ F5CC234918150277006B5E91 /* AESinkNULL.cpp in Sources */,
+ F5CC238A18150768006B5E91 /* AESinkProfiler.cpp in Sources */,
+ 7C8AE84E189DE3CD00C33786 /* CoreAudioChannelLayout.cpp in Sources */,
+ 7C8AE84F189DE3CD00C33786 /* CoreAudioDevice.cpp in Sources */,
+ 7C8AE850189DE3CD00C33786 /* CoreAudioHardware.cpp in Sources */,
+ 7C8AE851189DE3CD00C33786 /* CoreAudioStream.cpp in Sources */,
+ 7C8AE854189DE47F00C33786 /* CoreAudioHelpers.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DFF0F13617528350002DA3A4 /* Exception.cpp in Sources */,
DFF0F13717528350002DA3A4 /* ilog.cpp in Sources */,
DFF0F13817528350002DA3A4 /* AEEncoderFFmpeg.cpp in Sources */,
- DFF0F13917528350002DA3A4 /* CoreAudioAE.cpp in Sources */,
- DFF0F13A17528350002DA3A4 /* CoreAudioAEHAL.cpp in Sources */,
- DFF0F13B17528350002DA3A4 /* CoreAudioAEHALIOS.cpp in Sources */,
- DFF0F13C17528350002DA3A4 /* CoreAudioAESound.cpp in Sources */,
- DFF0F13D17528350002DA3A4 /* CoreAudioAEStream.cpp in Sources */,
DFF0F13E17528350002DA3A4 /* AEBitstreamPacker.cpp in Sources */,
DFF0F13F17528350002DA3A4 /* AEBuffer.cpp in Sources */,
DFF0F14017528350002DA3A4 /* AEChannelInfo.cpp in Sources */,
DFF0F15917528350002DA3A4 /* DVDAudioCodecLibMad.cpp in Sources */,
DFF0F15A17528350002DA3A4 /* DVDAudioCodecLPcm.cpp in Sources */,
DFF0F15B17528350002DA3A4 /* DVDAudioCodecPassthrough.cpp in Sources */,
- DFF0F15C17528350002DA3A4 /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
DFF0F15D17528350002DA3A4 /* DVDAudioCodecPcm.cpp in Sources */,
DFF0F15E17528350002DA3A4 /* DVDOverlayCodec.cpp in Sources */,
DFF0F15F17528350002DA3A4 /* DVDOverlayCodecCC.cpp in Sources */,
7C1409AB184015C9009F9411 /* InfoExpression.cpp in Sources */,
AE32174318313AE10003FAFC /* XSLTUtils.cpp in Sources */,
7C15DCBE1892481400FCE564 /* InfoBool.cpp in Sources */,
+ F5CC228F1814F7F7006B5E91 /* AESinkDARWINIOS.cpp in Sources */,
+ F5CC22E51814FF3B006B5E91 /* ActiveAE.cpp in Sources */,
+ F5CC22E61814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */,
+ F5CC22E71814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */,
+ F5CC22E81814FF3B006B5E91 /* ActiveAESink.cpp in Sources */,
+ F5CC22E91814FF3B006B5E91 /* ActiveAESound.cpp in Sources */,
+ F5CC22EA1814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */,
+ F5CC22FE18150065006B5E91 /* ActorProtocol.cpp in Sources */,
+ F5CC2304181500B1006B5E91 /* EndianSwap.cpp in Sources */,
+ F5CC230D18150118006B5E91 /* AESinkFactory.cpp in Sources */,
+ F5CC234818150277006B5E91 /* AESinkNULL.cpp in Sources */,
+ F5CC238918150768006B5E91 /* AESinkProfiler.cpp in Sources */,
+ DF374B2518AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E4991196174E5CEB00741B6D /* Exception.cpp in Sources */,
E4991197174E5CEB00741B6D /* ilog.cpp in Sources */,
E4991198174E5CF600741B6D /* AEEncoderFFmpeg.cpp in Sources */,
- E4991199174E5CFA00741B6D /* CoreAudioAE.cpp in Sources */,
- E499119A174E5CFA00741B6D /* CoreAudioAEHAL.cpp in Sources */,
- E499119B174E5CFA00741B6D /* CoreAudioAEHALIOS.cpp in Sources */,
- E499119D174E5CFA00741B6D /* CoreAudioAESound.cpp in Sources */,
- E499119E174E5CFA00741B6D /* CoreAudioAEStream.cpp in Sources */,
E49911A6174E5CFE00741B6D /* AEBitstreamPacker.cpp in Sources */,
E49911A7174E5CFE00741B6D /* AEBuffer.cpp in Sources */,
E49911A8174E5CFE00741B6D /* AEChannelInfo.cpp in Sources */,
E49911C1174E5D2500741B6D /* DVDAudioCodecLibMad.cpp in Sources */,
E49911C2174E5D2500741B6D /* DVDAudioCodecLPcm.cpp in Sources */,
E49911C3174E5D2500741B6D /* DVDAudioCodecPassthrough.cpp in Sources */,
- E49911C4174E5D2500741B6D /* DVDAudioCodecPassthroughFFmpeg.cpp in Sources */,
E49911C5174E5D2500741B6D /* DVDAudioCodecPcm.cpp in Sources */,
E49911C6174E5D2500741B6D /* DVDOverlayCodec.cpp in Sources */,
E49911C7174E5D2500741B6D /* DVDOverlayCodecCC.cpp in Sources */,
7C1409AA184015C9009F9411 /* InfoExpression.cpp in Sources */,
AE4E87A717354C4A00D15206 /* XSLTUtils.cpp in Sources */,
7C15DCBD1892481400FCE564 /* InfoBool.cpp in Sources */,
+ F5CC228E1814F7F7006B5E91 /* AESinkDARWINIOS.cpp in Sources */,
+ F5CC22DF1814FF3B006B5E91 /* ActiveAE.cpp in Sources */,
+ F5CC22E01814FF3B006B5E91 /* ActiveAEBuffer.cpp in Sources */,
+ F5CC22E11814FF3B006B5E91 /* ActiveAEResample.cpp in Sources */,
+ F5CC22E21814FF3B006B5E91 /* ActiveAESink.cpp in Sources */,
+ F5CC22E31814FF3B006B5E91 /* ActiveAESound.cpp in Sources */,
+ F5CC22E41814FF3B006B5E91 /* ActiveAEStream.cpp in Sources */,
+ F5CC22FD18150065006B5E91 /* ActorProtocol.cpp in Sources */,
+ F5CC2303181500B1006B5E91 /* EndianSwap.cpp in Sources */,
+ F5CC230C18150118006B5E91 /* AESinkFactory.cpp in Sources */,
+ F5CC234718150277006B5E91 /* AESinkNULL.cpp in Sources */,
+ F5CC238818150768006B5E91 /* AESinkProfiler.cpp in Sources */,
+ DF374B2418AC2BA20076B514 /* CoreAudioHelpers.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
<?xml version="1.0" encoding="UTF-8"?>
<addon id="xbmc.gui" version="5.0.1" provider-name="Team XBMC">
- <backwards-compatibility abi="4.00"/>
+ <backwards-compatibility abi="5.0.0"/>
<requires>
<import addon="xbmc.core" version="0.1.0"/>
</requires>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!-- Possible values for <charsets> -->
+<!-- ============================== -->
+<!-- -->
+<!-- ISO-Charsets: -->
+<!-- ISO-8859-1 - Western Europe (ISO) -->
+<!-- ISO-8859-2 - Central Europe (ISO) -->
+<!-- ISO-8859-3 - South Europe (ISO) -->
+<!-- ISO-8859-4 - Baltic (ISO) -->
+<!-- ISO-8859-5 - Cyrillic (ISO) -->
+<!-- ISO-8859-6 - Arabic (ISO) -->
+<!-- ISO-8859-7 - Greek (ISO) -->
+<!-- ISO-8859-8 - Hebrew (ISO) -->
+<!-- ISO-8859-9 - Turkish (ISO) -->
+<!-- -->
+<!-- Windows Charsets: -->
+<!-- CP1250 - Central Europe (Windows) -->
+<!-- CP1251 - Cyrillic (Windows) -->
+<!-- CP1252 - Western Europe (Windows) -->
+<!-- CP1253 - Greek (Windows) -->
+<!-- CP1254 - Turkish (Windows) -->
+<!-- CP1255 - Hebrew (Windows) -->
+<!-- CP1256 - Arabic (Windows) -->
+<!-- CP1257 - Baltic (Windows) -->
+<!-- CP1258 - Vietnamesse (Windows) -->
+<!-- CP874 - Thai (Windows) -->
+<!-- CP949 - Korean -->
+<!-- -->
+<!-- Others: -->
+<!-- GBK - Chinese Simplified (GBK) -->
+<!-- BIG5 - Chinese Traditional (Big5) -->
+<!-- BIG5-HKSCS - Hong Kong (Big5-HKSCS) -->
+<!-- SHIFT_JIS - Japanese (Shift-JIS) -->
+<!-- -->
+<!-- Link: (Country-Codepage comparison) -->
+<!-- http://www.science.co.il/Language/Locale-Codes.asp -->
+<!-- -->
+<!-- Possible values for <dvd> -->
+<!-- ========================= -->
+<!-- Language codes expected are two character -->
+<!-- codes as defined in ISO639. -->
+<!-- -->
+<!-- Link: -->
+<!-- http://etext.virginia.edu/tei/iso639.html -->
+
+
+
+<language locale="en">
+ <charsets>
+ <gui>CP1252</gui>
+ <subtitle>CP1252</subtitle>
+ </charsets>
+
+ <dvd>
+ <menu>en</menu>
+ <audio>en</audio>
+ <subtitle>en</subtitle>
+ </dvd>
+
+ <regions>
+ <region name="Australia (12h)" locale="AU">
+ <dateshort>DD/MM/YYYY</dateshort>
+ <datelong>DDDD, D MMMM YYYY</datelong>
+ <time symbolAM="AM" symbolPM="PM">h:mm:ss xx</time>
+ <tempunit>C</tempunit>
+ <speedunit>kmh</speedunit>
+ <timezone>GMT</timezone>
+ </region>
+ <region name="Australia (24h)" locale="AU">
+ <dateshort>DD/MM/YYYY</dateshort>
+ <datelong>DDDD, D MMMM YYYY</datelong>
+ <time symbolAM="AM" symbolPM="PM">H:mm:ss</time>
+ <tempunit>C</tempunit>
+ <speedunit>kmh</speedunit>
+ <timezone>GMT</timezone>
+ </region>
+ </regions>
+
+ <sorttokens>
+ <token>The</token>
+ </sorttokens>
+</language>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!-- About Timezone and daylight saving time - (DST) / SUMMER TIME. -->
+<!-- Link: -->
+<!-- http://www.worldtimezone.com/ -->
+
+<language locale="fr">
+ <charsets>
+ <gui>CP1252</gui>
+ <subtitle>CP1252</subtitle>
+ </charsets>
+
+ <dvd>
+ <menu>fr</menu>
+ <audio>fr</audio>
+ <subtitle>fr</subtitle>
+ </dvd>
+
+ <regions>
+ <region name="Canada" locale="CA">
+ <dateshort>DD/MM/YYYY</dateshort>
+ <datelong>DDDD D MMMM YYYY</datelong>
+ <time symbolAM="" symbolPM="">H:mm:ss</time>
+ <tempunit>C</tempunit>
+ <speedunit>kmh</speedunit>
+ <timezone>EST</timezone>
+ </region>
+ </regions>
+
+ <sorttokens>
+ <token>The</token>
+ <token>Le</token>
+ <token>La</token>
+ <token>Les</token>
+ <token>Un</token>
+ <token>Une</token>
+ <token>Des</token>
+ <token separators="'">L</token>
+ </sorttokens>
+</language>
+
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecFFmpeg.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLibMad.cpp" />
<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\Video\DVDVideoCodecCrystalHD.cpp" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodecFFmpeg.cpp" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecFFmpeg.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLibMad.h" />
<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\Video\DllLibMpeg2.h" />
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\DVDVideoCodec.h" />
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLPcm.cpp">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClCompile>
- <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthroughFFmpeg.cpp">
- <Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
- </ClCompile>
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.cpp">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClCompile>
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecLPcm.h">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClInclude>
- <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthroughFFmpeg.h">
- <Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
- </ClInclude>
<ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPcm.h">
<Filter>cores\dvdplayer\DVDCodecs\Audio</Filter>
</ClInclude>
<setting id="audiooutput.channels" help="36367" />
</group>
<group id="3">
- <setting id="audiooutput.ac3transcode">
+ <setting id="audiooutput.eac3passthrough">
<visible>false</visible>
</setting>
<setting id="audiooutput.truehdpassthrough">
<default>Default</default>
<visible>false</visible>
</setting>
- <setting id="audiooutput.ac3passthrough">
- <requirement>IsAppleTV2</requirement>
- </setting>
- <setting id="audiooutput.dtspassthrough">
- <requirement>IsAppleTV2</requirement>
- </setting>
- <setting id="audiooutput.truehdpassthrough">
- <visible>false</visible>
- </setting>
- <setting id="audiooutput.dtshdpassthrough">
- <visible>false</visible>
- </setting>
</group>
</category>
<category id="input">
{
// if the skin changes and the current theme is not the default one, reset
// the theme to the default value (which will also change lookandfeel.skincolors
- // which in turn will reload the skin
+ // which in turn will reload the skin. Similarly, if the current skin font is not
+ // the default, reset it as well.
if (settingId == "lookandfeel.skin" && CSettings::Get().GetString("lookandfeel.skintheme") != "SKINDEFAULT")
CSettings::Get().SetString("lookandfeel.skintheme", "SKINDEFAULT");
+ else if (settingId == "lookandfeel.skin" && CSettings::Get().GetString("lookandfeel.font") != "Default")
+ CSettings::Get().SetString("lookandfeel.font", "Default");
else
{
std::string builtin("ReloadSkin");
#include "AEFactory.h"
#include "Utils/AEUtil.h"
-#if defined(TARGET_DARWIN)
- #include "Engines/CoreAudio/CoreAudioAE.h"
- #include "settings/lib/SettingsManager.h"
-#else
- #include "Engines/ActiveAE/ActiveAE.h"
-#endif
+#include "Engines/ActiveAE/ActiveAE.h"
#include "guilib/LocalizeStrings.h"
#include "settings/lib/Setting.h"
bool CAEFactory::LoadEngine()
{
-#if defined(TARGET_DARWIN)
- return CAEFactory::LoadEngine(AE_ENGINE_COREAUDIO);
-#else
return CAEFactory::LoadEngine(AE_ENGINE_ACTIVE);
-#endif
}
bool CAEFactory::LoadEngine(enum AEEngine engine)
switch(engine)
{
case AE_ENGINE_NULL :
-#if defined(TARGET_DARWIN)
- case AE_ENGINE_COREAUDIO: AE = new CCoreAudioAE(); break;
-#else
case AE_ENGINE_ACTIVE : AE = new ActiveAE::CActiveAE(); break;
-#endif
default:
return false;
}
bool foundValue = false;
AEDeviceList sinkList;
EnumerateOutputDevices(sinkList, passthrough);
-#if !defined(TARGET_DARWIN)
if (sinkList.size() == 0)
list.push_back(std::make_pair("Error - no devices found", "error"));
else
{
-#endif
for (AEDeviceList::const_iterator sink = sinkList.begin(); sink != sinkList.end(); ++sink)
{
if (sink == sinkList.begin())
if (StringUtils::EqualsNoCase(current, sink->second))
foundValue = true;
}
-#if !defined(TARGET_DARWIN)
}
-#endif
if (!foundValue)
current = firstDevice;
if (AE)
AE->KeepConfiguration(millis);
}
+
+void CAEFactory::DeviceChange()
+{
+ if (AE)
+ AE->DeviceChange();
+}
static void SettingOptionsAudioStreamsilenceFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t);
static bool IsSettingVisible(const std::string &condition, const std::string &value, const std::string &settingId);
static void KeepConfiguration(unsigned int millis);
+ static void DeviceChange();
static void RegisterAudioCallback(IAudioCallback* pCallback);
static void UnregisterAudioCallback();
#include "Sinks/AESinkAUDIOTRACK.h"
#elif defined(TARGET_RASPBERRY_PI)
#include "Sinks/AESinkPi.h"
+#elif defined(TARGET_DARWIN_IOS)
+ #include "Sinks/AESinkDARWINIOS.h"
+#elif defined(TARGET_DARWIN_OSX)
+ #include "Sinks/AESinkDARWINOSX.h"
#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
#if defined(HAS_ALSA)
#include "Sinks/AESinkALSA.h"
driver == "AUDIOTRACK" ||
#elif defined(TARGET_RASPBERRY_PI)
driver == "PI" ||
+#elif defined(TARGET_DARWIN_IOS)
+ driver == "DARWINIOS" ||
+#elif defined(TARGET_DARWIN_OSX)
+ driver == "DARWINOSX" ||
#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
#if defined(HAS_ALSA)
driver == "ALSA" ||
sink = new CAESinkAUDIOTRACK();
#elif defined(TARGET_RASPBERRY_PI)
sink = new CAESinkPi();
+#elif defined(TARGET_DARWIN_IOS)
+ sink = new CAESinkDARWINIOS();
+#elif defined(TARGET_DARWIN_OSX)
+ sink = new CAESinkDARWINOSX();
#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
#if defined(HAS_PULSEAUDIO)
else if (driver == "PULSE")
if(!info.m_deviceInfoList.empty())
list.push_back(info);
+#elif defined(TARGET_DARWIN_IOS)
+
+ info.m_deviceInfoList.clear();
+ info.m_sinkName = "DARWINIOS";
+ CAESinkDARWINIOS::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ if(!info.m_deviceInfoList.empty())
+ list.push_back(info);
+
+#elif defined(TARGET_DARWIN_OSX)
+
+ info.m_deviceInfoList.clear();
+ info.m_sinkName = "DARWINOSX";
+ CAESinkDARWINOSX::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ if(!info.m_deviceInfoList.empty())
+ list.push_back(info);
+
#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
// check if user wants us to do something specific
if (getenv("AE_SINK"))
}
msg->Reply(CActiveAEControlProtocol::ACC);
return;
+ case CActiveAEControlProtocol::DEVICECHANGE:
+ time_t now;
+ time(&now);
+ while (!m_extLastDeviceChange.empty() && (now - m_extLastDeviceChange.front() > 0))
+ {
+ m_extLastDeviceChange.pop();
+ }
+ if (m_extLastDeviceChange.size() > 2)
+ {
+ CLog::Log(LOGWARNING,"CActiveAE - received %ld device change events within one second", m_extLastDeviceChange.size());
+ return;
+ }
+ m_extLastDeviceChange.push(now);
+ UnconfigureSink();
+ m_sink.EnumerateSinkList(true);
+ LoadSettings();
+ m_extError = false;
+ Configure();
+ if (!m_extError)
+ {
+ m_state = AE_TOP_CONFIGURED_PLAY;
+ m_extTimeout = 0;
+ }
+ else
+ {
+ m_state = AE_TOP_ERROR;
+ m_extTimeout = 500;
+ }
+ m_controlPort.PurgeOut(CActiveAEControlProtocol::DEVICECHANGE);
+ return;
case CActiveAEControlProtocol::PAUSESTREAM:
CActiveAEStream *stream;
stream = *(CActiveAEStream**)msg->data;
if (m_streams.empty())
{
inputFormat = m_sinkFormat;
+ if (m_sinkFormat.m_channelLayout.Count() > m_sinkRequestFormat.m_channelLayout.Count())
+ {
+ inputFormat.m_channelLayout = m_sinkRequestFormat.m_channelLayout;
+ inputFormat.m_channelLayout.ResolveChannels(m_sinkFormat.m_channelLayout);
+ }
inputFormat.m_dataFormat = AE_FMT_FLOAT;
inputFormat.m_frameSize = inputFormat.m_channelLayout.Count() *
(CAEUtil::DataFormatToBits(inputFormat.m_dataFormat) >> 3);
outputFormat.m_dataFormat = AE_FMT_FLOAT;
outputFormat.m_frameSize = outputFormat.m_channelLayout.Count() *
(CAEUtil::DataFormatToBits(outputFormat.m_dataFormat) >> 3);
+
+ // due to channel ordering of the driver, a sink may return more channels than
+ // requested, i.e. 2.1 request returns FL,FR,BL,BR,FC,LFE for ALSA
+ // in this case we need to downmix to requested format
+ if (m_sinkFormat.m_channelLayout.Count() > m_sinkRequestFormat.m_channelLayout.Count())
+ {
+ outputFormat.m_channelLayout = m_sinkRequestFormat.m_channelLayout;
+ outputFormat.m_channelLayout.ResolveChannels(m_sinkFormat.m_channelLayout);
+ }
+
// TODO: adjust to decoder
sinkInputFormat = outputFormat;
}
Message *reply;
if (m_controlPort.SendOutMessageSync(CActiveAEControlProtocol::INIT,
&reply,
- 5000))
+ 10000))
{
bool success = reply->signal == CActiveAEControlProtocol::ACC;
reply->Release();
m_controlPort.SendOutMessage(CActiveAEControlProtocol::KEEPCONFIG, &timeMs, sizeof(unsigned int));
}
+void CActiveAE::DeviceChange()
+{
+ m_controlPort.SendOutMessage(CActiveAEControlProtocol::DEVICECHANGE);
+}
+
void CActiveAE::OnLostDevice()
{
Message *reply;
#include "cores/AudioEngine/Interfaces/AESound.h"
#include "cores/AudioEngine/AEFactory.h"
#include "guilib/DispResource.h"
+#include <queue>
// ffmpeg
#include "DllAvFormat.h"
INIT = 0,
RECONFIGURE,
SUSPEND,
+ DEVICECHANGE,
MUTE,
VOLUME,
PAUSESTREAM,
virtual bool SupportsQualityLevel(enum AEQuality level);
virtual bool IsSettingVisible(const std::string &settingId);
virtual void KeepConfiguration(unsigned int millis);
+ virtual void DeviceChange();
virtual void RegisterAudioCallback(IAudioCallback* pCallback);
virtual void UnregisterAudioCallback();
XbmcThreads::EndTime m_extDrainTimer;
unsigned int m_extKeepConfig;
bool m_extDeferData;
+ std::queue<time_t> m_extLastDeviceChange;
enum
{
if (!m_sinkInfoList.empty() && !force)
return;
- unsigned int c_retry = 5;
+ unsigned int c_retry = 4;
m_sinkInfoList.clear();
CAESinkFactory::EnumerateEx(m_sinkInfoList);
while(m_sinkInfoList.size() == 0 && c_retry > 0)
{
CLog::Log(LOGNOTICE, "No Devices found - retry: %d", c_retry);
- Sleep(2000);
+ Sleep(1500);
c_retry--;
// retry the enumeration
CAESinkFactory::EnumerateEx(m_sinkInfoList, true);
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#include "CoreAudioAE.h"
-
-#include "CoreAudioAEStream.h"
-#include "CoreAudioAESound.h"
-#include "Application.h"
-#include "cores/AudioEngine/Utils/AEUtil.h"
-#include "settings/AdvancedSettings.h"
-#include "settings/Settings.h"
-#include "threads/SingleLock.h"
-#include "utils/EndianSwap.h"
-#include "utils/log.h"
-#include "utils/TimeUtils.h"
-#include "utils/MathUtils.h"
-#include "threads/SystemClock.h"
-
-#define DELAY_FRAME_TIME 20
-#define BUFFERSIZE 16416
-
-// on darwin when the devicelist changes
-// reinit by calling opencoreaudio with the last engine parameters
-// (this will fallback to default
-// device when our current output device vanishes
-// and on the other hand will go back to that device
-// if it re-appears).
-#if defined(TARGET_DARWIN_OSX)
-OSStatus deviceChangedCB( AudioObjectID inObjectID,
- UInt32 inNumberAddresses,
- const AudioObjectPropertyAddress inAddresses[],
- void* inClientData)
-{
- CCoreAudioAE *pEngine = (CCoreAudioAE *)inClientData;
- if (pEngine->GetHAL())
- {
- pEngine->AudioDevicesChanged();
- CLog::Log(LOGDEBUG, "CCoreAudioAE - audiodevicelist changed!");
- }
- return noErr;
-}
-
-void RegisterDeviceChangedCB(bool bRegister, void *ref)
-{
- OSStatus ret = noErr;
- const AudioObjectPropertyAddress inAdr =
- {
- kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
-
- if (bRegister)
- ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
- else
- ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
-
- if (ret != noErr)
- CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
-}
-#else//ios
-void RegisterDeviceChangedCB(bool bRegister, void *ref){}
-#endif
-
-CCoreAudioAE::CCoreAudioAE() :
- m_Initialized (false ),
- m_deviceLost (false ),
- m_callbackRunning (false ),
- m_lastStreamFormat (AE_FMT_INVALID),
- m_lastChLayoutCount (0 ),
- m_lastSampleRate (0 ),
- m_chLayoutCount (0 ),
- m_rawPassthrough (false ),
- m_volume (1.0f ),
- m_volumeBeforeMute (1.0f ),
- m_muted (false ),
- m_soundMode (AE_SOUND_OFF ),
- m_streamsPlaying (false ),
- m_isSuspended (false ),
- m_softSuspend (false ),
- m_softSuspendTimer (0 )
-{
- HAL = new CCoreAudioAEHAL;
-
- RegisterDeviceChangedCB(true, this);
-}
-
-CCoreAudioAE::~CCoreAudioAE()
-{
- RegisterDeviceChangedCB(false, this);
- Shutdown();
-}
-
-void CCoreAudioAE::Shutdown()
-{
- CSingleLock engineLock(m_engineLock);
-
- 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 sounds */
- CSingleLock soundLock(m_soundLock);
- while (!m_sounds.empty())
- {
- CCoreAudioAESound *s = m_sounds.front();
- m_sounds.pop_front();
- delete s;
- }
-
- delete HAL;
- HAL = NULL;
-}
-
-void CCoreAudioAE::AudioDevicesChanged()
-{
- if (!m_Initialized && !m_deviceLost)
- return;
-
- CSingleLock engineLock(m_engineLock);
-
- // re-check initialized since it can have changed when we waited and grabbed the lock
- if (!m_Initialized && !m_deviceLost)
- return;
-
- OpenCoreAudio(m_lastSampleRate, COREAUDIO_IS_RAW(m_lastStreamFormat), m_lastStreamFormat, m_transcode);
-
- // when we tried to open the default device or the last device
- // again there was an error preventing us from doing it (mostly
- // the device couldn't be found) - in that case
- // mark our device as lost and hope that another callback
- // for changed device list fires (e.x. device reappears)
- if (!m_Initialized)
- m_deviceLost = true;
- else
- m_deviceLost = false;
-}
-
-bool CCoreAudioAE::Initialize()
-{
- CSingleLock engineLock(m_engineLock);
-
- Stop();
-
- Deinitialize();
-
- bool ret = OpenCoreAudio(44100, false, AE_FMT_FLOAT, false);
- m_lastSampleRate = 44100;
- m_lastStreamFormat = AE_FMT_FLOAT;
-
- Start();
-
- return ret;
-}
-
-bool CCoreAudioAE::OpenCoreAudio(unsigned int sampleRate, bool forceRaw,
- enum AEDataFormat rawDataFormat, bool forceTranscode)
-{
- unsigned int maxChannelCountInStreams = 0;
- // 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();
- }
-
- if (stream->GetChannelCount() > maxChannelCountInStreams)
- maxChannelCountInStreams = stream->GetChannelCount();
-
- ++itt;
- }
-
- /* override the sample rate based on the oldest stream if there is one */
- if (!m_streams.empty())
- sampleRate = m_streams.front()->GetSampleRate();
-
- if (forceRaw)
- m_rawPassthrough = true;
- else
- m_rawPassthrough = !m_streams.empty() && m_streams.front()->IsRaw();
- streamLock.Leave();
-
- if (m_rawPassthrough)
- CLog::Log(LOGINFO, "CCoreAudioAE::OpenCoreAudio - RAW passthrough enabled");
-
- m_transcode = forceTranscode;
-
- if (m_transcode)
- CLog::Log(LOGINFO, "CCoreAudioAE::OpenCoreAudio - transcode to ac3 enabled");
-
- std::string m_outputDevice = CSettings::Get().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 (CSettings::Get().GetInt("audiooutput.channels"))
- {
- default:
- case 0: m_stdChLayout = AE_CH_LAYOUT_2_0; break; /* do not allow 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 (CSettings::Get().GetInt("audiooutput.config") == AE_CONFIG_FIXED && !m_rawPassthrough)
- {
- sampleRate = CSettings::Get().GetInt("audiooutput.samplerate");
- CLog::Log(LOGINFO, "CCoreAudioAE::passthrough - Forcing samplerate to %d", sampleRate);
- }
-
- if (m_rawPassthrough && !m_transcode)
- {
- 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:
- // audio midi setup can be setup to 2.0 or 7.1
- // if we have the number of max channels from streams we use that for
- // selecting either 2.0 or 7.1 setup depending on that.
- // This allows DPII modes on amps for enhancing stereo sound
- // (when switching to 7.1 - all 8 channels will be pushed out preventing most amps
- // to switch to DPII mode)
- if (maxChannelCountInStreams == 1 || maxChannelCountInStreams == 2)
- m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
- else
- m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_7_1);
- m_format.m_sampleRate = sampleRate;
- m_format.m_dataFormat = AE_FMT_FLOAT;
- break;
- default:
- break;
- }
- }
- else if (m_transcode)
- {
- // transcode is to ac3 only, do we copy the ac3 settings from above
- m_format.m_channelLayout = CAEChannelInfo(AE_CH_LAYOUT_2_0);
- m_format.m_sampleRate = 48000;
- m_format.m_dataFormat = AE_FMT_S16NE;
- }
- 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 || m_transcode, initformat, m_transcode ? AE_FMT_AC3 : rawDataFormat, m_outputDevice, m_volume);
-
- 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 && !m_transcode)
- {
- /* 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);
- CLog::Log(LOGINFO, " Transcode : %d", m_transcode);
-
- 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();
-}
-
-void CCoreAudioAE::OnSettingsChange(const std::string& setting)
-{
- if (setting == "audiooutput.dontnormalizelevels")
- {
- // re-init streams remapper
- 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.ac3passthrough" ||
- setting == "audiooutput.eac3passthrough" ||
- setting == "audiooutput.dtspassthrough" ||
- setting == "audiooutput.channels" ||
- setting == "audiooutput.samplerate" ||
- setting == "audiooutput.config" ||
- setting == "audiooutput.passthrough" )
- {
- // only reinit the engine if we not
- // suspended (resume will initialize
- // us again in that case)
- if (!m_isSuspended)
- 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;
- // track volume if we are not muted
- // we need this because m_volume is init'ed via
- // SetVolume and need to also init m_volumeBeforeMute.
- if (!m_muted)
- m_volumeBeforeMute = 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;
-}
-
-bool CCoreAudioAE::IsSuspended()
-{
- return m_isSuspended;
-}
-
-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(AEDataFormat format)
-{
- switch(format)
- {
- case AE_FMT_AC3:
- case AE_FMT_DTS:
- case AE_FMT_EAC3:
- case AE_FMT_LPCM:
- return true;
- default:
- return false;
- }
-}
-
-bool CCoreAudioAE::IsSettingVisible(const std::string &settingId)
-{
- if (settingId == "audiooutput.samplerate")
- {
- if (CSettings::Get().GetInt("audiooutput.config") == AE_CONFIG_FIXED)
- return true;
- else
- return false;
- }
- else if (settingId == "audiooutput.stereoupmix")
- {
- if (CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0)
- return true;
- }
-
- return true;
-}
-
-CCoreAudioAEHAL* CCoreAudioAE::GetHAL()
-{
- return HAL;
-}
-
-IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
- unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options)
-{
- // if we are suspended we don't
- // want anyone to mess with us
- if (m_isSuspended && !m_softSuspend)
-#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
- Resume();
-#else
- return NULL;
-#endif
-
- CAEChannelInfo channelInfo(channelLayout);
- CLog::Log(LOGINFO, "CCoreAudioAE::MakeStream - %s, %u, %u, %s",
- CAEUtil::DataFormatToStr(dataFormat), sampleRate, encodedSamplerate, ((std::string)channelInfo).c_str());
-
- bool multichannelpcm = CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0; //if more then 2 channels are set - assume lpcm capability
-#if defined(TARGET_DARWIN_IOS)
- multichannelpcm = false;
-#endif
- // determine if we need to transcode this audio
- // when we're called, we'll either get the audio in an encoded form (COREAUDIO_IS_RAW==true)
- // that we can passthrough based on user options, or we'll get it unencoded
- // if it's unencoded, and is 5.1, we'll transcode it to AC3 if possible
- bool transcode = CSettings::Get().GetBool("audiooutput.passthrough") && CSettings::Get().GetBool("audiooutput.ac3passthrough") && !multichannelpcm &&
- !COREAUDIO_IS_RAW(dataFormat) &&
- (channelInfo.Count() == 6);
-
- CCoreAudioAEStream *stream = new CCoreAudioAEStream(dataFormat, sampleRate, encodedSamplerate, channelLayout, options, transcode);
- CSingleLock streamLock(m_streamLock);
- m_streams.push_back(stream);
- streamLock.Leave();
-
- if ((options & AESTREAM_PAUSED) == 0)
- Stop();
-
- // reinit the engine if pcm format changes or always on raw format or always on transcode
- if (m_Initialized && ( m_lastStreamFormat != dataFormat ||
- m_lastChLayoutCount != channelLayout.Count() ||
- m_lastSampleRate != sampleRate ||
- COREAUDIO_IS_RAW(dataFormat) ||
- transcode))
- {
- CSingleLock engineLock(m_engineLock);
- Stop();
- Deinitialize();
- m_Initialized = OpenCoreAudio(sampleRate, COREAUDIO_IS_RAW(dataFormat), dataFormat, transcode);
- m_lastStreamFormat = dataFormat;
- m_lastChLayoutCount = channelLayout.Count();
- m_lastSampleRate = sampleRate;
- }
-
- /* if the stream was not initialized, do it now */
- if (!stream->IsValid())
- stream->Initialize();
-
- if ((options & AESTREAM_PAUSED) == 0)
- 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 and are not suspended,
- // reinit the hardware to come back to anlog out
- if (/*m_streams.empty() || */ m_rawPassthrough && !m_isSuspended)
- {
- 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) || (m_isSuspended && !m_softSuspend))
- 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)
-{
- // we don't make sounds
- // when suspended
- if (m_isSuspended)
- return NULL;
-
- 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()
-{
-#if defined(TARGET_DARWIN_OSX)
- if (CSettings::Get().GetInt("audiooutput.streamsilence") != 0)
- return;
-
- if (!m_streamsPlaying && m_playing_sounds.empty())
- {
- if (!m_softSuspend)
- {
- m_softSuspend = true;
- m_softSuspendTimer = XbmcThreads::SystemClockMillis() + 10000; //10.0 second delay for softSuspend
- }
- }
- else
- {
- if (m_isSuspended)
- {
- CSingleLock engineLock(m_engineLock);
- CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Acquire CA HAL.");
- Start();
- m_isSuspended = false;
- }
- m_softSuspend = false;
- }
-
- unsigned int curSystemClock = XbmcThreads::SystemClockMillis();
- if (!m_isSuspended && m_softSuspend && curSystemClock > m_softSuspendTimer)
- {
- Suspend();// locks m_engineLock internally
- CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Release CA HAL.");
- }
-#endif // TARGET_DARWIN_OSX
-}
-
-void CCoreAudioAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
-{
- if (m_isSuspended && !m_softSuspend)
- return;
-
- HAL->EnumerateOutputDevices(devices, passthrough);
-}
-
-void CCoreAudioAE::Start()
-{
- if (!m_Initialized)
- return;
-
- HAL->Start();
-}
-
-void CCoreAudioAE::Stop()
-{
- if (!m_Initialized)
- return;
-
- HAL->Stop();
-}
-
-bool CCoreAudioAE::Suspend()
-{
- CSingleLock engineLock(m_engineLock);
- CLog::Log(LOGDEBUG, "CCoreAudioAE::Suspend - Suspending AE processing");
- m_isSuspended = true;
- // stop all gui sounds
- StopAllSounds();
- // stop the CA thread
- Stop();
-
- return true;
-}
-
-bool CCoreAudioAE::Resume()
-{
- // fire up the engine again
- bool ret = Initialize();
- CLog::Log(LOGDEBUG, "CCoreAudioAE::Resume - Resuming AE processing");
- m_isSuspended = false;
-
- return ret;
-}
-
-//***********************************************************************************************
-// 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 = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
-
- return ret;
-}
-
-
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <list>
-#include <map>
-
-#include "system.h"
-
-#include "cores/AudioEngine/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(const std::string& setting);
-
- virtual bool Suspend(); /* Suspend output and de-initialize "hog-mode" sink for external players and power savings */
- virtual bool Resume(); /* Resume ouput and re-initialize sink after Suspend() above */
- virtual bool IsSuspended(); /* Returns true if in Suspend mode - used by players */
-
- 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(AEDataFormat format);
- virtual bool IsSettingVisible(const std::string &settingId);
- virtual bool SupportsDrain() { return true; }
-
- 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);
-
- void AudioDevicesChanged();
-
-
-private:
- CCriticalSection m_callbackLock;
- CCriticalSection m_engineLock;
- 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;
-
- // Prevent multiple init/deinit
- bool m_Initialized;
- bool m_deviceLost;
- bool m_callbackRunning;
-
- AEAudioFormat m_format;
- enum AEDataFormat m_lastStreamFormat;
- unsigned int m_lastChLayoutCount;
- unsigned int m_lastSampleRate;
- unsigned int m_chLayoutCount;
- bool m_rawPassthrough;
- bool m_transcode;
-
- enum AEStdChLayout m_stdChLayout;
-
- bool OpenCoreAudio(unsigned int sampleRate, bool forceRaw, enum AEDataFormat rawDataFormat, bool forceTranscode);
- 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;
- bool m_isSuspended;
- bool m_softSuspend;
- unsigned int m_softSuspendTimer;
-};
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioAEHAL.h"
-#include "CoreAudioAE.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);
-}
-
-const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string &str)
-{
- char fourCC[5] = {
- (desc.mFormatID >> 24) & 0xFF,
- (desc.mFormatID >> 16) & 0xFF,
- (desc.mFormatID >> 8) & 0xFF,
- desc.mFormatID & 0xFF,
- 0
- };
-
- 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 "
- << ((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
- << " ("
- << (UInt32)desc.mSampleRate
- << "Hz)";
- str = sstr.str();
- break;
- default:
- sstr << "["
- << fourCC
- << "]";
- break;
- }
- return str.c_str();
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <string>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-// Helper Functions
-std::string GetError(OSStatus error);
-const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string &str);
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#if defined(TARGET_DARWIN_IOS)
-#include "system.h"
-
-#include "CoreAudioAEHALIOS.h"
-
-#include "xbmc/cores/AudioEngine/Utils/AEUtil.h"
-#include "AEFactory.h"
-#include "CoreAudioAE.h"
-#include "utils/log.h"
-
-#include <math.h>
-
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-// use the maximum frames per slice allows audio play when the screen is locked
-#define BUFFERED_FRAMES 4096
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CIOSCoreAudioHardware
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-AudioComponentInstance CIOSCoreAudioHardware::FindAudioDevice(std::string searchName)
-{
- if (!searchName.length())
- return 0;
-
- AudioComponentInstance defaultDevice = GetDefaultOutputDevice();
-
- 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_pSource (NULL ),
-m_audioUnit (NULL ),
-m_audioNode (NULL ),
-m_audioGraph (NULL ),
-m_Initialized (false ),
-m_renderProc (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;
-
- 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;
- }
-
- 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 |= 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 = 0;
- UInt32 size=sizeof(UInt32);
-
- ret = AudioUnitGetProperty(m_audioUnit,kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
-
- if (!ret && 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_Output, kOutputBus, 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, float initVolume)
-{
- 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;
-
- UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
-
- if (!m_audioUnit->EnableInputOuput())
- return false;
-
- m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
-
- 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;
-
- m_mixerUnit->SetMaxFramesPerSlice(bufferFrames);
-
- // 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;
-
- m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
-
- 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;
- }
-
- SetCurrentVolume(initVolume);
-
- 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;
-
- 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;
-
- outputUnit->SetMaxFramesPerSlice(m_audioUnit->GetBufferFrameSize());
-
- 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_audioGraph (NULL ),
-m_Initialized (false ),
-m_Passthrough (false ),
-m_allowMixing (false ),
-m_encoded (false ),
-m_initVolume (1.0f ),
-m_NumLatencyFrames (0 ),
-m_OutputBufferIndex (0 ),
-m_ae (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, m_initVolume))
- {
- CLog::Log(LOGERROR, "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, float initVolume)
-{
- m_ae = (CCoreAudioAE *)ae;
-
- if (!m_ae)
- return false;
-
- m_initformat = format;
- m_Passthrough = passThrough;
- m_encoded = false;
- m_OutputBufferIndex = 0;
- m_rawDataFormat = rawDataFormat;
- m_initVolume = initVolume;
-
- 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(LOGERROR, "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;
-}
-
-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
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#if defined(TARGET_DARWIN_IOS)
-
-#include <list>
-#include <vector>
-
-#include "ICoreAudioAEHAL.h"
-#include "CoreAudioAEHAL.h"
-#include "utils/StdString.h"
-
-#include <AudioUnit/AudioUnit.h>
-#include <AudioUnit/AudioUnitProperties.h>
-#include <AudioToolbox/AudioToolbox.h>
-#include <AudioToolbox/AudioServices.h>
-#include <CoreAudio/CoreAudioTypes.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, float initVolume);
- 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;
- float m_initVolume;
-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, float initVolume);
- 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
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifdef TARGET_DARWIN_OSX
-
-#include "system.h"
-
-#include "CoreAudioAEHALOSX.h"
-
-#include "CoreAudioAE.h"
-#include "CoreAudioAEHAL.h"
-#include "CoreAudioUnit.h"
-#include "CoreAudioDevice.h"
-#include "CoreAudioGraph.h"
-#include "CoreAudioMixMap.h"
-#include "CoreAudioHardware.h"
-#include "CoreAudioChannelLayout.h"
-
-#include "cores/AudioEngine/Utils/AEUtil.h"
-#include "utils/log.h"
-#include "settings/Settings.h"
-
-CCoreAudioAEHALOSX::CCoreAudioAEHALOSX() :
- m_audioGraph (NULL ),
- m_Initialized (false ),
- m_Passthrough (false ),
- m_allowMixing (false ),
- m_encoded (false ),
- m_initVolume (1.0f ),
- m_NumLatencyFrames (0 ),
- m_OutputBufferIndex (0 ),
- m_ae (NULL )
-{
- m_AudioDevice = new CCoreAudioDevice();
- m_OutputStream = new CCoreAudioStream();
-
- 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.");
- }
- }
-}
-
-CCoreAudioAEHALOSX::~CCoreAudioAEHALOSX()
-{
- Deinitialize();
-
- delete m_audioGraph;
- delete m_AudioDevice;
- delete m_OutputStream;
-}
-
-bool CCoreAudioAEHALOSX::InitializePCM(ICoreAudioSource *pSource, AEAudioFormat &format, bool allowMixing, AudioDeviceID outputDevice, bool encoded)
-{
- if (m_audioGraph)
- m_audioGraph->Close(), delete m_audioGraph;
- m_audioGraph = new CCoreAudioGraph();
- if (!m_audioGraph)
- return false;
-
- AudioChannelLayoutTag layout = g_LayoutMap[ CSettings::Get().GetInt("audiooutput.channels") ];
- // force optical/coax to 2.0 output channels
- if (!m_Passthrough && CSettings::Get().GetInt("audiooutput.channels") == AE_CH_LAYOUT_2_0)
- layout = g_LayoutMap[1];
-
- if (!m_audioGraph->Open(pSource, format, outputDevice, allowMixing, layout, m_initVolume, encoded ))
- {
- 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)
-{
- // Prevent any other application from using this device.
- m_AudioDevice->SetHogStatus(true);
- // Try to disable mixing support. Effectiveness depends on the device.
- m_AudioDevice->SetMixingSupport(false);
- // Set the Sample Rate as defined by the spec.
- m_AudioDevice->SetNominalSampleRate((float)format.m_sampleRate);
-
- if (!InitializePCM(pSource, format, false, outputDevice, true))
- return false;
-
- return true;
-}
-
-bool CCoreAudioAEHALOSX::InitializeEncoded(AudioDeviceID outputDevice, AEAudioFormat &format)
-{
- std::string formatString;
- AudioStreamID outputStream = 0;
- AudioStreamBasicDescription outputFormat = {0};
-
- // Fetch a list of the streams defined by the output device
- UInt32 streamIndex = 0;
- AudioStreamIdList streams;
- 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
-
- // 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)
- {
- // check pcm output formats
- 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
- {
- // check encoded formats
- if (desc.mFormat.mFormatID == kAudioFormat60958AC3 || desc.mFormat.mFormatID == 'IAC3')
- {
- if (desc.mFormat.mChannelsPerFrame == m_initformat.m_channelLayout.Count() &&
- desc.mFormat.mSampleRate == m_initformat.m_sampleRate )
- {
- 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[%u] - id: 0x%04X, Physical Format: %s",
- m_OutputBufferIndex, (uint)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)
-
- // 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.
- CCoreAudioHardware::SetAutoHogMode(false);
- 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
- {
- // Hog the device if it is not set to be done automatically
- m_AudioDevice->SetHogStatus(true);
- // Try to disable mixing. If we cannot, it may not be a problem
- m_AudioDevice->SetMixingSupport(false);
- }
-
- m_NumLatencyFrames = m_AudioDevice->GetNumLatencyFrames();
-
- // Configure the output stream object, this is the one we will keep
- m_OutputStream->Open(outputStream);
-
- 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));
-
- // Set the active format (the old one will be reverted when we close)
- m_OutputStream->SetPhysicalFormat(&outputFormat);
- 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, float initVolume)
-{
- // 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;
- m_initVolume = initVolume;
-
- 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);
- m_AudioDevice->SetHogStatus(false);
- m_AudioDevice->SetMixingSupport(true);
-
- // 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);
- }
-
- // No suitable output format was able to be configured
- if (!configured)
- return false;
- }
-
- 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)
- 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;
-}
-
-void CCoreAudioAEHALOSX::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
-{
- CoreAudioDeviceList deviceList;
- CCoreAudioHardware::GetOutputDevices(&deviceList);
-
- devices.push_back(AEDevice("Default", "CoreAudio:default"));
-
- 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);
- }
-}
-
-double CCoreAudioAEHALOSX::GetDelay()
-{
- return (double)(m_NumLatencyFrames) / (m_initformat.m_sampleRate);
-}
-
-void CCoreAudioAEHALOSX::SetVolume(float volume)
-{
- if (m_encoded || m_Passthrough)
- return;
-
- m_audioGraph->SetCurrentVolume(volume);
-}
-
-unsigned int CCoreAudioAEHALOSX::GetBufferIndex()
-{
- return m_OutputBufferIndex;
-}
-
-#endif
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#if defined(TARGET_DARWIN_OSX)
-
-#include "ICoreAudioAEHAL.h"
-#include "ICoreAudioSource.h"
-
-#include <CoreAudio/CoreAudio.h>
-
-// Forward declarations
-class CCoreAudioAE;
-class CCoreAudioGraph;
-class CCoreAudioDevice;
-class CCoreAudioStream;
-
-class CAUOutputDevice;
-
-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;
- float m_initVolume;
-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, bool encoded = false);
- 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, float initVolume);
- 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
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <samplerate.h>
-
-#include "CoreAudioAESound.h"
-
-#include "CoreAudioAE.h"
-#include "threads/SingleLock.h"
-#include "cores/AudioEngine/AEFactory.h"
-#include "cores/AudioEngine/Utils/AEAudioFormat.h"
-#include "cores/AudioEngine/Interfaces/AESound.h"
-#include "cores/AudioEngine/Utils/AEConvert.h"
-#include "cores/AudioEngine/Utils/AERemap.h"
-#include "cores/AudioEngine/Utils/AEUtil.h"
-#include "utils/log.h"
-#include "utils/EndianSwap.h"
-
-/* typecast AE to CCoreAudioAE */
-#define AE (*(CCoreAudioAE*)CAEFactory::GetEngine())
-
-CCoreAudioAESound::CCoreAudioAESound(const std::string &filename) :
- IAESound (filename),
- m_filename (filename),
- m_volume (1.0f ),
- m_inUse (0 )
-{
- m_wavLoader.Load(filename);
-}
-
-CCoreAudioAESound::~CCoreAudioAESound()
-{
- DeInitialize();
-}
-
-
-std::string CCoreAudioAESound::GetFileName()
-{
- return m_filename;
-}
-
-void CCoreAudioAESound::DeInitialize()
-{
-}
-
-bool CCoreAudioAESound::Initialize()
-{
- if (!m_wavLoader.IsValid())
- return false;
-
- return m_wavLoader.Initialize(
- AE.GetSampleRate (),
- AE.GetChannelLayout(),
- AE_CH_LAYOUT_INVALID
- );
-}
-
-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);
- if(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);
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "cores/AudioEngine/Interfaces/AESound.h"
-#include "cores/AudioEngine/Utils/AEWAVLoader.h"
-#include "threads/CriticalSection.h"
-
-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;
-};
-
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#include "CoreAudioAE.h"
-#include "CoreAudioAEStream.h"
-
-#include "xbmc/cores/AudioEngine/Interfaces/AE.h"
-#include "xbmc/cores/AudioEngine/AEFactory.h"
-#include "xbmc/cores/AudioEngine/Utils/AEUtil.h"
-#include "xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h"
-#include "settings/Settings.h"
-#include "threads/SingleLock.h"
-#include "settings/AdvancedSettings.h"
-#include "utils/MathUtils.h"
-#include "utils/log.h"
-
-
-
-// typecast AE to CCoreAudioAE
-#define AE (*(CCoreAudioAE*)CAEFactory::GetEngine())
-
-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);
-}
-
-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;
- AudioDataType *_input = input;
- AudioDataType *_output = output;
-
- for (unsigned int i = 0; i < frames; i++)
- {
- // get input channels
- for(unsigned int j = 0; j < channelsInput; j++)
- *_output++ = *_input++;
- // set unused channels
- for(unsigned int 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, bool transcode) :
- m_outputUnit (NULL ),
- m_valid (false),
- m_delete (false),
- m_volume (1.0f ),
- m_rgain (1.0f ),
- m_slave (NULL ),
- m_convertFn (NULL ),
- m_Buffer (NULL ),
- m_convertBuffer (NULL ),
- m_ssrc (NULL ),
- m_draining (false),
- m_AvgBytesPerSec (0 ),
- m_audioCallback (NULL ),
- m_fadeRunning (false),
- m_frameSize (0 ),
- m_doRemap (true ),
- m_firstInput (true ),
- m_flushRequested (false),
- m_encoder (NULL )
-{
- m_ssrcData.data_out = NULL;
- m_transcode = transcode;
-
- if (!transcode)
- {
- 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_isRaw = COREAUDIO_IS_RAW(dataFormat);
- }
- else
- {
- m_rawDataFormat = AE_FMT_AC3;
- m_StreamFormat.m_dataFormat = AE_FMT_AC3;
- m_StreamFormat.m_sampleRate = 48000;
- m_StreamFormat.m_encodedRate = 0;
- enum AEChannel ac3Layout[3] = {AE_CH_RAW, AE_CH_RAW, AE_CH_NULL};
- m_StreamFormat.m_channelLayout = ac3Layout;
- m_isRaw = true;
-
- // setup encoder format
- m_encoderFormat.m_dataFormat = dataFormat;
- m_encoderFormat.m_sampleRate = sampleRate;
- m_encoderFormat.m_encodedRate = 0;
- m_encoderFormat.m_channelLayout = channelLayout;
- m_encoderFormat.m_frames = 0;
- m_encoderFormat.m_frameSamples = 0;
- m_encoderFormat.m_frameSize = 0;
- }
-
- m_incomingFormat = dataFormat;
- m_chLayoutCountStream = m_StreamFormat.m_channelLayout.Count();
- m_StreamFormat.m_frameSize = (CAEUtil::DataFormatToBits(m_rawDataFormat) >> 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_limiter.SetSamplerate(AE.GetSampleRate());
-}
-
-CCoreAudioAEStream::~CCoreAudioAEStream()
-{
- CloseConverter();
-
- m_delete = true;
- m_valid = false;
-
- InternalFlush();
-
- _aligned_free(m_convertBuffer); m_convertBuffer = NULL;
- //_aligned_free(m_resampleBuffer); m_resampleBuffer = NULL;
- _aligned_free(m_remapBuffer); m_remapBuffer = NULL;
- _aligned_free(m_vizRemapBuffer); m_vizRemapBuffer = NULL;
- _aligned_free(m_upmixBuffer); m_upmixBuffer = NULL;
-
- delete m_Buffer; m_Buffer = NULL;
-
- delete m_encoder;
- m_encoder = NULL;
- ResetEncoder();
-
- m_unencodedBuffer.DeAlloc();
-
- /*
- 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 (!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();
- }
- }
-}
-
-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 always 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 safe 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 || m_transcode)
- {
- // we are raw or transcode, 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_vizRemap.Initialize(m_OutputFormat.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true))
- {
- m_valid = false;
- return;
- }
- }
-
- // only try to convert if we're not in AE_FMT_FLOAT and (we're not raw or we are transcoding)
- m_convert =
- (m_StreamFormat.m_dataFormat != AE_FMT_FLOAT && !m_isRaw)
- ||
- (m_incomingFormat != AE_FMT_FLOAT && m_transcode);
-
-
- //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));
-
- if (!m_transcode)
- m_convertFn = CAEConvert::ToFloat(m_StreamFormat.m_dataFormat);
- else
- m_convertFn = CAEConvert::ToFloat(m_incomingFormat);
-
- if (!m_convertFn)
- m_valid = false;
- }
-
- // if we need to transcode, set it up
- if (m_transcode)
- {
- m_unencodedBuffer.Empty();
-
- if (!m_encoder || !m_encoder->IsCompatible(m_encoderFormat))
- SetupEncoder();
- }
-
- // 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 AERingBuffer(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;
- unsigned int channelsInBuffer = m_chLayoutCountStream;
-
- if (m_flushRequested && m_paused)
- InternalFlush();
-
- if (!m_valid || size == 0 || data == NULL || !m_Buffer || m_flushRequested)
- return 0;
-
- // if the stream is draining
- if (m_draining)
- {
- // if the stream has finished draining, cork it
- if (m_Buffer && m_Buffer->GetReadSize() == 0)
- m_draining = false;
- else
- return 0;
- }
-
- // convert the data if we need to
- if (m_convert)
- {
- CheckOutputBufferSize((void **)&m_convertBuffer, &m_convertBufferSize, frames * channelsInBuffer * m_OutputBytesPerSample);
-
- samples = m_convertFn(adddata, size / m_StreamBytesPerSample, m_convertBuffer);
- frames = samples / channelsInBuffer;
- addsize = frames * channelsInBuffer * m_OutputBytesPerSample;
- adddata = (uint8_t *)m_convertBuffer;
- }
- else
- {
- samples = size / m_StreamBytesPerSample;
- adddata = (uint8_t *)data;
- addsize = size;
- }
-
- if (samples == 0)
- return 0;
-
- // transcode if we need to
- if (m_transcode && m_encoder)
- {
- // put adddata in the unencodedBuffer
- if (m_unencodedBuffer.Free() < addsize)
- m_unencodedBuffer.ReAlloc(m_unencodedBuffer.Used() + addsize);
-
- m_unencodedBuffer.Push(adddata, addsize);
-
- unsigned int block = m_encoderFormat.m_frames * m_encoderFormat.m_frameSize;
-
- // only try to encode if we have enough data
- if (m_unencodedBuffer.Used() >= block)
- {
- frames = m_encoder->Encode((float *)m_unencodedBuffer.Raw(block), m_encoderFormat.m_frames);
- m_unencodedBuffer.Shift(NULL, frames * m_encoderFormat.m_frameSize);
- addsize = m_encoder->GetData(&adddata);
- samples = addsize / m_OutputBytesPerSample;
- }
- else
- // return size here or whoever calling us will block if we return 0
- // we have essentially been successful, because we add the audio to our buffer to encode
- return 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;
- channelsInBuffer = m_OutputFormat.m_channelLayout.Count();
- }
-
- // upmix the ouput to output channels
- if ( (!m_isRaw || m_rawDataFormat == AE_FMT_LPCM) && (m_chLayoutCountOutput > channelsInBuffer) )
- {
- CheckOutputBufferSize((void **)&m_upmixBuffer, &m_upmixBufferSize, frames * m_chLayoutCountOutput * sizeof(float));
- Upmix(adddata, channelsInBuffer, m_upmixBuffer, m_chLayoutCountOutput, frames, m_OutputFormat.m_dataFormat);
- adddata = m_upmixBuffer;
- addsize = frames * m_chLayoutCountOutput * sizeof(float);
- }
-
- unsigned int total_ms_sleep = 0;
- unsigned int room = m_Buffer->GetWriteSize();
- while (addsize > room && !m_paused && total_ms_sleep < 100)
- {
- // we got deleted
- if (!m_valid || !m_Buffer || m_draining )
- return 0;
-
- unsigned int ms_sleep_time = (1000 * room) / m_AvgBytesPerSec;
- if (ms_sleep_time == 0)
- ms_sleep_time++;
-
- // sleep until we have space (estimated) or 1ms min
- Sleep(ms_sleep_time);
- total_ms_sleep += ms_sleep_time;
-
- room = m_Buffer->GetWriteSize();
- }
-
- if (addsize > room)
- size = 0;
- else
- m_Buffer->Write(adddata, addsize);
-
- // still only return size to indicate success
- // we likely wrote something !size to m_Buffer, but our called doesn't realy care
- return size;
-}
-
-// this is only called on the context of the coreaudio thread!
-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;
-
- if (m_flushRequested)
- {
- InternalFlush();
- 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;
- unsigned int frames = samples / m_chLayoutCountOutput;
-
- // 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 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;
- }
- }
-
- if (m_volume < 1.0f)
- {
-#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);
- }
- // apply volume amplification by using the sogt limiter
- // TODO - maybe reinvent the coreaudio compressor for this after frodo
- else if (GetAmplification() != 1.0f)
- {
- for(unsigned int i = 0; i < frames; i++)
- {
- int frameIdx = i*m_chLayoutCountOutput;
- float amplification = RunLimiter(&floatBuffer[frameIdx], m_chLayoutCountOutput);
- float *frameStart = &floatBuffer[frameIdx];
-#ifdef __SSE___
- CAEUtil::SSEMulArray(frameStart, amplification, m_chLayoutCountOutput);
-#else
- for(unsigned int n = 0; n < m_chLayoutCountOutput; n++)
- frameStart[n] *= amplification;
-#endif
-
- }
- }
- }
-
- 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() / m_OutputBytesPerSample * m_StreamBytesPerSample;
-}
-
-double CCoreAudioAEStream::GetDelay()
-{
- if (m_delete || !m_Buffer || m_flushRequested)
- return 0.0f;
-
- double delayBuffer = (double)(m_Buffer->GetReadSize()) / (double)m_AvgBytesPerSec;
- double delayTranscoder = 0.0;
-
- if (m_transcode)
- delayTranscoder = (double)(m_unencodedBuffer.Used()) / (double)(m_encoderFormat.m_frameSize * m_encoderFormat.m_sampleRate);
-
- return AE.GetDelay() + delayBuffer + delayTranscoder;
-}
-
-bool CCoreAudioAEStream::IsBuffering()
-{
- return (m_Buffer->GetReadSize() == 0 && m_unencodedBuffer.Used() == 0);
-}
-
-double CCoreAudioAEStream::GetCacheTime()
-{
- if (m_delete || !m_Buffer || m_flushRequested)
- return 0.0f;
- double delayBuffer = (double)(m_Buffer->GetReadSize()) / (double)m_AvgBytesPerSec;
- double delayTranscoder = 0.0;
-
- if (m_transcode)
- delayTranscoder = (double)(m_unencodedBuffer.Used()) / (double)(m_encoderFormat.m_frameSize * m_encoderFormat.m_sampleRate);
-
- return AE.GetDelay() + delayBuffer + delayTranscoder;
-}
-
-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()
-{
-#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
- if (CAEFactory::IsSuspended())
- CAEFactory::Resume();
-#endif
- m_paused = false;
-}
-
-void CCoreAudioAEStream::Drain(bool wait)
-{
- m_draining = true;
-}
-
-bool CCoreAudioAEStream::IsDrained()
-{
- return m_Buffer->GetReadSize() == 0;
-}
-
-void CCoreAudioAEStream::Flush()
-{
- if (m_Buffer)
- m_flushRequested = true;
-}
-
-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;
- }
- }
-
- m_flushRequested = false;
- //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::Render(AudioUnitRenderActionFlags* actionFlags,
- const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufList)
-{
- OSStatus ret = OnRender(actionFlags, pTimeStamp, busNumber, frameCount, pBufList);
- return ret;
-}
-
-OSStatus CCoreAudioAEStream::OnRender(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- // 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;
-
- // the index is important if we run encoded
- unsigned int outputBufferIndex = AE.GetHAL()->GetBufferIndex();
-
- ioData->mBuffers[outputBufferIndex].mDataByteSize = GetFrames(
- (uint8_t*)ioData->mBuffers[outputBufferIndex].mData, size);
- if (!ioData->mBuffers[outputBufferIndex].mDataByteSize && ioActionFlags)
- *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
-
- return noErr;
-}
-
-void CCoreAudioAEStream::ResetEncoder()
-{
- if (m_encoder)
- m_encoder->Reset();
- m_unencodedBuffer.Empty();
-}
-
-bool CCoreAudioAEStream::SetupEncoder()
-{
- ResetEncoder();
- delete m_encoder;
- m_encoder = NULL;
-
- if (!m_transcode)
- return false;
-
- m_encoder = new CAEEncoderFFmpeg();
- if (m_encoder && m_encoder->Initialize(m_encoderFormat))
- return true;
-
- delete m_encoder;
- m_encoder = NULL;
- return false;
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <samplerate.h>
-#include <list>
-
-#include "ICoreAudioSource.h"
-#include "cores/AudioEngine/Utils/AEAudioFormat.h"
-#include "cores/AudioEngine/Interfaces/AEStream.h"
-#include "cores/AudioEngine/Utils/AEConvert.h"
-#include "cores/AudioEngine/Utils/AERemap.h"
-#include "cores/AudioEngine/Utils/AELimiter.h"
-#include "cores/AudioEngine/Utils/AERingBuffer.h"
-#include "cores/AudioEngine/Utils/AEBuffer.h"
-
-#if defined(TARGET_DARWIN_IOS)
-# include "CoreAudioAEHALIOS.h"
-#else
-# include "CoreAudioAEHALOSX.h"
-#endif
-
-class AERingBuffer;
-class CoreAudioRingBuffer;
-class IAEEncoder;
-
-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, bool transcode);
- 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(bool wait);
- virtual void Flush();
-
- virtual float GetVolume();
- virtual float GetReplayGain();
- virtual float GetAmplification() { return m_limiter.GetAmplification(); }
- virtual void SetVolume(float volume);
- virtual void SetReplayGain(float factor);
- virtual void SetAmplification(float amplify){ m_limiter.SetAmplification(amplify); }
-
- virtual float RunLimiter(float* frame, int channels) { return m_limiter.Run(&frame, channels); }
-
- 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);
-
-private:
- void InternalFlush();
- void ResetEncoder();
- bool SetupEncoder();
-
- OSStatus OnRender(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList *ioData);
-
- AEDataFormat m_rawDataFormat; /* the format we're output if we're outputting in raw mode */
- AEDataFormat m_incomingFormat; /* the format of the stream being sent to us */
-
- 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_transcode; /* true if we need to transcode to ac3 */
- 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 */
- CAELimiter m_limiter; /* volume amplification/limiter*/
- IAEStream *m_slave; /* slave aestream */
-
- CAEConvert::AEConvertToFn m_convertFn;
-
- AERingBuffer *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;
- bool m_flushRequested;
-
- /* the encoder */
- AEAudioFormat m_encoderFormat;
- IAEEncoder *m_encoder;
- CAEBuffer m_unencodedBuffer; /* this spools up the data before we encode it */
-};
-
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioChannelLayout.h"
-
-#include <AudioToolbox/AudioToolbox.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
-};
-
-CCoreAudioChannelLayout::CCoreAudioChannelLayout() :
- m_pLayout(NULL)
-{
-}
-
-CCoreAudioChannelLayout::CCoreAudioChannelLayout(AudioChannelLayout& layout) :
-m_pLayout(NULL)
-{
- CopyLayout(layout);
-}
-
-CCoreAudioChannelLayout::~CCoreAudioChannelLayout()
-{
- free(m_pLayout);
-}
-
-bool CCoreAudioChannelLayout::CopyLayout(AudioChannelLayout& layout)
-{
- enum {
- kVariableLengthArray_deprecated = 1
- };
-
- 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_deprecated) * 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;
-}
-
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-#include <list>
-#include <vector>
-#include <string>
-
-#include <CoreAudio/CoreAudio.h>
-
-typedef std::vector<SInt32> CoreAudioChannelList;
-typedef std::list<AudioChannelLayoutTag> AudioChannelLayoutList;
-
-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
-};
-
-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
-};
-
-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;
-};
-
-#endif
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioDevice.h"
-#include "CoreAudioAEHAL.h"
-#include "CoreAudioChannelLayout.h"
-#include "CoreAudioHardware.h"
-#include "utils/log.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// CCoreAudioDevice
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-CCoreAudioDevice::CCoreAudioDevice() :
- m_Started (false ),
- m_pSource (NULL ),
- m_DeviceId (0 ),
- m_MixerRestore (-1 ),
- m_IoProc (NULL ),
- m_ObjectListenerProc (NULL ),
- m_SampleRateRestore (0.0f ),
- m_HogPid (-1 ),
- m_frameSize (0 ),
- m_OutputBufferIndex (0 ),
- m_BufferSizeRestore (0 )
-{
-}
-
-CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
- m_Started (false ),
- m_pSource (NULL ),
- m_DeviceId (deviceId ),
- m_MixerRestore (-1 ),
- m_IoProc (NULL ),
- m_ObjectListenerProc (NULL ),
- m_SampleRateRestore (0.0f ),
- m_HogPid (-1 ),
- m_frameSize (0 ),
- m_OutputBufferIndex (0 ),
- m_BufferSizeRestore (0 )
-{
-}
-
-CCoreAudioDevice::~CCoreAudioDevice()
-{
- Close();
-}
-
-bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
-{
- m_DeviceId = deviceId;
- m_BufferSizeRestore = GetBufferSize();
- return true;
-}
-
-void CCoreAudioDevice::Close()
-{
- if (!m_DeviceId)
- return;
-
- // Stop the device if it was started
- Stop();
-
- // Unregister the IOProc if we have one
- if (m_IoProc)
- SetInputSource(NULL, 0, 0);
-
- SetHogStatus(false);
- CCoreAudioHardware::SetAutoHogMode(false);
-
- if (m_MixerRestore > -1) // We changed the mixer status
- SetMixingSupport((m_MixerRestore ? true : false));
- m_MixerRestore = -1;
-
- if (m_SampleRateRestore != 0.0f)
- SetNominalSampleRate(m_SampleRateRestore);
-
- if (m_BufferSizeRestore && m_BufferSizeRestore != GetBufferSize())
- {
- SetBufferSize(m_BufferSizeRestore);
- m_BufferSizeRestore = 0;
- }
-
- m_IoProc = NULL;
- m_pSource = NULL;
- m_DeviceId = 0;
- m_ObjectListenerProc = 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)
-{
- // Allow only one ObjectListener at a time
- if (!m_DeviceId || m_ObjectListenerProc)
- 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()
-{
- // Allow only one IOProc at a time
- if (!m_DeviceId || m_IoProc)
- 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();
-
- 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());
-
- m_IoProc = NULL; // Clear the reference no matter what
- m_pSource = NULL;
-
- Sleep(100);
-
- return true;
-}
-
-std::string CCoreAudioDevice::GetName()
-{
- if (!m_DeviceId)
- return NULL;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
-
- UInt32 propertySize;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
- if (ret != noErr)
- return NULL;
-
- std::string name = "";
- char *buff = new char[propertySize + 1];
- buff[propertySize] = 0x00;
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, buff);
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
- "Unable to get device name - id: 0x%04x. Error = %s", (uint)m_DeviceId, GetError(ret).c_str());
- }
- else
- {
- name = buff;
- }
- delete[] buff;
-
-
- return name;
-}
-
-UInt32 CCoreAudioDevice::GetTotalOutputChannels()
-{
- UInt32 channels = 0;
-
- if (!m_DeviceId)
- return channels;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
-
- UInt32 size = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &size);
- if (ret != noErr)
- return channels;
-
- AudioBufferList* pList = (AudioBufferList*)malloc(size);
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, pList);
- if (ret == noErr)
- {
- 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",
- (uint)m_DeviceId, GetError(ret).c_str());
- }
-
- free(pList);
-
- return channels;
-}
-
-bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
-{
- if (!pList || !m_DeviceId)
- return false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyStreams;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
- if (ret != noErr)
- return false;
-
- UInt32 streamCount = propertySize / sizeof(AudioStreamID);
- AudioStreamID* pStreamList = new AudioStreamID[streamCount];
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pStreamList);
- if (ret == noErr)
- {
- for (UInt32 stream = 0; stream < streamCount; stream++)
- pList->push_back(pStreamList[stream]);
- }
- delete[] pStreamList;
-
- return ret == noErr;
-}
-
-
-bool CCoreAudioDevice::IsRunning()
-{
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyDeviceIsRunning;
-
- UInt32 isRunning = 0;
- UInt32 propertySize = sizeof(isRunning);
- OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &isRunning);
- if (ret != noErr)
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyHogMode;
-
- if (hog)
- {
- // Not already set
- if (m_HogPid == -1)
- {
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(m_HogPid), &m_HogPid);
-
- // even if setting hogmode was successfull our PID might not get written
- // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
- // had success on getting hog status
- // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
- // it seems that in the other cases the GetHogStatus could return -1
- // which would overwrite our valid m_HogPid again
- // Man we should never touch this shit again ;)
- if (m_HogPid == -1)
- m_HogPid = GetHogStatus();
-
- if (ret || m_HogPid != getpid())
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
- "Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
- return false;
- }
- }
- }
- else
- {
- // Currently Set
- if (m_HogPid > -1)
- {
- pid_t hogPid = -1;
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, 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;
- }
- // Reset internal state
- m_HogPid = hogPid;
- }
- }
- return true;
-}
-
-pid_t CCoreAudioDevice::GetHogStatus()
-{
- if (!m_DeviceId)
- return false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyHogMode;
-
- pid_t hogPid = -1;
- UInt32 size = sizeof(hogPid);
- AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &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);
- }
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
-
- UInt32 mixEnable = mix ? 1 : 0;
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(mixEnable), &mixEnable);
- if (ret != noErr)
- {
- 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;
-
- UInt32 size;
- UInt32 mix = 0;
- Boolean writable = false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
-
- if( AudioObjectHasProperty( m_DeviceId, &propertyAddress ) )
- {
- OSStatus ret = AudioObjectIsPropertySettable(m_DeviceId, &propertyAddress, &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 = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &mix);
- if (ret != noErr)
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kHALOutputParam_Volume;
-
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float32), &vol);
- if (ret != noErr)
- {
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
- if (ret)
- return false;
-
- void* pBuf = malloc(propertySize);
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
- if (ret != noErr)
- CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
- "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
- else
- {
- // Copy the result into the caller's instance
- layout.CopyLayout(*((AudioChannelLayout*)pBuf));
- }
- free(pBuf);
- return (ret == noErr);
-}
-
-bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
-{
- if (!pList || !m_DeviceId)
- return false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyDataSources;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
- if (ret != noErr)
- return false;
-
- UInt32 sources = propertySize / sizeof(UInt32);
- UInt32* pSources = new UInt32[sources];
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pSources);
- if (ret == noErr)
- {
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
-
- Float64 sampleRate = 0.0f;
- UInt32 propertySize = sizeof(Float64);
- OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &sampleRate);
- if (ret != noErr)
- {
- 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
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
-
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float64), &sampleRate);
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
- "Unable to set current device sample rate to %0.0f. Error = %s",
- (float)sampleRate, GetError(ret).c_str());
- return false;
- }
- if (m_SampleRateRestore == 0.0f)
- m_SampleRateRestore = currentRate;
-
- return true;
-}
-
-UInt32 CCoreAudioDevice::GetNumLatencyFrames()
-{
- UInt32 num_latency_frames = 0;
- if (!m_DeviceId)
- return 0;
-
- // number of frames of latency in the AudioDevice
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyLatency;
-
- UInt32 i_param = 0;
- UInt32 i_param_size = sizeof(uint32_t);
- OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
- if (ret == noErr)
- num_latency_frames += i_param;
-
- // number of frames in the IO buffers
- propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
- if (ret == noErr)
- num_latency_frames += i_param;
-
- // number for frames in ahead the current hardware position that is safe to do IO
- propertyAddress.mSelector = kAudioDevicePropertySafetyOffset;
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
- if (ret == noErr)
- num_latency_frames += i_param;
-
- return (num_latency_frames);
-}
-
-UInt32 CCoreAudioDevice::GetBufferSize()
-{
- if (!m_DeviceId)
- return false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
-
- UInt32 size = 0;
- UInt32 propertySize = sizeof(size);
- OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &size);
- if (ret != noErr)
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
-
- UInt32 propertySize = sizeof(size);
- OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, propertySize, &size);
- if (ret != noErr)
- {
- 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.");
-
- 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;
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-
-#include <string>
-
-#include "ICoreAudioSource.h"
-#include "CoreAudioStream.h"
-
-#include <CoreAudio/CoreAudio.h>
-
-typedef std::list<UInt32> CoreAudioDataSourceList;
-typedef std::list<AudioDeviceID> CoreAudioDeviceList;
-
-class CCoreAudioChannelLayout;
-
-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();
-
- static OSStatus DirectRenderCallback(AudioDeviceID inDevice,
- const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime,
- AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData);
-
- bool m_Started;
- ICoreAudioSource *m_pSource;
- AudioDeviceID m_DeviceId;
- int m_MixerRestore;
- AudioDeviceIOProc m_IoProc;
- AudioObjectPropertyListenerProc m_ObjectListenerProc;
-
- Float64 m_SampleRateRestore;
- pid_t m_HogPid;
- unsigned int m_frameSize;
- unsigned int m_OutputBufferIndex;
- unsigned int m_BufferSizeRestore;
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioGraph.h"
-
-#include "CoreAudioAEHAL.h"
-#include "CoreAudioMixMap.h"
-#include "CoreAudioUnit.h"
-
-#include "utils/log.h"
-#include "utils/SystemInfo.h"
-
-CCoreAudioGraph::CCoreAudioGraph() :
- m_audioGraph (NULL ),
- m_inputUnit (NULL ),
- m_audioUnit (NULL ),
- m_mixerUnit (NULL ),
- m_initialized (false),
- m_deviceId (NULL ),
- m_allowMixing (false),
- m_mixMap (NULL )
-{
- for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
- {
- m_reservedBusNumber[i] = false;
- }
-}
-
-CCoreAudioGraph::~CCoreAudioGraph()
-{
- Close();
-
- delete m_mixMap;
-}
-
-bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format,
- AudioDeviceID deviceId, bool allowMixing, AudioChannelLayoutTag layoutTag, float initVolume, bool encoded)
-{
- AudioStreamBasicDescription fmt = {0};
- AudioStreamBasicDescription inputFormat = {0};
- AudioStreamBasicDescription outputFormat = {0};
-
- m_deviceId = deviceId;
- m_allowMixing = allowMixing;
-
- OSStatus 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->SetBus(GetFreeBus());
-
- m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt, encoded);
-
- if (!m_audioUnit->EnableInputOuput())
- return false;
-
- if (!m_audioUnit->SetCurrentDevice(deviceId))
- return false;
-
- SetCurrentVolume(initVolume);
-
- 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;
-
- // Update format structure to reflect the desired format from the mixer
- // The output format of the mixer is identical to the input format, except for the channel count
- AudioStreamBasicDescription mixOutput(fmt);
- mixOutput.mChannelsPerFrame = m_mixMap->GetOutputChannels();
-
- if (!m_mixerUnit->SetFormat(&mixOutput, 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;
- }
-
- UInt32 inputNumber = m_inputUnit->GetBus();
- int channelOffset = GetMixerChannelOffset(inputNumber);
- if (!CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &fmt, &mixOutput, channelOffset))
- return false;
-
- // Regenerate audio format and copy format for the Output AU
- outputFormat = mixOutput;
- }
- 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",
- (int)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_inputUnit)
- {
- if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
- {
- CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
- "Error setting Device Output Stream Format %s",
- StreamDescriptionToString(inputFormat, formatString));
- }
- }
-
- 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(LOGDEBUG, "CCoreAudioGraph::Open: audioUnit, Input Stream Format %s",
- StreamDescriptionToString(inputDesc_end, formatString));
- CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: audioUnit, 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(LOGDEBUG, "CCoreAudioGraph::Open: mixerUnit, Input Stream Format %s",
- StreamDescriptionToString(inputDesc_end, formatString));
- CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: mixerUnit, 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(LOGDEBUG, "CCoreAudioGraph::Open: inputUnit, Input Stream Format %s",
- StreamDescriptionToString(inputDesc_end, formatString));
- CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: inputUnit, 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);
-
- return Start();
-}
-
-bool CCoreAudioGraph::Close()
-{
- if (!m_audioGraph)
- return false;
-
- Stop();
-
- SetInputSource(NULL);
-
- while (!m_auUnitList.empty())
- {
- CAUOutputDevice *d = m_auUnitList.front();
- m_auUnitList.pop_front();
- ReleaseBus(d->GetBus());
- d->SetInputSource(NULL);
- 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;
- }
-
- OSStatus 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;
-
- Boolean isRunning = false;
- OSStatus 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());
- }
- }
-
- return true;
-}
-
-bool CCoreAudioGraph::Stop()
-{
- if (!m_audioGraph)
- return false;
-
- Boolean isRunning = false;
- OSStatus 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;
-
- AUGraphUpdate(m_audioGraph, NULL);
-
- Start();
-
- return NULL;
-}
-
-CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
-{
- if (!m_audioUnit || !m_mixerUnit)
- return NULL;
-
- AudioStreamBasicDescription fmt = {0};
- AudioStreamBasicDescription inputFormat = {0};
- AudioStreamBasicDescription outputFormat = {0};
-
- int busNumber = GetFreeBus();
- if (busNumber == INVALID_BUS)
- return NULL;
-
- OSStatus ret;
- // 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);
- 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 = {0};
-
- for (int i = 0; i < busNumber; i++)
- {
- memset(&fmt, 0x0, sizeof(fmt));
- m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
- offset += fmt.mChannelsPerFrame;
- }
- return offset;
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-#include <list>
-
-#include "ICoreAudioSource.h"
-#include <AudioToolbox/AUGraph.h>
-#include <CoreAudio/CoreAudio.h>
-
-#define MAX_CONNECTION_LIMIT 8
-#define MAXIMUM_MIXER_CHANNELS 9
-
-class CAUMatrixMixer;
-class CAUOutputDevice;
-class CCoreAudioMixMap;
-
-class CCoreAudioGraph
-{
-public:
- CCoreAudioGraph();
- ~CCoreAudioGraph();
-
- bool Open(ICoreAudioSource *pSource, AEAudioFormat &format, AudioDeviceID deviceId,
- bool allowMixing, AudioChannelLayoutTag layoutTag, float initVolume, bool encoded);
- bool Close();
- bool Start();
- bool Stop();
- static 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);
-
-private:
- AUGraph m_audioGraph;
-
- CAUOutputDevice *m_inputUnit;
- CAUOutputDevice *m_audioUnit;
- CAUMatrixMixer *m_mixerUnit;
-
- 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;
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioHardware.h"
-
-#include "CoreAudioAEHAL.h"
-#include "utils/log.h"
-#include "osx/DarwinUtils.h"
-
-bool CCoreAudioHardware::GetAutoHogMode()
-{
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
-
- UInt32 val = 0;
- UInt32 size = sizeof(val);
- OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &val);
- if (ret != noErr)
- {
- 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)
-{
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
-
- UInt32 val = enable ? 1 : 0;
- UInt32 size = sizeof(val);
- OSStatus ret = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &val);
- if (ret != noErr)
- CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: "
- "Unable to set auto 'hog' mode. Error = %s", GetError(ret).c_str());
-}
-
-void CCoreAudioHardware::ResetAudioDevices()
-{
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices resetting our devices to LPCM");
- CoreAudioDeviceList list;
- if (GetOutputDevices(&list))
- {
- for (CoreAudioDeviceList::iterator it = list.begin(); it != list.end(); it ++)
- {
- CCoreAudioDevice device = *it;
-
- AudioStreamIdList streams;
- if (device.GetStreams(&streams))
- {
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices %lu streams for device %s", streams.size(), device.GetName().c_str());
- for (AudioStreamIdList::iterator ait = streams.begin(); ait != streams.end(); ait ++)
- ResetStream(*ait);
- }
- }
- }
-}
-
-void CCoreAudioHardware::ResetStream(AudioStreamID streamId)
-{
- CCoreAudioStream stream;
- stream.Open(streamId);
-
- AudioStreamBasicDescription desc;
- if (stream.GetPhysicalFormat(&desc))
- {
- if (desc.mFormatID == 'IAC3' || desc.mFormatID == kAudioFormat60958AC3)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream stream 0x%x is in encoded format.. setting to LPCM", (unsigned int)streamId);
-
- StreamFormatList availableFormats;
- if (stream.GetAvailablePhysicalFormats(&availableFormats))
- {
- for (StreamFormatList::iterator fmtIt = availableFormats.begin(); fmtIt != availableFormats.end() ; fmtIt ++)
- {
- AudioStreamRangedDescription fmtDesc = *fmtIt;
- if (fmtDesc.mFormat.mFormatID == kAudioFormatLinearPCM)
- {
- AudioStreamBasicDescription newFmt = { 0 };
- newFmt = fmtDesc.mFormat;
-
- if (stream.SetPhysicalFormat(&newFmt))
- break;
- }
- }
- }
- }
- }
-
- stream.Close(false);
-}
-
-AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
-{
- AudioDeviceID deviceId = 0;
-
- if (!searchName.length())
- return deviceId;
-
- 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].", (uint)defaultDevice);
- return defaultDevice;
- }
- CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
- "Searching for device - %s.", searchName.c_str());
-
- // Obtain a list of all available audio devices
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioHardwarePropertyDevices;
-
- UInt32 size = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
- "Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
- return 0;
- }
-
- size_t deviceCount = size / sizeof(AudioDeviceID);
- AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
- ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
- if (ret != noErr)
- {
- 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 (size_t dev = 0; dev < deviceCount; dev++)
- {
- CCoreAudioDevice device;
- device.Open((pDevices[dev]));
- deviceName = device.GetName();
- 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()
-{
- AudioDeviceID deviceId = 0;
- static AudioDeviceID lastDeviceId = 0;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
-
- UInt32 size = sizeof(AudioDeviceID);
- OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
-
- // outputDevice is set to 0 if there is no audio device available
- // or if the default device is set to an encoded format
- if (ret != noErr || !deviceId)
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice:"
- " Unable to identify default output device. Error = %s", GetError(ret).c_str());
- // if there was no error and no deviceId was returned
- // return the last known default device
- if (ret == noErr && !deviceId)
- return lastDeviceId;
- else
- return 0;
- }
-
- lastDeviceId = deviceId;
-
- return deviceId;
-}
-
-void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
-{
- name = "Default";
- AudioDeviceID deviceId = GetDefaultOutputDevice();
-
- if (deviceId)
- {
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioObjectPropertyName;
-
- CFStringRef theDeviceName = NULL;
- UInt32 propertySize = sizeof(CFStringRef);
- OSStatus ret = AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &propertySize, &theDeviceName);
- if (ret != noErr)
- return;
-
- DarwinCFStringRefToUTF8String(theDeviceName, name);
-
- CFRelease(theDeviceName);
- }
-}
-
-UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList *pList)
-{
- UInt32 found = 0;
- if (!pList)
- return found;
-
- // Obtain a list of all available audio devices
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioHardwarePropertyDevices;
-
- UInt32 size = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
- " Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
- return found;
- }
-
- size_t deviceCount = size / sizeof(AudioDeviceID);
- AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
- ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
- if (ret != noErr)
- CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
- " Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
- else
- {
- for (size_t dev = 0; dev < deviceCount; dev++)
- {
- CCoreAudioDevice device(pDevices[dev]);
- if (device.GetTotalOutputChannels() == 0)
- continue;
- found++;
- pList->push_back(pDevices[dev]);
- }
- }
- delete[] pDevices;
-
- return found;
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-
-#include "CoreAudioDevice.h"
-
-// 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* FormatsList(AudioStreamID stream);
- static AudioStreamID* StreamsList(AudioDeviceID device);
- static void ResetAudioDevices();
- static void ResetStream(AudioStreamID streamId);
- static AudioDeviceID FindAudioDevice(const std::string &deviceName);
- static AudioDeviceID GetDefaultOutputDevice();
- static void GetOutputDeviceName(std::string &name);
- static UInt32 GetOutputDevices(CoreAudioDeviceList *pList);
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioMixMap.h"
-
-#include "CoreAudioUnit.h"
-#include "CoreAudioAEHAL.h"
-#include "utils/log.h"
-
-
-#include <AudioToolbox/AudioToolbox.h>
-#include <sstream>
-
-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;
- AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap,
- sizeof(layouts), layouts, &propSize);
- m_pMap = (Float32*)calloc(1,propSize);
-
- // Try and get a predefined mixmap
- OSStatus ret = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap,
- sizeof(layouts), layouts, &propSize, m_pMap);
- if (ret)
- {
- // If we for some reason don't find a predefined matrix let's build a diagonal matrix,
- // basically guessing here, but we need to have a mixmap that matches the output and input
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::CreateMap: No pre-defined mapping from %d to %d channels, building diagonal matrix.", m_inChannels, m_outChannels);
- for (UInt32 chan = 0; chan < std::min(m_inChannels, m_outChannels); ++chan)
- {
- Float32 *vol = m_pMap + (chan * m_outChannels + chan);
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::Rebuild %d = %f", chan, *vol);
- *vol = 1.;
- }
- }
- m_isValid = true;
-}
-
-CCoreAudioMixMap *CCoreAudioMixMap::CreateMixMap(CAUOutputDevice *audioUnit, AEAudioFormat &format, AudioChannelLayoutTag layoutTag)
-{
- if (!audioUnit)
- return NULL;
-
- AudioStreamBasicDescription fmt;
- AudioStreamBasicDescription inputFormat;
-
- // 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];
- // Convert from XBMC channel tag to CoreAudio channel tag
- pDesc->mChannelLabel = g_LabelMap[(unsigned int)channelLayout[chan]];
- 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_RightSurround)
- break; // Required condition cannot be true
-
- if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurroundDirect)
- {
- // Change [Back Left] to [Side Left]
- pDesc->mChannelLabel = kAudioChannelLabel_LeftSurround;
- CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: "
- "Detected faulty input channel map...fixing(Back Left-->Side Left)");
- }
- if (pDesc->mChannelLabel == kAudioChannelLabel_RightSurroundDirect)
- {
- // Change [Back Left] to [Side Left]
- pDesc->mChannelLabel = kAudioChannelLabel_RightSurround;
- 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(LOGDEBUG, "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(LOGDEBUG, "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(LOGDEBUG, "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
- CCoreAudioMixMap *mixMap = new CCoreAudioMixMap();
- mixMap->Rebuild(*sourceLayout, *(AudioChannelLayout*)deviceLayout);
- return mixMap;
-}
-
-bool CCoreAudioMixMap::SetMixingMatrix(CAUMatrixMixer *mixerUnit,
- CCoreAudioMixMap *mixMap, AudioStreamBasicDescription *inputFormat,
- AudioStreamBasicDescription *fmt, int channelOffset)
-{
- if (!mixerUnit || !inputFormat || !fmt)
- return false;
-
- // Fetch the mixing unit size
- UInt32 dims[2];
- UInt32 size = sizeof(dims);
- AudioUnitGetProperty(mixerUnit->GetUnit(),
- kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
-
- if(inputFormat->mChannelsPerFrame + channelOffset > dims[0])
- {
- CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - input format doesn't fit mixer size %u+%u > %u"
- , inputFormat->mChannelsPerFrame, channelOffset, dims[0]);
- return false;
- }
-
- if(fmt->mChannelsPerFrame > dims[1])
- {
- CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't fit mixer size %u > %u"
- , fmt->mChannelsPerFrame, dims[1]);
- return false;
- }
-
- if(fmt->mChannelsPerFrame < dims[1])
- {
- CLog::Log(LOGWARNING, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't specify all outputs %u < %u"
- , fmt->mChannelsPerFrame, dims[1]);
- }
-
- // Configure the mixing matrix
- // The return from kAudioFormatProperty_MatrixMixMap (See Rebuild above)
- // is a Float32* which is laid out like this:
- //
- // mapping 2 chan -> 2 chan
- // 1 0 0 1
- //
- // or better represented in a tow dimensional array:
- //
- // 1 0
- // 0 1
- //
- // mapping 6 chan -> 6 chan:
- // 1 0 0 0 0 0
- // 0 1 0 0 0 0
- // 0 0 1 0 0 0
- // ....
-
- Float32* val = (Float32*)*mixMap;
- for (UInt32 i = 0; i < inputFormat->mChannelsPerFrame; ++i)
- {
- UInt32 j = 0;
- std::stringstream layoutStr;
- for (; j < fmt->mChannelsPerFrame; ++j)
- {
- Float32 *vol = val + (i * mixMap->m_outChannels + j);
- layoutStr << *vol << ", ";
- AudioUnitSetParameter(mixerUnit->GetUnit(),
- kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, *vol, 0);
- }
- // zero out additional outputs from this input
- for (; j < dims[1]; ++j)
- {
- AudioUnitSetParameter(mixerUnit->GetUnit(),
- kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, 0.0f, 0);
- layoutStr << "0, ";
- }
-
- CLog::Log(LOGDEBUG, "CCoreAudioMixMap::SetMixingMatrix channel %d = [%s]", i, layoutStr.str().c_str());
- }
-
- CLog::Log(LOGDEBUG, "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;
-}
-
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-
-#include "cores/AudioEngine/Utils/AEAudioFormat.h"
-
-#include <CoreAudio/CoreAudio.h>
-
-class CAUMatrixMixer;
-class CAUOutputDevice;
-
-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;
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioStream.h"
-
-#include "CoreAudioAEHAL.h"
-#include "utils/log.h"
-#include "utils/StdString.h"
-
-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.", (uint)m_StreamId);
-
- // watch for physical property changes.
- AudioObjectPropertyAddress propertyAOPA;
- propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
- propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
- if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
- CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a physical property listener.");
-
- // watch for virtual property changes.
- propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
- propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
- if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
- CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a virtual property listener.");
-
- 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(bool restore)
-{
- if (!m_StreamId)
- return;
-
- std::string formatString;
-
- // remove the physical/virtual property listeners before we make changes
- // that will trigger callbacks that we do not care about.
- AudioObjectPropertyAddress propertyAOPA;
- propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
- propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
- if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
-
- propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
- propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
- if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
-
- // Revert any format changes we made
- if (restore && m_OriginalVirtualFormat.mFormatID && m_StreamId)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
- "Restoring original virtual format for stream 0x%04x. (%s)",
- (uint)m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
- AudioStreamBasicDescription setFormat = m_OriginalVirtualFormat;
- SetVirtualFormat(&setFormat);
- }
- if (restore && m_OriginalPhysicalFormat.mFormatID && m_StreamId)
- {
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
- "Restoring original physical format for stream 0x%04x. (%s)",
- (uint)m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
- AudioStreamBasicDescription setFormat = m_OriginalPhysicalFormat;
- SetPhysicalFormat(&setFormat);
- }
-
- m_OriginalVirtualFormat.mFormatID = 0;
- m_OriginalPhysicalFormat.mFormatID = 0;
- CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", (uint)m_StreamId);
- m_StreamId = 0;
-}
-
-UInt32 CCoreAudioStream::GetDirection()
-{
- if (!m_StreamId)
- return 0;
-
- UInt32 val = 0;
- UInt32 size = sizeof(UInt32);
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyDirection;
-
- OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val);
- if (ret)
- return 0;
-
- return val;
-}
-
-UInt32 CCoreAudioStream::GetTerminalType()
-{
- if (!m_StreamId)
- return 0;
-
- UInt32 val = 0;
- UInt32 size = sizeof(UInt32);
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyTerminalType;
-
- OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val);
- if (ret)
- return 0;
- return val;
-}
-
-UInt32 CCoreAudioStream::GetNumLatencyFrames()
-{
- if (!m_StreamId)
- return 0;
-
- UInt32 i_param_size = sizeof(uint32_t);
- UInt32 i_param, num_latency_frames = 0;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyLatency;
-
- // number of frames of latency in the AudioStream
- OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
- if (ret == noErr)
- {
- num_latency_frames += i_param;
- }
-
- return num_latency_frames;
-}
-
-bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &size);
- if (ret)
- return false;
-
- ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &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)
- {
- // 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.", (uint)m_StreamId);
- return false;
- }
- }
- m_virtual_format_event.Reset();
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
-
- UInt32 propertySize = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
- "Unable to set virtual format for stream 0x%04x. Error = %s",
- (uint)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.", (uint)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)",
- (uint)m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
- break;
- }
- m_virtual_format_event.WaitMSec(100);
- }
- return true;
-}
-
-bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
-{
- if (!pDesc || !m_StreamId)
- return false;
-
- UInt32 size = sizeof(AudioStreamBasicDescription);
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
-
- OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &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)
- {
- // 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.", (uint)m_StreamId);
- return false;
- }
- }
- m_physical_format_event.Reset();
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
-
- UInt32 propertySize = sizeof(AudioStreamBasicDescription);
- OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
- if (ret)
- {
- CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
- "Unable to set physical format for stream 0x%04x. Error = %s",
- (uint)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.", (uint)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)",
- (uint)m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
- break;
- }
- m_physical_format_event.WaitMSec(100);
- }
-
- return true;
-}
-
-bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
-{
- if (!pList || !m_StreamId)
- return false;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize);
- if (ret)
- return false;
-
- UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
- ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
- propertyAddress.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &propertySize);
- if (ret)
- return false;
-
- UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
- ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &propertySize, pFormatList);
- if (!ret)
- {
- for (UInt32 format = 0; format < formatCount; format++)
- pList->push_back(pFormatList[format]);
- }
- delete[] pFormatList;
- return (ret == noErr);
-}
-
-OSStatus CCoreAudioStream::HardwareStreamListener(AudioObjectID inObjectID,
- UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
-{
- CCoreAudioStream *ca_stream = (CCoreAudioStream*)inClientData;
-
- for (UInt32 i = 0; i < inNumberAddresses; i++)
- {
- if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat)
- {
- AudioStreamBasicDescription actualFormat;
- UInt32 propertySize = sizeof(AudioStreamBasicDescription);
- // hardware physical format has changed.
- if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
- {
- CStdString formatString;
- CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
- "Hardware physical format changed to %s", StreamDescriptionToString(actualFormat, formatString));
- ca_stream->m_physical_format_event.Set();
- }
- }
- else if (inAddresses[i].mSelector == kAudioStreamPropertyVirtualFormat)
- {
- // hardware virtual format has changed.
- AudioStreamBasicDescription actualFormat;
- UInt32 propertySize = sizeof(AudioStreamBasicDescription);
- if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
- {
- CStdString formatString;
- CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
- "Hardware virtual format changed to %s", StreamDescriptionToString(actualFormat, formatString));
- ca_stream->m_virtual_format_event.Set();
- }
- }
- }
-
- return noErr;
-}
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-
-#include "threads/Thread.h"
-#include <CoreAudio/CoreAudio.h>
-
-#include <list>
-
-typedef std::list<AudioStreamID> AudioStreamIdList;
-typedef std::list<AudioStreamRangedDescription> StreamFormatList;
-
-class CCoreAudioStream
-{
-public:
- CCoreAudioStream();
- virtual ~CCoreAudioStream();
-
- bool Open(AudioStreamID streamId);
- void Close(bool restore = true);
-
- 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:
- static OSStatus HardwareStreamListener(AudioObjectID inObjectID,
- UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* inClientData);
-
- CEvent m_virtual_format_event;
- CEvent m_physical_format_event;
-
-
- AudioStreamID m_StreamId;
- AudioStreamBasicDescription m_OriginalVirtualFormat;
- AudioStreamBasicDescription m_OriginalPhysicalFormat;
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "CoreAudioUnit.h"
-
-#include "CoreAudioAEHAL.h"
-#include "cores/AudioEngine/Utils/AEUtil.h"
-#include "utils/log.h"
-
-#include <AudioToolbox/AUGraph.h>
-
-CCoreAudioUnit::CCoreAudioUnit() :
- m_pSource (NULL ),
- m_audioUnit (NULL ),
- m_audioNode (NULL ),
- m_audioGraph (NULL ),
- m_Initialized (false ),
- m_renderProc (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, 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)
-{
- AudioComponentDescription desc = {0};
- desc.componentType = type;
- desc.componentSubType = subType;
- desc.componentManufacturer = manufacturer;
-
- 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 && ret != kAUGraphErr_NodeNotFound)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
- "Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
- }
-
- ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
- "Unable to remove 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 != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: "
- "Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s",
- (int)bus, (int)scope, 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 != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: "
- "Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s",
- (int)bus, (int)scope, 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 != noErr)
- {
- 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 || !pLayouts)
- return false;
-
- UInt32 propSize = 0;
- Boolean writable = false;
- OSStatus ret = AudioUnitGetPropertyInfo(m_audioUnit,
- kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Input, 0, &propSize, &writable);
- if (ret != noErr)
- {
- 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 != noErr)
- {
- 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 != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: "
- "Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
- return false;
- }
-
- m_renderProc = RenderCallback;
-
- return true;
-}
-
-bool CCoreAudioUnit::RemoveRenderProc()
-{
- if (!m_audioUnit || !m_renderProc)
- return false;
-
- AudioUnitInitialize(m_audioUnit);
-
- AURenderCallbackStruct callbackInfo;
- callbackInfo.inputProc = nil;
- callbackInfo.inputProcRefCon = nil;
- OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: "
- "Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
- return false;
- }
-
- 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, bool encoded)
-{
- 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 |= kAudioFormatFlagsAudioUnitCanonical;
- break;
- case AE_FMT_S16LE:
- if (encoded)
- streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
- else
- streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
- break;
- case AE_FMT_S16BE:
- streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
- if (encoded)
- streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
- else
- streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
- break;
- default:
- streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
- if (encoded)
- streamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
- else
- streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
- 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 |= kAudioFormatFlagsAudioUnitCanonical;
- 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 != noErr)
- {
- 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 = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, kOutputBus, &deviceId, sizeof(AudioDeviceID));
- if (ret != noErr)
- {
- CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentDevice: "
- "Unable to set current device. Error = %s", GetError(ret).c_str());
- return false;
- }
-
- m_DeviceId = 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 != noErr)
- 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 != noErr)
- 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 != noErr)
- {
- 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 != noErr)
- {
- 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 bufferSize = 0;
- UInt32 size = sizeof(UInt32);
-
- OSStatus ret = AudioUnitGetProperty(m_audioUnit,
- kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Input, 0, &bufferSize, &size);
- if (ret != noErr)
- {
- 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;
-
- UInt32 hasio;
- UInt32 size=sizeof(UInt32);
- OSStatus ret = AudioUnitGetProperty(m_audioUnit,
- kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
-
- if (hasio)
- {
- UInt32 enable;
- enable = 0;
- ret = AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
- if (ret != noErr)
- {
- 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 != noErr)
- {
- 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;
-
- AudioObjectPropertyAddress propertyAddress;
- propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
- propertyAddress.mElement = 0;
- propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
- if (!AudioObjectHasProperty(m_DeviceId, &propertyAddress))
- return false;
-
- UInt32 propertySize = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
- if (ret != noErr)
- CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
- "Unable to retrieve preferred channel layout size. Error = %s", GetError(ret).c_str());
-
- void *pBuf = malloc(propertySize);
- ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
- if (ret != noErr)
- CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
- "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
- else
- {
- // Copy the result into the caller's instance
- layout.CopyLayout(*((AudioChannelLayout*)pBuf));
- }
- 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;
-}
-
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "system.h"
-
-#if defined(TARGET_DARWIN_OSX)
-
-#define INVALID_BUS -1
-#define kOutputBus 0
-#define kInputBus 1
-
-#include "ICoreAudioSource.h"
-#include "CoreAudioChannelLayout.h"
-
-#include <AudioToolbox/AUGraph.h>
-#include <CoreAudio/CoreAudio.h>
-#include <CoreServices/CoreServices.h>
-
-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 bool GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts);
- virtual void GetFormatDesc(AEAudioFormat format,
- AudioStreamBasicDescription *streamDesc, AudioStreamBasicDescription *coreaudioDesc, bool encoded = false);
- 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);
-};
-
-#endif
\ No newline at end of file
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "cores/AudioEngine/Utils/AEAudioFormat.h"
-#include "cores/AudioEngine/Interfaces/AE.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, float initVolume) = 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;
-};
+++ /dev/null
-#pragma once
-/*
- * Copyright (C) 2011-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <AudioUnit/AudioUnit.h>
-
-#include "cores/AudioEngine/Utils/AEAudioFormat.h"
-#include "cores/AudioEngine/Interfaces/AE.h"
-#include "utils/StdString.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; };
-};
* @param millis time for which old configuration should be kept
*/
virtual void KeepConfiguration(unsigned int millis) {return; }
+
+ /**
+ * Instruct AE to re-initialize, e.g. after ELD change event
+ */
+ virtual void DeviceChange() {return; }
};
SRCS = AEFactory.cpp
-ifeq ($(findstring osx,@ARCH@),osx)
-SRCS += Engines/CoreAudio/CoreAudioAE.cpp
-SRCS += Engines/CoreAudio/CoreAudioAEHAL.cpp
-SRCS += Engines/CoreAudio/CoreAudioAEHALOSX.cpp
-SRCS += Engines/CoreAudio/CoreAudioAESound.cpp
-SRCS += Engines/CoreAudio/CoreAudioAEStream.cpp
-SRCS += Engines/CoreAudio/CoreAudioChannelLayout.cpp
-SRCS += Engines/CoreAudio/CoreAudioDevice.cpp
-SRCS += Engines/CoreAudio/CoreAudioGraph.cpp
-SRCS += Engines/CoreAudio/CoreAudioHardware.cpp
-SRCS += Engines/CoreAudio/CoreAudioMixMap.cpp
-SRCS += Engines/CoreAudio/CoreAudioStream.cpp
-SRCS += Engines/CoreAudio/CoreAudioUnit.cpp
-else
-
SRCS += AESinkFactory.cpp
SRCS += Sinks/AESinkNULL.cpp
SRCS += Sinks/AESinkProfiler.cpp
ifeq (@USE_ANDROID@,1)
SRCS += Sinks/AESinkAUDIOTRACK.cpp
+else ifeq ($(findstring ios,@ARCH@),ios)
+SRCS += Sinks/AESinkDARWINIOS.cpp
+else ifeq ($(findstring osx,@ARCH@),osx)
+SRCS += Sinks/AESinkDARWINOSX.cpp
+SRCS += Sinks/osx/CoreAudioChannelCount.cpp
+SRCS += Sinks/osx/CoreAudioDevice.cpp
+SRCS += Sinks/osx/CoreAudioHardware.cpp
+SRCS += Sinks/osx/CoreAudioHelpers.cpp
+SRCS += Sinks/osx/CoreAudioStream.cpp
else
SRCS += Sinks/AESinkALSA.cpp
SRCS += Sinks/AESinkOSS.cpp
SRCS += Sinks/AESinkPULSE.cpp
endif
endif
-endif
SRCS += Utils/AEChannelInfo.cpp
SRCS += Utils/AEBuffer.cpp
--- /dev/null
+/*
+ * Copyright (C) 2005-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cores/AudioEngine/Sinks/AESinkDARWINIOS.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "cores/AudioEngine/Utils/AERingBuffer.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
+#include "osx/DarwinUtils.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "threads/Condition.h"
+#include "windowing/WindowingFactory.h"
+
+#include <sstream>
+#include <AudioToolbox/AudioToolbox.h>
+
+#define CA_MAX_CHANNELS 8
+static enum AEChannel CAChannelMap[CA_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_NULL
+};
+
+/***************************************************************************************/
+/***************************************************************************************/
+#if DO_440HZ_TONE_TEST
+static void SineWaveGeneratorInitWithFrequency(SineWaveGenerator *ctx, double frequency, double samplerate)
+{
+ // Given:
+ // frequency in cycles per second
+ // 2*PI radians per sine wave cycle
+ // sample rate in samples per second
+ //
+ // Then:
+ // cycles radians seconds radians
+ // ------ * ------- * ------- = -------
+ // second cycle sample sample
+ ctx->currentPhase = 0.0;
+ ctx->phaseIncrement = frequency * 2*M_PI / samplerate;
+}
+
+static int16_t SineWaveGeneratorNextSampleInt16(SineWaveGenerator *ctx)
+{
+ int16_t sample = INT16_MAX * sinf(ctx->currentPhase);
+
+ ctx->currentPhase += ctx->phaseIncrement;
+ // Keep the value between 0 and 2*M_PI
+ while (ctx->currentPhase > 2*M_PI)
+ ctx->currentPhase -= 2*M_PI;
+
+ return sample / 4;
+}
+static float SineWaveGeneratorNextSampleFloat(SineWaveGenerator *ctx)
+{
+ float sample = MAXFLOAT * sinf(ctx->currentPhase);
+
+ ctx->currentPhase += ctx->phaseIncrement;
+ // Keep the value between 0 and 2*M_PI
+ while (ctx->currentPhase > 2*M_PI)
+ ctx->currentPhase -= 2*M_PI;
+
+ return sample / 4;
+}
+#endif
+
+/***************************************************************************************/
+/***************************************************************************************/
+class CAAudioUnitSink
+{
+ public:
+ CAAudioUnitSink();
+ ~CAAudioUnitSink();
+
+ bool open(AudioStreamBasicDescription outputFormat);
+ bool close();
+ bool play(bool mute);
+ bool mute(bool mute);
+ bool pause();
+ void drain();
+ double getDelay();
+ double cacheSize();
+ unsigned int write(uint8_t *data, unsigned int byte_count);
+ unsigned int chunkSize() { return m_bufferDuration * m_sampleRate; }
+ unsigned int getRealisedSampleRate() { return m_outputFormat.mSampleRate; }
+ static Float64 getCoreAudioRealisedSampleRate();
+
+ private:
+ void setCoreAudioBuffersize();
+ bool setCoreAudioInputFormat();
+ void setCoreAudioPreferredSampleRate();
+ bool setupAudio();
+ bool checkAudioRoute();
+ bool checkSessionProperties();
+ bool activateAudioSession();
+ void deactivateAudioSession();
+
+ // callbacks
+ static void sessionPropertyCallback(void *inClientData,
+ AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData);
+
+ static void sessionInterruptionCallback(void *inClientData, UInt32 inInterruption);
+
+ static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+
+ bool m_setup;
+ bool m_initialized;
+ bool m_activated;
+ AudioUnit m_audioUnit;
+ AudioStreamBasicDescription m_outputFormat;
+ AERingBuffer *m_buffer;
+
+ bool m_mute;
+ Float32 m_outputVolume;
+ Float32 m_outputLatency;
+ Float32 m_bufferDuration;
+
+ unsigned int m_sampleRate;
+ unsigned int m_frameSize;
+ unsigned int m_frames;
+
+ bool m_playing;
+ bool m_playing_saved;
+ volatile bool m_started;
+};
+
+CAAudioUnitSink::CAAudioUnitSink()
+: m_initialized(false)
+, m_activated(false)
+, m_buffer(NULL)
+, m_playing(false)
+, m_playing_saved(false)
+, m_started(false)
+{
+}
+
+CAAudioUnitSink::~CAAudioUnitSink()
+{
+ close();
+}
+
+bool CAAudioUnitSink::open(AudioStreamBasicDescription outputFormat)
+{
+ m_mute = false;
+ m_setup = false;
+ m_outputFormat = outputFormat;
+ m_outputLatency = 0.0;
+ m_bufferDuration= 0.0;
+ m_outputVolume = 1.0;
+ m_sampleRate = (unsigned int)outputFormat.mSampleRate;
+ m_frameSize = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
+
+ /* TODO: Reduce the size of this buffer, pre-calculate the size based on how large
+ the buffers are that CA calls us with in the renderCallback - perhaps call
+ the checkSessionProperties() before running this? */
+ m_buffer = new AERingBuffer(16384);
+
+ return setupAudio();
+}
+
+bool CAAudioUnitSink::close()
+{
+ deactivateAudioSession();
+
+ delete m_buffer;
+ m_buffer = NULL;
+
+ m_started = false;
+ return true;
+}
+
+bool CAAudioUnitSink::play(bool mute)
+{
+ if (!m_playing)
+ {
+ if (activateAudioSession())
+ {
+ CAAudioUnitSink::mute(mute);
+ m_playing = !AudioOutputUnitStart(m_audioUnit);
+ }
+ }
+
+ return m_playing;
+}
+
+bool CAAudioUnitSink::mute(bool mute)
+{
+ m_mute = mute;
+
+ return true;
+}
+
+bool CAAudioUnitSink::pause()
+{
+ if (m_playing)
+ m_playing = AudioOutputUnitStop(m_audioUnit);
+
+ return m_playing;
+}
+
+double CAAudioUnitSink::getDelay()
+{
+ double delay = (double)m_buffer->GetReadSize() / m_frameSize;
+ delay /= m_sampleRate;
+ delay += m_bufferDuration + m_outputLatency;
+
+ return delay;
+}
+
+double CAAudioUnitSink::cacheSize()
+{
+ return (double)m_buffer->GetMaxSize() / (double)(m_frameSize * m_sampleRate);
+}
+
+CCriticalSection mutex;
+XbmcThreads::ConditionVariable condVar;
+
+unsigned int CAAudioUnitSink::write(uint8_t *data, unsigned int frames)
+{
+ if (m_buffer->GetWriteSize() < frames * m_frameSize)
+ { // no space to write - wait for a bit
+ CSingleLock lock(mutex);
+ if (!m_started)
+ condVar.wait(lock);
+ else
+ condVar.wait(lock, 900 * frames / m_sampleRate);
+ }
+
+ unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_frameSize);
+ if (write_frames)
+ m_buffer->Write(data, write_frames * m_frameSize);
+
+ return write_frames;
+}
+
+void CAAudioUnitSink::drain()
+{
+ CCriticalSection mutex;
+ unsigned int bytes = m_buffer->GetReadSize();
+ while (bytes)
+ {
+ CSingleLock lock(mutex);
+ condVar.wait(mutex, 900 * bytes / (m_sampleRate * m_frameSize));
+ bytes = m_buffer->GetReadSize();
+ }
+}
+
+void CAAudioUnitSink::setCoreAudioBuffersize()
+{
+#if !TARGET_IPHONE_SIMULATOR
+ OSStatus status = noErr;
+ // set the buffer size, this affects the number of samples
+ // that get rendered every time the audio callback is fired.
+ Float32 preferredBufferSize = 512 * m_outputFormat.mChannelsPerFrame / m_outputFormat.mSampleRate;
+ CLog::Log(LOGNOTICE, "%s setting buffer duration to %f", __PRETTY_FUNCTION__, preferredBufferSize);
+ status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
+ sizeof(preferredBufferSize), &preferredBufferSize);
+ if (status != noErr)
+ CLog::Log(LOGWARNING, "%s preferredBufferSize couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
+#endif
+}
+
+bool CAAudioUnitSink::setCoreAudioInputFormat()
+{
+ // Set the output stream format
+ UInt32 ioDataSize = sizeof(AudioStreamBasicDescription);
+ OSStatus status = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 0, &m_outputFormat, ioDataSize);
+ if (status != noErr)
+ {
+ CLog::Log(LOGERROR, "%s error setting stream format on audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
+ return false;
+ }
+ return true;
+}
+
+void CAAudioUnitSink::setCoreAudioPreferredSampleRate()
+{
+ Float64 preferredSampleRate = m_outputFormat.mSampleRate;
+ CLog::Log(LOGNOTICE, "%s requesting hw samplerate %f", __PRETTY_FUNCTION__, preferredSampleRate);
+ OSStatus status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate,
+ sizeof(preferredSampleRate), &preferredSampleRate);
+ if (status != noErr)
+ CLog::Log(LOGWARNING, "%s preferredSampleRate couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
+}
+
+Float64 CAAudioUnitSink::getCoreAudioRealisedSampleRate()
+{
+ Float64 outputSampleRate = 0.0;
+ UInt32 ioDataSize = sizeof(outputSampleRate);
+ if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
+ &ioDataSize, &outputSampleRate) != noErr)
+ CLog::Log(LOGERROR, "%s: error getting CurrentHardwareSampleRate", __FUNCTION__);
+ return outputSampleRate;
+}
+
+bool CAAudioUnitSink::setupAudio()
+{
+ OSStatus status = noErr;
+ if (m_setup && m_audioUnit)
+ return true;
+
+ // Audio Session Setup
+ UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
+ status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
+ sizeof(sessionCategory), &sessionCategory);
+ if (status != noErr)
+ {
+ CLog::Log(LOGERROR, "%s error setting sessioncategory (error: %d)", __PRETTY_FUNCTION__, (int)status);
+ return false;
+ }
+
+ AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
+ sessionPropertyCallback, this);
+
+ AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume,
+ sessionPropertyCallback, this);
+
+ if (AudioSessionSetActive(true) != noErr)
+ return false;
+
+ // Audio Unit Setup
+ // Describe a default output unit.
+ AudioComponentDescription description = {};
+ description.componentType = kAudioUnitType_Output;
+ description.componentSubType = kAudioUnitSubType_RemoteIO;
+ description.componentManufacturer = kAudioUnitManufacturer_Apple;
+
+ // Get component
+ AudioComponent component;
+ component = AudioComponentFindNext(NULL, &description);
+ status = AudioComponentInstanceNew(component, &m_audioUnit);
+ if (status != noErr)
+ {
+ CLog::Log(LOGERROR, "%s error creating audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
+ return false;
+ }
+
+ setCoreAudioPreferredSampleRate();
+
+ // Get the output samplerate for knowing what was setup in reality
+ Float64 realisedSampleRate = getCoreAudioRealisedSampleRate();
+ if (m_outputFormat.mSampleRate != realisedSampleRate)
+ {
+ CLog::Log(LOGNOTICE, "%s couldn't set requested samplerate %d, coreaudio will resample to %d instead", __PRETTY_FUNCTION__, (int)m_outputFormat.mSampleRate, (int)realisedSampleRate);
+ // if we don't ca to resample - but instead let activeae resample -
+ // reflect the realised samplerate to the outputformat here
+ // well maybe it is handy in the future - as of writing this
+ // ca was about 6 times faster then activeae ;)
+ //m_outputFormat.mSampleRate = realisedSampleRate;
+ //m_sampleRate = realisedSampleRate;
+ }
+
+ setCoreAudioBuffersize();
+ if (!setCoreAudioInputFormat())
+ return false;
+
+ // Attach a render callback on the unit
+ AURenderCallbackStruct callbackStruct = {};
+ callbackStruct.inputProc = renderCallback;
+ callbackStruct.inputProcRefCon = this;
+ status = AudioUnitSetProperty(m_audioUnit,
+ kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
+ 0, &callbackStruct, sizeof(callbackStruct));
+ if (status != noErr)
+ {
+ CLog::Log(LOGERROR, "%s error setting render callback for audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
+ return false;
+ }
+
+ status = AudioUnitInitialize(m_audioUnit);
+ if (status != noErr)
+ {
+ CLog::Log(LOGERROR, "%s error initializing audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
+ return false;
+ }
+
+ checkSessionProperties();
+
+ m_setup = true;
+ std::string formatString;
+ CLog::Log(LOGNOTICE, "%s setup audio format: %s", __PRETTY_FUNCTION__, StreamDescriptionToString(m_outputFormat, formatString));
+
+ return m_setup;
+}
+
+bool CAAudioUnitSink::checkAudioRoute()
+{
+ // why do we need to know the audio route ?
+ CFStringRef route;
+ UInt32 propertySize = sizeof(CFStringRef);
+ if (AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route) != noErr)
+ return false;
+
+ return true;
+}
+
+bool CAAudioUnitSink::checkSessionProperties()
+{
+ checkAudioRoute();
+
+ UInt32 ioDataSize;
+ ioDataSize = sizeof(m_outputVolume);
+ if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
+ &ioDataSize, &m_outputVolume) != noErr)
+ CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputVolume", __FUNCTION__);
+
+ ioDataSize = sizeof(m_outputLatency);
+ if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
+ &ioDataSize, &m_outputLatency) != noErr)
+ CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputLatency", __FUNCTION__);
+
+ ioDataSize = sizeof(m_bufferDuration);
+ if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
+ &ioDataSize, &m_bufferDuration) != noErr)
+ CLog::Log(LOGERROR, "%s: error getting CurrentHardwareIOBufferDuration", __FUNCTION__);
+
+ CLog::Log(LOGDEBUG, "%s: volume = %f, latency = %f, buffer = %f", __FUNCTION__, m_outputVolume, m_outputLatency, m_bufferDuration);
+ return true;
+}
+
+bool CAAudioUnitSink::activateAudioSession()
+{
+ if (!m_activated)
+ {
+ if (!m_initialized)
+ {
+ OSStatus osstat = AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, sessionInterruptionCallback, this);
+ if (osstat == kAudioSessionNoError || osstat == kAudioSessionAlreadyInitialized)
+ m_initialized = true;
+ else
+ {
+ CLog::Log(LOGERROR, "%s error initializing audio session (error: %d)", __PRETTY_FUNCTION__, (int)osstat);
+ return false;
+ }
+ }
+ if (checkAudioRoute() && setupAudio())
+ m_activated = true;
+ }
+
+ return m_activated;
+}
+
+void CAAudioUnitSink::deactivateAudioSession()
+{
+ if (m_activated)
+ {
+ pause();
+ AudioUnitUninitialize(m_audioUnit);
+ AudioComponentInstanceDispose(m_audioUnit), m_audioUnit = NULL;
+ AudioSessionSetActive(false);
+ AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange,
+ sessionPropertyCallback, this);
+ AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_CurrentHardwareOutputVolume,
+ sessionPropertyCallback, this);
+
+ m_setup = false;
+ m_activated = false;
+ }
+}
+
+void CAAudioUnitSink::sessionPropertyCallback(void *inClientData,
+ AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
+{
+ CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
+
+ if (inID == kAudioSessionProperty_AudioRouteChange)
+ {
+ if (sink->checkAudioRoute())
+ sink->checkSessionProperties();
+ }
+ else if (inID == kAudioSessionProperty_CurrentHardwareOutputVolume)
+ {
+ if (inData && inDataSize == 4)
+ sink->m_outputVolume = *(float*)inData;
+ }
+}
+
+void CAAudioUnitSink::sessionInterruptionCallback(void *inClientData, UInt32 inInterruption)
+{
+ CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
+
+ if (inInterruption == kAudioSessionBeginInterruption)
+ {
+ CLog::Log(LOGDEBUG, "Bgn interuption");
+ sink->m_playing_saved = sink->m_playing;
+ sink->pause();
+ }
+ else if (inInterruption == kAudioSessionEndInterruption)
+ {
+ CLog::Log(LOGDEBUG, "End interuption");
+ if (sink->m_playing_saved)
+ {
+ sink->m_playing_saved = false;
+ sink->play(sink->m_mute);
+ }
+ }
+}
+
+OSStatus CAAudioUnitSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+ CAAudioUnitSink *sink = (CAAudioUnitSink*)inRefCon;
+
+ sink->m_started = true;
+
+ for (unsigned int i = 0; i < ioData->mNumberBuffers; i++)
+ {
+ // buffers come from CA already zero'd, so just copy what is wanted
+ unsigned int wanted = ioData->mBuffers[i].mDataByteSize;
+ unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
+ sink->m_buffer->Read((unsigned char*)ioData->mBuffers[i].mData, bytes);
+ if (bytes != wanted)
+ CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
+ }
+ // tell the sink we're good for more data
+ condVar.notifyAll();
+
+ return noErr;
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+static void EnumerateDevices(AEDeviceInfoList &list)
+{
+ CAEDeviceInfo device;
+
+ device.m_deviceName = "default";
+ device.m_displayName = "Default";
+ device.m_displayNameExtra = "";
+#if defined(TARGET_DARWIN_IOS_ATV2)
+ device.m_deviceType = AE_DEVTYPE_IEC958;
+ device.m_dataFormats.push_back(AE_FMT_AC3);
+ device.m_dataFormats.push_back(AE_FMT_DTS);
+#else
+ // TODO screen changing on ios needs to call
+ // devices changed once this is available in activae
+ if (g_Windowing.GetCurrentScreen() > 0)
+ {
+ device.m_deviceType = AE_DEVTYPE_IEC958; //allow passthrough for tvout
+ device.m_dataFormats.push_back(AE_FMT_AC3);
+ device.m_dataFormats.push_back(AE_FMT_DTS);
+ }
+ else
+ device.m_deviceType = AE_DEVTYPE_PCM;
+#endif
+
+ // add channel info
+ CAEChannelInfo channel_info;
+ for (UInt32 chan = 0; chan < 2; ++chan)
+ {
+ if (!device.m_channels.HasChannel(CAChannelMap[chan]))
+ device.m_channels += CAChannelMap[chan];
+ channel_info += CAChannelMap[chan];
+ }
+
+ // there are more supported ( one of those 2 gets resampled
+ // by coreaudio anyway) - but for keeping it save ignore
+ // the others...
+ device.m_sampleRates.push_back(44100);
+ device.m_sampleRates.push_back(48000);
+
+ device.m_dataFormats.push_back(AE_FMT_S16LE);
+ //device.m_dataFormats.push_back(AE_FMT_S24LE3);
+ //device.m_dataFormats.push_back(AE_FMT_S32LE);
+ // AE_FMT_FLOAT is 3% slower on atv2
+ // then S16LE - so leave it out for now
+ //device.m_dataFormats.push_back(AE_FMT_FLOAT);
+
+ CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
+
+ list.push_back(device);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+AEDeviceInfoList CAESinkDARWINIOS::m_devices;
+
+CAESinkDARWINIOS::CAESinkDARWINIOS()
+: m_audioSink(NULL)
+{
+}
+
+CAESinkDARWINIOS::~CAESinkDARWINIOS()
+{
+}
+
+bool CAESinkDARWINIOS::Initialize(AEAudioFormat &format, std::string &device)
+{
+ bool found = false;
+ bool forceRaw = false;
+
+ std::string devicelower = device;
+ StringUtils::ToLower(devicelower);
+ for (size_t i = 0; i < m_devices.size(); i++)
+ {
+ if (devicelower.find(m_devices[i].m_deviceName) != std::string::npos)
+ {
+ m_info = m_devices[i];
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+
+ AudioStreamBasicDescription audioFormat = {};
+
+ // AE_FMT_FLOAT is 3% slower on atv2
+ // then S16LE - so leave it out for now
+ // just leave the code commented in here
+ // as it might come handy at some point maybe ...
+ //if (format.m_dataFormat == AE_FMT_FLOAT)
+ // audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+ //else// this will be selected when AE wants AC3 or DTS or anything other then float
+ {
+ audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ if (AE_IS_RAW(format.m_dataFormat))
+ forceRaw = true;
+ format.m_dataFormat = AE_FMT_S16LE;
+ }
+
+ format.m_channelLayout = m_info.m_channels;
+ format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+
+
+ audioFormat.mFormatID = kAudioFormatLinearPCM;
+ switch(format.m_sampleRate)
+ {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ audioFormat.mSampleRate = 44100;
+ break;
+ default:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ case 384000:
+ audioFormat.mSampleRate = 48000;
+ break;
+ }
+
+ if (forceRaw)//make sure input and output samplerate match for preventing resampling
+ audioFormat.mSampleRate = CAAudioUnitSink::getCoreAudioRealisedSampleRate();
+
+ audioFormat.mFramesPerPacket = 1;
+ audioFormat.mChannelsPerFrame= 2;// ios only supports 2 channels
+ audioFormat.mBitsPerChannel = CAEUtil::DataFormatToBits(format.m_dataFormat);
+ audioFormat.mBytesPerFrame = format.m_frameSize;
+ audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
+ audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
+
+#if DO_440HZ_TONE_TEST
+ SineWaveGeneratorInitWithFrequency(&m_SineWaveGenerator, 440.0, audioFormat.mSampleRate);
+#endif
+
+ m_audioSink = new CAAudioUnitSink;
+ m_audioSink->open(audioFormat);
+
+ format.m_frames = m_audioSink->chunkSize();
+ format.m_frameSamples = format.m_frames * audioFormat.mChannelsPerFrame;
+ // reset to the realised samplerate
+ format.m_sampleRate = m_audioSink->getRealisedSampleRate();
+ m_format = format;
+
+ m_volume_changed = false;
+ m_audioSink->play(false);
+
+ return true;
+}
+
+void CAESinkDARWINIOS::Deinitialize()
+{
+ delete m_audioSink;
+ m_audioSink = NULL;
+}
+
+bool CAESinkDARWINIOS::IsCompatible(const AEAudioFormat &format, const std::string &device)
+{
+ return ((m_format.m_sampleRate == format.m_sampleRate) &&
+ (m_format.m_dataFormat == format.m_dataFormat) &&
+ (m_format.m_channelLayout == format.m_channelLayout));
+}
+
+double CAESinkDARWINIOS::GetDelay()
+{
+ if (m_audioSink)
+ return m_audioSink->getDelay();
+ return 0.0;
+}
+
+double CAESinkDARWINIOS::GetCacheTotal()
+{
+ if (m_audioSink)
+ return m_audioSink->cacheSize();
+ return 0.0;
+}
+
+unsigned int CAESinkDARWINIOS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
+{
+
+#if DO_440HZ_TONE_TEST
+ if (m_format.m_dataFormat == AE_FMT_FLOAT)
+ {
+ float *samples = (float*)data;
+ for (unsigned int j = 0; j < frames ; j++)
+ {
+ float sample = SineWaveGeneratorNextSampleFloat(&m_SineWaveGenerator);
+ *samples++ = sample;
+ *samples++ = sample;
+ }
+
+ }
+ else
+ {
+ int16_t *samples = (int16_t*)data;
+ for (unsigned int j = 0; j < frames ; j++)
+ {
+ int16_t sample = SineWaveGeneratorNextSampleInt16(&m_SineWaveGenerator);
+ *samples++ = sample;
+ *samples++ = sample;
+ }
+ }
+#endif
+ if (m_audioSink)
+ return m_audioSink->write(data, frames);
+ return 0;
+}
+
+void CAESinkDARWINIOS::Drain()
+{
+ if (m_audioSink)
+ m_audioSink->drain();
+}
+
+bool CAESinkDARWINIOS::HasVolume()
+{
+ return false;
+}
+
+void CAESinkDARWINIOS::SetVolume(float scale)
+{
+ // CoreAudio uses fixed steps, reverse scale back to percent
+ float gain = CAEUtil::ScaleToGain(scale);
+ m_volume = CAEUtil::GainToPercent(gain);
+ m_volume_changed = true;
+}
+
+void CAESinkDARWINIOS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
+{
+ m_devices.clear();
+ EnumerateDevices(m_devices);
+ list = m_devices;
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2005-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cores/AudioEngine/Interfaces/AESink.h"
+#include "cores/AudioEngine/Utils/AEDeviceInfo.h"
+
+#define DO_440HZ_TONE_TEST 0
+
+#if DO_440HZ_TONE_TEST
+typedef struct {
+ float currentPhase;
+ float phaseIncrement;
+} SineWaveGenerator;
+#endif
+
+class AERingBuffer;
+class CAAudioUnitSink;
+
+class CAESinkDARWINIOS : public IAESink
+{
+public:
+ virtual const char *GetName() { return "DARWINIOS"; }
+
+ CAESinkDARWINIOS();
+ virtual ~CAESinkDARWINIOS();
+
+ 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 GetCacheTotal ();
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
+ virtual void Drain ();
+ virtual bool HasVolume ();
+ virtual void SetVolume (float scale);
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
+
+private:
+ static AEDeviceInfoList m_devices;
+ CAEDeviceInfo m_info;
+ AEAudioFormat m_format;
+ double m_volume;
+ bool m_volume_changed;
+
+ CAAudioUnitSink *m_audioSink;
+#if DO_440HZ_TONE_TEST
+ SineWaveGenerator m_SineWaveGenerator;
+#endif
+};
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2005-2014 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cores/AudioEngine/AEFactory.h"
+#include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "cores/AudioEngine/Utils/AERingBuffer.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h"
+#include "osx/DarwinUtils.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "threads/Condition.h"
+#include "threads/CriticalSection.h"
+
+#include <sstream>
+
+#define CA_MAX_CHANNELS 8
+static enum AEChannel CAChannelMap[CA_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_NULL
+};
+
+static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
+{
+ for (size_t i = 0; i < list.size(); ++i)
+ {
+ if (list[i] == samplerate)
+ return true;
+ }
+ return false;
+}
+
+static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
+{
+ for (size_t i = 0; i < list.size(); ++i)
+ {
+ if (list[i] == format)
+ return true;
+ }
+ return false;
+}
+
+typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
+
+static void EnumerateDevices(CADeviceList &list)
+{
+ CAEDeviceInfo device;
+
+ std::string defaultDeviceName;
+ CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
+
+ CoreAudioDeviceList deviceIDList;
+ CCoreAudioHardware::GetOutputDevices(&deviceIDList);
+ while (!deviceIDList.empty())
+ {
+ AudioDeviceID deviceID = deviceIDList.front();
+ CCoreAudioDevice caDevice(deviceID);
+
+ device.m_channels.Reset();
+ device.m_dataFormats.clear();
+ device.m_sampleRates.clear();
+
+ device.m_deviceType = AE_DEVTYPE_PCM;
+ device.m_deviceName = caDevice.GetName();
+ device.m_displayName = device.m_deviceName;
+ device.m_displayNameExtra = "";
+
+ if (device.m_deviceName.find("HDMI") != std::string::npos)
+ device.m_deviceType = AE_DEVTYPE_HDMI;
+
+ CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
+ AudioStreamIdList streams;
+ if (caDevice.GetStreams(&streams))
+ {
+ for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
+ {
+ StreamFormatList streams;
+ if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streams))
+ {
+ for (StreamFormatList::iterator i = streams.begin(); i != streams.end(); ++i)
+ {
+ AudioStreamBasicDescription desc = i->mFormat;
+ std::string formatString;
+ CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
+ StreamDescriptionToString(desc, formatString));
+ // add stream format info
+ switch (desc.mFormatID)
+ {
+ case kAudioFormatAC3:
+ case kAudioFormat60958AC3:
+ if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
+ device.m_dataFormats.push_back(AE_FMT_AC3);
+ if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
+ device.m_dataFormats.push_back(AE_FMT_DTS);
+ // if we are not hdmi, this is an S/PDIF device
+ if (device.m_deviceType != AE_DEVTYPE_HDMI)
+ device.m_deviceType = AE_DEVTYPE_IEC958;
+ break;
+ default:
+ AEDataFormat format = AE_FMT_INVALID;
+ switch(desc.mBitsPerChannel)
+ {
+ case 16:
+ if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
+ format = AE_FMT_S16BE;
+ else
+ {
+ /* Passthrough is possible with a 2ch digital output */
+ if (desc.mChannelsPerFrame == 2 && CCoreAudioStream::IsDigitalOuptut(*j))
+ {
+ if (desc.mSampleRate == 48000)
+ {
+ if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
+ device.m_dataFormats.push_back(AE_FMT_AC3);
+ if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
+ device.m_dataFormats.push_back(AE_FMT_DTS);
+ }
+ else if (desc.mSampleRate == 192000)
+ {
+ if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
+ device.m_dataFormats.push_back(AE_FMT_EAC3);
+ }
+ }
+ format = AE_FMT_S16LE;
+ }
+ break;
+ case 24:
+ if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
+ format = AE_FMT_S24BE3;
+ else
+ format = AE_FMT_S24LE3;
+ break;
+ case 32:
+ if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
+ format = AE_FMT_FLOAT;
+ else
+ {
+ if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
+ format = AE_FMT_S32BE;
+ else
+ format = AE_FMT_S32LE;
+ }
+ break;
+ }
+ if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
+ device.m_dataFormats.push_back(format);
+ break;
+ }
+
+ // add channel info
+ CAEChannelInfo channel_info;
+ for (UInt32 chan = 0; chan < CA_MAX_CHANNELS && chan < desc.mChannelsPerFrame; ++chan)
+ {
+ if (!device.m_channels.HasChannel(CAChannelMap[chan]))
+ device.m_channels += CAChannelMap[chan];
+ channel_info += CAChannelMap[chan];
+ }
+
+ // add sample rate info
+ if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
+ device.m_sampleRates.push_back(desc.mSampleRate);
+ }
+ }
+ }
+ }
+
+ list.push_back(std::make_pair(deviceID, device));
+ //in the first place of the list add the default device
+ //with name "default" - if this is selected
+ //we will output to whatever osx claims to be default
+ //(allows transition from headphones to speaker and stuff
+ //like that
+ if(defaultDeviceName == device.m_deviceName)
+ {
+ device.m_deviceName = "default";
+ device.m_displayName = "Default";
+ list.insert(list.begin(), std::make_pair(deviceID, device));
+ }
+
+ deviceIDList.pop_front();
+ }
+}
+
+/* static, threadsafe access to the device list */
+static CADeviceList s_devices;
+static CCriticalSection s_devicesLock;
+
+static void EnumerateDevices()
+{
+ CADeviceList devices;
+ EnumerateDevices(devices);
+ {
+ CSingleLock lock(s_devicesLock);
+ s_devices = devices;
+ }
+}
+
+static CADeviceList GetDevices()
+{
+ CADeviceList list;
+ {
+ CSingleLock lock(s_devicesLock);
+ list = s_devices;
+ }
+ return list;
+}
+
+OSStatus deviceChangedCB(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses,
+ const AudioObjectPropertyAddress inAddresses[],
+ void* inClientData)
+{
+ CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
+ CAEFactory::DeviceChange();
+ CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
+ return noErr;
+}
+
+void RegisterDeviceChangedCB(bool bRegister, void *ref)
+{
+ OSStatus ret = noErr;
+ const AudioObjectPropertyAddress inAdr =
+ {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ if (bRegister)
+ ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
+ else
+ ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
+
+ if (ret != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+CAESinkDARWINOSX::CAESinkDARWINOSX()
+: m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_buffer(NULL)
+{
+ // 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.
+ 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.");
+ }
+ RegisterDeviceChangedCB(true, this);
+ m_started = false;
+}
+
+CAESinkDARWINOSX::~CAESinkDARWINOSX()
+{
+ RegisterDeviceChangedCB(false, this);
+}
+
+float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
+{
+ float score = 0;
+ if (format.m_dataFormat == AE_FMT_AC3 ||
+ format.m_dataFormat == AE_FMT_DTS)
+ {
+ if (desc.mFormatID == kAudioFormat60958AC3 ||
+ desc.mFormatID == 'IAC3' ||
+ desc.mFormatID == kAudioFormatAC3)
+ {
+ if (desc.mSampleRate == format.m_sampleRate &&
+ desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
+ desc.mChannelsPerFrame == format.m_channelLayout.Count())
+ {
+ // perfect match
+ score = FLT_MAX;
+ }
+ }
+ }
+ if (format.m_dataFormat == AE_FMT_AC3 ||
+ format.m_dataFormat == AE_FMT_DTS ||
+ format.m_dataFormat == AE_FMT_EAC3)
+ { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
+ if (desc.mSampleRate == format.m_sampleRate &&
+ desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
+ desc.mChannelsPerFrame == format.m_channelLayout.Count() &&
+ desc.mFormatID == kAudioFormatLinearPCM)
+ {
+ score = FLT_MAX / 2;
+ }
+ }
+ else
+ { // non-passthrough, whatever works is fine
+ if (desc.mFormatID == kAudioFormatLinearPCM)
+ {
+ if (desc.mSampleRate == format.m_sampleRate)
+ score += 10;
+ else if (desc.mSampleRate > format.m_sampleRate)
+ score += 1;
+ if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
+ score += 5;
+ else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
+ score += 1;
+ if (format.m_dataFormat == AE_FMT_FLOAT)
+ { // for float, prefer the highest bitdepth we have
+ if (desc.mBitsPerChannel >= 16)
+ score += (desc.mBitsPerChannel / 8);
+ }
+ else
+ {
+ if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
+ score += 5;
+ else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
+ score += 1;
+ }
+ }
+ }
+ return score;
+}
+
+bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
+{
+ AudioDeviceID deviceID = 0;
+ CADeviceList devices = GetDevices();
+ if (StringUtils::EqualsNoCase(device, "default"))
+ {
+ CCoreAudioHardware::GetOutputDeviceName(device);
+ CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
+ }
+
+ for (size_t i = 0; i < devices.size(); i++)
+ {
+ if (device.find(devices[i].second.m_deviceName) != std::string::npos)
+ {
+ m_info = devices[i].second;
+ deviceID = devices[i].first;
+ break;
+ }
+ }
+ if (!deviceID)
+ {
+ CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
+ return false;
+ }
+
+ m_device.Open(deviceID);
+
+ // Fetch a list of the streams defined by the output device
+ AudioStreamIdList streams;
+ m_device.GetStreams(&streams);
+
+ CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
+
+ bool passthrough = false;
+ UInt32 outputIndex = 0;
+ float outputScore = 0;
+ AudioStreamBasicDescription outputFormat = {0};
+ AudioStreamID outputStream = 0;
+
+ /* The theory is to score based on
+ 1. Matching passthrough characteristics (i.e. passthrough flag)
+ 2. Matching sample rate.
+ 3. Matching bits per channel (or higher).
+ 4. Matching number of channels (or higher).
+ */
+ UInt32 index = 0;
+ for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
+ {
+ // Probe physical formats
+ StreamFormatList formats;
+ CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
+ for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
+ {
+ const AudioStreamBasicDescription &desc = j->mFormat;
+
+ float score = ScoreStream(desc, format);
+
+ std::string formatString;
+ CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
+
+ if (score > outputScore)
+ {
+ passthrough = score > 1000;
+ outputScore = score;
+ outputFormat = desc;
+ outputStream = *i;
+ outputIndex = index;
+ }
+ }
+ index++;
+ }
+
+ if (!outputFormat.mFormatID)
+ {
+ CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
+ return false;
+ }
+
+ /* Update our AE format */
+ format.m_sampleRate = outputFormat.mSampleRate;
+ if (outputFormat.mChannelsPerFrame != format.m_channelLayout.Count())
+ { /* update the channel count. We assume that they're layed out as given in CAChannelMap.
+ if they're not, this is plain wrong */
+ format.m_channelLayout.Reset();
+ for (unsigned int i = 0; i < outputFormat.mChannelsPerFrame; i++)
+ format.m_channelLayout += CAChannelMap[i];
+ }
+
+ m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
+
+ std::string formatString;
+ CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, outputIndex, outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
+
+ SetHogMode(passthrough);
+
+ // Configure the output stream object
+ m_outputStream.Open(outputStream);
+
+ AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
+ m_outputStream.GetVirtualFormat(&virtualFormat);
+ m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
+ CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
+ CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
+
+ m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
+ m_outputStream.GetVirtualFormat(&virtualFormat);
+ CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
+ CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
+
+ m_latentFrames = m_device.GetNumLatencyFrames();
+ m_latentFrames += m_outputStream.GetNumLatencyFrames();
+
+ /* TODO: Should we use the virtual format to determine our data format? */
+ format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
+ format.m_frames = m_device.GetBufferSize();
+ format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
+
+ if (m_outputBitstream)
+ {
+ m_outputBuffer = new int16_t[format.m_frameSamples];
+ /* TODO: Do we need this? */
+ m_device.SetNominalSampleRate(format.m_sampleRate);
+ }
+
+ unsigned int num_buffers = 4;
+ m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
+ CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
+
+ m_format = format;
+ if (passthrough)
+ format.m_dataFormat = AE_FMT_S16NE;
+ else
+ format.m_dataFormat = AE_FMT_FLOAT;
+
+ // Register for data request callbacks from the driver and start
+ m_device.AddIOProc(renderCallback, this);
+ m_device.Start();
+ return true;
+}
+
+void CAESinkDARWINOSX::SetHogMode(bool on)
+{
+ // 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)
+ if (on)
+ {
+ // 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.
+ CCoreAudioHardware::SetAutoHogMode(false);
+ bool autoHog = CCoreAudioHardware::GetAutoHogMode();
+ CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
+ "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
+ if (autoHog)
+ return;
+ }
+ m_device.SetHogStatus(on);
+ m_device.SetMixingSupport(!on);
+}
+
+void CAESinkDARWINOSX::Deinitialize()
+{
+ m_device.Stop();
+ m_device.RemoveIOProc();
+
+ m_outputStream.Close();
+ m_device.Close();
+ if (m_buffer)
+ {
+ delete m_buffer;
+ m_buffer = NULL;
+ }
+ m_outputBitstream = false;
+
+ delete[] m_outputBuffer;
+ m_outputBuffer = NULL;
+
+ m_started = false;
+}
+
+bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
+{
+ return ((m_format.m_sampleRate == format.m_sampleRate) &&
+ (m_format.m_dataFormat == format.m_dataFormat) &&
+ (m_format.m_channelLayout == format.m_channelLayout));
+}
+
+double CAESinkDARWINOSX::GetDelay()
+{
+ if (m_buffer)
+ {
+ // Calculate the duration of the data in the cache
+ double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
+ delay += (double)m_latentFrames;
+ delay /= (double)m_format.m_sampleRate;
+ return delay;
+ }
+ return 0.0;
+}
+
+double CAESinkDARWINOSX::GetCacheTotal()
+{
+ return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
+}
+
+CCriticalSection mutex;
+XbmcThreads::ConditionVariable condVar;
+
+unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
+{
+ if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
+ { // no space to write - wait for a bit
+ CSingleLock lock(mutex);
+ if (!m_started)
+ condVar.wait(lock);
+ else
+ condVar.wait(lock, 900 * frames / m_format.m_sampleRate);
+ }
+
+ unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
+ if (write_frames)
+ m_buffer->Write(data, write_frames * m_format.m_frameSize);
+
+ return write_frames;
+}
+
+void CAESinkDARWINOSX::Drain()
+{
+ CCriticalSection mutex;
+ int bytes = m_buffer->GetReadSize();
+ while (bytes)
+ {
+ CSingleLock lock(mutex);
+ condVar.wait(mutex, 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize));
+ bytes = m_buffer->GetReadSize();
+ }
+}
+
+void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
+{
+ EnumerateDevices();
+ list.clear();
+ for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
+ list.push_back(i->second);
+}
+
+OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
+{
+ CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
+
+ sink->m_started = true;
+ for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
+ {
+ if (sink->m_outputBitstream)
+ {
+ /* HACK for bitstreaming AC3/DTS via PCM.
+ We reverse the float->S16LE conversion done in the stream or device */
+ static const float mul = 1.0f / (INT16_MAX + 1);
+
+ unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples) * sizeof(int16_t);
+ if (wanted <= sink->m_buffer->GetReadSize())
+ {
+ sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
+ int16_t *src = sink->m_outputBuffer;
+ float *dest = (float*)outOutputData->mBuffers[i].mData;
+ for (unsigned int i = 0; i < wanted / 2; i++)
+ *dest++ = *src++ * mul;
+ }
+ }
+ else
+ {
+ /* buffers appear to come from CA already zero'd, so just copy what is wanted */
+ unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
+ unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
+ sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
+ if (bytes != wanted)
+ CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
+ }
+
+ // tell the sink we're good for more data
+ condVar.notifyAll();
+ }
+ return noErr;
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2005-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cores/AudioEngine/Interfaces/AESink.h"
+#include "cores/AudioEngine/Utils/AEDeviceInfo.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioDevice.h"
+#include "cores/AudioEngine/Sinks/osx/CoreAudioStream.h"
+
+class AERingBuffer;
+
+class CAESinkDARWINOSX : public IAESink
+{
+public:
+ virtual const char *GetName() { return "DARWINOSX"; }
+
+ CAESinkDARWINOSX();
+ virtual ~CAESinkDARWINOSX();
+
+ 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 GetCacheTotal ();
+ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio, bool blocking = false);
+ virtual void Drain ();
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
+
+private:
+ static OSStatus renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData);
+ void SetHogMode(bool on);
+
+ CAEDeviceInfo m_info;
+ AEAudioFormat m_format;
+
+ volatile bool m_draining;
+
+ CCoreAudioDevice m_device;
+ CCoreAudioStream m_outputStream;
+ unsigned int m_latentFrames;
+
+ bool m_outputBitstream; ///< true if we're bistreaming into a LinearPCM stream rather than AC3 stream.
+ int16_t *m_outputBuffer; ///< buffer for bitstreaming
+
+ AERingBuffer *m_buffer;
+ volatile bool m_started; // set once we get a callback from CoreAudio, which can take a little while.
+};
pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
pa_threaded_mainloop_signal(m, 0);
}
+
+static void SinkChangedCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
+{
+ CAESinkPULSE* p = (CAESinkPULSE*) userdata;
+ if(!p)
+ return;
+
+ CSingleLock lock(p->m_sec);
+ if (p->IsInitialized())
+ {
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
+ {
+ CLog::Log(LOGDEBUG, "Sink appeared");
+ CAEFactory::DeviceChange();
+ }
+ else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
+ {
+ CLog::Log(LOGDEBUG, "Sink removed");
+ CAEFactory::DeviceChange();
+ }
+ else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE)
+ {
+ CLog::Log(LOGDEBUG, "Sink changed");
+ //CAEFactory::DeviceChange();
+ }
+ }
+}
+
struct SinkInfoStruct
{
AEDeviceInfoList *list;
bool CAESinkPULSE::Initialize(AEAudioFormat &format, std::string &device)
{
- m_IsAllocated = false;
+ {
+ CSingleLock lock(m_sec);
+ m_IsAllocated = false;
+ }
m_passthrough = false;
m_BytesPerSecond = 0;
m_BufferSize = 0;
pa_threaded_mainloop_lock(m_MainLoop);
+ {
+ // Register Callback for Sink changes
+ CSingleLock lock(m_sec);
+ pa_context_set_subscribe_callback(m_Context, SinkChangedCallback, this);
+ const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK;
+ pa_operation *op = pa_context_subscribe(m_Context, mask, NULL, this);
+ if (op != NULL)
+ pa_operation_unref(op);
+ }
+
struct pa_channel_map map;
pa_channel_map_init(&map);
}
pa_threaded_mainloop_unlock(m_MainLoop);
-
- m_IsAllocated = true;
+
format.m_frameSize = frameSize;
format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
m_format = format;
format.m_dataFormat = m_passthrough ? AE_FMT_S16NE : format.m_dataFormat;
Pause(false);
+ {
+ CSingleLock lock(m_sec);
+ m_IsAllocated = true;
+ }
return true;
}
void CAESinkPULSE::Deinitialize()
{
+ CSingleLock lock(m_sec);
m_IsAllocated = false;
m_passthrough = false;
}
}
+bool CAESinkPULSE::IsInitialized()
+{
+ CSingleLock lock(m_sec);
+ return m_IsAllocated;
+}
+
bool CAESinkPULSE::Pause(bool pause)
{
pa_threaded_mainloop_lock(m_MainLoop);
#include "system.h"
#include "cores/AudioEngine/Interfaces/AESink.h"
+#include "cores/AudioEngine/AEFactory.h"
#include "Utils/AEDeviceInfo.h"
#include "Utils/AEUtil.h"
#include <pulse/pulseaudio.h>
+#include "threads/CriticalSection.h"
class CAESinkPULSE : public IAESink
{
virtual void SetVolume(float volume);
static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
+ bool IsInitialized();
+ CCriticalSection m_sec;
private:
bool Pause(bool pause);
static inline bool WaitForOperation(pa_operation *op, pa_threaded_mainloop *mainloop, const char *LogEntry);
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CoreAudioChannelLayout.h"
+
+#include <AudioToolbox/AudioToolbox.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
+};
+
+CCoreAudioChannelLayout::CCoreAudioChannelLayout() :
+ m_pLayout(NULL)
+{
+}
+
+CCoreAudioChannelLayout::CCoreAudioChannelLayout(AudioChannelLayout& layout) :
+m_pLayout(NULL)
+{
+ CopyLayout(layout);
+}
+
+CCoreAudioChannelLayout::~CCoreAudioChannelLayout()
+{
+ free(m_pLayout);
+}
+
+bool CCoreAudioChannelLayout::CopyLayout(AudioChannelLayout& layout)
+{
+ enum {
+ kVariableLengthArray_deprecated = 1
+ };
+
+ 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_deprecated) * 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;
+}
+
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#if defined(TARGET_DARWIN_OSX)
+#include <list>
+#include <vector>
+#include <string>
+
+#include <CoreAudio/CoreAudio.h>
+
+typedef std::vector<SInt32> CoreAudioChannelList;
+typedef std::list<AudioChannelLayoutTag> AudioChannelLayoutList;
+
+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
+};
+
+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
+};
+
+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;
+};
+
+#endif
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CoreAudioDevice.h"
+#include "CoreAudioHelpers.h"
+#include "CoreAudioChannelLayout.h"
+#include "CoreAudioHardware.h"
+#include "utils/log.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CCoreAudioDevice
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+CCoreAudioDevice::CCoreAudioDevice() :
+ m_Started (false ),
+ m_DeviceId (0 ),
+ m_MixerRestore (-1 ),
+ m_IoProc (NULL ),
+ m_ObjectListenerProc (NULL ),
+ m_SampleRateRestore (0.0f ),
+ m_HogPid (-1 ),
+ m_frameSize (0 ),
+ m_OutputBufferIndex (0 ),
+ m_BufferSizeRestore (0 )
+{
+}
+
+CCoreAudioDevice::CCoreAudioDevice(AudioDeviceID deviceId) :
+ m_Started (false ),
+ m_DeviceId (deviceId ),
+ m_MixerRestore (-1 ),
+ m_IoProc (NULL ),
+ m_ObjectListenerProc (NULL ),
+ m_SampleRateRestore (0.0f ),
+ m_HogPid (-1 ),
+ m_frameSize (0 ),
+ m_OutputBufferIndex (0 ),
+ m_BufferSizeRestore (0 )
+{
+}
+
+CCoreAudioDevice::~CCoreAudioDevice()
+{
+ Close();
+}
+
+bool CCoreAudioDevice::Open(AudioDeviceID deviceId)
+{
+ m_DeviceId = deviceId;
+ m_BufferSizeRestore = GetBufferSize();
+ return true;
+}
+
+void CCoreAudioDevice::Close()
+{
+ if (!m_DeviceId)
+ return;
+
+ // Stop the device if it was started
+ Stop();
+
+ // Unregister the IOProc if we have one
+ RemoveIOProc();
+
+ SetHogStatus(false);
+ CCoreAudioHardware::SetAutoHogMode(false);
+
+ if (m_MixerRestore > -1) // We changed the mixer status
+ SetMixingSupport((m_MixerRestore ? true : false));
+ m_MixerRestore = -1;
+
+ if (m_SampleRateRestore != 0.0f)
+ SetNominalSampleRate(m_SampleRateRestore);
+
+ if (m_BufferSizeRestore && m_BufferSizeRestore != GetBufferSize())
+ {
+ SetBufferSize(m_BufferSizeRestore);
+ m_BufferSizeRestore = 0;
+ }
+
+ m_IoProc = NULL;
+ m_DeviceId = 0;
+ m_ObjectListenerProc = 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)
+{
+ // Allow only one ObjectListener at a time
+ if (!m_DeviceId || m_ObjectListenerProc)
+ 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::AddIOProc(AudioDeviceIOProc ioProc, void* pCallbackData)
+{
+ // Allow only one IOProc at a time
+ if (!m_DeviceId || m_IoProc)
+ return false;
+
+ OSStatus ret = AudioDeviceCreateIOProcID(m_DeviceId, ioProc, pCallbackData, &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();
+
+ 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());
+
+ m_IoProc = NULL; // Clear the reference no matter what
+
+ Sleep(100);
+
+ return true;
+}
+
+std::string CCoreAudioDevice::GetName()
+{
+ if (!m_DeviceId)
+ return "";
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
+
+ UInt32 propertySize;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
+ if (ret != noErr)
+ return "";
+
+ std::string name;
+ char *buff = new char[propertySize + 1];
+ buff[propertySize] = 0x00;
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, buff);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetName: "
+ "Unable to get device name - id: 0x%04x. Error = %s", (uint)m_DeviceId, GetError(ret).c_str());
+ }
+ else
+ {
+ name = buff;
+ name.erase(name.find_last_not_of(" ") + 1);
+ }
+ delete[] buff;
+
+ return name;
+}
+
+UInt32 CCoreAudioDevice::GetTotalOutputChannels()
+{
+ UInt32 channels = 0;
+
+ if (!m_DeviceId)
+ return channels;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+ UInt32 size = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &size);
+ if (ret != noErr)
+ return channels;
+
+ AudioBufferList* pList = (AudioBufferList*)malloc(size);
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, pList);
+ if (ret == noErr)
+ {
+ 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",
+ (uint)m_DeviceId, GetError(ret).c_str());
+ }
+
+ free(pList);
+
+ return channels;
+}
+
+bool CCoreAudioDevice::GetStreams(AudioStreamIdList* pList)
+{
+ if (!pList || !m_DeviceId)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyStreams;
+
+ UInt32 propertySize = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
+ if (ret != noErr)
+ return false;
+
+ UInt32 streamCount = propertySize / sizeof(AudioStreamID);
+ AudioStreamID* pStreamList = new AudioStreamID[streamCount];
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pStreamList);
+ if (ret == noErr)
+ {
+ for (UInt32 stream = 0; stream < streamCount; stream++)
+ pList->push_back(pStreamList[stream]);
+ }
+ delete[] pStreamList;
+
+ return ret == noErr;
+}
+
+
+bool CCoreAudioDevice::IsRunning()
+{
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyDeviceIsRunning;
+
+ UInt32 isRunning = 0;
+ UInt32 propertySize = sizeof(isRunning);
+ OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &isRunning);
+ if (ret != noErr)
+ 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;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyHogMode;
+
+ if (hog)
+ {
+ // Not already set
+ if (m_HogPid == -1)
+ {
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(m_HogPid), &m_HogPid);
+
+ // even if setting hogmode was successfull our PID might not get written
+ // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
+ // had success on getting hog status
+ // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
+ // it seems that in the other cases the GetHogStatus could return -1
+ // which would overwrite our valid m_HogPid again
+ // Man we should never touch this shit again ;)
+ if (m_HogPid == -1)
+ m_HogPid = GetHogStatus();
+
+ if (ret || m_HogPid != getpid())
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetHogStatus: "
+ "Unable to set 'hog' status. Error = %s", GetError(ret).c_str());
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Currently Set
+ if (m_HogPid > -1)
+ {
+ pid_t hogPid = -1;
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, 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;
+ }
+ // Reset internal state
+ m_HogPid = hogPid;
+ }
+ }
+ return true;
+}
+
+pid_t CCoreAudioDevice::GetHogStatus()
+{
+ if (!m_DeviceId)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyHogMode;
+
+ pid_t hogPid = -1;
+ UInt32 size = sizeof(hogPid);
+ AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &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);
+ }
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
+
+ UInt32 mixEnable = mix ? 1 : 0;
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(mixEnable), &mixEnable);
+ if (ret != noErr)
+ {
+ 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;
+
+ UInt32 size;
+ UInt32 mix = 0;
+ Boolean writable = false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertySupportsMixing;
+
+ if( AudioObjectHasProperty( m_DeviceId, &propertyAddress ) )
+ {
+ OSStatus ret = AudioObjectIsPropertySettable(m_DeviceId, &propertyAddress, &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 = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &mix);
+ if (ret != noErr)
+ mix = 0;
+ }
+ }
+ CLog::Log(LOGDEBUG, "CCoreAudioDevice::SupportsMixing: "
+ "Device mixing support : %s.", mix ? "'Yes'" : "'No'");
+
+ return (mix > 0);
+}
+
+bool CCoreAudioDevice::SetCurrentVolume(Float32 vol)
+{
+ if (!m_DeviceId)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kHALOutputParam_Volume;
+
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float32), &vol);
+ if (ret != noErr)
+ {
+ 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;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
+
+ UInt32 propertySize = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
+ if (ret)
+ return false;
+
+ void* pBuf = malloc(propertySize);
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
+ if (ret != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioDevice::GetPreferredChannelLayout: "
+ "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
+ else
+ {
+ // Copy the result into the caller's instance
+ layout.CopyLayout(*((AudioChannelLayout*)pBuf));
+ }
+ free(pBuf);
+ return (ret == noErr);
+}
+
+bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList)
+{
+ if (!pList || !m_DeviceId)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyDataSources;
+
+ UInt32 propertySize = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
+ if (ret != noErr)
+ return false;
+
+ UInt32 sources = propertySize / sizeof(UInt32);
+ UInt32* pSources = new UInt32[sources];
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pSources);
+ if (ret == noErr)
+ {
+ 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;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
+
+ Float64 sampleRate = 0.0f;
+ UInt32 propertySize = sizeof(Float64);
+ OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &sampleRate);
+ if (ret != noErr)
+ {
+ 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
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
+
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, sizeof(Float64), &sampleRate);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioDevice::SetNominalSampleRate: "
+ "Unable to set current device sample rate to %0.0f. Error = %s",
+ (float)sampleRate, GetError(ret).c_str());
+ return false;
+ }
+ if (m_SampleRateRestore == 0.0f)
+ m_SampleRateRestore = currentRate;
+
+ return true;
+}
+
+UInt32 CCoreAudioDevice::GetNumLatencyFrames()
+{
+ UInt32 num_latency_frames = 0;
+ if (!m_DeviceId)
+ return 0;
+
+ // number of frames of latency in the AudioDevice
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyLatency;
+
+ UInt32 i_param = 0;
+ UInt32 i_param_size = sizeof(uint32_t);
+ OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
+ if (ret == noErr)
+ num_latency_frames += i_param;
+
+ // number of frames in the IO buffers
+ propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
+ if (ret == noErr)
+ num_latency_frames += i_param;
+
+ // number for frames in ahead the current hardware position that is safe to do IO
+ propertyAddress.mSelector = kAudioDevicePropertySafetyOffset;
+ ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
+ if (ret == noErr)
+ num_latency_frames += i_param;
+
+ return (num_latency_frames);
+}
+
+UInt32 CCoreAudioDevice::GetBufferSize()
+{
+ if (!m_DeviceId)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
+
+ UInt32 size = 0;
+ UInt32 propertySize = sizeof(size);
+ OSStatus ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &size);
+ if (ret != noErr)
+ 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;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
+ propertyAddress.mElement = 0;
+ propertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
+
+ UInt32 propertySize = sizeof(size);
+ OSStatus ret = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, propertySize, &size);
+ if (ret != noErr)
+ {
+ 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.");
+
+ return (ret == noErr);
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#if defined(TARGET_DARWIN_OSX)
+
+#include <string>
+
+#include "cores/AudioEngine/Sinks/osx/CoreAudioStream.h"
+
+#include <CoreAudio/CoreAudio.h>
+
+typedef std::list<UInt32> CoreAudioDataSourceList;
+typedef std::list<AudioDeviceID> CoreAudioDeviceList;
+
+class CCoreAudioChannelLayout;
+
+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);
+
+ bool AddIOProc(AudioDeviceIOProc ioProc, void* pCallbackData);
+ bool RemoveIOProc();
+protected:
+
+ bool m_Started;
+ AudioDeviceID m_DeviceId;
+ int m_MixerRestore;
+ AudioDeviceIOProc m_IoProc;
+ AudioObjectPropertyListenerProc m_ObjectListenerProc;
+
+ Float64 m_SampleRateRestore;
+ pid_t m_HogPid;
+ unsigned int m_frameSize;
+ unsigned int m_OutputBufferIndex;
+ unsigned int m_BufferSizeRestore;
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CoreAudioHardware.h"
+
+#include "CoreAudioHelpers.h"
+#include "utils/log.h"
+#include "osx/DarwinUtils.h"
+
+bool CCoreAudioHardware::GetAutoHogMode()
+{
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
+
+ UInt32 val = 0;
+ UInt32 size = sizeof(val);
+ OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &val);
+ if (ret != noErr)
+ {
+ 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)
+{
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioHardwarePropertyHogModeIsAllowed;
+
+ UInt32 val = enable ? 1 : 0;
+ UInt32 size = sizeof(val);
+ OSStatus ret = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &val);
+ if (ret != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioHardware::SetAutoHogMode: "
+ "Unable to set auto 'hog' mode. Error = %s", GetError(ret).c_str());
+}
+
+void CCoreAudioHardware::ResetAudioDevices()
+{
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices resetting our devices to LPCM");
+ CoreAudioDeviceList list;
+ if (GetOutputDevices(&list))
+ {
+ for (CoreAudioDeviceList::iterator it = list.begin(); it != list.end(); it ++)
+ {
+ CCoreAudioDevice device = *it;
+
+ AudioStreamIdList streams;
+ if (device.GetStreams(&streams))
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetAudioDevices %lu streams for device %s", streams.size(), device.GetName().c_str());
+ for (AudioStreamIdList::iterator ait = streams.begin(); ait != streams.end(); ait ++)
+ ResetStream(*ait);
+ }
+ }
+ }
+}
+
+void CCoreAudioHardware::ResetStream(AudioStreamID streamId)
+{
+ CCoreAudioStream stream;
+ stream.Open(streamId);
+
+ AudioStreamBasicDescription desc;
+ if (stream.GetPhysicalFormat(&desc))
+ {
+ if (desc.mFormatID == 'IAC3' || desc.mFormatID == kAudioFormat60958AC3)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::ResetStream stream 0x%x is in encoded format.. setting to LPCM", (unsigned int)streamId);
+
+ StreamFormatList availableFormats;
+ if (stream.GetAvailablePhysicalFormats(&availableFormats))
+ {
+ for (StreamFormatList::iterator fmtIt = availableFormats.begin(); fmtIt != availableFormats.end() ; fmtIt ++)
+ {
+ AudioStreamRangedDescription fmtDesc = *fmtIt;
+ if (fmtDesc.mFormat.mFormatID == kAudioFormatLinearPCM)
+ {
+ AudioStreamBasicDescription newFmt = { 0 };
+ newFmt = fmtDesc.mFormat;
+
+ if (stream.SetPhysicalFormat(&newFmt))
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ stream.Close(false);
+}
+
+AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
+{
+ AudioDeviceID deviceId = 0;
+
+ if (!searchName.length())
+ return deviceId;
+
+ 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].", (uint)defaultDevice);
+ return defaultDevice;
+ }
+ CLog::Log(LOGDEBUG, "CCoreAudioHardware::FindAudioDevice: "
+ "Searching for device - %s.", searchName.c_str());
+
+ // Obtain a list of all available audio devices
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioHardwarePropertyDevices;
+
+ UInt32 size = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::FindAudioDevice: "
+ "Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
+ return 0;
+ }
+
+ size_t deviceCount = size / sizeof(AudioDeviceID);
+ AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
+ ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
+ if (ret != noErr)
+ {
+ 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 (size_t dev = 0; dev < deviceCount; dev++)
+ {
+ CCoreAudioDevice device;
+ device.Open((pDevices[dev]));
+ deviceName = device.GetName();
+ 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()
+{
+ AudioDeviceID deviceId = 0;
+ static AudioDeviceID lastDeviceId = 0;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+
+ UInt32 size = sizeof(AudioDeviceID);
+ OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
+
+ // outputDevice is set to 0 if there is no audio device available
+ // or if the default device is set to an encoded format
+ if (ret != noErr || !deviceId)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice:"
+ " Unable to identify default output device. Error = %s", GetError(ret).c_str());
+ // if there was no error and no deviceId was returned
+ // return the last known default device
+ if (ret == noErr && !deviceId)
+ return lastDeviceId;
+ else
+ return 0;
+ }
+
+ lastDeviceId = deviceId;
+
+ return deviceId;
+}
+
+void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
+{
+ name = "Default";
+ AudioDeviceID deviceId = GetDefaultOutputDevice();
+
+ if (deviceId)
+ {
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioObjectPropertyName;
+
+ CFStringRef theDeviceName = NULL;
+ UInt32 propertySize = sizeof(CFStringRef);
+ OSStatus ret = AudioObjectGetPropertyData(deviceId, &propertyAddress, 0, NULL, &propertySize, &theDeviceName);
+ if (ret != noErr)
+ return;
+
+ DarwinCFStringRefToUTF8String(theDeviceName, name);
+
+ CFRelease(theDeviceName);
+ }
+}
+
+UInt32 CCoreAudioHardware::GetOutputDevices(CoreAudioDeviceList *pList)
+{
+ UInt32 found = 0;
+ if (!pList)
+ return found;
+
+ // Obtain a list of all available audio devices
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioHardwarePropertyDevices;
+
+ UInt32 size = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
+ if (ret != noErr)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
+ " Unable to retrieve the size of the list of available devices. Error = %s", GetError(ret).c_str());
+ return found;
+ }
+
+ size_t deviceCount = size / sizeof(AudioDeviceID);
+ AudioDeviceID* pDevices = new AudioDeviceID[deviceCount];
+ ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, pDevices);
+ if (ret != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioHardware::GetOutputDevices:"
+ " Unable to retrieve the list of available devices. Error = %s", GetError(ret).c_str());
+ else
+ {
+ for (size_t dev = 0; dev < deviceCount; dev++)
+ {
+ CCoreAudioDevice device(pDevices[dev]);
+ if (device.GetTotalOutputChannels() == 0)
+ continue;
+ found++;
+ pList->push_back(pDevices[dev]);
+ }
+ }
+ delete[] pDevices;
+
+ return found;
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#if defined(TARGET_DARWIN_OSX)
+
+#include "cores/AudioEngine/Sinks/osx/CoreAudioDevice.h"
+
+// 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* FormatsList(AudioStreamID stream);
+ static AudioStreamID* StreamsList(AudioDeviceID device);
+ static void ResetAudioDevices();
+ static void ResetStream(AudioStreamID streamId);
+ static AudioDeviceID FindAudioDevice(const std::string &deviceName);
+ static AudioDeviceID GetDefaultOutputDevice();
+ static void GetOutputDeviceName(std::string &name);
+ static UInt32 GetOutputDevices(CoreAudioDeviceList *pList);
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CoreAudioHelpers.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);
+}
+
+const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string &str)
+{
+ char fourCC[5] = {
+ (desc.mFormatID >> 24) & 0xFF,
+ (desc.mFormatID >> 16) & 0xFF,
+ (desc.mFormatID >> 8) & 0xFF,
+ desc.mFormatID & 0xFF,
+ 0
+ };
+
+ 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 "
+ << ((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
+ << " ("
+ << (UInt32)desc.mSampleRate
+ << "Hz)";
+ str = sstr.str();
+ break;
+ default:
+ sstr << "["
+ << fourCC
+ << "]";
+ break;
+ }
+ return str.c_str();
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <AudioToolbox/AudioToolbox.h>
+
+// Helper Functions
+std::string GetError(OSStatus error);
+const char* StreamDescriptionToString(AudioStreamBasicDescription desc, std::string &str);
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "CoreAudioStream.h"
+
+#include "CoreAudioHelpers.h"
+#include "utils/log.h"
+#include "utils/StdString.h"
+
+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.", (uint)m_StreamId);
+
+ // watch for physical property changes.
+ AudioObjectPropertyAddress propertyAOPA;
+ propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
+ propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
+ if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a physical property listener.");
+
+ // watch for virtual property changes.
+ propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
+ propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
+ if (AudioObjectAddPropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
+ CLog::Log(LOGERROR, "CCoreAudioStream::Open: couldn't set up a virtual property listener.");
+
+ 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(bool restore)
+{
+ if (!m_StreamId)
+ return;
+
+ std::string formatString;
+
+ // remove the physical/virtual property listeners before we make changes
+ // that will trigger callbacks that we do not care about.
+ AudioObjectPropertyAddress propertyAOPA;
+ propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
+ propertyAOPA.mSelector = kAudioStreamPropertyPhysicalFormat;
+ if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
+
+ propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
+ propertyAOPA.mSelector = kAudioStreamPropertyVirtualFormat;
+ if (AudioObjectRemovePropertyListener(m_StreamId, &propertyAOPA, HardwareStreamListener, this) != noErr)
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Couldn't remove property listener.");
+
+ // Revert any format changes we made
+ if (restore && m_OriginalVirtualFormat.mFormatID && m_StreamId)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
+ "Restoring original virtual format for stream 0x%04x. (%s)",
+ (uint)m_StreamId, StreamDescriptionToString(m_OriginalVirtualFormat, formatString));
+ AudioStreamBasicDescription setFormat = m_OriginalVirtualFormat;
+ SetVirtualFormat(&setFormat);
+ }
+ if (restore && m_OriginalPhysicalFormat.mFormatID && m_StreamId)
+ {
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: "
+ "Restoring original physical format for stream 0x%04x. (%s)",
+ (uint)m_StreamId, StreamDescriptionToString(m_OriginalPhysicalFormat, formatString));
+ AudioStreamBasicDescription setFormat = m_OriginalPhysicalFormat;
+ SetPhysicalFormat(&setFormat);
+ }
+
+ m_OriginalVirtualFormat.mFormatID = 0;
+ m_OriginalPhysicalFormat.mFormatID = 0;
+ CLog::Log(LOGDEBUG, "CCoreAudioStream::Close: Closed stream 0x%04x.", (uint)m_StreamId);
+ m_StreamId = 0;
+}
+
+UInt32 CCoreAudioStream::GetDirection()
+{
+ if (!m_StreamId)
+ return 0;
+
+ UInt32 val = 0;
+ UInt32 size = sizeof(UInt32);
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyDirection;
+
+ OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &size, &val);
+ if (ret)
+ return 0;
+
+ return val;
+}
+
+bool CCoreAudioStream::IsDigitalOuptut(AudioStreamID id)
+{
+ UInt32 type = GetTerminalType(id);
+ return (type == kAudioStreamTerminalTypeDigitalAudioInterface ||
+ type == kAudioStreamTerminalTypeDisplayPort ||
+ type == kAudioStreamTerminalTypeHDMI);
+}
+
+UInt32 CCoreAudioStream::GetTerminalType(AudioStreamID id)
+{
+ if (!id)
+ return 0;
+
+ UInt32 val = 0;
+ UInt32 size = sizeof(UInt32);
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyTerminalType;
+
+ OSStatus ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &size, &val);
+ if (ret)
+ return 0;
+ return val;
+}
+
+UInt32 CCoreAudioStream::GetNumLatencyFrames()
+{
+ if (!m_StreamId)
+ return 0;
+
+ UInt32 i_param_size = sizeof(uint32_t);
+ UInt32 i_param, num_latency_frames = 0;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyLatency;
+
+ // number of frames of latency in the AudioStream
+ OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &i_param_size, &i_param);
+ if (ret == noErr)
+ {
+ num_latency_frames += i_param;
+ }
+
+ return num_latency_frames;
+}
+
+bool CCoreAudioStream::GetVirtualFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
+ OSStatus ret = AudioObjectGetPropertyDataSize(m_StreamId, &propertyAddress, 0, NULL, &size);
+ if (ret)
+ return false;
+
+ ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &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)
+ {
+ // 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.", (uint)m_StreamId);
+ return false;
+ }
+ }
+ m_virtual_format_event.Reset();
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
+
+ UInt32 propertySize = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetVirtualFormat: "
+ "Unable to set virtual format for stream 0x%04x. Error = %s",
+ (uint)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.", (uint)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)",
+ (uint)m_StreamId, StreamDescriptionToString(checkVirtualFormat, formatString));
+ break;
+ }
+ m_virtual_format_event.WaitMSec(100);
+ }
+ return true;
+}
+
+bool CCoreAudioStream::GetPhysicalFormat(AudioStreamBasicDescription* pDesc)
+{
+ if (!pDesc || !m_StreamId)
+ return false;
+
+ UInt32 size = sizeof(AudioStreamBasicDescription);
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
+
+ OSStatus ret = AudioObjectGetPropertyData(m_StreamId, &propertyAddress, 0, NULL, &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)
+ {
+ // 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.", (uint)m_StreamId);
+ return false;
+ }
+ }
+ m_physical_format_event.Reset();
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
+
+ UInt32 propertySize = sizeof(AudioStreamBasicDescription);
+ OSStatus ret = AudioObjectSetPropertyData(m_StreamId, &propertyAddress, 0, NULL, propertySize, pDesc);
+ if (ret)
+ {
+ CLog::Log(LOGERROR, "CCoreAudioStream::SetPhysicalFormat: "
+ "Unable to set physical format for stream 0x%04x. Error = %s",
+ (uint)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.", (uint)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)",
+ (uint)m_StreamId, StreamDescriptionToString(checkPhysicalFormat, formatString));
+ break;
+ }
+ m_physical_format_event.WaitMSec(100);
+ }
+
+ return true;
+}
+
+bool CCoreAudioStream::GetAvailableVirtualFormats(StreamFormatList* pList)
+{
+ return GetAvailableVirtualFormats(m_StreamId, pList);
+}
+
+bool CCoreAudioStream::GetAvailableVirtualFormats(AudioStreamID id, StreamFormatList* pList)
+{
+ if (!pList || !id)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
+
+ UInt32 propertySize = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(id, &propertyAddress, 0, NULL, &propertySize);
+ if (ret)
+ return false;
+
+ UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
+ AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
+ ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &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)
+{
+ return GetAvailablePhysicalFormats(m_StreamId, pList);
+}
+
+bool CCoreAudioStream::GetAvailablePhysicalFormats(AudioStreamID id, StreamFormatList* pList)
+{
+ if (!pList || !id)
+ return false;
+
+ AudioObjectPropertyAddress propertyAddress;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+ propertyAddress.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
+
+ UInt32 propertySize = 0;
+ OSStatus ret = AudioObjectGetPropertyDataSize(id, &propertyAddress, 0, NULL, &propertySize);
+ if (ret)
+ return false;
+
+ UInt32 formatCount = propertySize / sizeof(AudioStreamRangedDescription);
+ AudioStreamRangedDescription *pFormatList = new AudioStreamRangedDescription[formatCount];
+ ret = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &propertySize, pFormatList);
+ if (!ret)
+ {
+ for (UInt32 format = 0; format < formatCount; format++)
+ pList->push_back(pFormatList[format]);
+ }
+ delete[] pFormatList;
+ return (ret == noErr);
+}
+
+OSStatus CCoreAudioStream::HardwareStreamListener(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
+{
+ CCoreAudioStream *ca_stream = (CCoreAudioStream*)inClientData;
+
+ for (UInt32 i = 0; i < inNumberAddresses; i++)
+ {
+ if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat)
+ {
+ AudioStreamBasicDescription actualFormat;
+ UInt32 propertySize = sizeof(AudioStreamBasicDescription);
+ // hardware physical format has changed.
+ if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
+ {
+ CStdString formatString;
+ CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
+ "Hardware physical format changed to %s", StreamDescriptionToString(actualFormat, formatString));
+ ca_stream->m_physical_format_event.Set();
+ }
+ }
+ else if (inAddresses[i].mSelector == kAudioStreamPropertyVirtualFormat)
+ {
+ // hardware virtual format has changed.
+ AudioStreamBasicDescription actualFormat;
+ UInt32 propertySize = sizeof(AudioStreamBasicDescription);
+ if (AudioObjectGetPropertyData(ca_stream->m_StreamId, &inAddresses[i], 0, NULL, &propertySize, &actualFormat) == noErr)
+ {
+ CStdString formatString;
+ CLog::Log(LOGINFO, "CCoreAudioStream::HardwareStreamListener: "
+ "Hardware virtual format changed to %s", StreamDescriptionToString(actualFormat, formatString));
+ ca_stream->m_virtual_format_event.Set();
+ }
+ }
+ }
+
+ return noErr;
+}
--- /dev/null
+#pragma once
+/*
+ * Copyright (C) 2011-2013 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#if defined(TARGET_DARWIN_OSX)
+
+#include "threads/Event.h"
+#include <CoreAudio/CoreAudio.h>
+
+#include <list>
+
+typedef std::list<AudioStreamID> AudioStreamIdList;
+typedef std::list<AudioStreamRangedDescription> StreamFormatList;
+
+class CCoreAudioStream
+{
+public:
+ CCoreAudioStream();
+ virtual ~CCoreAudioStream();
+
+ bool Open(AudioStreamID streamId);
+ void Close(bool restore = true);
+
+ AudioStreamID GetId() {return m_StreamId;}
+ UInt32 GetDirection();
+ static UInt32 GetTerminalType(AudioStreamID id);
+ 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);
+ static bool GetAvailableVirtualFormats(AudioStreamID id, StreamFormatList *pList);
+ static bool GetAvailablePhysicalFormats(AudioStreamID id, StreamFormatList *pList);
+ static bool IsDigitalOuptut(AudioStreamID id);
+
+protected:
+ static OSStatus HardwareStreamListener(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* inClientData);
+
+ CEvent m_virtual_format_event;
+ CEvent m_physical_format_event;
+
+ AudioStreamID m_StreamId;
+ AudioStreamBasicDescription m_OriginalVirtualFormat;
+ AudioStreamBasicDescription m_OriginalPhysicalFormat;
+};
+
+#endif
m_channels = 0;
m_layout = 0;
- m_bLpcmMode = false;
-
m_pFrame1 = NULL;
m_iSampleFormat = AV_SAMPLE_FMT_NONE;
}
return false;
}
-#if defined(TARGET_DARWIN_OSX)
- if (CSettings::Get().GetInt("audiooutput.channels") > AE_CH_LAYOUT_2_0)
- m_bLpcmMode = true;
-#endif
-
m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
m_pCodecContext->debug_mv = 0;
m_pCodecContext->debug = 0;
default:
convert = true;
}
- if(m_bLpcmMode || convert)
+ if(convert)
ConvertToFloat();
return iBytesUsed;
enum AEDataFormat CDVDAudioCodecFFmpeg::GetDataFormat()
{
- if(m_bLpcmMode)
- {
- return AE_FMT_LPCM;
- }
- else
+ switch(m_pCodecContext->sample_fmt)
{
- switch(m_pCodecContext->sample_fmt)
- {
- case AV_SAMPLE_FMT_U8 : return AE_FMT_U8;
- case AV_SAMPLE_FMT_S16: return AE_FMT_S16NE;
- case AV_SAMPLE_FMT_S32: return AE_FMT_S32NE;
- case AV_SAMPLE_FMT_FLT: return AE_FMT_FLOAT;
- case AV_SAMPLE_FMT_DBL: return AE_FMT_DOUBLE;
- case AV_SAMPLE_FMT_NONE:
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::GetDataFormat - invalid data format");
- return AE_FMT_INVALID;
- default:
- return AE_FMT_FLOAT;
- }
+ case AV_SAMPLE_FMT_U8 : return AE_FMT_U8;
+ case AV_SAMPLE_FMT_S16: return AE_FMT_S16NE;
+ case AV_SAMPLE_FMT_S32: return AE_FMT_S32NE;
+ case AV_SAMPLE_FMT_FLT: return AE_FMT_FLOAT;
+ case AV_SAMPLE_FMT_DBL: return AE_FMT_DOUBLE;
+ case AV_SAMPLE_FMT_NONE:
+ CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::GetDataFormat - invalid data format");
+ return AE_FMT_INVALID;
+ default:
+ return AE_FMT_FLOAT;
}
}
SwrContext* m_pConvert;
enum AVSampleFormat m_iSampleFormat;
CAEChannelInfo m_channelLayout;
- bool m_bLpcmMode;
AVFrame* m_pFrame1;
int m_iBufferSize1;
+++ /dev/null
-/*
- * Copyright (C) 2005-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "DVDAudioCodecPassthroughFFmpeg.h"
-#include "DVDCodecs/DVDCodecs.h"
-#include "DVDStreamInfo.h"
-#include "cores/AudioEngine/Utils/AEUtil.h"
-#include "settings/MediaSettings.h"
-#include "settings/Settings.h"
-#include "utils/log.h"
-#include "cores/AudioEngine/AEFactory.h"
-
-//These values are forced to allow spdif out
-#define OUT_SAMPLESIZE 16
-#define OUT_CHANNELS 2
-#define OUT_SAMPLERATE 48000
-
-/* Lookup tables */
-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 uint16_t DTSFSCod [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0};
-
-#define NULL_MUXER(muxer) \
- muxer.m_pFormat = NULL; \
- muxer.m_pStream = NULL; \
- muxer.m_OutputSize = 0; \
- muxer.m_Consumed = 0; \
- muxer.m_Buffer = NULL; \
- muxer.m_BufferSize = 0;
-
-CDVDAudioCodecPassthroughFFmpeg::CDVDAudioCodecPassthroughFFmpeg(void)
-{
- NULL_MUXER(m_SPDIF);
- NULL_MUXER(m_ADTS );
-
- m_pSyncFrame = NULL;
- m_Needed = 0;
- m_NeededUsed = 0;
- m_SampleRate = 0;
-
- m_Codec = NULL;
-
- /* make enough room for at-least two audio frames */
- m_DecodeSize = 0;
- m_DecodeBuffer = NULL;
- m_bSupportsAC3Out = false;
- m_bSupportsDTSOut = false;
- m_bSupportsAACOut = false;
- m_LostSync = false;
-
-}
-
-CDVDAudioCodecPassthroughFFmpeg::~CDVDAudioCodecPassthroughFFmpeg(void)
-{
- Dispose();
-}
-
-/*===================== MUXER FUNCTIONS ========================*/
-bool CDVDAudioCodecPassthroughFFmpeg::SetupMuxer(CDVDStreamInfo &hints, CStdString muxerName, Muxer &muxer)
-{
- CLog::Log(LOGINFO, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Trying to setup %s muxer", muxerName.c_str());
-
- /* get the muxer */
- AVOutputFormat *fOut = NULL;
-
- fOut = m_dllAvFormat.av_guess_format(muxerName.c_str(), NULL, NULL);
- if (!fOut)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Failed to get the FFmpeg %s muxer", muxerName.c_str());
- Dispose();
- return false;
- }
-
- /* allocate a the format context */
- muxer.m_pFormat = m_dllAvFormat.avformat_alloc_context();
- if (!muxer.m_pFormat)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Failed to allocate AVFormat context");
- Dispose();
- return false;
- }
-
- muxer.m_pFormat->oformat = fOut;
-
- /* allocate a put_byte struct so we can grab the output */
- muxer.m_pFormat->pb = m_dllAvFormat.avio_alloc_context(muxer.m_BCBuffer, sizeof(muxer.m_BCBuffer), AVIO_FLAG_READ, &muxer, NULL, MuxerReadPacket, NULL);
- if (!muxer.m_pFormat->pb)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Failed to allocate ByteIOContext");
- Dispose();
- return false;
- }
-
- /* this is streamed, no file, and ignore the index */
- muxer.m_pFormat->pb->seekable = 0;
- muxer.m_pFormat->flags |= AVFMT_NOFILE | AVFMT_FLAG_IGNIDX;
- muxer.m_pFormat->bit_rate = hints.bitrate;
-
- /* While this is strictly only needed on big-endian systems, we do it on
- * both to avoid as much dead code as possible.
- * CoreAudio (at least on the cases we've seen) wants IEC 61937 in
- * little-endian format even on big-endian systems. */
-#if defined(WORDS_BIGENDIAN) && !defined(TARGET_DARWIN)
- const char *spdifFlags = "+be";
-#else
- const char *spdifFlags = "-be";
-#endif
-
- /* request output of wanted endianness */
- if (!fOut->priv_class || m_dllAvUtil.av_opt_set(muxer.m_pFormat->priv_data, "spdif_flags", spdifFlags, 0) != 0)
- {
-#if defined(WORDS_BIGENDIAN) && !defined(TARGET_DARWIN)
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Unable to set big-endian stream mode (FFmpeg too old?), disabling passthrough");
- Dispose();
- return false;
-#endif
- }
-
- /* add a stream to it */
- muxer.m_pStream = m_dllAvFormat.avformat_new_stream(muxer.m_pFormat, NULL);
- if (!muxer.m_pStream)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Failed to allocate AVStream context");
- Dispose();
- return false;
- }
-
-
- /* set the stream's parameters */
- m_SampleRate = hints.samplerate;
- if(!m_SampleRate && hints.codec == AV_CODEC_ID_AC3)
- m_SampleRate = 48000;
-
- AVCodecContext *codec = muxer.m_pStream->codec;
- codec->codec_type = AVMEDIA_TYPE_AUDIO;
- codec->codec_id = hints.codec;
- codec->sample_rate = m_SampleRate;
- codec->sample_fmt = AV_SAMPLE_FMT_S16;
- codec->channels = hints.channels;
- codec->bit_rate = hints.bitrate;
- codec->extradata = new uint8_t[hints.extrasize];
- codec->extradata_size = hints.extrasize;
- memcpy(codec->extradata, hints.extradata, hints.extrasize);
-
- muxer.m_WroteHeader = m_dllAvFormat.avformat_write_header(muxer.m_pFormat, NULL) == 0;
- if (!muxer.m_WroteHeader)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - Failed to write the frame header");
- return false;
- }
-
- CLog::Log(LOGINFO, "CDVDAudioCodecPassthroughFFmpeg::SetupMuxer - %s muxer ready", muxerName.c_str());
- return true;
-}
-
-int CDVDAudioCodecPassthroughFFmpeg::MuxerReadPacket(void *opaque, uint8_t *buf, int buf_size)
-{
- /* create a new packet and push it into our output buffer */
- DataPacket *packet = new DataPacket();
- packet->size = buf_size;
- packet->data = new uint8_t[buf_size];
- memcpy(packet->data, buf, buf_size);
-
- Muxer *muxer = (Muxer*)opaque;
- muxer->m_OutputBuffer.push_back(packet);
- muxer->m_OutputSize += buf_size;
-
- /* return how much we wrote to our buffer */
- return buf_size;
-}
-
-void CDVDAudioCodecPassthroughFFmpeg::WriteFrame(Muxer &muxer, uint8_t *pData, int iSize)
-{
- AVPacket pkt;
- m_dllAvCodec.av_init_packet(&pkt);
- pkt.data = pData;
- pkt.size = iSize;
-
- muxer.m_Consumed += iSize;
- if (m_dllAvFormat.av_write_frame(muxer.m_pFormat, &pkt) < 0)
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::WriteFrame - Failed to write the frame data");
-}
-
-int CDVDAudioCodecPassthroughFFmpeg::GetMuxerData(Muxer &muxer, uint8_t** dst)
-{
- int size;
- if(muxer.m_OutputSize)
- {
- /* check if the buffer is allocated */
- if (muxer.m_Buffer)
- {
- /* only re-allocate the buffer it is too small */
- if (muxer.m_BufferSize < muxer.m_OutputSize)
- {
- delete[] muxer.m_Buffer;
- muxer.m_Buffer = new uint8_t[muxer.m_OutputSize];
- muxer.m_BufferSize = muxer.m_OutputSize;
- }
- }
- else
- {
- /* allocate the buffer */
- muxer.m_Buffer = new uint8_t[muxer.m_OutputSize];
- muxer.m_BufferSize = muxer.m_OutputSize;
- }
-
- /* fill the buffer with the output data */
- uint8_t *offset;
- offset = muxer.m_Buffer;
- while(!muxer.m_OutputBuffer.empty())
- {
- DataPacket* packet = muxer.m_OutputBuffer.front();
- muxer.m_OutputBuffer.pop_front();
-
- memcpy(offset, packet->data, packet->size);
- offset += packet->size;
-
- delete[] packet->data;
- delete packet;
- }
-
- *dst = muxer.m_Buffer;
- size = muxer.m_OutputSize;
- muxer.m_OutputSize = 0;
- muxer.m_Consumed = 0;
- return size;
- }
- else
- return 0;
-}
-
-void CDVDAudioCodecPassthroughFFmpeg::ResetMuxer(Muxer &muxer)
-{
- muxer.m_OutputSize = 0;
- muxer.m_Consumed = 0;
- while(!muxer.m_OutputBuffer.empty())
- {
- DataPacket* packet = muxer.m_OutputBuffer.front();
- muxer.m_OutputBuffer.pop_front();
- delete[] packet->data;
- delete packet;
- }
-}
-
-void CDVDAudioCodecPassthroughFFmpeg::DisposeMuxer(Muxer &muxer)
-{
- ResetMuxer(muxer);
- delete[] muxer.m_Buffer;
- muxer.m_Buffer = NULL;
- muxer.m_BufferSize = 0;
- if (muxer.m_pFormat)
- {
- if (muxer.m_WroteHeader)
- m_dllAvFormat.av_write_trailer(muxer.m_pFormat);
- muxer.m_WroteHeader = false;
- if (muxer.m_pStream)
- delete[] muxer.m_pStream->codec->extradata;
- m_dllAvUtil.av_freep(&muxer.m_pFormat->pb);
- m_dllAvUtil.av_freep(&muxer.m_pFormat);
- m_dllAvUtil.av_freep(&muxer.m_pStream);
- }
-}
-/*===================== END MUXER FUNCTIONS ========================*/
-
-bool CDVDAudioCodecPassthroughFFmpeg::SupportsFormat(CDVDStreamInfo &hints)
-{
- m_pSyncFrame = NULL;
-
- if (m_bSupportsAC3Out && hints.codec == AV_CODEC_ID_AC3) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAC3;
- else if (m_bSupportsDTSOut && hints.codec == AV_CODEC_ID_DTS) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncDTS;
- else if (m_bSupportsAACOut && hints.codec == AV_CODEC_ID_AAC) m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAAC;
- else return false;
-
- return true;
-}
-
-bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
-{
- // TODO - move this stuff somewhere else
- bool m_bSupportsAC3Out = CAEFactory::SupportsRaw(AE_FMT_AC3);
- bool m_bSupportsEAC3Out = CAEFactory::SupportsRaw(AE_FMT_EAC3);
- bool m_bSupportsDTSOut = CAEFactory::SupportsRaw(AE_FMT_DTS);
-
- if ((hints.codec == AV_CODEC_ID_AC3 && !m_bSupportsAC3Out) ||
- (hints.codec == AV_CODEC_ID_EAC3 && !m_bSupportsEAC3Out) ||
- (hints.codec == AV_CODEC_ID_DTS && !m_bSupportsDTSOut))
- {
- return false;
- }
-
- // TODO - this is only valid for video files, and should be moved somewhere else
- if( hints.channels == 2 && CMediaSettings::Get().GetCurrentVideoSettings().m_OutputToAllSpeakers )
- {
- CLog::Log(LOGINFO, "CDVDAudioCodecPassthroughFFmpeg::Open - disabled passthrough due to video OTAS");
- return false;
- }
-
- // TODO - some soundcards do support other sample rates, but they are quite uncommon
- if( hints.samplerate > 0 && hints.samplerate != 48000 )
- {
- CLog::Log(LOGINFO, "CDVDAudioCodecPassthroughFFmpeg::Open - disabled passthrough due to sample rate not being 48000");
- return false;
- }
-
- if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load())
- return false;
-
- m_dllAvFormat.av_register_all();
-
- /* see if the muxer supports our codec (see spdif.c for supported formats) */
- if (!SupportsFormat(hints))
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - FFmpeg SPDIF muxer does not support this codec");
- Dispose();
- return false;
- }
- else
- {
- /* aac needs to be wrapped into ADTS frames */
- if (hints.codec == AV_CODEC_ID_AAC)
- if (!SetupMuxer(hints, "adts", m_ADTS))
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - Unable to setup ADTS muxer");
- Dispose();
- return false;
- }
-
- m_Codec = NULL;
- }
-
- if (!SetupMuxer(hints, "spdif", m_SPDIF))
- return false;
-
- Reset();
-
- /* we will check the first packet's crc */
- m_LostSync = true;
-
- m_codec = hints.codec;
-
- return true;
-}
-
-void CDVDAudioCodecPassthroughFFmpeg::Dispose()
-{
- Reset();
-
- DisposeMuxer(m_SPDIF);
- DisposeMuxer(m_ADTS );
-
- if (m_DecodeBuffer)
- {
- _aligned_free(m_DecodeBuffer);
- m_DecodeBuffer = NULL;
- }
-
- delete m_Codec;
- m_Codec = NULL;
-}
-
-int CDVDAudioCodecPassthroughFFmpeg::Decode(uint8_t* pData, int iSize)
-{
- unsigned int used, fSize;
- fSize = iSize;
-
- /* if we are muxing into ADTS (AAC) */
- int adts_used = 0;
- if (m_ADTS.m_pFormat)
- {
- adts_used = iSize;
- WriteFrame(m_ADTS, pData, iSize);
- iSize = GetMuxerData(m_ADTS, &pData);
- }
-
- used = 0;
- while(iSize > 0)
- {
- /* skip data until we can sync and know how much we need */
- if (m_Needed == 0)
- {
- /* if we have a sync function for this codec */
- if (m_pSyncFrame)
- {
- int skip = (this->*m_pSyncFrame)(pData, iSize, &m_Needed);
- if (skip > 0)
- {
- /* we lost sync, so invalidate our buffer */
- m_NeededUsed = 0;
- return used + skip;
- }
- }
- else
- m_Needed = iSize;
- }
-
- if(m_SPDIF.m_pStream->codec->sample_rate != m_SampleRate)
- {
- CLog::Log(LOGDEBUG, "CDVDAudioCodecPassthroughFFmpeg::Decode - stream changed sample rate from %d to %d"
- , m_SPDIF.m_pStream->codec->sample_rate
- , m_SampleRate);
- m_SPDIF.m_pStream->codec->sample_rate = m_SampleRate;
- }
-
- /* check for bad parsing */
- assert(m_Needed > 0);
-
- /* append one block or less of data */
- int copy;
- int room = sizeof(m_NeededBuffer) - m_NeededUsed;
- int need = m_Needed - m_NeededUsed;
- copy = room > need ? need : room;
- copy = iSize > copy ? copy : iSize;
- memcpy(m_NeededBuffer + m_NeededUsed, pData, copy);
-
- m_NeededUsed += copy;
- used += copy;
- iSize -= copy;
- pData += copy;
-
- /* if we have enough data in the buffer, write it out */
- if (m_NeededUsed == m_Needed)
- {
- WriteFrame(m_SPDIF, m_NeededBuffer, m_Needed);
- m_NeededUsed = 0;
- m_Needed = 0;
- }
- }
-
- /* return how much data we copied */
- if (m_ADTS.m_pFormat)
- return adts_used;
- else
- return used;
-}
-
-int CDVDAudioCodecPassthroughFFmpeg::GetData(uint8_t** dst)
-{
- return GetMuxerData(m_SPDIF, dst);
-}
-
-void CDVDAudioCodecPassthroughFFmpeg::Reset()
-{
- m_DecodeSize = 0;
- m_LostSync = true;
- m_Needed = 0;
- m_NeededUsed = 0;
-
- ResetMuxer(m_SPDIF);
- ResetMuxer(m_ADTS );
-}
-
-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 2;
-}
-
-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 AV_CODEC_ID_AC3: return AE_FMT_AC3;
- case AV_CODEC_ID_DTS: return AE_FMT_DTS;
- default:
- return AE_FMT_INVALID; //Unknown stream type
- }
-}
-
-
-int CDVDAudioCodecPassthroughFFmpeg::GetBitsPerSample()
-{
- return OUT_SAMPLESIZE;
-}
-
-int CDVDAudioCodecPassthroughFFmpeg::GetBufferSize()
-{
- if (m_Codec)
- return m_Codec->GetBufferSize();
- else
- return m_SPDIF.m_Consumed + m_ADTS.m_Consumed + m_NeededUsed;
-}
-
-/* ========================== SYNC FUNCTIONS ========================== */
-unsigned int CDVDAudioCodecPassthroughFFmpeg::SyncAC3(uint8_t* pData, unsigned int iSize, unsigned int *fSize)
-{
- unsigned int skip = 0;
- for(skip = 0; iSize - skip > 6; ++skip, ++pData)
- {
- /* search for an ac3 sync word */
- if(pData[0] != 0x0b || pData[1] != 0x77)
- continue;
-
- uint8_t fscod = pData[4] >> 6;
- uint8_t frmsizecod = pData[4] & 0x3F;
- uint8_t bsid = pData[5] >> 3;
-
- /* sanity checks on the header */
- if (
- fscod == 3 ||
- frmsizecod > 37 ||
- bsid > 0x11
- ) continue;
-
- /* get the details we need to check crc1 and framesize */
- uint16_t 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;
- }
-
- *fSize = framesize * 2;
- m_SampleRate = AC3FSCod[fscod];
-
- /* dont do extensive testing if we have not lost sync */
- if (!m_LostSync && 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 <= iSize - skip)
- crc_size = framesize - 1;
- else crc_size = (framesize >> 1) + (framesize >> 3) - 1;
-
- if (crc_size <= iSize - skip)
- if(m_dllAvUtil.av_crc(m_dllAvUtil.av_crc_get_table(AV_CRC_16_ANSI), 0, &pData[2], crc_size * 2))
- continue;
-
- /* if we get here, we can sync */
- m_LostSync = false;
- return skip;
- }
-
- /* if we get here, the entire packet is invalid and we have lost sync */
- m_LostSync = true;
- return iSize;
-}
-
-unsigned int CDVDAudioCodecPassthroughFFmpeg::SyncDTS(uint8_t* pData, unsigned int iSize, unsigned int *fSize)
-{
- unsigned int skip;
- unsigned int srCode;
- bool littleEndian;
-
- for(skip = 0; iSize - skip > 8; ++skip, ++pData)
- {
- /* 16bit le */ if (pData[0] == 0x7F && pData[1] == 0xFE && pData[2] == 0x80 && pData[3] == 0x01 ) littleEndian = true ; else
- /* 14bit le */ if (pData[0] == 0x1F && pData[1] == 0xFF && pData[2] == 0xE8 && pData[3] == 0x00 && pData[4] == 0x07 && (pData[5] & 0xF0) == 0xF0) littleEndian = true ; else
- /* 16bit be */ if (pData[1] == 0x7F && pData[0] == 0xFE && pData[3] == 0x80 && pData[2] == 0x01 ) littleEndian = false; else
- /* 14bit be */ if (pData[1] == 0x1F && pData[0] == 0xFF && pData[3] == 0xE8 && pData[2] == 0x00 && pData[5] == 0x07 && (pData[4] & 0xF0) == 0xF0) littleEndian = false; else
- continue;
-
- if (littleEndian)
- {
- /* if it is not a termination frame, check the next 6 bits are set */
- if ((pData[4] & 0x80) == 0x80 && (pData[4] & 0x7C) != 0x7C)
- continue;
-
- /* get the frame size */
- *fSize = ((((pData[5] & 0x3) << 8 | pData[6]) << 4) | ((pData[7] & 0xF0) >> 4)) + 1;
- srCode = (pData[8] & 0x3C) >> 2;
- }
- else
- {
- /* if it is not a termination frame, check the next 6 bits are set */
- if ((pData[5] & 0x80) == 0x80 && (pData[5] & 0x7C) != 0x7C)
- continue;
-
- /* get the frame size */
- *fSize = ((((pData[4] & 0x3) << 8 | pData[7]) << 4) | ((pData[6] & 0xF0) >> 4)) + 1;
- srCode = (pData[9] & 0x3C) >> 2;
- }
-
- /* make sure the framesize is sane */
- if (*fSize < 96 || *fSize > 16384)
- continue;
-
- m_SampleRate = DTSFSCod[srCode];
- m_LostSync = false;
- return skip;
- }
-
- m_LostSync = true;
- return iSize;
-}
-
-unsigned int CDVDAudioCodecPassthroughFFmpeg::SyncAAC(uint8_t* pData, unsigned int iSize, unsigned int *fSize)
-{
- unsigned int skip;
- for(skip = 0; iSize - skip > 5; ++skip, ++pData)
- {
- if (pData[0] != 0xFF || (pData[1] & 0xF0) != 0xF0)
- continue;
-
- *fSize = (pData[3] & 0x03) << 11 | pData[4] << 3 | (pData[5] & 0xE0) >> 5;
- if (*fSize < 7)
- continue;
-
- m_LostSync = false;
- return skip;
- }
-
- m_LostSync = true;
- 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}
- };
-
- return map[0];
-}
+++ /dev/null
-#pragma once
-
-/*
- * Copyright (C) 2005-2013 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, see
- * <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <list>
-#include "system.h"
-#include "DllAvFormat.h"
-#include "DllAvCodec.h"
-#include "DllAvUtil.h"
-
-#include "DVDAudioCodec.h"
-
-class CDVDAudioCodecPassthroughFFmpeg : public CDVDAudioCodec
-{
-public:
- CDVDAudioCodecPassthroughFFmpeg();
- virtual ~CDVDAudioCodecPassthroughFFmpeg();
-
- virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
- virtual void Dispose();
- virtual int Decode(uint8_t* pData, int iSize);
- virtual int GetData(uint8_t** dst);
- virtual void Reset();
- virtual int GetChannels();
- 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"; }
- virtual int GetBufferSize();
-private:
- DllAvFormat m_dllAvFormat;
- DllAvUtil m_dllAvUtil;
- DllAvCodec m_dllAvCodec;
-
- typedef struct
- {
- int size;
- uint8_t *data;
- } DataPacket;
-
- typedef struct
- {
- AVFormatContext *m_pFormat;
- AVStream *m_pStream;
- std::list<DataPacket*> m_OutputBuffer;
- unsigned int m_OutputSize;
- bool m_WroteHeader;
- unsigned char m_BCBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
- unsigned int m_Consumed;
- unsigned int m_BufferSize;
- uint8_t *m_Buffer;
- } Muxer;
-
- Muxer m_SPDIF, m_ADTS;
- bool SetupMuxer(CDVDStreamInfo &hints, CStdString muxerName, Muxer &muxer);
- static int MuxerReadPacket(void *opaque, uint8_t *buf, int buf_size);
- void WriteFrame(Muxer &muxer, uint8_t *pData, int iSize);
- int GetMuxerData(Muxer &muxer, uint8_t** dst);
- void ResetMuxer(Muxer &muxer);
- void DisposeMuxer(Muxer &muxer);
-
- bool m_bSupportsAC3Out;
- bool m_bSupportsDTSOut;
- bool m_bSupportsAACOut;
-
- CDVDAudioCodec *m_Codec;
- uint8_t *m_DecodeBuffer;
- unsigned int m_DecodeSize;
- bool SupportsFormat(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;
- AVCodecID m_codec;
-
- unsigned int (CDVDAudioCodecPassthroughFFmpeg::*m_pSyncFrame)(uint8_t* pData, unsigned int iSize, unsigned int *fSize);
- unsigned int SyncAC3(uint8_t* pData, unsigned int iSize, unsigned int *fSize);
- unsigned int SyncDTS(uint8_t* pData, unsigned int iSize, unsigned int *fSize);
- unsigned int SyncAAC(uint8_t* pData, unsigned int iSize, unsigned int *fSize);
-};
-
SRCS += DVDAudioCodecPassthrough.cpp
SRCS += DVDAudioCodecPcm.cpp
-ifeq ($(findstring osx,@ARCH@),osx)
-SRCS += DVDAudioCodecPassthroughFFmpeg.cpp
-endif
-
LIB=Audio.a
include @abs_top_srcdir@/Makefile.include
#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"
CDVDCodecOptions options;
// try passthrough first
-#if defined(TARGET_DARWIN_OSX) || defined(TARGET_DARWIN_IOS)
- switch(hint.codec)
- {
- case AV_CODEC_ID_AC3:
- case AV_CODEC_ID_DTS:
- pCodec = OpenCodec( new CDVDAudioCodecPassthroughFFmpeg(), hint, options );
- if( pCodec ) return pCodec;
- break;
- default:
- break;
- }
-#endif
pCodec = OpenCodec( new CDVDAudioCodecPassthrough(), hint, options );
if( pCodec ) return pCodec;
#include "DynamicDll.h"
#include "cores/dvdplayer/DVDClock.h"
+#include "cores/VideoRenderers/RenderFlags.h"
#include "cores/VideoRenderers/RenderManager.h"
#include "guilib/GraphicContext.h"
#include "settings/MediaSettings.h"
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
// AppContext - Application state
+#define MODE_3D_DISABLE 0x00000000
+#define MODE_3D_LR 0x00000101
+#define MODE_3D_BT 0x00000201
+#define MODE_3D_TO_2D_L 0x00000102
+#define MODE_3D_TO_2D_R 0x00000902
+#define MODE_3D_TO_2D_T 0x00000202
+#define MODE_3D_TO_2D_B 0x00000a02
+
#define PTS_FREQ 90000
#define UNIT_FREQ 96000
#define AV_SYNC_THRESH PTS_FREQ*30
aml_set_sysfs_int("/sys/class/tsync/enable", 1);
ShowMainVideo(false);
-
}
void CAMLCodec::Reset()
return;
}
+void CAMLCodec::SetVideo3dMode(const int mode3d)
+{
+ aml_set_sysfs_int("/sys/class/ppmgr/ppmgr_3d_mode", mode3d);
+}
+
+std::string CAMLCodec::GetStereoMode()
+{
+ std::string stereo_mode;
+
+ switch(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoMode)
+ {
+ case RENDER_STEREO_MODE_SPLIT_VERTICAL: stereo_mode = "left_right"; break;
+ case RENDER_STEREO_MODE_SPLIT_HORIZONTAL: stereo_mode = "top_bottom"; break;
+ default: stereo_mode = m_hints.stereo_mode; break;
+ }
+
+ if(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoInvert)
+ stereo_mode = RenderManager::GetStereoModeInvert(stereo_mode);
+ return stereo_mode;
+}
+
void CAMLCodec::RenderFeaturesCallBack(const void *ctx, Features &renderFeatures)
{
CAMLCodec *codec = (CAMLCodec*)ctx;
// this routine gets called every video frame
// and is in the context of the renderer thread so
// do not do anything stupid here.
+ bool update = false;
// video zoom adjustment.
float zoom = CMediaSettings::Get().GetCurrentVideoSettings().m_CustomZoomAmount;
m_brightness = brightness;
}
- // check if destination rect or video view mode has changed
- if ((m_dst_rect != DestRect) || (m_view_mode != CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode))
+ // video view mode
+ int view_mode = CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode;
+ if (m_view_mode != view_mode)
+ {
+ m_view_mode = view_mode;
+ update = true;
+ }
+
+ // video stereo mode/view.
+ RENDER_STEREO_MODE stereo_mode = g_graphicsContext.GetStereoMode();
+ if (m_stereo_mode != stereo_mode)
+ {
+ m_stereo_mode = stereo_mode;
+ update = true;
+ }
+ RENDER_STEREO_VIEW stereo_view = g_graphicsContext.GetStereoView();
+ if (m_stereo_view != stereo_view)
+ {
+ // left/right/top/bottom eye,
+ // this might change every other frame.
+ // we do not care but just track it.
+ m_stereo_view = stereo_view;
+ }
+
+ // dest_rect
+ if (m_dst_rect != DestRect)
{
m_dst_rect = DestRect;
- m_view_mode = CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode;
+ update = true;
}
- else
+
+ if (!update)
{
// mainvideo 'should' be showing already if we get here, make sure.
ShowMainVideo(true);
dst_rect.y2 *= yscale;
}
- ShowMainVideo(false);
+#if 0
+ std::string rectangle = StringUtils::Format("%i,%i,%i,%i",
+ (int)dst_rect.x1, (int)dst_rect.y1,
+ (int)dst_rect.Width(), (int)dst_rect.Height());
+ CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:dst_rect(%s)", rectangle.c_str());
+ CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:m_stereo_mode(%d)", m_stereo_mode);
+ CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:m_stereo_view(%d)", m_stereo_view);
+#endif
+
+ if (m_stereo_mode == RENDER_STEREO_MODE_MONO)
+ {
+ std::string mode = GetStereoMode();
+ CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:mode(%s)", mode.c_str());
+ if (mode == "left_right")
+ SetVideo3dMode(MODE_3D_TO_2D_L);
+ else if (mode == "right_left")
+ SetVideo3dMode(MODE_3D_TO_2D_R);
+ else if (mode == "top_bottom")
+ SetVideo3dMode(MODE_3D_TO_2D_T);
+ else if (mode == "bottom_top")
+ SetVideo3dMode(MODE_3D_TO_2D_B);
+ else
+ SetVideo3dMode(MODE_3D_DISABLE);
+ }
+ else if (m_stereo_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+ {
+ dst_rect.x2 *= 2.0;
+ //SetVideo3dMode(MODE_3D_LR);
+ SetVideo3dMode(MODE_3D_DISABLE);
+ }
+ else if (m_stereo_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+ {
+ dst_rect.y2 *= 2.0;
+ //SetVideo3dMode(MODE_3D_BT);
+ SetVideo3dMode(MODE_3D_DISABLE);
+ }
+ else
+ {
+ SetVideo3dMode(MODE_3D_DISABLE);
+ }
// goofy 0/1 based difference in aml axis coordinates.
// fix them.
dst_rect.x2--;
dst_rect.y2--;
- char video_axis[256] = {0};
+ char video_axis[256] = {};
sprintf(video_axis, "%d %d %d %d", (int)dst_rect.x1, (int)dst_rect.y1, (int)dst_rect.x2, (int)dst_rect.y2);
+
aml_set_sysfs_str("/sys/class/video/axis", video_axis);
// make sure we are in 'full stretch' so we can stretch
aml_set_sysfs_int("/sys/class/video/screen_mode", 1);
-/*
- CStdString rectangle;
- rectangle.Format("%i,%i,%i,%i",
- (int)dst_rect.x1, (int)dst_rect.y1,
- (int)dst_rect.Width(), (int)dst_rect.Height());
- CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:dst_rect(%s)", rectangle.c_str());
-*/
-
// we only get called once gui has changed to something
// that would show video playback, so show it.
ShowMainVideo(true);
#include "cores/dvdplayer/DVDStreamInfo.h"
#include "cores/VideoRenderers/RenderFeatures.h"
#include "guilib/Geometry.h"
+#include "rendering/RenderSystem.h"
#include "threads/Thread.h"
typedef struct am_private_t am_private_t;
void SetVideoBrightness(const int brightness);
void SetVideoSaturation(const int saturation);
void GetRenderFeatures(Features &renderFeatures);
+ void SetVideo3dMode(const int mode3d);
+ std::string GetStereoMode();
static void RenderFeaturesCallBack(const void *ctx, Features &renderFeatures);
void SetVideoRect(const CRect &SrcRect, const CRect &DestRect);
static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect);
CRect m_dst_rect;
int m_view_mode;
+ RENDER_STEREO_MODE m_stereo_mode;
+ RENDER_STEREO_VIEW m_stereo_view;
float m_zoom;
int m_contrast;
int m_brightness;
bool CDVDInputStreamFFmpeg::Open(const char* strFile, const std::string& content)
{
CFileItem item(strFile, false);
+ std::string selected;
if (item.IsInternetStream() && item.IsType(".m3u8"))
{
// get the available bandwidth and determine the most appropriate stream
int bandwidth = CSettings::Get().GetInt("network.bandwidth");
if(bandwidth <= 0)
bandwidth = INT_MAX;
- std::string selected = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(strFile, bandwidth);
+ selected = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(strFile, bandwidth);
if (selected.compare(strFile) != 0)
{
CLog::Log(LOGINFO, "CDVDInputStreamFFmpeg: Auto-selecting %s based on configured bandwidth.", selected.c_str());
#include "AnimatedGif.h"
#include "filesystem/SpecialProtocol.h"
#include "utils/EndianSwap.h"
+#include "utils/log.h"
#ifdef TARGET_WINDOWS
extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
NextImage->Init(gifid.Width, gifid.Height, LocalColorMap ? (gifid.PackedFields&7) + 1 : GlobalBPP);
+ /* verify that all the image is inside the screen dimensions */
+ if (gifid.xPos + gifid.Width > giflsd.ScreenWidth || gifid.yPos + gifid.Height > giflsd.ScreenHeight)
+ return 0;
+
// Fill NextImage Data
NextImage->xPos = gifid.xPos;
NextImage->yPos = gifid.yPos;
else
{
delete NextImage;
+ CLog::Log(LOGERROR, "CAnimatedGifSet::LoadGIF: gif file corrupt: %s", szFileName);
ERRORMSG("GIF File Corrupt");
}
short InitCodeSize, int AlignedWidth,
int Width, int Height, const int Interlace)
{
+ if (InitCodeSize < 1 || InitCodeSize >= LZW_MAXBITS)
+ return 0;
int n;
int row = 0, col = 0; // used to point output if Interlaced
int nPixels, maxPixels; // Output pixel counter
short OutCode; // Code to output
// Translation Table:
- short Prefix[4096] = {}; // Prefix: index of another Code
- unsigned char Suffix[4096] = {}; // Suffix: terminating character
+ short Prefix[LZW_SIZETABLE] = {}; // Prefix: index of another Code
+ unsigned char Suffix[LZW_SIZETABLE] = {}; // Suffix: terminating character
short FirstEntry; // Index of first free entry in table
short NextEntry; // Index of next free entry in table
- unsigned char OutStack[4097]; // Output buffer
+ unsigned char OutStack[LZW_SIZETABLE + 1]; // Output buffer
int OutIndex; // Characters in OutStack
int RowOffset; // Offset in output buffer for current row
// - Table Suffices contain the raw codes to be output
while (OutCode >= FirstEntry)
{
- if (OutIndex > 4096 || OutCode >= 4096)
+ if (OutIndex > LZW_SIZETABLE || OutCode >= LZW_SIZETABLE)
return 0;
OutStack[OutIndex++] = Suffix[OutCode]; // Add suffix to Output Stack
OutCode = Prefix[OutCode]; // Loop with preffix
}
// NOW OutCode IS A RAW CODE, ADD IT TO OUTPUT STACK.
- if (OutIndex > 4096)
+ if (OutIndex > LZW_SIZETABLE)
return 0;
OutStack[OutIndex++] = (unsigned char) OutCode;
// (EXCEPT IF PREVIOUS CODE WAS A CLEARCODE)
if (PrevCode != ClearCode)
{
+ // Prevent Translation table overflow:
+ if (NextEntry >= LZW_SIZETABLE)
+ return 0;
+
Prefix[NextEntry] = PrevCode;
Suffix[NextEntry] = (unsigned char) OutCode;
NextEntry++;
- // Prevent Translation table overflow:
- if (NextEntry >= 4096)
- return 0;
-
// INCREASE CodeSize IF NextEntry IS INVALID WITH CURRENT CodeSize
if (NextEntry >= (1 << CodeSize))
{
- if (CodeSize < 12) CodeSize++;
+ if (CodeSize < LZW_MAXBITS) CodeSize++;
else
{
;
#pragma pack(1)
+#define LZW_MAXBITS 12
+#define LZW_SIZETABLE (1<<LZW_MAXBITS)
+
/*!
\ingroup textures
\brief
m_rtl = iter->second.rtl;
m_vecUrls = iter->second.url;
m_vecIntervals = iter->second.interval;
- if (m_scrollInfo.pixelSpeed > 0 && m_rtl)
- m_scrollInfo.pixelSpeed *= -1;
- else if (m_scrollInfo.pixelSpeed < 0 && !m_rtl)
- m_scrollInfo.pixelSpeed *= -1;
+ m_scrollInfo.SetSpeed(m_label.scrollSpeed * (m_rtl ? -1 : 1));
}
dirty = true;
if ( m_stopped )
m_scrollInfo.SetSpeed(0);
else
- m_scrollInfo.SetSpeed(m_label.scrollSpeed);
+ m_scrollInfo.SetSpeed(m_label.scrollSpeed * (m_rtl ? -1 : 1));
if(m_label.font->UpdateScrollInfo(m_feed, m_scrollInfo))
dirty = true;
bool ret = WakeUpHost(server);
+ if (!ret) // extra log if we fail for some reason
+ CLog::Log(LOGWARNING,"WakeOnAccess failed to bring up [%s] - there may be trouble ahead !", hostName.c_str());
+
TouchHostEntry(hostName);
return ret;
}
}
+ // we have ping response ; just add extra wait-for-services before returning if requested
+
{
WaitCondition waitObj ; // wait uninteruptable fixed time for services ..
- if (ProgressDialogHelper::Success != dlg.ShowAndWait (waitObj, server.wait_services_sec, LOCALIZED(13032)))
- return false;
+ dlg.ShowAndWait (waitObj, server.wait_services_sec, LOCALIZED(13032));
CLog::Log(LOGNOTICE,"WakeOnAccess sequence completed, server started");
}
NPT_Cardinal res_index;
NPT_CHECK_LABEL_SEVERE(m_control->FindBestResource(m_delegate->m_device, *obj, res_index), failed);
-
+ // get the transport info to evaluate the TransportState to be able to
+ // determine whether we first need to call Stop()
timeout.Set(timeout.GetInitialTimeoutValue());
- /* dlna specifies that a return code of 705 should be returned
- * if TRANSPORT_STATE is not STOPPED or NO_MEDIA_PRESENT */
- NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device
- , m_delegate->m_instance
- , m_delegate), failed);
- NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed);
- NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed);
+ NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
+ , m_delegate->m_instance
+ , m_delegate), failed);
+ NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed);
+
+ if (m_delegate->m_trainfo.cur_transport_state != "NO_MEDIA_PRESENT" &&
+ m_delegate->m_trainfo.cur_transport_state != "STOPPED")
+ {
+ timeout.Set(timeout.GetInitialTimeoutValue());
+ NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device
+ , m_delegate->m_instance
+ , m_delegate), failed);
+ NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed);
+ NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed);
+ }
timeout.Set(timeout.GetInitialTimeoutValue());
#include "Application.h"
#include "WindowingFactory.h"
#include "settings/DisplaySettings.h"
+#include "cores/AudioEngine/AEFactory.h"
#undef BOOL
#import <Foundation/Foundation.h>
{
[self changeScreenSelector:dict];
}
+
+ // re-enumerate audio devices in that case too
+ // as we might gain passthrough capabilities via HDMI
+ CAEFactory::DeviceChange();
return true;
}
//--------------------------------------------------------------
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
-@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate, AVAudioSessionDelegate> {
+@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate> {
}
@end
{
ELOG(@"AVAudioSession setActive failed: %@", err);
}
- [[AVAudioSession sharedInstance] setDelegate:self];
-}
-
-- (void)beginInterruption
-{
- PRINT_SIGNATURE();
- [m_xbmcController beginInterruption];
-}
-- (void)endInterruptionWithFlags:(NSUInteger)flags
-{
- LOG(@"%s: %d", __PRETTY_FUNCTION__, flags);
- if (flags & AVAudioSessionInterruptionFlags_ShouldResume)
- {
- NSError *err = nil;
- if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
- {
- ELOG(@"AVAudioSession::endInterruption setActive failed: %@", err);
- }
- [m_xbmcController endInterruption];
- }
}
- (void)dealloc
UIInterfaceOrientation orientation;
bool m_isPlayingBeforeInactive;
- bool m_isInterrupted;
UIBackgroundTaskIdentifier m_bgTask;
NSTimer *m_networkAutoSuspendTimer;
IOSPlaybackState m_playbackState;
- (void) enterBackground;
- (void) enterForeground;
- (void) becomeInactive;
-- (void) beginInterruption;
-- (void) endInterruption;
- (void) setIOSNowPlayingInfo:(NSDictionary *)info;
- (void) sendKey: (XBMCKey) key;
- (void) observeDefaultCenterStuff: (NSNotification *) notification;
#include "MusicInfoTag.h"
#include "SpecialProtocol.h"
#include "PlayList.h"
-#include "AEFactory.h"
#include "ApplicationMessenger.h"
#include "Application.h"
#include "interfaces/AnnouncementManager.h"
}
else if (msg == "OnPause")
{
- CAEFactory::Suspend();
[g_xbmcController performSelectorOnMainThread:@selector(onPause:) withObject:[dict valueForKey:@"item"] waitUntilDone:NO];
}
else if (msg == "OnStop")
{
- CAEFactory::Suspend();
[g_xbmcController performSelectorOnMainThread:@selector(onStop:) withObject:[dict valueForKey:@"item"] waitUntilDone:NO];
}
}
return ( nil );
m_isPlayingBeforeInactive = NO;
- m_isInterrupted = NO;
m_bgTask = UIBackgroundTaskInvalid;
m_playbackState = IOS_PLAYBACK_STOPPED;
CApplicationMessenger::Get().MediaUnPause();
m_isPlayingBeforeInactive = NO;
}
- m_isInterrupted = NO;
}
- (void)becomeInactive
{
- LOG(@"%s: was interrupted: %d", __PRETTY_FUNCTION__, m_isInterrupted);
// if we were interrupted, already paused here
// else if user background us or lock screen, only pause video here, audio keep playing.
if (g_application.m_pPlayer->IsPlayingVideo() && !g_application.m_pPlayer->IsPaused())
// check whether we need disable network auto suspend.
[self rescheduleNetworkAutoSuspend];
}
-
-- (void)beginInterruption
-{
- PRINT_SIGNATURE();
- m_isInterrupted = YES;
- CAEFactory::Suspend();
-}
-
-- (void)endInterruption
-{
- PRINT_SIGNATURE();
- if (CAEFactory::IsSuspended())
- CAEFactory::Resume();
-}
//--------------------------------------------------------------
- (void)pauseAnimation
{
#include "network/WakeOnAccess.h"
#if defined(TARGET_DARWIN_OSX)
#include "osx/XBMCHelper.h"
-#include "cores/AudioEngine/Engines/CoreAudio/CoreAudioHardware.h"
#endif // defined(TARGET_DARWIN_OSX)
#if defined(TARGET_DARWIN)
#include "osx/DarwinUtils.h"
#endif
#endif
-#if defined(TARGET_DARWIN)
- #if !defined(TARGET_DARWIN_IOS)
- CStdString defaultAudioDeviceName;
- CCoreAudioHardware::GetOutputDeviceName(defaultAudioDeviceName);
- ((CSettingString*)m_settingsManager->GetSetting("audiooutput.audiodevice"))->SetDefault(defaultAudioDeviceName);
- ((CSettingString*)m_settingsManager->GetSetting("audiooutput.passthroughdevice"))->SetDefault(defaultAudioDeviceName);
- #endif
-#elif !defined(TARGET_WINDOWS)
+#if !defined(TARGET_WINDOWS)
((CSettingString*)m_settingsManager->GetSetting("audiooutput.audiodevice"))->SetDefault(CAEFactory::GetDefaultDevice(false));
((CSettingString*)m_settingsManager->GetSetting("audiooutput.passthroughdevice"))->SetDefault(CAEFactory::GetDefaultDevice(true));
#endif
system("su -c chmod 666 /sys/class/video/disable_video");
system("su -c chmod 666 /sys/class/tsync/pts_pcrscr");
system("su -c chmod 666 /sys/class/audiodsp/digital_raw");
+ system("su -c chmod 666 /sys/class/ppmgr/ppmgr_3d_mode");
system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
system("su -c chmod 666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
while (ReceiveOutMessage(&msg))
msg->Release();
}
+
+void Protocol::PurgeIn(int signal)
+{
+ Message *msg;
+ std::queue<Message*> msgs;
+
+ CSingleLock lock(criticalSection);
+
+ while (!inMessages.empty())
+ {
+ msg = inMessages.front();
+ inMessages.pop();
+ if (msg->signal != signal)
+ msgs.push(msg);
+ }
+ while (!msgs.empty())
+ {
+ msg = msgs.front();
+ msgs.pop();
+ inMessages.push(msg);
+ }
+}
+
+void Protocol::PurgeOut(int signal)
+{
+ Message *msg;
+ std::queue<Message*> msgs;
+
+ CSingleLock lock(criticalSection);
+
+ while (!outMessages.empty())
+ {
+ msg = outMessages.front();
+ outMessages.pop();
+ if (msg->signal != signal)
+ msgs.push(msg);
+ }
+ while (!msgs.empty())
+ {
+ msg = msgs.front();
+ msgs.pop();
+ outMessages.push(msg);
+ }
+}
bool ReceiveOutMessage(Message **msg);
bool ReceiveInMessage(Message **msg);
void Purge();
+ void PurgeIn(int signal);
+ void PurgeOut(int signal);
void DeferIn(bool value) {inDefered = value;};
void DeferOut(bool value) {outDefered = value;};
void Lock() {criticalSection.lock();};
virtual bool BeginRender();
virtual bool EndRender();
virtual int GetNumScreens();
+ virtual int GetCurrentScreen();
void InitDisplayLink(void);
void DeinitDisplayLink(void);
#else
#import "ios/XBMCController.h"
#endif
+#import "osx/IOSScreenManager.h"
#include "osx/DarwinUtils.h"
#import <dlfcn.h>
return [[UIScreen screens] count];
}
+int CWinSystemIOS::GetCurrentScreen()
+{
+ int idx = 0;
+ if ([[IOSScreenManager sharedInstance] isExternalScreen])
+ {
+ idx = 1;
+ }
+ return idx;
+}
+
bool CWinSystemIOS::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
{
// Figure out the screen size. (default to main screen)
{
if (m_can_display_switch)
{
- // send pre-configuration change now and do not
- // wait for switch videomode callback. This gives just
- // a little more advanced notice of the display pre-change.
- if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
- CheckDisplayChanging(kCGDisplayBeginConfigurationFlag);
-
// switch videomode
SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
m_lastDisplayNr = res.iScreen;