Dźwięki polifoniczne z mikrokontrolera?

14

Mogę wydawać dźwięki monofoniczne, przełączając pojedynczy pin ( w różnym tempie ) podłączony do brzęczyka piezoelektrycznego.

Jak mogę wygenerować dwa zmiksowane sygnały audio w oprogramowaniu, aby stworzyć polifonię?

Oto kod, którego używam do grania prostej melodii.

#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);
    }
}
Toby Jaffey
źródło
Hej, czy to może emitować ludzki język? Mam na myśli jak słowa?
Rick_2047
1
Spójrz na Cantarino - code.google.com/p/tinkerit/wiki/Cantarino
Toby Jaffey
@Joby zasób, który podałeś był świetny, ale widziałem demo, w rzeczywistości nie mówi nic słyszalnego. Czy znasz kogoś innego?
Rick_2047
Nie bez DAC, nie.
Toby Jaffey
@Joby co masz z DAC?
Rick_2047,

Odpowiedzi:

8

Cóż, jedną łatwą sztuczką jest użycie dwóch pinów z PWM i przywiązanie ich do przeciwnych stron głośnika. Następnie moduluj każdy pin z inną prędkością i możesz grać dwie nuty jednocześnie ... w zasadzie głośnik miksuje je razem. Więcej niż dwie notatki i będziesz musiał to zrobić w oprogramowaniu, jak wspomniano.

davr
źródło
1
Jeśli używasz PWM (przełączanie na znacznie wyższej częstotliwości niż pożądany sygnał), możesz już miksować wiele sygnałów razem, używając tylko jednego styku wyjściowego.
endolith
5

Standardowym sposobem uzyskania polifonii jest przerywanie z określoną stałą częstotliwością przerywania (najczęściej 8000 Hz lub 44100 Hz), uzyskanie „wysokiego” (+1) lub „niskiego” (-1) (lub czegoś pośredniego) z każdego źródła dźwięku , dodaj wszystkie liczby, aby uzyskać sumę, a następnie wyślij tę liczbę do DAC.

Jak powiedzieli inni, przy odrobinie sprytu szybki PWM może zastąpić przetwornik cyfrowo-analogowy.

Strona „polifonia mikrokontrolera” zawiera więcej szczegółów i wskazówek.

Davidcary
źródło
3

Myślę, że ten stary, miły klejnot do gier DOS na PC wykorzystał prawdziwy dźwięk polifoniczny przez głośnik PC: Digger .

Nie wiem, jak to zrobili, ale możesz pobrać kod źródłowy C ze strony.


źródło
Wciąż słyszę melodię w mojej głowie
Toby Jaffey
2

Jeśli korzystasz z oprogramowania do mierzenia czasu swoich mówców, najłatwiejszym sposobem jest prawdopodobnie wygenerowanie dwóch niezależnych strumieni danych i przełączanie między nimi. To podejście może działać całkiem dobrze, niezależnie od tego, czy wyjście głośnika jest kontrolowane przez pin I / O lub DAC. Na przykład:

selektor int;
uint16_t faza [8], freq [8];

void interrupt (void) { selektor ++; selektor & = 7; faza [selektor] + freq [selektor]; DAC_OUT = fala sinusoidalna [faza [selektor] >> 8]; }

Powyższe jest podstawowym podejściem, którego użyłem w pozytywce opartej na PIC w 1996 roku (używając kodu asemblera zamiast C). Zauważ, że częstotliwość przerwań musi być 8-krotna efektywna częstotliwość próbkowania, ale każde przerwanie musi przetworzyć tylko jeden głos. Zauważ, że jeśli filtrowanie wyjściowe jest dobre, to podejście zapewni 3-bitową efektywną rozdzielczość DAC niż dodawanie próbek numerycznie, a następnie ich wysyłanie, ale generuje dużo szumu przy częstotliwości próbkowania i jej wielokrotności. Filtrowanie jest zatem jeszcze ważniejsze niż byłoby inaczej.

supercat
źródło
1

Robili to na starych systemach gier i w czasach „ głośników PC ”, ale nie wiem jak.

Najpierw zgadnij: pomyśl o fali, którą idealnie byś utworzył, a następnie wyobraź sobie, że zniekształcasz ją do mocno obciętego kwadratu, a następnie utwórz ten kwadratowy kształt, przełączając wyjście w odpowiednim momencie. Miałby jednak wiele intermodulacji .

Druga myśl: czy można znacznie zwiększyć częstotliwość oscylacji i wyjściowe sygnały analogowe w stylu PWM ?

endolit
źródło
2
Pamiętam, jak dawno temu patrzyłem na emulator NES i sądzę, że użyli trzech przebiegów o programowalnej częstotliwości. Dwie fale kwadratowe i jedna fala trójkątna.
mjh2007
... i najwyraźniej jedno źródło hałasu. en.wikipedia.org/wiki/NES_Sound_Format
endolith
1

Jak już wspomniano, można to zrobić w taki sam sposób, jak w przypadku głośnika komputerowego (który obsługuje tylko włączanie / wyłączanie opcjonalnie podłączone do kontrolera PWM). Zasadniczo rozumiem metodę polegającą na włączaniu i wyłączaniu głośnika wystarczająco szybko, aby nigdy nie był w pełni włączony lub wyłączony (trochę tak, jak działa zasilacz impulsowy). To sprawia, że ​​głośnik ciągle się porusza między włączaniem i wyłączaniem, generując sygnał analogowy.

Jedyne problemy to to, że potrzebujesz prawdziwego głośnika (myślę, że piezo porusza się tak szybko, że osiąga pełne włączenie i wyłączenie zbyt szybko) i musisz być w stanie wystarczająco szybko przełączyć bit. Przeprowadziłem kilka eksperymentów i opracowałem maksymalną prędkość około 5 MHz, która powinna wystarczyć na sygnał audio 11,025 Hz (prawdopodobnie najlepszą jakość, jaką można uzyskać).

Oczywiście 11025Hz przy 8 bitach to 11 kilobajtów / sekundę, co jest znacznie szybsze niż prędkość portu szeregowego. Pozwoliłoby to na przechowanie w pamięci flash może drugiej lub dwóch wartości audio, więc jesteś prawie ograniczony do odtwarzania dźwięku generowanego w locie, pod warunkiem, że pozostawia to dość wolnego czasu procesora, aby skręcić głośnik!

Istnieje również kilka innych metod, aby to osiągnąć, i wygląda na to, że istnieje już implementacja dla Arduino metody opisanej powyżej.

Malvineous
źródło
2
Możesz użyć filtra przed głośnikiem, aby wygładzić PWM, niezależnie od tego, jak szybko porusza się sam głośnik.
endolith
1

Odtwarzaj przez chwilę dźwięk A, na przykład może 50 ms, a następnie dźwięk B i przełączaj się tam iz powrotem. Chodzi o to, aby przełączać się szybciej niż ucho może to rozpoznać i będzie to brzmiało tak, jakby grało jednocześnie.

Matt Williamson
źródło
1

Uważam, że dla Arduino istnieje biblioteka brzmień, która wykonuje dwa dźwięki. Powinieneś być w stanie dostosować kod do używanego układu AVR. Na stronie arduino.cc znajduje się także kilka doskonałych wątków generujących fale

Jeśli zdecydujesz się na dodanie przetwornika cyfrowo-analogowego, mam przykład oscylatora sterowanego numerycznie pod adresem http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Cztery niezależne kanały wyjściowe. Quad DAC i referencja to tylko około 2 USD.

jluciani
źródło
0

Oto mój kod do grania 2 melodii jednocześnie. Niestety, musisz się zarejestrować u maniaków AVR, aby uzyskać dostęp.

avra
źródło
4
Oddałbym ci głos, jeśli opublikowałeś kod tutaj lub gdzieś nie potrzebuję konta ...
Toby Jaffey