Я могу издавать монофонические звуки, переключая один контакт ( с разной скоростью ), подключенный к пьезоизлучателю.
Как я могу сгенерировать два смешанных аудиосигнала в программном обеспечении для создания полифонии?
Вот код, который я использую для воспроизведения простой мелодии.
#define F_CPU 8000000UL // 8MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
// number of timer0 overflows/sec
#define INT_PER_SEC 31250
// Frequencies (in Hz) of notes
#define F_FSH_4 370
#define F_A_4 440
#define F_B_4 494
#define F_E_4 330
#define F_CSH_5 554
#define F_D_5 587
#define F_FSH_5 740
#define F_CSH_4 277
#define F_GSH_4 415
// number of timer0 overflows for notes
#define REST -1 // special case
#define FSH_4 INT_PER_SEC/F_FSH_4
#define A_4 INT_PER_SEC/F_A_4
#define B_4 INT_PER_SEC/F_B_4
#define E_4 INT_PER_SEC/F_E_4
#define CSH_5 INT_PER_SEC/F_CSH_5
#define D_5 INT_PER_SEC/F_D_5
#define FSH_5 INT_PER_SEC/F_FSH_5
#define CSH_4 INT_PER_SEC/F_CSH_4
#define GSH_4 INT_PER_SEC/F_GSH_4
#define SEMIQUAVER_TIME 60 // ms
#define BREATH_TIME 20 // ms
volatile uint32_t intrs = 0;
volatile int32_t curNote = REST;
// TIMER0 overflow
ISR(TIMER0_OVF_vect)
{
if (curNote == REST)
intrs = 0;
else
{
intrs++;
if (intrs >= curNote)
{
PORTD ^= _BV(PD4);
intrs = 0;
}
}
}
void play(int32_t note, uint32_t len)
{
int i;
curNote = note;
for (i = 0; i< len; i++)
_delay_ms(SEMIQUAVER_TIME);
curNote = REST;
_delay_ms(BREATH_TIME);
}
int main(void)
{
/* setup clock divider. Timer0 overflows on counting to 256.
* 8Mhz / 1 (CS0=1) = 8000000 increments/sec. Overflows every 256, so 31250
* overflow interrupts/sec */
TCCR0B |= _BV(CS00);
// enable overflow interrupts
TIMSK0 |= _BV(TOIE0);
// PD4 as output
DDRD = _BV(PD4);
TCNT0 = 0;
intrs = 0;
curNote = REST;
// enable interrupts
sei();
while (1)
{
// Axel F
play(FSH_4, 2);
play(REST, 2);
play(A_4, 3);
play(FSH_4, 2);
play(FSH_4, 1);
play(B_4, 2);
play(FSH_4, 2);
play(E_4, 2);
play(FSH_4, 2);
play(REST, 2);
play(CSH_5, 3);
play(FSH_4, 2);
play(FSH_4, 1);
play(D_5, 2);
play(CSH_5, 2);
play(A_4, 2);
play(FSH_4, 2);
play(CSH_5, 2);
play(FSH_5, 2);
play(FSH_4, 1);
play(E_4, 2);
play(E_4, 1);
play(CSH_4, 2);
play(GSH_4, 2);
play(FSH_4, 6);
play(REST, 12);
}
}
Что ж, один простой трюк — использовать два контакта с ШИМ и привязать их к противоположным сторонам динамика. Затем модулируйте каждый контакт с разной скоростью, и вы сможете воспроизводить две ноты одновременно... по сути, динамик микширует их вместе для вас. Более двух заметок, и вам придется делать это в программном обеспечении, как уже упоминалось.
Стандартный способ получения полифонии — прерывание с некоторой фиксированной частотой прерывания (чаще всего 8000 Гц или 44100 Гц), получение «высокого» (+1) или «низкого» (-1) (или чего-то промежуточного) от каждого источника звука. , сложите все числа, чтобы получить общее количество, а затем отправьте это общее число из ЦАП.
Как уже говорили здесь, с небольшим умом высокоскоростной ШИМ может заменить ЦАП.
На странице "Полифония микроконтроллеров" приведены дополнительные сведения и советы.
Я думаю, что эта милая старая жемчужина компьютерной DOS-игры использовала настоящий полифонический звук через динамик ПК: Digger .
Я не знаю, как они это сделали, но вы можете скачать исходный код C с сайта.
Это может помочь -> простой PWM DAC
вы можете просто добавить две прямоугольные волны и использовать быстрый ШИМ для вывода «аналогового» сигнала на динамик.
вот еще один другой метод, если вам нравится звук игры, быстрый и грязный:
Если вы используете программное обеспечение для хронометража ваших выступлений, самый простой подход, вероятно, состоит в том, чтобы сгенерировать два независимых потока данных и чередовать их. Этот подход может работать довольно хорошо, независимо от того, управляется ли выход динамика с помощью контакта ввода-вывода или ЦАП. Например:
внутренний селектор; uint16_t фаза[8],частота[8];Вышеизложенный подход является основным подходом, который я использовал в музыкальной шкатулке на основе PIC в 1996 году (используя ассемблерный код, а не C). Обратите внимание, что частота прерывания должна быть в 8 раз больше эффективной частоты дискретизации, но каждое прерывание должно обрабатывать только один голос. Обратите внимание, что если выходная фильтрация хороша, этот подход даст на 3 бита больше эффективного разрешения ЦАП, чем численное добавление отсчетов и их последующий вывод, но он будет генерировать много шума на частоте дискретизации и кратной ей частоте. Таким образом, фильтрация даже более важна, чем в противном случае.недействительное прерывание (недействительное) { селектор++; селектор&=7; фаза[селектор] + частота[селектор]; DAC_OUT = синусоида [фаза [селектор] >> 8]; }
Раньше так делали на старых игровых системах и во времена " компьютерных колонок ", но я не знаю как.
Первое предположение: подумайте о волне, которую вы хотели бы создать в идеале, затем представьте, что вы искажаете ее в сильно обрезанную квадратную форму, а затем создаете эту квадратную форму, переключая выходной сигнал в соответствующие моменты времени. Однако интермодуляций будет много .
Вторая мысль: можно ли значительно увеличить частоту колебаний и выводить аналоговые сигналы в стиле ШИМ ?
Как уже упоминалось, вы можете сделать это так же, как это делалось с динамиком ПК (который поддерживает только включение / выключение, опционально подключенное к ШИМ-контроллеру). достаточно быстро, чтобы он никогда полностью не включался и не выключался (немного похоже на то, как работает импульсный источник питания). Это заставляет динамик постоянно переключаться между включением и выключением, генерируя аналоговый сигнал.
Единственная проблема заключается в том, что вам нужен настоящий динамик (я думаю, что пьезоэлектрический динамик движется так быстро, что он слишком быстро включается и выключается), и вы должны иметь возможность переключать бит достаточно быстро. Я провел несколько экспериментов и получил максимальную скорость около 5 МГц , которой должно хватить для звукового сигнала частотой 11 025 Гц (вероятно, лучшее качество, на которое вы могли рассчитывать).
Конечно, 11025 Гц при 8 битах — это 11 килобайт в секунду, что намного быстрее, чем скорость последовательного порта. Это позволило бы сохранить во флэш-памяти только одну или две секунды звука, поэтому вы в значительной степени ограничены воспроизведением звука, сгенерированного на лету, при условии, что у процессора остается достаточно свободного времени для вращения динамика!
Есть несколько других способов добиться этого, и похоже, что для Arduino уже есть реализация метода, описанного выше.
Вы можете просто добавить потоки, как описано здесь:
Воспроизведите звук A на мгновение, например, 50 мс, затем звук B и переключитесь назад и вперед. Идея состоит в том, чтобы переключаться быстрее, чем может сказать ухо, и это будет звучать так, как будто оба играют одновременно.
Я считаю, что для Arduino есть библиотека тонов, которая воспроизводит два тона. Вы должны иметь возможность адаптировать код к используемому чипу AVR. На arduino.cc также есть несколько отличных тем по генерации сигналов.
Если вы решите добавить ЦАП, у меня есть пример генератора с числовым управлением по адресу http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html . Четыре независимых выходных канала. Четырехканальный ЦАП и ссылка стоят всего около 2 долларов или около того.
Вот мой код для воспроизведения двух мелодий одновременно. Извините, чтобы получить доступ, вам нужно зарегистрироваться на AVR Freaks.
Рик_2047
Тоби Джеффи
Рик_2047
Тоби Джеффи
Рик_2047
Тоби Джеффи
Джофоркер