Nagrywanie do AAC z RemoteIO: dane są zapisywane, ale nie można odtworzyć pliku
Próbowałem nagrywać z jednostki RemoteIO bezpośrednio do AAC w renderCallback w iOS 5 na iPadzie 2. Widziałem sprzeczne informacje mówiące, że nie jest to możliwe i że jest to możliwe (w komentarzachtutaj). Powodem, dla którego chcę to zrobić, jest to, że nagrywanie na PCM wymaga tak dużo miejsca na dysku, aby nagrać dowolną długość - nawet jeśli zostanie później przekonwertowane na AAC.
Jestem jednak gotów się poddać. Przechwyciłem przez Google, SO, książkę Core Audio i listę mailingową i fora Apple Core-Audio i osiągnąłem punkt, w którym nie dostaję żadnych błędów - i nagrywamcoś na dysk, ale wynikowy plik jest niemożliwy do odtworzenia. Dotyczy to zarówno symulatora, jak i urządzenia.
Więc ... jeśli ktoś ma z tym doświadczenie, byłbym wdzięczny za szturchnięcie we właściwym kierunku. Konfiguracja polega na tym, że RemoteIO odtwarza sygnał wyjściowy z AUSamplerów i działa prawidłowo.
Oto co robię w poniższym kodzie
OkreślićAudioStreamBasicDescription
formaty dla urządzenia remoteIO dokAudioFormatLinearPCM
Utwórz i określ format docelowy dlaExtAudioFileRef
Określ format klienta, pobierając go z jednostki RemoteIO
Określ renderCallback dla jednostki RemoteID
W renderCallback zapisz dane wkAudioUnitRenderAction_PostRender
faza
Jak już powiedziałem, nie otrzymuję żadnych błędów, a wynikowe rozmiary plików audio pokazują, że coś jest pisane, ale plik jest nie do odtworzenia. Być może mam zepsute formaty?
W każdym razie jest to moja wiadomość w butelce i / lub flaga „Be Here Dragons” dla każdego, kto walczy z ciemnymi wodami Core-Audio.
// Nieszczęśliwy komunikat, który otrzymuję podczas próby odtworzenia pliku:
// część konfiguracji remoteIO
<code> // Enable IO for recording UInt32 flag = 1; result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, // == 1 &flag, sizeof(flag)); if (noErr != result) {[self printErrorMessage: @"Enable IO for recording" withStatus: result]; return;} // Describe format - - - - - - - - - - size_t bytesPerSample = sizeof (AudioUnitSampleType); AudioStreamBasicDescription audioFormat; memset(&audioFormat, 0, sizeof(audioFormat)); audioFormat.mSampleRate = 44100.00; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; audioFormat.mFramesPerPacket = 1; audioFormat.mChannelsPerFrame = 1; audioFormat.mBitsPerChannel = 16; audioFormat.mBytesPerPacket = 2; audioFormat.mBytesPerFrame = 2; result = AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, // == 1 &audioFormat, sizeof(audioFormat)); result = AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, // == 0 &audioFormat, sizeof(audioFormat)); </code>
// Funkcja, która ustawia plik i renderowanie wywołania zwrotnego
<code> - (void)startRecordingAAC { OSStatus result; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"]; CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)recordFile, kCFURLPOSIXPathStyle, false); AudioStreamBasicDescription destinationFormat; memset(&destinationFormat, 0, sizeof(destinationFormat)); destinationFormat.mChannelsPerFrame = 2; destinationFormat.mFormatID = kAudioFormatMPEG4AAC; UInt32 size = sizeof(destinationFormat); result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat); if(result) printf("AudioFormatGetProperty %ld \n", result); result = ExtAudioFileCreateWithURL(destinationURL, kAudioFileM4AType, &destinationFormat, NULL, kAudioFileFlags_EraseFile, &extAudioFileRef); if(result) printf("ExtAudioFileCreateWithURL %ld \n", result); AudioStreamBasicDescription clientFormat; memset(&clientFormat, 0, sizeof(clientFormat)); result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size); if(result) printf("AudioUnitGetProperty %ld \n", result); result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat),&clientFormat); if(result) printf("ExtAudioFileSetProperty %ld \n", result); result = ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL); if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];} result = AudioUnitAddRenderNotify(ioUnit, renderCallback, (__bridge void*)self); if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];} } </code>
// I wreszcie renderowanie zwrotne
<code>static OSStatus renderCallback (void * inRefCon, AudioUnitRenderActionFlags * ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { OSStatus result; if (*ioActionFlags == kAudioUnitRenderAction_PostRender){ MusicPlayerController* THIS = (__bridge MusicPlayerController *)inRefCon; result = ExtAudioFileWriteAsync(THIS->extAudioFileRef, inNumberFrames, ioData); if(result) printf("ExtAudioFileWriteAsync %ld \n", result); } return noErr; } </code>