I developed an iOS low-latency audio app using remoteIO for 3 years now and
I thought I knew my way around AudioUnits. Mono recording works fine, but then I
tried for 2 days to get stereo recording working and I had no luck. I tested it
with an iPad in the Alesis IO Dock and with GuitarJack on an old iPod Touch.
GarageBand for iOS offers channel selection and records stereo just fine. All
I'm getting is the left channel.
Here's my question:
In a recording callback
static OSStatus
recordingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData)
{
...
AudioBufferList
*inputBufferList = calloc(1, offsetof(AudioBufferList, mBuffers) +
(sizeof(AudioBuffer)));
inputBufferList->mNumberBuffers = 1;
inputBufferList->mBuffers[0].mNumberChannels =
2;
inputBufferList->mBuffers[0].mDataByteSize = 2 *
sizeof(SInt16) * inNumberFrames;
inputBufferList->mBuffers[0].mData =
malloc(inputBufferList->mBuffers[0].mDataByteSize);
AudioUnitRender(audioUnit, ioActionFlags,
inTimeStamp, 1, inNumberFrames, inputBufferList); // 1 stands for the input bus
...
}
where during initialization, the format was set to
AudioStreamBasicDescription inputFormat = { 0
};
inputFormat.mSampleRate = 44100;
inputFormat.mFormatID = kAudioFormatLinearPCM;
inputFormat.mFormatFlags =
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
inputFormat.mFramesPerPacket = 1;
inputFormat.mChannelsPerFrame = 2; // of course I detected
mono/stereo before and set this to 1 if only mono is available
inputFormat.mBitsPerChannel = 16;
inputFormat.mBytesPerFrame =
inputFormat.mChannelsPerFrame * sizeof(SInt16);
inputFormat.mBytesPerPacket =
inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
UInt32
inputFormatSize = sizeof(inputFormat);
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &inputFormat,
inputFormatSize); //
1 stands for the input bus
How do I retrieve both channels from inputBufferList?
FAIL 1:
Since I didn't specify kAudioFormatFlagIsNonInterleaved, I expected to get
1 buffer with both channels interleaved. Is the size of the buffer (2 *
inNumberFrames * sizeof(SInt16)) or (inNumberFrames * sizeof(SInt16)) ?
De-interleaving the data doesn't work:
SInt16 *stereoBuffer =
malloc(inputBufferList->mBuffers[0].mDataByteSize);
SInt16 *stereoBufferPtr = stereoBuffer;
SInt16 *leftBufferPtr = inputBufferList->mBuffers[0].mData;
SInt16 *rightBufferPtr = inputBufferList->mBuffers[0].mData;
rightBufferPtr += inNumberFrames;
for(UInt32 i = 0; i < inNumberFrames; ++i)
{
*stereoBufferPtr++ = *leftBufferPtr++;
*stereoBufferPtr++ = *rightBufferPtr++;
}
// ... copying stereoBuffer into my output buffer ...
free(stereoBuffer);
FAIL 2:
I also tried setting kAudioFormatFlagIsNonInterleaved and therefore
allocating
inputBufferList->mNumberBuffers = 2;
for(UInt32 i = 0; i < inputBufferList->mNumberBuffers; ++i)
{
inputBufferList->mBuffers[0].mNumberChannels =
1;
inputBufferList->mBuffers[0].mDataByteSize =
sizeof(SInt16) * inNumberFrames;
inputBufferList->mBuffers[0].mData =
malloc(inputBufferList->mBuffers[0].mDataByteSize);
}
Is it right to assume that I first need to set the
kAudioUnitProperty_StreamFormat and then allocate an AudioBufferList, and that
both need to be compatible and that they define how AudioUnitRender stores the
data into my AudioBufferList?
What am I overlooking or doing wrong? Is there a good example or
documentation anywhere on the internet? The aurioTouch example didn't help
because it does input and output in the same callback. Thanks in advance for any
advice!