Dem Klang auf der Spur (Teil 10)
Als krönender Abschluß dieses Kurses wird das Programm Sound-Editor zu einem kompletten Synthesizer-Programm ergänzt.
In der heutigen und letzten Folge des Synthesizer-Projekts soll der in Teil 9 beschriebene Sequenzer in den Sound-Editor integriert werden. Zum Sequenzer gibt es im Gesamtprogramm ein eigenes Untermenü, von dem aus er gestartet und gestoppt werden kann. In diesem Untermenü kann man auch das Spieltempo und einige weitere Parameter interaktiv einstellen. Damit liegt jetzt ein komplettes Synthesizer-Programm vor, das von den vielen klanglichen Möglichkeiten her die kommerziell angebotenen Synthesizer-Programme für den C 64 übertreffen dürfte. Für die grafische Gestaltung gibt es natürlich schönere und aufwendigere Konzepte, diese waren aber auch nicht das Thema dieses Kurses.
Um zu dem erwähnten Komplettprogramm zu gelangen, benötigt man diesmal mehrere Teile aus verschiedenen Abschnitten des Kurses:
MODULATOR | (Teil 4) |
SOUND EDITOR | (Teil 6) |
SEQUENCER.OBJ | (Teil 8) |
REM TEXT KILLER | (Teil 10/Listing 1) |
SOUND.ED.ZUSATZ | (Teil 10/Listing 2) |
SEQ.ERG.OBJ | (Teil 10/Listing 3) |
Zunächst muß das Programm »Sound-Editor« erweitert werden. Da es den zur Verfügung stehenden Speicher ($0801 — $8FFF) fast vollständig belegt, muß es erst einmal verkürzt werden. Dies ist durch Entfernen der vielen Kommentartexte auch leicht möglich. Wer schon beim Eintippen des Programms die Kommentartexte weggelassen hat, spart sich natürlich jetzt diese Arbeit.
Zum Entfernen von REM-Zeilen gibt es zwar gute und schnelle Tools, diese sind aber für den Sound-Editor leider nicht geeignet, da die REM-Zeilen auch als Sprungziele auftreten. Dabei werden die Zeilennummern der Sprungziele meistens aus einer Tabelle entnommen.
Der »REM-Text-Killer« aus Listing 1 entfernt lediglich Kommentartexte und läßt dabei die Zeilennummern mit leeren REM-Anweisungen stehen. Das kurze Programm muß zum Sound-Editor eingetippt werden. Zeile 0 des Sound-Editors muß gelöscht werden. Anschließend gibt man POKE 253,1: POKE 254,8 (RETURN) im Direktmodus ein und startet den REM-Text-Killer mit RUN. Dann wird der REM-Text-Killer wieder gelöscht und Zeile 0 wieder eingegeben.
Nun kann Listing 2 (Sound, ed. Erg.) eingegeben werden. Listing 2 enthält sowohl Zeilen, die im alten Sound Editor geändert werden müssen, als auch neu hinzukommende Programmteile, »Sound. Ed. Erg.« ist für sich allein aber kein lauffähiges Programm. Beim Eintippen kann man natürlich die Kommentartexte gleich weglassen. Das entstehende Komplettprogramm sollte man dann sofort auf Disk speichern. Es kann getestet werden, wenn alle Maschinenprogramme und ein Musikdatensatz zur Verfügung stehen.
An Maschinenprogrammen werden nachgeladen:
- MODULATOR
- SEQUENCER.OBJ
- SEQ.ERG.OBJ
Bei dem letzten Programm handelt es sich um eine Ergänzung zum Sequenzer aus Teil 9. Diese Ergänzung, die mit dem MSE eingegeben werden muß (Listing 3), ist für den Betrieb zusammen mit dem Sound-Editor und mit »Modulator« erforderlich. Die technischen Einzelheiten wurden bereits in Teil 9 behandelt und sollen hier nicht mehr besprochen werden.
Um das bis jetzt aufgebaute System von Programmen zu testen, kann man den kleinen Musikdatensatz »Test.Song« (Tonleiter und Kadenz) aus MSE-Listing 4 verwenden.
Bedienung des Sequenzers
Der erweiterte Sound-Editor wird geladen und gestartet. Nach einer Initialisierungszeit von zirka 30 Sekunden meldet er sich mit dem Hauptmenü, von dem aus man mit »A« den Sequenzer erreichen kann. Parameter werden mit den Cursortasten angewählt und können mit den F-Tasten verändert werden. Solange allerdings kein Song (= Musikdatensatz) geladen worden ist, was in der Titelzeile angezeigt wird, kann der Sequenzer nicht gestartet werden.
Im Untermenü Disk (mit »D« zu erreichen) kann man mit »F2« den Musikdatensatz »Test.Song« laden und gelangt dann automatisch wieder in das Sequenzer-Untermenü. Der Sequenzer kann jetzt mit »F5« gestartet und mit »F3« wieder angehalten werden. »F1« setzt ihn an den Anfang des geladenen Songs zurück.
TEMPO
Über das Tempo-Feld kann man die Abspielgeschwindigkeit im Bereich von 40 bis 480 bpm (beats per minute) einstellen. Geschwindigkeiten über 300 bpm sind allerdings musikalisch kaum noch sinnvoll, sondern eher für Klangexperimente gedacht.
MODUS
Im Song-Modus wird das ganze Stück gemäß Sequenzfolgeliste gespielt. Der Sequenzmodus ermöglicht es dagegen, einzelne Sequenzen beliebig oft zu hören.
SEQNR
Im Sequenz-Modus erscheint hier die Nummer der gespielten Sequenz. Damit ist die aktuelle Position in der Sequenzfolgeliste gemeint und nicht die Nummer der Sequenz bei ihrer Definition. Es kann also unter verschiedenen Nummern die gleiche Sequenz mehrmals zu hören sein. So besteht zum Beispiel »Test.Song« aus zwei verschiedenen Sequenzen, einer Tonleiter und einer Kadenz. Die Tonleiter wird wiederholt und ist unter den Sequenznummern 1 und 2 zu hören. Die Kadenz ist unter Nummer 3 zu finden. Im Song-Modus hat das SEQNR-Feld keine Bedeutung.
SOFT-EG
Die Funktion entspricht der schon bekannten, mit Shift-Space erreichbaren Kopplung des Soft-EG an das Tastenfeld-Spiel.
SUSTAIN
Die Sustain-Funktion ist auch beim Sequenzer wirksam. Sie verhindert das Rücksetzen der GATE-Bits der drei Stimmen. Dadurch klingen die Töne ohne Lautstärke-Dynamik durchgehend auf dem Sustain-Pegel. Während der Sequenzer läuft, bleiben alle anderen Funktionen des Sound-Editors voll erhalten. Man kann also an einem laufenden Musikstück Änderungen an der Klangeinstellung testen. Lediglich bei Diskettenoperationen wird der Sequenzer unterbrochen.
Mit dem Sequenzgenerator (Listing 5) kann man relativ einfach Song-Dateien erzeugen, die die für den Sequenzer erforderliche Struktur haben. Das Programm erzeugt aus einer in DATA-Zeilen abgelegten Folge von Noten einen Datensatz mit der in Folge 9 beschriebenen Zeigerstruktur. Die Daten werden direkt hinter die Soundparameter ($9000 bis $9A07) also ab $9A08 gespeichert. Das geschieht in der Reihenfolge:
- Zeiger auf Sequenzfolgeliste
- Eine oder mehrere Sequenzen, bestehend aus jeweils 1 bis 3 Tracks
- Sequenzfolgeliste (sie enthält die Startadressen der Sequenzen in der Reihenfolge, in der sie abgespielt werden sollen).
Es steht der Speicherbereich bis $BFFF, also auch der RAM-Bereich $A0000 bis $BFFF unter dem Basic-ROM zur Verfügung. Da sich POKE-Befehle immer auf das RAM beziehen, sind dazu keine weiteren Maßnahmen notwendig. Programmteile, die auf diesen Bereich lesend zugreifen, sorgen selbständig für das erforderliche Umschalten zwischen RAM und ROM. Auf Editierfunktionen wurde bei diesem Programm verzichtet. Da die Noten in DATA-Zeilen geschrieben werden, kann man diese wie Basic-Programme mit dem Bildschirm-Editor behandeln.
Syntax der Notendaten
Ein Song ist in einzelne Sequenzen gegliedert, welche wiederum in bis zu drei Tracks (= Tonspuren) zerfallen. Den Aufbau macht man sich am besten anhand von Listing 5, Zeilen 8000-8820, klar. Dort steht das schon in Teil 9 als Hex-Dump abgedruckte Musikstück. Jenes ist allerdings für den Sequenzer in der jetzigen Form nicht weiter verwendbar, da es nicht im Speicherbereich ab $9A08 liegt. Die Zeilen 8100 bis 8130 beschreiben eine Sequenz, in der nur Stimme 3 programmiert wird. Der Track besteht dabei aus sieben Viertelnoten.
Sequenzen müssen mit SEQUENZ, (laufende Nummer) eingeleitet werden. Die laufende Nummer muß im Bereich 1 bis 200 liegen.
Innerhalb einer Sequenz wird mit TRACK, (1, 2 oder 3) angegeben, welcher Stimme die nachfolgenden Noten zuzuordnen sind. Man kann in einer Sequenz auch weniger als drei Tracks programmieren. Allen nicht programmierten Tracks ordnet das Generatorprogramm einen 4 Byte langen Dummy-Track zu, der nur aus einer Pause besteht und der nur einmal im Speicher stehen muß.
Innerhalb eines Tracks sind dann folgende Daten zulässig: a — b stellt das Verhältnis zwischen GATE-ON- und GATE-0FF-Zeit ein. Voreinstellung ist 1-1, das heißt, beide Zeiten sind gleich lang. Bei 1-0 hat die GATE-ON-Zeit die maximale Länge, bei 0-1 werden die Noten nur sehr kurz angeschlagen. Die Einstellung hat keinen Einfluß auf die Gesamtlänge der Noten. Diese wird mit a / b eingestellt. Beispiele sind:1/1 ganze Note
- 1/2 halbe Note
- 1/4 Viertelnote
- 1/6 Vierteltriole
- 3/8 punktierte Viertelnote
- 1/8 Achtelnote
- 1/12 Achteltriole
Die Längenangabe bezieht sich auf alle Noten bis zur nächsten Längenangabe.
Als Notennamen werden die üblichen Bezeichnungen C,D,E,F,G,A,H verwendet. Die Notennamen können mit»#« (zum Beispiel F# = Fis) zur Erhöhung um einen Halbton oder mit »B« (zum Beispiel EB = Es) zur Erniedrigung um einen Halbton ergänzt werden. Die Notennamen müssen mit einer Oktavnummer zwischen 0 und 6 versehen sein. Der Kammerton a mit 440 Hz hat in dieser Schreibweise den Namen A3.
P kennzeichnet eine Pause, für die ebenfalls die Längeneinstellung a/b gilt.
SEQUENZFOLGE, n1, n2, n3…0. Diese Anweisung darf an beliebiger Stelle stehen und muß einmal vorhanden sein. Sie stellt die schon erwähnte Sequenzfolgeliste dar, von der die Abspielreihenfolge der Sequenzen gesteuert wird. Die Sequenzen n1, n2 und so weiter müssen natürlich definiert werden. Die Liste wird mit einer 0 abgeschlossen.
ENDE schließt den Datensatz ab.
Das Generatorprogramm weist fehlerhafte Daten mit Fehlermeldungen zurück. Am Ende eines Generatorlaufs kann man den erzeugten Datensatz speichern. Dieser kann dann direkt vom erweiterten Sound-Editor geladen werden.
Mit der Integration des Sequenzers in den Sound-Editor findet dieser Kurs seinen Abschluß. Dem an Computermusik interessierten Anwender stehen nun leistungsfähige Programme zum Experimentieren und zur Realisierung seiner Ideen zur Verfügung. Der erfahrene Programmierer wird vielleicht das eine oder andere hier veröffentlichte Programm seinen Bedürfnissen anpassen können. Über eine Resonanz von seiten der Leser zum Beispiel in Form von Sound- oder Song-Dateien oder Ideen zur Erweiterung oder Verbesserung der Programme würde sich der Autor freuen.
(Thomas Krätzig/tr)15 ad=peek(253)+256*peek(254) 16 ll=peek(ad):lh=peek(ad+1) 17 poke 253,ll:poke 254,lh 18 ad=ll+256*lh 19 ll=peek(ad):lh=peek(ad+1) 20 if ll=0 and lh=0 then end 21 if peek(ad+4)<>143 then 17 22 zn=peek(ad+2)+256*peek(ad+3) 23 print chr$(145);zn;chr$(157);" rem" 24 print " run " 25 poke 631,145:poke 632,145 26 poke 633,145:poke 634,145 27 poke 635,13 :poke 636,13 28 poke 198,6 29 end
1068 if m=7.5 then 7960 1080 if a=0 then a=.1:load"modulator",8,1 1082 if a=.1 then a=.2:load"sequencer.obj",8,1 1084 if a=.2 then a= 1:load"seq.erg.obj",8,1 1160 dim v%(8,255),tn%(255),th(24) 3358 poke 50334+sn,c(sn) 3400 rem------------------------------- 3405 rem tempo 3410 if pw<40 then pw=40 3415 if pw>480 then pw=480 3420 te=pw:sys do,pa,int(6e7/(24*te)) 3425 sys pr,6,11,f2$;right$(str$(pw),3) 3430 return 3840 poke 50344,-su:if su then print chr$(18); 4132 sys pr,13,1,f2$;" a";f1$;" sequencer" 7429 : poke 50334+i,c(i) 7532 sys pr,6,4,f2$;"f2" 7534 sys pr,6,7,f1$;"song laden" 7632 sys 50198:gosub 2140:rem mod/seq aus 7670 ns=-1:a=211 7675 sys do,56326,int(6e7/(24*te)) 7680 if sr then sys 51093:goto 1550 7685 sys mo+1033:goto 1550 7735 sys 50198:gosub 2140:remk mod/seq aus 7805 sys do,56326,int(6e7/(24*te)) 7810 if sr then sys 51093:return 7815 sys 50185:return:rem nur mod. an 7900 rem------------------------------- 7905 rem song laden 7910 sys pr,6,7,f2$;"song laden" 7915 sys pr,10,4,f1$;"dateiname "; 7920 sys pr,10,14,;:input dn$ 7925 sys 50966:gosub 2140 7930 open 8,8,8,dn$+",p,r":close 8 7935 open 1,8,15:input#1,a,a$,x,y:close 1 7940 sys pr,12,4," " 7945 if a=0 then 7955 7950 sys pr,12,3,a;a$;x;y:goto 7920 7955 a=0:m=7.5:load dn$,8,1 7960 sys pr,6,7,f1$;"song laden" 7965 m=7:sq=-1 7970 sys do,56326,int(6e7/(24*te)) 7975 sys do,50310,usr(39432) 7980 sys 51001 :rem seq/mod start/init 7985 if not sr then sys 50966 7990 a=65:goto 1550 8130 on l goto 8150,8220,8280,8310,8310 8180 for i=0 to 8:v%(i,a)=zn:next 8400 : for j=0 to 8:v%(j,a)=2000:next 8482 so=36864:ci=56320 8578 : poke 50334+sn,c(sn) 8627 poke 50303,0 :rem eg abkoppeln(seq) 8632 te=120 :rem tempo 8920 sq=0:sm=0:sr=0:a=2:return 9530 data ma,3700,160,3800,s,9990,a 9750 data m7,7600,133,7700,134,7900,137 9800 data m8,10420,029,10450,157 10000 rem============================== 10010 rem untermenue sequencer 10020 rem============================== 10030 m=8:pv=0:sys cl:print"{home}{down}";f1$; 10040 print" sequencer"; 10045 if not sq then print" (kein song vorhanden)" 10050 sys pr,4,10,"tempo modus seqnr soft-eg" 10060 sys pr,5,4,"{CBM-A}CCCC{CBM-R}CCCCC{CBM-R}CCCCC{CBM-R}CCCCC{CBM-R}CCCCCCCCC{CBM-S}" 10070 sys pr,6,4,"B B B B B 1 2 3 B" 10080 sys pr,7,4,"{CBM-Z}CCCC{CBM-E}CCCCC{CBM-E}CCCCC{CBM-E}CCCCC{CBM-E}CCCCCCCCC{CBM-X}" 10090 sys pr,9,1,f2$;"f1";f1$;" reset ++ 1" 10100 sys pr,10,1,f2$;"f3";f1$;" stop + song + 2" 10110 sys pr,11,1,f2$;"f5";f1$;" run - seq - 3" 10120 sys pr,12,1,f2$;"f7";f1$;" --" 10130 if sr then 10150 10140 sys pr,6,5,"stop":goto 10160 10150 sys pr,6,5,"run " 10160 sys pr,6,11,right$(str$(te),3) 10170 if sm then 10190 10180 sys pr,6,16,"song":goto 10210 10190 sys pr,6,16,"seq " 10200 sys pr,6,22,right$(" "+str$(se),3) 10210 rem 10220 x=peek(50303) 10230 if x and 1 then sys pr,6,28,"{rvon} 1 " 10240 if x and 2 then sys pr,6,31,"{rvon} 2 " 10250 if x and 4 then sys pr,6,34,"{rvon} 3 " 10300 av=10500:sys gs,av:return 10400 rem------------------------------ 10410 rem parameterwahl durch cursor 10420 rem rechts 10430 av=av+100:if av>10900 then av=10500 10440 goto 10470 10450 rem links 10460 av=av-100:if av<10500 then av=10900 10470 for i=pb to pb+8:poke i,f1:next 10480 sys gt,av 10500 rem------------------------------ 10510 rem run/stop waehlen 10520 pb=fa+245 10530 for i=pb to pb+3:poke i,f2:next 10540 pa=ci+12:pv=11000:return 10600 rem------------------------------ 10610 rem tempo waehlen 10620 pb=fa+250 10630 for i=pb to pb+3:poke i,f2:next 10640 pa=ci+6:pw=te 10650 pm=500:p1=1:p2=10:pv=3400:return 10700 rem------------------------------ 10710 rem modus waehlen 10720 pb=fa+256 10730 for i=pb to pb+3:poke i,f2:next 10740 pa=50345:pw=peek(pa) 10750 pv=11100:return 10800 rem------------------------------ 10810 rem seqnr waehlen 10820 pb=fa+262 10830 for i=pb to pb+2:poke i,f2:next 10840 pw=se:p1=1:p2=1:pm=1000 10850 pv=11200:return 10900 rem------------------------------ 10910 rem soft-eg waehlen 10920 pb=fa+268 10930 for i=pb to pb+8:poke i,f2:next 10940 pa=50303:pv=11300:return 11000 rem------------------------------ 11010 rem sequencer reset/stop/run 11015 if not sq then return 11020 if a<133 or a>135 then return 11030 on a-132 goto 11040,11060,11080 11040 rem reset 11042 if sm then sys 51116:return 11045 sys 51001 11050 if not sr then sys 50966 11055 return 11060 rem stop 11065 sys 50966:sys pr,6,5,f2$;"stop" 11070 sr=0:if not su then 2140 11075 return 11080 rem run 11085 sys 51093:sys pr,6,5,f2$;"run " 11090 sr=-1:return 11100 rem------------------------------ 11110 rem sequencer-modus (song/seq) 11112 if not sq then return 11115 if a=134 then 11130 11120 if a=135 then 11150 11125 return 11130 rem song-modus 11135 sm=0 :poke pa,0 11140 sys pr,6,16,f2$;"song" 11145 sys pr,6,22," ":return 11150 rem sequenz-modus 11155 sm=-1:poke pa,1:sys pr,6,16,"seq " 11160 sys pr,6,16,f2$;"seq " 11165 se=(usr(50312)-usr(50310))/3+1 11170 sys pr,6,22,f1$;right$(" "+str$(se),3) 11175 return 11200 rem------------------------------ 11210 rem sequenz-nummer 11220 if not sm then return 11230 if pw=0 then pw=1:return 11240 ad=usr(50310)+(pw-1)*3 11250 if usr(ad)=0 then pw=pw-1:return 11260 sys do,50312,ad:se=pw 11270 sys do,50314,usr(ad) 11280 sys 51116 :rem nextseq 11285 sys pr,6,22,f2$;right$(" "+str$(se),3) 11290 return 11300 rem------------------------------ 11310 rem sequencer soft-eg-steuerung 11320 x=peek(pa) 11330 for i=0 to 2 11340 : if a<>133+i then 11420 11350 : y=2^i 11360 : if (x and y) then 11400 11370 : x=x or y:poke pa,x 11380 : sys pr,6,28+3*i,f2$;"{rvon}";i+1;"{left} " 11390 : goto 11420 11400 : x=x and (255-y):poke pa,x 11410 : sys pr,6,28+3*i,f2$;i+1;"{left} " 11420 next i:return
PROGRAMM : SEQ.ERG.OBJ C739 C855 ----------------------------------- C739 : 78 A5 FE 48 A5 FF 48 A9 1B C741 : EA A2 02 9D 35 C5 9D A2 EE C749 : C5 BD 92 C7 9D 5B C6 CA F0 C751 : 10 F1 AD 86 C4 8D 88 C4 FA C759 : AD 87 C4 8D 89 C4 20 ED C8 C761 : C5 A9 E2 8D D7 C4 A9 C3 37 C769 : 8D D8 C4 A9 1E 8D 14 03 6D C771 : A9 C5 8D 15 03 A9 D7 8D FB C779 : D3 C4 A9 C7 8D D4 C4 A9 F7 C781 : 82 8D 0D DC A9 11 8D 0F 20 C789 : DC 68 85 FF 68 85 FE 58 5A C791 : 60 4C BE C7 78 A9 1E 8D 28 C799 : 14 03 A9 C5 8D 15 03 A9 33 C7A1 : 82 8D 0D DC A9 11 8D 0F 40 C7A9 : DC 58 60 78 A5 FE 48 A5 97 C7B1 : FF 48 20 ED C5 68 85 FF 50 C7B9 : 68 85 FE 58 60 AE 83 C4 C2 C7C1 : BD A1 C4 2C 7F C4 F0 08 F7 C7C9 : AD 48 C0 29 FE 8D 48 C0 EF C7D1 : AD A8 C4 4C 5E C6 AE 85 6F C7D9 : C4 9D 00 C0 A5 FE 9D 01 4F C7E1 : C0 A5 FB 48 A5 FC 48 A5 2A C7E9 : FD 48 86 FE 20 6A C2 AE 49 C7F1 : 83 C4 BD 18 C0 D0 12 AE 81 C7F9 : 85 C4 BD 05 C0 9D 00 D4 93 C801 : BD 06 C0 9D 01 D4 4C 2C E5 C809 : C8 20 43 C2 AE 85 C4 BD B0 C811 : 06 C0 85 FD 20 A6 C0 AE 30 C819 : 85 C4 18 BD 05 C0 65 FB A2 C821 : 9D 00 D4 BD 06 C0 65 FC A1 C829 : 9D 01 D4 AE 83 C4 BD A1 EA C831 : C4 2C 7F C4 F0 08 AD 48 1A C839 : C0 09 01 8D 48 C0 BD 9E 2F C841 : C4 09 01 AE 85 C4 9D 04 9D C849 : D4 68 85 FD 68 85 FC 68 EA C851 : 85 FB 60 00 CF
PROGRAMM : TEST.SONG 9A08 9A5D ----------------------------------- 9A08 : 4E 9A 01 61 EF 00 00 15 39 9A10 : 9A 0A 9A 0A 9A 06 67 B0 70 9A18 : B2 B4 B5 B7 B9 BB C0 C0 87 9A20 : BA B8 B7 B5 B3 B2 B0 00 6E 9A28 : 2E 9A 38 9A 43 9A 12 67 25 9A30 : C0 C0 C0 BB 48 79 C0 00 4B 9A38 : 12 67 B7 B9 24 6D B7 48 40 9A40 : 79 B7 00 12 67 B4 B5 24 12 9A48 : 6D B2 48 79 B4 00 0F 9A 0C 9A50 : 00 0F 9A 00 28 9A 00 00 D6 9A58 : 00 00 00 01 08 20
1000 rem ****************************** 1010 rem * * 1020 rem * leseprogramm * 1030 rem * fuer sequencer-daten * 1040 rem * * 1050 rem * erzeugt aus data-zeilen * 1060 rem * einen absolut ladbaren * 1070 rem * datensatz fuer sequencer * 1080 rem * * 1090 rem * thomas kraetzig sep 85 * 1100 rem * * 1110 rem ****************************** 1120 rem 1130 rem konstanten und 1140 rem variablen mit vorbesetzungen 1150 rem 1160 dim sf(200) :rem sequenzfolgeliste 1170 dim sa(200) :rem sequenzadressenl. 1180 dim ta(3) :rem trackadressen 1190 rem notennamen 1200 nn$="ccddeffggaah" 1210 dn$="" :rem dateiname 1220 s=1 :rem aktuelle sequenz 1230 t=1 :rem aktueller track 1240 ad=0 :rem allg. adresse 1250 sa=39432 :rem $9a08 1260 q=96 :rem quantisierung 1270 r=0.5 :rem gate on/laenge 1280 nl=1/4 :rem notenlaenge 1290 l=0 :rem stringlaenge 1300 sv=12*4096 :rem save-routine 1500 rem------------------------------- 1510 rem programmstart 1520 rem------------------------------- 1530 rem save-maschinenprogramm 1540 for i=0 to 17 1550 read x:poke sv+i,x:next i 2020 rem 2030 rem dummy-track erzeugen 2040 ad=sa+2 2050 poke ad ,1 :poke ad+1,97 2060 poke ad+2,239:poke ad+3,0 2070 h0=int(ad/256):l0=ad-256*h0 2080 ad=ad+4:a$="" 2100 rem naechstes datum lesen 2110 print" ";a$;:read a$ 2120 l=len(a$):l$=left$(a$,1) 2150 if a$="p" then 3100 2160 if a$="pause" then 3100 2165 if a$="track" then 3800 2170 if a$="sequenz" then 4000 2175 if a$="sequenzfolge" then 3600 2190 if a$="ende" then 4300 2200 rem 2210 rem nach notennamen suchen 2220 rem 2230 if l=1 then 5000 2240 if l>3 then 2500 2250 n=1 2260 if l$=mid$(nn$,n,1) then 2300 2270 n=n+1:if n<13 then 2260 2280 goto 2500 :rem keine note 2300 r$=right$(a$,1):o=val(r$) 2310 if o=0 and r$<>"0" then 2420 2320 if o>6 then 2420 2330 if l=2 then 3000 2340 m$=mid$(a$,2,1) 2350 if m$="#" then n=n+1:goto 3000 2360 if m$="b" then n=n-1:goto 3000 2380 f$="nur "+l$+"#"+r$+" oder "+l$+"b"+r$ 2390 f$=f$+" oder "+l$+r$+" moeglich" 2400 goto 5000 2420 f$="oktavbereich 0-6" 2430 goto 5000 2500 rem 2510 rem nach / oder - suchen 2520 rem 2530 m$=mid$(a$,2,1) 2540 if m$<>"/" and m$<>"-" then 2560 2550 r$=right$(a$,l-2):goto 2600 2560 m$=mid$(a$,3,1) 2570 if m$<>"/" and m$<>"-" then 5000 2580 l$=left$(a$,2):r$=right$(a$,l-3) 2600 rem 2610 rem zaehler und nenner untersuchen 2620 rem 2630 z=val(l$) 2640 if z>0 and z<99 then 2670 2650 f$="zaehler-bereich 1 bis 99" 2660 goto 5000 2670 n=val(r$) 2680 if n>0 or m$="-" then 2710 2690 f$="nenner muss groesser 0 sein" 2700 goto 5000 2710 if m$="/" then 3200 :rem zeit 2720 if m$="-" then 3400 :rem on/off 2730 goto 5000 3000 rem------------------------------- 3010 rem note (tonnummer n oktave o) 3020 rem------------------------------- 3030 if n=0 then n=12:o=o-1 3040 if n=13 then n=1 :o=o+1 3050 poke ad,128+16*o+n-1:ad=ad+1 3060 goto 2110 3100 rem------------------------------- 3110 rem pause 3120 rem------------------------------- 3130 poke ad,239:ad=ad+1:goto 2110 3200 rem------------------------------- 3210 rem zeit (notendauer) 3220 rem------------------------------- 3230 t=int(q*z/n) :rem gesamtzeit 3240 an=int(r*q*z/n):rem gate-on-zeit 3250 of=t-an :rem gate-off-zeit 3260 if an<=96 then 3290 3270 f$="gate-on-zeit zu gross" 3280 goto 5000 3290 if of<=30 then 3320 3300 f$="gate-off-zeit zu gross" 3310 goto 5000 3320 poke ad,an:ad=ad+1 3330 poke ad,of+97:ad=ad+1 3340 goto 2110 3400 rem------------------------------- 3410 rem verhaeltnis gate-on/gesamtzeit 3420 rem (z/n = gate on/gate-off) 3430 rem------------------------------- 3440 r=z/(z+n):goto 2110 3600 rem------------------------------- 3610 rem sequenzfolge 3620 rem------------------------------- 3630 print:print:as=0 3640 print" ";a$;:reada$:a=int(val(a$)) 3650 if a=0 then 3670 3660 sf(as)=a:as=as+1:goto 3640 3670 if a$<>"0" then 3690 3680 print" 0":read a$:goto 2120 3690 f$="liste muss mit 0 abgeschlossen sein" 3700 goto 5000 3800 rem------------------------------- 3810 rem track 3820 rem------------------------------- 3830 print:print:print a$; 3840 read a$:a=int(val(a$)) 3850 if a>=1 and a<=3 then 3880 3860 f$="nur 1,2,3 zulaessig" 3870 goto 5000 3880 print a:t=a 3890 if ns then ns=0:goto 3910 3900 poke ad,0:ad=ad+1 3910 hi=int(ad/256):lo=ad-256*hi 3920 poke sa(s)+(t-1)*2,lo 3930 poke sa(s)+(t-1)*2+1,hi 3940 read a$:goto 2120 4000 rem------------------------------- 4010 rem sequenz 4020 rem------------------------------- 4030 print:print:print:print a$; 4040 read a$:a=int(val(a$)) 4050 if a>=1 and a<=200 then 4080 4060 f$="nur 1-200 zulaessig" 4070 goto 5000 4080 print a:s=a 4090 poke ad,0:ad=ad+1:ns=-1:sa(s)=ad 4100 rem track-zeiger auf dummy-track 4110 rem initialisieren 4120 poke ad ,l0:poke ad+1,h0 4130 poke ad+2,l0:poke ad+3,h0 4140 poke ad+4,l0:poke ad+5,h0 4150 ad=ad+6 4160 read a$:goto 2120 4300 rem------------------------------- 4310 rem ende 4320 rem sequenzfolgeliste aufbauen 4330 rem------------------------------- 4340 print:print a$ 4350 poke ad,0:ad=ad+1 4360 hi=int(ad/256):lo=ad-256*hi 4370 poke sa,lo:poke sa+1,hi 4380 for i=0 to as-1 4390 : x=sa(sf(i)):if x>0 then 4410 4400 : print"sequenz";i;"nicht definiert":goto 4420 4410 : hi=int(x/256):lo=x-256*hi 4420 : poke ad+3*i,lo 4430 : poke ad+3*i+1,hi 4440 : poke ad+3*i+2,0 4450 next i 4460 for i=0 to 2:poke ad+3*as+i,0:next 4470 ad=ad+3*as+3 4500 rem 4510 rem bereich sa-ad auf disk 4520 rem 4530 input"abspeichern (j/n) ";a$ 4540 if a$<>"j" then end 4550 input"dateiname ";dn$ 4560 ah=int(sa/256):al=sa-256*ah 4570 eh=int(ad/256):el=ad-256*eh 4580 open 1,8,1,dn$ 4590 poke 252,al:poke 253,ah 4600 poke 780,252 4610 poke 781,el:poke 782,eh 4620 sys sv:close 1 4630 end 5000 rem------------------------------- 5010 rem fehler 5020 rem------------------------------- 5030 print:print 5040 print"fehlerhaftes datum: ";a$ 5050 if f$<>"" then print f$ 5060 print:a$="":f$="":goto 2110 7000 rem------------------------------- 7010 rem save-routine 7020 rem------------------------------- 7030 data 072,165,001,041,254,133,001 7040 data 104,032,216,255,165,001,009 7050 data 001,133,001,096 8000 rem------------------------------- 8010 rem musikstueck 8020 rem (edvard grieg kobold) 8030 rem------------------------------- 8100 data sequenz,1 8110 data track,3 8120 data 1-1,1/4,eb1,hb1,eb1,hb0 8130 data eb1,hb1,eb1 8140 rem 8200 data sequenz,2 8210 data track,1 8220 data 1-0,1/4,p,1/8,hb3,3/8,p 8230 data 1/8,hb3,3/8,p,1-1,1/4 8240 data hb3,gb3,gb3,hb3,a3,f3,f3,a3 8250 data 1/2,a3,p,1-0 8260 data 1/8,eb4,3/8,p,1/8,eb4,3/8,p 8270 data 1-1,1/4,eb4,cb4,cb4,eb4 8280 data d4,hb3,hb3,d4,1-0,1/4,d4 8290 data 1/2,p 8300 data track,2 8310 data 1-0,1/8,eb3,f3 8320 data gb3,f3,eb3,f3,gb3,f3,eb3,f3 8330 data 1-1,1/4,gb3,eb3,eb3,gb3 8340 data f3,c3,c3,f3,1/2,f3,1/4,p 8350 data 1-0,1/8,ab3,hb3 8360 data cb4,hb3,ab3,hb3 8370 data cb4,hb3,ab3,hb3,1-1,1/4 8380 data cb4,ab3,ab3,cb4,hb3,f3,f3,hb3 8390 data 1/2,hb3,1/4,p 8400 data track,3 8410 data 1-1,1/4,hb0,eb1,hb1,eb1 8420 rem 8430 data sequenz,3 8440 data track,1 8450 data 1-1,1/4,p 8460 data db5,p,db5,p,ab4,p,ab4,p 8470 data gb4,p,gb4,p,db4,p,db4 8480 data track,2 8490 data 1-0,1/8 8500 data gb4,ab4,hb4,ab4,gb4,ab4,hb4,p 8510 data db4,eb4,f4,eb4,db4,eb4,f4,p 8520 data cb4,db4,eb4,db4,cb4,db4,eb4,p 8530 data gb3,ab3,hb3,ab3,gb3,ab3,hb3,p 8540 data track,3 8550 data 1-0,1/4,p,1/2 8560 data gb3,p,db3,p,cb3,p,gb2,1/4,p 8570 rem 8600 data sequenz,4 8610 data track,1 8620 data 1-0,1/4,p,1/1,p,p,p,p,1/8 8630 data hb1,f2,hb2,hb2,f3,hb3,hb3,f4 8640 data 1/4,hb4,3/4,p 8650 data track,2 8660 data 1-0,1/8,f2,gb2,ab2,gb2,f2,p 8670 data gb2,f2,eb2,p,f2,eb2,db2,p 8680 data eb2,db2,c2,p,1/1,p,1/8 8690 data eb2,db2,cb2,p,1/2,p 8700 data 1/1,f1,1/4,f4,3/4,p 8710 data track,3 8720 data 1-0,1/8,f1,gb1,ab1,gb1,f1,p 8730 data gb1,f1,eb1,p,f1,eb1,db1,p 8740 data eb1,db1,c1,p,1/1,p,1/8 8750 data eb1,db1,cb1,p,1/2,p 8760 data 1/1,f0,1/4,f2,3/4,p 8800 rem 8810 data sequenzfolge,1,2,2,3,3,4,0 8820 data ende