6. Musik und Sound
Sounds, aber auch die
herausragenden Musikfähigkeiten, haben den C64 erst zu dem gemacht, was er ist:
Der meistverkaufte Computer der 80-er und 90-er. Dies verdankt er vor allem dem
SID-Soundchip. Deshalb gilt: Wer dem C64 Musik entlocken will, muss sich
eingehend mit dem SID befassen. Leider stößt BASIC hierbei an seine Grenzen -
wieder ein Grund mehr, sich nach dem BASIC-Kurs direkt der
Assembler-Programmierung zu widmen. Vieles im Bereich Musik und Sound geht
nämlich nicht ohne Maschinenprogramme.
6.1 Der SID-Soundchip
Der SID (dies
ist die Abkürzung von Sound Interface Device) wurde von
Michael Yannings Anfang der 80-er Jahre für Commodore
entwickelt, um dem Anwender einen einfach zu programmierenden Synthesizer-Chip
zur Verfügung zu stellen. Dieser Chip sollte möglichst einfach aufgebaut sein,
aber dennoch effizient arbeiten. Heraus kann der SID, der beide Kriterien
erfüllt: Der interne Aufbau ist eher schlicht, aber zusammen mit den
Möglichkeiten, externe analoge Filter zu verwenden, und der sehr feinen
Skalierbarkeit der Ausgabefrequenzen, leistet der SID in Ihrem C64 gute
Dienste.
6.1.1 Interner Aufbau
Um den SID optimal zu
programmieren, müssen Sie zunächst ein paar Dinge über den internen Aufbau
wissen, und auch ein paar physikalische Grundlagen der Erzeugung von Tönen
verstehen. Keine Angst, dies ist wirklich nicht schwer, und Sie müssen auch
keine höhere Mathematik beherrschen (na gut, wenn Sie höhere Mathematik
beherrschen, wird vieles einfacher). Sie müssen sich einfach zunächst nur einen
Zähler vorstellen, der zu dem aktuellen Wert, der bei 0 beginnt, immer jeweils
einen bestimmten Betrag addiert - beispielsweise 1. Auf diese Weise erhalten
Sie die folgende Zahlenreihe:
0, 1, 2, 3, …
Stellen Sie sich nun
vor, dass der Zähler eine Breite von 24 Bit besitzt, das heißt, dass der
höchste Wert, den dieser Zähler annehmen kann, 224-1 ist. Wenn diese
Zahl (224-1=16.777.215=$FFFFFF) erreicht ist, dann springt der
Zähler wieder auf 0 zurück (dies nennt man dann einen Überlauf).
Angenommen, Sie haben
nun die Möglichkeit, Zahlenwerte als Spannungen auszugeben, und diese
Spannungen an den Audio-Eingang Ihres Monitors weiterzuleiten. Was würde nun
geschehen, wenn Sie einen Zähler in eine Spannung umwandeln, und diese Spannung
an den Audio-Ausgang weitergeben? Die Antwort ist, dass im Falle des hier
beschriebenen Zählers nicht viel geschehen würde, weil sich der 24-Bit-Zähler,
der in jedem Schritt immer nur um 1 erhöht wird, viel zu langsam ändert, als
dass Ihr Ohr einen hörbaren Ton daraus machen könnte (naja, im Endeffekt
erzeugt Ihr Gehirn die wahrnehmbaren Töne, nicht direkt Ihr Ohr). Aber wie
können Sie das vorige Problem lösen, und hörbare Töne erzeugen? Die Antwort ist
die Verwendung der Modulo-Funktion. Die Modulo-Funktion liefert den Rest
einer Division. Wenn Sie Ihren 24-Bit-Zähler Modulo 4096 nehmen, dann erhalten
Sie die folgende Zahlenreihe:
0, 1, 2, …, 4095, 0, 1,
2, …
Wenn Sie nun annehmen,
dass zu Ihrem Zähler einmal pro Mikrosekunde der Wert 1 addiert wird, dann
erhalten Sie schon 1.000.000/4.096=244,14 Durchläufe pro Sekunde. Dies ist
schon schnell genug, um einen hörbaren Ton zu erzeugen. Und Sie haben noch mehr
Glück: Der SID tut genau dies: Er wandelt genau solche Zähler durch einen
internen DAC in Spannungen zwischen 0 und 3,3V um. Ein DAC (Digital/Analog
Converter) ist ein elektronischer Baustein, der digitale Werte in
analoge Spannungen umwandelt. Diese Spannungen können, wenn sie sich schnell
genug ändern, dazu benutzt werden, hörbare Töne zu erzeugen. Hierzu werden die
Spannungen an einen entsprechenden Verstärker-Baustein in Ihrem Monitor oder an
eine Stereoanlage weitergegeben. Und Sie haben auch hier wieder Glück: Der SID
generiert Line-Pegel, was nichts anderes bedeutet, als dass Sie den
Ausgang des SID direkt in den Line-In-Eingang eines Verstärkers stecken können.
Allerdings müssen Sie dazu einen entsprechenden Adapter im Internet bestellen,
denn der alte C64 verwendet noch DIN- und keine Chinch-Stecker.
Aber was haben Sie nun
von dem bisher Gesagten? Ganz einfach: Der SID besitzt drei Zähler, die unabhängig
voneinander arbeiten, und zu denen auch andere Werte addiert werden können,
als 1. Zusätzlich können die einzelnen Zähler auch in verschiedener Weise
benutzt werden. Der SID kann nämlich aus einem der drei Ausgangszähler in der
folgenden Weise die folgenden vier Wellenformen erzeugen:
Sägezahn-Wellenform
·
Addieren
des Referenzwertes für den aktuellen Zähler zum aktuellen Zähler.
·
Verschiebung
des aktuellen Zählers um 16 Bits nach rechts.
·
Weitergabe
der verbleibenden 8 Bits an den DAC (dieser hat beim SID eine breite von 8 Bit).
Die Sägezahn-Wellenform
ist also eine aufsteigende Zahlenfolge, die ab dem Wert 255 wieder bei 0
anfängt. Die Sägezahn-Wellenform hört sich auch rau an, und hat in der
Tat etwas von einer Säge. Verwendet wird diese Wellenform unter anderem
für Klavier-Sounds.
Dreieck-Wellenform
·
Addieren
des Referenzwertes für den aktuellen Zähler zum aktuellen Zähler
·
Verschiebung
des aktuellen Zählers um 12 Bits nach rechts. Dadurch entsteht ein 4096 mal
langsamerer 12-Bit-Zähler.
·
Wenn
das oberste Bit des neuen Zählers 0 ist, Weitergabe des um 3 Bits verschobenen
neuen Zählers an den DAC
·
Wenn
das oberste Bit des neuen Zählers 1 ist, Umdrehen sämtlicher Bits des neuen
Zählers, und anschließendes Ausmaskieren des 12. Bits des neuen Zählers. Auch
hier werden anschließend nur die Bits 11-4 des neuen Zählers an den DAC
weitergereicht.
Die Dreieck-Wellenform
ist also eine aufsteigende Zahlenfolge, die ab dem Wert 255 aber nicht wieder
bei 0 anfängt, sondern anschließend zu einer absteigenden Zahlenfolge wird. Ab
dem Wert 0 wird dann wieder mit einer aufsteigenden Zahlenfolge weitergemacht.
Dieses Verhalten führt auf dem Oszilloskop zu einer Kurve, die wie ein Dreieck
aussieht. Die Dreieck-Wellenform hört sich auch nicht rau an, sondern weich
und harmonisch. Leider ist die Dreieck-Wellenform besonders für die unteren
Oktaven sehr leise. Diese Wellenform wird für Gitarren-Sounds benutzt, oft auch
zusammen mit SID-Filtern.
Rechteck-Wellenform
·
Addieren
des Referenzwertes für den aktuellen Zähler zum aktuellen Zähler
·
Verschiebung
des aktuellen Zählers um 12 Bits nach rechts. Dadurch entsteht ein 4096 mal
langsamerer 12-Bit-Zähler.
·
Wenn
der neue Zähler eine vorher definierte Grenze (die sog. Pulsbreite)
unterschreitet, wird der Wert 255 an den DAC weitergegeben
·
Wenn
der neue Zähler eine vorher definierte Grenze (die sog. Pulsbreite)
überschreitet, wird der Wert 0 an den DAC weitergegeben
Die Rechteck-Wellenform
springt dauernd zwischen den Werten 0 und 255 hin und her. Dieses Verhalten
führt auf dem Oszilloskop zu einer Kurve, die wie ein Rechteck aussieht.
Die Rechteck-Wellenform hört sich synthetisch an, etwa so, wie eine alte
Videopac-Spielekonsole. Die Rechteck-Wellenform wird oft zusammen mit
SID-Filtern benutzt, um z.B. Motorengeräusche nachzuahmen. Allerdings erzeugen
Kolben bei der Explosion im Motor oft komplexe Wellenformen, die nicht naturgetreu
vom SID nachgeahmt werden können.
Rauschen
Der SID kann auch aus
einem Zähler mittels eines Algorithmus Pseudo-Zufallswerte generieren
und an den DAC weiterleiten. Zufallswerte werden vom Ohr als Rauschen
wahrgenommen. Rauschen kann zusammen mit einem ausklingenden Ton für Explosionen
oder Wasser-Sounds benutzt werden.
6.2 Ausgabe von Tönen
mit einer Stimme
Nun wollen wir den
Kammerton A4 (440 Hz) ausgeben. Dazu benutzen wir den ersten der drei
verfügbaren Zähler, die auch als die erste von drei Stimmen bezeichnet wird (Stimme
0). Damit Sie auf die einzelnen Zähler zugreifen können, blendet der SID
seine Prozessorregister in den Speicher des C64 so ein, dass das erste
Register (SID-Register Nr. 0) an der Adresse 54272 liegt.
6.2.1 Frequenzauswahl
mit den SID-Registern
Der SID wird nun mit
etwa 1 MHz getaktet, und läuft synchron zum 6510-Prozessor. Das bedeutet, dass
im Falle der Sägezahn-Wellenform Ihr Ton nur mit 1/16 Hz ausgegeben wird, wenn
Sie pro Mikrosekunde den Wert 1 zu dem entsprechenden Zähler addieren. Dies
können Sie leicht ausrechnen: Von dem Zähler, der bis 16.777.215 zählt, werden
nur die obersten 12 Bits für die Erzeugung der Wellenformen benutzt, dies
entspricht dann nur noch 4.096 Zählerschritten pro Sekunde. Da jedoch die
Sägezahnwellenform diesen neuen Zähler noch einmal auf einen Bereich zwischen 0
und 255 abbildet, erhalten Sie dann nur noch eine Frequenz von 256/4096=1/16
Hz. Wenn Sie dann zu dem ursprünglichen Zähler jede Mikrosekunde statt 1 den
Wert 2 addieren, erhalten Sie eine Frequenz von 2/16 Hz. Für den Kammerton A4
ist also der in jedem Schritt zu addierende Wert 440/(1/16)=7040. Diesen Wert müssen Sie nun in die SID-Register
Nr. 0 und 1 schreiben. Hierbei enthält dann SID-Register Nr. 0 das erste
Byte des Wertes 7040 (Lo-Byte), und SID-Register Nr. 1 das zweite Byte
des Wertes 7040 (Hi-Byte). Die zwei folgenden BASIC-Zeilen leisten dies
nun:
10
SI=54272: F=7040
20 HI=INT(F/256): LO=F-256*HI
30 POKE SI+1,HI: POKE SI,LO
Wenn Sie die Zähler initialisiert
haben, müssen Sie den Ton mit voller Lautstärke einschalten. Dies erreichen
Sie, indem Sie den Wert 15 in SID-Register Nr. 24 schreiben:
40
POKE SI+24,15
Nun müssen Sie die Wellenform
auswählen, die Sie verwenden wollen. Für die erste Stimme müssen Sie dazu
einen der folgenden Werte in SID-Register Nr. 4 schreiben:
·
16
(Bit 4=1) für die Dreieck-Wellenform
·
32
(Bit 5=1) für die Sägezahn-Wellenform
·
64
(Bit 6=1) für die Rechteck-Wellenform
·
128
(Bit 7=1) für Rauschen
Sie schreiben also
50
POKE SI+4,32
und geben anschließend RUN
ein. Leider geschieht nichts und Sie hören keinen Ton. Dies liegt daran, dass
die Sache so einfach dann auch wieder nicht ist. Sie haben dem SID nämlich
überhaupt nicht mitgeteilt, wann Ihr Kammerton A4 abgespielt werden soll, und
erst recht nicht, wie lange. Soll der Ton gehalten werden? Soll der Ton
anschwellen, oder sogar nachhallen? Zu diesem Zweck müssen Sie zusätzlich noch
eine Hüllkurve angeben. Die Hüllkurve ist das, was den Ton selbst
ausmacht. Ohne Definition einer Hüllkurve hören Sie deswegen nichts. Die
Hüllkurve wird durch die folgenden 4 Parameter definiert:
·
Die
Anschwellzeit A (attack),
die angibt, wie lange der Ton braucht, um zu seiner vollen Lautstärke zu kommen.
·
Die
Abschwellzeit D (decay),
die angibt, wie lange der Ton braucht, um bis zu seinem Haltepegel abzuklingen.
·
Der
Haltepegel S (sustain), der die
Lautstärke eines eventuell gehaltenen Tons angibt (also der Haltepegel).
·
Die
Ausklingzeit R (release), die die Nachklingzeit vom Haltepegel auf 0 angibt.
Das Einstellen der
Hüllkurve, also die Programmierung des Hüllkurvengenerators, ist auf dem SID
etwas speziell zu handhaben. Es wurde, um Platz zu sparen, das Startbit (das
sog. Gate-Bit) für den Ton in Register Nr. 4 untergebracht. Sie
müssen also den Ton zusammen mit der Wellenformauswahl starten. Auch die
Werte für A, D, S und R müssen Sie in spezieller Weise handhaben, nämlich als
einzelne Nibbles. So stehen z.B. bei der ersten Stimme (Stimme 0) die Werte für
A und D im hohen bzw. niedrigen Nibble von SID-Register Nr. 5. Auch bei
den Werten für S und R ist dies so, und S steht im hohen, und R im niedrigen
Nibble von SID-Register Nr. 6. Dies bedeutet natürlich auch, dass Sie
die Werte für A, D, S und R nicht direkt in Millisekunden eingeben können,
sondern stattdessen folgende Tabelle verwenden müssen:
Attack-Zeit
|
Wert |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|
Zeit (ms) |
2 |
8 |
16 |
24 |
38 |
56 |
68 |
80 |
100 |
250 |
500 |
800 |
1000 |
3000 |
5000 |
8000 |
Decay/Release-Zeit
|
Wert |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|
Zeit (ms) |
6 |
24 |
48 |
72 |
114 |
168 |
204 |
240 |
300 |
750 |
1500 |
2400 |
3000 |
9000 |
15000 |
24000 |
Für das letzte Beispiel
bedeutet dies: Sie müssen das Programm so abändern, dass Sie erst die
Tonfrequenz auswählen, und dann die Hüllkurve erstellen. Um anschließend den
Ton zu starten, und den SID in den Zyklus Attack/Decay/Sustain
zu versetzen, müssen Sie zusammen mit der Wellenform das Gate-Bit (Bit
0 in SID-Register Nr. 4) auf 1 setzen. Dies führt dann zu dem
folgenden, einwandfrei laufenden Programm:
16-A440
10
SI=54272: F=7040: POKE SI+24,15
20 HI=INT(F/256): LO=F-256*HI
30 POKE SI+1,HI: POKE SI,LO
40 POKE SI+5,10: POKE SI+6,0: REM A=0, D=10,
D=0, R=0
50
POKE SI+4,32
60
POKE SI+4,33
An dem letzten Beispiel
sehen Sie sehr schön, wie die Tonausgabe durch das Gate-Bit gesteuert wird:
Wenn das Gate-Bit 0 ist, geht der SID in die Release-Phase über (die hier nicht
verwendet wird), wenn das Gate-Bit 1 ist, geht der SID in die Attack-Phase über, der nahtlos die Decay- und die
Sustain-Phase folgen. Wenn dann der Wert für S und R=0 ist, dann kann der Ton
durch Setzen des Gate-Bits auf 0 direkt gestoppt werden. Da der SID unabhängig
vom Hauptprozessor läuft, benutzen viele Spiele die oben verwendete Technik, S
und R auf 0 zu setzen, um Melodien im Hintergrund abzuspielen. Allerdings
werden bei Spielen oft Maschinenprogramme benutzt, die die SID-Register viel
schneller beschreiben können, als BASIC.
6.2.2 Die SID-Register (Quelle: https://www.c64-wiki.de/wiki/SID)
Wie auch schon im Falle
des VIC möchte ich Ihnen nun eine Zusammenstellung sämtlicher Register des SID
vorstellen. Auch diese Tabelle werden Sie wahrscheinlich in Zukunft noch oft
benötigen.
|
SID-Register |
Adresse |
Bedeutung |
|
0 |
54272 |
Frequenz Stimme 0
Lo-Byte |
|
1 |
54273 |
Frequenz Stimme 0
Hi-Byte |
|
2 |
54274 |
Rechteck-Pulsbreite Stimme
0 Lo-Byte |
|
3 |
54275 |
Rechteck-Pulsbreite Stimme
0 Hi-Nibble |
|
4 |
54276 |
Wellenform Stimme
0 und SID-Status-Register** Bit 7: Rauschen Bit 6: Rechteck Bit 5: Sägezahn Bit 4: Dreieck Ein gesetztes
Wellenform-Bit wählt die Wellenform aus, es kann nur immer eine Wellenform
ausgewählt werden, wobei das höchste Bit, das gesetzt ist, entscheidet,
welche Wellenform benutzt wird. Bit 3: Testbit Erst Testbit auf 1 setzen, dann auslesen. Wenn das Testbit noch 1 ist, ist der SID OK. Bit 2: Ring-Modus Ist dieses Bit 1,
können mehrere SID-Chips im Verbund arbeiten. Wenn die Verbindung erfolgreich
ist, ist das Sync-Bit (Bit 1) gesetzt. Bit 0: Gate-Flag 1=ADS-Phase einleiten 0=Release-Phase einleiten |
|
5 |
54277 |
Hi-Nibble: Attack-Zeit
für Stimme 0 Lo-Nibble: Decay-Zeit für
Stimme 0 |
|
6 |
54278 |
Hi-Nibble: Sustain
(Haltepegel) für Stimme 0 Lo-Nibble: Release-Zeit für
Stimme 0 (Gate 0=0) |
|
7 |
54279 |
Frequenz Stimme 1
Lo-Byte |
|
8 |
54280 |
Frequenz Stimme 1
Hi-Byte |
|
9 |
54281 |
Rechteck-Pulsbreite Stimme
1 Lo-Byte |
|
10 |
54282 |
Rechteck-Pulsbreite Stimme
1 Hi-Nibble |
|
11 |
54283 |
Wellenform Stimme
1 (Bit 1-3 unbenutzt) Wellenform- und
Gate-Bits stimmen mit Stimme 0 überein |
|
12 |
54284 |
Hi-Nibble: Attack-Zeit
für Stimme 1 Lo-Nibble: Decay-Zeit für Stimme
1 |
|
13 |
54285 |
Hi-Nibble: Sustain
(Haltepegel) für Stimme 1 Lo-Nibble: Release-Zeit für Stimme
1 (Gate 1=0) |
|
14 |
54286 |
Frequenz Stimme 2
Lo-Byte |
|
15 |
54287 |
Frequenz Stimme 2
Hi-Byte |
|
16 |
54288 |
Rechteck-Pulsbreite Stimme
2 Lo-Byte |
|
17 |
54289 |
Rechteck-Pulsbreite Stimme
2 Hi-Nibble |
|
18 |
54290 |
Wellenform Stimme
2 (Bit 1-3 unbenutzt) Wellenform- und
Gate-Bits stimmen mit Stimme 0 überein |
|
19 |
54291 |
Hi-Nibble: Attack-Zeit
für Stimme 2 Lo-Nibbe: Decay-Zeit für Stimme
2 |
|
20 |
54292 |
Hi-Nibble: Sustain
(Haltepegel) für Stimme 2 Lo-Nibble: Release-Zeit für Stimme
2 (Gate 2=0) |
|
21 |
54293 |
Grenzfrequenz für den
SID-Filter (Lo-Byte)* |
|
22 |
54294 |
Grenzfrequenz für den
SID-Filter (Hi-Byte)* |
|
23 |
54295 |
Hi-Nibble: Resonanz (Stärke)
des SID-Filters Lo-Nibble: Gefilterte
Stimmen (Bit 0-2), aber immer nur eine einzige Bit 3: Externer
Filter (einschalten=1), z.B. über eine Subgruppe am Mischpult (Ist Bit 3 gesetzt,
so werden sämtliche Stimmen gefiltert) |
|
24 |
54296 |
Lo-Nibble: Lautstärke
0-15 für alle Stimmen Hi-Nibble:
Filtermodus (es kann immer nur ein einziger Filter aktiv sein) Bit 7: Ausschalten
von Stimme 2 Bit 6: Wählen des
Hochpassfilters (1=an) Bit 5: Wählen des
Bandpassfilters (1=an) Bit 4: Wählen des Tiefpassfilters (1=an) |
|
25 |
54297 |
X-Koordinate (0-255)
eines Paddles oder einer Maus, wenn dieses Gerät angeschlossen ist, ansonsten
0 |
|
26 |
54298 |
Y-Koordinate (0-255)
eines Paddles oder einer Maus, wenn dieses Gerät angeschlossen ist, ansonsten
0 |
|
27 |
54299 |
Oszillator-Register
für Stimme 2: Hier erscheinen die
8-Bit-Werte, die Stimme 2 an den DAC weitergibt. Wenn Stimme 2
ausgeschaltet wurde, dann werden hier bei Auswahl der Rauschen-Wellenform
Pseudo-Zufallszahlen erzeugt. |
|
28 |
54300 |
Hüllkurven-Geber für Stimme
2: Hier erscheint der
aktuelle Lautstärke-Wert, den der Hüllkurvengenerator gerade für Stimme 2
benutzt. Mit schnellen Maschinensprache-Routinen kann man diesen Wert
auslesen, und mit den Frequenzregistern für die anderen Stimmen koppeln. Auf
diese Weise können interessante Effekte programmiert werden. |
* Die Grenzfrequenz des
SID-Filters hängt davon ab, mit welchem externen Kondensator man den SID
beschaltet. Beim C64 ist die Grenzfrequenz ungefähr mit dem binären 12-Bit-Wert
identisch, den Sie in die SID-Register 21 und 22 eintragen. Wenn Sie
also hier den Wert 1000 eintragen (SID-Register Nr. 21=232, SID-Register Nr.
22=3), dann erhalten Sie auch ungefähr eine Grenzfrequenz von 1000 Hz. Da jede Stimme ein
eigenes Filter-Bit besitzt, kann der SID-Filter für jede Stimme unabhängig
benutzt werden, allerdings gelten die Filtereinstellungen global. Das heißt,
dass nicht Stimme 0 den Hochpassfilter, und Stimme 1 den Tiefpassfilter
benutzen kann.
** Das
SID-Status-Register ist entgegen der Angaben in der Dokumentation des SID durch
MOS Technology Inc. nur ein einziges Mal vorhanden, und nicht für jede Stimme
separat. Das heißt, dass nur mit dem SID-Register Nr. 4 (und nicht mit
SID-Register Nr. 11 und 18) der SID z.B. in den Ring-Modus versetzt werden
kann. Auch das Test-Flag gibt es, wie auch das Sync-Bit, nur in SID-Register Nr. 4.
6.3 Abspielen von Songs
Kommen wir nun zum
ersten Beispiel. In diesem Listing wird ein einfacher Song abgespielt, den Sie
in ähnlicher Weise auch immer wieder im Internet antreffen, z.B. auf www.poke-53280.com, wo ich dann dieses Listing
auch herhabe.
17-SIMPLESONG
10
SI=54272
20
POKE SI+24,15: REM VOLLE LAUTSTAERKE
30 POKE SI+5,9:POKE
SI+6,0:REM KLAVIER
40 READ HF,LF,L:IF HF<0 THEN END
50 POKE SI,LF:POKE SI+1,HF
60 POKE SI+4,33:REM
GATE-BIT=1 STARTET DEN TON (ADS-PHASE)
70 FOR
T=1 TO L:NEXT T:REM
WARTESCHLEIFE FUER DEN TON
80
POKE SI+4,32:REM GATE-BIT=0 STARTET DIE R-PHASE
90 FOR
T=0 TO 50:NEXT T:REM TON
ETWAS AUSKLINGEN LASSEN
100 GOTO 40
110 DATA 25,177,250,28,214,250
120 DATA 25,177,250,25,177,250
130 DATA 25,177,125,28,214,125
140 DATA 32,94,250,25,177,250
150 DATA 28,214,250,19,63,250
160 DATA 19,63,250,19,63,250
170 DATA 21,154,125,24,125,63
180 DATA 25,177,250,24,63,125
190 DATA 19,63,250,-1,-1,-1
Im letzten Beispiel
wird nur der Wert für Decay benutzt (D=9), nicht aber der Wert für Attack (A=0), Sustain (D=0) und Release (R=0). Dies
bedeutet, dass der Ton sofort präsent ist, und innerhalb von 0,75 Sekunden
vollständig abschwellt. Deshalb muss in Zeile 30 auch nur der Wert 9
in SID-Register Nr. 5 eingetragen werden, SID-Register Nr. 6 wird
dagegen auf 0 gesetzt. Natürlich müssen Sie auch beim SID vorher in Zeile
10 die Basisadresse durch die Anweisung SI=54272 festlegen,
denn sonst können Sie nicht anhand der Nummer auf die Register zugreifen.
Anders, als beim VIC, müssen Sie den SID allerdings dadurch initialisieren,
dass Sie in Zeile 20 das Lautstärkeregister auf den Wert 15
setzen, denn sonst hören Sie keinen Ton.
Nach der
Initialisierung der SID-Register muss der Song in einer Schleife (Zeile
40-100) abgespielt werden. Der Song selbst steht in DATA-Zeilen (Zeile
110-190). Der Song besitzt nur eine Stimme, und die Werte für die
Frequenzangaben für die einzelnen Noten wurden in diesem Beispiel so in den DATA-Zeilen
abgelegt, dass sie direkt in die SID-Register geschrieben werden können. Zeile
40 liest diese Daten nun als Dreierpäckchen der Form {Hi-Byte (HF), Lo-Byte
(LF), Länge (L)} aus, und schreibt Lo- und Hi-Byte in SID-Register Nr. 0 und
1. SID-Register Nr. 0 enthält das Lo-Byte, und SID-Register
Nr. 1 das Hi-Byte für die Frequenzangabe für Stimme 0. So erhalten
Sie einen gültigen 16-Bit-Wert, der für den Zähler der ersten Stimme benutzt
werden kann. In Zeile 60 wird nun die Sägezahn-Wellenform zusammen mit
einem gesetzten Gate-Bit in SID-Register Nr. 4 eingetragen, wodurch auch
gleich der Ton gestartet wird. Da der Ton sofort auf 0 abschwillt, kann das
Programm direkt nach Starten des Tons in eine Warteschleife (Zeile 70)
eintreten. Wie lange diese Schleife läuft, bestimmt das dritte Byte des zuvor
ausgelesenen Dreierpäckchens (also die Variable L). Nach der
Warteschleife für die Notendauer wird das Gate-Bit in SID-Register Nr. 4
wieder gelöscht, und der Ton wird in Zeile 90 noch etwas nachklingen
lassen. Diese Zeile wurde eingefügt, damit zwischen zwei gleichen Noten eine
kleine Pause ist. Ohne diese Pause würde man zwei hintereinander folgende
gleiche Noten als eine einzige Note wahrnehmen.
Der Song endet, wenn
für Frequenz und Länge der Wert -1 aus den DATA-Zeilen gelesen wird.
Diese Werte werden für die SID-Register nicht verwendet, und können deshalb als
Stopp-Marker dienen. Kommen wir nun zum nächsten Beispiel. Das Listing ist mit
dem vorigen Listing identisch, demonstriert Ihnen aber durch einen
Fortschrittsbalken, dass der SID wirklich unabhängig von Ihrem Hauptprogramm
arbeitet.
18-SIMPLESONG2
10 SI=54272:K=0:PRINT"[SHIFT+CLR/HOME]SPIELE SONG"
20 POKE SI+24,15:REM
VOLLE LAUTSTAERKE
30 POKE SI+5,9:POKE
SI+6,0:REM KLAVIER
40 READ HF,LF,L:IF HF<0 THEN END
50 POKE SI,LF:POKE SI+1,HF
60 POKE SI+4,32:POKE
SI+4,33
70 FOR T=1 TO L
80 POKE 1064+(K/10),46:K=K+1
90 NEXT T
100 GOTO 40
110 DATA 25,177,25,28,214,25
120 DATA 25,177,25,25,177,25
130 DATA 25,177,12,28,214,12
140 DATA 32,94,25,25,177,25
150
DATA 28,214,25,19,63,25
160
DATA 19,63,25,19,63,25
170
DATA 21,154,12,24,63,12
180
DATA 25,177,25,24,63,12
190
DATA 19,63,25,-1,-1,-1
Die Warteschleife in Zeile
70-90 wartet nicht nur die Zeit L, die Sie bei der Notendauer
angeben, sondern schreibt auch gleichzeitig einen Fortschrittsbalken in Form
von Pünktchen in die zweite Zeile Ihres Bildschirms. Hierdurch müssen Sie
jedoch die Längenangabe für Ihre Noten durch 10 teilen, was leider ein reiner
Erfahrungswert ist.
6.3.1 Notenwerte in
Frequenzen umrechnen
Vielleicht gefallen Ihnen
die letzten Beispiele nicht so wirklich. Zugegeben: Mir auch nicht! Aber das
ist auch kein Wunder, denn ich habe die letzten Beispiele bewusst so gewählt,
weil sie eben Unzulänglichkeiten enthalten. Die größte Unzulänglichkeit ist,
dass Sie Ihre Noten nicht direkt in Ihren DATA-Zeilen angeben können,
z.B. durch die Anzahl der Halbtonschritte ab einem bestimmten tiefsten, gerade
noch hörbaren Ton. Dieser tiefste, gerade noch hörbare Ton ist auf dem SID
beispielsweise 8,176 Hz und entspricht dem Ton C1, der noch einmal um zwei
Oktaven erniedrigt wird.
Wählen wir nun den
tiefst möglichen Ton, der auf dem SID einer Frequenz von 8,176 Hz entspricht,
als Ausgangspunkt einer Abbildungsfunktion von Notenwerten auf eine
Frequenz in Hz. Sie denken vielleicht nun, dass dies nicht so schwer sein kann,
denn im Endeffekt müssen Sie ja nur eine simple Geradengleichung der Form
y=ax+b
aufstellen, und für
b=8,176 Hz, sowie für a Ihren Notenwert (gemessen in Halbtonschritten)
einsetzen. Leider ist es aber so, dass sich in jeder neuen Oktave die Frequenz
verdoppelt, und Sie es dann auch niemals schaffen, eine lineare Abstufung
zwischen den Noten zu erhalten. Das menschliche Ohr hört also logarithmisch,
deshalb benötigen sie auch die folgende Gleichung, um Noten auf Frequenzen
abzubilden:
F(x)=a0*2x/12
F(x) ist hier die
Frequenz, gemessen in Hz, und a0 ist die Frequenz des tiefsten Tons,
den Sie auf Ihrem wie immer gearteten Instrument spielen können. Die Variable x
beschreibt dann die Anzahl der Schritte, die Sie ab dem Ton a0 gehen
müssen, um zu Ihrer gewünschten Note zu gelangen.
Natürlich können Sie
schon sehr gut BASIC, und es ist für Sie deshalb ein Leichtes, eine FOR-Schleife
zu erstellen, die die Frequenzwerte für die einzelnen Noten in einem Array
ablegt. Dieses Array müssen Sie anschließend nur noch auslesen. Und da Sie
wissen, dass die Frequenzwerte in Schritten von 1/16 Hz in die SID-Register 0
und 1 eingetragen werden müssen, können Sie diese dann auch direkt in
POKE-Anweisungen einbauen.
Leider liegt der Teufel
wie immer im Detail, denn der SID ist, wie auch der VIC, fest mit dem Speicher
des C64 verbunden. Schlimmer noch: Der SID wird über den Prozessortakt
gesteuert, und diesen Prozessortakt gibt im Endeffekt der VIC vor. Der VIC gibt
also den Takt an, und dies ist auch gut so: Wenn der VIC nicht den Takt angeben
würde, dann würde auch das Fernsehbild nicht korrekt mit dem Rasterstrahl
synchronisiert. Und Sie sähen dann auch… Genau: Gar nichts! Aber auch dies ist
nicht alles: Es gibt zusätzlich noch verschiedene Fernsehnormen. Die
Fernsehnorm, die der C64 von Haus aus verwendet, ist die amerikanische NTSC-Norm
(National Television System Company Standard).
Ein NTSC-Bild besteht aus 480 Zeilen mit 720 Spalten, und wird mit 60 Hz
Bildrate aufgebaut. Der VIC (und auch der SID) läuft dann mit 975,5 kHz
Taktfrequenz. Wenn Sie allerdings in Deutschland leben, dann benutzen Sie
wahrscheinlich die PAL-Fernsehnorm (Phase Alternate Line
Imaging) mit 576 Zeilen und 720 Spalten, was dann dazu führt, dass Ihr SID mit 982,5
kHz getaktet wird. Wenn Sie es also sehr genau nehmen, dann müssen Sie die
Frequenztabelle für Ihre Noten noch entsprechend korrigieren, je nachdem,
welche Fernsehnorm Sie verwenden. Das nächste Listing tut genau dies: Es
erzeugt eine Frequenztabelle für Ihre Notenwerte (C/-1 ist mit 8,176 Hz der
tiefste Ton), und wählt für diese anschließend die PAL-Fernsehnorm aus (wenn
Sie einen amerikanischen NTSC-C64 verwenden, müssen Sie Zeile 11 entsprechend
anpassen). Anschließend wird der Song mit Notenwerten, anstatt mit
SID-Register-Werten abgespielt.
19-SIMPLESONG3
10 SI=54272:K=0:PRINT"[SHIFT+CLR/HOME]SPIELE
SONG"
11
FF=982.5:REM FREQUENZ DES CPU IN MHZ (PAL=982.5,
NTSC=975.5)
12 FF=16*(1000/FF)
15 DIM NT(110)
16 FOR I=0 TO 109:NT(I)=INT(FF*(8.176*2^(I/12))):NEXT I
20 POKE SI+24,15:REM
VOLLE LAUTSTAERKE
30 POKE SI+5,9:POKE
SI+6,0:REM KLAVIER
40 READ NO,L:IF NO<0
THEN END
45 NO=NT(NO)
50 POKE SI+1,NO/256:POKE SI,NO-256*INT(NO/256)
60 POKE SI+4,32:POKE
SI+4,33
70 FOR T=1 TO L
80 POKE 1064+(K/10),46:K=K+1
90 NEXT T
100
GOTO 40
110
DATA 71,25,73,25,71,25,71,25,71,12,73,12,75,25,71,25,73,25,66,25
120
DATA 66,25,66,25,68,12,70,12,71,25,70,12,66,25,-1,-1
Im Array NT (note table) stehen hier Werte
zwischen 0 und 65535, es wird also das Hi- und das Lo-Byte zu einem einzigen
Wert zusammengefasst. Natürlich muss dann das Hi-Byte und das Lo-Byte in Zeile
50 wieder getrennt, und anschließend korrekt in die SID-Register Nr. 0
und 1 geschrieben werden. Die Notentabelle selbst wird in Zeile 16
durch die Formel F(x)=a0*2I/12 berechnet (I ist
hier der Schleifenzähler), allerdings müssen die einzelnen Frequenzwerte noch
zusätzlich mit dem Faktor FF skaliert werden (der für einen PAL-C64 FF=1000/982.5
ist). In die Notentabelle passen 110 Frequenzwerte, danach ist der zulässige
Wertebereich von 0-65535 überschritten. Der Song selbst verwendet aus dieser
Tabelle allerdings nur die Oktaven zwischen C3 und C5.
6.3.2 Verwenden
mehrerer Stimmen
Das nächste Beispiel
spielt nun den Song aus dem letzten Beispiel mit mehreren Stimmen ab. Hierbei
sind sämtliche Noten sämtlicher Stimmen identisch, Ihr Song sollte also auf
diese Weise dreimal lauter klingen, als im Beispiel davor.
20-ESGEHTNICHT
10 SI=54272:K=0:SA=8192:PRINT"[SHIFT+CLR/HOME]SPIELE SONG"
11
FF=982.5:REM FREQUENZ DES CPU IN MHZ (PAL=982.5,
NTSC=975.5)
12
FF=16*(1000/FF)
15 DIM
NT(110):DIM NP(3):DIM NZ(3,2)
16 FOR I=0 TO 109:NT(I)=INT(FF*(8.176*2^(I/12))):NEXT I
20 POKE SI+24,15:REM VOLLE
LAUTSTAERKE
30 POKE SI+5,9:POKE
SI+6,0:REM STIMME 0
31 POKE SI+12,9:POKE
SI+13,0:REM STIMME 1
32 POKE SI+19,9:POKE
SI+20,0:REM STIMME 2
35 VO=0:NP(0)=8192
40 READ NO,L:IF NO<0
THEN GOTO 60
45 POKE SA,NO:POKE SA+1,L:SA=SA+2
60 POKE SA,255:POKE
SA+1,255:SA=SA+2
70 VO=VO+1:IF VO=3 THEN
GOTO 90
80
NP(VO)=SA:GOTO 40
90 CL=TI:REM COMPUTERUHR NACH CL UEBERTRAGEN
100 IF (TI-CL)=0 THEN GOTO 100
110 FOR I=0 TO 2
120 NZ(I,0)=NZ(I,0)+CL:IF NZ(I,0)>NZ(I,1) THEN NO=PEEK(NP(I)):VO=I: GOSUB 2000
130 NEXT I
140 IF NO=-1 THEN END
150 CL=TI:GOTO 100
1000 REM *** STIMME 0 ***
1010 DATA
71,25,73,25,71,25,71,25,71,12,73,12,75,25,71,25,73,25,66,25
1020 DATA
66,25,66,25,68,12,70,12,71,25,70,12,66,25,-1,-1
1030 REM *** STIMME 1 ***
1040 DATA
71,25,73,25,71,25,71,25,71,12,73,12,75,25,71,25,73,25,66,25
1050 DATA
66,25,66,25,68,12,70,12,71,25,70,12,66,25,-1,-1
1060 REM *** STIMME 2 ***
1070 DATA
71,25,73,25,71,25,71,25,71,12,73,12,75,25,71,25,73,25,66,25
1080 DATA 66,25,66,25,68,12,70,12,71,25,70,12,66,25,-1,-1
2000 REM *** NOTENSPIELER ***
2001 IF NO=255 THEN RETURN
2002 IF NO=0 THEN GOTO 2040:REM
0=PAUSE
2005 NO=NT(NO)
2010 OF=7*VO:REM OFFSET
FUER STIMME
2020 POKE SI+OF+1,NO/256:POKE SI+OF,NO-(256*INT(NO/256))
2030 POKE SI+OF+4,32:POKE
SI+OF+4,33
2040 NP(VO)=NP(VO)+1
2050 NZ(VO,1)=PEEK(NP(VO))
2060
NZ(VO,0)=0:NP(VO)=NP(VO)+1
2070
RETURN
Ich habe nun das letzte
Listing bewusst nicht ausgiebig kommentiert, und auch bewusst den Namen „es
geht nicht“ gewählt. Sie sollen auch das letzte Beispiel nur mit RUN
ausführen. Wundern Sie sich bitte nicht darüber, dass nach einiger Zeit ein
Song erklingt, bei dem die Noten der einzelnen Stimmen verzögert abgespielt
werden, obwohl sämtliche Stimmen durch die Computeruhr synchronisiert werden,
und auch alle die gleichen Noten enthalten. Versuchen Sie auch bitte, den
Fehler im Programm zu finden, und studieren den Code ausgiebig. Doch ich
versichere Ihnen: Wie Sie es auch anstellen, Sie bekommen es mit BASIC nicht
hin, die einzelnen Gate-Bits für Ihre Stimmen gleichzeitig einzuschalten, so,
dass Sie keine Verzögerung im Anschlag der Noten wahrnehmen. Dies liegt einfach
daran, dass der POKE-Befehl zu langsam arbeitet. Selbst, wenn Sie die
Computeruhr benutzen, um den Anschlagzeitpunkt für die einzelnen Noten zu
bestimmen, ändert dies nichts an der Tatsache, dass Sie allein mit BASIC keine
mehrstimmigen Stücke wiedergeben können. Auch hier brauchen Sie wieder
Maschinensprache-Routinen.
6.4 Wirklich synchrone
Stimmen
Wie schon gesagt,
können Sie wirklich synchrone Stimmen in einem Musikstück nur durch
Maschinensprache erreichen. Die Synchronisierung der Stimmen wird hier dadurch
erreicht, dass der Player an den Timer-Interrupt
gebunden wird, der normalerweise genau 60-mal in der Sekunde vom Kernal aufgerufen wird. Das heißt dann auch, dass der
Player bei jedem Aufruf immer nur ein kleines Stückchen Ihres Songs abspielt.
Da aber der Player so schnell hintereinander immer wieder aufgerufen wird,
klingt die Wiedergabe sehr flüssig, und die Stimmen sind dann auch synchron.
Dies heißt natürlich nun auch folgendes: Sie sind nun mit BASIC an die Grenze
des Machbaren gestoßen, und müssen Maschinensprache lernen, wenn Sie noch mehr
aus Ihrem SID herausholen wollen.