Der Button wird ja _nur_ ausserhalb der Lauflicht-Prozedur abgefragt.
Das machen wir da ziemlich hemdsärmelig...
Man _kann_ das durchaus auch anders coden, so dass _sofort_ auf nen Tastendruck reagiert wird, aber das ist etwas fortgeschrittener.
Dann nutzt man einen Interrupt (der immer auch mitten in ner laufenden Routine ausgelöst werden kann).
Vielleicht kommt da in deinem Buch noch was zu- da müssen einige Sachen beachtet werden, damits einwandfrei funktioniert.
Es gibt da mehrere Möglichkeiten:
-der Tastendruck wird _auch_ während der Lauflicht-Sequenz erkannt, aber die Reaktion erfolgt auch erst, wenn ein Durchlauf beendet ist
-die Sequenz wird _sofort_ abgebrochen.
Durch den Interrupt muss der Button nicht zyklisch abgefragt werden.
Aber Interrupts haben auch kleinere Tücken- und das geht nicht an jedem Pin.

int buttonStateOld = 0;

Die "Null" bedeutet hier ja keine PIN-Zuweisung am Arduino. Hier können (ganzzahlige) Werte zwischen -32000 bis +32000 (gerundet, 16 bit???) stehen. So zumindest wird es in der Arduino-Sprache beschrieben. Allerdings verarbeitet der doch nur 8 bit, was genaugenommen nur 256 (0-255) Werten entspricht. Warum hier 16 bit-Werte möglich sind, ist mir auch (noch) unklar, ist aber ein anderes Thema.

Es gibt nicht ohne Grund verschiedeneVariablentypen.Im Grunde legst du mit int() fest, wieviel Speicherplatz für die Variable reserviert werden soll.
Eine Int belegt dann einfach mehrere Speicheradressen, da sie, wie du richtig erkannt hast, eben bei 8bit _nicht_ in eine Speicherstelle passt.
Deshalb kann es irgendwann wichtig werden, das _richtig_ zu machen, denn grundsätzlich kann man auch alles als float() deklarieren (und dann nur für ein Bit, also ne bool nutzen, das geht), verschwendet dabei aber ne Menge Speicher, den man womöglich noch braucht.
Umgedreht funktioniert es natürlich nicht: in eine bool() passt keine float() oder int().
Ob Arduino tatsächlich mit Bool() arbeiten kann (also da tatsächlich nur ein Bit reserviert) , bin ich mir grade nicht ganz sicher, aber zumindest Byte() bringt richtig was, wenn man Speicher sparen will.
Ein Byte passt in eine Speicherzelle, während Integer schon zwei belegen.
Byte kann man z.B. prima für die ganzen Pindefinitionen benutzen, das reicht vom Wertebereich her sogar aufm Mega mit seinen >50 Pins mühelos.
Richtige Speicherfresser sind Strings: pro Zeichen ein Byte.
Wenn man viele Textausgaben hat, kann es lohnen, die mehrmals zu benutzen.

Naja, allein zur Speicheroptimierung könnte man noch ne ganze Menge mehr erzählen..bei den kleinen Rechenknechten wird das schnell mal wichtig.

Ach ja: dein Programm müsste auch dann stoppen, wenn du den Button länger gedrückt hälst- eben so lange, _bis_ er mal abgefragt wird.

Ich will da aber deinem Buch jetzt nicht vorgreifen...da kommt sicher noch was, denn gerade bei Midi ist Schnelligkeit alles.
Erfahrungsgemäss muss man unbedingt unter 5ms bleiben zwischen "Ereignis ausgelöst" und "Dinge werden getan", sonst hört man die Verzögerung deutlich.
Das heisst, man braucht wirklich schnellen Code, da in der Latenz auch Dinge enthalten sind wie "noteOff", Kanal-oder Bankwechsel und anderes.