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
23 #include "backends/coreaudio.h"
32 #include "ringbuffer.h"
33 #include "converter.h"
35 #include "backends/base.h"
38 #include <AudioUnit/AudioUnit.h>
39 #include <AudioToolbox/AudioToolbox.h>
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
;
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
);
91 void CoreAudioPlayback::open(const ALCchar
*name
)
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
;
102 desc
.componentSubType
= kAudioUnitSubType_RemoteIO
;
104 desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
106 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
107 desc
.componentFlags
= 0;
108 desc
.componentFlagsMask
= 0;
110 AudioComponent comp
{AudioComponentFindNext(NULL
, &desc
)};
112 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not find audio component"};
114 OSStatus err
{AudioComponentInstanceNew(comp
, &mAudioUnit
)};
116 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not create component instance: %u",
119 /* init and start the default audio unit... */
120 err
= AudioUnitInitialize(mAudioUnit
);
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
)};
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");
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
);
154 /* set default output unit's input side to match output side */
155 err
= AudioUnitSetProperty(mAudioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
,
156 0, &streamFormat
, size
);
159 ERR("AudioUnitSetProperty failed\n");
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
)
175 mDevice
->FmtChans
= DevFmtMono
;
178 mDevice
->FmtChans
= DevFmtStereo
;
181 mDevice
->FmtChans
= DevFmtQuad
;
184 mDevice
->FmtChans
= DevFmtX51
;
187 mDevice
->FmtChans
= DevFmtX61
;
190 mDevice
->FmtChans
= DevFmtX71
;
193 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat
.mChannelsPerFrame
);
194 mDevice
->FmtChans
= DevFmtStereo
;
195 streamFormat
.mChannelsPerFrame
= 2;
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
)
207 mDevice
->FmtType
= DevFmtByte
;
210 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
211 streamFormat
.mBitsPerChannel
= 8;
214 mDevice
->FmtType
= DevFmtShort
;
217 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
218 streamFormat
.mBitsPerChannel
= 16;
221 mDevice
->FmtType
= DevFmtInt
;
224 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
225 streamFormat
.mBitsPerChannel
= 32;
228 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsFloat
;
229 streamFormat
.mBitsPerChannel
= 32;
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
));
243 ERR("AudioUnitSetProperty failed\n");
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
));
257 ERR("AudioUnitSetProperty failed\n");
261 /* init the default audio unit... */
262 err
= AudioUnitInitialize(mAudioUnit
);
265 ERR("AudioUnitInitialize failed\n");
272 void CoreAudioPlayback::start()
274 const OSStatus err
{AudioOutputUnitStart(mAudioUnit
)};
276 throw al::backend_exception
{ALC_INVALID_DEVICE
, "AudioOutputUnitStart failed: %d", err
};
279 void CoreAudioPlayback::stop()
281 OSStatus err
{AudioOutputUnitStop(mAudioUnit
)};
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()
323 AudioComponentInstanceDispose(mAudioUnit
);
328 OSStatus
CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags
*,
329 const AudioTimeStamp
*inTimeStamp
, UInt32
, UInt32 inNumberFrames
,
330 AudioBufferList
*) noexcept
332 AudioUnitRenderActionFlags flags
= 0;
334 al::byte _
[sizeof(AudioBufferList
) + sizeof(AudioBuffer
)*2];
335 AudioBufferList list
;
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
;
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
)};
366 ERR("AudioUnitRender error: %d\n", err
);
370 mRing
->writeAdvance(inNumberFrames
);
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
;
385 AudioObjectPropertyAddress propertyAddress
;
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
;
398 desc
.componentSubType
= kAudioUnitSubType_RemoteIO
;
400 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
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
);
409 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not find audio component"};
411 // Open the component
412 err
= AudioComponentInstanceNew(comp
, &mAudioUnit
);
414 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not create component instance: %u",
417 // Turn off AudioUnit output
419 err
= AudioUnitSetProperty(mAudioUnit
, kAudioOutputUnitProperty_EnableIO
,
420 kAudioUnitScope_Output
, 0, &enableIO
, sizeof(ALuint
));
422 throw al::backend_exception
{ALC_INVALID_VALUE
,
423 "Could not disable audio unit output property: %u", err
};
425 // Turn on AudioUnit input
427 err
= AudioUnitSetProperty(mAudioUnit
, kAudioOutputUnitProperty_EnableIO
,
428 kAudioUnitScope_Input
, 1, &enableIO
, sizeof(ALuint
));
430 throw al::backend_exception
{ALC_INVALID_VALUE
,
431 "Could not enable audio unit input property: %u", err
};
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
);
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
));
454 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not set input device: %u", err
};
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
));
465 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not set capture callback: %u", err
};
467 // Initialize the device
468 err
= AudioUnitInitialize(mAudioUnit
);
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
)
483 requestedFormat
.mBitsPerChannel
= 8;
484 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
487 requestedFormat
.mBitsPerChannel
= 16;
488 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
491 requestedFormat
.mBitsPerChannel
= 32;
492 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
495 requestedFormat
.mBitsPerChannel
= 32;
496 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
501 throw al::backend_exception
{ALC_INVALID_VALUE
, "%s samples not suppoted",
502 DevFmtTypeString(mDevice
->FmtType
)};
505 switch(mDevice
->FmtChans
)
508 requestedFormat
.mChannelsPerFrame
= 1;
511 requestedFormat
.mChannelsPerFrame
= 2;
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
));
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) /
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
));
560 throw al::backend_exception
{ALC_INVALID_VALUE
, "Failed to set capture frame count: %u",
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
)};
579 throw al::backend_exception
{ALC_INVALID_DEVICE
, "AudioOutputUnitStart failed: %d", err
};
582 void CoreAudioCapture::stop()
584 OSStatus err
{AudioOutputUnitStop(mAudioUnit
)};
586 ERR("AudioOutputUnitStop failed\n");
589 ALCenum
CoreAudioCapture::captureSamples(al::byte
*buffer
, ALCuint samples
)
593 mRing
->read(buffer
, samples
);
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
);
614 ALCuint
CoreAudioCapture::availableSamples()
616 if(!mConverter
) return static_cast<ALCuint
>(mRing
->readSpace());
617 return mConverter
->availableOut(static_cast<ALCuint
>(mRing
->readSpace()));
622 BackendFactory
&CoreAudioBackendFactory::getFactory()
624 static CoreAudioBackendFactory 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
;
638 case BackendType::Playback
:
639 case BackendType::Capture
:
640 /* Includes null char. */
641 outnames
.append(ca_device
, sizeof(ca_device
));
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
}};