Гранулярный синтез в iOS 6 с использованием AudioFileServices

У меня есть вопрос относительно приложения для синтеза звука, над которым я работаю. Я пытаюсь прочитать аудиофайл, создать рандомизированные «зерна», используя методы гранулярного синтеза, поместите их в выходной буфер и затем сможете воспроизвести это для пользователя, используя OpenAL. В целях тестирования я просто записываю выходной буфер в файл, который затем могу прослушать.

Судя по моим результатам, я нахожусь на правильном пути, но у меня появляются некоторые проблемы с алиасами и звучанием, которые кажутся не совсем правильными. В середине выходного файла обычно довольно громкий звук, а уровни громкости иногда ОЧЕНЬ громкие.

Вот шаги, которые я предпринял, чтобы получить нужные мне результаты, но я немного запутался в паре вещей, а именно в форматах, которые я указываю для моей AudioStreamBasicDescription.

  1. Прочитайте аудиофайл из моего mainBundle, который является монофоническим файлом в формате .aiff:

    ExtAudioFileRef extAudioFile;
    CheckError(ExtAudioFileOpenURL(loopFileURL,
    &extAudioFile),
    "couldn't open extaudiofile for reading");
    memset(&player->dataFormat, 0, sizeof(player->dataFormat));
    
    player->dataFormat.mFormatID = kAudioFormatLinearPCM;
    player->dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    player->dataFormat.mSampleRate = S_RATE;
    player->dataFormat.mChannelsPerFrame = 1;
    player->dataFormat.mFramesPerPacket = 1;
    player->dataFormat.mBitsPerChannel = 16;
    player->dataFormat.mBytesPerFrame = 2;
    player->dataFormat.mBytesPerPacket = 2;
    
    // tell extaudiofile about our format
    CheckError(ExtAudioFileSetProperty(extAudioFile,
    kExtAudioFileProperty_ClientDataFormat,
    sizeof(AudioStreamBasicDescription),
    &player->dataFormat),
    "couldnt set client format on extaudiofile");
    
    SInt64 fileLengthFrames;
    UInt32 propSize = sizeof(fileLengthFrames);
    ExtAudioFileGetProperty(extAudioFile,
    kExtAudioFileProperty_FileLengthFrames,
    &propSize,
    &fileLengthFrames);
    
    player->bufferSizeBytes = fileLengthFrames * player->dataFormat.mBytesPerFrame;
    
  2. Затем я объявляю свой AudioBufferList и устанавливаю еще несколько свойств

    AudioBufferList *buffers;
    UInt32 ablSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
    buffers = (AudioBufferList *)malloc(ablSize);
    
    player->sampleBuffer = (SInt16 *)malloc(sizeof(SInt16) * player->bufferSizeBytes);
    
    buffers->mNumberBuffers = 1;
    buffers->mBuffers[0].mNumberChannels = 1;
    buffers->mBuffers[0].mDataByteSize = player->bufferSizeBytes;
    buffers->mBuffers[0].mData = player->sampleBuffer;
    
  3. Насколько я понимаю, .mData будет тем, что было указано в formatFlags (в данном случае, введите SInt16). Так как это типа *), Я хочу преобразовать это в плавающие данные, что очевидно для манипуляций со звуком. Прежде чем я установил цикл for, который просто перебирал буфер и приводил каждый образец к плавающей точке *. Это казалось ненужным, так что теперь я передаю свой буфер .mData функции, которую я создал, которая затем гранулирует звук:

        float *theOutBuffer = [self granularizeWithData:(float *)buffers->mBuffers[0].mData with:framesRead];
    
  4. В этой функции я динамически распределяю некоторые буферы, создаю зерна произвольного размера, помещаю их в свой выходной буфер после создания окон, используя окно Хемминга, и возвращаю этот буфер (который является данными с плавающей запятой). Все круто до этого момента.

  5. Далее я настроил все мои выходные файлы ASBD и такие:

    AudioStreamBasicDescription outputFileFormat;
    
    bzero(audioFormatPtr, sizeof(AudioStreamBasicDescription));
    
    outputFileFormat->mFormatID = kAudioFormatLinearPCM;
    outputFileFormat->mSampleRate = 44100.0;
    outputFileFormat->mChannelsPerFrame = numChannels;
    outputFileFormat->mBytesPerPacket = 2 * numChannels;
    outputFileFormat->mFramesPerPacket = 1;
    outputFileFormat->mBytesPerFrame = 2 * numChannels;
    outputFileFormat->mBitsPerChannel = 16;
    outputFileFormat->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
    
    UInt32 flags = kAudioFileFlags_EraseFile;
    ExtAudioFileRef outputAudioFileRef = NULL;
    NSString *tmpDir = NSTemporaryDirectory();
    NSString *outFilename = @"Decomp.caf";
    NSString *outPath = [tmpDir stringByAppendingPathComponent:outFilename];
    NSURL *outURL = [NSURL fileURLWithPath:outPath];AudioBufferList *outBuff;
    UInt32 abSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
    outBuff = (AudioBufferList *)malloc(abSize);
    
    outBuff->mNumberBuffers = 1;
    outBuff->mBuffers[0].mNumberChannels = 1;
    outBuff->mBuffers[0].mDataByteSize = abSize;
    outBuff->mBuffers[0].mData = theOutBuffer;
    
    CheckError(ExtAudioFileCreateWithURL((__bridge CFURLRef)outURL,
    kAudioFileCAFType,
    &outputFileFormat,
    NULL,
    flags,
    &outputAudioFileRef),
    "ErrorCreatingURL_For_EXTAUDIOFILE");
    
    CheckError(ExtAudioFileSetProperty(outputAudioFileRef,
    kExtAudioFileProperty_ClientDataFormat,
    sizeof(outputFileFormat),
    &outputFileFormat),
    "ErrorSettingProperty_For_EXTAUDIOFILE");
    
    CheckError(ExtAudioFileWrite(outputAudioFileRef,
    framesRead,
    outBuff),
    "ErrorWritingFile");
    

Файл написан правильно, в формате CAF. Мой вопрос заключается в следующем: правильно ли я работаю с буфером .mData, так как я приводю примеры к плавающим данным, манипулирую (гранулирую) окнами различных размеров и записываю их в файл с помощью ExtAudioFileWrite (в формате CAF)? Есть ли более элегантный способ сделать это, например, объявив мой ASBD formatFlag как kAudioFlagIsFloat? Мой выходной файл CAF содержит несколько щелчков, и когда я открываю его в логике, похоже, что существует много псевдонимов. Это имеет смысл, если я пытаюсь отправить данные с плавающей запятой, но происходит какое-то преобразование, о котором я не знаю.

Заранее спасибо за любые советы по этому вопросу! Я был заядлым читателем практически всех исходных материалов в Интернете, включая Core Audio Book, различные блоги, учебные пособия и т. Д. Конечная цель моего приложения — воспроизводить детализированный звук в реальном времени для пользователя с наушниками, чтобы запись в файл сейчас используется только для тестирования. Спасибо!

4

Решение

То, что вы говорите о шаге 3, подсказывает мне, что вы интерпретируете массив шорт как массив чисел с плавающей точкой? Если это так, мы нашли причину вашей проблемы. Можете ли вы присваивать короткие значения одно за другим в массиве с плавающей точкой? Это должно исправить это.

Это выглядит как mData это void * указывая на массив шорт. Приведение этого указателя к float * не меняет базовые данные в float но ваша функция обработки звука будет обрабатывать их так, как если бы они были. Тем не мение, float а также short значения хранятся совершенно по-разному, поэтому математика, которую вы выполняете в этой функции, будет работать с очень разными значениями, которые не имеют ничего общего с вашим истинным входным сигналом. Чтобы исследовать это экспериментально, попробуйте следующее:

short data[4] = {-27158, 16825, 23024, 15};
void *pData = data;

void Указатель не указывает, на какие данные он указывает, поэтому ошибочно можно предположить, что он указывает на float ценности. Обратите внимание, что short имеет ширину 2 байта, но float имеет ширину 4 байта. Это совпадение, что ваш код не потерпел крах при нарушении прав доступа. Интерпретируется как float приведенный выше массив достаточно длинен для двух значений. Давайте просто посмотрим на первое значение:

float *pfData = (float *)pData;
printf("%d == %f\n", data[0], pfData[0]);

Выход этого будет -27158 == 23.198200 иллюстрирующий, как вместо ожидаемого -27158.0f вы получаете примерно 23.2f, Произошли две проблемные вещи. Первый, sizeof(float) не является sizeof(short), Во-вторых, «единицы и нули» числа с плавающей запятой хранятся совсем не так, как целое число. Увидеть http://en.wikipedia.org/wiki/Single_precision_floating-point_format.

Как решить проблему? Есть как минимум два простых решения. Во-первых, вы можете преобразовать каждый элемент массива, прежде чем передать его в свой аудиопроцессор:

int k;
float *pfBuf = (float *)malloc(n_data * sizeof(float));
short *psiBuf = (short *)buffers->mBuffers[0].mData[k];
for (k = 0; k < n_data; k ++)
{
pfBuf[k] = psiBuf[k];
}
[self granularizeWithData:pfBuf with:framesRead];
for (k = 0; k < n_data; k ++)
{
psiBuf[k] = pfBuf[k];
}
free(pfBuf);

Вы видите, что, скорее всего, вам придется конвертировать все обратно в short после вашего звонка granularizeWithData: with:, Таким образом, второе решение было бы сделать всю обработку в short хотя из того, что ты пишешь, я полагаю, тебе не понравится этот последний подход.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]