Обработка аудио: воспроизведение с уровнем громкости - PullRequest
8 голосов
/ 21 октября 2010

Я хочу прочитать звуковой файл из комплекта приложения, скопировать его, воспроизвести с максимальным уровнем громкости (значение усиления или пиковая мощность, я не уверен в его техническом названии), а затем записать его как другой файлв связку снова.

Я сделал часть копирования и записи.Результирующий файл идентичен входному файлу.Для этого я использую функции AudioFileReadBytes () и AudioFileWriteBytes () сервисов AudioFile в рамках AudioToolbox.

Итак, у меня есть байты входного файла, а также его формат аудиоданных (с помощью AudioFileGetProperty () с kAudioFilePropertyDataFormat), но я не могу найти в них переменную для воспроизведения с максимальным уровнем громкости исходного файла.

Чтобы прояснить мою цель, я пытаюсь создать другой звуковой файл, уровень громкости которого увеличен или уменьшен по сравнению с исходным, поэтому меня не волнует уровень громкости системы, который устанавливаетсяпользователь или iOS.

Возможно ли это сделать с фреймворком, о котором я говорил?Если нет, есть ли альтернативные предложения?

Спасибо


edit: Проходя через ответ Сэма относительно некоторых аудио основ, я решил расширить вопрос с другой альтернативой.

Можно ли использовать службы AudioQueue для записи существующего звукового файла (который входит в комплект) в другой файл и воспроизведения с уровнем громкости (с помощью фреймворка) на этапе записи?


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

AudioFileID inFileID;

CFURLRef inURL = [self inSoundURL];

AudioFileOpenURL(inURL, kAudioFileReadPermission, kAudioFileWAVEType, &inFileID)

UInt32 fileSize = [self audioFileSize:inFileID];
Float32 *inData = malloc(fileSize * sizeof(Float32)); //I used Float32 type with jv42's suggestion
AudioFileReadBytes(inFileID, false, 0, &fileSize, inData);

Float32 *outData = malloc(fileSize * sizeof(Float32));

//Art's suggestion, if I've correctly understood him

float ampScale = 0.5f; //this will reduce the 'volume' by -6db
for (int i = 0; i < fileSize; i++) {
    outData[i] = (Float32)(inData[i] * ampScale);
}

AudioStreamBasicDescription outDataFormat = {0};
[self audioDataFormat:inFileID];

AudioFileID outFileID;

CFURLRef outURL = [self outSoundURL];
AudioFileCreateWithURL(outURL, kAudioFileWAVEType, &outDataFormat, kAudioFileFlags_EraseFile, &outFileID)

AudioFileWriteBytes(outFileID, false, 0, &fileSize, outData);

AudioFileClose(outFileID);
AudioFileClose(inFileID);

Ответы [ 4 ]

14 голосов
/ 21 октября 2010

Вы не найдете операций масштабирования амплитуды в (Ext) AudioFile, потому что это самый простой DSP, который вы можете сделать.

Предположим, вы используете ExtAudioFile для преобразования всего, что вы прочитали, в 32-разрядные числа с плавающей запятой. Чтобы изменить амплитуду, вы просто умножаете:

float ampScale = 0.5f; //this will reduce the 'volume' by -6db
for (int ii=0; ii<numSamples; ++ii) {
    *sampOut = *sampIn * ampScale;
    sampOut++; sampIn++;
}

Чтобы увеличить усиление, вы просто используете шкалу> 1.f. Например, ampScale 2.f даст вам +6 дБ усиления.

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

Использование служб AudioQueue только для получения доступа к свойству тома - серьезное, серьезное излишество.

UPDATE:

В вашем обновленном коде вы умножаете каждый байт на 0,5 вместо каждого образца. Вот быстрое и грязное исправление для вашего кода, , но смотрите мои заметки ниже . Я бы не стал делать то, что ты делаешь.

...

// create short pointers to our byte data
int16_t *inDataShort = (int16_t *)inData;
int16_t *outDataShort = (int16_t *)inData;

int16_t ampScale = 2;
for (int i = 0; i < fileSize; i++) {
    outDataShort[i] = inDataShort[i] / ampScale;
}

...

Конечно, это не лучший способ сделать что-то: предполагается, что ваш файл представляет собой 16-разрядный линейный PCM со знаком с прямым порядком байтов. (Большинство файлов WAV, но не AIFF, m4a, mp3 и т. Д.) Я бы использовал API-интерфейс ExtAudioFile вместо API AudioFile, поскольку это преобразует любой формат, который вы читаете, в любой формат, который вы хотите использовать в коде. Обычно самое простое, что нужно сделать - это прочитать ваши примеры как 32-битные числа с плавающей запятой. Вот пример вашего кода, использующего ExtAudioAPI для обработки любого формата входного файла, включая стерео v. Mono

void ScaleAudioFileAmplitude(NSURL *theURL, float ampScale) {
    OSStatus err = noErr;

    ExtAudioFileRef audiofile;
    ExtAudioFileOpenURL((CFURLRef)theURL, &audiofile);
    assert(audiofile);

    // get some info about the file's format.
    AudioStreamBasicDescription fileFormat;
    UInt32 size = sizeof(fileFormat);
    err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileDataFormat, &size, &fileFormat);

    // we'll need to know what type of file it is later when we write 
    AudioFileID aFile;
    size = sizeof(aFile);
    err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_AudioFile, &size, &aFile);
    AudioFileTypeID fileType;
    size = sizeof(fileType);
    err = AudioFileGetProperty(aFile, kAudioFilePropertyFileFormat, &size, &fileType);


    // tell the ExtAudioFile API what format we want samples back in
    AudioStreamBasicDescription clientFormat;
    bzero(&clientFormat, sizeof(clientFormat));
    clientFormat.mChannelsPerFrame = fileFormat.mChannelsPerFrame;
    clientFormat.mBytesPerFrame = 4;
    clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame;
    clientFormat.mFramesPerPacket = 1;
    clientFormat.mBitsPerChannel = 32;
    clientFormat.mFormatID = kAudioFormatLinearPCM;
    clientFormat.mSampleRate = fileFormat.mSampleRate;
    clientFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved;
    err = ExtAudioFileSetProperty(audiofile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);

    // find out how many frames we need to read
    SInt64 numFrames = 0;
    size = sizeof(numFrames);
    err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileLengthFrames, &size, &numFrames);

    // create the buffers for reading in data
    AudioBufferList *bufferList = malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (clientFormat.mChannelsPerFrame - 1));
    bufferList->mNumberBuffers = clientFormat.mChannelsPerFrame;
    for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
        bufferList->mBuffers[ii].mDataByteSize = sizeof(float) * numFrames;
        bufferList->mBuffers[ii].mNumberChannels = 1;
        bufferList->mBuffers[ii].mData = malloc(bufferList->mBuffers[ii].mDataByteSize);
    }

    // read in the data
    UInt32 rFrames = (UInt32)numFrames;
    err = ExtAudioFileRead(audiofile, &rFrames, bufferList);

    // close the file
    err = ExtAudioFileDispose(audiofile);

    // process the audio
    for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
        float *fBuf = (float *)bufferList->mBuffers[ii].mData;
        for (int jj=0; jj < rFrames; ++jj) {
            *fBuf = *fBuf * ampScale;
            fBuf++;
        }
    }

    // open the file for writing
    err = ExtAudioFileCreateWithURL((CFURLRef)theURL, fileType, &fileFormat, NULL, kAudioFileFlags_EraseFile, &audiofile);

    // tell the ExtAudioFile API what format we'll be sending samples in
    err = ExtAudioFileSetProperty(audiofile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);

    // write the data
    err = ExtAudioFileWrite(audiofile, rFrames, bufferList);

    // close the file
    ExtAudioFileDispose(audiofile);

    // destroy the buffers
    for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
        free(bufferList->mBuffers[ii].mData);
    }
    free(bufferList);
    bufferList = NULL;

}
1 голос
/ 21 октября 2010

Я думаю, что вы должны избегать работы с 8-битными беззнаковыми символами для аудио, если можете.Попробуйте получить данные в 16 или 32 битах, чтобы избежать проблем с шумом / плохим качеством.

0 голосов
/ 21 октября 2010

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

http://en.wikipedia.org/wiki/Audio_bit_depth

Правильно сохраненные аудиофайлы будут иметь максимальную громкость на максимально допустимом уровне или около него.для битовой глубины файла.Если вы попытаетесь «уменьшить громкость» звукового файла, вы по сути просто ухудшите качество звука.

0 голосов
/ 21 октября 2010

Для большинства распространенных форматов аудиофайлов не существует единой переменной громкости. Вместо этого вам нужно будет взять (или преобразовать в) сэмплы звука PCM и выполнить хотя бы некоторую минимальную цифровую обработку сигнала (умножение, насыщение / ограничение / AGC, формирование шума квантования и т. Д.) Для каждого сэмпла.

...