8 KB ought to be enough for anybody

Klampfinator Elektronik Bild: Klampfinator Elektronik - Bestimmte Rechte vorbehalten: Andy Drop (cc-by 3.0)

Als ich mit dem Programmieren anfing, war Arbeitsspeicher noch etwas, um das man sich kümmern musste. Mein erster Computer war ein Commodore VC-20, der hatte 5 KB RAM, wobei einem nur ca 3,5 KB zum Arbeiten zur Verfügung standen. Der Arduino MEGA2560, den ich im Klampfinator einsetze hat dagegen 256 KB. Das sollte also eine Weile reichen. Dachte ich zumindest bis gestern.

Der Soundcube

Da das Wetter angenehm war, und der Rest der Familie Termine hatte, war am Sonntag DIE Gelegenheit, einen größeren Umbau an der Software des Klampfinators vorzunehmen, den ich schon länger machen wollte. Bisher war es nämlich so, das ich hauptsächlich im Strumming – Modus gespielt habe. Da werden einfach alle Saiten angeschlagen (bis auf die gesperrten Saiten), und dann klingt das halt wie ein Akkord. Das Fingerpicking hatte ich bisher ebenso gelöst, es wurden immer gleiche Zupfmuster gespielt, während die linke Hand den Akkord vorgab. Leider ist das in der Realität beim Picking eben nicht so. Je nach gegriffenem Akkord, werden die gezupften Saiten ein wenig angepasst. Hauptsächlich im Bass-Bereich.

Dieses Verhalten muss der Klampfinator unbedingt auch beherrschen, denn einen falschen Ton hört man beim Picking sehr deutlich.

Zu diesem Zweck habe ich ein Konstrukt namens Soundcube eingeführt. Dabei handelt es sich aus Programmierersicht um ein 3-dimensionales Array aus Integern. So ein Array stellt man sich am besten als Würfel vor.

  • Die X-Achse stellt die 16 möglichen Akkorde dar, die mir an der linken Hand zur Verfügung stehen
  • Die Y-Achse stellt die 16 Picking-Patterns dar, die mit der rechten Hand wählbar sind
  • Die Z-Achse ist einer von 16 Momenten innerhalb eines Taktes

In jedem dieser Feldelemente liegt dann ein Integer, der angibt welche Saiten in diesem Moment wie laut gespielt werden müssen, und ob sie evtl. gleich wieder gemutet werden, etc.
Dieser Würfel wird zum Liedbeginn einmal gefüllt, und danach nur noch gelesen. Der Speicherbedarf ergibt sich wie folgt:

Mem = 16 * 16 * 16 * 2 Byte (Integer) = 8192 Byte = 8 KB

Soweit, so gut. Kaum hatte ich die Programmierung realisiert und in den Arduino geladen, lief auch schon nichts mehr.

Ominöse Abstürze

Um den Fehler zu finden habe ich diverse Meldungen vom Programm zur Laufzeit ausgeben lassen. Das ist ja recht einfach möglich, weil der Klampfinator über ein kleines Text-Display verfügt.
Das klappte auch ganz gut, solange ich den Soundcube im Code nicht initialisiert hatte. Sobald ich das aber tat, stürzte das Programm unkontrolliert ab. Manchmal hatte ich dann statt der erwarteten Variablenwerte im Display solche Ausgaben wie “wwwwwwww”. Das war schon mal ein guter Hinweis darauf, das ich statt meiner erwarteten Anzeige hier offensichtlich ein beliebiges Stückchen Speicher sah.

Daraufhin habe ich mich mal kurz mit dem Speicher des Arduino beschäftigt, und siehe da schon auf den ersten Blick war klar, was hier gerade passiert. Der Arduino hat nämlich 3 verschiedene Speicher

1. Flash-Speicher (256 KB): Hier wird der Programmcode abgelegt
2. SRAM (8KB): Der Arbeitsspeicher des Arduino, in dem zur Laufzeit alle Variablen und der Stack abgelegt werden
3. EEPROM (4KB): Ein Speicherbereich, den das Programm lesen und schreiben kann, und der auch nach dem abschalten des Arduino erhalten bleibt

Interessant ist für mich also der Bereich des SRAM, und den brauche ich schon alleine mit dem Soundcube komplett auf. Da bleibt weder Platz für auch nur eine einzige andere Variable, noch (und das ist für die Abstürze verantwortlich) für den Stack. Schöne Kacke!

Wege aus der Speicherkrise

Nun ist also Diät angesagt. Die Gesamtsumme aller Variablen des Programms darf zusammen 8192 Bytes nie überschreiten (eigentlich sogar noch weniger, da ja immer noch ein wenig Platz für den Stack bleiben muss, der ein paar Rücksprung-Adressen, und lokale Variablen enthält). In einem ersten Schritt bin ich zunächst mal meine Variablendeklarationen durchgegangen. Ich hatte zum Beispiel für jede mögliche Tonart einen Zähler hinterlegt, der eigentlich nur der Lesbarkeit des Codes für einen Menschen diente. Lesbarkeit kann man aber auch genauso gut in den Kommentaren erzeugen. Zack! 400 Bytes gespart. auf diese Weise ließen sich insgesamt gut 3 KB an Speicherbedarf einsparen.

Dann hat das Programm noch eine große Sammlung von eigentlich statischen Daten im Bauch. Das sind vor allem die möglichen Strumming- und Picking-Muster, sowie alle möglichen Akkorde, die sich greifen lassen. Da diese Werte sich nie ändern, sind diese ein heißer Kandidat für das EEPROM. Ich werde die Werte in der nächsten Version aus dem Programm herausnehmen, und davon ausgehen, das sie im EEPROM gelesen werden können. Dann muss ich nur noch ein kleines Programm schreiben, das die Daten einmalig dorthin befördert. Auf diese Art sollten sich noch einmal 3 KB einsparen lassen. Eventuell passt sogar noch die Menüstruktur ins EEPROM.

Als dritter Schritt wurde auch der Soundcube kleiner. An der Anzahl der Takte kann ich nicht schrauben, aber die Anzahl der Akkorde und der Picking-Muster lässt sich verringern. Für die Akkorde müssen es mindestens 8 sein, die Picking-Muster kommen theoretisch mit 2 aus, aber jedes Muster mehr macht das Spiel lebendiger. Wenn es sein muss kann ich also auf 8*2*16*2 Bytes herunter gehen, was einem halben KB entspricht. Ich werde es allerdings zunächst einmal mit 12 Akkorden und 12 Mustern versuchen, und komme damit auf einen Soundcube von 4,5 KB. Die restlichen 3,5 KB sollten für den Stack und die anderen Variablen reichen.

Insgesamt war die Fehlersuche ein amüsanter Ausflug zurück in die Anfänge des Programmierens, als Speicher noch wertvoll war. Ich bin gespannt welche der Tricks aus diesen alten Tagen ich bei der konkreten Lösung noch auspacken darf. Ich könnte z.B. einige der Daten auch binär noch etwas komprimieren.