14 votos

¿Sonidos polifónicos desde un microcontrolador?

Puedo hacer sonidos monofónicos alternando un solo pin ( a un ritmo variable ) conectado a un zumbador piezoeléctrico.

¿Cómo puedo generar dos señales de audio mezcladas en el software para crear polifonía?

Aquí está el código que estoy usando para reproducir una melodía simple.

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

8voto

Bueno un truco fácil es usar dos pines con PWM, y atarlos a los lados opuestos del altavoz. Entonces modula cada pin a una velocidad diferente, y puedes tocar dos notas a la vez... básicamente el altavoz las mezcla por ti. Más de dos notas y tendrás que hacerlo en software como se ha mencionado.

5voto

Jeremy Ruten Puntos 59989

La forma estándar de obtener la polifonía es interrumpir a alguna tasa de interrupción fija (casi siempre 8000 Hz o 44100 Hz), obtener un "alto" (+1) o "bajo" (-1) (o algo intermedio) de cada fuente de sonido, sumar todos los números para obtener un total, y luego enviar ese número total fuera del DAC.

Como otros han dicho aquí, con un poco de ingenio un PWM de alta velocidad puede sustituir a un DAC.

Le site "polifonía del microcontrolador" ofrece más detalles y consejos.

3voto

Creo que esta pequeña joya de juego para PC DOS utilizaba sonido polifónico real a través del altavoz del PC: Digger .

No sé cómo lo hicieron, pero puedes descargar el código fuente en C desde el sitio.

2voto

Alex Andronov Puntos 178

Si utilizas un software para cronometrar los eventos de los altavoces, el enfoque más sencillo es probablemente generar dos flujos de datos independientes y alternar entre ellos. Este enfoque puede funcionar bastante bien tanto si la salida del altavoz está controlada por un pin de E/S como por un DAC. Por ejemplo:

int selector;
uint16\_t phase\[8\],freq\[8\];

void interrupt(void)
{
  selector++;
  selector&=7;
  phase\[selector\] + freq\[selector\];
  DAC\_OUT = sinewave\[phase\[selector\] >> 8\];
}

Lo anterior es el enfoque esencial que utilicé en una caja de música basada en PIC en 1996 (utilizando código ensamblador en lugar de C). Ten en cuenta que la tasa de interrupción tiene que ser 8 veces la tasa de muestreo efectiva, pero cada interrupción sólo tiene que hacer el procesamiento para una sola voz. Ten en cuenta que si el filtrado de la salida es bueno, este enfoque producirá 3 bits más de resolución efectiva del DAC que si se sumaran las muestras numéricamente y luego se emitieran, pero generará mucho ruido a la velocidad de muestreo y sus múltiplos. Por tanto, el filtrado es aún más importante de lo que sería en caso contrario.

2voto

Podrías simplemente añadir dos ondas cuadradas, y utilizar un pwm rápido para emitir la señal "analógica" al altavoz.

aquí hay otro método diferente si te gusta el sonido del juego, rápido y sucio:

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

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X