Резонансный алгоритм для обнаружения смол - PullRequest
0 голосов
/ 01 ноября 2010

Я искал разные способы определения высоты звука, исполняемого в микрофон.

Когда я хочу узнать, насколько близко он резонирует с определенным классом высоты звука, мне интересно, смогу ли я это сделать?какой-то физический резонансный алгоритм.

Если вы удерживаете нажатой, чтобы удерживать педаль на пианино, и поете в ней тон (и если вы достаточно близки к одному из существующих тонов), заметкасочувственно отзывайтесь.

Я бы хотел смоделировать это поведение.Но как бы я справился с задачей?Кто-нибудь может помочь мне продвинуть это вперед?

Ответы [ 6 ]

2 голосов
/ 17 ноября 2010

Одно интересное решение, которое я нашел, - просто ввод микрофонного входа в алгоритм Karplus Strong.

Таким образом, Karplus Strong симулирует оторванную строку:

  • создание кругового буфера (если мы производим выборку на частоте 44,1 кГц и мы хотим смоделировать среднюю точку A, то есть A4, которая составляет 440 Гц, тогда наш размер буфера будет ~ 101 элемент)
  • заполнение статического поля между -1 и 1
  • ходя по кругу, каждый раз устанавливая текущее значение на среднее значение двух предыдущих (и передавая текущее значение на динамик)
  • можно добавить константу демпфирования

Теперь, если мы добавим микрофонный поток в этот процесс, то:

x = ( ringBuf[prev] + ring theBuf[prev2] ) * 0.5 * 0.998;
micOut[moPtr++] = x;
ringBuf[curr] = x + micIn[miPtr++];

Это действительно точно имитирует пение на гитаре. если вы услышите свой тон, он действительно вопит.

Но есть серьезная проблема с этим подходом: рассмотрим шаг, генерируемый буфером из 100 элементов, и шаг, генерируемый буфером из 101 элемента. нет никакого способа генерировать любой шаг между этими двумя значениями. Мы ограничены дискретным рабочим набором смол. хотя это будет довольно точно для низких нот (A2 будет иметь длину буфера ~ 400), чем выше мы пойдем, тем больше будет ошибка: A7 будет иметь длину буфера ~ 12,5. Эта ошибка, вероятно, из-за полутона.

Я не вижу способа противостоять этой проблеме. Я думаю, что подход должен быть отброшен.

2 голосов
/ 01 ноября 2010

Взгляните на функцию автокорреляции .

1 голос
/ 17 ноября 2010

Один подход, который я нашел полезным, состоит в том, чтобы сгенерировать две опорные волны с разницей в 90 градусов (я называю их «синус» и «косинус») и взять точечное произведение входной формы волны с этими опорными волнами на некоторых довольно коротких(скажем, 1/60 секунды) растягивает ввод.Это даст вам несколько зашумленный индикатор того, сколько входной частоты у вас в фазе или против фазы относительно ваших опорных волн (квадратный корень из суммы квадратов значений, сгенерированных с использованием двух опорных волн, будетбыть амплитудой).При небольшом размере окна вы заметите, что выходные данные довольно шумные, но если вы фильтруете выходные данные с помощью чего-то вроде простого КИХ или БИХ-фильтра, вы, вероятно, должны получить что-то довольно разумное.

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

Оба измерения амплитуды будут испытывать одинаковую задержку, но первоебудет гораздо более избирательным, чем второй;таким образом, вы можете очень четко сказать, является ли частота «правильной» или немного выключенной.Используя этот подход, можно быстро обнаруживать тональные сигналы DTMF, отклоняя тоны, которые даже на несколько Гц отключены (тоны вне тона будут восприниматься на «свободном» детекторе гораздо сильнее, чем на жестком).

Пример кода:

double sine_phase,sine_freq;
void process_some_waves(double *input, int16 len, 
  double *sine_phase, double sine_freq, 
  double *sine_result, double *cosine_result)
{
  int i;
  double phase, sin_tot,cos_tot;
  phase = *sine_phase;
  sin_tot = cos_tot = 0;
  for (i=0; len > i; i++)
  {
    sin_tot += input[i] * sin(phase);
    cos_tot += input[i] * cos(phase);
    phase += sine_freq;
  }
  *sine_result = sin_tot;
  *cosine_result = cos_tot;
  *sine_phase = phase;
}

/* Takes first element in buffer and 'smears' it through buffer with simple Gaussian resp. */
void simple_fir_filter(double *buff, int buffsize)
{
  int i;

  for (i=buffsize-1; i>=2; i--)
    buff[i] = (buff[i-1] + buff[i-2])/2;
}

#define FILTER_SIZE1 10
#define FILTER_SIZE2 8
#define SECTION_LENGTH 128
#define FREQ whatever

double sine_buff1[FILTER_SIZE1], sine_buff2[FILTER_SIZE2];
double cos_buff1[FILTER_SIZE1], cos_buff2[FILTER_SIZE2];
double combined_buff[FILTER_SIZE2];
double tight_amplitude, loose_amplitude;
double ref_phase;

void handle_some_data(double *input)
{
  /* Put results in first element of filter buffers */
  process_some_waves(input, SECTION_LENGTH, &ref_phase, FREQ, sine_buff1, cos_buff1);

  /* Run first stage of filtering */

  simple_fir_filter(sine_buff1, FILTER_SIZE1); 
  simple_fir_filter(cosine_buff1, FILTER_SIZE1);

  /* Last element of each array will hold results of filtering. */
  /* Now do second stage */

  sine_buff2[0] = sine_buff1[FILTER_SIZE1-1];
  cosine_buff2[0] = cosine_buff1[FILTER_SIZE1-1];
  combined_buff[0] = sine_buff2[0]*sine_buff2[0] + cosine_buff2[0]*cosine_buff2[0];
  simple_fir_filter(sine_buff2, FILTER_SIZE2); 
  simple_fir_filter(cosine_buff2, FILTER_SIZE2); 
  simple_fir_filter(combined_buff, FILTER_SIZE2); 

  tight_amplitude = sine_buff2[FILTER_SIZE2-1]*sine_buff2[FILTER_SIZE2-1] + 
                    cosine_buff2[FILTER_SIZE2-1]*cosine_buff2[FILTER_SIZE2-1];
  loose_amplitude = combined_buff2[FILTER_SIZE2-1];
}

Код здесь использует 'double' для всей математики, кроме подписки на массив.На практике почти наверняка было бы быстрее заменить некоторые математические вычисления на целочисленные.На машинах с плавающей запятой, я ожидаю, что лучшим подходом было бы сохранить фазу в виде 32-разрядного целого числа и использовать таблицу с ~ 4096 «одиночными» значениями синуса (чем меньше размер таблицы в ОЗУ, тем лучше когерентность кэшаспектакль).Я использовал код, очень похожий на приведенный выше, для DSP с фиксированной (целочисленной) точкой с большим успехом;вычисления синуса и косинуса в process_some_waves выполнялись в отдельных «циклах», причем каждый «цикл» реализовывался как отдельная инструкция с префиксом «repeat».

1 голос
/ 03 ноября 2010

Вы можете использовать демпфированный гармонический генератор с входом в качестве движущей силы. Выберите параметры генератора так, чтобы его резонансная частота соответствовала требуемой частоте.

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

1 голос
/ 03 ноября 2010

Алгоритм, полностью основанный на дискретном преобразовании Фурье (ДПФ), имеет ряд недостатков.Одной из проблем является временное разрешение, поскольку DFT работает с выборками в пределах окна, вы не можете определить изменения высоты тона в этом окне.Другая проблема - дискретное логарифмическое разрешение по частоте DFT, которое может быть недостаточно хорошим для детектора основного тона.В конце концов, DFT находит только волны с целыми длинами волн размера окна.

Слегка продвинутый алгоритм может сделать что-то вроде этого:).

Полосовой сигнал для фильтрации частоты основного тона изоляции. Подсчет количества выборок между двумя пиками в отфильтрованных сигналах.

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

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

0 голосов
/ 03 ноября 2010

Я читал об анализе Фурье.

В основном, если вы хотите извлечь частоту f из сигнала, вы просто добавляете синусоидальную частоту f, умножаете ее на исходный сигнал и интегрируете

Если исходный сигнал не содержал ничего с частотой f, вы должны получить почти ноль.если это ДЕЙСТВИТЕЛЬНО, тогда вы получите меру того, сколько энергии в сигнале находится на этой частоте.

Хотя за этим стоит довольно сложная математика, она имеет смысл интуитивно: просто смотреть на нее, всев сигнале, который находится на частоте f, будет конструктивно вмешиваться в синусоидальную волну, оставляя остаток;все, что не находится на частоте f, по сути можно рассматривать как случайный шум (т.е. количество элементов выше нуля примерно такое же, как и ниже), не оказывающее чистого эффекта при умножении на нашу синусоидальную волну.все отменяется.

Это связано с тем, на что я ловил рыбу.Для завершения приведенной выше аналогии: чтобы проверить, какие ноты содержит пианино, вы просто удерживаете педаль и поете в нее нарастающим тоном, и всякий раз, когда возникает симпатический резонанс, вы можете заметить, что у пианино есть нота на этой частоте.

Конечно, это не без недостатков: если вы удерживаете C1 (на этот раз не педаль) и поете / играете C2, C1 будет резонировать с удвоенной основной частотой, создавая звук C2.

Аналогичноигра в G2 заставит его резонировать в три раза больше его основной частоты и т.д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...