Sonidos polifónicos de un microcontrolador?

14

Puedo hacer sonidos monofónicos al alternar un solo pin ( a una velocidad variable ) conectado a un zumbador piezoeléctrico.

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

Aquí está el código que estoy usando para tocar 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);
    }
}
Toby Jaffey
fuente
Oye, ¿puede esta cosa emitir lenguaje humano? Quiero decir como palabras?
Rick_2047
1
Echa un vistazo a Cantarino - code.google.com/p/tinkerit/wiki/Cantarino
Toby Jaffey
@Joby, el recurso que diste fue excelente, pero vi la demostración, en realidad no dice nada audible. ¿Algún otro que conozcas?
Rick_2047
No sin un DAC, no.
Toby Jaffey
@Joby, ¿qué tienes con un DAC?
Rick_2047

Respuestas:

8

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

davr
fuente
1
Si está utilizando PWM (conmutación a una frecuencia mucho más alta que la señal deseada), entonces ya puede mezclar varias señales usando solo un pin de salida.
endolito
5

La forma estándar de obtener polifonía es interrumpir a una frecuencia de interrupción fija (con mayor frecuencia 8000 Hz o 44100 Hz), obtener un "alto" (+1) o "bajo" (-1) (o algo intermedio) de cada fuente de sonido , sume todos los números para obtener un total, luego envíe ese número total al DAC.

Como otros han dicho aquí, con un poco de inteligencia, un PWM de alta velocidad puede reemplazar un DAC.

La página de "polifonía de microcontroladores" ofrece más detalles y consejos.

davidcary
fuente
3

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

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


fuente
Todavía puedo escuchar la melodía en mi cabeza
Toby Jaffey
2

Esto podría ayudar -> DAC PWM simple

Jim
fuente
Para que Arduino reproduzca las notas de forma asincrónica, necesitará usar un sistema similar al MIDI, con comandos separados para activar y desactivar la nota, etc., la biblioteca de tonos incorporada hace esto pero no hace polifonía, pero la última versión parece lo hace - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
Jim
2

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

int selector;
uint16_t fase [8], frecuencia [8];

interrupción nula (nula) { selector ++; selector & = 7; fase [selector] + frecuencia [selector]; DAC_OUT = onda sinusoidal [fase [selector] >> 8]; }

Lo anterior es el enfoque esencial que utilicé en una caja de música basada en PIC en 1996 (usando código de ensamblaje en lugar de C). Tenga en cuenta que la frecuencia de interrupción debe ser 8 veces la frecuencia de muestreo efectiva, pero cada interrupción solo tiene que hacer el procesamiento para una sola voz. Tenga en cuenta que si el filtrado de salida es bueno, este enfoque producirá 3 bits más de resolución efectiva de DAC que agregar las muestras numéricamente y luego emitirlas, pero generará mucho ruido a la frecuencia de muestreo y sus múltiplos. Por lo tanto, el filtrado es aún más importante de lo que sería de otra manera.

Super gato
fuente
1

Solían hacer esto en sistemas de juegos antiguos y en los días de los " parlantes de PC ", pero no sé cómo.

Primero adivine: piense en la onda que idealmente haría, luego imagine distorsionarla en una forma cuadrada muy recortada, luego cree esa forma cuadrada alternando su salida en los momentos apropiados. Sin embargo, tendría mucha intermodulación .

Pensamiento secundario: ¿puede aumentar en gran medida la frecuencia de las señales analógicas de oscilación y salida al estilo PWM ?

endolito
fuente
2
Recuerdo haber visto un emulador NES hace mucho tiempo y creo que usaron tres formas de onda, cada una con una frecuencia programable. Dos ondas cuadradas y una onda triangular.
mjh2007
... y una fuente de ruido, aparentemente. en.wikipedia.org/wiki/NES_Sound_Format
endolith
1

Como se ha mencionado, podría hacerlo de la misma manera que solía hacerlo con un altavoz de PC (que solo admite encendido / apagado opcionalmente conectado a un controlador PWM). Básicamente, mi comprensión del método es que enciende y apaga el altavoz lo suficientemente rápido como para que nunca se encienda o apague completamente (un poco como funciona una fuente de alimentación de modo de interruptor). Esto deja al altavoz en constante movimiento entre encendido y apagado, generando una señal analógica.

Los únicos inconvenientes son que necesita un altavoz real (creo que un piezo se mueve tan rápido que se enciende y se apaga demasiado rápido) y necesita poder alternar el bit lo suficientemente rápido. Hice algunos experimentos y obtuve una velocidad máxima de alrededor de 5MHz que debería ser suficiente para una señal de audio de 11,025 Hz (probablemente la mejor calidad que podrías esperar obtener).

Por supuesto, 11025Hz a 8 bits es 11 kilobytes / segundo, que es mucho más rápido que la velocidad de un puerto serie. Solo permitiría almacenar un segundo o dos de audio en el flash, por lo que está bastante limitado a reproducir audio generado sobre la marcha, ¡siempre que tenga suficiente tiempo de CPU para girar el altavoz!

Hay un par de otros métodos para lograr esto también, y parece que hay ya una implementación para el Arduino del método descrito anteriormente.

Malvinoso
fuente
2
Puede usar un filtro antes del altavoz para suavizar el PWM, independientemente de la velocidad del movimiento del altavoz.
endolito
1

Reproduzca el sonido A por un momento, como 50 ms, luego suene B y cambie de un lado a otro. La idea es cambiar más rápido de lo que el oído puede ver y sonará como si ambos estuvieran jugando al mismo tiempo.

Matt Williamson
fuente
1

Creo que hay una biblioteca de tonos para el Arduino que tiene dos tonos. Debería poder adaptar el código al chip AVR que está utilizando. También hay un par de excelentes hilos de generación de formas de onda en arduino.cc

Si decide agregar un DAC, tengo un ejemplo de oscilador controlado numéricamente en http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Cuatro canales de salida independientes. El DAC cuádruple y la referencia solo cuestan alrededor de $ 2.

jluciani
fuente
0

Aquí está mi código para tocar 2 melodías al mismo tiempo. Lo sentimos, debes registrarte en AVR freaks para obtener acceso.

avra
fuente
44
Le daría un voto positivo si publicó el código aquí, o en algún lugar donde no necesite una cuenta ...
Toby Jaffey