Limit convolution processing to the output ambisonic order
[openal-soft.git] / alc / backends / coreaudio.cpp
blob25fb71ab8e300b6e26a5dc2c5e87bbe629e44b32
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 "logging.h"
35 #include "backends/base.h"
37 #include <unistd.h>
38 #include <AudioUnit/AudioUnit.h>
39 #include <AudioToolbox/AudioToolbox.h>
42 namespace {
44 static const ALCchar ca_device[] = "CoreAudio Default";
47 struct CoreAudioPlayback final : public BackendBase {
48 CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
49 ~CoreAudioPlayback() override;
51 OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
52 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
53 AudioBufferList *ioData) noexcept;
54 static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
55 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
56 AudioBufferList *ioData) noexcept
58 return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
59 inBusNumber, inNumberFrames, ioData);
62 void open(const ALCchar *name) override;
63 bool reset() override;
64 void start() override;
65 void stop() override;
67 AudioUnit mAudioUnit{};
69 ALuint mFrameSize{0u};
70 AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
72 DEF_NEWDEL(CoreAudioPlayback)
75 CoreAudioPlayback::~CoreAudioPlayback()
77 AudioUnitUninitialize(mAudioUnit);
78 AudioComponentInstanceDispose(mAudioUnit);
82 OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32,
83 UInt32, AudioBufferList *ioData) noexcept
85 mDevice->renderSamples(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize,
86 ioData->mBuffers[0].mNumberChannels);
87 return noErr;
91 void CoreAudioPlayback::open(const ALCchar *name)
93 if(!name)
94 name = ca_device;
95 else if(strcmp(name, ca_device) != 0)
96 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
98 /* open the default output unit */
99 AudioComponentDescription desc{};
100 desc.componentType = kAudioUnitType_Output;
101 #if TARGET_OS_IOS
102 desc.componentSubType = kAudioUnitSubType_RemoteIO;
103 #else
104 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
105 #endif
106 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
107 desc.componentFlags = 0;
108 desc.componentFlagsMask = 0;
110 AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
111 if(comp == nullptr)
112 throw al::backend_exception{ALC_INVALID_VALUE, "Could not find audio component"};
114 OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
115 if(err != noErr)
116 throw al::backend_exception{ALC_INVALID_VALUE, "Could not create component instance: %u",
117 err};
119 /* init and start the default audio unit... */
120 err = AudioUnitInitialize(mAudioUnit);
121 if(err != noErr)
122 throw al::backend_exception{ALC_INVALID_VALUE, "Could not initialize audio unit: %u", err};
124 mDevice->DeviceName = name;
127 bool CoreAudioPlayback::reset()
129 OSStatus err{AudioUnitUninitialize(mAudioUnit)};
130 if(err != noErr)
131 ERR("-- AudioUnitUninitialize failed.\n");
133 /* retrieve default output unit's properties (output side) */
134 AudioStreamBasicDescription streamFormat{};
135 auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
136 err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
137 0, &streamFormat, &size);
138 if(err != noErr || size != sizeof(AudioStreamBasicDescription))
140 ERR("AudioUnitGetProperty failed\n");
141 return false;
144 #if 0
145 TRACE("Output streamFormat of default output unit -\n");
146 TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
147 TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
148 TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
149 TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
150 TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
151 TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
152 #endif
154 /* set default output unit's input side to match output side */
155 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
156 0, &streamFormat, size);
157 if(err != noErr)
159 ERR("AudioUnitSetProperty failed\n");
160 return false;
163 if(mDevice->Frequency != streamFormat.mSampleRate)
165 mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
166 streamFormat.mSampleRate / mDevice->Frequency);
167 mDevice->Frequency = static_cast<ALuint>(streamFormat.mSampleRate);
170 /* FIXME: How to tell what channels are what in the output device, and how
171 * to specify what we're giving? eg, 6.0 vs 5.1 */
172 switch(streamFormat.mChannelsPerFrame)
174 case 1:
175 mDevice->FmtChans = DevFmtMono;
176 break;
177 case 2:
178 mDevice->FmtChans = DevFmtStereo;
179 break;
180 case 4:
181 mDevice->FmtChans = DevFmtQuad;
182 break;
183 case 6:
184 mDevice->FmtChans = DevFmtX51;
185 break;
186 case 7:
187 mDevice->FmtChans = DevFmtX61;
188 break;
189 case 8:
190 mDevice->FmtChans = DevFmtX71;
191 break;
192 default:
193 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
194 mDevice->FmtChans = DevFmtStereo;
195 streamFormat.mChannelsPerFrame = 2;
196 break;
198 setDefaultWFXChannelOrder();
200 /* use channel count and sample rate from the default output unit's current
201 * parameters, but reset everything else */
202 streamFormat.mFramesPerPacket = 1;
203 streamFormat.mFormatFlags = 0;
204 switch(mDevice->FmtType)
206 case DevFmtUByte:
207 mDevice->FmtType = DevFmtByte;
208 /* fall-through */
209 case DevFmtByte:
210 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
211 streamFormat.mBitsPerChannel = 8;
212 break;
213 case DevFmtUShort:
214 mDevice->FmtType = DevFmtShort;
215 /* fall-through */
216 case DevFmtShort:
217 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
218 streamFormat.mBitsPerChannel = 16;
219 break;
220 case DevFmtUInt:
221 mDevice->FmtType = DevFmtInt;
222 /* fall-through */
223 case DevFmtInt:
224 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
225 streamFormat.mBitsPerChannel = 32;
226 break;
227 case DevFmtFloat:
228 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
229 streamFormat.mBitsPerChannel = 32;
230 break;
232 streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
233 streamFormat.mBitsPerChannel / 8;
234 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
235 streamFormat.mFormatID = kAudioFormatLinearPCM;
236 streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
237 kLinearPCMFormatFlagIsPacked;
239 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
240 0, &streamFormat, sizeof(AudioStreamBasicDescription));
241 if(err != noErr)
243 ERR("AudioUnitSetProperty failed\n");
244 return false;
247 /* setup callback */
248 mFrameSize = mDevice->frameSizeFromFmt();
249 AURenderCallbackStruct input{};
250 input.inputProc = CoreAudioPlayback::MixerProcC;
251 input.inputProcRefCon = this;
253 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
254 kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
255 if(err != noErr)
257 ERR("AudioUnitSetProperty failed\n");
258 return false;
261 /* init the default audio unit... */
262 err = AudioUnitInitialize(mAudioUnit);
263 if(err != noErr)
265 ERR("AudioUnitInitialize failed\n");
266 return false;
269 return true;
272 void CoreAudioPlayback::start()
274 const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
275 if(err != noErr)
276 throw al::backend_exception{ALC_INVALID_DEVICE, "AudioOutputUnitStart failed: %d", err};
279 void CoreAudioPlayback::stop()
281 OSStatus err{AudioOutputUnitStop(mAudioUnit)};
282 if(err != noErr)
283 ERR("AudioOutputUnitStop failed\n");
287 struct CoreAudioCapture final : public BackendBase {
288 CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
289 ~CoreAudioCapture() override;
291 OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
292 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
293 UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
294 static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
295 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
296 AudioBufferList *ioData) noexcept
298 return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
299 inBusNumber, inNumberFrames, ioData);
302 void open(const ALCchar *name) override;
303 void start() override;
304 void stop() override;
305 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
306 ALCuint availableSamples() override;
308 AudioUnit mAudioUnit{0};
310 ALuint mFrameSize{0u};
311 AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
313 SampleConverterPtr mConverter;
315 RingBufferPtr mRing{nullptr};
317 DEF_NEWDEL(CoreAudioCapture)
320 CoreAudioCapture::~CoreAudioCapture()
322 if(mAudioUnit)
323 AudioComponentInstanceDispose(mAudioUnit);
324 mAudioUnit = 0;
328 OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
329 const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames,
330 AudioBufferList*) noexcept
332 AudioUnitRenderActionFlags flags = 0;
333 union {
334 al::byte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2];
335 AudioBufferList list;
336 } audiobuf{};
338 auto rec_vec = mRing->getWriteVector();
339 inNumberFrames = static_cast<UInt32>(minz(inNumberFrames,
340 rec_vec.first.len+rec_vec.second.len));
342 // Fill the ringbuffer's two segments with data from the input device
343 if(rec_vec.first.len >= inNumberFrames)
345 audiobuf.list.mNumberBuffers = 1;
346 audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
347 audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
348 audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame;
350 else
352 const auto remaining = static_cast<ALuint>(inNumberFrames - rec_vec.first.len);
353 audiobuf.list.mNumberBuffers = 2;
354 audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
355 audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
356 audiobuf.list.mBuffers[0].mDataByteSize = static_cast<UInt32>(rec_vec.first.len) *
357 mFormat.mBytesPerFrame;
358 audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame;
359 audiobuf.list.mBuffers[1].mData = rec_vec.second.buf;
360 audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame;
362 OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers,
363 inNumberFrames, &audiobuf.list)};
364 if(err != noErr)
366 ERR("AudioUnitRender error: %d\n", err);
367 return err;
370 mRing->writeAdvance(inNumberFrames);
371 return noErr;
375 void CoreAudioCapture::open(const ALCchar *name)
377 AudioStreamBasicDescription requestedFormat; // The application requested format
378 AudioStreamBasicDescription hardwareFormat; // The hardware format
379 AudioStreamBasicDescription outputFormat; // The AudioUnit output format
380 AURenderCallbackStruct input;
381 AudioComponentDescription desc;
382 UInt32 outputFrameCount;
383 UInt32 propertySize;
384 #if !TARGET_OS_IOS
385 AudioObjectPropertyAddress propertyAddress;
386 #endif
387 UInt32 enableIO;
388 AudioComponent comp;
389 OSStatus err;
391 if(!name)
392 name = ca_device;
393 else if(strcmp(name, ca_device) != 0)
394 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
396 desc.componentType = kAudioUnitType_Output;
397 #if TARGET_OS_IOS
398 desc.componentSubType = kAudioUnitSubType_RemoteIO;
399 #else
400 desc.componentSubType = kAudioUnitSubType_HALOutput;
401 #endif
402 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
403 desc.componentFlags = 0;
404 desc.componentFlagsMask = 0;
406 // Search for component with given description
407 comp = AudioComponentFindNext(NULL, &desc);
408 if(comp == NULL)
409 throw al::backend_exception{ALC_INVALID_VALUE, "Could not find audio component"};
411 // Open the component
412 err = AudioComponentInstanceNew(comp, &mAudioUnit);
413 if(err != noErr)
414 throw al::backend_exception{ALC_INVALID_VALUE, "Could not create component instance: %u",
415 err};
417 // Turn off AudioUnit output
418 enableIO = 0;
419 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
420 kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
421 if(err != noErr)
422 throw al::backend_exception{ALC_INVALID_VALUE,
423 "Could not disable audio unit output property: %u", err};
425 // Turn on AudioUnit input
426 enableIO = 1;
427 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
428 kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
429 if(err != noErr)
430 throw al::backend_exception{ALC_INVALID_VALUE,
431 "Could not enable audio unit input property: %u", err};
433 #if !TARGET_OS_IOS
435 // Get the default input device
436 AudioDeviceID inputDevice = kAudioDeviceUnknown;
438 propertySize = sizeof(AudioDeviceID);
439 propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
440 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
441 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
443 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr,
444 &propertySize, &inputDevice);
445 if(err != noErr)
446 throw al::backend_exception{ALC_INVALID_VALUE, "Could not get input device: %u", err};
447 if(inputDevice == kAudioDeviceUnknown)
448 throw al::backend_exception{ALC_INVALID_VALUE, "Unknown input device"};
450 // Track the input device
451 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
452 kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
453 if(err != noErr)
454 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set input device: %u", err};
456 #endif
458 // set capture callback
459 input.inputProc = CoreAudioCapture::RecordProcC;
460 input.inputProcRefCon = this;
462 err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
463 kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
464 if(err != noErr)
465 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set capture callback: %u", err};
467 // Initialize the device
468 err = AudioUnitInitialize(mAudioUnit);
469 if(err != noErr)
470 throw al::backend_exception{ALC_INVALID_VALUE, "Could not initialize audio unit: %u", err};
472 // Get the hardware format
473 propertySize = sizeof(AudioStreamBasicDescription);
474 err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
475 1, &hardwareFormat, &propertySize);
476 if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
477 throw al::backend_exception{ALC_INVALID_VALUE, "Could not get input format: %u", err};
479 // Set up the requested format description
480 switch(mDevice->FmtType)
482 case DevFmtUByte:
483 requestedFormat.mBitsPerChannel = 8;
484 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
485 break;
486 case DevFmtShort:
487 requestedFormat.mBitsPerChannel = 16;
488 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
489 break;
490 case DevFmtInt:
491 requestedFormat.mBitsPerChannel = 32;
492 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
493 break;
494 case DevFmtFloat:
495 requestedFormat.mBitsPerChannel = 32;
496 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
497 break;
498 case DevFmtByte:
499 case DevFmtUShort:
500 case DevFmtUInt:
501 throw al::backend_exception{ALC_INVALID_VALUE, "%s samples not suppoted",
502 DevFmtTypeString(mDevice->FmtType)};
505 switch(mDevice->FmtChans)
507 case DevFmtMono:
508 requestedFormat.mChannelsPerFrame = 1;
509 break;
510 case DevFmtStereo:
511 requestedFormat.mChannelsPerFrame = 2;
512 break;
514 case DevFmtQuad:
515 case DevFmtX51:
516 case DevFmtX51Rear:
517 case DevFmtX61:
518 case DevFmtX71:
519 case DevFmtAmbi3D:
520 throw al::backend_exception{ALC_INVALID_VALUE, "%s not supported",
521 DevFmtChannelsString(mDevice->FmtChans)};
524 requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
525 requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
526 requestedFormat.mSampleRate = mDevice->Frequency;
527 requestedFormat.mFormatID = kAudioFormatLinearPCM;
528 requestedFormat.mReserved = 0;
529 requestedFormat.mFramesPerPacket = 1;
531 // save requested format description for later use
532 mFormat = requestedFormat;
533 mFrameSize = mDevice->frameSizeFromFmt();
535 // Use intermediate format for sample rate conversion (outputFormat)
536 // Set sample rate to the same as hardware for resampling later
537 outputFormat = requestedFormat;
538 outputFormat.mSampleRate = hardwareFormat.mSampleRate;
540 // The output format should be the requested format, but using the hardware sample rate
541 // This is because the AudioUnit will automatically scale other properties, except for sample rate
542 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
543 1, &outputFormat, sizeof(outputFormat));
544 if(err != noErr)
545 throw al::backend_exception{ALC_INVALID_VALUE, "Could not set input format: %u", err};
547 // Set the AudioUnit output format frame count
548 uint64_t FrameCount64{mDevice->UpdateSize};
549 FrameCount64 = static_cast<uint64_t>(FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) /
550 mDevice->Frequency;
551 FrameCount64 += MAX_RESAMPLER_PADDING;
552 if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2)
553 throw al::backend_exception{ALC_INVALID_VALUE,
554 "Calculated frame count is too large: %" PRIu64, FrameCount64};
556 outputFrameCount = static_cast<uint32_t>(FrameCount64);
557 err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
558 kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
559 if(err != noErr)
560 throw al::backend_exception{ALC_INVALID_VALUE, "Failed to set capture frame count: %u",
561 err};
563 // Set up sample converter if needed
564 if(outputFormat.mSampleRate != mDevice->Frequency)
565 mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
566 mFormat.mChannelsPerFrame, static_cast<ALuint>(hardwareFormat.mSampleRate),
567 mDevice->Frequency, Resampler::FastBSinc24);
569 mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
571 mDevice->DeviceName = name;
575 void CoreAudioCapture::start()
577 OSStatus err{AudioOutputUnitStart(mAudioUnit)};
578 if(err != noErr)
579 throw al::backend_exception{ALC_INVALID_DEVICE, "AudioOutputUnitStart failed: %d", err};
582 void CoreAudioCapture::stop()
584 OSStatus err{AudioOutputUnitStop(mAudioUnit)};
585 if(err != noErr)
586 ERR("AudioOutputUnitStop failed\n");
589 ALCenum CoreAudioCapture::captureSamples(al::byte *buffer, ALCuint samples)
591 if(!mConverter)
593 mRing->read(buffer, samples);
594 return ALC_NO_ERROR;
597 auto rec_vec = mRing->getReadVector();
598 const void *src0{rec_vec.first.buf};
599 auto src0len = static_cast<ALuint>(rec_vec.first.len);
600 ALuint got{mConverter->convert(&src0, &src0len, buffer, samples)};
601 size_t total_read{rec_vec.first.len - src0len};
602 if(got < samples && !src0len && rec_vec.second.len > 0)
604 const void *src1{rec_vec.second.buf};
605 auto src1len = static_cast<ALuint>(rec_vec.second.len);
606 got += mConverter->convert(&src1, &src1len, buffer+got, samples-got);
607 total_read += rec_vec.second.len - src1len;
610 mRing->readAdvance(total_read);
611 return ALC_NO_ERROR;
614 ALCuint CoreAudioCapture::availableSamples()
616 if(!mConverter) return static_cast<ALCuint>(mRing->readSpace());
617 return mConverter->availableOut(static_cast<ALCuint>(mRing->readSpace()));
620 } // namespace
622 BackendFactory &CoreAudioBackendFactory::getFactory()
624 static CoreAudioBackendFactory factory{};
625 return factory;
628 bool CoreAudioBackendFactory::init() { return true; }
630 bool CoreAudioBackendFactory::querySupport(BackendType type)
631 { return type == BackendType::Playback || type == BackendType::Capture; }
633 std::string CoreAudioBackendFactory::probe(BackendType type)
635 std::string outnames;
636 switch(type)
638 case BackendType::Playback:
639 case BackendType::Capture:
640 /* Includes null char. */
641 outnames.append(ca_device, sizeof(ca_device));
642 break;
644 return outnames;
647 BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
649 if(type == BackendType::Playback)
650 return BackendPtr{new CoreAudioPlayback{device}};
651 if(type == BackendType::Capture)
652 return BackendPtr{new CoreAudioCapture{device}};
653 return nullptr;