Re: How to capture audio generated by a render callback, iOS
Re: How to capture audio generated by a render callback, iOS
- Subject: Re: How to capture audio generated by a render callback, iOS
- From: Tim Kemp <email@hidden>
- Date: Tue, 04 Dec 2012 11:07:47 -0500
I'm having a little trouble getting this working. I have an EXC_BAD_ACCESS in the call to ExtAudioFileWriteAsync. My Googling around hints that it might be down to a bad ASBD, but I'm not sure what a good one looks like. I want to write LPCM to a CAF. (It's just for internal use in the app and for eventual pasting to the pasteboard if this makes a difference.)
Here is how I set it up. Firstly, my output ASBD which works fine. This is used by the RemoteIO for actually producing sound from the speakers. Then _fileASBD is for the recorded file:
// Hardware output
memset(&_outputASBD, 0, sizeof(_outputASBD));
_outputASBD.mSampleRate = 44100.0;
_outputASBD.mFormatID = kAudioFormatLinearPCM;
_outputASBD.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
_outputASBD.mBytesPerPacket = 2 * sizeof(SInt16);
_outputASBD.mFramesPerPacket = 1;
_outputASBD.mBytesPerFrame = 2 * sizeof(SInt16);
_outputASBD.mChannelsPerFrame = 2;
_outputASBD.mBitsPerChannel = 16;
// Set recording format
memset(&_fileASBD, 0, sizeof(_fileASBD));
_fileASBD.mSampleRate = 44100.00;
_fileASBD.mFormatID = kAudioFormatLinearPCM;
_fileASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; // Have tried various combinations here, but I think I can put whatever I want in a CAF.
_fileASBD.mBytesPerPacket = 2 * sizeof(SInt16);
_fileASBD.mFramesPerPacket = 1;
_fileASBD.mBytesPerFrame = 2 * sizeof(SInt16);
_fileASBD.mChannelsPerFrame = 2;
_fileASBD.mBitsPerChannel = 16;
I know I could be a little cleaner and safer about creating these ASBDs. Then I create the file and 'prime' the recorder. (I have verified the file exists, so I won't show the URL creating boilerplate.)
checkError(ExtAudioFileCreateWithURL(urlRef,
kAudioFileCAFType,
&_fileASBD,
NULL,
kAudioFileFlags_EraseFile,
&_recFileRef),
"ExtAudioFileCreateWithURL failed: creating file");
checkError(ExtAudioFileWriteAsync(_recFileRef,
0,
NULL),
"ExtAudioFileWriteAsync failed: initializing write buffers");
(Chris Adamson will doubtless recognize checkError.)
Now, in my render callback I do this:
- (OSStatus) getGeneratedSamples:(AudioBufferList *) ioData frames:(int) numFrames
{
OSStatus result = [[self delegate] generateSamples:ioData frames:numFrames];
if (_isRecording) {
checkError(ExtAudioFileWriteAsync(_recFileRef,
numFrames,
ioData),
"ExtAudioFileWriteAsync failed");
}
return result;
}
That ObjC method is called directly by the render callback itself, which simply does:
TKCoreAudioBridge * output = (__bridge TKCoreAudioBridge *) inRefCon;
return [output getGeneratedSamples:ioData frames:inNumberFrames];
It blows up with a bad access on that call. There's no error reported. Backtrace below, which suggests to me that something is uninitialized (address=0x0)?
This by the way is on the simulator. I haven't tried it on the device yet, though I'd expect it to work in both places.
* thread #13: tid = 0x2f03, 0x028e2f77 libsystem_sim_c.dylib`memcpy$VARIANT$sse42 + 154, stop reason = EXC_BAD_ACCESS (code=2, address=0x0)
frame #0: 0x028e2f77 libsystem_sim_c.dylib`memcpy$VARIANT$sse42 + 154
frame #1: 0x002e2cad AudioToolbox`AudioRingBuffer::Store(AudioBufferList const*, unsigned long, long long) + 765
frame #2: 0x003ab516 AudioToolbox`ExtAudioFile::WriteFramesAsync(unsigned long, AudioBufferList const*) + 566
frame #3: 0x003ac5f4 AudioToolbox`ExtAudioFileWriteAsync + 116
frame #4: 0x0005527b Cellular`-[TKCoreAudioBridge getGeneratedSamples:frames:] + 187 at TKCoreAudioBridge.m:209
frame #5: 0x0005507a Cellular`auRenderCallback + 106 at TKCoreAudioBridge.m:227
frame #6: 0x003cd0e4 AudioToolbox`AUMultiChannelMixerInputElement::PullMixerInput(unsigned long, unsigned long&, AudioTimeStamp const&, unsigned long, AudioBufferList*) + 180
frame #7: 0x003cc18a AudioToolbox`AUMultiChannelMixer::RenderInput(unsigned long, unsigned long&, AudioTimeStamp const&, unsigned long) + 362
frame #8: 0x003ce93e AudioToolbox`AUMultiChannelMixer::Render(unsigned long&, AudioTimeStamp const&, unsigned long) + 1086
frame #9: 0x00381ddc AudioToolbox`AUBase::RenderBus(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long) + 140
frame #10: 0x00396a2a AudioToolbox`AUBase::DoRenderBus(unsigned long&, AudioTimeStamp const&, unsigned long, AUOutputElement*, unsigned long, AudioBufferList&) + 266
frame #11: 0x00394c52 AudioToolbox`AUBase::DoRender(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long, AudioBufferList&) + 530
frame #12: 0x003c1a35 AudioToolbox`AUMethodRender(void*, unsigned long*, AudioTimeStamp const*, unsigned long, unsigned long, AudioBufferList*) + 85
frame #13: 0x003c0329 AudioToolbox`AudioUnitRender + 105
frame #14: 0x0039916c AudioToolbox`AUInputElement::PullInput(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long) + 140
frame #15: 0x0038a433 AudioToolbox`AUInputFormatConverter2::InputProc(OpaqueAudioConverter*, unsigned long*, AudioBufferList*, AudioStreamPacketDescription**, void*) + 339
frame #16: 0x002d4b9b AudioToolbox`AudioConverterChain::CallInputProc(unsigned long) + 475
frame #17: 0x002d4822 AudioToolbox`AudioConverterChain::FillBufferFromInputProc(unsigned long*, CABufferList*) + 98
frame #18: 0x002b2440 AudioToolbox`BufferedAudioConverter::GetInputBytes(unsigned long, unsigned long&, CABufferList const*&) + 192
frame #19: 0x004894bb AudioToolbox`SRCWrapper::RenderOutput(CABufferList*, unsigned long, unsigned long&) + 347
frame #20: 0x002ac38c AudioToolbox`SampleRateConverter::RenderOutput(CABufferList*, unsigned long, unsigned long&, AudioStreamPacketDescription*) + 44
frame #21: 0x002b2219 AudioToolbox`BufferedAudioConverter::FillBuffer(unsigned long&, AudioBufferList&, AudioStreamPacketDescription*) + 249
frame #22: 0x002b241b AudioToolbox`BufferedAudioConverter::GetInputBytes(unsigned long, unsigned long&, CABufferList const*&) + 155
frame #23: 0x002a73df AudioToolbox`CBRConverter::RenderOutput(CABufferList*, unsigned long, unsigned long&, AudioStreamPacketDescription*) + 95
frame #24: 0x002b2219 AudioToolbox`BufferedAudioConverter::FillBuffer(unsigned long&, AudioBufferList&, AudioStreamPacketDescription*) + 249
frame #25: 0x002d4651 AudioToolbox`AudioConverterChain::RenderOutput(CABufferList*, unsigned long, unsigned long&, AudioStreamPacketDescription*) + 113
frame #26: 0x002b2219 AudioToolbox`BufferedAudioConverter::FillBuffer(unsigned long&, AudioBufferList&, AudioStreamPacketDescription*) + 249
frame #27: 0x002a6910 AudioToolbox`AudioConverterFillComplexBuffer + 336
frame #28: 0x0038a288 AudioToolbox`AUInputFormatConverter2::PullAndConvertInput(AudioTimeStamp const&, unsigned long&, AudioBufferList&, AudioStreamPacketDescription*, bool&) + 168
frame #29: 0x0038a11a AudioToolbox`AUConverterBase::RenderBus(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long) + 1162
frame #30: 0x00396a2a AudioToolbox`AUBase::DoRenderBus(unsigned long&, AudioTimeStamp const&, unsigned long, AUOutputElement*, unsigned long, AudioBufferList&) + 266
frame #31: 0x00394c52 AudioToolbox`AUBase::DoRender(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long, AudioBufferList&) + 530
frame #32: 0x0037700c AudioToolbox`AURemoteIO::PerformIO(unsigned int, unsigned int, AudioTimeStamp const&, AudioTimeStamp const&, AudioBufferList const*, AudioBufferList*, int&) + 604
frame #33: 0x003775d3 AudioToolbox`AURIOCallbackReceiver_PerformIO + 515
frame #34: 0x00369c59 AudioToolbox`_XPerformIO + 201
frame #35: 0x003347bd AudioToolbox`mshMIGPerform + 509
frame #36: 0x0033495b AudioToolbox`MSHMIGDispatchMessage + 59
frame #37: 0x00377300 AudioToolbox`AURemoteIO::IOThread::Run() + 96
frame #38: 0x0037a111 AudioToolbox`AURemoteIO::IOThread::Entry(void*) + 17
frame #39: 0x002e1bb3 AudioToolbox`CAPThread::Entry(CAPThread*) + 227
frame #40: 0x9b8f9ed9 libsystem_c.dylib`_pthread_start + 335
Any suggestions appreciated.
Thanks
Tim
On 3 Dec 2012, at 12:49, Gregory Wieber wrote:
> Perhaps this will help:
>
> https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html
>
> In particular, I'd check out ExtAudioFileWriteAsync.
>
>
> On Sun, Dec 2, 2012 at 12:51 PM, Tim Kemp <email@hidden> wrote:
> Hi,
>
> My iOS synth has an AUGraph like this:
>
> (Render callback on bus 0 input)->MultiChannelMixer->RemoteIO
>
> (There will be a file player unit also going in to the mixer.)
>
> The render callback gets samples in realtime from the synth engine as the user plays. I now want to record the generated sound to disk. What's the best way to hook this up? I am writing generated samples to a ring buffer already; I just need something to take them and put them in a file. Obviously this is a low priority process, so I'm not sure that I should be doing it within the render callback. (I also don't see anywhere to add a recording AU into the AUGraph; I am targeting iOS 5.0+ so I don't have a matrix mixer, which is what I originally thought of.) Should I add a render notify to the RemoteIO unit?
>
> Thanks,
>
> Tim
>
>
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Coreaudio-api mailing list (email@hidden)
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden