Может ли кто-нибудь объяснить, как этот код преобразует громкость в децибелы с помощью ускорения 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
будет иметь больше смысла для вас.
Последняя маленькая часть кода выглядит как бы простое сглаживание результата, чтобы сделать счетчик немного менее упругим.