【问题标题】:The math behind Apple's Speak here exampleApple 的 Speak here 示例背后的数学原理
【发布时间】:2012-12-06 00:02:05
【问题描述】:

我对 Apple 在 speak here example 中使用的数学有疑问。

一点背景知识:我知道 AVAudioRecorder 和 AVAudioPlayer 返回的平均功率和峰值功率以 dB 为单位。我也明白为什么 RMS 功率以 dB 为单位,并且需要使用 pow(10, (0.5 * avgPower)) 将其转换为放大器。

我的问题是:

Apple 使用这个公式来创建它的“Meter Table”

MeterTable::MeterTable(float inMinDecibels, size_t inTableSize, float inRoot)
    : mMinDecibels(inMinDecibels),
    mDecibelResolution(mMinDecibels / (inTableSize - 1)), 
    mScaleFactor(1. / mDecibelResolution)
{
    if (inMinDecibels >= 0.)
    {
        printf("MeterTable inMinDecibels must be negative");
        return;
    }

    mTable = (float*)malloc(inTableSize*sizeof(float));

    double minAmp = DbToAmp(inMinDecibels);
    double ampRange = 1. - minAmp;
    double invAmpRange = 1. / ampRange;

    double rroot = 1. / inRoot;
    for (size_t i = 0; i < inTableSize; ++i) {
        double decibels = i * mDecibelResolution;
        double amp = DbToAmp(decibels);
        double adjAmp = (amp - minAmp) * invAmpRange;
        mTable[i] = pow(adjAmp, rroot);
    }
}

所有的计算是什么 - 或者更确切地说,这些步骤中的每一个都做了什么?我认为 mDecibelResolutionmScaleFactor 用于绘制超过 400 个值的 80dB 范围(除非我弄错了)。但是,inRootampRangeinvAmpRangeadjAmp 的意义是什么?另外,为什么meter表中的第i个条目是“mTable[i] = pow(adjAmp, rroot);”?

非常感谢任何帮助! :)

在此先感谢您!

【问题讨论】:

    标签: objective-c ios audio core-audio avaudioplayer


    【解决方案1】:

    不是专家,但基于物理和数学:

    假设最大振幅为 1,最小值为 0.0001 [对应于 -80db,这是苹果示例中设置的最小 db 值:#define kMinDBvalue -80.0 in AQLevelMeter.h]

    minAmp 是本例的最小振幅 = 0.0001

    现在,所做的只是将分贝分辨率倍数的幅度与最小幅度进行调整:
    调整幅度 = (amp-minamp)/(1-minamp)
    这使得调整幅度的范围 = 0 到 1,而不是 0.0001 到 1(如果需要)。

    inRoot 在这里设置为 2。 rroot=1/2 - 1/2 的幂是平方根。来自苹果的文件:
    // inRoot - 这控制响应的曲率。 2.0 是平方根,3.0 是立方根。但 inRoot 不必是整数值,它可以是 1.8 或 2.5 等。
    本质上再次给您一个介于 0 和 1 之间的响应,其曲率会根据您为 inRoot 设置的值而变化。

    【讨论】:

    • 感谢您的回复!所以,如果我理解正确,adjAmp 本质上是在 0 到 1 的比例上绘制的 -80 到 0 的线性插值,确保我们只绘制 400 个值(在本例中)?然后 rroot 基本上使它成为“非线性”,但更多的是弯曲的响应?谢谢!
    • 将 -80db 的调整后的 amp 线性插值称为 0 是错误的。线性插值是对应于分贝值的幅度(最小值为 -80)。 db 到 amp 的转换不是线性的(它是指数的)。 amp 到调整后的 amp 转换是线性插值。
    • 你好。再次感谢您的回复。是的,我了解 log v/s 线性比例,以及为什么在线性上绘图很重要。我想确认的是“响应曲线”和调整,但似乎我非常关注“变量名称的含义”。 >_
    【解决方案2】:

    我已经有一个月没有提出这个问题了,感谢 Geebs 的回复! :)

    因此,这与我一直在从事的一个项目有关,并且基于此的功能是在提出该问题后大约 2 天实施的。显然,我在发布结束回复时有所懈怠(对此感到抱歉)。我也在 1 月 7 日发表了评论,但回过头来,似乎我对 var 名称感到困惑。 >_

    所以,这里是:

    //mDecibelResolution is the "weight" factor of each of the values in the meterTable.
    //Here, the table is of size 400, and we're looking at values 0 to 399.
    //Thus, the "weight" factor of each value is minValue / 399.
    
    
    MeterTable::MeterTable(float inMinDecibels, size_t inTableSize, float inRoot)
        : mMinDecibels(inMinDecibels),
        mDecibelResolution(mMinDecibels / (inTableSize - 1)), 
        mScaleFactor(1. / mDecibelResolution)
    {
        if (inMinDecibels >= 0.)
        {
            printf("MeterTable inMinDecibels must be negative");
            return;
        }
    
        //Allocate a table to store the 400 values
        mTable = (float*)malloc(inTableSize*sizeof(float));
    
        //Remember, "dB" is a logarithmic scale.
        //If we have a range of -160dB to 0dB, -80dB is NOT 50% power!!!
        //We need to convert it to a linear scale. Thus, we do pow(10, (0.05 * dbValue)), as stated in my question.
    
        double minAmp = DbToAmp(inMinDecibels);
    
        //For the next couple of steps, you need to know linear interpolation.
        //Again, remember that all calculations are on a LINEAR scale.
        //Attached is an image of the basic linear interpolation formula, and some simple equation solving.
    

        //As per the image, and the following line, (y1 - y0) is the ampRange - 
        //where y1 = maxAmp and y0 = minAmp.
        //In this case, maxAmp = 1amp, as our maxDB is 0dB - FYI: 0dB = 1amp.
        //Thus, ampRange = (maxAmp - minAmp) = 1. - minAmp
        double ampRange = 1. - minAmp;
    
        //As you can see, invAmpRange is the extreme right hand side fraction on our image's "Step 3"
        double invAmpRange = 1. / ampRange;
    
        //Now, if we were looking for different values of x0, x1, y0 or y1, simply substitute it in that equation and you're good to go. :)
        //The only reason we were able to get rid of x0 was because our minInterpolatedValue was 0.
    
        //I'll come to this later.
        double rroot = 1. / inRoot;
    
        for (size_t i = 0; i < inTableSize; ++i) {
            //Thus, for each entry in the table, multiply that entry with it's "weight" factor.
            double decibels = i * mDecibelResolution;
    
            //Convert the "weighted" value to amplitude using pow(10, (0.05 * decibelValue));
            double amp = DbToAmp(decibels);
    
            //This is linear interpolation - based on our image, this is the same as "Step 3" of the image.
            double adjAmp = (amp - minAmp) * invAmpRange;
    
            //This is where inRoot and rroot come into picture.
            //Linear interpolation gives you a "straight line" between 2 end-points.
           //rroot =  0.5
           //If I raise a variable, say myValue by 0.5, it is essentially taking the square root of myValue.
           //So, instead of getting a "straight line" response, by storing the square root of the value,
           //we get a curved response that is similar to the one drawn in the image (note: not to scale).
            mTable[i] = pow(adjAmp, rroot);
        }
    }
    

    响应曲线图片:如您所见,“线性曲线”并不完全是一条曲线。 >_

    希望这对社区有所帮助。 :)

    【讨论】:

      猜你喜欢
      • 2020-02-15
      • 2019-01-04
      • 1970-01-01
      • 2012-05-04
      • 2021-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-23
      相关资源
      最近更新 更多