генерация тона iOS - PullRequest
3 голосов
/ 06 марта 2012

Я смотрю на этот пример генерации звука на iOS , потому что мне нужно сделать что-то похожее, но есть некоторые части, которые я не понимаю, и я надеялся, что кто-то может помочь мне с этим.

В этой части кода:

    double theta_increment = 2.0 * M_PI * viewController->frequency / viewController->sampleRate;
    // Generate the samples
    for (UInt32 frame = 0; frame < inNumberFrames; frame++) 
    {
        buffer[frame] = sin(theta) * amplitude;

        theta += theta_increment;
        if (theta > 2.0 * M_PI)
        {
            theta -= 2.0 * M_PI;
        }
    }

Я не очень понимаю, для чего нужна часть theta += theta_increment;. Для меня имеет больше смысла делать что-то подобное внутри цикла for:

buffer[frame] = sin(theta_increment * frame);

Есть идеи, почему это не сработает? Кроме того, я понятия не имею, для чего предназначена эта часть кода: if (theta > 2.0 * M_PI), поэтому любые объяснения по этому поводу также будут приветствоваться.

Ответы [ 2 ]

0 голосов
/ 06 марта 2012

Ваш подход может быть использован для создания одного и того же результата, выраженного по-разному. Однако theta += theta_increment; будет более простым выражением (для расчета), чем то, что вы предлагаете.

Домен фазы переносится в sin' s область логических параметров. Этот шаг действительно не нужен для коротких образцов. Из-за ограничений в хранении с плавающей запятой ваша частота может в конечном итоге изменяться и в конечном итоге никогда не будет увеличиваться, если значение не будет перенесено, в зависимости от того, сколько сэмплов вы генерируете и используете ли вы float или double. Подумайте об этом так: что произойдет, если у вас есть огромное положительное число с плавающей запятой (значение вашего фазового аккумулятора), и вы попытаетесь добавить к нему 0.000004? Ошибка с плавающей запятой будет округлять ее, чтобы она соответствовала числу с плавающей запятой или двойному, и ошибка приведет к фазовой нестабильности и, в конечном счете, к нестабильности основного тона. Для коротких выборок (например, некоторых циклов) в этом случае обертка не понадобится, но для многих многих циклов она служит для стабилизации основного тона и фазового аккумулятора с течением времени.

Наконец, тэта будет использоваться для хранения последнего значения линейного изменения фазы, чтобы возобновить генерацию с того места, где оно остановилось при следующем вызове рендеринга. Без этого выход перезапустится с 0 на границах вызова рендера, что приведет к очень неприятным шумам и неправильным частотам.

С учетом всех обстоятельств: скорее всего, это была простая демонстрация и быстрый способ создания синуса в этом контексте. Ваш подход имеет несколько «дорогостоящих» преобразований, но он не требует разветвлений - он может быть быстрее, чем оригинал, особенно для более высоких частот.

0 голосов
/ 06 марта 2012

Я полагаю, что это будет работать нормально, но не забудьте умножить на амплитуду:

buffer[frame] = sin(theta_increment * frame) * amplitude;

Просто два разных способа выражения одной и той же математики.Похоже, что человек, который написал оригинальный код, хотел сохранить 0 <= theta <2pi, но это, вероятно, не обязательно (если только вызов sin () не является странным, о чем я не знаю).Кроме того, они, возможно, хотели сохранить «математическую» часть независимой от переменной кадра в случае, если тот же фрагмент обнаруживается в других циклах где-то еще, но это всего лишь предположение. </p>

...