Recording with remote I/O, AudioUnitRender -50 return code
audiounit, core-audio, iphone
Solution
If you have 2 channels per frame, you cannot have `bytesPerSample` as the size of the frame. Since the terminology is a bit confusing:
- A sample is a single value at a given position in a waveform
- A channel refers to data associated with a particular audio stream, ie, left/right channel for stereo, a single channel for mono, etc.
- A frame contains the samples for all channels for a given position in a waveform
- A packet contains one or more frames
So basically, you need to use `bytesPerSample * mChannelsPerFrame` for `mBytesPerFrame`, and use `mBytesPerFrame * mFramesPerPacket` for `mBytesPerPacket`.
Also I noticed that you are using 32-bits for your sample size. I'm not sure if you really want to do this -- usually, you want to record audio using 16-bit samples. The sound difference between 16 and 32 bit audio is almost impossible for most listeners to hear (the average CD is mastered at 44.1kHz, 16-bit PCM), and it will spare you 50% of the I/O and storage costs.
Problem
I've been working on a frequency detection application for iOS and I'm having an issue filling a user-defined AudioBufferList with audio samples from the microphone. I'm getting a return code of -50 when I call AudioUnitRender in my InputCallback method. I believe this means one of my parameters is invalid. I'm guessing it's the AudioBufferList, but I haven't been able to figure out what is wrong with it. I think I've set it up so it matches the data format I've specified in my ASBD. Below is the remote I/O setup and function calls that I believe could be incorrect: ASBD: ``` size_t bytesPerSample = sizeof(AudioUnitSampleType); AudioStreamBasicDescription localStreamFormat = {0}; localStreamFormat.mFormatID = kAudioFormatLinearPCM; localStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical; localStreamFormat.mBytesPerPacket = bytesPerSample; localStreamFormat.mBytesPerFrame = bytesPerSample; localStreamFormat.mFramesPerPacket = 1; localStreamFormat.mBitsPerChannel = 8 * bytesPerSample; localStreamFormat.mChannelsPerFrame = 2; localStreamFormat.mSampleRate = sampleRate; ``` InputCallback Declaration: ``` err = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct)); ``` AudioBufferList Declaration: ``` // Allocate AudioBuffers bufferList = (AudioBufferList *)malloc(sizeof(AudioBuffer)); bufferList->mNumberBuffers = 1; bufferList->mBuffers[0].mNumberChannels = 2; bufferList->mBuffers[0].mDataByteSize = 1024; bufferList->mBuffers[0].mData = calloc(256, sizeof(uint32_t)); ``` InputCallback Function: ``` AudioUnit rioUnit = THIS->ioUnit; OSStatus renderErr; UInt32 bus1 = 1; renderErr = AudioUnitRender(rioUnit, ioActionFlags, inTimeStamp, bus1, inNumberFrames, THIS->bufferList); ``` A few things to note: - Sample Rate = 22050 Hz - Since the canonical format of remote I/O data is 8.24-bit fixed point, I'm assuming the samples are 32 bits each (or 4 bytes). Since an unsigned int is 4 bytes, I'm using that to allocate my audio buffer. - I can get the same code to render audio correctly if I implement the audio data flow as PassThru rather than input only. - I've already looked at Michael Tyson's blog post on Remote I/O. Didn't see anything there different from what I'm doing. Thanks again, you all are awesome! Demetri