🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

947 lines
32 KiB

  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 <inttypes.h>
  23. #include <stdint.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28. #include <cmath>
  29. #include <memory>
  30. #include <string>
  31. #include "alnumeric.h"
  32. #include "core/converter.h"
  33. #include "core/device.h"
  34. #include "core/logging.h"
  35. #include "ringbuffer.h"
  36. #include <AudioUnit/AudioUnit.h>
  37. #include <AudioToolbox/AudioToolbox.h>
  38. namespace {
  39. #if TARGET_OS_IOS || TARGET_OS_TV
  40. #define CAN_ENUMERATE 0
  41. #else
  42. #define CAN_ENUMERATE 1
  43. #endif
  44. constexpr auto OutputElement = 0;
  45. constexpr auto InputElement = 1;
  46. #if CAN_ENUMERATE
  47. struct DeviceEntry {
  48. AudioDeviceID mId;
  49. std::string mName;
  50. };
  51. std::vector<DeviceEntry> PlaybackList;
  52. std::vector<DeviceEntry> CaptureList;
  53. OSStatus GetHwProperty(AudioHardwarePropertyID propId, UInt32 dataSize, void *propData)
  54. {
  55. const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
  56. kAudioObjectPropertyElementMaster};
  57. return AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, nullptr, &dataSize,
  58. propData);
  59. }
  60. OSStatus GetHwPropertySize(AudioHardwarePropertyID propId, UInt32 *outSize)
  61. {
  62. const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
  63. kAudioObjectPropertyElementMaster};
  64. return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, nullptr, outSize);
  65. }
  66. OSStatus GetDevProperty(AudioDeviceID devId, AudioDevicePropertyID propId, bool isCapture,
  67. UInt32 elem, UInt32 dataSize, void *propData)
  68. {
  69. static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
  70. kAudioDevicePropertyScopeInput};
  71. const AudioObjectPropertyAddress addr{propId, scopes[isCapture], elem};
  72. return AudioObjectGetPropertyData(devId, &addr, 0, nullptr, &dataSize, propData);
  73. }
  74. OSStatus GetDevPropertySize(AudioDeviceID devId, AudioDevicePropertyID inPropertyID,
  75. bool isCapture, UInt32 elem, UInt32 *outSize)
  76. {
  77. static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
  78. kAudioDevicePropertyScopeInput};
  79. const AudioObjectPropertyAddress addr{inPropertyID, scopes[isCapture], elem};
  80. return AudioObjectGetPropertyDataSize(devId, &addr, 0, nullptr, outSize);
  81. }
  82. std::string GetDeviceName(AudioDeviceID devId)
  83. {
  84. std::string devname;
  85. CFStringRef nameRef;
  86. /* Try to get the device name as a CFString, for Unicode name support. */
  87. OSStatus err{GetDevProperty(devId, kAudioDevicePropertyDeviceNameCFString, false, 0,
  88. sizeof(nameRef), &nameRef)};
  89. if(err == noErr)
  90. {
  91. const CFIndex propSize{CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
  92. kCFStringEncodingUTF8)};
  93. devname.resize(static_cast<size_t>(propSize)+1, '\0');
  94. CFStringGetCString(nameRef, &devname[0], propSize+1, kCFStringEncodingUTF8);
  95. CFRelease(nameRef);
  96. }
  97. else
  98. {
  99. /* If that failed, just get the C string. Hopefully there's nothing bad
  100. * with this.
  101. */
  102. UInt32 propSize{};
  103. if(GetDevPropertySize(devId, kAudioDevicePropertyDeviceName, false, 0, &propSize))
  104. return devname;
  105. devname.resize(propSize+1, '\0');
  106. if(GetDevProperty(devId, kAudioDevicePropertyDeviceName, false, 0, propSize, &devname[0]))
  107. {
  108. devname.clear();
  109. return devname;
  110. }
  111. }
  112. /* Clear extraneous nul chars that may have been written with the name
  113. * string, and return it.
  114. */
  115. while(!devname.back())
  116. devname.pop_back();
  117. return devname;
  118. }
  119. UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
  120. {
  121. UInt32 propSize{};
  122. auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
  123. &propSize);
  124. if(err)
  125. {
  126. ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
  127. return 0;
  128. }
  129. auto buflist_data = std::make_unique<char[]>(propSize);
  130. auto *buflist = reinterpret_cast<AudioBufferList*>(buflist_data.get());
  131. err = GetDevProperty(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, propSize,
  132. buflist);
  133. if(err)
  134. {
  135. ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
  136. return 0;
  137. }
  138. UInt32 numChannels{0};
  139. for(size_t i{0};i < buflist->mNumberBuffers;++i)
  140. numChannels += buflist->mBuffers[i].mNumberChannels;
  141. return numChannels;
  142. }
  143. void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
  144. {
  145. UInt32 propSize{};
  146. if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
  147. {
  148. ERR("Failed to get device list size: %u\n", err);
  149. return;
  150. }
  151. auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
  152. if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
  153. {
  154. ERR("Failed to get device list: %u\n", err);
  155. return;
  156. }
  157. std::vector<DeviceEntry> newdevs;
  158. newdevs.reserve(devIds.size());
  159. AudioDeviceID defaultId{kAudioDeviceUnknown};
  160. GetHwProperty(isCapture ? kAudioHardwarePropertyDefaultInputDevice :
  161. kAudioHardwarePropertyDefaultOutputDevice, sizeof(defaultId), &defaultId);
  162. if(defaultId != kAudioDeviceUnknown)
  163. {
  164. newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
  165. const auto &entry = newdevs.back();
  166. TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
  167. }
  168. for(const AudioDeviceID devId : devIds)
  169. {
  170. if(devId == kAudioDeviceUnknown)
  171. continue;
  172. auto match_devid = [devId](const DeviceEntry &entry) noexcept -> bool
  173. { return entry.mId == devId; };
  174. auto match = std::find_if(newdevs.cbegin(), newdevs.cend(), match_devid);
  175. if(match != newdevs.cend()) continue;
  176. auto numChannels = GetDeviceChannelCount(devId, isCapture);
  177. if(numChannels > 0)
  178. {
  179. newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
  180. const auto &entry = newdevs.back();
  181. TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
  182. }
  183. }
  184. if(newdevs.size() > 1)
  185. {
  186. /* Rename entries that have matching names, by appending '#2', '#3',
  187. * etc, as needed.
  188. */
  189. for(auto curitem = newdevs.begin()+1;curitem != newdevs.end();++curitem)
  190. {
  191. auto check_match = [curitem](const DeviceEntry &entry) -> bool
  192. { return entry.mName == curitem->mName; };
  193. if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
  194. {
  195. std::string name{curitem->mName};
  196. size_t count{1};
  197. auto check_name = [&name](const DeviceEntry &entry) -> bool
  198. { return entry.mName == name; };
  199. do {
  200. name = curitem->mName;
  201. name += " #";
  202. name += std::to_string(++count);
  203. } while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
  204. curitem->mName = std::move(name);
  205. }
  206. }
  207. }
  208. newdevs.shrink_to_fit();
  209. newdevs.swap(list);
  210. }
  211. #else
  212. static constexpr char ca_device[] = "CoreAudio Default";
  213. #endif
  214. struct CoreAudioPlayback final : public BackendBase {
  215. CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  216. ~CoreAudioPlayback() override;
  217. OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
  218. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
  219. AudioBufferList *ioData) noexcept;
  220. static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
  221. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
  222. AudioBufferList *ioData) noexcept
  223. {
  224. return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
  225. inBusNumber, inNumberFrames, ioData);
  226. }
  227. void open(const char *name) override;
  228. bool reset() override;
  229. void start() override;
  230. void stop() override;
  231. AudioUnit mAudioUnit{};
  232. uint mFrameSize{0u};
  233. AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
  234. DEF_NEWDEL(CoreAudioPlayback)
  235. };
  236. CoreAudioPlayback::~CoreAudioPlayback()
  237. {
  238. AudioUnitUninitialize(mAudioUnit);
  239. AudioComponentInstanceDispose(mAudioUnit);
  240. }
  241. OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32,
  242. UInt32, AudioBufferList *ioData) noexcept
  243. {
  244. for(size_t i{0};i < ioData->mNumberBuffers;++i)
  245. {
  246. auto &buffer = ioData->mBuffers[i];
  247. mDevice->renderSamples(buffer.mData, buffer.mDataByteSize/mFrameSize,
  248. buffer.mNumberChannels);
  249. }
  250. return noErr;
  251. }
  252. void CoreAudioPlayback::open(const char *name)
  253. {
  254. #if CAN_ENUMERATE
  255. AudioDeviceID audioDevice{kAudioDeviceUnknown};
  256. if(!name)
  257. GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
  258. &audioDevice);
  259. else
  260. {
  261. if(PlaybackList.empty())
  262. EnumerateDevices(PlaybackList, false);
  263. auto find_name = [name](const DeviceEntry &entry) -> bool
  264. { return entry.mName == name; };
  265. auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
  266. if(devmatch == PlaybackList.cend())
  267. throw al::backend_exception{al::backend_error::NoDevice,
  268. "Device name \"%s\" not found", name};
  269. audioDevice = devmatch->mId;
  270. }
  271. #else
  272. if(!name)
  273. name = ca_device;
  274. else if(strcmp(name, ca_device) != 0)
  275. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  276. name};
  277. #endif
  278. /* open the default output unit */
  279. AudioComponentDescription desc{};
  280. desc.componentType = kAudioUnitType_Output;
  281. #if CAN_ENUMERATE
  282. desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
  283. kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
  284. #else
  285. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  286. #endif
  287. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  288. desc.componentFlags = 0;
  289. desc.componentFlagsMask = 0;
  290. AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
  291. if(comp == nullptr)
  292. throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
  293. AudioUnit audioUnit{};
  294. OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
  295. if(err != noErr)
  296. throw al::backend_exception{al::backend_error::NoDevice,
  297. "Could not create component instance: %u", err};
  298. #if CAN_ENUMERATE
  299. if(audioDevice != kAudioDeviceUnknown)
  300. AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
  301. kAudioUnitScope_Global, OutputElement, &audioDevice, sizeof(AudioDeviceID));
  302. #endif
  303. err = AudioUnitInitialize(audioUnit);
  304. if(err != noErr)
  305. throw al::backend_exception{al::backend_error::DeviceError,
  306. "Could not initialize audio unit: %u", err};
  307. /* WARNING: I don't know if "valid" audio unit values are guaranteed to be
  308. * non-0. If not, this logic is broken.
  309. */
  310. if(mAudioUnit)
  311. {
  312. AudioUnitUninitialize(mAudioUnit);
  313. AudioComponentInstanceDispose(mAudioUnit);
  314. }
  315. mAudioUnit = audioUnit;
  316. #if CAN_ENUMERATE
  317. if(name)
  318. mDevice->DeviceName = name;
  319. else
  320. {
  321. UInt32 propSize{sizeof(audioDevice)};
  322. audioDevice = kAudioDeviceUnknown;
  323. AudioUnitGetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
  324. kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize);
  325. std::string devname{GetDeviceName(audioDevice)};
  326. if(!devname.empty()) mDevice->DeviceName = std::move(devname);
  327. else mDevice->DeviceName = "Unknown Device Name";
  328. }
  329. #else
  330. mDevice->DeviceName = name;
  331. #endif
  332. }
  333. bool CoreAudioPlayback::reset()
  334. {
  335. OSStatus err{AudioUnitUninitialize(mAudioUnit)};
  336. if(err != noErr)
  337. ERR("-- AudioUnitUninitialize failed.\n");
  338. /* retrieve default output unit's properties (output side) */
  339. AudioStreamBasicDescription streamFormat{};
  340. UInt32 size{sizeof(streamFormat)};
  341. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
  342. OutputElement, &streamFormat, &size);
  343. if(err != noErr || size != sizeof(streamFormat))
  344. {
  345. ERR("AudioUnitGetProperty failed\n");
  346. return false;
  347. }
  348. #if 0
  349. TRACE("Output streamFormat of default output unit -\n");
  350. TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
  351. TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
  352. TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
  353. TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
  354. TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
  355. TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
  356. #endif
  357. /* Use the sample rate from the output unit's current parameters, but reset
  358. * everything else.
  359. */
  360. if(mDevice->Frequency != streamFormat.mSampleRate)
  361. {
  362. mDevice->BufferSize = static_cast<uint>(mDevice->BufferSize*streamFormat.mSampleRate/
  363. mDevice->Frequency + 0.5);
  364. mDevice->Frequency = static_cast<uint>(streamFormat.mSampleRate);
  365. }
  366. /* FIXME: How to tell what channels are what in the output device, and how
  367. * to specify what we're giving? e.g. 6.0 vs 5.1
  368. */
  369. streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
  370. streamFormat.mFramesPerPacket = 1;
  371. streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
  372. streamFormat.mFormatID = kAudioFormatLinearPCM;
  373. switch(mDevice->FmtType)
  374. {
  375. case DevFmtUByte:
  376. mDevice->FmtType = DevFmtByte;
  377. /* fall-through */
  378. case DevFmtByte:
  379. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  380. streamFormat.mBitsPerChannel = 8;
  381. break;
  382. case DevFmtUShort:
  383. mDevice->FmtType = DevFmtShort;
  384. /* fall-through */
  385. case DevFmtShort:
  386. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  387. streamFormat.mBitsPerChannel = 16;
  388. break;
  389. case DevFmtUInt:
  390. mDevice->FmtType = DevFmtInt;
  391. /* fall-through */
  392. case DevFmtInt:
  393. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  394. streamFormat.mBitsPerChannel = 32;
  395. break;
  396. case DevFmtFloat:
  397. streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
  398. streamFormat.mBitsPerChannel = 32;
  399. break;
  400. }
  401. streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame*streamFormat.mBitsPerChannel/8;
  402. streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame*streamFormat.mFramesPerPacket;
  403. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  404. OutputElement, &streamFormat, sizeof(streamFormat));
  405. if(err != noErr)
  406. {
  407. ERR("AudioUnitSetProperty failed\n");
  408. return false;
  409. }
  410. setDefaultWFXChannelOrder();
  411. /* setup callback */
  412. mFrameSize = mDevice->frameSizeFromFmt();
  413. AURenderCallbackStruct input{};
  414. input.inputProc = CoreAudioPlayback::MixerProcC;
  415. input.inputProcRefCon = this;
  416. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
  417. kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
  418. if(err != noErr)
  419. {
  420. ERR("AudioUnitSetProperty failed\n");
  421. return false;
  422. }
  423. /* init the default audio unit... */
  424. err = AudioUnitInitialize(mAudioUnit);
  425. if(err != noErr)
  426. {
  427. ERR("AudioUnitInitialize failed\n");
  428. return false;
  429. }
  430. return true;
  431. }
  432. void CoreAudioPlayback::start()
  433. {
  434. const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
  435. if(err != noErr)
  436. throw al::backend_exception{al::backend_error::DeviceError,
  437. "AudioOutputUnitStart failed: %d", err};
  438. }
  439. void CoreAudioPlayback::stop()
  440. {
  441. OSStatus err{AudioOutputUnitStop(mAudioUnit)};
  442. if(err != noErr)
  443. ERR("AudioOutputUnitStop failed\n");
  444. }
  445. struct CoreAudioCapture final : public BackendBase {
  446. CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
  447. ~CoreAudioCapture() override;
  448. OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
  449. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
  450. UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
  451. static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
  452. const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
  453. AudioBufferList *ioData) noexcept
  454. {
  455. return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
  456. inBusNumber, inNumberFrames, ioData);
  457. }
  458. void open(const char *name) override;
  459. void start() override;
  460. void stop() override;
  461. void captureSamples(al::byte *buffer, uint samples) override;
  462. uint availableSamples() override;
  463. AudioUnit mAudioUnit{0};
  464. uint mFrameSize{0u};
  465. AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
  466. SampleConverterPtr mConverter;
  467. RingBufferPtr mRing{nullptr};
  468. DEF_NEWDEL(CoreAudioCapture)
  469. };
  470. CoreAudioCapture::~CoreAudioCapture()
  471. {
  472. if(mAudioUnit)
  473. AudioComponentInstanceDispose(mAudioUnit);
  474. mAudioUnit = 0;
  475. }
  476. OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
  477. const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames,
  478. AudioBufferList*) noexcept
  479. {
  480. AudioUnitRenderActionFlags flags = 0;
  481. union {
  482. al::byte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2];
  483. AudioBufferList list;
  484. } audiobuf{};
  485. auto rec_vec = mRing->getWriteVector();
  486. inNumberFrames = static_cast<UInt32>(minz(inNumberFrames,
  487. rec_vec.first.len+rec_vec.second.len));
  488. // Fill the ringbuffer's two segments with data from the input device
  489. if(rec_vec.first.len >= inNumberFrames)
  490. {
  491. audiobuf.list.mNumberBuffers = 1;
  492. audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
  493. audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
  494. audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame;
  495. }
  496. else
  497. {
  498. const auto remaining = static_cast<uint>(inNumberFrames - rec_vec.first.len);
  499. audiobuf.list.mNumberBuffers = 2;
  500. audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
  501. audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
  502. audiobuf.list.mBuffers[0].mDataByteSize = static_cast<UInt32>(rec_vec.first.len) *
  503. mFormat.mBytesPerFrame;
  504. audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame;
  505. audiobuf.list.mBuffers[1].mData = rec_vec.second.buf;
  506. audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame;
  507. }
  508. OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers,
  509. inNumberFrames, &audiobuf.list)};
  510. if(err != noErr)
  511. {
  512. ERR("AudioUnitRender error: %d\n", err);
  513. return err;
  514. }
  515. mRing->writeAdvance(inNumberFrames);
  516. return noErr;
  517. }
  518. void CoreAudioCapture::open(const char *name)
  519. {
  520. #if CAN_ENUMERATE
  521. AudioDeviceID audioDevice{kAudioDeviceUnknown};
  522. if(!name)
  523. GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
  524. &audioDevice);
  525. else
  526. {
  527. if(CaptureList.empty())
  528. EnumerateDevices(CaptureList, true);
  529. auto find_name = [name](const DeviceEntry &entry) -> bool
  530. { return entry.mName == name; };
  531. auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
  532. if(devmatch == CaptureList.cend())
  533. throw al::backend_exception{al::backend_error::NoDevice,
  534. "Device name \"%s\" not found", name};
  535. audioDevice = devmatch->mId;
  536. }
  537. #else
  538. if(!name)
  539. name = ca_device;
  540. else if(strcmp(name, ca_device) != 0)
  541. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  542. name};
  543. #endif
  544. AudioComponentDescription desc{};
  545. desc.componentType = kAudioUnitType_Output;
  546. #if CAN_ENUMERATE
  547. desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
  548. kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
  549. #else
  550. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  551. #endif
  552. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  553. desc.componentFlags = 0;
  554. desc.componentFlagsMask = 0;
  555. // Search for component with given description
  556. AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
  557. if(comp == NULL)
  558. throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
  559. // Open the component
  560. OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
  561. if(err != noErr)
  562. throw al::backend_exception{al::backend_error::NoDevice,
  563. "Could not create component instance: %u", err};
  564. // Turn off AudioUnit output
  565. UInt32 enableIO{0};
  566. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
  567. kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
  568. if(err != noErr)
  569. throw al::backend_exception{al::backend_error::DeviceError,
  570. "Could not disable audio unit output property: %u", err};
  571. // Turn on AudioUnit input
  572. enableIO = 1;
  573. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
  574. kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
  575. if(err != noErr)
  576. throw al::backend_exception{al::backend_error::DeviceError,
  577. "Could not enable audio unit input property: %u", err};
  578. #if CAN_ENUMERATE
  579. if(audioDevice != kAudioDeviceUnknown)
  580. AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
  581. kAudioUnitScope_Global, InputElement, &audioDevice, sizeof(AudioDeviceID));
  582. #endif
  583. // set capture callback
  584. AURenderCallbackStruct input{};
  585. input.inputProc = CoreAudioCapture::RecordProcC;
  586. input.inputProcRefCon = this;
  587. err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
  588. kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
  589. if(err != noErr)
  590. throw al::backend_exception{al::backend_error::DeviceError,
  591. "Could not set capture callback: %u", err};
  592. // Disable buffer allocation for capture
  593. UInt32 flag{0};
  594. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_ShouldAllocateBuffer,
  595. kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
  596. if(err != noErr)
  597. throw al::backend_exception{al::backend_error::DeviceError,
  598. "Could not disable buffer allocation property: %u", err};
  599. // Initialize the device
  600. err = AudioUnitInitialize(mAudioUnit);
  601. if(err != noErr)
  602. throw al::backend_exception{al::backend_error::DeviceError,
  603. "Could not initialize audio unit: %u", err};
  604. // Get the hardware format
  605. AudioStreamBasicDescription hardwareFormat{};
  606. UInt32 propertySize{sizeof(hardwareFormat)};
  607. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  608. InputElement, &hardwareFormat, &propertySize);
  609. if(err != noErr || propertySize != sizeof(hardwareFormat))
  610. throw al::backend_exception{al::backend_error::DeviceError,
  611. "Could not get input format: %u", err};
  612. // Set up the requested format description
  613. AudioStreamBasicDescription requestedFormat{};
  614. switch(mDevice->FmtType)
  615. {
  616. case DevFmtByte:
  617. requestedFormat.mBitsPerChannel = 8;
  618. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  619. break;
  620. case DevFmtUByte:
  621. requestedFormat.mBitsPerChannel = 8;
  622. requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
  623. break;
  624. case DevFmtShort:
  625. requestedFormat.mBitsPerChannel = 16;
  626. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
  627. | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  628. break;
  629. case DevFmtUShort:
  630. requestedFormat.mBitsPerChannel = 16;
  631. requestedFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  632. break;
  633. case DevFmtInt:
  634. requestedFormat.mBitsPerChannel = 32;
  635. requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
  636. | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  637. break;
  638. case DevFmtUInt:
  639. requestedFormat.mBitsPerChannel = 32;
  640. requestedFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
  641. break;
  642. case DevFmtFloat:
  643. requestedFormat.mBitsPerChannel = 32;
  644. requestedFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian
  645. | kAudioFormatFlagIsPacked;
  646. break;
  647. }
  648. switch(mDevice->FmtChans)
  649. {
  650. case DevFmtMono:
  651. requestedFormat.mChannelsPerFrame = 1;
  652. break;
  653. case DevFmtStereo:
  654. requestedFormat.mChannelsPerFrame = 2;
  655. break;
  656. case DevFmtQuad:
  657. case DevFmtX51:
  658. case DevFmtX61:
  659. case DevFmtX71:
  660. case DevFmtX3D71:
  661. case DevFmtAmbi3D:
  662. throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
  663. DevFmtChannelsString(mDevice->FmtChans)};
  664. }
  665. requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
  666. requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
  667. requestedFormat.mSampleRate = mDevice->Frequency;
  668. requestedFormat.mFormatID = kAudioFormatLinearPCM;
  669. requestedFormat.mReserved = 0;
  670. requestedFormat.mFramesPerPacket = 1;
  671. // save requested format description for later use
  672. mFormat = requestedFormat;
  673. mFrameSize = mDevice->frameSizeFromFmt();
  674. // Use intermediate format for sample rate conversion (outputFormat)
  675. // Set sample rate to the same as hardware for resampling later
  676. AudioStreamBasicDescription outputFormat{requestedFormat};
  677. outputFormat.mSampleRate = hardwareFormat.mSampleRate;
  678. // The output format should be the requested format, but using the hardware sample rate
  679. // This is because the AudioUnit will automatically scale other properties, except for sample rate
  680. err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
  681. InputElement, &outputFormat, sizeof(outputFormat));
  682. if(err != noErr)
  683. throw al::backend_exception{al::backend_error::DeviceError,
  684. "Could not set input format: %u", err};
  685. /* Calculate the minimum AudioUnit output format frame count for the pre-
  686. * conversion ring buffer. Ensure at least 100ms for the total buffer.
  687. */
  688. double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
  689. auto FrameCount64 = maxu64(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
  690. static_cast<UInt32>(outputFormat.mSampleRate)/10);
  691. FrameCount64 += MaxResamplerPadding;
  692. if(FrameCount64 > std::numeric_limits<int32_t>::max())
  693. throw al::backend_exception{al::backend_error::DeviceError,
  694. "Calculated frame count is too large: %" PRIu64, FrameCount64};
  695. UInt32 outputFrameCount{};
  696. propertySize = sizeof(outputFrameCount);
  697. err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
  698. kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
  699. if(err != noErr || propertySize != sizeof(outputFrameCount))
  700. throw al::backend_exception{al::backend_error::DeviceError,
  701. "Could not get input frame count: %u", err};
  702. outputFrameCount = static_cast<UInt32>(maxu64(outputFrameCount, FrameCount64));
  703. mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
  704. /* Set up sample converter if needed */
  705. if(outputFormat.mSampleRate != mDevice->Frequency)
  706. mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
  707. mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
  708. mDevice->Frequency, Resampler::FastBSinc24);
  709. #if CAN_ENUMERATE
  710. if(name)
  711. mDevice->DeviceName = name;
  712. else
  713. {
  714. UInt32 propSize{sizeof(audioDevice)};
  715. audioDevice = kAudioDeviceUnknown;
  716. AudioUnitGetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
  717. kAudioUnitScope_Global, InputElement, &audioDevice, &propSize);
  718. std::string devname{GetDeviceName(audioDevice)};
  719. if(!devname.empty()) mDevice->DeviceName = std::move(devname);
  720. else mDevice->DeviceName = "Unknown Device Name";
  721. }
  722. #else
  723. mDevice->DeviceName = name;
  724. #endif
  725. }
  726. void CoreAudioCapture::start()
  727. {
  728. OSStatus err{AudioOutputUnitStart(mAudioUnit)};
  729. if(err != noErr)
  730. throw al::backend_exception{al::backend_error::DeviceError,
  731. "AudioOutputUnitStart failed: %d", err};
  732. }
  733. void CoreAudioCapture::stop()
  734. {
  735. OSStatus err{AudioOutputUnitStop(mAudioUnit)};
  736. if(err != noErr)
  737. ERR("AudioOutputUnitStop failed\n");
  738. }
  739. void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples)
  740. {
  741. if(!mConverter)
  742. {
  743. mRing->read(buffer, samples);
  744. return;
  745. }
  746. auto rec_vec = mRing->getReadVector();
  747. const void *src0{rec_vec.first.buf};
  748. auto src0len = static_cast<uint>(rec_vec.first.len);
  749. uint got{mConverter->convert(&src0, &src0len, buffer, samples)};
  750. size_t total_read{rec_vec.first.len - src0len};
  751. if(got < samples && !src0len && rec_vec.second.len > 0)
  752. {
  753. const void *src1{rec_vec.second.buf};
  754. auto src1len = static_cast<uint>(rec_vec.second.len);
  755. got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got);
  756. total_read += rec_vec.second.len - src1len;
  757. }
  758. mRing->readAdvance(total_read);
  759. }
  760. uint CoreAudioCapture::availableSamples()
  761. {
  762. if(!mConverter) return static_cast<uint>(mRing->readSpace());
  763. return mConverter->availableOut(static_cast<uint>(mRing->readSpace()));
  764. }
  765. } // namespace
  766. BackendFactory &CoreAudioBackendFactory::getFactory()
  767. {
  768. static CoreAudioBackendFactory factory{};
  769. return factory;
  770. }
  771. bool CoreAudioBackendFactory::init() { return true; }
  772. bool CoreAudioBackendFactory::querySupport(BackendType type)
  773. { return type == BackendType::Playback || type == BackendType::Capture; }
  774. std::string CoreAudioBackendFactory::probe(BackendType type)
  775. {
  776. std::string outnames;
  777. #if CAN_ENUMERATE
  778. auto append_name = [&outnames](const DeviceEntry &entry) -> void
  779. {
  780. /* Includes null char. */
  781. outnames.append(entry.mName.c_str(), entry.mName.length()+1);
  782. };
  783. switch(type)
  784. {
  785. case BackendType::Playback:
  786. EnumerateDevices(PlaybackList, false);
  787. std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
  788. break;
  789. case BackendType::Capture:
  790. EnumerateDevices(CaptureList, true);
  791. std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
  792. break;
  793. }
  794. #else
  795. switch(type)
  796. {
  797. case BackendType::Playback:
  798. case BackendType::Capture:
  799. /* Includes null char. */
  800. outnames.append(ca_device, sizeof(ca_device));
  801. break;
  802. }
  803. #endif
  804. return outnames;
  805. }
  806. BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendType type)
  807. {
  808. if(type == BackendType::Playback)
  809. return BackendPtr{new CoreAudioPlayback{device}};
  810. if(type == BackendType::Capture)
  811. return BackendPtr{new CoreAudioCapture{device}};
  812. return nullptr;
  813. }