Запись аудио с аудиоустройством с файлами, сегментированными по X секунд.
Я был в этом в течение нескольких дней. Я не очень знаком со слоем Audio Unit фреймворка. Может ли кто-нибудь указать мне полный пример того, как я могу разрешить запись пользователя, а затем записать файл на лету с x номером интервала. Например, пользователь нажимает на запись, каждые 10 секунд, я хочу записать в файл, на 11-й секунде он будет записывать в следующий файл, а на 21-й это тоже самое. Поэтому, когда я записываю 25-секундное слово аудио, он будет производить 3 разных файла.
Я пробовал это с AVCapture, но он производит щелчки и хлопки в середине. Я прочитал об этом, это связано с миллисекундами между операциями чтения и записи. Я пробовал Audio Queue Services, но, зная приложение, над которым я работаю, мне понадобится полный контроль над звуковым слоем; поэтому я решил пойти с Audio Unit.
1 ответ
Я думаю, что становлюсь ближе... все еще довольно потерянный. В итоге я использовал The Amazing Audio Engine (TAAE). Я сейчас смотрю на AEAudioReceiver, мой код обратного вызова выглядит следующим образом. Я думаю, что логически правильно, но я не думаю, что это реализовано правильно.
Задача под рукой: запись ~5-секундных сегментов в формате AAC.
Попытка: используйте обратный вызов AEAudioReciever и сохраните AudioBufferList в кольцевом буфере. Отслеживать количество секунд аудио, полученных в классе рекордера; как только он пройдет отметку 5 секунд (это может быть немного больше, но не 6 секунд). Вызовите метод Obj-c для записи файла с помощью AEAudioFileWriter
Результат: не сработало, записи звучали очень медленно и много шума постоянно; Я слышу некоторые записи звука; поэтому я знаю, что некоторые данные есть, но, похоже, я теряю много данных. Я даже не уверен, как отладить это (я продолжу пробовать, но в данный момент довольно потерянный).
Еще один элемент - преобразование в AAC. Должен ли я сначала записать файл в формате PCM, а не преобразовать в AAC, или возможно ли преобразовать только аудиосегмент в AAC?
Спасибо за помощь!
----- круговой буфер инициализации -----
//trying to get 5 seconds audio, how do I know what the length is if I don't know the frame size yet? and is that even the right question to ask?
TPCircularBufferInit(&_buffer, 1024 * 256);
----- обратный вызов AEAudioReceiver ------
static void receiverCallback(__unsafe_unretained MyAudioRecorder *THIS,
__unsafe_unretained AEAudioController *audioController,
void *source,
const AudioTimeStamp *time,
UInt32 frames,
AudioBufferList *audio) {
//store the audio into the buffer
TPCircularBufferCopyAudioBufferList(&THIS->_buffer, audio, time, kTPCircularBufferCopyAll, NULL);
//increase the time interval to track by THIS
THIS.numberOfSecondInCurrentRecording += AEConvertFramesToSeconds(THIS.audioController, frames);
//if number of seconds passed an interval of 5 seconds, than write the last 5 seconds of the buffer to a file
if (THIS.numberOfSecondInCurrentRecording > 5 * THIS->_currentSegment + 1) {
NSLog(@"Segment %d is full, writing file", THIS->_currentSegment);
[THIS writeBufferToFile];
//segment tracking variables
THIS->_numberOfReceiverLoop = 0;
THIS.lastTimeStamp = nil;
THIS->_currentSegment += 1;
} else {
THIS->_numberOfReceiverLoop += 1;
}
// Do something with 'audio'
if (!THIS.lastTimeStamp) {
THIS.lastTimeStamp = (AudioTimeStamp *)time;
}
}
---- Запись в файл (метод внутри MyAudioRecorderClass) ----
- (void)writeBufferToFileHandler {
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
objectAtIndex:0];
NSString *filePath = [documentsFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"Segment_%d.aiff", _currentSegment]];
NSError *error = nil;
//setup audio writer, should the buffer be converted to aac first or save the file than convert; and how the heck do you do that?
AEAudioFileWriter *writeFile = [[AEAudioFileWriter alloc] initWithAudioDescription:_audioController.inputAudioDescription];
[writeFile beginWritingToFileAtPath:filePath fileType:kAudioFileAIFFType error:&error];
if (error) {
NSLog(@"Error in init. the file: %@", error);
return;
}
int i = 1;
//loop to write all the AudioBufferLists that is in the Circular Buffer; retrieve the ones based off of the _lastTimeStamp; but I had it in NULL too and worked the same way.
while (1) {
//NSLog(@"Processing buffer file list for segment [%d] and buffer index [%d]", _currentSegment, i);
i += 1;
// Discard any buffers with an incompatible format, in the event of a format change
AudioBufferList *nextBuffer = TPCircularBufferNextBufferList(&_buffer, _lastTimeStamp);
Float32 *frame = (Float32*) &nextBuffer->mBuffers[0].mData;
//if buffer runs out, than we are done writing it and exit loop to close the file
if ( !nextBuffer ) {
NSLog(@"Ran out of frames, there were [%d] AudioBufferList", i - 1);
break;
}
//Adding audio using AudioFileWriter, is the length correct?
OSStatus status = AEAudioFileWriterAddAudio(writeFile, nextBuffer, sizeof(nextBuffer->mBuffers[0].mDataByteSize));
if (status) {
NSLog(@"Writing Error? %d", status);
}
//consume/clear the buffer
TPCircularBufferConsumeNextBufferList(&_buffer);
}
//close the file and hope it worked
[writeFile finishWriting];
}
----- Аудио контроллер AudioStreamBasicDescription ------
//interleaved16BitStereoAudioDescription
AudioStreamBasicDescription audioDescription;
memset(&audioDescription, 0, sizeof(audioDescription));
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioDescription.mChannelsPerFrame = 2;
audioDescription.mBytesPerPacket = sizeof(SInt16)*audioDescription.mChannelsPerFrame;
audioDescription.mFramesPerPacket = 1;
audioDescription.mBytesPerFrame = sizeof(SInt16)*audioDescription.mChannelsPerFrame;
audioDescription.mBitsPerChannel = 8 * sizeof(SInt16);
audioDescription.mSampleRate = 44100.0;