скрин
Вид для печати
скрин
В данном случае работает потому, что 'случайно' параметры кратны друг другу.
при 50Гц 48000/50 = 960 (а это 480*2),
ну и при 100Гц 48000/100 = 480.
Т.е. все кратно дискретности буфера на Win7.
---------- Post added at 03:24 ---------- Previous post was at 02:25 ----------
Попробовал понять, все-таки ничего не понял)
Попробуй изобразить на си, если не сложно.
блин, ну я же уже и словами описывал... что именно там не понятно? функции (def) должны быть понятны.
# filter 1
fff.append(t2-t1)
fff=fff[-200:]
avg=float(sum(fff))/len(fff)
fff - массив из чисел. append добавляет в него новое число, fff=fff[-200:] оставляет последние 200 чисел, avg - сумма всех чисел делится на кол-во чисел. второй фильтр - то же самое.
в регуляторе чистая математика. где не понятно?
Набросал на си твою програму, как я ее понял:
На 882 выходит только через несколько сот итераций.Код:// PSB-Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "math.h"
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define DIMLEN 200
float QUANT = 882; // рассчетное число сэмплов в прерывание
float RES = 480; // разрешение указателей звукового буфера
float bufpos = 0; // позиция буфера записи
float playpos = 0; // позиция буфера чтения
float FFF[DIMLEN]; // фильтр 1
float FFF2[DIMLEN]; // фильтр 2
float delta = 0; // добавка к рассчетному числу сэмплов
float I = 0; // сост. интегратора
float avg = 0; // среднее зн. числа сэмплов
long len = 1; // начальная длина массива
void render_frame(float n) // put data to buffer
{
bufpos = bufpos + long(n + 0.5);
}
void play_frame(float n)
{
n = n - 5 + rand() * 10.0 / RAND_MAX; // add some noise to samples number
playpos = playpos + n;
if (bufpos < playpos)
printf("Underrun!\n");
}
float get_pos(void) // get rounded position
{
return((long)((long)playpos / RES) * RES);
}
float lim(float v, float l, float h) // limit value from l to h
{
return(min(h, max(l, v)));
}
int _tmain(int argc, _TCHAR* argv[])
{
long j, k;
float t1, t2, sum, err, P, smps;
printf("Test!\n");
render_frame(RES); // пребуфферинг
for (j=0; j<1050; j++) // заполняем" буфер, проигрыванием, вычисляем изменение указателя
{
t1 = get_pos(); //
render_frame(QUANT + delta); //
play_frame(QUANT); //
t2 = get_pos(); //
printf("avg = %f, b-p = %f, delta = %f, I = %f\n",
avg, bufpos-playpos, delta, I);
// filter 1
for (k=0; k<(DIMLEN-1); k++) //
FFF[k] = FFF[k+1]; //
FFF[k] = t2 - t1; //
sum = 0; //
for (k=DIMLEN-len; k<DIMLEN; k++) //
sum = sum + FFF[k]; //
avg = sum / len; //
// filter 2
for (k=0; k<(DIMLEN-1); k++) //
FFF2[k] = FFF2[k+1]; //
FFF2[k] = avg; //
sum = 0; //
for (k=DIMLEN-len; k<DIMLEN; k++) //
sum = sum + FFF2[k]; //
avg = sum / len; // вычисленное реальное число сэмплов за фрейм
// ПИ-регулятор
if (len == 200) // работает, если есть статистика по 200 фреймам
{
smps = long(QUANT + delta + 0.5); // кол-во положенных сэмплов (то, что ушло в render_frame() в этот раз)
err = lim(avg - smps, -5, 5); // разница между вычисленным средним и отправленным
I = I + err * 0.01; // ограниченный интегратор
I = lim(I, -5, 5); //
P = 0.5 * err; // проп. звено
delta = P + I; // сумма
}
else
len++;
}
return 0;
}
А колебания длины кадра даже на 1000-й итерации доходят до 0.1, что очень грубо.
Пока что не вникал в подробности, только перенес на Си.
да, 0.1 семпла.
В реальных условиях, если я не ошибаюсь, около 0.01. Причем никаких заметных на слух колебаний даже в самом начале регулировки. Таким образом сразу после запуска эмулятора слышится ровный, никуда не плавающий звук.
И еще, ты не знаешь о таком моменте, что нет никакой гарантии точного временнОго детектирования начала кадра. Т.е. в среднем, колебания может быть в несколько сканлайнов, но, особенно в зависимости от загрузки винды, скачки могут быть непредсказуемые и достаточно большие. У меня на это тоже идет корректировка, без чего не было бы такой точности.
+- даже несколько целых сэмплов за фрейм ты не услышишь.