Wir kommen heute zum Abschluss der Sound-Projektreihe und gleichzeitig ist dies auch das letzte Bastelprojekt des Kurses. Du bist also schon weit gekommen und fast schon ein Experte für den Arduino!

Schauen wir kurz zurück auf die letzen Lektionen. Da haben wir Schritt für Schritt die Sound-Fähigkeiten des Arduino entdeckt und ausgebaut. Begonnen hatte es mit einem Sketch, der eine simple kleine Melodie über den eingebauten Befehl tone  spielt. Im nächsten Teil gab es dann eine Analogschaltung, einen kleinen Verstärker, der ein schwaches Audiosignal aufnehmen und hörbar auf einem Lautsprecher wiedergeben kann.

Danach haben wir die Klangerzeugung im Inneren des Arduino selbst übernommen und unseren Sound per Digital-Analog Wandler hörbar gemacht.

Module für den Baukasten

Auch diesmal werden wir wieder Module aus den letzten Projekten brauchen. Elektronik funktioniert oft ja nach einem Bausteinprinzip. Eine einmal irgendwo entwickelte Baugruppe kann oft an einer ganz anderern Stelle wieder verwendet werden und dort nützlich sein.

Jedes kleine Modul erledigt eine bestimmte Teilaufgabe und hat einen Eingang und einen Ausgang. Nun muss man nur die Module über Jumperkabel zusammenstecken, also den Ausgang des einen mit dem Eingang des anderen verbinden. So kannst du die Module wie in einem Baukasten immer neu zusammenschalten und musst nicht jedesmal das Rad neu erfinden.

Für Audio-Projekte haben wir zwei sehr wichtige Module jetzt in der Trickkiste:

  • Das Audio-Verstärkermodul
  • Das Digital-Analog-Wandlermodul

Du erinnerst dich an die Widerstandsbrückenschaltung, die mit PORTD  eine Reihe von 8-bit Zahlen in analoge Spannungswerte verwandelt hat. Wir haben sie benutzt, um drei verschiedene Wellenformen hörbar zu machen, nämlich Sinus-, Dreieck- und Rechteckwellen.

Damit haben wir aber nur im Ansatz angekratzt, wozu solch ein DA-Wandler fähig ist. Denn das spannende ist ja gerade, dass dieser Baustein nicht nur periodische Wellenformen in Analogsignale umwandeln und wiedergeben kann, sondern jede beliebige Zahlenfolge und damit auch jede beliebige unregelmäßige Wellenform.

Wir können also auch einen Sampler bauen und kurze, gesampelte Audio-Schnipsel abspielen.

Eine Drum Machine oder auch Drumcomputer genannt macht nichts anderes. In diesen Geräten sind die Einzelgeräusche aller Sounds eines Schlagzeugs als digitalisierte Audiodaten gespeichert. Als Nutzer kannst du aus diesen Drumsounds einzelne Takte zusammenstellen, die über einen Timer dann von der Maschine abgespielt werden.

So wird der Arduino zur Drum Machine

Der Aufbau ähnelt dem aus dem Synthesizer Projekt. Die Neuerungen werden also vor allem im Sketch liegen. Das ist wieder ein Beispiel dafür, wie vielseitig die Hardware nutzbar ist, allein durch Änderungen auf der Softwareseite.

led

Als kleine Neuerung schließen wir 4 LEDs an, die nachher anzeigen, welcher Sample gerade abgespielt wird. Das sieht nicht nur gut aus, sondern wird bei den großen Drum Machines genauso gemacht.

Damit hat diese Schaltung jetzt 12 von 14 digitalen Pins belegt (8 für den D/A-Wandler und nochmal 4 für die LEDs). Wir sind also durchaus schon soweit, den Arduino bis an die Grenzen auszureizen.

Außerdem benutzen wir wieder den analogen Pin 1 um über ein Poti das Tempo einzustellen, mit dem die Drum Loops abgespielt werden.

Hier siehst du den kompletten Aufbau:

drumMachine_Steckplatine

Die Stückliste siht so aus:

  • 4 x Widerstand 220 Ohm
  • 4 x LED (rot, grün oder gelb)
  • 1 x Potentiometer 10 kOhm
  • 1 x Lautsprecher (8 Ohm)
  • Verstärkermodul (siehe Lektion 11)
  • D/A-Wandlermodul (siehe Lektion 12)

Die digitalen Pins 0 bis 7 speisen den D/A-Wandler und dessen Ausgang geht an den Verstärker. An den Pins 8 bis 11 hängen die Kontrolldioden, jeweils über einen 220 Ohm Vorwiderstand.

Wie gesagt, der Aufbau ähnelt stark dem Synthesizer aus Lektion 12, schau auch dort nochmal nach, wenn du näheres über die Funktion der Hardware erfahren möchtest.

aufbau

Der große Unterschied kommt jetzt mit dem Sketch.

Retro-Drumsound mit 8 bit

Zunächst brauchen wir natürlich Samples mit Schlagzeugsounds.  Profi-Geräte haben hochwertige Samples, die von einem echten Schlagzeug abgenommen wurden und davon nicht zu unterscheiden sind. Das kriegen wir mit dem Arduino natürlich so nicht hin, dafür ist der Speicher zu klein. Aber der Sketch produziert einen netten 8 bit-Drumsound, der ein bißchen retro ist und an alte Computerspiele erinnert.

Außerdem kann man daran wunderbar das Prinzip lernen, denn die großen Sampler und Drum Machines funktionieren im Prinzip auch nicht anders.

Die Hauptschwierigkeit besteht darin, die Audio-Samples für den Arduino herunterzubrechen. Denn normale Aufnahmen für Samples haben 16 bit oder gar 24 bit Auflösung bei Studioqualität und werden mit einer Samplerate von 44 kHz abgetastet. Das bedeutet, dass pro Sekunde ca. 45000 Werte gelesen werden (44*1024).

Das ist für den Arduino viel zu groß. Statt 16 bit haben wir nur 8 bit Auflösung zur Verfügung und auch bei  Sampleraten größer 8 kHz kommt der Arduino Controller mit dem Timing an die Grenze.

Wir müssen also ein gegebenes Audiosample erst für den Arduino umrechnen. Dafür gibt es  einige Tools, aber der Vorgang ist nicht ganz so einfach und würde hier den Rahmen sprengen.

Zum Glück gibt es Maker, die das schon einmal erledigt haben. Daher greife ich hier auf die Samples aus einem ähnlichen  Projekt von Sebastian Tomczak zurück.

Wir lesen die Samples aus den Datenarrays Wert für Wert aus und schicken das ganze via PORTD an den Wandler. Das Zeitdelay zwischen zwei Samplewerten holen wir vom analogen Inputport 0 ab, denn daran hängt das Poti.

Die drei Samplesounds heißen kick, snare, hat und crash, was jedem Schlagzeuger sicher bekannt vorkommt. Die Funktion playBeat(byte s) bekommt eine Samplenummer von 0 bis 4 übergeben und spielt die entsprechende Sampledatei ab.

Das wird über eine switch-case Konstruktion gelöst. Der Samplecode 0 steht für Pause, die Werte 1 bis 4 spielen die jeweiligen Audiodateien ab. Außerdem sorgt die Funktion dafür, dass die zugehörige LED während des Abspielens leuchtet.

Die Abspielfunktionen playKick(), playSnare(), playHat(), playCrash()

übernehmen jetzt das Abspielen der Soundsamples und berücksichtigen das mit dem Poti eingestellte Delay.

Soweit alles klar. Jetzt müssen wir nur noch der Drum Machine mitteilen, was sie spielen soll. Diese Abspiellisten werden Patterns genannt und sie sind in einzelne Takte unterteilt.

Jeder Takt ist wiederum in Beats unterteilt und im Pattern steht die Information, welcher Drumsound zum Zeitpunkt des Beats abgespielt werden soll. Du meinst, das klingt nach einem typischen Anwendungsfall für ein Array? Genauso ist es. 🙂

Allerdings versteckt sich hier eine kleine, aber feine Neuerung: Wir gestalten das Drumpattern als zweidimensionales Array!

Arrays gibt’s auch in 2D oder 3D

Bisher haben wir ein Array als Liste von Variablen verwendet, die über einen Index ansprechbar ist. Etwa, wenn bei playBeat die Soundsamples in einer for-Schleife Wert für Wert ausgelesen werden.

In manchen Fällen ist es aber sinnvoll, Daten nach mehr als einem Index anzuordnen. Nimm zum Beispiel eine Tabelle. Dort kann man nach dem Wert in der dritten Zeile und der fünften Spalte fragen. Damit brauchen wir zwei Parameter, um einen Wert in der Tabelle eindeutig festzulegen, sie ist also zweidimensional. Um einen Ort eines Flugzeuges eindeutig anzugeben sind sogar im drei Werte nötig: Längengrad, Breitengrad und die Höhe über dem Meeresspiegel.

Glücklicherweise bietet der Compiler uns die Möglichkeit, Arrays nach mehreren Indizes aufzubauen. Diese heißen dann mehrdimensionale Arrays. Jede Grafikdatei ist zum Beispiel als zweidimensionales Array angelegt, um einen Pixel anzusprechen musst du die x- und die y-Position übergeben.

Eine Besonderheit der mehrdimensionalen Array ist, dass du die Dimensionen bei der Definition explizit angeben musst, damit der Compiler entsprechend Speicherplatz reservieren kann.

Das kleine Beispiel zeigt mit einer ganz typischen Anwendung wie es geht. Wir definieren eine Tabelle mit 3 Zeilen und 4 Spalten und schreiben irgendwelche Werte hinein.

Um die Elemente anzusprechen werden die Indizes einfach hintereinander in eckigen Klammern angegeben:

Nun willst du die Tabelle übersichtlich ausdrucken, jede Zeile soll auch auf dem Display eine Zeile einnehmen. Nehmen wir an, wir haben eine Funktion print, die Daten auf einem Display ausgibt.

Dazu verwenden wir dann zwei geschachtelte for-Schleifen. Nachdem die innere for-Schleife die Inhalte einer Zeile ausgedruckt hat, wird durch print("\n") ein Zeilenvorschub erzwungen, bevor die nächste Zeile gedruckt wird:

 

Genau so organisieren wir auch die Patternliste für die Drum Machine. Dies geschieht bei der Definition der Variablen bars (engl. für Takte).

Ein Takt ist ein Array von 16 int-Werten und die komplette Playlist ist eine Liste von Bars, also eine Liste aus Listen und das ist nichts anderes, als ein zweidimensionales Array.

Diese Strukturierung hat den Vorteil, dass du jeden Takt auch einzeln ansprechen und abspielen kannst. Du könntest eine Funktion playBar(int barNr) entwickeln, die gezielt einen der Takte aus der Liste abspielt. Das wäre nicht möglich, wenn das Drumpattern nur als eindimensionales Array gespeichert wäre.

Gehen wir den Sketch mal gemeinsam durch:

  • In den Zeilen 7 bund 8 werden die Grenzen des bars-Array festgelegt. Das Schlüsselwort int const steht für einen konstanten Integerwert. Er kann zur Laufzeit des Sketches nicht mehr verändert werden
  • In Zeile 17 beginnt die Definition des Drum-Patterns als zweidimensionales Array. Du kannst beliebig viele Bars hinzufügen, dann musst du nur danch die Konstante barNr in Zeile 6 anpassen. Die Zahlen in der Liste geben die zu spielenden Samples an, o: Stille, 1: Kick, 2: Snare, 3: Hat, 4: Crash
  • In den Zeilen 30 bis 38 werden die Audiodaten für die Soundsamples definiert.
  • Die Funktion setup() in Zeile 30 setzt alle nötigen digitalen Pins auf Output. Im loop -Block haben wir die aus dem Tabellen-Beispiel schon bekannten geschachtelten for-Schleiden, die dafür sorgen, dass die Takte nacheinander abgespielt werden.
  • In den Zeilen 56 bis 91 steht die zentrale Play-Funktion, die über eine Case-Abfrage das Abspielen der Einzelsamples regelt.
  • Schließlich findest du ab Zeile 93 die Funktionen, welche die einzelnen Samples abspielen. Sie arbeiten analog zum Code aus dem Synthesizer-Projekt.

 

So, damit sind wir am Ende der heutigen Lektion und fast auch schon am Ende des online Kurses. Nächstes Mal gibt es einen kleinen Ausblick, der dich neugierig machen soll auf deine Zukunft als Maker und welche Möglichkeiten dir nun offen stehen, nachdem du die ersten Schritte erfolgreich bewältigt hast.

Bis dahin: Viel Spaß beim Rhytmen-Tüfteln! 🙂

Arduino Drum Machine

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.