Avoid raw lock/unlock calls
[openal-soft.git] / alc / backends / coreaudio.cpp
blob3a97c105046cedd153adc43ae5e5e7f99ac5e814
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.
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.
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
21 #include "config.h"
23 #include "backends/coreaudio.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include "alcmain.h"
30 #include "alexcpt.h"
31 #include "alu.h"
32 #include "ringbuffer.h"
33 #include "converter.h"
34 #include "backends/base.h"
36 #include <unistd.h>
37 #include <AudioUnit/AudioUnit.h>
38 #include <AudioToolbox/AudioToolbox.h>
41 namespace {
43 static const ALCchar ca_device[] = "CoreAudio Default";
46 struct CoreAudioPlayback final : public BackendBase {
47 CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
48 ~CoreAudioPlayback() override;
50 static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
51 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
52 AudioBufferList *ioData)
54 return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
55 inBusNumber, inNumberFrames, ioData);
57 OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
58 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
59 AudioBufferList *ioData);
61 void open(const ALCchar *name) override;
62 bool reset() override;
63 bool start() override;
64 void stop() override;
66 AudioUnit mAudioUnit{};
68 ALuint mFrameSize{0u};
69 AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
71 DEF_NEWDEL(CoreAudioPlayback)
74 CoreAudioPlayback::~CoreAudioPlayback()
76 AudioUnitUninitialize(mAudioUnit);
77 AudioComponentInstanceDispose(mAudioUnit);
81 OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*,
82 const AudioTimeStamp*, UInt32, UInt32, AudioBufferList *ioData)
84 std::lock_guard<CoreAudioPlayback> _{*this};
85 aluMixData(mDevice, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize);
86 return noErr;
90 void CoreAudioPlayback::open(const ALCchar *name)
92 if(!name)
93 name = ca_device;
94 else if(strcmp(name, ca_device) != 0)
95 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
97 /* open the default output unit */
98 AudioComponentDescription desc{};
99 desc.componentType = kAudioUnitType_Output;
100 #if TARGET_OS_IOS
101 desc.componentSubType = kAudioUnitSubType_RemoteIO;
102 #else
103 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
104 #endif
105 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
106 desc.componentFlags = 0;
107 desc.componentFlagsMask = 0;
109 AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
110 if(comp == nullptr)
112 ERR("AudioComponentFindNext failed\n");
113 throw al::backend_exception{ALC_INVALID_VALUE, "Could not find audio component"};
116 OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
117 if(err != noErr)
119 ERR("AudioComponentInstanceNew failed\n");
120 throw al::backend_exception{ALC_INVALID_VALUE, "Could not create component instance: %u",
121 err};
124 /* init and start the default audio unit... */
125 err = AudioUnitInitialize(mAudioUnit);
126 if(err != noErr)
128 ERR("AudioUnitInitialize failed\n");
129 throw al::backend_exception{ALC_INVALID_VALUE, "Could not initialize audio unit: %u", err};
132 mDevice->DeviceName = name;
135 bool CoreAudioPlayback::reset()
137 OSStatus err{AudioUnitUninitialize(mAudioUnit)};
138 if(err != noErr)
139 ERR("-- AudioUnitUninitialize failed.\n");
141 /* retrieve default output unit's properties (output side) */
142 AudioStreamBasicDescription streamFormat{};
143 auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
144 err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
145 0, &streamFormat, &size);
146 if(err != noErr || size != sizeof(AudioStreamBasicDescription))
148 ERR("AudioUnitGetProperty failed\n");
149 return false;
152 #if 0
153 TRACE("Output streamFormat of default output unit -\n");
154 TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
155 TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
156 TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
157 TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
158 TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
159 TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
160 #endif
162 /* set default output unit's input side to match output side */
163 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
164 0, &streamFormat, size);
165 if(err != noErr)
167 ERR("AudioUnitSetProperty failed\n");
168 return false;
171 if(mDevice->Frequency != streamFormat.mSampleRate)
173 mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
174 streamFormat.mSampleRate / mDevice->Frequency);
175 mDevice->Frequency = static_cast<ALuint>(streamFormat.mSampleRate);
178 /* FIXME: How to tell what channels are what in the output device, and how
179 * to specify what we're giving? eg, 6.0 vs 5.1 */
180 switch(streamFormat.mChannelsPerFrame)
182 case 1:
183 mDevice->FmtChans = DevFmtMono;
184 break;
185 case 2:
186 mDevice->FmtChans = DevFmtStereo;
187 break;
188 case 4:
189 mDevice->FmtChans = DevFmtQuad;
190 break;
191 case 6:
192 mDevice->FmtChans = DevFmtX51;
193 break;
194 case 7:
195 mDevice->FmtChans = DevFmtX61;
196 break;
197 case 8:
198 mDevice->FmtChans = DevFmtX71;
199 break;
200 default:
201 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
202 mDevice->FmtChans = DevFmtStereo;
203 streamFormat.mChannelsPerFrame = 2;
204 break;
206 SetDefaultWFXChannelOrder(mDevice);
208 /* use channel count and sample rate from the default output unit's current
209 * parameters, but reset everything else */
210 streamFormat.mFramesPerPacket = 1;
211 streamFormat.mFormatFlags = 0;
212 switch(mDevice->FmtType)
214 case DevFmtUByte:
215 mDevice->FmtType = DevFmtByte;
216 /* fall-through */
217 case DevFmtByte:
218 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
219 streamFormat.mBitsPerChannel = 8;
220 break;
221 case DevFmtUShort:
222 mDevice->FmtType = DevFmtShort;
223 /* fall-through */
224 case DevFmtShort:
225 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
226 streamFormat.mBitsPerChannel = 16;
227 break;
228 case DevFmtUInt:
229 mDevice->FmtType = DevFmtInt;
230 /* fall-through */
231 case DevFmtInt:
232 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
233 streamFormat.mBitsPerChannel = 32;
234 break;
235 case DevFmtFloat:
236 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
237 streamFormat.mBitsPerChannel = 32;
238 break;
240 streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
241 streamFormat.mBitsPerChannel / 8;
242 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
243 streamFormat.mFormatID = kAudioFormatLinearPCM;
244 streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
245 kLinearPCMFormatFlagIsPacked;
247 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
248 0, &streamFormat, sizeof(AudioStreamBasicDescription));
249 if(err != noErr)
251 ERR("AudioUnitSetProperty failed\n");
252 return false;
255 /* setup callback */
256 mFrameSize = mDevice->frameSizeFromFmt();
257 AURenderCallbackStruct input{};
258 input.inputProc = CoreAudioPlayback::MixerProcC;
259 input.inputProcRefCon = this;
261 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
262 kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
263 if(err != noErr)
265 ERR("AudioUnitSetProperty failed\n");
266 return false;
269 /* init the default audio unit... */
270 err = AudioUnitInitialize(mAudioUnit);
271 if(err != noErr)
273 ERR("AudioUnitInitialize failed\n");
274 return false;
277 return true;
280 bool CoreAudioPlayback::start()
282 OSStatus err{AudioOutputUnitStart(mAudioUnit)};
283 if(err != noErr)
285 ERR("AudioOutputUnitStart failed\n");
286 return false;
288 return true;
291 void CoreAudioPlayback::stop()
293 OSStatus err{AudioOutputUnitStop(mAudioUnit)};
294 if(err != noErr)
295 ERR("AudioOutputUnitStop failed\n");
299 struct CoreAudioCapture final : public BackendBase {
300 CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
301 ~CoreAudioCapture() override;
303 static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
304 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
305 AudioBufferList *ioData)
307 return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
308 inBusNumber, inNumberFrames, ioData);
310 OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
311 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
312 UInt32 inNumberFrames, AudioBufferList *ioData);
314 void open(const ALCchar *name) override;
315 bool start() override;
316 void stop() override;
317 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
318 ALCuint availableSamples() override;
320 AudioUnit mAudioUnit{0};
322 ALuint mFrameSize{0u};
323 AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
325 SampleConverterPtr mConverter;
327 RingBufferPtr mRing{nullptr};
329 DEF_NEWDEL(CoreAudioCapture)
332 CoreAudioCapture::~CoreAudioCapture()
334 if(mAudioUnit)
335 AudioComponentInstanceDispose(mAudioUnit);
336 mAudioUnit = 0;
340 OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
341 const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames,
342 AudioBufferList*)
344 AudioUnitRenderActionFlags flags = 0;
345 union {
346 al::byte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2];
347 AudioBufferList list;
348 } audiobuf{};
350 auto rec_vec = mRing->getWriteVector();
351 inNumberFrames = static_cast<UInt32>(minz(inNumberFrames,
352 rec_vec.first.len+rec_vec.second.len));
354 // Fill the ringbuffer's two segments with data from the input device
355 if(rec_vec.first.len >= inNumberFrames)
357 audiobuf.list.mNumberBuffers = 1;
358 audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
359 audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
360 audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame;
362 else
364 const auto remaining = static_cast<ALuint>(inNumberFrames - rec_vec.first.len);
365 audiobuf.list.mNumberBuffers = 2;
366 audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
367 audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
368 audiobuf.list.mBuffers[0].mDataByteSize = static_cast<UInt32>(rec_vec.first.len) *
369 mFormat.mBytesPerFrame;
370 audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame;
371 audiobuf.list.mBuffers[1].mData = rec_vec.second.buf;
372 audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame;
374 OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers,
375 inNumberFrames, &audiobuf.list)};
376 if(err != noErr)
378 ERR("AudioUnitRender error: %d\n", err);
379 return err;
382 mRing->writeAdvance(inNumberFrames);
383 return noErr;
387 void CoreAudioCapture::open(const ALCchar *name)
389 AudioStreamBasicDescription requestedFormat; // The application requested format
390 AudioStreamBasicDescription hardwareFormat; // The hardware format
391 AudioStreamBasicDescription outputFormat; // The AudioUnit output format
392 AURenderCallbackStruct input;
393 AudioComponentDescription desc;
394 UInt32 outputFrameCount;
395 UInt32 propertySize;
396 #if !TARGET_OS_IOS
397 AudioObjectPropertyAddress propertyAddress;
398 #endif
399 UInt32 enableIO;
400 AudioComponent comp;
401 OSStatus err;
403 if(!name)
404 name = ca_device;
405 else if(strcmp(name, ca_device) != 0)
406 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
408 desc.componentType = kAudioUnitType_Output;
409 #if TARGET_OS_IOS
410 desc.componentSubType = kAudioUnitSubType_RemoteIO;
411 #else
412 desc.componentSubType = kAudioUnitSubType_HALOutput;
413 #endif
414 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
415 desc.componentFlags = 0;
416 desc.componentFlagsMask = 0;
418 // Search for component with given description
419 comp = AudioComponentFindNext(NULL, &desc);
420 if(comp == NULL)
422 ERR("AudioComponentFindNext failed\n");
423 throw al::backend_exception{ALC_INVALID_VALUE, "Could not finda udio component"};
426 // Open the component
427 err = AudioComponentInstanceNew(comp, &mAudioUnit);
428 if(err != noErr)
430 ERR("AudioComponentInstanceNew failed\n");
431 throw al::backend_exception{ALC_INVALID_VALUE, "Could not create component instance: %u",
432 err};
435 // Turn off AudioUnit output
436 enableIO = 0;
437 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
438 kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
439 if(err != noErr)
441 ERR("AudioUnitSetProperty failed\n");
442 throw al::backend_exception{ALC_INVALID_VALUE,
443 "Could not disable audio unit output property: %u", err};
446 // Turn on AudioUnit input
447 enableIO = 1;
448 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
449 kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
450 if(err != noErr)
452 ERR("AudioUnitSetProperty failed\n");
453 throw al::backend_exception{ALC_INVALID_VALUE,
454 "Could not enable audio unit input property: %u", err};
457 #if !TARGET_OS_IOS
459 // Get the default input device
460 AudioDeviceID inputDevice = kAudioDeviceUnknown;
462 propertySize = sizeof(AudioDeviceID);
463 propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
464 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
465 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
467 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr,
468 &propertySize, &inputDevice);
469 if(err != noErr)
471 ERR("AudioObjectGetPropertyData failed\n");
472 throw al::backend_exception{ALC_INVALID_VALUE, "Could not get input device: %u", err};
474 if(inputDevice == kAudioDeviceUnknown)
476 ERR("No input device found\n");
477 throw al::backend_exception{ALC_INVALID_VALUE, "Unknown input device"};
480 // Track the input device
481 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
482 kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
483 if(err != noErr)
485 ERR("AudioUnitSetProperty failed\n");
486 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set input device: %u", err};
489 #endif
491 // set capture callback
492 input.inputProc = CoreAudioCapture::RecordProcC;
493 input.inputProcRefCon = this;
495 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
496 kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
497 if(err != noErr)
499 ERR("AudioUnitSetProperty failed\n");
500 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set capture callback: %u", err};
503 // Initialize the device
504 err = AudioUnitInitialize(mAudioUnit);
505 if(err != noErr)
507 ERR("AudioUnitInitialize failed\n");
508 throw al::backend_exception{ALC_INVALID_VALUE, "Could not initialize audio unit: %u", err};
511 // Get the hardware format
512 propertySize = sizeof(AudioStreamBasicDescription);
513 err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
514 1, &hardwareFormat, &propertySize);
515 if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
517 ERR("AudioUnitGetProperty failed\n");
518 throw al::backend_exception{ALC_INVALID_VALUE, "Could not get input format: %u", err};
521 // Set up the requested format description
522 switch(mDevice->FmtType)
524 case DevFmtUByte:
525 requestedFormat.mBitsPerChannel = 8;
526 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
527 break;
528 case DevFmtShort:
529 requestedFormat.mBitsPerChannel = 16;
530 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
531 break;
532 case DevFmtInt:
533 requestedFormat.mBitsPerChannel = 32;
534 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
535 break;
536 case DevFmtFloat:
537 requestedFormat.mBitsPerChannel = 32;
538 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
539 break;
540 case DevFmtByte:
541 case DevFmtUShort:
542 case DevFmtUInt:
543 ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
544 throw al::backend_exception{ALC_INVALID_VALUE, "%s samples not suppoted",
545 DevFmtTypeString(mDevice->FmtType)};
548 switch(mDevice->FmtChans)
550 case DevFmtMono:
551 requestedFormat.mChannelsPerFrame = 1;
552 break;
553 case DevFmtStereo:
554 requestedFormat.mChannelsPerFrame = 2;
555 break;
557 case DevFmtQuad:
558 case DevFmtX51:
559 case DevFmtX51Rear:
560 case DevFmtX61:
561 case DevFmtX71:
562 case DevFmtAmbi3D:
563 ERR("%s not supported\n", DevFmtChannelsString(mDevice->FmtChans));
564 throw al::backend_exception{ALC_INVALID_VALUE, "%s not supported",
565 DevFmtChannelsString(mDevice->FmtChans)};
568 requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
569 requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
570 requestedFormat.mSampleRate = mDevice->Frequency;
571 requestedFormat.mFormatID = kAudioFormatLinearPCM;
572 requestedFormat.mReserved = 0;
573 requestedFormat.mFramesPerPacket = 1;
575 // save requested format description for later use
576 mFormat = requestedFormat;
577 mFrameSize = mDevice->frameSizeFromFmt();
579 // Use intermediate format for sample rate conversion (outputFormat)
580 // Set sample rate to the same as hardware for resampling later
581 outputFormat = requestedFormat;
582 outputFormat.mSampleRate = hardwareFormat.mSampleRate;
584 // The output format should be the requested format, but using the hardware sample rate
585 // This is because the AudioUnit will automatically scale other properties, except for sample rate
586 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
587 1, &outputFormat, sizeof(outputFormat));
588 if(err != noErr)
590 ERR("AudioUnitSetProperty failed\n");
591 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set input format: %u", err};
594 // Set the AudioUnit output format frame count
595 uint64_t FrameCount64{mDevice->UpdateSize};
596 FrameCount64 = static_cast<uint64_t>(FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) /
597 mDevice->Frequency;
598 FrameCount64 += MAX_RESAMPLER_PADDING;
599 if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2)
601 ERR("FrameCount too large\n");
602 throw al::backend_exception{ALC_INVALID_VALUE,
603 "Calculated frame count is too lareg: %" PRIu64, FrameCount64};
606 outputFrameCount = static_cast<uint32_t>(FrameCount64);
607 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
608 kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
609 if(err != noErr)
611 ERR("AudioUnitSetProperty failed: %d\n", err);
612 throw al::backend_exception{ALC_INVALID_VALUE, "Failed to set capture frame count: %u",
613 err};
616 // Set up sample converter if needed
617 if(outputFormat.mSampleRate != mDevice->Frequency)
618 mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
619 mFormat.mChannelsPerFrame, static_cast<ALuint>(hardwareFormat.mSampleRate),
620 mDevice->Frequency, Resampler::FastBSinc24);
622 mRing = CreateRingBuffer(outputFrameCount, mFrameSize, false);
623 if(!mRing) throw al::backend_exception{ALC_INVALID_VALUE, "Failed to allocate ring buffer"};
625 mDevice->DeviceName = name;
629 bool CoreAudioCapture::start()
631 OSStatus err{AudioOutputUnitStart(mAudioUnit)};
632 if(err != noErr)
634 ERR("AudioOutputUnitStart failed\n");
635 return false;
637 return true;
640 void CoreAudioCapture::stop()
642 OSStatus err{AudioOutputUnitStop(mAudioUnit)};
643 if(err != noErr)
644 ERR("AudioOutputUnitStop failed\n");
647 ALCenum CoreAudioCapture::captureSamples(al::byte *buffer, ALCuint samples)
649 if(!mConverter)
651 mRing->read(buffer, samples);
652 return ALC_NO_ERROR;
655 auto rec_vec = mRing->getReadVector();
656 const void *src0{rec_vec.first.buf};
657 auto src0len = static_cast<ALuint>(rec_vec.first.len);
658 ALuint got{mConverter->convert(&src0, &src0len, buffer, samples)};
659 size_t total_read{rec_vec.first.len - src0len};
660 if(got < samples && !src0len && rec_vec.second.len > 0)
662 const void *src1{rec_vec.second.buf};
663 auto src1len = static_cast<ALuint>(rec_vec.second.len);
664 got += mConverter->convert(&src1, &src1len, buffer+got, samples-got);
665 total_read += rec_vec.second.len - src1len;
668 mRing->readAdvance(total_read);
669 return ALC_NO_ERROR;
672 ALCuint CoreAudioCapture::availableSamples()
674 if(!mConverter) return static_cast<ALCuint>(mRing->readSpace());
675 return mConverter->availableOut(static_cast<ALCuint>(mRing->readSpace()));
678 } // namespace
680 BackendFactory &CoreAudioBackendFactory::getFactory()
682 static CoreAudioBackendFactory factory{};
683 return factory;
686 bool CoreAudioBackendFactory::init() { return true; }
688 bool CoreAudioBackendFactory::querySupport(BackendType type)
689 { return type == BackendType::Playback || type == BackendType::Capture; }
691 void CoreAudioBackendFactory::probe(DevProbe type, std::string *outnames)
693 switch(type)
695 case DevProbe::Playback:
696 case DevProbe::Capture:
697 /* Includes null char. */
698 outnames->append(ca_device, sizeof(ca_device));
699 break;
703 BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
705 if(type == BackendType::Playback)
706 return BackendPtr{new CoreAudioPlayback{device}};
707 if(type == BackendType::Capture)
708 return BackendPtr{new CoreAudioCapture{device}};
709 return nullptr;