diff options
Diffstat (limited to 'alc/backends/coreaudio.cpp')
-rw-r--r-- | alc/backends/coreaudio.cpp | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp new file mode 100644 index 00000000..b4b46382 --- /dev/null +++ b/alc/backends/coreaudio.cpp @@ -0,0 +1,709 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "backends/coreaudio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "alcmain.h" +#include "alu.h" +#include "ringbuffer.h" +#include "converter.h" +#include "backends/base.h" + +#include <unistd.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + + +namespace { + +static const ALCchar ca_device[] = "CoreAudio Default"; + + +struct CoreAudioPlayback final : public BackendBase { + CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { } + ~CoreAudioPlayback() override; + + static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + + ALCenum open(const ALCchar *name) override; + ALCboolean reset() override; + ALCboolean start() override; + void stop() override; + + AudioUnit mAudioUnit; + + ALuint mFrameSize{0u}; + AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD + + DEF_NEWDEL(CoreAudioPlayback) +}; + +CoreAudioPlayback::~CoreAudioPlayback() +{ + AudioUnitUninitialize(mAudioUnit); + AudioComponentInstanceDispose(mAudioUnit); +} + + +OSStatus CoreAudioPlayback::MixerProcC(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, + inBusNumber, inNumberFrames, ioData); +} + +OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, + const AudioTimeStamp*, UInt32, UInt32, AudioBufferList *ioData) +{ + lock(); + aluMixData(mDevice, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize); + unlock(); + return noErr; +} + + +ALCenum CoreAudioPlayback::open(const ALCchar *name) +{ + if(!name) + name = ca_device; + else if(strcmp(name, ca_device) != 0) + return ALC_INVALID_VALUE; + + /* open the default output unit */ + AudioComponentDescription desc{}; + desc.componentType = kAudioUnitType_Output; +#if TARGET_OS_IOS + desc.componentSubType = kAudioUnitSubType_RemoteIO; +#else + desc.componentSubType = kAudioUnitSubType_DefaultOutput; +#endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + AudioComponent comp{AudioComponentFindNext(NULL, &desc)}; + if(comp == nullptr) + { + ERR("AudioComponentFindNext failed\n"); + return ALC_INVALID_VALUE; + } + + OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)}; + if(err != noErr) + { + ERR("AudioComponentInstanceNew failed\n"); + return ALC_INVALID_VALUE; + } + + /* init and start the default audio unit... */ + err = AudioUnitInitialize(mAudioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + AudioComponentInstanceDispose(mAudioUnit); + return ALC_INVALID_VALUE; + } + + mDevice->DeviceName = name; + return ALC_NO_ERROR; +} + +ALCboolean CoreAudioPlayback::reset() +{ + OSStatus err{AudioUnitUninitialize(mAudioUnit)}; + if(err != noErr) + ERR("-- AudioUnitUninitialize failed.\n"); + + /* retrieve default output unit's properties (output side) */ + AudioStreamBasicDescription streamFormat{}; + auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription)); + err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + 0, &streamFormat, &size); + if(err != noErr || size != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + return ALC_FALSE; + } + +#if 0 + TRACE("Output streamFormat of default output unit -\n"); + TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); + TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); + TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); + TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); + TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); + TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); +#endif + + /* set default output unit's input side to match output side */ + err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, &streamFormat, size); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + if(mDevice->Frequency != streamFormat.mSampleRate) + { + mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} * + streamFormat.mSampleRate / mDevice->Frequency); + mDevice->Frequency = streamFormat.mSampleRate; + } + + /* FIXME: How to tell what channels are what in the output device, and how + * to specify what we're giving? eg, 6.0 vs 5.1 */ + switch(streamFormat.mChannelsPerFrame) + { + case 1: + mDevice->FmtChans = DevFmtMono; + break; + case 2: + mDevice->FmtChans = DevFmtStereo; + break; + case 4: + mDevice->FmtChans = DevFmtQuad; + break; + case 6: + mDevice->FmtChans = DevFmtX51; + break; + case 7: + mDevice->FmtChans = DevFmtX61; + break; + case 8: + mDevice->FmtChans = DevFmtX71; + break; + default: + ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame); + mDevice->FmtChans = DevFmtStereo; + streamFormat.mChannelsPerFrame = 2; + break; + } + SetDefaultWFXChannelOrder(mDevice); + + /* use channel count and sample rate from the default output unit's current + * parameters, but reset everything else */ + streamFormat.mFramesPerPacket = 1; + streamFormat.mFormatFlags = 0; + switch(mDevice->FmtType) + { + case DevFmtUByte: + mDevice->FmtType = DevFmtByte; + /* fall-through */ + case DevFmtByte: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 8; + break; + case DevFmtUShort: + mDevice->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 16; + break; + case DevFmtUInt: + mDevice->FmtType = DevFmtInt; + /* fall-through */ + case DevFmtInt: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + streamFormat.mBitsPerChannel = 32; + break; + case DevFmtFloat: + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; + streamFormat.mBitsPerChannel = 32; + break; + } + streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * + streamFormat.mBitsPerChannel / 8; + streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian | + kLinearPCMFormatFlagIsPacked; + + err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, &streamFormat, sizeof(AudioStreamBasicDescription)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* setup callback */ + mFrameSize = mDevice->frameSizeFromFmt(); + AURenderCallbackStruct input{}; + input.inputProc = CoreAudioPlayback::MixerProcC; + input.inputProcRefCon = this; + + err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* init the default audio unit... */ + err = AudioUnitInitialize(mAudioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +ALCboolean CoreAudioPlayback::start() +{ + OSStatus err{AudioOutputUnitStart(mAudioUnit)}; + if(err != noErr) + { + ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + return ALC_TRUE; +} + +void CoreAudioPlayback::stop() +{ + OSStatus err{AudioOutputUnitStop(mAudioUnit)}; + if(err != noErr) + ERR("AudioOutputUnitStop failed\n"); +} + + +struct CoreAudioCapture final : public BackendBase { + CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { } + ~CoreAudioCapture() override; + + static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *ioData); + + ALCenum open(const ALCchar *name) override; + ALCboolean start() override; + void stop() override; + ALCenum captureSamples(void *buffer, ALCuint samples) override; + ALCuint availableSamples() override; + + AudioUnit mAudioUnit{0}; + + ALuint mFrameSize{0u}; + AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD + + SampleConverterPtr mConverter; + + RingBufferPtr mRing{nullptr}; + + DEF_NEWDEL(CoreAudioCapture) +}; + +CoreAudioCapture::~CoreAudioCapture() +{ + if(mAudioUnit) + AudioComponentInstanceDispose(mAudioUnit); + mAudioUnit = 0; +} + + +OSStatus CoreAudioCapture::RecordProcC(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, + inBusNumber, inNumberFrames, ioData); +} + +OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*, + const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames, + AudioBufferList*) +{ + AudioUnitRenderActionFlags flags = 0; + union { + ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2]; + AudioBufferList list; + } audiobuf = { { 0 } }; + + auto rec_vec = mRing->getWriteVector(); + inNumberFrames = minz(inNumberFrames, rec_vec.first.len+rec_vec.second.len); + + // Fill the ringbuffer's two segments with data from the input device + if(rec_vec.first.len >= inNumberFrames) + { + audiobuf.list.mNumberBuffers = 1; + audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame; + audiobuf.list.mBuffers[0].mData = rec_vec.first.buf; + audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame; + } + else + { + const size_t remaining{inNumberFrames-rec_vec.first.len}; + audiobuf.list.mNumberBuffers = 2; + audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame; + audiobuf.list.mBuffers[0].mData = rec_vec.first.buf; + audiobuf.list.mBuffers[0].mDataByteSize = rec_vec.first.len * mFormat.mBytesPerFrame; + audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame; + audiobuf.list.mBuffers[1].mData = rec_vec.second.buf; + audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame; + } + OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers, + inNumberFrames, &audiobuf.list)}; + if(err != noErr) + { + ERR("AudioUnitRender error: %d\n", err); + return err; + } + + mRing->writeAdvance(inNumberFrames); + return noErr; +} + + +ALCenum CoreAudioCapture::open(const ALCchar *name) +{ + AudioStreamBasicDescription requestedFormat; // The application requested format + AudioStreamBasicDescription hardwareFormat; // The hardware format + AudioStreamBasicDescription outputFormat; // The AudioUnit output format + AURenderCallbackStruct input; + AudioComponentDescription desc; + UInt32 outputFrameCount; + UInt32 propertySize; + AudioObjectPropertyAddress propertyAddress; + UInt32 enableIO; + AudioComponent comp; + OSStatus err; + + if(!name) + name = ca_device; + else if(strcmp(name, ca_device) != 0) + return ALC_INVALID_VALUE; + + desc.componentType = kAudioUnitType_Output; +#if TARGET_OS_IOS + desc.componentSubType = kAudioUnitSubType_RemoteIO; +#else + desc.componentSubType = kAudioUnitSubType_HALOutput; +#endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + // Search for component with given description + comp = AudioComponentFindNext(NULL, &desc); + if(comp == NULL) + { + ERR("AudioComponentFindNext failed\n"); + return ALC_INVALID_VALUE; + } + + // Open the component + err = AudioComponentInstanceNew(comp, &mAudioUnit); + if(err != noErr) + { + ERR("AudioComponentInstanceNew failed\n"); + return ALC_INVALID_VALUE; + } + + // Turn off AudioUnit output + enableIO = 0; + err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_INVALID_VALUE; + } + + // Turn on AudioUnit input + enableIO = 1; + err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_INVALID_VALUE; + } + +#if !TARGET_OS_IOS + { + // Get the default input device + AudioDeviceID inputDevice = kAudioDeviceUnknown; + + propertySize = sizeof(AudioDeviceID); + propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + propertyAddress.mElement = kAudioObjectPropertyElementMaster; + + err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice); + if(err != noErr) + { + ERR("AudioObjectGetPropertyData failed\n"); + return ALC_INVALID_VALUE; + } + if(inputDevice == kAudioDeviceUnknown) + { + ERR("No input device found\n"); + return ALC_INVALID_VALUE; + } + + // Track the input device + err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_INVALID_VALUE; + } + } +#endif + + // set capture callback + input.inputProc = CoreAudioCapture::RecordProcC; + input.inputProcRefCon = this; + + err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_INVALID_VALUE; + } + + // Initialize the device + err = AudioUnitInitialize(mAudioUnit); + if(err != noErr) + { + ERR("AudioUnitInitialize failed\n"); + return ALC_INVALID_VALUE; + } + + // Get the hardware format + propertySize = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 1, &hardwareFormat, &propertySize); + if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription)) + { + ERR("AudioUnitGetProperty failed\n"); + return ALC_INVALID_VALUE; + } + + // Set up the requested format description + switch(mDevice->FmtType) + { + case DevFmtUByte: + requestedFormat.mBitsPerChannel = 8; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtShort: + requestedFormat.mBitsPerChannel = 16; + requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + case DevFmtInt: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + case DevFmtFloat: + requestedFormat.mBitsPerChannel = 32; + requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked; + break; + case DevFmtByte: + case DevFmtUShort: + case DevFmtUInt: + ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType)); + return ALC_INVALID_VALUE; + } + + switch(mDevice->FmtChans) + { + case DevFmtMono: + requestedFormat.mChannelsPerFrame = 1; + break; + case DevFmtStereo: + requestedFormat.mChannelsPerFrame = 2; + break; + + case DevFmtQuad: + case DevFmtX51: + case DevFmtX51Rear: + case DevFmtX61: + case DevFmtX71: + case DevFmtAmbi3D: + ERR("%s not supported\n", DevFmtChannelsString(mDevice->FmtChans)); + return ALC_INVALID_VALUE; + } + + requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8; + requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame; + requestedFormat.mSampleRate = mDevice->Frequency; + requestedFormat.mFormatID = kAudioFormatLinearPCM; + requestedFormat.mReserved = 0; + requestedFormat.mFramesPerPacket = 1; + + // save requested format description for later use + mFormat = requestedFormat; + mFrameSize = mDevice->frameSizeFromFmt(); + + // Use intermediate format for sample rate conversion (outputFormat) + // Set sample rate to the same as hardware for resampling later + outputFormat = requestedFormat; + outputFormat.mSampleRate = hardwareFormat.mSampleRate; + + // The output format should be the requested format, but using the hardware sample rate + // This is because the AudioUnit will automatically scale other properties, except for sample rate + err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + 1, (void*)&outputFormat, sizeof(outputFormat)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed\n"); + return ALC_INVALID_VALUE; + } + + // Set the AudioUnit output format frame count + uint64_t FrameCount64{mDevice->UpdateSize}; + FrameCount64 = (FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) / + mDevice->Frequency; + FrameCount64 += MAX_RESAMPLE_PADDING*2; + if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2) + { + ERR("FrameCount too large\n"); + return ALC_INVALID_VALUE; + } + + outputFrameCount = static_cast<uint32_t>(FrameCount64); + err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount)); + if(err != noErr) + { + ERR("AudioUnitSetProperty failed: %d\n", err); + return ALC_INVALID_VALUE; + } + + // Set up sample converter if needed + if(outputFormat.mSampleRate != mDevice->Frequency) + mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType, + mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, mDevice->Frequency, + BSinc24Resampler); + + mRing = CreateRingBuffer(outputFrameCount, mFrameSize, false); + if(!mRing) return ALC_INVALID_VALUE; + + mDevice->DeviceName = name; + return ALC_NO_ERROR; +} + + +ALCboolean CoreAudioCapture::start() +{ + OSStatus err{AudioOutputUnitStart(mAudioUnit)}; + if(err != noErr) + { + ERR("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + return ALC_TRUE; +} + +void CoreAudioCapture::stop() +{ + OSStatus err{AudioOutputUnitStop(mAudioUnit)}; + if(err != noErr) + ERR("AudioOutputUnitStop failed\n"); +} + +ALCenum CoreAudioCapture::captureSamples(void *buffer, ALCuint samples) +{ + if(!mConverter) + { + mRing->read(buffer, samples); + return ALC_NO_ERROR; + } + + auto rec_vec = mRing->getReadVector(); + const void *src0{rec_vec.first.buf}; + auto src0len = static_cast<ALsizei>(rec_vec.first.len); + auto got = static_cast<ALuint>(mConverter->convert(&src0, &src0len, buffer, samples)); + size_t total_read{rec_vec.first.len - src0len}; + if(got < samples && !src0len && rec_vec.second.len > 0) + { + const void *src1{rec_vec.second.buf}; + auto src1len = static_cast<ALsizei>(rec_vec.second.len); + got += static_cast<ALuint>(mConverter->convert(&src1, &src1len, + static_cast<char*>(buffer)+got, samples-got)); + total_read += rec_vec.second.len - src1len; + } + + mRing->readAdvance(total_read); + return ALC_NO_ERROR; +} + +ALCuint CoreAudioCapture::availableSamples() +{ + if(!mConverter) return mRing->readSpace(); + return mConverter->availableOut(mRing->readSpace()); +} + +} // namespace + +BackendFactory &CoreAudioBackendFactory::getFactory() +{ + static CoreAudioBackendFactory factory{}; + return factory; +} + +bool CoreAudioBackendFactory::init() { return true; } + +bool CoreAudioBackendFactory::querySupport(BackendType type) +{ return type == BackendType::Playback || type == BackendType::Capture; } + +void CoreAudioBackendFactory::probe(DevProbe type, std::string *outnames) +{ + switch(type) + { + case DevProbe::Playback: + case DevProbe::Capture: + /* Includes null char. */ + outnames->append(ca_device, sizeof(ca_device)); + break; + } +} + +BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type) +{ + if(type == BackendType::Playback) + return BackendPtr{new CoreAudioPlayback{device}}; + if(type == BackendType::Capture) + return BackendPtr{new CoreAudioCapture{device}}; + return nullptr; +} |