МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus II для платы Марсоход

Апгрейд =Подмосковных вечеров=

`а как на этой штуке сыграть двузвучие или аккорд?...`
mcTopinambur

Пост опять про музыку, но на этот раз без экзотики, играть будем по нотам.

За основу взят проект "Подмосковные вечера", и для понимания данного, стоит сначала изучить базовый вариант. Цель работы - реализовать полифонию, а также сделать музыкальный инструмент поинтереснее чем "телефонный гудок" (меандр). Для этих целей во-первых нужно иметь возможность не просто давать попеременно 0 и 1 с частотой звука,а плавно менять уровень сигнала. Это уже умеет делать модуль "ЦАП" из моего прошлого поста, преобразующий 8-битный уровень сигнала в ШИМ:

ШИМ, Широтно Импульсная Модуляция
Как изменить частоту на один тон? Надо всё делать быстрее в 2^(1/12) раз. В проекте "Подмосковные вечера" водитель ритма посылает импульс один раз на полуцикл звуковой частоты, но нам для усложненного сэмплера нужно в 200 раз больше отсчетов, но если мы просто разделим константы отсюда:


...
// 8:  begin factor = 6378; note_leds = 8'b00010000; end    //G
// 9:  begin factor = 6017; note_leds = 8'b00110000; end    //G#
// 10: begin factor = 5682; note_leds = 8'b00100000; end    //A
// 11: begin factor = 5364; note_leds = 8'b01100000; end    //A#
...


на 200, то получим слишком маленькие числа, а значит высокую погрешность. Поэтому механизм водителя ритма немного изменен:


...
8:  begin factor = 2055; end    //G
// 9:  begin factor = 2177; end    //G#
10: begin factor = 2307; end    //A    440(Hz)*200(отсчетов на сэмпл)*2^15(cnt[])*2(нот)/5000000
// 11: begin factor = 2888; end    //A#    A* 2^(1/12)
...
cnt[n_queue] <= cnt[n_queue] + factor;
...
assign    sampler_clock[0] = cnt[0][15];
assign    sampler_clock[1] = cnt[1][15];
...


В результате реализовано два канала, в первом "инструментом" остался меандр, во втором стала синусоида.

Для экономии ресурсов используется разделение по времени, n_queue указывает, для какого из каналов производятся вычисления. В результате получилось добавить "вечерам" басовый инструмент, вот только динамик плохо справлялся, поэтому я повысил частоту на октаву.

Замечания:

  • В схеме один выход перенаправлен по сравнению с первоначальным проектом на другой пин (F1->F2).
  • Добавлен reset по включению питания (нужен для синуса).
  • Ресурсов не хватало, поэтому всё не очень нужное повыкидовано, на более ёмкой ПЛИС-е можно создать более интересные инструменты, сделать больше каналов и увеличить играемый фрагмент.

Взять весь проект можно здесь: icon Апгрейд проекта =Подмосковные вечера= (37.96 Кбайт)

И еще, по просьбе читателей, дополнение: про генератор синуса.
Берем единичный вектор на плоскости и начинаем равномерно вращать:


X(0)= 0
Y(0)= 1
X(t+1)= X(t) + alpha*Y(t)
Y(t+1)= Y(t) - alpha*X(t)


X и Y тут дают синус и косинус, вторые части выражений - приращения компонент за единицу времени и они зависят от угла, на который поворачиваем за шаг. Угол выбираем вида 2^(-С)  так, чтобы умножение на него превращалось в знаковый сдвиг вправо на С. Отсюда взялась константа 200: 2*Pi/200 ~= 1/32.

На верилоге это будет записано как-то так:


if ( reset ) begin
   y <= (1<<<Rm)-1; /* Rm-размер дробной части числа с фиксированной точкой */
   x <= 0;
end
else begin

   x <= ( x + (y>>>N) );
   y <= ( y - (x>>>N) );
end


Реальный код немного сложнее, из-за борьбы с накапливающейся погрешностью:


if ( reset ) begin
   y <= (1<<<Rm)-1;
   x <= 0;
end
else begin
  if
( y[R:Rm]== 2'b01 ) begin
     /*После каждого поворота переинициализируем, чтобы не накапливалась погрешность*/
     y <= (1<<<Rm)-1;
     x <= 0;
  end
  else begin

     x <= ( x + (y>>>N) - (x>>>(2*N+1)) );  /*Для большей точности, учитываем вторую производную*/
     y <= ( y - (x>>>N) - (y>>>(2*N+1)) );
   end
end


Обратите внимание, что этот простой способ годится только для получения последовательных значений синуса, для вычисления sin от произвольного х, придётся использовать алгоритм посложнее, например метод цифра за цифрой (CORDIC).

 

Комментарии  

+1 #5 lesha birukov 13.03.2012 11:00
Цитирую MAXvaLL:
Теперь все встало на свои места. Фактически, это упрощенная версия метода CORDIC, который подробно описан. Спасибо!

Ну, да, есть общие черты, хотя CORDIC вычисляет функцию с нуля и требует столько итераций, сколько нужно битов результата, а в данном случае мы отталкиваемся от предыдущего значения.
0 #4 MAXvaLL 13.03.2012 06:03
Теперь все встало на свои места. Фактически, это упрощенная версия метода CORDIC, который подробно описан. Спасибо!
0 #3 lesha birukov 10.03.2012 10:35
Не влезает тут, попрошу Николая добавить часть про синус в основную запись.
0 #2 lesha birukov 10.03.2012 10:32
Цитирую MAXvaLL:
Интересно. Но весьма скудно описано!
Особенно заинтересовала идея генерации синуса.
Поподробнее бы.


Увы, не всем дано умение писать популярно. Скажите, чего непонятно, я попробую раскрыть дополнительно.
Про генератор синуса.
Берем единичный вектор на плоскости и начинаем равномерно вращать:

X(0)= 0
Y(0)= 1

X(t+1)= X(t) + alpha*Y(t)
Y(t+1)= Y(t) - alpha*X(t)

X и Y тут дают синус и косинус, вторые части выражений - приращения компонент за единицу времени и они зависят от угла, на который поворачиваем за шаг. Угол выбираем вида 2^(-С) так, чтобы умножение на него превращалось в знаковый сдвиг вправо на С. Отсюда взялась константа 200: 2*Pi/200 ~= 1/32.

На верилоге это будет записано как-то так:

if ( reset ) begin
y
0 #1 MAXvaLL 07.03.2012 05:20
Интересно. Но весьма скудно описано!
Особенно заинтересовала идея генерации синуса.
Поподробнее бы.

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Проекты Проект Марсоход Апгрейд =Подмосковных вечеров=