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