Математика за примером Apple Speak здесь

У меня есть вопрос относительно математики, которую Apple использует в ней здесь, вот пример.

Небольшой фон: я знаю, что средняя мощность и максимальная мощность, возвращаемые AVAudioRecorder и AVAudioPlayer, находятся в дБ. Я также понимаю, почему мощность RMS находится в дБ и что ее необходимо преобразовать в усилитель с помощью pow(10, (0.5 * avgPower)).

Мой вопрос:

Apple использует эту формулу для ее создания "Таблица счетчиков"

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

Каковы все вычисления - точнее, что делают каждый из этих шагов? Я думаю, что mDecibelResolution и mScaleFactor используются для построения диапазона 80 дБ более 400 значений (если только я не ошибаюсь). Однако каково значение inRoot, ampRange, invAmpRange и adjAmp? Кроме того, почему i-я запись в таблице счетчиков "mTable[i] = pow(adjAmp, rroot);"?

Любая помощь очень ценится!:)

Спасибо заранее и приветствую!

Ответ 1

Прошел месяц с тех пор, как я задал этот вопрос, и спасибо, Geebs, за ваш ответ!:)

Итак, это связано с проектом, над которым я работал, и функция, основанная на этом, была реализована примерно через 2 дня после запроса этого вопроса. Ясно, что я отступил от публикации заключительного ответа (извините за это). Я также опубликовал комментарий 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.

Linear Interpolation Equation

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

Изображение кривой ответа: как вы можете видеть, "Линейная кривая" не является точно кривой. > _ & Л; Square root response image

Надеюсь, что это поможет сообществу в некотором роде.:)

Ответ 2

Нет экспертов, но на основе физики и математики:

Предположим, что максимальная амплитуда равна 1, а минимальная - 0,0001 [соответствует -80 дБ, то есть значение min db установлено в примере яблока: #define kMinDBvalue -80.0 в AQLevelMeter.h]

minAmp - минимальная амплитуда = 0,0001 для этого примера

Теперь все, что делается, это амплитуды, кратные разрешению децибела, скорректированы с минимальной амплитудой:
скорректированная амплитуда = (amp-minamp)/(1-minamp)
Это делает диапазон скорректированной амплитуды = от 0 до 1 вместо 0,0001 к 1 (если это было необходимо).

inRoot устанавливается здесь 2. rroot = 1/2 - повышение до степени 1/2 является квадратным корнем. из файла apple:
//inRoot - это контролирует кривизну ответа. 2.0 - квадратный корень, 3.0 - корень куба. Но inRoot не обязательно должен быть целочисленным, он может быть 1,8 или 2,5 и т.д.
По существу, вы снова получаете ответ от 0 до 1, и кривизна этого зависит от того, какое значение вы задали для inRoot.