Sons polyphoniques d'un microcontrôleur?

14

Je peux faire des sons monophoniques en basculant une seule broche ( à un rythme variable ) connectée à un buzzer piézo.

Comment puis-je générer deux signaux audio mixtes dans un logiciel pour créer une polyphonie?

Voici le code que j'utilise pour jouer un morceau 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
la source
Hé, cette chose peut-elle émettre un langage humain? Je veux dire comme des mots?
Rick_2047
1
Jetez un œil à Cantarino - code.google.com/p/tinkerit/wiki/Cantarino
Toby Jaffey
@Joby, la ressource que vous avez donnée était géniale, mais j'ai vu la démo, elle ne dit en fait rien d'audible.
Rick_2047
Pas sans DAC, non.
Toby Jaffey
@Joby qu'est-ce que vous avez avec un DAC?
Rick_2047

Réponses:

8

Eh bien, une astuce simple consiste à utiliser deux broches avec PWM et à les attacher aux côtés opposés de l'enceinte. Modulez ensuite chaque broche à une vitesse différente, et vous pouvez jouer deux notes à la fois ... fondamentalement, le haut-parleur les mélange ensemble pour vous. Plus de deux notes et vous devrez le faire dans le logiciel comme mentionné.

davr
la source
1
Si vous utilisez PWM (commutation à une fréquence beaucoup plus élevée que le signal souhaité), vous pouvez déjà mélanger plusieurs signaux ensemble en utilisant uniquement une broche de sortie.
endolith
5

La manière standard d'obtenir la polyphonie est d'interrompre à un taux d'interruption fixe (le plus souvent 8000 Hz ou 44100 Hz), d'obtenir un "haut" (+1) ou "bas" (-1) (ou quelque chose d'intermédiaire) de chaque source sonore , additionnez tous les chiffres pour obtenir un total, puis envoyez ce nombre total au CAD.

Comme d'autres l'ont dit ici, avec un peu d'intelligence, un PWM haute vitesse peut remplacer un DAC.

La page "polyphonie du microcontrôleur" donne plus de détails et de conseils.

davidcary
la source
3

Je pense que ce joli petit bijou de jeu PC DOS utilisait un vrai son polyphonique via le haut-parleur PC: Digger .

Je ne sais pas comment ils l'ont fait, mais vous pouvez télécharger le code source C depuis le site.


la source
Je peux encore entendre l'air dans ma tête
Toby Jaffey
2

Cela pourrait aider -> simple PWM DAC

Jim
la source
Pour que l'Arduino joue les notes de manière asynchrone, vous devez utiliser un système similaire au MIDI - avec des commandes distinctes pour activer et désactiver la note, etc., la bibliothèque de sons intégrée le fait mais ne fait pas de polyphonie - mais la dernière version ressemble à c'est le cas - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
Jim
2

Si vous utilisez un logiciel pour chronométrer vos événements de conférencier, l'approche la plus simple consiste probablement à générer deux flux de données indépendants et à les alterner. Cette approche peut très bien fonctionner, que la sortie du haut-parleur soit contrôlée par une broche d'E / S ou un DAC. Par exemple:

sélecteur int;
phase uint16_t [8], freq [8];

annuler l'interruption (vide) { selector ++; sélecteur & = 7; phase [sélecteur] + freq [sélecteur]; DAC_OUT = onde sinusoïdale [phase [sélecteur] >> 8]; }

Ce qui précède est l'approche essentielle que j'ai utilisée dans une boîte à musique basée sur PIC en 1996 (en utilisant le code assembleur plutôt que C). Notez que le taux d'interruption doit être 8 fois le taux d'échantillonnage effectif, mais chaque interruption ne doit effectuer le traitement que pour une seule voix. Notez que si le filtrage de sortie est bon, cette approche produira 3 bits de résolution DAC efficace de plus que l'ajout numérique des échantillons puis leur sortie, mais cela générera beaucoup de bruit à la fréquence d'échantillonnage et des multiples de celle-ci. Le filtrage est donc encore plus important qu'il ne le serait autrement.

supercat
la source
1

Ils le faisaient sur les anciens systèmes de jeu et à l'époque des " haut-parleurs PC " ", mais je ne sais pas comment.

Première supposition: pensez à l'onde que vous feriez idéalement, puis imaginez-la en la déformant en une forme carrée fortement découpée, puis créez cette forme carrée en basculant votre sortie aux moments appropriés. Aurait beaucoup d' intermodulation , cependant.

Deuxième réflexion: pouvez-vous augmenter considérablement la fréquence des oscillations et produire des signaux analogiques de type PWM ?

endolith
la source
2
Je me souviens d'avoir regardé un émulateur NES il y a longtemps et je pense qu'ils utilisaient trois formes d'onde chacune avec une fréquence programmable. Deux ondes carrées et une onde triangulaire.
mjh2007
... et une source de bruit, apparemment. en.wikipedia.org/wiki/NES_Sound_Format
endolith
1

Comme cela a été mentionné, vous pouvez le faire de la même manière qu'auparavant avec un haut-parleur PC (qui ne prend en charge que la mise sous / hors tension éventuellement attachée à un contrôleur PWM.) Ma compréhension de la méthode consiste essentiellement à allumer et à éteindre le haut-parleur assez rapide pour qu'il ne soit jamais complètement allumé ou éteint (un peu comme le fonctionnement d'une alimentation à découpage). Cela laisse le haut-parleur constamment en marche et en arrêt, générant un signal analogique.

Le seul problème, c'est que vous avez besoin d'un véritable haut-parleur (je pense qu'un piézo se déplace si vite qu'il atteint son plein maximum et trop vite) et que vous devez pouvoir basculer le bit assez rapidement. J'ai fait quelques expériences et j'ai trouvé une vitesse maximale d'environ 5 MHz, ce qui devrait être suffisant pour un signal audio à 11 025 Hz (probablement la meilleure qualité que vous puissiez espérer obtenir).

Bien sûr, 11025 Hz @ 8 bits est de 11 kilo-octets / seconde, ce qui est beaucoup plus rapide que la vitesse d'un port série. Cela ne permettrait peut-être qu'une seconde ou deux d'audio à être stocké dans le flash, vous êtes donc à peu près limité à la lecture audio générée à la volée, à condition que cela laisse suffisamment de temps CPU disponible pour déformer le haut-parleur!

Il existe également quelques autres méthodes pour y parvenir, et il semble qu'il existe déjà une implémentation pour l'Arduino de la méthode décrite ci-dessus.

Malvineous
la source
2
Vous pouvez utiliser un filtre avant l'enceinte pour lisser le PWM, quelle que soit la vitesse à laquelle l'enceinte se déplace.
endolith
1

Jouez le son A pendant un moment, comme peut-être 50 ms, puis sonnez B et basculez d'avant en arrière. L'idée est de passer plus vite que l'oreille ne peut le dire et cela sonnera comme si les deux jouaient en même temps.

Matt Williamson
la source
1

Je crois qu'il y a une bibliothèque de sons pour l'Arduino qui fait deux sons. Vous devriez pouvoir adapter le code à la puce AVR que vous utilisez. Il y a aussi quelques excellents threads de génération de formes d'onde sur arduino.cc

Si vous décidez d'ajouter un DAC, j'ai un exemple d'oscillateur à commande numérique sur http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Quatre canaux de sortie indépendants. Le quad DAC et la référence ne coûtent que 2 $ environ.

jluciani
la source
0

Voici mon code pour jouer 2 morceaux en même temps. Désolé, vous devez vous inscrire à AVR freaks pour y accéder.

avra
la source
4
Je vous donnerais un vote positif si vous avez posté le code ici, ou dans un endroit où je n'ai pas besoin d'un compte ...
Toby Jaffey