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"
34 #include "backends/base.h"
37 #include <AudioUnit/AudioUnit.h>
38 #include <AudioToolbox/AudioToolbox.h>
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
;
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
);
90 void CoreAudioPlayback::open(const ALCchar
*name
)
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
;
101 desc
.componentSubType
= kAudioUnitSubType_RemoteIO
;
103 desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
105 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
106 desc
.componentFlags
= 0;
107 desc
.componentFlagsMask
= 0;
109 AudioComponent comp
{AudioComponentFindNext(NULL
, &desc
)};
112 ERR("AudioComponentFindNext failed\n");
113 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not find audio component"};
116 OSStatus err
{AudioComponentInstanceNew(comp
, &mAudioUnit
)};
119 ERR("AudioComponentInstanceNew failed\n");
120 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not create component instance: %u",
124 /* init and start the default audio unit... */
125 err
= AudioUnitInitialize(mAudioUnit
);
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
)};
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");
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
);
162 /* set default output unit's input side to match output side */
163 err
= AudioUnitSetProperty(mAudioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
,
164 0, &streamFormat
, size
);
167 ERR("AudioUnitSetProperty failed\n");
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
)
183 mDevice
->FmtChans
= DevFmtMono
;
186 mDevice
->FmtChans
= DevFmtStereo
;
189 mDevice
->FmtChans
= DevFmtQuad
;
192 mDevice
->FmtChans
= DevFmtX51
;
195 mDevice
->FmtChans
= DevFmtX61
;
198 mDevice
->FmtChans
= DevFmtX71
;
201 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat
.mChannelsPerFrame
);
202 mDevice
->FmtChans
= DevFmtStereo
;
203 streamFormat
.mChannelsPerFrame
= 2;
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
)
215 mDevice
->FmtType
= DevFmtByte
;
218 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
219 streamFormat
.mBitsPerChannel
= 8;
222 mDevice
->FmtType
= DevFmtShort
;
225 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
226 streamFormat
.mBitsPerChannel
= 16;
229 mDevice
->FmtType
= DevFmtInt
;
232 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
233 streamFormat
.mBitsPerChannel
= 32;
236 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsFloat
;
237 streamFormat
.mBitsPerChannel
= 32;
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
));
251 ERR("AudioUnitSetProperty failed\n");
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
));
265 ERR("AudioUnitSetProperty failed\n");
269 /* init the default audio unit... */
270 err
= AudioUnitInitialize(mAudioUnit
);
273 ERR("AudioUnitInitialize failed\n");
280 bool CoreAudioPlayback::start()
282 OSStatus err
{AudioOutputUnitStart(mAudioUnit
)};
285 ERR("AudioOutputUnitStart failed\n");
291 void CoreAudioPlayback::stop()
293 OSStatus err
{AudioOutputUnitStop(mAudioUnit
)};
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()
335 AudioComponentInstanceDispose(mAudioUnit
);
340 OSStatus
CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags
*,
341 const AudioTimeStamp
*inTimeStamp
, UInt32
, UInt32 inNumberFrames
,
344 AudioUnitRenderActionFlags flags
= 0;
346 al::byte _
[sizeof(AudioBufferList
) + sizeof(AudioBuffer
)*2];
347 AudioBufferList list
;
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
;
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
)};
378 ERR("AudioUnitRender error: %d\n", err
);
382 mRing
->writeAdvance(inNumberFrames
);
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
;
397 AudioObjectPropertyAddress propertyAddress
;
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
;
410 desc
.componentSubType
= kAudioUnitSubType_RemoteIO
;
412 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
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
);
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
);
430 ERR("AudioComponentInstanceNew failed\n");
431 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not create component instance: %u",
435 // Turn off AudioUnit output
437 err
= AudioUnitSetProperty(mAudioUnit
, kAudioOutputUnitProperty_EnableIO
,
438 kAudioUnitScope_Output
, 0, &enableIO
, sizeof(ALuint
));
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
448 err
= AudioUnitSetProperty(mAudioUnit
, kAudioOutputUnitProperty_EnableIO
,
449 kAudioUnitScope_Input
, 1, &enableIO
, sizeof(ALuint
));
452 ERR("AudioUnitSetProperty failed\n");
453 throw al::backend_exception
{ALC_INVALID_VALUE
,
454 "Could not enable audio unit input property: %u", err
};
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
);
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
));
485 ERR("AudioUnitSetProperty failed\n");
486 throw al::backend_exception
{ALC_INVALID_VALUE
, "Could not set input device: %u", err
};
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
));
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
);
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
)
525 requestedFormat
.mBitsPerChannel
= 8;
526 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
529 requestedFormat
.mBitsPerChannel
= 16;
530 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
533 requestedFormat
.mBitsPerChannel
= 32;
534 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
537 requestedFormat
.mBitsPerChannel
= 32;
538 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
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
)
551 requestedFormat
.mChannelsPerFrame
= 1;
554 requestedFormat
.mChannelsPerFrame
= 2;
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
));
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) /
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
));
611 ERR("AudioUnitSetProperty failed: %d\n", err
);
612 throw al::backend_exception
{ALC_INVALID_VALUE
, "Failed to set capture frame count: %u",
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
)};
634 ERR("AudioOutputUnitStart failed\n");
640 void CoreAudioCapture::stop()
642 OSStatus err
{AudioOutputUnitStop(mAudioUnit
)};
644 ERR("AudioOutputUnitStop failed\n");
647 ALCenum
CoreAudioCapture::captureSamples(al::byte
*buffer
, ALCuint samples
)
651 mRing
->read(buffer
, samples
);
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
);
672 ALCuint
CoreAudioCapture::availableSamples()
674 if(!mConverter
) return static_cast<ALCuint
>(mRing
->readSpace());
675 return mConverter
->availableOut(static_cast<ALCuint
>(mRing
->readSpace()));
680 BackendFactory
&CoreAudioBackendFactory::getFactory()
682 static CoreAudioBackendFactory 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
)
695 case DevProbe::Playback
:
696 case DevProbe::Capture
:
697 /* Includes null char. */
698 outnames
->append(ca_device
, sizeof(ca_device
));
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
}};