Полифонические звуки от микроконтроллера?

Я могу издавать монофонические звуки, переключая один контакт ( с разной скоростью ), подключенный к пьезоизлучателю.

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

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

#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);
    }
}
Эй, эта штука может издавать человеческий язык? Я имею в виду, как слова?
Взгляните на Кантарино - code.google.com/p/tinkerit/wiki/Cantarino
@ Джоби, ресурс, который вы дали, был отличным, но я видел демоверсию, на самом деле она ничего не говорит внятно. Что-нибудь еще, о чем вы знаете?
Не без ЦАП, нет.
@Джоби, что у тебя с ЦАП?
Микширование аудиосэмплов на GBA/ARM7 — hackpadsoftware.com/projects/gbaboal.html
Посмотрите на чипофон. Исходного кода нет, но он устроен аналогично синтезатору в NES. Нет этой дрянной двухголосной фигни. 38 кГц 12-битная 8-канальная полифония от AVR. linusakesson.net/chipophone/index.php

Ответы (12)

Что ж, один простой трюк — использовать два контакта с ШИМ и привязать их к противоположным сторонам динамика. Затем модулируйте каждый контакт с разной скоростью, и вы сможете воспроизводить две ноты одновременно... по сути, динамик микширует их вместе для вас. Более двух заметок, и вам придется делать это в программном обеспечении, как уже упоминалось.

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

Стандартный способ получения полифонии — прерывание с некоторой фиксированной частотой прерывания (чаще всего 8000 Гц или 44100 Гц), получение «высокого» (+1) или «низкого» (-1) (или чего-то промежуточного) от каждого источника звука. , сложите все числа, чтобы получить общее количество, а затем отправьте это общее число из ЦАП.

Как уже говорили здесь, с небольшим умом высокоскоростной ШИМ может заменить ЦАП.

На странице "Полифония микроконтроллеров" приведены дополнительные сведения и советы.

Я думаю, что эта милая старая жемчужина компьютерной DOS-игры использовала настоящий полифонический звук через динамик ПК: Digger .

Я не знаю, как они это сделали, но вы можете скачать исходный код C с сайта.

Я все еще слышу мелодию в своей голове

Это может помочь -> простой PWM DAC

Чтобы заставить Arduino воспроизводить ноты асинхронно, вам нужно будет использовать систему, подобную MIDI — с отдельными командами для включения и выключения нот и т. Д., Встроенная библиотека тонов делает это, но не делает полифонию — но последняя версия выглядит как это так - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation

вы можете просто добавить две прямоугольные волны и использовать быстрый ШИМ для вывода «аналогового» сигнала на динамик.

вот еще один другой метод, если вам нравится звук игры, быстрый и грязный:

https://gitweb.bl0rg.net/cgi-bin/gitweb.cgi?p=arduinisten.git;a=blob;f=projekte/soundz/arpeggio/arpeggio.pde;h=6ceb64a57916c094e87e5983c07b5dd1b4623083;hb=HEAD

Если вы используете программное обеспечение для хронометража ваших выступлений, самый простой подход, вероятно, состоит в том, чтобы сгенерировать два независимых потока данных и чередовать их. Этот подход может работать довольно хорошо, независимо от того, управляется ли выход динамика с помощью контакта ввода-вывода или ЦАП. Например:

внутренний селектор;
uint16_t фаза[8],частота[8];

недействительное прерывание (недействительное) { селектор++; селектор&=7; фаза[селектор] + частота[селектор]; DAC_OUT = синусоида [фаза [селектор] >> 8]; }

Вышеизложенный подход является основным подходом, который я использовал в музыкальной шкатулке на основе PIC в 1996 году (используя ассемблерный код, а не C). Обратите внимание, что частота прерывания должна быть в 8 раз больше эффективной частоты дискретизации, но каждое прерывание должно обрабатывать только один голос. Обратите внимание, что если выходная фильтрация хороша, этот подход даст на 3 бита больше эффективного разрешения ЦАП, чем численное добавление отсчетов и их последующий вывод, но он будет генерировать много шума на частоте дискретизации и кратной ей частоте. Таким образом, фильтрация даже более важна, чем в противном случае.

Раньше так делали на старых игровых системах и во времена " компьютерных колонок ", но я не знаю как.

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

Вторая мысль: можно ли значительно увеличить частоту колебаний и выводить аналоговые сигналы в стиле ШИМ ?

Я помню, как давным-давно смотрел на эмулятор NES, и я полагаю, что они использовали три формы волны, каждая с программируемой частотой. Две прямоугольные волны и одна треугольная волна.
...и, видимо, один источник шума. en.wikipedia.org/wiki/NES_Sound_Format

Как уже упоминалось, вы можете сделать это так же, как это делалось с динамиком ПК (который поддерживает только включение / выключение, опционально подключенное к ШИМ-контроллеру). достаточно быстро, чтобы он никогда полностью не включался и не выключался (немного похоже на то, как работает импульсный источник питания). Это заставляет динамик постоянно переключаться между включением и выключением, генерируя аналоговый сигнал.

Единственная проблема заключается в том, что вам нужен настоящий динамик (я думаю, что пьезоэлектрический динамик движется так быстро, что он слишком быстро включается и выключается), и вы должны иметь возможность переключать бит достаточно быстро. Я провел несколько экспериментов и получил максимальную скорость около 5 МГц , которой должно хватить для звукового сигнала частотой 11 025 Гц (вероятно, лучшее качество, на которое вы могли рассчитывать).

Конечно, 11025 Гц при 8 битах — это 11 килобайт в секунду, что намного быстрее, чем скорость последовательного порта. Это позволило бы сохранить во флэш-памяти только одну или две секунды звука, поэтому вы в значительной степени ограничены воспроизведением звука, сгенерированного на лету, при условии, что у процессора остается достаточно свободного времени для вращения динамика!

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

Вы можете использовать фильтр перед динамиком, чтобы сгладить ШИМ, независимо от того, насколько быстро движется сам динамик.

Вы можете просто добавить потоки, как описано здесь:

http://www.vttoth.com/digimix.htm

Воспроизведите звук A на мгновение, например, 50 мс, затем звук B и переключитесь назад и вперед. Идея состоит в том, чтобы переключаться быстрее, чем может сказать ухо, и это будет звучать так, как будто оба играют одновременно.

Я считаю, что для Arduino есть библиотека тонов, которая воспроизводит два тона. Вы должны иметь возможность адаптировать код к используемому чипу AVR. На arduino.cc также есть несколько отличных тем по генерации сигналов.

Если вы решите добавить ЦАП, у меня есть пример генератора с числовым управлением по адресу http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html . Четыре независимых выходных канала. Четырехканальный ЦАП и ссылка стоят всего около 2 долларов или около того.

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

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