coreaudio.cpp 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 1999-2007 by authors.
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include "coreaudio.h"
  22. #include <cinttypes>
  23. #include <cmath>
  24. #include <functional>
  25. #include <memory>
  26. #include <optional>
  27. #include <stdint.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string>
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include <vector>
  34. #include "alnumeric.h"
  35. #include "alstring.h"
  36. #include "core/converter.h"
  37. #include "core/device.h"
  38. #include "core/logging.h"
  39. #include "fmt/core.h"
  40. #include "ringbuffer.h"
  41. #include <AudioUnit/AudioUnit.h>
  42. #include <AudioToolbox/AudioToolbox.h>
  43. #if TARGET_OS_IOS || TARGET_OS_TV
  44. #define CAN_ENUMERATE 0
  45. #else
  46. #include <IOKit/audio/IOAudioTypes.h>
  47. #define CAN_ENUMERATE 1
  48. #endif
  49. namespace {
  50. constexpr auto OutputElement = 0;
  51. constexpr auto InputElement = 1;
  52. // These following arrays should always be defined in ascending AudioChannelLabel value order
  53. constexpr std::array<AudioChannelLabel, 1> MonoChanMap { kAudioChannelLabel_Mono };
  54. constexpr std::array<AudioChannelLabel, 2> StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right};
  55. constexpr std::array<AudioChannelLabel, 4> QuadChanMap {
  56. kAudioChannelLabel_Left, kAudioChannelLabel_Right,
  57. kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
  58. };
  59. constexpr std::array<AudioChannelLabel, 6> X51ChanMap {
  60. kAudioChannelLabel_Left, kAudioChannelLabel_Right,
  61. kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
  62. kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
  63. };
  64. constexpr std::array<AudioChannelLabel, 6> X51RearChanMap {
  65. kAudioChannelLabel_Left, kAudioChannelLabel_Right,
  66. kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
  67. kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
  68. };
  69. constexpr std::array<AudioChannelLabel, 7> X61ChanMap {
  70. kAudioChannelLabel_Left, kAudioChannelLabel_Right,
  71. kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
  72. kAudioChannelLabel_CenterSurround,
  73. kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
  74. };
  75. constexpr std::array<AudioChannelLabel, 8> X71ChanMap {
  76. kAudioChannelLabel_Left, kAudioChannelLabel_Right,
  77. kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
  78. kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround,
  79. kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter
  80. };
  81. struct FourCCPrinter {
  82. char mString[sizeof(UInt32) + 1]{};
  83. explicit constexpr FourCCPrinter(UInt32 code) noexcept
  84. {
  85. for(size_t i{0};i < sizeof(UInt32);++i)
  86. {
  87. const auto ch = static_cast<char>(code & 0xff);
  88. /* If this breaks early it'll leave the first byte null, to get
  89. * read as a 0-length string.
  90. */
  91. if(ch <= 0x1f || ch >= 0x7f)
  92. break;
  93. mString[sizeof(UInt32)-1-i] = ch;
  94. code >>= 8;
  95. }
  96. }
  97. explicit constexpr FourCCPrinter(OSStatus code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
  98. constexpr const char *c_str() const noexcept { return mString; }
  99. };
  100. #if CAN_ENUMERATE
  101. struct DeviceEntry {
  102. AudioDeviceID mId;
  103. std::string mName;
  104. };
  105. std::vector<DeviceEntry> PlaybackList;
  106. std::vector<DeviceEntry> CaptureList;
  107. OSStatus GetHwProperty(AudioHardwarePropertyID propId, UInt32 dataSize, void *propData)
  108. {
  109. const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
  110. kAudioObjectPropertyElementMaster};
  111. return AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, nullptr, &dataSize,
  112. propData);
  113. }
  114. OSStatus GetHwPropertySize(AudioHardwarePropertyID propId, UInt32 *outSize)
  115. {
  116. const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
  117. kAudioObjectPropertyElementMaster};
  118. return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, nullptr, outSize);
  119. }
  120. OSStatus GetDevProperty(AudioDeviceID devId, AudioDevicePropertyID propId, bool isCapture,
  121. UInt32 elem, UInt32 dataSize, void *propData)
  122. {
  123. static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
  124. kAudioDevicePropertyScopeInput};
  125. const AudioObjectPropertyAddress addr{propId, scopes[isCapture], elem};
  126. return AudioObjectGetPropertyData(devId, &addr, 0, nullptr, &dataSize, propData);
  127. }
  128. OSStatus GetDevPropertySize(AudioDeviceID devId, AudioDevicePropertyID inPropertyID,
  129. bool isCapture, UInt32 elem, UInt32 *outSize)
  130. {
  131. static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
  132. kAudioDevicePropertyScopeInput};
  133. const AudioObjectPropertyAddress addr{inPropertyID, scopes[isCapture], elem};
  134. return AudioObjectGetPropertyDataSize(devId, &addr, 0, nullptr, outSize);
  135. }
  136. std::string GetDeviceName(AudioDeviceID devId)
  137. {
  138. std::string devname;
  139. CFStringRef nameRef;
  140. /* Try to get the device name as a CFString, for Unicode name support. */
  141. OSStatus err{GetDevProperty(devId, kAudioDevicePropertyDeviceNameCFString, false, 0,
  142. sizeof(nameRef), &nameRef)};
  143. if(err == noErr)
  144. {
  145. const CFIndex propSize{CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
  146. kCFStringEncodingUTF8)};
  147. devname.resize(static_cast<size_t>(propSize)+1, '\0');
  148. CFStringGetCString(nameRef, &devname[0], propSize+1, kCFStringEncodingUTF8);
  149. CFRelease(nameRef);
  150. }
  151. else
  152. {
  153. /* If that failed, just get the C string. Hopefully there's nothing bad
  154. * with this.
  155. */
  156. UInt32 propSize{};
  157. if(GetDevPropertySize(devId, kAudioDevicePropertyDeviceName, false, 0, &propSize))
  158. return devname;
  159. devname.resize(propSize+1, '\0');
  160. if(GetDevProperty(devId, kAudioDevicePropertyDeviceName, false, 0, propSize, &devname[0]))
  161. {
  162. devname.clear();
  163. return devname;
  164. }
  165. }
  166. /* Clear extraneous nul chars that may have been written with the name
  167. * string, and return it.
  168. */
  169. while(!devname.empty() && !devname.back())
  170. devname.pop_back();
  171. return devname;
  172. }
  173. auto GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) -> UInt32
  174. {
  175. auto propSize = UInt32{};
  176. auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
  177. &propSize);
  178. if(err)
  179. {
  180. ERR("kAudioDevicePropertyStreamConfiguration size query failed: '{}' ({})",
  181. FourCCPrinter{err}.c_str(), err);
  182. return 0;
  183. }
  184. auto buflist_data = std::make_unique<char[]>(propSize);
  185. auto *buflist = reinterpret_cast<AudioBufferList*>(buflist_data.get());
  186. err = GetDevProperty(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, propSize,
  187. buflist);
  188. if(err)
  189. {
  190. ERR("kAudioDevicePropertyStreamConfiguration query failed: '{}' ({})",
  191. FourCCPrinter{err}.c_str(), err);
  192. return 0;
  193. }
  194. auto numChannels = UInt32{0};
  195. for(size_t i{0};i < buflist->mNumberBuffers;++i)
  196. numChannels += buflist->mBuffers[i].mNumberChannels;
  197. return numChannels;
  198. }
  199. void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
  200. {
  201. UInt32 propSize{};
  202. if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
  203. {
  204. ERR("Failed to get device list size: {}", err);
  205. return;
  206. }
  207. auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
  208. if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
  209. {
  210. ERR("Failed to get device list: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  211. return;
  212. }
  213. std::vector<DeviceEntry> newdevs;
  214. newdevs.reserve(devIds.size());
  215. AudioDeviceID defaultId{kAudioDeviceUnknown};
  216. GetHwProperty(isCapture ? kAudioHardwarePropertyDefaultInputDevice :
  217. kAudioHardwarePropertyDefaultOutputDevice, sizeof(defaultId), &defaultId);
  218. if(defaultId != kAudioDeviceUnknown)
  219. {
  220. newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
  221. const auto &entry = newdevs.back();
  222. TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
  223. }
  224. for(const AudioDeviceID devId : devIds)
  225. {
  226. if(devId == kAudioDeviceUnknown)
  227. continue;
  228. auto match_devid = [devId](const DeviceEntry &entry) noexcept -> bool
  229. { return entry.mId == devId; };
  230. auto match = std::find_if(newdevs.cbegin(), newdevs.cend(), match_devid);
  231. if(match != newdevs.cend()) continue;
  232. auto numChannels = GetDeviceChannelCount(devId, isCapture);
  233. if(numChannels > 0)
  234. {
  235. newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
  236. const auto &entry = newdevs.back();
  237. TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
  238. }
  239. }
  240. if(newdevs.size() > 1)
  241. {
  242. /* Rename entries that have matching names, by appending '#2', '#3',
  243. * etc, as needed.
  244. */
  245. for(auto curitem = newdevs.begin()+1;curitem != newdevs.end();++curitem)
  246. {
  247. auto check_match = [curitem](const DeviceEntry &entry) -> bool
  248. { return entry.mName == curitem->mName; };
  249. if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
  250. {
  251. auto name = std::string{curitem->mName};
  252. auto count = 1_uz;
  253. auto check_name = [&name](const DeviceEntry &entry) -> bool
  254. { return entry.mName == name; };
  255. do {
  256. name = fmt::format("{} #{}", curitem->mName, ++count);
  257. } while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
  258. curitem->mName = std::move(name);
  259. }
  260. }
  261. }
  262. newdevs.shrink_to_fit();
  263. newdevs.swap(list);
  264. }
  265. struct DeviceHelper {
  266. DeviceHelper()
  267. {
  268. AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
  269. kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
  270. OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
  271. if (status != noErr)
  272. ERR("AudioObjectAddPropertyListener fail: {}", status);
  273. }
  274. ~DeviceHelper()
  275. {
  276. AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
  277. kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
  278. OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
  279. if (status != noErr)
  280. ERR("AudioObjectRemovePropertyListener fail: {}", status);
  281. }
  282. static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
  283. const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/)
  284. {
  285. for(UInt32 i = 0; i < inNumberAddresses; ++i)
  286. {
  287. switch(inAddresses[i].mSelector)
  288. {
  289. case kAudioHardwarePropertyDefaultOutputDevice:
  290. case kAudioHardwarePropertyDefaultSystemOutputDevice:
  291. alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
  292. "Default playback device changed: "+std::to_string(inAddresses[i].mSelector));
  293. break;
  294. case kAudioHardwarePropertyDefaultInputDevice:
  295. alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
  296. "Default capture device changed: "+std::to_string(inAddresses[i].mSelector));
  297. break;
  298. }
  299. }
  300. return noErr;
  301. }
  302. };
  303. static std::optional<DeviceHelper> sDeviceHelper;
  304. #else
  305. static constexpr char ca_device[] = "CoreAudio Default";
  306. #endif
  307. struct CoreAudioPlayback final : public BackendBase {
  308. explicit CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  309. ~CoreAudioPlayback() override;
  310. OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
  311. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
  312. AudioBufferList *ioData) noexcept;
  313. void open(std::string_view name) override;
  314. bool reset() override;
  315. void start() override;
  316. void stop() override;
  317. AudioUnit mAudioUnit{};
  318. uint mFrameSize{0u};
  319. AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
  320. };
  321. CoreAudioPlayback::~CoreAudioPlayback()
  322. {
  323. AudioUnitUninitialize(mAudioUnit);
  324. AudioComponentInstanceDispose(mAudioUnit);
  325. }
  326. OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32,
  327. UInt32, AudioBufferList *ioData) noexcept
  328. {
  329. for(size_t i{0};i < ioData->mNumberBuffers;++i)
  330. {
  331. auto &buffer = ioData->mBuffers[i];
  332. mDevice->renderSamples(buffer.mData, buffer.mDataByteSize/mFrameSize,
  333. buffer.mNumberChannels);
  334. }
  335. return noErr;
  336. }
  337. void CoreAudioPlayback::open(std::string_view name)
  338. {
  339. #if CAN_ENUMERATE
  340. AudioDeviceID audioDevice{kAudioDeviceUnknown};
  341. if(name.empty())
  342. GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
  343. &audioDevice);
  344. else
  345. {
  346. if(PlaybackList.empty())
  347. EnumerateDevices(PlaybackList, false);
  348. auto find_name = [name](const DeviceEntry &entry) -> bool
  349. { return entry.mName == name; };
  350. auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
  351. if(devmatch == PlaybackList.cend())
  352. throw al::backend_exception{al::backend_error::NoDevice,
  353. "Device name \"{}\" not found", name};
  354. audioDevice = devmatch->mId;
  355. }
  356. #else
  357. if(name.empty())
  358. name = ca_device;
  359. else if(name != ca_device)
  360. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
  361. name};
  362. #endif
  363. /* open the default output unit */
  364. AudioComponentDescription desc{};
  365. desc.componentType = kAudioUnitType_Output;
  366. #if CAN_ENUMERATE
  367. desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
  368. kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
  369. #else
  370. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  371. #endif
  372. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  373. desc.componentFlags = 0;
  374. desc.componentFlagsMask = 0;
  375. AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
  376. if(comp == nullptr)
  377. throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
  378. AudioUnit audioUnit{};
  379. OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
  380. if(err != noErr)
  381. throw al::backend_exception{al::backend_error::NoDevice,
  382. "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  383. #if CAN_ENUMERATE
  384. if(audioDevice != kAudioDeviceUnknown)
  385. AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
  386. kAudioUnitScope_Global, OutputElement, &audioDevice, sizeof(AudioDeviceID));
  387. #endif
  388. err = AudioUnitInitialize(audioUnit);
  389. if(err != noErr)
  390. throw al::backend_exception{al::backend_error::DeviceError,
  391. "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  392. /* WARNING: I don't know if "valid" audio unit values are guaranteed to be
  393. * non-0. If not, this logic is broken.
  394. */
  395. if(mAudioUnit)
  396. {
  397. AudioUnitUninitialize(mAudioUnit);
  398. AudioComponentInstanceDispose(mAudioUnit);
  399. }
  400. mAudioUnit = audioUnit;
  401. #if CAN_ENUMERATE
  402. if(!name.empty())
  403. mDeviceName = name;
  404. else
  405. {
  406. UInt32 propSize{sizeof(audioDevice)};
  407. audioDevice = kAudioDeviceUnknown;
  408. AudioUnitGetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
  409. kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize);
  410. std::string devname{GetDeviceName(audioDevice)};
  411. if(!devname.empty()) mDeviceName = std::move(devname);
  412. else mDeviceName = "Unknown Device Name";
  413. }
  414. if(audioDevice != kAudioDeviceUnknown)
  415. {
  416. UInt32 type{};
  417. err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
  418. kAudioObjectPropertyElementMaster, sizeof(type), &type);
  419. if(err != noErr)
  420. WARN("Failed to get audio device type: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  421. else
  422. {
  423. TRACE("Got device type '{}'", FourCCPrinter{type}.c_str());
  424. mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
  425. }
  426. }
  427. #else
  428. mDeviceName = name;
  429. #endif
  430. }
  431. bool CoreAudioPlayback::reset()
  432. {
  433. OSStatus err{AudioUnitUninitialize(mAudioUnit)};
  434. if(err != noErr)
  435. ERR("AudioUnitUninitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  436. /* retrieve default output unit's properties (output side) */
  437. AudioStreamBasicDescription streamFormat{};
  438. UInt32 size{sizeof(streamFormat)};
  439. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
  440. OutputElement, &streamFormat, &size);
  441. if(err != noErr || size != sizeof(streamFormat))
  442. {
  443. ERR("AudioUnitGetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
  444. err);
  445. return false;
  446. }
  447. /* Use the sample rate from the output unit's current parameters, but reset
  448. * everything else.
  449. */
  450. if(mDevice->mSampleRate != streamFormat.mSampleRate)
  451. {
  452. mDevice->mBufferSize = static_cast<uint>(mDevice->mBufferSize*streamFormat.mSampleRate/
  453. mDevice->mSampleRate + 0.5);
  454. mDevice->mSampleRate = static_cast<uint>(streamFormat.mSampleRate);
  455. }
  456. struct ChannelMap {
  457. DevFmtChannels fmt;
  458. al::span<const AudioChannelLabel> map;
  459. bool is_51rear;
  460. };
  461. static constexpr std::array<ChannelMap,7> chanmaps{{
  462. { DevFmtX71, X71ChanMap, false },
  463. { DevFmtX61, X61ChanMap, false },
  464. { DevFmtX51, X51ChanMap, false },
  465. { DevFmtX51, X51RearChanMap, true },
  466. { DevFmtQuad, QuadChanMap, false },
  467. { DevFmtStereo, StereoChanMap, false },
  468. { DevFmtMono, MonoChanMap, false }
  469. }};
  470. if(!mDevice->Flags.test(ChannelsRequest))
  471. {
  472. auto propSize = UInt32{};
  473. auto writable = Boolean{};
  474. err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
  475. kAudioUnitScope_Output, OutputElement, &propSize, &writable);
  476. if(err == noErr)
  477. {
  478. auto layout_data = std::make_unique<char[]>(propSize);
  479. auto *layout = reinterpret_cast<AudioChannelLayout*>(layout_data.get());
  480. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
  481. kAudioUnitScope_Output, OutputElement, layout, &propSize);
  482. if(err == noErr)
  483. {
  484. auto descs = al::span{std::data(layout->mChannelDescriptions),
  485. layout->mNumberChannelDescriptions};
  486. auto labels = std::vector<AudioChannelLayoutTag>(descs.size());
  487. std::transform(descs.begin(), descs.end(), labels.begin(),
  488. std::mem_fn(&AudioChannelDescription::mChannelLabel));
  489. sort(labels.begin(), labels.end());
  490. auto check_labels = [&labels](const ChannelMap &chanmap) -> bool
  491. {
  492. return std::includes(labels.begin(), labels.end(), chanmap.map.begin(),
  493. chanmap.map.end());
  494. };
  495. auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(), check_labels);
  496. if(chaniter != chanmaps.cend())
  497. mDevice->FmtChans = chaniter->fmt;
  498. }
  499. }
  500. }
  501. /* TODO: Also set kAudioUnitProperty_AudioChannelLayout according to the AL
  502. * device's channel configuration.
  503. */
  504. streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
  505. streamFormat.mFramesPerPacket = 1;
  506. streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
  507. streamFormat.mFormatID = kAudioFormatLinearPCM;
  508. switch(mDevice->FmtType)
  509. {
  510. case DevFmtUByte:
  511. mDevice->FmtType = DevFmtByte;
  512. /* fall-through */
  513. case DevFmtByte:
  514. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  515. streamFormat.mBitsPerChannel = 8;
  516. break;
  517. case DevFmtUShort:
  518. mDevice->FmtType = DevFmtShort;
  519. /* fall-through */
  520. case DevFmtShort:
  521. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  522. streamFormat.mBitsPerChannel = 16;
  523. break;
  524. case DevFmtUInt:
  525. mDevice->FmtType = DevFmtInt;
  526. /* fall-through */
  527. case DevFmtInt:
  528. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  529. streamFormat.mBitsPerChannel = 32;
  530. break;
  531. case DevFmtFloat:
  532. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
  533. streamFormat.mBitsPerChannel = 32;
  534. break;
  535. }
  536. streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame*streamFormat.mBitsPerChannel/8;
  537. streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame*streamFormat.mFramesPerPacket;
  538. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  539. OutputElement, &streamFormat, sizeof(streamFormat));
  540. if(err != noErr)
  541. {
  542. ERR("AudioUnitSetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
  543. err);
  544. return false;
  545. }
  546. setDefaultWFXChannelOrder();
  547. /* setup callback */
  548. mFrameSize = mDevice->frameSizeFromFmt();
  549. AURenderCallbackStruct input{};
  550. input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
  551. { return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
  552. input.inputProcRefCon = this;
  553. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
  554. kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
  555. if(err != noErr)
  556. {
  557. ERR("AudioUnitSetProperty(SetRenderCallback) failed: '{}' ({})",
  558. FourCCPrinter{err}.c_str(), err);
  559. return false;
  560. }
  561. /* init the default audio unit... */
  562. err = AudioUnitInitialize(mAudioUnit);
  563. if(err != noErr)
  564. {
  565. ERR("AudioUnitInitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  566. return false;
  567. }
  568. return true;
  569. }
  570. void CoreAudioPlayback::start()
  571. {
  572. const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
  573. if(err != noErr)
  574. throw al::backend_exception{al::backend_error::DeviceError,
  575. "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  576. }
  577. void CoreAudioPlayback::stop()
  578. {
  579. OSStatus err{AudioOutputUnitStop(mAudioUnit)};
  580. if(err != noErr)
  581. ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  582. }
  583. struct CoreAudioCapture final : public BackendBase {
  584. explicit CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
  585. ~CoreAudioCapture() override;
  586. OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
  587. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
  588. UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
  589. void open(std::string_view name) override;
  590. void start() override;
  591. void stop() override;
  592. void captureSamples(std::byte *buffer, uint samples) override;
  593. uint availableSamples() override;
  594. AudioUnit mAudioUnit{0};
  595. uint mFrameSize{0u};
  596. AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
  597. SampleConverterPtr mConverter;
  598. std::vector<char> mCaptureData;
  599. RingBufferPtr mRing{nullptr};
  600. };
  601. CoreAudioCapture::~CoreAudioCapture()
  602. {
  603. if(mAudioUnit)
  604. AudioComponentInstanceDispose(mAudioUnit);
  605. mAudioUnit = 0;
  606. }
  607. OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
  608. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
  609. AudioBufferList*) noexcept
  610. {
  611. union {
  612. std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
  613. AudioBufferList list;
  614. } audiobuf{};
  615. audiobuf.list.mNumberBuffers = 1;
  616. audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
  617. audiobuf.list.mBuffers[0].mData = mCaptureData.data();
  618. audiobuf.list.mBuffers[0].mDataByteSize = static_cast<UInt32>(mCaptureData.size());
  619. OSStatus err{AudioUnitRender(mAudioUnit, ioActionFlags, inTimeStamp, inBusNumber,
  620. inNumberFrames, &audiobuf.list)};
  621. if(err != noErr)
  622. {
  623. ERR("AudioUnitRender capture error: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  624. return err;
  625. }
  626. std::ignore = mRing->write(mCaptureData.data(), inNumberFrames);
  627. return noErr;
  628. }
  629. void CoreAudioCapture::open(std::string_view name)
  630. {
  631. #if CAN_ENUMERATE
  632. AudioDeviceID audioDevice{kAudioDeviceUnknown};
  633. if(name.empty())
  634. GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
  635. &audioDevice);
  636. else
  637. {
  638. if(CaptureList.empty())
  639. EnumerateDevices(CaptureList, true);
  640. auto find_name = [name](const DeviceEntry &entry) -> bool
  641. { return entry.mName == name; };
  642. auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
  643. if(devmatch == CaptureList.cend())
  644. throw al::backend_exception{al::backend_error::NoDevice,
  645. "Device name \"{}\" not found", name};
  646. audioDevice = devmatch->mId;
  647. }
  648. #else
  649. if(name.empty())
  650. name = ca_device;
  651. else if(name != ca_device)
  652. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
  653. name};
  654. #endif
  655. AudioComponentDescription desc{};
  656. desc.componentType = kAudioUnitType_Output;
  657. #if CAN_ENUMERATE
  658. desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
  659. kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
  660. #else
  661. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  662. #endif
  663. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  664. desc.componentFlags = 0;
  665. desc.componentFlagsMask = 0;
  666. // Search for component with given description
  667. AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
  668. if(comp == NULL)
  669. throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
  670. // Open the component
  671. OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
  672. if(err != noErr)
  673. throw al::backend_exception{al::backend_error::NoDevice,
  674. "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  675. // Turn off AudioUnit output
  676. UInt32 enableIO{0};
  677. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
  678. kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
  679. if(err != noErr)
  680. throw al::backend_exception{al::backend_error::DeviceError,
  681. "Could not disable audio unit output property: '{}' ({})", FourCCPrinter{err}.c_str(),
  682. err};
  683. // Turn on AudioUnit input
  684. enableIO = 1;
  685. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
  686. kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
  687. if(err != noErr)
  688. throw al::backend_exception{al::backend_error::DeviceError,
  689. "Could not enable audio unit input property: '{}' ({})", FourCCPrinter{err}.c_str(),
  690. err};
  691. #if CAN_ENUMERATE
  692. if(audioDevice != kAudioDeviceUnknown)
  693. AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
  694. kAudioUnitScope_Global, InputElement, &audioDevice, sizeof(AudioDeviceID));
  695. #endif
  696. // set capture callback
  697. AURenderCallbackStruct input{};
  698. input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
  699. { return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
  700. input.inputProcRefCon = this;
  701. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
  702. kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
  703. if(err != noErr)
  704. throw al::backend_exception{al::backend_error::DeviceError,
  705. "Could not set capture callback: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  706. // Disable buffer allocation for capture
  707. UInt32 flag{0};
  708. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_ShouldAllocateBuffer,
  709. kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
  710. if(err != noErr)
  711. throw al::backend_exception{al::backend_error::DeviceError,
  712. "Could not disable buffer allocation property: '{}' ({})", FourCCPrinter{err}.c_str(),
  713. err};
  714. // Initialize the device
  715. err = AudioUnitInitialize(mAudioUnit);
  716. if(err != noErr)
  717. throw al::backend_exception{al::backend_error::DeviceError,
  718. "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  719. // Get the hardware format
  720. AudioStreamBasicDescription hardwareFormat{};
  721. UInt32 propertySize{sizeof(hardwareFormat)};
  722. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  723. InputElement, &hardwareFormat, &propertySize);
  724. if(err != noErr || propertySize != sizeof(hardwareFormat))
  725. throw al::backend_exception{al::backend_error::DeviceError,
  726. "Could not get input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  727. // Set up the requested format description
  728. AudioStreamBasicDescription requestedFormat{};
  729. switch(mDevice->FmtType)
  730. {
  731. case DevFmtByte:
  732. requestedFormat.mBitsPerChannel = 8;
  733. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  734. break;
  735. case DevFmtUByte:
  736. requestedFormat.mBitsPerChannel = 8;
  737. requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
  738. break;
  739. case DevFmtShort:
  740. requestedFormat.mBitsPerChannel = 16;
  741. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
  742. | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  743. break;
  744. case DevFmtUShort:
  745. requestedFormat.mBitsPerChannel = 16;
  746. requestedFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  747. break;
  748. case DevFmtInt:
  749. requestedFormat.mBitsPerChannel = 32;
  750. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
  751. | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  752. break;
  753. case DevFmtUInt:
  754. requestedFormat.mBitsPerChannel = 32;
  755. requestedFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  756. break;
  757. case DevFmtFloat:
  758. requestedFormat.mBitsPerChannel = 32;
  759. requestedFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian
  760. | kAudioFormatFlagIsPacked;
  761. break;
  762. }
  763. switch(mDevice->FmtChans)
  764. {
  765. case DevFmtMono:
  766. requestedFormat.mChannelsPerFrame = 1;
  767. break;
  768. case DevFmtStereo:
  769. requestedFormat.mChannelsPerFrame = 2;
  770. break;
  771. case DevFmtQuad:
  772. case DevFmtX51:
  773. case DevFmtX61:
  774. case DevFmtX71:
  775. case DevFmtX714:
  776. case DevFmtX7144:
  777. case DevFmtX3D71:
  778. case DevFmtAmbi3D:
  779. throw al::backend_exception{al::backend_error::DeviceError, "{} not supported",
  780. DevFmtChannelsString(mDevice->FmtChans)};
  781. }
  782. requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
  783. requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
  784. requestedFormat.mSampleRate = mDevice->mSampleRate;
  785. requestedFormat.mFormatID = kAudioFormatLinearPCM;
  786. requestedFormat.mReserved = 0;
  787. requestedFormat.mFramesPerPacket = 1;
  788. // save requested format description for later use
  789. mFormat = requestedFormat;
  790. mFrameSize = mDevice->frameSizeFromFmt();
  791. // Use intermediate format for sample rate conversion (outputFormat)
  792. // Set sample rate to the same as hardware for resampling later
  793. AudioStreamBasicDescription outputFormat{requestedFormat};
  794. outputFormat.mSampleRate = hardwareFormat.mSampleRate;
  795. // The output format should be the requested format, but using the hardware sample rate
  796. // This is because the AudioUnit will automatically scale other properties, except for sample rate
  797. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
  798. InputElement, &outputFormat, sizeof(outputFormat));
  799. if(err != noErr)
  800. throw al::backend_exception{al::backend_error::DeviceError,
  801. "Could not set input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  802. /* Calculate the minimum AudioUnit output format frame count for the pre-
  803. * conversion ring buffer. Ensure at least 100ms for the total buffer.
  804. */
  805. double srateScale{outputFormat.mSampleRate / mDevice->mSampleRate};
  806. auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->mBufferSize*srateScale)),
  807. static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
  808. FrameCount64 += MaxResamplerPadding;
  809. if(FrameCount64 > std::numeric_limits<int32_t>::max())
  810. throw al::backend_exception{al::backend_error::DeviceError,
  811. "Calculated frame count is too large: {}", FrameCount64};
  812. UInt32 outputFrameCount{};
  813. propertySize = sizeof(outputFrameCount);
  814. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
  815. kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
  816. if(err != noErr || propertySize != sizeof(outputFrameCount))
  817. throw al::backend_exception{al::backend_error::DeviceError,
  818. "Could not get input frame count: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  819. mCaptureData.resize(outputFrameCount * mFrameSize);
  820. outputFrameCount = static_cast<UInt32>(std::max(uint64_t{outputFrameCount}, FrameCount64));
  821. mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
  822. /* Set up sample converter if needed */
  823. if(outputFormat.mSampleRate != mDevice->mSampleRate)
  824. mConverter = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
  825. mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
  826. mDevice->mSampleRate, Resampler::FastBSinc24);
  827. #if CAN_ENUMERATE
  828. if(!name.empty())
  829. mDeviceName = name;
  830. else
  831. {
  832. UInt32 propSize{sizeof(audioDevice)};
  833. audioDevice = kAudioDeviceUnknown;
  834. AudioUnitGetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
  835. kAudioUnitScope_Global, InputElement, &audioDevice, &propSize);
  836. std::string devname{GetDeviceName(audioDevice)};
  837. if(!devname.empty()) mDeviceName = std::move(devname);
  838. else mDeviceName = "Unknown Device Name";
  839. }
  840. #else
  841. mDeviceName = name;
  842. #endif
  843. }
  844. void CoreAudioCapture::start()
  845. {
  846. OSStatus err{AudioOutputUnitStart(mAudioUnit)};
  847. if(err != noErr)
  848. throw al::backend_exception{al::backend_error::DeviceError,
  849. "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
  850. }
  851. void CoreAudioCapture::stop()
  852. {
  853. OSStatus err{AudioOutputUnitStop(mAudioUnit)};
  854. if(err != noErr)
  855. ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
  856. }
  857. void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
  858. {
  859. if(!mConverter)
  860. {
  861. std::ignore = mRing->read(buffer, samples);
  862. return;
  863. }
  864. auto rec_vec = mRing->getReadVector();
  865. const void *src0{rec_vec[0].buf};
  866. auto src0len = static_cast<uint>(rec_vec[0].len);
  867. uint got{mConverter->convert(&src0, &src0len, buffer, samples)};
  868. size_t total_read{rec_vec[0].len - src0len};
  869. if(got < samples && !src0len && rec_vec[1].len > 0)
  870. {
  871. const void *src1{rec_vec[1].buf};
  872. auto src1len = static_cast<uint>(rec_vec[1].len);
  873. got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got);
  874. total_read += rec_vec[1].len - src1len;
  875. }
  876. mRing->readAdvance(total_read);
  877. }
  878. uint CoreAudioCapture::availableSamples()
  879. {
  880. if(!mConverter) return static_cast<uint>(mRing->readSpace());
  881. return mConverter->availableOut(static_cast<uint>(mRing->readSpace()));
  882. }
  883. } // namespace
  884. BackendFactory &CoreAudioBackendFactory::getFactory()
  885. {
  886. static CoreAudioBackendFactory factory{};
  887. return factory;
  888. }
  889. bool CoreAudioBackendFactory::init()
  890. {
  891. #if CAN_ENUMERATE
  892. sDeviceHelper.emplace();
  893. #endif
  894. return true;
  895. }
  896. bool CoreAudioBackendFactory::querySupport(BackendType type)
  897. { return type == BackendType::Playback || type == BackendType::Capture; }
  898. auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
  899. {
  900. std::vector<std::string> outnames;
  901. #if CAN_ENUMERATE
  902. auto append_name = [&outnames](const DeviceEntry &entry) -> void
  903. { outnames.emplace_back(entry.mName); };
  904. switch(type)
  905. {
  906. case BackendType::Playback:
  907. EnumerateDevices(PlaybackList, false);
  908. outnames.reserve(PlaybackList.size());
  909. std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
  910. break;
  911. case BackendType::Capture:
  912. EnumerateDevices(CaptureList, true);
  913. outnames.reserve(CaptureList.size());
  914. std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
  915. break;
  916. }
  917. #else
  918. switch(type)
  919. {
  920. case BackendType::Playback:
  921. case BackendType::Capture:
  922. outnames.emplace_back(ca_device);
  923. break;
  924. }
  925. #endif
  926. return outnames;
  927. }
  928. BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendType type)
  929. {
  930. if(type == BackendType::Playback)
  931. return BackendPtr{new CoreAudioPlayback{device}};
  932. if(type == BackendType::Capture)
  933. return BackendPtr{new CoreAudioCapture{device}};
  934. return nullptr;
  935. }
  936. alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
  937. {
  938. switch(eventType)
  939. {
  940. case alc::EventType::DefaultDeviceChanged:
  941. return alc::EventSupport::FullSupport;
  942. case alc::EventType::DeviceAdded:
  943. case alc::EventType::DeviceRemoved:
  944. case alc::EventType::Count:
  945. break;
  946. }
  947. return alc::EventSupport::NoSupport;
  948. }