2012-02-12 8 views
7

Me gustaría medir el volumen del sonido del entorno, no estoy seguro de si estoy haciendo lo correcto.Estoy haciendo lo correcto para convertir decibeles de -120 - 0 a 0 - 120

Me gustaría crear un medidor VU de un rango de 0 (silencioso) a 120 (muy ruidoso).

Obtuve la potencia máxima y media pero soy muy alto en un entorno silencioso normal. Dame un puntero.

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 


    //creating an audio CAF file in the temporary directory, this isn’t ideal but it’s the only way to get this class functioning (the temporary directory is erased once the app quits). Here we also specifying a sample rate of 44.1kHz (which is capable of representing 22 kHz of sound frequencies according to the Nyquist theorem), and 1 channel (we do not need stereo to measure noise). 

    NSDictionary* recorderSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
             [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey, 
             [NSNumber numberWithInt:44100],AVSampleRateKey, 
             [NSNumber numberWithInt:1],AVNumberOfChannelsKey, 
             [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, 
             nil]; 
    NSError* error; 

    NSURL *url = [NSURL fileURLWithPath:@"/dev/null"]; 
    recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recorderSettings error:&error]; 

    //enable measuring 
    //tell the recorder to start recording: 
    [recorder record]; 

    if (recorder) { 
     [recorder prepareToRecord]; 
     recorder.meteringEnabled = YES; 
     [recorder record]; 
     levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES]; 

    } else 
    { 
     NSLog(@"%@",[error description]); 
    }   
} 

- (void)levelTimerCallback:(NSTimer *)timer { 
    [recorder updateMeters]; 

    const double ALPHA = 0.05; 
    double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0])); 
    lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;  

    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults); 

    float tavgPow =[recorder averagePowerForChannel:0] + 120.0; 
    float tpPow = [recorder peakPowerForChannel:0] + 120.0; 

    float avgPow = tavgPow;//(float)abs([recorder averagePowerForChannel:0]); 
    float pPow = tpPow;//(float)abs([recorder peakPowerForChannel:0]); 

    NSString *tempAvg = [NSString stringWithFormat:@"%0.2f",avgPow]; 
     NSString *temppeak = [NSString stringWithFormat:@"%0.2f",pPow]; 
    [avg setText:tempAvg]; 
     [peak setText:temppeak]; 
    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", avgPow,pPow , lowPassResults); 
} 

Respuesta

8

La fórmula para convertir una amplitud lineal de decibelios cuando se quiere utilizar 1,0 como su referencia (por 0 dB), es

20 * log10(amp); 

así que no estoy seguro acerca de la intención de mirar su código, pero es probable que desee

float db = 20 * log10([recorder averagePowerForChannel:0]); 

Esto irá desde -infinity a una amplitud de cero, a 0 dB a una amplitud de 1. Si realmente se necesita para subir a entre 0 y 120 que puede agregar 120 y usar una función máxima en cero.

Así, después de que la línea anterior:

db += 120; 
db = db < 0 ? 0 : db; 

La fórmula está utilizando parece ser la fórmula para la conversión de DB a AMP, que creo que es lo contrario de lo que desea.

Editar: He vuelto a leer y parece que ya puede tener el valor de decibelios.

Si este es el caso, simplemente no convertir a la amplitud y añadir 120.

lo que el cambio

double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0])); 

a

double peakPowerForChannel = [recorder averagePowerForChannel:0]; 

y que debe estar bien para ir.

+0

Hola Michael, gracias por la respuesta. Creo que el averagePowerForChannel son el valor de decibelios en -x le gustaría convertirlo en 0 - 120 Valor – Desmond

+1

@Desmond: Ok, así que hacer el último paso le sugiero cambiar peakPowerForChannel utilizar directamente el valor de decibelios. Usted está agregando 120 más tarde. También necesitarás asegurarte de que no sea menor a cero usando un máximo (0, db) como lo hice con 'db = db <0? 0: db; ' –

+0

gracias Michael, sin embargo, los decibelios son muy altos en una habitación silenciosa ... descargo una aplicación de decibel10 para comprobar que los diferentes decibelios son enormes. La aplicación muestra aproximadamente 40db, la mía muestra 70db. mi objetivo principal aquí es comprobar si el usuario está haciendo ruido. si excede el umbral se disparará algo. – Desmond

1

En realidad, el rango de decibelios es de -160 a 0, pero puede ir a valores positivos (AVAudioRecorder Class Reference - averagePowerForChannel: método).

Entonces es mejor escribir db += 160; en lugar de db += 120;. Por supuesto, también puedes poner un desplazamiento para corregirlo.

22

Apple utiliza una tabla de búsqueda en su muestra SpeakHere que convierte de dB a un valor lineal que se muestra en un medidor de nivel. Esto es para ahorrar energía del dispositivo (supongo).

También lo necesitaba, pero no creía que un par de cálculos de flotación cada 1/10s (mi frecuencia de actualización) costara tanto poder de dispositivo. Así, en lugar de construir una mesa moldeé su código en:

float  level;    // The linear 0.0 .. 1.0 value we need. 
const float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room. 
float  decibels = [audioRecorder averagePowerForChannel:0]; 

if (decibels < minDecibels) 
{ 
    level = 0.0f; 
} 
else if (decibels >= 0.0f) 
{ 
    level = 1.0f; 
} 
else 
{ 
    float root   = 2.0f; 
    float minAmp   = powf(10.0f, 0.05f * minDecibels); 
    float inverseAmpRange = 1.0f/(1.0f - minAmp); 
    float amp    = powf(10.0f, 0.05f * decibels); 
    float adjAmp   = (amp - minAmp) * inverseAmpRange; 

    level = powf(adjAmp, 1.0f/root); 
} 

estoy usando un AVAudioRecorder, por lo tanto, se ve llegar la década de dB con averagePowerForChannel:, pero se puede llenar su propio valor en dB allí.

El ejemplo de Apple usó double cálculos, que no entiendo porque para la medición de audio float la precisión es más que suficiente, y cuesta menos energía del dispositivo.

No hace falta decir que ahora puede escalar este calculado level a su rango 0 .. 120 con un simple level * 120.0f.

El código anterior se puede acelerar cuando fijamos root en 2.0f, mediante la sustitución de powf(adjAmp, 1.0f/root) con sqrtf(adjAmp); pero eso es algo menor, y un muy buen compilador podría hacer esto por nosotros. Y estoy casi seguro de que inverseAmpRange se calculará una vez en tiempo de compilación.

-3

Simplemente configure su valor máximo y mínimo. Como si obtuvieras un rango de 0-120. Si quieres un rango de 0-60. Basta con dividir valor a un medio para obtener el rango medio y así sucesivamente ..

0

I crea un modelo de regresión para convertir la relación de correspondencia entre los datos generados a partir de wav NSRecorder y los datos de decibelios de NSRecorder.averagePowerForChannel

NSRecorder.averagePowerForChannel (dB) = -80 + 6 log2 (wav_RMS)

Donde wav_RMS es el valor cuadrático medio de los datos wav en un tiempo corto, es decir, 0,1 seg.

Cuestiones relacionadas