Может ли кто-нибудь объяснить, как этот код преобразует громкость в децибелы с помощью ускорения Framework?

Я создаю приложение iOS с помощью EZAudio. Он делегирует обратно буфер float**, который содержит значения float, указывающие обнаруженный том. Этот делегат вызывается постоянно, и он работает с другим потоком.

То, что я пытаюсь сделать, это взять значение float из EZAudio и преобразовать его в децибелы.


EZAudioDelegate

Здесь мой упрощенный делегат EZAudio для получения данных микрофона:

- (void)microphone:(EZMicrophone *)microphone hasAudioReceived:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
    /*
     *  Returns a float array called buffer that contains the stereo signal data
     *  buffer[0] is the left audio channel
     *  buffer[1] is the right audio channel
     */

    // Using a separate audio thread to not block the main UI thread
    dispatch_async(dispatch_get_main_queue(), ^{

        float decibels = [self getDecibelsFromVolume:buffer withBufferSize:bufferSize];

        NSLog(@"Decibels: %f", decibels);

    });

}

Проблема

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


Код

Решение использует следующие методы из Accelerate Framework для преобразования тома в децибелы:

Ниже приведен метод getDecibelsFromVolume, который вызывается из делегата EZAudio. Он передается float** buffer и bufferSize из делегата.

- (float)getDecibelsFromVolume:(float**)buffer withBufferSize:(UInt32)bufferSize {

    // Decibel Calculation.

    float one = 1.0;
    float meanVal = 0.0;
    float tiny = 0.1;
    float lastdbValue = 0.0;

    vDSP_vsq(buffer[0], 1, buffer[0], 1, bufferSize);

    vDSP_meanv(buffer[0], 1, &meanVal, bufferSize);

    vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);


    // Exponential moving average to dB level to only get continous sounds.

    float currentdb = 1.0 - (fabs(meanVal) / 100);

    if (lastdbValue == INFINITY || lastdbValue == -INFINITY || isnan(lastdbValue)) {
        lastdbValue = 0.0;
    }

    float dbValue = ((1.0 - tiny) * lastdbValue) + tiny * currentdb;

    lastdbValue = dbValue;

    return dbValue;
}

Ответ 1

Я объясню, как можно вычислить значение дБ для сигнала с использованием кода, а затем показать, как это относится к примеру vDSP.

Сначала вычислите среднеквадратичную сумму части данных

double sumSquared = 0;
for (int i = 0 ; i < numSamples ; i++)
{
   sumSquared += samples[i]*samples[i];
}
double rms = sumSquared/numSamples;

Для получения дополнительной информации о RMS

Затем преобразуйте значение RMS в дБ

double dBvalue = 20*log10(rms);

Как это относится к примеру кода

vDSP_vsq(buffer[0], 1, buffer[0], 1, bufferSize);

Эта строка пересекает буфер и вычисляет квадраты всех элементов в буфере. Если буфер содержал значения [1,2,3,4] перед вызовом, то после вызова он содержал бы значения [1,4,9,16]

vDSP_meanv(buffer[0], 1, &meanVal, bufferSize);

Эта строка пересекает буфер, суммируя значения в буфере и затем возвращая сумму, деленную на количество элементов. Итак, для входного буфера [1,4,9,16] при вычислении суммы 30 делит на 4 и возвращает результат 7.5.

vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);

Эта строка преобразует meanVal в децибелы. Здесь нет смысла вызывать векторизованную функцию, поскольку она работает только с одним элементом. Однако, что он делает, это включение параметров в следующую формулу:

meanVal = n*log10(meanVal/one)

где n либо 10, либо 20 в зависимости от последнего параметра. В этом случае это 10. 10 используется для измерения мощности, а 20 используется для амплитуд. Я думаю, что 20 будет иметь больше смысла для вас.

Последняя маленькая часть кода выглядит как бы простое сглаживание результата, чтобы сделать счетчик немного менее упругим.