Simplify resampling with CoreAudio capture
The ringbuffer holds the samples from the device, and we use our own converter for resampling, calling it on demand with data from the ring buffer.
This commit is contained in:
parent
4dca2f2ee5
commit
323cf58f02
@ -29,6 +29,7 @@
|
|||||||
#include "alMain.h"
|
#include "alMain.h"
|
||||||
#include "alu.h"
|
#include "alu.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
#include "converter.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <AudioUnit/AudioUnit.h>
|
#include <AudioUnit/AudioUnit.h>
|
||||||
@ -39,10 +40,10 @@ static const ALCchar ca_device[] = "CoreAudio Default";
|
|||||||
|
|
||||||
|
|
||||||
struct ALCcoreAudioPlayback final : public ALCbackend {
|
struct ALCcoreAudioPlayback final : public ALCbackend {
|
||||||
AudioUnit AudioUnit;
|
AudioUnit mAudioUnit;
|
||||||
|
|
||||||
ALuint FrameSize;
|
ALuint mFrameSize{0u};
|
||||||
AudioStreamBasicDescription Format; // This is the OpenAL format as a CoreAudio ASBD
|
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
|
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
|
||||||
@ -66,15 +67,12 @@ static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice
|
|||||||
new (self) ALCcoreAudioPlayback{};
|
new (self) ALCcoreAudioPlayback{};
|
||||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||||
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
|
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
|
||||||
|
|
||||||
self->FrameSize = 0;
|
|
||||||
self->Format = AudioStreamBasicDescription{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
||||||
{
|
{
|
||||||
AudioUnitUninitialize(self->AudioUnit);
|
AudioUnitUninitialize(self->mAudioUnit);
|
||||||
AudioComponentInstanceDispose(self->AudioUnit);
|
AudioComponentInstanceDispose(self->mAudioUnit);
|
||||||
|
|
||||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||||
self->~ALCcoreAudioPlayback();
|
self->~ALCcoreAudioPlayback();
|
||||||
@ -90,7 +88,7 @@ static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
|
|||||||
|
|
||||||
ALCcoreAudioPlayback_lock(self);
|
ALCcoreAudioPlayback_lock(self);
|
||||||
aluMixData(device, ioData->mBuffers[0].mData,
|
aluMixData(device, ioData->mBuffers[0].mData,
|
||||||
ioData->mBuffers[0].mDataByteSize / self->FrameSize);
|
ioData->mBuffers[0].mDataByteSize / self->mFrameSize);
|
||||||
ALCcoreAudioPlayback_unlock(self);
|
ALCcoreAudioPlayback_unlock(self);
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
@ -127,7 +125,7 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
|
|||||||
return ALC_INVALID_VALUE;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = AudioComponentInstanceNew(comp, &self->AudioUnit);
|
err = AudioComponentInstanceNew(comp, &self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioComponentInstanceNew failed\n");
|
ERR("AudioComponentInstanceNew failed\n");
|
||||||
@ -135,11 +133,11 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* init and start the default audio unit... */
|
/* init and start the default audio unit... */
|
||||||
err = AudioUnitInitialize(self->AudioUnit);
|
err = AudioUnitInitialize(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitInitialize failed\n");
|
ERR("AudioUnitInitialize failed\n");
|
||||||
AudioComponentInstanceDispose(self->AudioUnit);
|
AudioComponentInstanceDispose(self->mAudioUnit);
|
||||||
return ALC_INVALID_VALUE;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,13 +153,14 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
OSStatus err;
|
OSStatus err;
|
||||||
UInt32 size;
|
UInt32 size;
|
||||||
|
|
||||||
err = AudioUnitUninitialize(self->AudioUnit);
|
err = AudioUnitUninitialize(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
ERR("-- AudioUnitUninitialize failed.\n");
|
ERR("-- AudioUnitUninitialize failed.\n");
|
||||||
|
|
||||||
/* retrieve default output unit's properties (output side) */
|
/* retrieve default output unit's properties (output side) */
|
||||||
size = sizeof(AudioStreamBasicDescription);
|
size = sizeof(AudioStreamBasicDescription);
|
||||||
err = AudioUnitGetProperty(self->AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
err = AudioUnitGetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||||
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
||||||
{
|
{
|
||||||
ERR("AudioUnitGetProperty failed\n");
|
ERR("AudioUnitGetProperty failed\n");
|
||||||
@ -179,7 +178,8 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* set default output unit's input side to match output side */
|
/* set default output unit's input side to match output side */
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
@ -263,7 +263,8 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
||||||
kLinearPCMFormatFlagIsPacked;
|
kLinearPCMFormatFlagIsPacked;
|
||||||
|
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
@ -271,11 +272,12 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* setup callback */
|
/* setup callback */
|
||||||
self->FrameSize = device->frameSizeFromFmt();
|
self->mFrameSize = device->frameSizeFromFmt();
|
||||||
input.inputProc = ALCcoreAudioPlayback_MixerProc;
|
input.inputProc = ALCcoreAudioPlayback_MixerProc;
|
||||||
input.inputProcRefCon = self;
|
input.inputProcRefCon = self;
|
||||||
|
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_SetRenderCallback,
|
||||||
|
kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
@ -283,7 +285,7 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* init the default audio unit... */
|
/* init the default audio unit... */
|
||||||
err = AudioUnitInitialize(self->AudioUnit);
|
err = AudioUnitInitialize(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitInitialize failed\n");
|
ERR("AudioUnitInitialize failed\n");
|
||||||
@ -295,7 +297,7 @@ static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
|||||||
|
|
||||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
|
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
|
||||||
{
|
{
|
||||||
OSStatus err = AudioOutputUnitStart(self->AudioUnit);
|
OSStatus err = AudioOutputUnitStart(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioOutputUnitStart failed\n");
|
ERR("AudioOutputUnitStart failed\n");
|
||||||
@ -307,24 +309,21 @@ static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
|
|||||||
|
|
||||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
|
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
|
||||||
{
|
{
|
||||||
OSStatus err = AudioOutputUnitStop(self->AudioUnit);
|
OSStatus err = AudioOutputUnitStop(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
ERR("AudioOutputUnitStop failed\n");
|
ERR("AudioOutputUnitStop failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ALCcoreAudioCapture final : public ALCbackend {
|
struct ALCcoreAudioCapture final : public ALCbackend {
|
||||||
AudioUnit AudioUnit;
|
AudioUnit mAudioUnit{0};
|
||||||
|
|
||||||
ALuint FrameSize;
|
ALuint mFrameSize{0u};
|
||||||
ALdouble SampleRateRatio; // Ratio of hardware sample rate / requested sample rate
|
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
||||||
AudioStreamBasicDescription Format; // This is the OpenAL format as a CoreAudio ASBD
|
|
||||||
|
|
||||||
AudioConverterRef AudioConverter; // Sample rate converter if needed
|
std::unique_ptr<SampleConverter> mConverter;
|
||||||
AudioBufferList *BufferList; // Buffer for data coming from the input device
|
|
||||||
ALCvoid *ResampleBuffer; // Buffer for returned RingBuffer data when resampling
|
|
||||||
|
|
||||||
RingBufferPtr Ring{nullptr};
|
RingBufferPtr mRing{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
|
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
|
||||||
@ -343,56 +342,18 @@ DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
|
|||||||
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
|
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
|
||||||
|
|
||||||
|
|
||||||
static AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
|
||||||
{
|
|
||||||
AudioBufferList *list;
|
|
||||||
|
|
||||||
list = static_cast<AudioBufferList*>(calloc(1,
|
|
||||||
FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize));
|
|
||||||
if(list)
|
|
||||||
{
|
|
||||||
list->mNumberBuffers = 1;
|
|
||||||
|
|
||||||
list->mBuffers[0].mNumberChannels = channelCount;
|
|
||||||
list->mBuffers[0].mDataByteSize = byteSize;
|
|
||||||
list->mBuffers[0].mData = &list->mBuffers[1];
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_buffer_list(AudioBufferList *list)
|
|
||||||
{
|
|
||||||
free(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
|
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
|
||||||
{
|
{
|
||||||
new (self) ALCcoreAudioCapture{};
|
new (self) ALCcoreAudioCapture{};
|
||||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||||
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
|
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
|
||||||
|
|
||||||
self->AudioUnit = 0;
|
|
||||||
self->AudioConverter = NULL;
|
|
||||||
self->BufferList = NULL;
|
|
||||||
self->ResampleBuffer = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
|
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
|
||||||
{
|
{
|
||||||
free(self->ResampleBuffer);
|
if(self->mAudioUnit)
|
||||||
self->ResampleBuffer = NULL;
|
AudioComponentInstanceDispose(self->mAudioUnit);
|
||||||
|
self->mAudioUnit = 0;
|
||||||
destroy_buffer_list(self->BufferList);
|
|
||||||
self->BufferList = NULL;
|
|
||||||
|
|
||||||
if(self->AudioConverter)
|
|
||||||
AudioConverterDispose(self->AudioConverter);
|
|
||||||
self->AudioConverter = NULL;
|
|
||||||
|
|
||||||
if(self->AudioUnit)
|
|
||||||
AudioComponentInstanceDispose(self->AudioUnit);
|
|
||||||
self->AudioUnit = 0;
|
|
||||||
|
|
||||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||||
self->~ALCcoreAudioCapture();
|
self->~ALCcoreAudioCapture();
|
||||||
@ -405,39 +366,47 @@ static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
|
|||||||
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
|
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
|
||||||
{
|
{
|
||||||
auto self = static_cast<ALCcoreAudioCapture*>(inRefCon);
|
auto self = static_cast<ALCcoreAudioCapture*>(inRefCon);
|
||||||
RingBuffer *ring{self->Ring.get()};
|
RingBuffer *ring{self->mRing.get()};
|
||||||
AudioUnitRenderActionFlags flags = 0;
|
AudioUnitRenderActionFlags flags = 0;
|
||||||
|
union {
|
||||||
|
ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
|
||||||
|
AudioBufferList list;
|
||||||
|
} audiobuf = { { 0 } };
|
||||||
OSStatus err;
|
OSStatus err;
|
||||||
|
|
||||||
// fill the BufferList with data from the input device
|
auto rec_vec = ring->getWriteVector();
|
||||||
err = AudioUnitRender(self->AudioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->BufferList);
|
|
||||||
|
// Fill the ringbuffer's first segment with data from the input device
|
||||||
|
size_t total_read{minz(rec_vec.first.len, inNumberFrames)};
|
||||||
|
audiobuf.list.mNumberBuffers = 1;
|
||||||
|
audiobuf.list.mBuffers[0].mNumberChannels = self->mFormat.mChannelsPerFrame;
|
||||||
|
audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
|
||||||
|
audiobuf.list.mBuffers[0].mDataByteSize = total_read * self->mFormat.mBytesPerFrame;
|
||||||
|
err = AudioUnitRender(self->mAudioUnit, &flags, inTimeStamp, 1, inNumberFrames,
|
||||||
|
&audiobuf.list);
|
||||||
|
if(err == noErr && inNumberFrames > rec_vec.first.len && rec_vec.second.len > 0)
|
||||||
|
{
|
||||||
|
/* If there's still more to get and there's space in the ringbuffer's
|
||||||
|
* second segment, fill that with data too.
|
||||||
|
*/
|
||||||
|
const size_t remlen{inNumberFrames - rec_vec.first.len};
|
||||||
|
const size_t toread{minz(rec_vec.second.len, remlen)};
|
||||||
|
total_read += toread;
|
||||||
|
|
||||||
|
audiobuf.list.mNumberBuffers = 1;
|
||||||
|
audiobuf.list.mBuffers[0].mNumberChannels = self->mFormat.mChannelsPerFrame;
|
||||||
|
audiobuf.list.mBuffers[0].mData = rec_vec.second.buf;
|
||||||
|
audiobuf.list.mBuffers[0].mDataByteSize = toread * self->mFormat.mBytesPerFrame;
|
||||||
|
err = AudioUnitRender(self->mAudioUnit, &flags, inTimeStamp, 1, inNumberFrames,
|
||||||
|
&audiobuf.list);
|
||||||
|
}
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitRender error: %d\n", err);
|
ERR("AudioUnitRender error: %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ring->write(self->BufferList->mBuffers[0].mData, inNumberFrames);
|
ring->writeAdvance(total_read);
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
|
|
||||||
UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
|
|
||||||
AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
|
|
||||||
void *inUserData)
|
|
||||||
{
|
|
||||||
auto self = reinterpret_cast<ALCcoreAudioCapture*>(inUserData);
|
|
||||||
RingBuffer *ring{self->Ring.get()};
|
|
||||||
|
|
||||||
// Read from the ring buffer and store temporarily in a large buffer
|
|
||||||
ring->read(self->ResampleBuffer, *ioNumberDataPackets);
|
|
||||||
|
|
||||||
// Set the input data
|
|
||||||
ioData->mNumberBuffers = 1;
|
|
||||||
ioData->mBuffers[0].mNumberChannels = self->Format.mChannelsPerFrame;
|
|
||||||
ioData->mBuffers[0].mData = self->ResampleBuffer;
|
|
||||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->Format.mBytesPerFrame;
|
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,29 +450,31 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open the component
|
// Open the component
|
||||||
err = AudioComponentInstanceNew(comp, &self->AudioUnit);
|
err = AudioComponentInstanceNew(comp, &self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioComponentInstanceNew failed\n");
|
ERR("AudioComponentInstanceNew failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn off AudioUnit output
|
// Turn off AudioUnit output
|
||||||
enableIO = 0;
|
enableIO = 0;
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_EnableIO,
|
||||||
|
kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn on AudioUnit input
|
// Turn on AudioUnit input
|
||||||
enableIO = 1;
|
enableIO = 1;
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_EnableIO,
|
||||||
|
kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !TARGET_OS_IOS
|
#if !TARGET_OS_IOS
|
||||||
@ -520,20 +491,21 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioObjectGetPropertyData failed\n");
|
ERR("AudioObjectGetPropertyData failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
if(inputDevice == kAudioDeviceUnknown)
|
if(inputDevice == kAudioDeviceUnknown)
|
||||||
{
|
{
|
||||||
ERR("No input device found\n");
|
ERR("No input device found\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track the input device
|
// Track the input device
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -542,28 +514,30 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||||
input.inputProcRefCon = self;
|
input.inputProcRefCon = self;
|
||||||
|
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
|
||||||
|
kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the device
|
// Initialize the device
|
||||||
err = AudioUnitInitialize(self->AudioUnit);
|
err = AudioUnitInitialize(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitInitialize failed\n");
|
ERR("AudioUnitInitialize failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the hardware format
|
// Get the hardware format
|
||||||
propertySize = sizeof(AudioStreamBasicDescription);
|
propertySize = sizeof(AudioStreamBasicDescription);
|
||||||
err = AudioUnitGetProperty(self->AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
err = AudioUnitGetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||||
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
||||||
{
|
{
|
||||||
ERR("AudioUnitGetProperty failed\n");
|
ERR("AudioUnitGetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the requested format description
|
// Set up the requested format description
|
||||||
@ -589,7 +563,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
case DevFmtUShort:
|
case DevFmtUShort:
|
||||||
case DevFmtUInt:
|
case DevFmtUInt:
|
||||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(device->FmtChans)
|
switch(device->FmtChans)
|
||||||
@ -608,7 +582,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
case DevFmtX71:
|
case DevFmtX71:
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
|
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
|
||||||
@ -619,80 +593,61 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||||||
requestedFormat.mFramesPerPacket = 1;
|
requestedFormat.mFramesPerPacket = 1;
|
||||||
|
|
||||||
// save requested format description for later use
|
// save requested format description for later use
|
||||||
self->Format = requestedFormat;
|
self->mFormat = requestedFormat;
|
||||||
self->FrameSize = device->frameSizeFromFmt();
|
self->mFrameSize = device->frameSizeFromFmt();
|
||||||
|
|
||||||
// Use intermediate format for sample rate conversion (outputFormat)
|
// Use intermediate format for sample rate conversion (outputFormat)
|
||||||
// Set sample rate to the same as hardware for resampling later
|
// Set sample rate to the same as hardware for resampling later
|
||||||
outputFormat = requestedFormat;
|
outputFormat = requestedFormat;
|
||||||
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
||||||
|
|
||||||
// Determine sample rate ratio for resampling
|
|
||||||
self->SampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
|
||||||
|
|
||||||
// The output format should be the requested format, but using the hardware sample rate
|
// 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
|
// This is because the AudioUnit will automatically scale other properties, except for sample rate
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
err = AudioUnitSetProperty(self->mAudioUnit, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed\n");
|
ERR("AudioUnitSetProperty failed\n");
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the AudioUnit output format frame count
|
// Set the AudioUnit output format frame count
|
||||||
outputFrameCount = device->UpdateSize * self->SampleRateRatio;
|
ALuint64 FrameCount64{device->UpdateSize};
|
||||||
err = AudioUnitSetProperty(self->AudioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
FrameCount64 = (FrameCount64*outputFormat.mSampleRate + device->Frequency-1) /
|
||||||
|
device->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(self->mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
|
||||||
|
kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioUnitSetProperty failed: %d\n", err);
|
ERR("AudioUnitSetProperty failed: %d\n", err);
|
||||||
goto error;
|
return ALC_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up sample converter
|
// Set up sample converter if needed
|
||||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &self->AudioConverter);
|
if(outputFormat.mSampleRate != device->Frequency)
|
||||||
if(err != noErr)
|
self->mConverter.reset(CreateSampleConverter(device->FmtType, device->FmtType,
|
||||||
{
|
self->mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, device->Frequency,
|
||||||
ERR("AudioConverterNew failed: %d\n", err);
|
BSinc24Resampler));
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a buffer for use in the resample callback
|
self->mRing.reset(ll_ringbuffer_create(outputFrameCount, self->mFrameSize, false));
|
||||||
self->ResampleBuffer = malloc(device->UpdateSize * self->FrameSize * self->SampleRateRatio);
|
if(!self->mRing) return ALC_INVALID_VALUE;
|
||||||
|
|
||||||
// Allocate buffer for the AudioUnit output
|
|
||||||
self->BufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->FrameSize * self->SampleRateRatio);
|
|
||||||
if(self->BufferList == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
self->Ring.reset(ll_ringbuffer_create(
|
|
||||||
(size_t)ceil(device->UpdateSize*self->SampleRateRatio*device->NumUpdates),
|
|
||||||
self->FrameSize, false));
|
|
||||||
if(!self->Ring) goto error;
|
|
||||||
|
|
||||||
device->DeviceName = name;
|
device->DeviceName = name;
|
||||||
return ALC_NO_ERROR;
|
return ALC_NO_ERROR;
|
||||||
|
|
||||||
error:
|
|
||||||
self->Ring = nullptr;
|
|
||||||
free(self->ResampleBuffer);
|
|
||||||
self->ResampleBuffer = NULL;
|
|
||||||
destroy_buffer_list(self->BufferList);
|
|
||||||
self->BufferList = NULL;
|
|
||||||
|
|
||||||
if(self->AudioConverter)
|
|
||||||
AudioConverterDispose(self->AudioConverter);
|
|
||||||
self->AudioConverter = NULL;
|
|
||||||
if(self->AudioUnit)
|
|
||||||
AudioComponentInstanceDispose(self->AudioUnit);
|
|
||||||
self->AudioUnit = 0;
|
|
||||||
|
|
||||||
return ALC_INVALID_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
||||||
{
|
{
|
||||||
OSStatus err = AudioOutputUnitStart(self->AudioUnit);
|
OSStatus err = AudioOutputUnitStart(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
{
|
{
|
||||||
ERR("AudioOutputUnitStart failed\n");
|
ERR("AudioOutputUnitStart failed\n");
|
||||||
@ -703,46 +658,46 @@ static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
|||||||
|
|
||||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
|
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
|
||||||
{
|
{
|
||||||
OSStatus err = AudioOutputUnitStop(self->AudioUnit);
|
OSStatus err = AudioOutputUnitStop(self->mAudioUnit);
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
ERR("AudioOutputUnitStop failed\n");
|
ERR("AudioOutputUnitStop failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
|
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||||
{
|
{
|
||||||
union {
|
RingBuffer *ring{self->mRing.get()};
|
||||||
ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
|
|
||||||
AudioBufferList list;
|
|
||||||
} audiobuf = { { 0 } };
|
|
||||||
UInt32 frameCount;
|
|
||||||
OSStatus err;
|
|
||||||
|
|
||||||
// If no samples are requested, just return
|
if(!self->mConverter)
|
||||||
if(samples == 0) return ALC_NO_ERROR;
|
|
||||||
|
|
||||||
// Point the resampling buffer to the capture buffer
|
|
||||||
audiobuf.list.mNumberBuffers = 1;
|
|
||||||
audiobuf.list.mBuffers[0].mNumberChannels = self->Format.mChannelsPerFrame;
|
|
||||||
audiobuf.list.mBuffers[0].mDataByteSize = samples * self->FrameSize;
|
|
||||||
audiobuf.list.mBuffers[0].mData = buffer;
|
|
||||||
|
|
||||||
// Resample into another AudioBufferList
|
|
||||||
frameCount = samples;
|
|
||||||
err = AudioConverterFillComplexBuffer(self->AudioConverter,
|
|
||||||
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL
|
|
||||||
);
|
|
||||||
if(err != noErr)
|
|
||||||
{
|
{
|
||||||
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
|
ring->read(buffer, samples);
|
||||||
return ALC_INVALID_VALUE;
|
return ALC_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto rec_vec = ring->getReadVector();
|
||||||
|
const void *src0{rec_vec.first.buf};
|
||||||
|
auto src0len = static_cast<ALsizei>(rec_vec.first.len);
|
||||||
|
auto got = static_cast<ALuint>(SampleConverterInput(self->mConverter.get(), &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>(SampleConverterInput(self->mConverter.get(), &src1, &src1len,
|
||||||
|
static_cast<char*>(buffer)+got, samples-got));
|
||||||
|
total_read += rec_vec.second.len - src1len;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring->readAdvance(total_read);
|
||||||
return ALC_NO_ERROR;
|
return ALC_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
|
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
|
||||||
{
|
{
|
||||||
RingBuffer *ring{self->Ring.get()};
|
RingBuffer *ring{self->mRing.get()};
|
||||||
return ring->readSpace() / self->SampleRateRatio;
|
|
||||||
|
if(!self->mConverter) return ring->readSpace();
|
||||||
|
return SampleConverterAvailableOut(self->mConverter.get(), ring->readSpace());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user