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

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

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


ЭЗАудиоделегат

Вот мой упрощенный делегат 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 Delegate. Он передается 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;
}

person Jamie    schedule 20.02.2015    source источник
comment
Вы смотрели в Accelerate/vDSP.h, что делают функции vDSP?   -  person Ian Ollmann    schedule 25.02.2015


Ответы (1)


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

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

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

Для получения дополнительной информации о 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] in вычисляет сумму 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 будет иметь больше смысла для вас.

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

person jaket    schedule 26.02.2015
comment
Большое спасибо @jacket! Не могли бы вы объяснить, что вы имеете в виду под: "There is really no point in calling a vectorized function here since it is only operating on a single element." Вы имеете в виду vDSP_vdbcon, когда говорите vectorized function? В чем будет заключаться изменение? - person Jamie; 26.02.2015
comment
Я имею в виду, что vDSP_vdbcon оптимизирован для преобразования массива значений в дБ, но ваш код вызывает его только с одним числом с плавающей запятой meanVal. Это не совсем бессмысленно, просто не будет никакого выигрыша в производительности от написания кода, подобного meanVal = 20*log10(meanVal); - person jaket; 27.02.2015
comment
О боже… Apple действительно любит все усложнять. Большое спасибо за объяснение. - person denisb411; 17.05.2017