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 14:48:19 -0500
As a follow-up, if I replace the calls to ExtAudioFileWriteAsync with the synchronous version then I get lots of -50 errors on every write. I believe that's a parameter error, so something isn't right in what I'm passing in.
I see the -50 on both the 'prime' call right after I create the file, then also every time the callback runs when I actually try to write the data.
Weirdly, by buffer size seems to vary around 470. I was expecting 512 length buffers. Could this be related?
Thanks
On 4 Dec 2012, at 11:07, Tim Kemp wrote:
> 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
_______________________________________________
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