Polyphone Klänge aus einem Mikrocontroller?

Ich kann monophone Töne erzeugen, indem ich einen einzelnen Stift ( mit unterschiedlicher Geschwindigkeit ) umschalte, der mit einem Piezo-Summer verbunden ist.

Wie kann ich in Software zwei gemischte Audiosignale erzeugen, um Polyphonie zu erzeugen?

Hier ist der Code, den ich verwende, um eine einfache Melodie zu spielen.

#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);
    }
}
Hey, kann dieses Ding menschliche Sprache aussenden? Ich meine wie Worte?
Schauen Sie sich Cantarino an - code.google.com/p/tinkerit/wiki/Cantarino
@Joby Die Ressource, die Sie gegeben haben, war großartig, aber ich habe die Demo gesehen, sie sagt eigentlich nichts Hörbares aus. Kennen Sie noch andere?
Nicht ohne DAC, nein.
@Joby was hast du mit einem DAC?
Mischen von Audio-Samples auf einem GBA/ARM7 - hackpadsoftware.com/projects/gbaboal.html
Schauen Sie sich das Chipophon an. Kein Quellcode, aber ähnlich aufgebaut wie der Synthesizer im NES. Nichts von diesem beschissenen 2-stimmigen Müll. 38-kHz-12-Bit-8-Kanal-Polyphonie von einem AVR. linusakesson.net/chipophone/index.php

Antworten (12)

Nun, ein einfacher Trick besteht darin, zwei Pins mit PWM zu verwenden und sie an den gegenüberliegenden Seiten des Lautsprechers zu befestigen. Modulieren Sie dann jeden Pin mit einer anderen Geschwindigkeit, und Sie können zwei Noten gleichzeitig spielen ... im Grunde mischt der Lautsprecher sie für Sie zusammen. Mehr als zwei Noten und Sie müssen dies wie erwähnt in der Software tun.

Wenn Sie PWM verwenden (Schalten mit einer viel höheren Frequenz als das gewünschte Signal), können Sie bereits mehrere Signale mit nur einem Ausgangspin mischen.

Die Standardmethode, um Polyphonie zu erhalten, besteht darin, bei einer festen Unterbrechungsrate (meistens 8000 Hz oder 44100 Hz) zu unterbrechen und von jeder Tonquelle ein "hoch" (+1) oder "niedrig" (-1) (oder etwas dazwischen) zu erhalten , addieren Sie alle Zahlen, um eine Gesamtzahl zu erhalten, und senden Sie diese Gesamtzahl dann an den DAC.

Wie andere hier gesagt haben, kann ein Hochgeschwindigkeits-PWM mit ein bisschen Cleverness einen DAC ersetzen.

Auf der Seite "Mikrocontroller-Polyphonie" finden Sie weitere Details und Tipps.

Ich denke, dieses nette alte kleine PC-DOS-Spieljuwel verwendet echten polyphonen Sound über den PC-Lautsprecher: Digger .

Ich weiß nicht, wie sie es gemacht haben, aber Sie können den C-Quellcode von der Website herunterladen.

Ich kann die Melodie immer noch in meinem Kopf hören

Dies könnte helfen -> einfacher PWM-DAC

Um den Arduino dazu zu bringen, die Noten asynchron zu spielen, müssten Sie ein MIDI-ähnliches System verwenden - mit separaten Befehlen für Note ein und Note aus usw., die eingebaute Tonbibliothek tut dies, macht aber keine Polyphonie - aber die neueste Version sieht so aus es tut - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation

Sie könnten einfach zwei Rechteckwellen hinzufügen und schnelle PWM verwenden, um das "analoge" Signal an den Lautsprecher auszugeben.

Hier ist noch eine andere Methode, wenn Sie Spielsound mögen, schnell und schmutzig:

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

Wenn Sie Software verwenden, um Ihre Rednerereignisse zu timen, besteht der einfachste Ansatz wahrscheinlich darin, zwei unabhängige Datenströme zu generieren und zwischen ihnen zu wechseln. Dieser Ansatz kann ziemlich gut funktionieren, unabhängig davon, ob der Lautsprecherausgang von einem I/O-Pin oder einem DAC gesteuert wird. Zum Beispiel:

int-Selektor;
uint16_t phase[8],freq[8];

ungültige Unterbrechung (nichtig) { Selektor++; Selektor&=7; Phase[Wahlschalter] + Frequenz[Wahlschalter]; DAC_OUT = Sinuswelle [Phase [Selektor] >> 8]; }

Das obige ist der wesentliche Ansatz, den ich 1996 in einer PIC-basierten Spieluhr verwendet habe (unter Verwendung von Assembler-Code anstelle von C). Beachten Sie, dass die Interrupt-Rate das 8-fache der effektiven Abtastrate betragen muss, aber jeder Interrupt nur die Verarbeitung für eine einzelne Stimme übernehmen muss. Beachten Sie, dass dieser Ansatz bei guter Ausgangsfilterung 3 Bit mehr effektive DAC-Auflösung liefert, als wenn die Abtastwerte numerisch addiert und dann ausgegeben würden, aber er erzeugt viel Rauschen bei der Abtastrate und Vielfachen davon. Das Filtern ist daher noch wichtiger als es sonst der Fall wäre.

Früher hat man das auf alten Spielsystemen und zu Zeiten der „ PC-Lautsprecher “ gemacht, aber ich weiß nicht wie.

Erste Vermutung: Denken Sie an die Welle, die Sie idealerweise erzeugen würden, stellen Sie sich dann vor, sie in eine stark beschnittene quadratische Form zu verzerren, und erzeugen Sie dann diese quadratische Form, indem Sie Ihre Ausgabe zu den entsprechenden Zeiten umschalten. Hätte aber viel Intermodulation .

Zweiter Gedanke: Können Sie die Schwingungsfrequenz stark erhöhen und analoge Signale im PWM-Stil ausgeben ?

Ich erinnere mich, dass ich vor langer Zeit einen NES-Emulator gesehen habe und ich glaube, dass sie drei Wellenformen mit jeweils programmierbarer Frequenz verwendeten. Zwei Rechteckwellen und eine Dreieckwelle.
... und anscheinend eine Geräuschquelle. en.wikipedia.org/wiki/NES_Sound_Format

Wie bereits erwähnt, können Sie dies genauso tun wie früher mit einem PC-Lautsprecher (der nur das Ein- und Ausschalten unterstützt, das optional an einen PWM-Controller angeschlossen ist). Grundsätzlich verstehe ich die Methode, dass Sie den Lautsprecher ein- und ausschalten schnell genug, dass es nie vollständig ein- oder ausgeschaltet ist (ein bisschen wie ein Schaltnetzteil funktioniert). Dadurch bewegt sich der Lautsprecher ständig zwischen ein und aus und erzeugt ein analoges Signal.

Die einzigen Fallstricke sind, dass Sie einen echten Lautsprecher brauchen (ich denke, ein Piezo bewegt sich so schnell, dass er zu schnell voll an und voll aus ist) und Sie müssen in der Lage sein, das Bit schnell genug umzuschalten. Ich habe einige Experimente durchgeführt und bin auf eine maximale Geschwindigkeit von etwa 5 MHz gekommen , was für ein 11.025-Hz-Audiosignal ausreichend sein sollte (wahrscheinlich die beste Qualität, die Sie sich erhoffen können).

Natürlich sind 11025 Hz bei 8 Bit 11 Kilobyte/Sekunde, was viel schneller ist als die Geschwindigkeit einer seriellen Schnittstelle. Es würde nur das Speichern von Audio im Wert von vielleicht ein oder zwei Sekunden im Flash erlauben, also sind Sie ziemlich darauf beschränkt, Audio abzuspielen, das spontan generiert wird, vorausgesetzt, dass genügend CPU-Zeit übrig bleibt, um den Lautsprecher zu drehen!

Es gibt auch ein paar andere Methoden, um dies zu erreichen, und es sieht so aus, als ob es bereits eine Implementierung für das Arduino der oben beschriebenen Methode gibt.

Sie können einen Filter vor dem Lautsprecher verwenden, um die PWM zu glätten, unabhängig davon, wie schnell sich der Lautsprecher selbst bewegt.

Sie können die Streams einfach wie hier beschrieben hinzufügen:

http://www.vttoth.com/digimix.htm

Spielen Sie Ton A für einen Moment, etwa 50 ms, dann Ton B und schalten Sie hin und her. Die Idee ist, schneller umzuschalten, als das Ohr sagen kann, und es wird so klingen, als würden beide gleichzeitig spielen.

Ich glaube, es gibt eine Tonbibliothek für den Arduino, die zwei Töne macht. Sie sollten den Code an den von Ihnen verwendeten AVR-Chip anpassen können. Auf arduino.cc gibt es auch ein paar hervorragende Threads zur Wellenformerzeugung

Wenn Sie sich entscheiden, einen DAC hinzuzufügen, habe ich ein Beispiel für einen numerisch gesteuerten Oszillator unter http://wiblocks.luciani.org/docs/app-notes/nb1a-nco.html Vier unabhängige Ausgangskanäle. Der Quad-DAC und die Referenz kosten nur etwa 2 US-Dollar.

Hier ist mein Code zum gleichzeitigen Abspielen von 2 Melodien. Entschuldigung, Sie müssen sich bei AVR-Freaks registrieren, um Zugriff zu erhalten.

Ich würde dir eine positive Stimme geben, wenn du den Code hier postest, oder irgendwohin, wo ich kein Konto benötige ...