5. Musik machen mit Assembler
Bis jetzt können Sie Musik nur mit BASIC und vielen, vielen POKE-Befehlen
programmieren. Ihre Noten müssen Sie aus separaten DATA-Zeilen auslesen.
Mit dem „Saver“ können Sie Ihre Maschinenroutinen und
Noten in einer schnellen Weise nachladen, allerdings zerfällt dann Ihre
Anwendung oder Ihr Spiel in viele kleine Dateien, und kann unter Umständen nur
als ganze Diskette weitergegeben werden. Irgendwann wird die Sache trotzdem zu
langsam, besonders, wenn Sie ein Musikstück mit mehreren Stimmen flüssig
abspielen wollen - hier stößt BASIC an die Grenzen des Machbaren. In diesem
Kapitel geht es vor Allem um Interrupt-Programmierung, und zwar in der Weise,
dass Ihr Songplayer an den Timer-Interrupt
gebunden wird. Dies ist nicht sehr schwierig, Sie müssen aber trotzdem die
richtigen Programmiertechniken beherrschen.
5.1 Der Timer-Interrupt
Wenn das Kernal korrekt läuft, dann gibt die Uhr
den Takt an. Diese wird 60-mal in der Sekunde aktualisiert, und zwar über
Unterprogramme, die der Kernal auch periodisch
aufruft. Bei den Kernal-Funktionen in Kapitel 4 haben
Sie eine dieser Funktionen schon kennengelernt, nämlich UDTIM (update timer). Es geht hier aber nicht um UDTIM, sondern
darum, dass normalerweise 60-mal in der Sekunde ein Interrupt ausgelöst wird,
der den C64 dazu veranlasst, periodisch einige Arbeiten auszuführen. Z.B. wird
dann die Tastatur abgefragt, und eventuell wird dann auch der Tastaturpuffer
mit einem neuen Zeichen beschrieben. Außerdem wird die serielle Schnittstelle
oder der Commodore-Bus abgefragt, und wenn dies nötig
ist, werden auch die zugehörigen Dateien aktualisiert. Aber auch dies ist an
dieser Stelle nicht so wichtig, denn die meisten Dinge, die sich mit der Ein-
und Ausgabe von Daten befassen, laufen automatisch ab. Wichtig ist jedoch, dass
die Arbeiten, die normalerweise bei einem Timer-Interrupt
ausgelöst werden, dem folgenden Befehl entsprechen:
JMP $EA31
Aber wer oder was führt diesen JMP-Befehl aus? Die Antwort ist, dass
auch auf die Adresse $EA31 ein Zeiger verweist, und dieser steht als Lo- und
Hi-Byte in den Adressen 788 und 789. $EA31 entspricht nun den Dezimalzahlen 49
und 234, und genau diese Zahlen stehen auch direkt nach dem Einschalten in den
Adressen 788 und 789. Denken Sie an dieser Stelle bitte daran, dass indirekte
Sprünge erlaubt sind, und dass deshalb das Kommando
JMP (788)
erlaubt ist. Aber was haben Sie nun davon? Ganz einfach: Wenn Sie den
Zeiger in den Adressen 788 und 789 „umbiegen“, dann können Sie dem C64 eigene
Interrupt-Routinen unterschieben, die dann auch alle 1/60 Sekunde ausgeführt
werden. Das Einzige, das Sie an dieser Stelle beachten müssen, sind die
folgenden Dinge:
· Ihre eigene Routine darf zusammen mit den danach noch anstehenden
Aufräumarbeiten nicht länger als 1/60 Sekunde dauern
· Ihre eigene Interrupt-Routine darf nicht mit RTS abgeschlossen
werden, sondern muss mit JMP $EA31 enden
Ein sehr einfaches Programm, das nur das erste Zeichen auf dem Bildschirm
alle 1/60 Sekunde um 1 erhöht, wäre das folgende:
10 - .BA 49152
20 - INC 1024
30 - JMP $EA31
Aber wie erreichen Sie nun, dass Ihre Interrupt-Routine auch periodisch
ausgeführt wird? Vielleicht mit folgendem Kommando?
POKE 788,0:POKE 789,192
Leider geht dies schief, denn BASIC ist viel zu langsam, um den Zeiger in
den Adressen 788 und 789 zu ändern, noch bevor ein neuer Timer-Interrupt ausgelöst wird. Die letzte BASIC-Zeile
führt also zum Absturz des Systems. Sie können natürlich Ihre Routine so
umschreiben, dass Sie nur das Hi-Byte verändern müssen:
20 - INC 1024
30 - JMP $EA31
RUN
POKE 789,192
Wenn sich nun Ihr Zeichen an der Adresse 1024 automatisch dauernd ändert,
dann haben Sie alles richtig gemacht. Leider können Sie Ihre Interrupt-Routine
nicht mit SYS 49152 starten bzw. stoppen, sondern müssen stattdessen die
folgenden Zeilen benutzen:
POKE 789,192:REM ROUTINE AN
POKE 789,234:REM ROUTINE AUS
Es geht aber auch einfacher, nämlich mit schnellem Assembler, der die
Inhalte der Adressen 788 und 789 so ändert, dass Ihr Timer
dies nicht mitbekommt. Dies geht auf die folgende Weise:
20 - SEI
30 - LDA #<(IRQROUTINE)
40 - STA 788
50 - LDA #>(IRQROUTINE)
60 - STA 789
70 - CLI
80 - RTS
90
-IRQROUTINE INC 1024
100 - JMP $EA31
Der Trick ist hier (zumindest bei Hypra-Ass), dass
das Lo-Byte bzw. Hi-Byte eines Labels ermittelt werden kann, und dann auch in
der korrekten Weise als Immidiate an den Akkumulator
weitergereicht werden kann. Und nicht nur das: Mit SEI können Sie
während der Veränderungen des Zeigers in den Adressen 788 und 789 zusätzlich
verhindern, dass Ihnen die Uhr dabei mit einem neuen Interrupt dazwischenfunkt.
Sie dürfen nur nicht vergessen, die Interrupts mit CLI wieder
zuzulassen, bevor Sie mit RTS zu BASIC zurückkehren.
5.1.1 Songs im Hintergrund abspielen
Nun geht es ja um Musik, und nicht um Interrupt-Programmierung. Die
Interrupt-Programmierung, die ja im vorigen Kapitel schon für die
Sprite-Steuerung benutzt wurde, ist hier deshalb wieder nur ein Werkzeug, um
Songs im Hintergrund stimmensynchron abzuspielen, während z.B. BASIC
weiterläuft. Aber wie genau können Sie Ihre Noten der einzelnen Stimmen
verwalten? Ganz einfach: Sie benutzen für jede der 3 SID-Stimmen ein
2-Byte-Tupel. Das erste Byte enthält einfach einen Zähler, der bei jedem Aufruf
Ihrer Interrupt-Routine um 1 erhöht wird (mit INC), das zweite Byte
enthält dann einen Wert, der angibt, wie groß Ihr Zähler maximal sein darf.
Wenn dann Ihr Zähler über diesem Wert liegt, dann wird Ihr Zähler auf 0
zurückgesetzt, und die nächste Note für Ihre Stimme wird aus einem Array
eingelesen, das die Notenwerte und die dazugehörigen Längen enthält. Nun
benötigen wir noch einige Zeiger, um mit dem Songplayer auch korrekt arbeiten
zu können. Hierfür belege ich nun die folgenden Speicheradressen in der
folgenden Weise:
· 900/901: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 0
· 902: Aktueller Zeitzähler für SID-Stimme 0
· 903: Länge der aktuellen Note für SID-Stimme 0
· 904/905: Frequenzwerte für die SID-Register 0 und 1 für Stimme 0
· 906/907: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 0 (für
Repeat-Funktion)
· 908: Wellenform für Stimme 0
· 909: Frei für Erweiterungen oder zusätzliche Flags für Stimme 0
·
910: Attack/Decay für Stimme 0
·
911: Sustain/Release für Stimme 0
· 912/913: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 1
· 914: Aktueller Zeitzähler für SID-Stimme 1
· 915: Länge der aktuellen Note für SID-Stimme 1 in 1/60 Sekunden
· 916/917: Frequenzwerte für die SID-Register 7 und 8 für Stimme 1
· 918/919: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 1 (für
Repeat-Funktion)
· 920: Wellenform für Stimme 1
· 921: Frei für Erweiterungen oder zusätzliche Flags für Stimme 1
·
922: Attack/Decay für Stimme 1
·
923: Sustain/Release für Stimme 1
· 924/925: Zeiger auf die aktuelle Note des Noten-Arrays von SID-Stimme 2
· 926: Zeitzähler für SID-Stimme 2
· 927: Länge der aktuellen Note für SID-Stimme 2
· 928/929: Frequenzwerte für die SID-Register 14 und 15 für Stimme 2
· 930/931: Zeiger auf den Anfang des Noten-Arrays von SID-Stimme 2 (für
Repeat-Funktion)
· 932: Wellenform für Stimme 2
· 933: Frei für Erweiterungen oder zusätzliche Flags für Stimme 2
·
934: Attack/Decay für Stimme 2
·
935: Sustain/Release für Stimme 2
Ich habe hier bewusst dafür gesorgt, dass die Größe der Datenstruktur für
die einzelnen Stimmen ein Vielfaches von 2 ist, denn in diesem Fall können an
vielen Stellen für die Zeigeroperationen einfache ASL-Befehle anstatt
eines aufwendigen Multiplikationsverfahrens benutzt werden. Außerdem enthalten die 12 Bytes großen
Datenpakete mehr Bytes, als nötig ist, deshalb ist hier noch genug Platz für
spätere Erweiterungen. Ferner ist die Verarbeitung der eigentlichen Daten für
jede Stimme identisch, das heißt, dass die Hauptschleife, die Stimme 0
verarbeitet einfach nur dreimal hintereinander ausgeführt werden muss. Setzen
wir nun den Songplayer Stück für Stück zusammen. Der Ausgangspunkt ist die
folgende Grundstruktur:
10
.EQ
SI=54272
20 .BA 49152
30 SEI
40 LDA 999
50 CMP #0
60 BEQ INIT
70 CMP #1
80 BEQ DEINIT
90 RTS
10 INIT LDA #<(PLAYER)
110 STA 788
120 LDA #>(PLAYER)
130 STA 789
140 LDA #1
150 STA 999
160 LDA 910
170 STA SI+5
180 LDA 911
190 STA SI+6
200 LDA 922
210 STA SI+12
220 LDA 923
230 STA SI+13
240 LDA 934
250 STA SI+19
260 LDA 935
270 STA SI+20
280 CLI
290 RTS
300
DEINIT LDA #49
310 STA 788
320 LDA #234
330 STA 789
340 LDA #0
350 STA 999
360 CLI
370 RTS
Wenn Sie den C64 einschalten, dann steht normalerweise in den Adressen
900-1023 der Wert 0, also auch in der Adresse 999. Dies ist dann das Zeichen
dafür, dass der Songplayer noch nicht initialisiert ist. Deshalb liest das
Maschinenprogramm, das Sie mit SYS 49152 aufrufen, zunächst die Adresse
999 aus. Wenn dort der Wert 0 steht, dann wird das Unterprogramm INIT
aufgerufen, das den Timer-Interrupt
auf den Songplayer umbiegt, und an die Adresse 999 den Wert 1 schreibt.
Zusätzlich werden noch die Hüllkurven für die einzelnen Stimmen initialisiert,
indem die entsprechenden Daten aus der entsprechenden Datenstruktur ab der
Adresse 900 ausgelesen werden.
Wenn allerdings an Adresse 999 schon eine 1 seht, dann wird das
Unterprogramm DEINIT aufgerufen, das den alten Timer-Interrupt und den alten Wert in Adresse 999 wiederherstellt.
Die Zeiger auf die Notenarrays werden dadurch aber nicht geändert, deshalb
können Sie den Songplayer auch beliebig starten, anhalten und (bei
Neubeschreiben der Zeiger auf die Notenarrays) auch vor und zurückspulen.
Der Songplayer
Kommen wir nun zum Songplayer selbst. Dieser ist wie gesagt sehr einfach
gestrickt, das heißt, dass hier vorausgesetzt wird, dass sich schon die
richtigen Noten im Speicher befinden, und Sie die Lautstärke schon richtig mit
SID-Register Nr. 24 eingestellt haben. Auch die Tabelle mit den Frequenzwerten
der einzelnen Noten muss sich bereits im Speicher befinden, und zwar an der
Adresse 50000. Die Noten werden vom Player also nur „angestoßen“, wie dies etwa
bei einem Klavier der Fall ist. Natürlich können Sie auf diese Weise nicht alle
Möglichkeiten des SID ausreizen, dafür ist der Player aber sehr schnell
programmiert. Nun sollten Sie sich die einfache Variante in Ruhe anschauen, und
den Songplayer mit einem Spiel vergleichen, das Sie gerade erst mit dem 1. Level
begonnen haben. Schauen Sie sich nun das komplette Listing für den einfachen
Songplayer an.
14-SONGPLAYER
10
.EQ
SI=54272
20 .BA 49152
30 SEI
40 LDA 999
50 CMP #0
60 BEQ INIT
70 CMP #1
80 BEQ DEINIT
90 RTS
10 INIT LDA #<(PLAYER)
110 STA 788
120 LDA #>(PLAYER)
130 STA 789
140 LDA #1
150 STA 999
160 LDA 910
170 STA SI+5
180 LDA 911
190 STA SI+6
200 LDA 922
210 STA SI+12
220 LDA 923
230 STA SI+13
240 LDA 934
250 STA SI+19
260 LDA 935
270 STA SI+20
280 CLI
290 RTS
300
DEINIT LDA #49
310 STA 788
320 LDA #234
330 STA 789
340 LDA #0
350 STA 999
360 CLI
370 RTS
380 PLAYER LDA248
390 PHA
400 LDA 249
410 PHA
420 LDA 250
430 PHA
440 LDA 251
450 PHA
460 LDA #$00
470 STA 250
480 LDA #$D4
490 STA 251
500 LDX #0
510 DOVOICE LDA 903,X
520 CMP 902,X
530 BNE TIMECOUNT
540 LDA 900,X
550 STA 248
560 LDA 901,X
570 STA 249
580 LDA #0
590 STA 902,X
600 LDY #1
610 LDA (248),Y
620 CMP #$FF
630 BNE NOREPEAT
640 LDA 906,X
650 STA 900,X
660 LDA 907,X
670 STA 901,X
680 LDA 900,X
690 STA 248
700 LDA 901,X
710 STA 249
720 LDY #1
730 LDA (248),Y
740 NOREPEAT STA 903,X
750 LDY #0
760 LDA (248),Y
770 ASL
780 TAY
790 LDA 50000,Y
800 STA 904,X
810 LDA 50001,Y
820 STA 905,X
830 LDY #0
840 LDA 904,X
850 STA (250),Y
860 LDY #1
870 LDA 905,X
880 STA (250),Y
890 LDY #4
900 LDA 908,X
910 STA (250),Y
920 ORA #1
930 STA (250),Y
940 CLC
950 LDA 900,X
960 ADC #2
970 STA 900,X
980 LDA 901,X
990 ADC #0
1000 STA 901,X
1010 JMP NEXTVOICE
1020 TIMECOUNT INC 902,X
1030 NEXTVOICE CLC
1040 TXA
1050 ADC #12
1060 TAX
1070 CLC
1080 LDA 250
1090 ADC #7
1100 STA 250
1110 CPX #36
1120 BEQ EXIT
1130 JMP DOVOICE
1140 EXIT PLA
1150 STA 251
1160 PLA
1170 STA 250
1180 PLA
1190 STA 249
1200 PLA
1210 STA 248
1220 JMP $EA31
Zunächst müssen in Zeile 380 - 450 die Inhalte der Speicheradressen 248-251
auf dem Stack abgelegt werden. Dies ist wichtig, da innerhalb einer
Interrupt-Routine der Inhalt der Zeropage nicht
verändert werden darf.
Die Adressen 248 und 249 werden nun vom Song-Player dazu benutzt, auf das
Noten-Array der einzelnen Stimmen per indirekter Adressierung zuzugreifen, die
Adressen 250 und 251 enthalten die Startadresse der SID-Register. Die
eigentliche Arbeit leistet aber nicht die Zeigerinitialisierung in Zeile 380
- 500, sondern die Routine DOVOICE, die in Zeile 510 beginnt.
DOVOICE liest nun zunächst den Teil der Datenstruktur in den Adressen
900-936 für die aktuelle Stimme aus, für die der Address-Offset
im X-Register steht. Die Datenstruktur selbst startet an der Adresse
900, und DOVOICE muss nun zunächst den Zeitzählerstand für die aktuelle
Stimme aus der Adresse 903,X auslesen, die
maximal erreicht werden darf. In Zeile 520 wird dieser Zeitzählerstand
nun mit dem Zeitzählerstand verglichen, der aktuell vorliegt. Ist dieser
Zählerstand noch nicht beim maximalen Wert angekommen, springt der bedingte
Sprungbefehl in Zeile 530 zum Label TIMECOUNT- das Einlesen der
nächsten Note aus dem Notenarray der aktuellen Stimme wird dadurch
übersprungen. Wenn allerdings der Zähler in der Adresse 902,X
den maximalen Wert erreicht hat, dann muss eine neue Note ausgewählt und
angeschlagen werden.
Für das Abspielen einer neuen Note muss zunächst der aktuelle Notenzeiger
aus den Adressen 900,X und 901,X in die
Adressen 248 und 249 kopiert werden. Dies ist nötig, weil die Y-indizierte
indirekte Adressierung nur mit Zeropage-Zeigern funktioniert. Außerdem muss in Zeile
580 und 590 noch der aktuelle Zeitzähler auf 0 gesetzt werden (Adresse 902,X). Die Notenlänge, die in die Adresse 903,X geschrieben werden soll, steht an der 2. Stelle
des entsprechenden Wertepaares im Noten-Array , und wird in den Zeilen 600
und 610 erst einmal in den Akkumulator geladen.
An dieser Stelle habe ich nun eine Funktion eingefügt, die sehr oft in
Spielen benutzt wird: Wenn die Längenangabe den Wert $FF enthält, dann
wird die entsprechende Stimme von vorn wiederholt. Auf diese Weise
können Sie einen Song in einfacher Weise endlos abspielen. Normalerweise
springt der bedingte Sprungbefehl in Zeile 630 zum Label NOREPEAT,
und die Stimme wird nicht von vorn gestartet. Wenn allerdings die Längenangabe
den Wert 255 hat, werden in Zeile 640 - 730 die Werte von den Adressen 906,X und 907,X in die Adressen 900,X
und 901,X kopiert. Dadurch wird der Zeiger für die aktuelle Note wieder
durch den Zeiger auf den Beginn des Notenarrays für die aktuelle Stimme
zurückgesetzt. Natürlich wird dann auch die aktuelle Notenlänge neu ermittelt.
Auf jeden Fall landet die aktuelle Notenlänge immer in der Adresse 903,X. Die Note selbst dagegen befindet sich immer im
Notenarray vor der Längenangabe, und ist die erste Zahl des Notenpaars {Note,Länge}. Die Note selbst wird
in den Zeilen 750 und 760 eingelesen. In der Notenfrequenztabelle ab
Adresse 50000 befinden sich nun stets 2 Werte, und zwar in der Form, wie
sie auch in das entsprechende SID-Frequenzregister geschrieben werden muss.
Deshalb muss hier der Akkumulator-Inhalt zunächst um 1 Bit nach links
verschoben werden, was einer Multiplikation mit 2 entspricht. Da im X-Register
schon eine wichtige Information steht (nämlich der Zeiger auf die aktuelle
Stimme in der Datenstruktur ab Adresse 900), wird nun das Y-Register
benutzt, um den korrekten Frequenzwert aus der Notentabelle auszuwählen.
Deshalb ist dann auch in Zeile 780 ein TAY-Befehl nötig, der den Inhalt
des Akkumulators in das Y-Register kopiert.
In den Zeilen 790 - 820 werden nun die korrekten Werte, die für die
SID-Register benutzt werden sollen, in den Adressen 904,X
und 905,X zwischengespeichert. Anschließend werden mit diesen Werten in Zeile
830 - 930 die SID-Register für die aktuelle Stimme in der richtigen Weise
befüllt. Dies geschieht wie folgt: Zunächst wird die Frequenz ausgewählt.
Anschließend wird das Gate-Bit für die aktuelle Stimme zuerst gelöscht, und
anschließend wieder gesetzt. Leider können Sie die SID-Register nicht auslesen,
sondern nur beschreiben. Genau hierfür benötigt man dann auch den korrekten
Wellenform-Wert in der Adresse 908,X: Dieser
wird zunächst ohne Gate-Bit, und anschließend mit Gate-Bit (ORA-Befehl
in Zeile 920) in das entsprechende SID-Register geschrieben, das die
Wellenform auswählt. Am Ende muss dann natürlich noch der Zeiger in Adresse 900,X und 901,X um 2 erhöht werden, damit
dieser auf die nächste Note im Noten-Array für die aktuelle Stimme zeigt. Am
Ende landen Sie dann nach der Auswahl und dem „Anstoßen“ der Note beim Label NEXTVOICE
in Zeile 1030. Die Routine NEXTVOICE erhöht den Zeiger in den
Adressen 250 und 250 um 7 und wählt dadurch den Bereich der SID-Register für
die nächste Stimme aus. Vorher wird aber noch das X-Register um 12
erhöht- was aber nur durch einen Umweg über den Akkumulator möglich ist.
Anschließend landen Sie dann wieder bei DOVIOICE, aber nur dann, wenn im
X-Register nicht der Wert 36 steht. Wenn dies dennoch der Fall ist, dann
verlassen Sie durch den Befehl JMP EXIT die IRQ-Player-Routine. In
diesem Fall ist ein separater Label nötig, um vor der Rückkehr zu BASIC den
veränderten Inhalt der Adressen 248-251 wiederherzustellen und den tack
aufzuräumen.
Wenn Sie jedoch zuvor keine neue Note ausgewählt haben, dann landen Sie in Zeile
1020 beim Label TIMECOUNT. Im Endeffekt wird hier nur der aktuelle
Zeitzähler in der Adresse 902,X um 1 erhöht,
und anschließend springen Sie zurück zu DOVOICE.
5.1.2 Noten für den Songplayer erstellen
Der Songplayer ist einfach gehalten, soll aber trotzdem flexibel sein. Dies
hat leider den Nachteil, dass die Tabelle mit den Notenfrequenzen an der
Adresse 50000 nicht fest vorgegeben ist, und erst einmal erstellt werden muss.
So schwer ist dies aber auch wieder nicht, denn Sie können dazu folgendes
BASIC-Programm verwenden:
15-NOTETAB
10 PRINT"[SHIFT+CLR/HOME]BITTE WARTEN..."
20 FOR I=1 TO 80
30 F=16*(INT(27.5*2^(I/12)))
40 F=F*0.9825:REM PAL-MASCHINE
50 HI=INT(F/256):LO=F-(256*HI)
60 POKE 50000+(2*I),LO:POKE 50001+(2*I)+1,HI
90 NEXT I
In dem letzten Beispiel-Listing ist der tiefste Ton der Ton A0 mit einer
Frequenz von 27,5 HZ. Um eine Note, die I Halbtonschritte über A0 liegt, in
eine Frequenz umzurechnen, wird die entsprechende Formel benutzt:
F=27,5*2(I/12)
Wenn der 6510-Prozessor mit genau 1 MHz laufen würde, dann wären die in die
SID-Register einzutragenden Frequenzwerte genau 1 Schritt pro 1/16 Hz, und die
Formel in Zeile 30 wäre auch so korrekt. Da aber z.B. auf meiner
PAL-Maschine der 6510 nur mit 982,5 kHz läuft, muss der Wert F, den die
Formel in Zeile 30 liefert, in Zeile 40 noch einmal mit dem
Korrekturfaktor 0,9825 multipliziert werden. Zeile 50 und 60 machen nun
nichts anderes, als F in ein Lo- und ein Hi-Byte zu zerlegen und in
dieser Reihenfolge in die Frequenztabelle ab Adresse 50000 einzutragen. Das
letzte Beispiel benutzt 80 mögliche Notenwerte (Wert 1 bis 80), der Wert 0 ist
für Pausen reserviert.
Nun fehlt natürlich noch die wichtigste Sache, nämlich die Noten selbst. An
dieser Stelle ist nun guter Rat teuer, denn woher wollen Sie Noten nehmen? Die
einfachste Variante ist die, sich einfach Notenbücher in einem
Musikfachgeschäft Ihrer Wahl zu besorgen, und diese Note für Note per Hand in
entsprechende Zahlen für den Songplayer umzusetzen. Am Ende haben Sie dann nach
mehren Tagen ein BASIC-Programm mit zahlreichen
DATA-Zeilen, die Sie irgendwann (wenn alles einwandfrei läuft) mit dem „Saver“ in eine Datei auslagern können. In der Tat sind die
ersten Spieleentwickler, die auch meist gute Musiker waren, so vorgegangen, und
haben auch sehr erstaunliche Resultate erzielt. Wenn Sie z.B. das Spiel „trolls and tribulations“ kennen,
dann wissen Sie wahrscheinlich, dass Sie mit sehr einfachen Möglichkeiten
beeindruckende Mozart-Stücke in Ihr Spiel einbauen können.
Zugegebenermaßen kann ich Ihnen an dieser Stelle auch keine einfache Lösung
bieten, denn ich bin hauptberuflich Buchautor, und kann nicht meine gesamte
Zeit am C64 verbringen. Auch, wenn ich mich mit Musik auskenne, weil ich 10
Jahre lang Bands abgemischt habe, kann ich Ihnen kein Patentrezept für gute
C64-Songs anbieten. Deshalb ist auch mein Noteneditor, an dem ich von Zeit zu
Zeit arbeite, noch lange nicht fertig- ich werde diesen aber in den
Download-Bereich stellen, sobald ich sämtliche Bugs, von denen es leider noch
zahlreiche gibt, entfernt habe. Wie Sie sehen, ist das Musikmachen auch auf dem
C64 keine einfache Sache, denn Musik ist leider das Komplexeste, das der Mensch
neben der Mathematik erfunden hat. Und nicht nur das: Musik und Mathematik haben
sehr viele Berührungspunkte, deshalb können viele Bereiche der Musik auch
mathematisch beschrieben werden.