Polyphone Klänge von einem Mikrocontroller?

14

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

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

Hier ist der Code, mit dem ich eine einfache Melodie spiele.

#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
quelle
Hey, kann dieses Ding menschliche Sprache ausstrahlen? Ich meine wie Worte?
Rick_2047
1
Schauen Sie sich Cantarino - code.google.com/p/tinkerit/wiki/Cantarino
Toby Jaffey am
@Joby die Ressource, die Sie gaben, war großartig, aber ich habe die Demo gesehen, sie sagt eigentlich nichts Hörbares. Kennen Sie andere?
Rick_2047
Nicht ohne DAC, nein.
Toby Jaffey
@Joby was hast du mit einem DAC?
Rick_2047

Antworten:

8

Nun, ein einfacher Trick besteht darin, zwei Stifte mit PWM zu verwenden und sie an die gegenüberliegenden Seiten des Lautsprechers zu binden. Dann modulieren Sie 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 es in der Software tun, wie erwähnt.

davr
quelle
1
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 zusammenmischen.
Endolith
5

Die Standardmethode, um Polyphonie zu erhalten, besteht darin, mit einer festgelegten Interrupt-Rate (meistens 8000 Hz oder 44100 Hz) zu unterbrechen und von jeder Tonquelle ein "High" (+1) oder "Low" (-1) (oder etwas dazwischenliegendes) zu erhalten Addieren Sie alle Zahlen, um eine Summe zu erhalten, und senden Sie diese Gesamtzahl an den DAC.

Wie andere hier bereits gesagt haben, kann ein Hochgeschwindigkeits-PWM mit ein wenig Geschick einen DAC ersetzen.

Die "Mikrocontroller-Polyphonie" weitere Informationen und Tipps.

Davidcary
quelle
3

Ich denke, dieses nette alte kleine PC-DOS-Spiel verwendete 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.


quelle
Ich kann immer noch die Melodie in meinem Kopf hören
Toby Jaffey
2

Dies könnte helfen -> einfacher PWM-DAC

Jim
quelle
Um das Arduino dazu zu bringen, die Noten asynchron zu spielen, müssten Sie ein MIDI-ähnliches System verwenden - mit separaten Befehlen für das Ein- und Ausschalten von Noten usw. Die eingebaute Tonbibliothek erledigt dies, aber nicht für Polyphonie - aber die neueste Version sieht so aus es funktioniert - code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation
Jim
2

Wenn Sie Software verwenden, um Ihre Lautsprecherereignisse zeitlich abzustimmen, besteht der einfachste Ansatz wahrscheinlich darin, zwei unabhängige Datenströme zu generieren und zwischen diesen zu wechseln. Dieser Ansatz kann ziemlich gut funktionieren, unabhängig davon, ob der Lautsprecherausgang von einem E / A-Pin oder einem DAC gesteuert wird. Beispielsweise:

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

void interrupt (nichtig) { Selector ++; Selektor & = 7; Phase [Selektor] + Frequenz [Selektor]; DAC_OUT = Sinuswelle [Phase [Selektor] >> 8]; }

Das Obige ist der wesentliche Ansatz, den ich 1996 in einer PIC-basierten Musikbox verwendet habe (mit Assembler-Code anstelle von C). Beachten Sie, dass die Interrupt-Rate das Achtfache der effektiven Sample-Rate betragen muss, aber jeder Interrupt nur die Verarbeitung für eine einzelne Stimme durchführen muss. Beachten Sie, dass diese Methode bei guter Ausgangsfilterung eine um 3 Bit höhere effektive DAC-Auflösung liefert, als wenn die Abtastwerte numerisch addiert und dann ausgegeben würden, aber bei der Abtastrate und Vielfachen davon viel Rauschen erzeugt wird. Filtern ist also noch wichtiger als sonst.

Superkatze
quelle
1

Früher machten sie das auf alten Spielsystemen und in den Tagen von " PC-Lautsprechern ", aber ich weiß nicht wie.

Zuerst raten Sie: Denken Sie an die Welle, die Sie idealerweise erzeugen würden, und stellen Sie sich dann vor, Sie würden sie in eine stark abgeschnittene quadratische Form verzerren. Erstellen Sie dann diese quadratische Form, indem Sie Ihre Ausgabe zu den entsprechenden Zeitpunkten umschalten. Hätte allerdings viel Intermodulation .

Zweiter Gedanke: Können Sie die Frequenz der Oszillation stark erhöhen und analoge Signale PWM-artig ausgeben ?

Endolith
quelle
2
Ich erinnere mich, dass ich mir vor langer Zeit einen NES-Emulator angesehen habe und ich glaube, dass sie drei Wellenformen mit jeweils einer programmierbaren Frequenz verwendet haben. Zwei Rechteckwellen und eine Dreieckwelle.
mjh2007
... und anscheinend eine Geräuschquelle. en.wikipedia.org/wiki/NES_Sound_Format
Endolith
1

Wie bereits erwähnt, können Sie dies auf die gleiche Weise tun, wie Sie es bisher mit einem PC-Lautsprecher getan haben (der nur das Ein- und Ausschalten unterstützt, das optional an einen PWM-Controller angeschlossen ist). Grundsätzlich besteht mein Verständnis der Methode darin, dass Sie den Lautsprecher ein- und ausschalten schnell genug, dass es nie ganz ein- oder ausgeschaltet wird (ein bisschen wie bei einem Schaltnetzteil). Dadurch bewegt sich der Lautsprecher ständig zwischen Ein und Aus und erzeugt ein analoges Signal.

Der einzige Nachteil ist, dass Sie einen richtigen Lautsprecher benötigen (ich denke, ein Piezo bewegt sich so schnell, dass er zu schnell voll auf und voll aus geht) und dass Sie in der Lage sein müssen, das Bit schnell genug umzuschalten. Ich habe einige Experimente durchgeführt und eine maximale Geschwindigkeit von ca. 5 MHz gefunden, die für ein Audiosignal von 11.025 Hz ausreichen sollte (wahrscheinlich die beste Qualität, die Sie sich erhoffen können).

Natürlich ist 11025Hz @ 8-Bit 11 Kilobyte / Sekunde, was viel schneller ist als die Geschwindigkeit eines seriellen Ports. Das Speichern von Audiodaten im Flash-Speicher ist möglicherweise nur für ein oder zwei Sekunden zulässig. Sie können also nur Audiodaten abspielen, die im laufenden Betrieb generiert werden, vorausgesetzt, Sie haben genügend freie CPU-Zeit, um den Lautsprecher zu drehen.

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

Bösartig
quelle
2
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.
Endolith
1

Spielen Sie Ton A für einen Moment, wie vielleicht 50 ms, dann Ton B und schalten Sie hin und her. Die Idee ist, schneller umzuschalten, als das Ohr erkennen kann, und es klingt, als würden beide gleichzeitig spielen.

Matt Williamson
quelle
1

Ich glaube, es gibt eine Tonbibliothek für den Arduino, die zwei Töne ausführt. Sie sollten in der Lage sein, den Code an den von Ihnen verwendeten AVR-Chip anzupassen. Auf arduino.cc gibt es auch einige exzellente Threads zur Erzeugung von Wellenformen

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 sind nur ungefähr 2 US-Dollar oder so.

jluciani
quelle
0

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

avra
quelle
4
Ich würde dir eine Gegenstimme geben, wenn du den Code hier posten würdest oder an einen Ort, an dem ich keinen Account benötige ...
Toby Jaffey