Suoni polifonici da un microcontrollore?


14

Posso emettere suoni monofonici attivando un singolo pin ( a una velocità variabile ) collegato a un cicalino piezoelettrico.

Come posso generare due segnali audio misti nel software per creare polifonia?

Ecco il codice che sto usando per suonare una melodia semplice.

#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);
    }
}

Ehi, questa cosa può emettere un linguaggio umano? Intendo come parole?
Rick_2047,

1
Dai

@Joby la risorsa che hai dato è stata grandiosa, ma ho visto la demo, in realtà non sta dicendo nulla di udibile. Qualche altro che conosci?
Rick_2047,

Non senza un DAC, no.
Toby Jaffey,

@Joby cosa hai con un DAC?
Rick_2047,

Risposte:


8

Bene, un trucco semplice è usare due pin con PWM e legarli ai lati opposti dell'altoparlante. Quindi modula ciascun pin a una velocità diversa e puoi suonare due note contemporaneamente ... in pratica l'altoparlante le sta mescolando insieme per te. Più di due note e dovrai farlo nel software come detto.


1
Se stai usando PWM (passando a una frequenza molto più alta del segnale desiderato), allora puoi già mescolare più segnali insieme usando solo un pin di uscita.
endolith

5

Il modo standard per ottenere la polifonia è interrompere ad una certa frequenza di interruzione fissa (il più delle volte 8000 Hz o 44100 Hz), ottenere un "alto" (+1) o "basso" (-1) (o qualcosa di intermedio) da ciascuna sorgente sonora , sommare tutti i numeri per ottenere un totale, quindi inviare quel numero totale al DAC.

Come altri hanno già detto qui, con un po 'di intelligenza un PWM ad alta velocità può sostituire un DAC.

La pagina "polifonia microcontrollore" fornisce ulteriori dettagli e suggerimenti.


3

Penso che questa bella gemma del vecchio gioco DOS per PC abbia usato un suono polifonico reale attraverso l'altoparlante del PC: Digger .

Non so come l'hanno fatto, ma puoi scaricare il codice sorgente C dal sito.


Riesco ancora a sentire la melodia nella mia testa
Toby Jaffey,



2

Se stai utilizzando un software per cronometrare gli eventi dei relatori, l'approccio più semplice è probabilmente quello di generare due flussi di dati indipendenti e alternarli. Questo approccio può funzionare abbastanza bene se l'uscita dei diffusori è controllata da un pin I / O o un DAC. Per esempio:

selettore int;
uint16_t phase [8], freq [8];

void interrupt (void) { selettore ++; Selettore & = 7; fase [selettore] + freq [selettore]; DAC_OUT = sinusoidale [fase [selettore] >> 8]; }

Quanto sopra è l'approccio essenziale che ho usato in un carillon basato su PIC nel 1996 (usando il codice assembly anziché C). Si noti che la frequenza di interruzione deve essere 8 volte la frequenza di campionamento effettiva, ma ogni interruzione deve eseguire l'elaborazione per una sola voce. Si noti che se il filtro di uscita è buono, questo approccio produrrà 3 bit in più di risoluzione DAC effettiva rispetto all'aggiunta numerica dei campioni e alla loro emissione, ma genererà molto rumore alla frequenza di campionamento e ai suoi multipli. Il filtro è quindi ancora più importante di quanto non sarebbe altrimenti.


1

Lo facevano sui vecchi sistemi di gioco e ai tempi degli " altoparlanti per PC ", ma non so come.

Prima ipotesi: pensa all'onda che creeresti idealmente, quindi immagina di distorcerla in una forma quadrata fortemente ritagliata, quindi crea quella forma quadrata attivando l'output nei momenti appropriati. Avrebbe molta intermodulazione , però.

Secondo pensiero: puoi aumentare notevolmente la frequenza di oscillazione e emettere segnali analogici in stile PWM ?


2
Ricordo di aver guardato un emulatore NES molto tempo fa e credo che abbiano usato tre forme d'onda ciascuna con una frequenza programmabile. Due onde quadrate e un'onda triangolare.
mjh2007,

... e una fonte di rumore, a quanto pare. it.wikipedia.org/wiki/NES_Sound_Format
endolith

1

Come è stato accennato, è possibile farlo allo stesso modo di un altoparlante per PC (che supporta solo l'accensione e lo spegnimento opzionalmente collegati a un controller PWM). Fondamentalmente la mia comprensione del metodo è quella di accendere e spegnere l'altoparlante abbastanza veloce da non essere mai completamente acceso o spento (un po 'come funziona un alimentatore a commutazione). Questo lascia l'altoparlante costantemente in movimento tra acceso e spento, generando un segnale analogico.

Gli unici aspetti positivi sono che hai bisogno di un vero altoparlante (penso che un piezo si muova così velocemente da raggiungere il pieno e il pieno troppo in fretta) e devi essere in grado di attivare il bit abbastanza velocemente. Ho fatto alcuni esperimenti e ho trovato una velocità massima di circa 5 MHz che dovrebbe essere ampia per un segnale audio a 11.025 Hz (probabilmente la migliore qualità che si possa sperare di ottenere).

Ovviamente 11025Hz a 8 bit è 11 kilobyte / secondo, che è molto più veloce della velocità di una porta seriale. Consentirebbe solo un secondo o due audio di essere archiviati nel flash, quindi sei praticamente limitato a riprodurre l'audio generato al volo, a condizione che lasci abbastanza tempo CPU libero per modificare l'altoparlante!

Ci sono anche un paio di altri metodi per raggiungere questo obiettivo, e sembra che ci sia già un'implementazione per Arduino del metodo sopra descritto.


2
È possibile utilizzare un filtro prima dell'altoparlante per appianare il PWM, indipendentemente dalla velocità con cui si muove l'altoparlante stesso.
endolito il


1

Riproduci il suono A per un momento, come forse 50 ms, poi suona B e passa avanti e indietro. L'idea è di passare più velocemente di quanto l'orecchio possa dire e suonerà come se entrambi suonassero contemporaneamente.


1

Credo che ci sia una libreria di toni per Arduino che fa due toni. Dovresti essere in grado di adattare il codice al chip AVR che stai utilizzando. Ci sono anche un paio di eccellenti thread di generazione di forme d'onda su arduino.cc

Se decidi di aggiungere un DAC, ho un esempio di oscillatore a controllo numerico su http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Quattro canali di uscita indipendenti. Il DAC quad e il riferimento sono solo circa $ 2 o giù di lì.


0

Ecco il mio codice per suonare 2 brani contemporaneamente. Spiacenti, devi registrarti agli appassionati di AVR per accedere.


4
Ti darei un voto se avessi pubblicato il codice qui o in un posto in cui non ho bisogno di un account ...
Toby Jaffey,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.