8

我想测量周围的音量,不太确定我是否做对了。

我想创建一个范围为 0(安静)到 120(非常嘈杂)的 VU 表。

我得到了峰值和平均功率,但在正常安静的环境中非常高。请给我一些指导。

- (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);
}   
4

5 回答 5

28

Apple 在他们的SpeakHere示例中使用了一个查找表,该表将 dB 转换为电平表上显示的线性值。这是为了节省设备电量(我猜)。

我也需要这个,但不认为每 1/10 秒(我的刷新率)进行几次浮点计算会花费如此多的设备功率。所以,我没有建立一个表格,而是将他们的代码塑造成:

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);
}

我使用的是AVAudioRecorder,因此您会看到使用 获取 dB averagePowerForChannel:,但您可以在那里填写自己的 dB 值。

Apple 的示例使用double了计算,我不明白,因为音频计量float精度绰绰有余,而且设备功耗更低。

不用说,您现在可以level使用简单的level * 120.0f.

root当我们固定在时2.0f,上面的代码可以通过替换powf(adjAmp, 1.0f / root)sqrtf(adjAmp);来加速。但这是一件小事,一个非常好的编译器可能能够为我们做到这一点。而且我几乎可以肯定inverseAmpRange会在编译时计算一次。

于 2013-04-24T12:49:55.717 回答
8

当您想使用 1.0 作为参考(对于 0db)时,将线性幅度转换为分贝的公式是

20 * log10(amp);

所以我不确定查看您的代码的意图,但您可能想要

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

这将从幅度为零的 -infinity 变为幅度为 1 的 0db。如果您真的需要它上升到 0 到 120 之间,您可以添加 120 并在零处使用 max 函数。

因此,在上述行之后:

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

您使用的公式似乎是将 DB 转换为 amp 的公式,我认为这与您想要的相反。

编辑:我重读了,看来您可能已经有了分贝值。

如果是这种情况,请不要转换为幅度并添加 120。

所以改变

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

double peakPowerForChannel = [recorder averagePowerForChannel:0];

你应该可以走了。

于 2012-02-12T07:57:48.420 回答
1

实际上,分贝的范围是从-160到0,但它可以转到正值。(AVAudioRecorder类参考-averagePowerForChannel:方法)

那么最好写db += 160;而不是db += 120;. 当然你也可以放一个偏移量来纠正它。

于 2013-03-20T12:45:44.813 回答
0

NSRecorder我制作了一个回归模型来转换生成的 wav 数据和来自的分贝数据之间的映射关系NSRecorder.averagePowerForChannel

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

wav_RMSwav数据在短时间内的均方根值在哪里,即0.1秒。

于 2017-08-09T02:32:45.463 回答
-3

只需设置您的最大值和最小值。就像你得到0-120的范围一样。如果你想要0-60的范围。只需将值除以一半即可获得一半范围,依此类推。

于 2015-04-18T12:52:12.200 回答