Der Kaffee steht bereit, der Synthesizer ist an, und das heissgeliebte, selbstgeschriebene MIDI-Programm flimmert grau in grau über die Mattscheibe des ATARI ST. Die MIDI-Messages purzeln munter auf dem Bildschirm herum, die Finger glühen bei jedem Tastendruck auf dem Keyboard. Doch - o Schreck - ein paar Bömbchen zieren den Bildschirm, gerade zu dem Zeitpunkt, als man glaubte, alle Fehler beseitigt zu haben. Da hat sich doch wohl nicht der MIDI-Schalk im Kabel versteckt?? Na warte...
Doch wie das leider so ist, verstreicht Stunde um Stunde, und der Fehler bleibt unentdeckt. Programmiererschicksal! "Ja wie denn, es gibt da so ein Programm, das mir weiterhelfen kann? Was?? 50 Mark? Nee. das ist mir zu teuer. Da helfe ich mir lieber selbst!"
Damit Sie davon verschont bleiben und nicht kostbare Zeit damit verplempern, ein Progrämmchen zu entwickeln, das Ihnen zeigt, was im Lande MIDI so vor sich geht, habe ich Ihnen die Arbeit abgenommen. Dieser MINI-MIDI-MONITOR (ab sofort MMM genannt), zeigt Ihnen übersichtlichst alle am ATARI eingehenden MIDI-Daten an und erlaubt Ihnen sogar, alles Unwichtige mittels Filtern auszublenden (einfach einen Filter per Mausklick einschalten und von nun an werden Sie nichts mehr von dieser Nachricht am Bildschirm sehen).
Doch bevor ich zum Wesentlichen. nämlich dem Listing zum MMM. komme, sollte ich zum Verständnis noch ein paar Worte über MIDI verlieren. Nein. Sie müssen nicht wieder das langweilige, ausführliche Geschreibe über die Schnittstelle MIDI im allgemeinen und besonderen über sich ergehen lassen, vielmehr möchte ich kurz einen Überblick über alle MIDI-Nachrichten (Messages) geben.
Die MIDI-Daten werden grob in 2 Untergruppen aufgeteilt:
a) Voice-Nachrichten (Note on usw.)
b) Modus-Nachrichten
a) Common (Song Position usw.)
b) Real_Time (Clock usw.)
c) System Exclusive
Alle MIDI-Daten werden nach dem gleichen Prinzip übertragen:
Die diesem Status folgenden Bytes werden Daten-Bytes genannt. Im Normalfall sind dies maximal zwei, wobei die Anzahl der Daten-Bytes für die einzelnen Nachrichtenarten fest vorgeschrieben ist. Eine Ausnahme bildet die System Exclusive-Nachricht. die für die freie Übertragung von Synthesizer-Systemdaten (Sounddaten etc.) gedacht ist.
Da man die Länge einer solchen Nachricht nicht Voraussagen kann (ein Sampler überträgt ja mehr Daten als ein kleiner Analogsynthesizer), muß man den Beginn und das > Ende der Daten markieren. Dies geschieht durch die Status-) Befehls-)Bytes:
START : F0 (hex) ENDE : F7 (hex)
Um nicht zu viele Worte über all dies zu verlieren, komme ich nun zur Übersicht über alle MIDI-Messages: (siehe Tabelle 1).
Das n im Status-Byte der Channel-Nachrichten stellt den MIDI-Kanal (0..F). auf dem diese Nachricht gesendet wird, dar. LSB, MSB bedeuten Last Significant Byte und Most Significant Byte, d.h. ein in zwei Bytes aufgespaltenes Datum.
Die "-" bezeichnen ein fehlendes Daten-Byte.
Die Modus-Nachrichten sind weiter aufgeteilt in verschiedene Unterbefehlsgruppen (siehe Tabelle 2).
Der MONO- und der POLY-Modus schließen sich gegenseitig aus. d.h. MONO ON bedeutet gleichzeitig POLY OFF und umgekehrt. Der Vollständigkeit halber sollte ich noch auf eine Variante der CHANNEL-Nachrichten hinweisen: Manche Synthesizer lassen aus Geschwindigkeitsgründen das Status-Byte bei schnell aufeinanderfolgenden gleichartigen Nachrichten weg. Werden z.B. 20 NOTE ON-Nachrichten über MIDI verschickt, wird nur das erste Status-Byte gesendet, danach folgen nur noch die Daten-Bytes. Diesen Status nennt man bezeichnenderweise RUNNING-STATUS.
So, ich hoffe, ich habe nichts vergessen, so daß wir “stande pede" zur Beschreibung vom MMM schreiten können.
Status hex | 1. Datenbyte | 2. Datenbyte | Beschreibung |
---|---|---|---|
8n | Tonhöhe | Release | NOTE OFF |
9n | Tonhöhe | Lautstärke | NOTE ON |
9n | Tonhöhe | "Druck" | POLY PRESSURE |
An | Parameter | Wert | CONTROLLER (Regler. ) |
Bn | Prog.Nr. | - | PROGRAM CHANGE |
Cn | “Druck" | - | CHANNEL PRESSURE |
Dn | LSB | MSB | PITCH BENDING |
CHANNEL-MODUS-NACHRICHTEN
En | Modus | Omni | MODUS-Nachrichten |
SYSTEM-NACHRICHTEN
F0 | bei. Datenbytes | SYSTEM EXCL. ON | |
F1 | Undefiniert | ||
F2 | LSB | MSB | SONG-POSITION |
F3 | Wert | - | SONG-SELECT |
F4 | Undefiniert | ||
F5 | Undefiniert | ||
F6 | - | - | TUNE-REQUEST |
F7 | EOX (s o.) | ||
F8 | - | - | TIMING-CLOCK |
F9 | Undefiniert | ||
FA | - | - | TAPE START |
FB | - | - | TAPE CONTINUE |
FC | - | - | TAPE STOP |
FD | Undefiniert | ||
FE | - | - | ACTIVE SENSING |
FF | - | - | SYSTEM RESET |
Tabelle 1
Der MINI-MIDI-MONITOR MMM ist vollkommen in GFA-BASIC 3.0 geschrieben. Betrachten Sie dazu das zugehörige Listing 1.
MMM besteht aus einem Installationsteil, zu dem die Prozeduren
aufbau_screen
init_buffer
clear_midi_buffer
gehören: einer Hauptprogrammschleife, die sich um die Abfrage der MIDI-Schnittstelle und der Maus kümmert: und zwei Bearbeitungsroutinen für die SYSTEM-Nachrichten.
Die Initialisierung findet in den ersten 50 Zeilen statt. Als erstes wird der Bildschirm aufgebaut (siehe Bild nächste Seite) und ein Teil davon in den String screen$ kopiert. Diese Sicherung dient später zur schnellen Löschung des linken Bildschirmteils.
Es folgt die Dimensionierung der Arrays in den Zeilen 28 und 29:
status$(): aus 16 Texteinträgen bestehend. Beinhaltet die Texte der MIDI-Nachrichten in Klarschrift.
anz_byte|(): aus 16 1Byte-Integer-Einträgen bestehend. Beinhaltet die Anzahl der Daten-Bytes für den entsprechenden MIDI-Typen.
buffer|(): aus 3 1Byte-Integer-Einträgen bestehend. Beinhaltet später das gelesene Status-Byte und die (maximal) 2 Daten-Bytes.
buff%(): aus 8192 4Byte-Integer-Einträgen bestehend. Das ist die Platzreservierung für unseren neuen MIDI-Buffer. der wegen der doch nicht allzu schnellen BASIC-INP-Routine recht groß gehalten werden muß. hier 32768 Bytes lang: und das. damit bei schnellen Byte-Folgen (wie z.B. bei Pitch Bending) die Schreibmarke der BIOS-Routine die Lesemarke nicht überholt.
Als nächstes werden die Texte eingelesen, die sich als DATA-Zeilen von Zeile 5 bis 21 befinden. Anschließend an jeden Text befinden sich die Zahlen für die Anzahl der dem Status-Byte folgenden Daten-Bytes. Im Falle von System-Exclusive wurde die Anzahl der folgenden Daten-Bytes mit "0" angegeben, da das Auslesen der SYSEX-Daten in einer separaten Routine erfolgt.
In Zeile 40 wird der MIDI-Buffer neu initialisiert, d.h. es wird die Prozedur init buffer aufgerufen. Sehen wir uns dazu die Prozedur init_buffer (Zeile 217-224) einmal etwas genauer an. In der Variablen buffer% wird die Adresse des ersten Array-Elements von buffer%() abgelegt. Diese markiert nun den Beginn unseres neuen MIDI-Buffers. Mit dem Befehl buff_adr% = XBIOS(14,2) in Zeile 219 holen wir die Adresse des Buffer-Parameterblocks. Dieser Parameterblock ist folgendermaßen angelegt:
Um den neuen MIDI-Buffer zu initialisieren, müssen wir nur den Zeiger umbiegen und die neue Länge angeben. In unserem Fall machen dies die Befehle LPOKE buff_adr%,buffer%, dies ist die Adresse des ersten Array-Elements . und DPOKE buff_adr%+4,32768. Vorher müssen wir uns natürlich die Lage und Länge des alten MIDI-Buffers merken, um ihn vor Programmende w jeder restaurieren zu können. Damit das System nicht durcheinanderkommt, leeren wir den Buffer mit dem Aufruf der Prozedur dear midi buffer, die in Zeile 237-240 steht. Da der neue Buffer sehr lang ist, würde das einzelne Auslesen der Daten viel zu lange dauern. Daher setzt unsere Prozedur nur die Schreib-/Lesepositionen und die “Wassermarken" auf Null. Das geht flott von der Hand und stört den Ablauf des Programms in keinster Weise.
Tabelle 3
Bn | 7A | 0 | LOCAL OFF |
Bn | 7A | 7F | LOCAL ON |
Bn | 7B | 0 | ALL NOTES OFF (ANO) |
Bn | 7C | 0 | OMNI OFF + ANO |
Bn | 7D | 0 | OMNI ON + ANO |
Bn | 7E | M | MONO ON (M=Anzahl der Übertr.kanäle) |
Bn | 7F | 0 | POLY ON + ANO |
Tabelle 2
Weiter geht es in Zeile 45. Mit der Belegung des Platzhalters für die MIDI-Bytes lang$=" " und dem Setzen des Zeilenzahlers auf die Anfangsposition ist die Initialisierung beendet.
Was nun folgt, ist die Hauptprogrammschleife. Diese beginnt in der Zeile 51 und endet in Zeile 162. Sie besteht zum einen aus einer Warteschleife (Einleseschleife) Zeile 57 bis 83. die auf das Status-Byte der MIDI-Message [EXIT IF BIOS(1, 3) in Zeile 82) wartet und die Mausaktivitäten testet. Da die Maus während der ganzen Zeit aktiv sein soll, ohne den Programmablauf zu stören, wurde auf eine Warteschleife verzichtet, die auf das Loslassen der Maustaste wartet. Statt dessen wurde eine Variable vom Typ BOOLEAN verwendet (maus_button_an!), die im Falle von "TRUE" anzeigt, daß derzeit eine Maustaste gedrückt gehalten wird. Tritt dieses ein, wird nicht zur Mausabfrage verzweigt.
Im anderen Fall (MOUSEK=1 AND maus_button_an!=FALSE, siehe Zeile 61) werden entsprechende Aktionen eingeleitet, wie das Testen. ob die Maustaste über einem Filter-Button gedrückt wurde. Die Variable nummer& gibt dabei die Nummer des angeklickten Funktions-Buttons an. Mit der Prozedur inv_button kann ein Funktions-Button invertiert werden.
MMM bleibt solange in dieser Einleseschleife, bis ein MIDI-Status-Byte erkannt wurde. Dieses wird dann in die Variable buffer| (Zeile 87) gepackt. Die darauffolgenden Aktionen in den Zeilen 88 bis 112 testen auf die verschiedenen Modi [CHANNEL (Zeile 88) / SYSTEM (Zeile 94 und 98) Nachrichten oder RUNNING STATUS (Zeile 103)). Falls eine SYSTEM-Nachricht ohne System-Exclusive erkannt wird, wird in die Prozedur system_realtime verzweigt, die dem Status-Byte einen Index namens s_array| für das Text-Array status$() zuweist. Ebenfalls wird einer Variablen stelle| die Position des entsprechenden Filter-Bits in der Variablen filter% zugewiesen. Diese SYSTEM-Nachrichten behandelt man genauso weiter wie die CHANNEL-Nachrichten. Wird jedoch ein System-Exclusive Status-Byte erkannt, wird nach system_exclusive verzweigt, aber die Behandlung der Daten-Bytes erfolgt in der Prozedur (ab Zeile 276) selbst; und zwar werden solange Bytes eingelesen, bis ein EOX (hex F0) erkannt wird. Im Ausgabetext können Sie dann die Länge der Nachricht ablesen. Sie können diese Verfahrensweise leicht abändern, indem Sie die system_exclusive-Prozedur aus Listing 2 statt der aus Listing 1 eingeben, denn damit wird die gesamte System-Exclusive-Nachricht mit allen Daten-Bytes in 10 Spalten auf dem Bildschirm ausgegeben. Doch nun wollen wir den Weg nachvollziehen, den die übrigen Daten - außer System-Exclusive - gehen: Aus den CHANNEL-Nachrichten werden der o.g. Index s_array| und das Filter-Bit stelle| berechnet. Das geschieht in der Zeile 113 und 114. Das Text-Array status$() wurde so angelegt, daß es in aufsteigender Reihenfolge die Status-Bytes repräsentiert. wenn die folgende Rechenoperation durchgeführt wird:
s_array|=(buffer|/4)-8
Die Position des entsprechenden Filter-Bits ist dabei dieselbe Zahl, die s_array| beinhaltet.
Der Index und das Filter-Bit der SYSTEM-Nachrichten wurde ja in der Prozedur system realtime festgelegt. Daher kann jetzt die Abfrage des Filters (in Zeile 120) stattfinden. der durch die Variable filterte repräsentiert wird. Jedes Bit innerhalb von filter% stellt einen Filter für eine bestimmte Nachricht dar. Ist dieses Bit gesetzt, ist der Filter aktiviert. Wird durch die Testroutine festgestellt, daß ein Filter eingeschaltet ist. werden zwar alle übrigen Bytes der Nachricht ausgelesen, aber der entsprechende Text nicht auf dem Bildschirm ausgegeben. Stattdessen wird zum Anfang der Hauptprogrammschleife verzweigt (marke 1 in Zeile 52).
Falls kein Filter aktiviert war. erfolgt die Verbreitung der Ausgabe des Textes (Zeile 132 und 133). Den gesamten Text können Sie später in dem String status$ wiederfinden.
Nun holen Sie die restlichen Daten-Bytes und packen sie ebenfalls in den Ausgabe-String (Zeile 137 bis 143). Ist dies geschehen, können wilden Text in status$ auf den Bildschirm ausgeben. Die Ausgabe bewerkstelligen Sie durch den Befehl TEXT 20,zaehler&,status$ in der Zeile 153. Die Variable zaehler% wird nach jeder Zeilenausgabe um 8 erhöht. Erlangt zaehler% die 390-te Pixel-Zeile auf dem Bildschirm, wird der Ausgabebereich gelöscht und der Zähler auf die erste "Fenster"-Zeile gesetzt (Pixel 48 in y-Richtung). Dies ist die schnellste Art der Ausgabe in GFA-BASIC für unser Projekt (ausgenommen man programmiert ein schnelles Scrolling, wobei man aber sehr schnell die Übersicht verliert - alles schon dagewesen). Für unsere Zwecke reicht diese Geschwindigkeit vollkommen aus.
Zu guter Letzt setzt man die Zwischenspeicher für die Nachrichten buffer() auf Null (Zeile 158 bis 161).
Welche Routinen fehlen noch in der Besprechung nicht besprochen? Nun, da sind nur noch die Prozeduren
inv_button Zeile 171-183
test_mouse(VAR nummer&) Zeile 188-212
old_buffer Zeile 229-232
aufbau_screen Zeile 300-379
Bis auf die Prozedur old_buffer sind diese Prozeduren austauschbar gegen selbst programmierte, denn sie erzeugen nur die Testoberfläche des Programms und deren Abfrage. Die Routine old_buffer dahingegen ist sehr wichtig. Sie wird vor Beendigung des Programms aufgerufen, um Sie vor wilden Abstürzen anderer MIDI-Programme zu bewahren, die nach MMM aufgerufen werden. Um der BIOS-Unterroutine für die MIDI-Schnittstellenabfrage ihre gewohnte Umgebung wiederzugeben, poken wir die bei der Initialisierung gesicherten alten MIDI-Buffer-Werte wieder zurück.
So, das war schon die Beschreibung von MMM.
Was nachzutragen bleibt, ist eine kurze Bedienungsanleitung:
Nach dem Start von MMM erscheint der Bildschirm wie im Bild gezeigt. Linker Hand befindet sich der Ausgabebereich für die MIDI-Nachrichten, rechter Hand entdecken Sie die Bedienungselemente wie Filter und zwei zusätzliche Funktions-Buttons, wobei CLEAR die Bedeutung von lösche MIDI-Buffer hat und QUIT das Programm abbricht. Der Button CLEAR ist eine große Hilfe, wenn sich noch Unmengen von Daten im MIDI-Buffer befinden (z.B. bei PITCH BENDING etc.), die Sie eigentlich überhaupt nicht sehen wollen. Dann klicken Sie einfach auf CLEAR, und die Sache ist erledigt.
Noch einen letzten Satz zu den FILTERN: ein schwarzer FILTER-Button bedeutet, daß eine Nachricht diesen Typs auf dem Bildschirm erscheint - also Filter aus. Dementsprechend bedeutet ein weißer Button Filter an. Falls Sie das genau umgekehrt haben wollen, streichen Sie einfach den Aufruf der Prozedur inv_button (Zeile 375) in aufbau_screen so ziemlich am Ende.
Ich bin auch am Ende, nein nicht meiner Nerven, sondern der Beschreibung. Bleibt nur noch zu sagen: viel Spaß beim Eingeben und Benutzen von MINI-MIDI-MONITOR. Und nicht vergessen: die Zeilenangaben im Listing 1 dienen nur der Orientierung und dürfen NICHT miteingegeben werden.
CLS ! (C) MAXON Computer GmbH
@aufbau_screen
GET 0,0,400,399,screen$
' --- Ausgabe Text Anzahl der Datenbyte -----
DATA "Note off ",2
DATA "Note on ",2
DATA "Polypresssure ",2
DATA "Control Change ",2
DATA "Program Change ",1
DATA "After Touch ",1
DATA "Pitch Bend ",2
DATA "System Exclusive ",0
DATA "Song Position ",2
DATA "Song Select ",1
DATA "Tune Request ",0
DATA "Timing Clock ",0
DATA "Tape Start ",0
DATA "Tape Continue ",0
DATA "Tape Stop ",0
DATA "Active Sensing ",0
DATA "U N D E F I N E D ",0
'
' -------------------------------------------
' status$() = Bedeutung des Statuswortes
' anz_byte|() = Anzahl der Datenworte in Byte
' -------------------------------------------
'
DIM status$(16),anz_byte|(16)
DIM buffer|(2),buff%(8192)
'
' -----einlesen der Statusworte und Datenlängen
RESTORE
FOR i|=0 TO 16
READ status$(i|)
READ anz_byte|(i|)
NEXT i|
'
' --MIDI - Buffer vergrößern und auf Null setzen
'
@init_buffer
@clear_midi_buffer
'
'
DEFMOUSE 0
lang$=" " ! Länge eines Statuswortes in Hexdarstellung
zaehler&=48 ! Zeilenzähler
SHOWM
'
' ------------------- Hauptprogrammschleife
'
DO
marke1:
CLR wert2|
'
' ******************* EINLESE SCHLEIFE ********
'
DO
IF MOUSEK=0
maus_button_an!=FALSE ! damit kommt man ohne REPEAT UNTIL aus
ENDIF
IF MOUSEK=1 AND maus_button_an!=FALSE ! Maustaste gedrückt?
@test_mouse(nummer&) ! wenn ja, dann teste
IF nummer&>-1 AND nummer&<14 ! Filter klick
@inv_button(nummer&) ! invertiere Button
filter%=BCHG(filter%,nummer&) ! Filterbit
ELSE IF nummer&>15 ! Funktion
@inv_button(nummer&)
IF nummer&=16 ! CLEAR angeklickt
@clear_midi_buffer ! MIDI Bufflösch
@inv_button(nummer&)
ELSE ! QUIT angeklickt
ALERT 2," | Really QUIT ",1," NO |YES",dummy%
IF dummy%=2
@old_buffer ! alten MIDI Buffer wiederherstellen
END
ELSE
@inv_button(nummer&)
ENDIF
ENDIF
ENDIF
ENDIF
EXIT IF BIOS(1,3) ! Wenn MIDI-Byte anliegt, zur Ausgabe
LOOP
'
' ************ ENDE EINLESE SCHLEIFE **********
'
buffer|=INP(3) ! Hole MIDI Byte
IF buffer|>&HF0 ! System Realtime Nachrichten ?
buffer|(0)=buffer|
@system_realtime
help|=0
help1$=" " ! Nur für Running Status -> leer
CLR help2$
GOTO marke2 ! Ausgabe
ELSE IF buffer|=&HF0 ! extra für System Exclusive
buffer|(0)=buffer|
@system_exclusive
GOTO marke3 ! Ausgabe Text
ELSE IF buffer|>=&H80 ! Normale Status Nachrichten
buffer|(0)=buffer|
r_status|=buffer| ! Falls Running Byte merken
CLR help|
help1$=" "
CLR help2$
ELSE
IF r_status|<&H80 OR r_status|=>&HF0 ! Fehler abfangen
GOTO marke1
ENDIF
buffer|(0)=r_status| ! Running Status Byte ergänzen
buffer|(1)=buffer| ! erstes Datebyte
INC wert2| ! nur noch evtl. 2.D-byte lesen
help|=1 ! offset für noch zu 1s Bytes
help1$=HEX$(buffer|) ! erstes Datenbyte schon beschr.
help2$=" "
ENDIF
s_array|=SUB(SHR|(buffer|(0),4),8) ! welche Pos. im Array status$()?
stelle|=s_array| ! Filterposition Note Messages
marke2:
wert|=SUB(anz_byte|(s_array|),help|) ! wieviele Bytes noch übrig ?
'
' -------------------Filter testen
'
IF BTST(filter%,stelle|)
DO ! alle übrigen Bytes auslesen
DEC wert|
IF BIOS(1,3)
~INP(3) ! und verschlucken
ENDIF
LOOP UNTIL wert|=0
GOTO marke1 ! zum Anfang der Schleife
ENDIF
'
' ----- Ausgabe vor- und aufbereiten
'
RSET lang$=help1$
status$=status$(s_array|)+HEX$(buffer|(0))+" "+help2$+lang$+" ";
'
' ------- restliche Datenbytes holen
'
WHILE wert|>0
DEC wert|
INC wert2|
buffer|(wert2|)=INP(3)
RSET lang$=HEX$(buffer|(wert2|))
status$=status$+lang$+" "
WEND
' -----------------------------------------------
'
marke3:
' ------------------------- Ausgabe Text --------
IF status$<>""
IF zaehler&>390
PUT 0,0,screen$
zaehler&=48
ENDIF
TEXT 20,zaehler&,status$
SHOWM
ADD zaehler&,8
ENDIF
' -----------------------------------------------
CLR buffer|
buffer|(0)=0
buffer|(1)=0
buffer|(2)=0
LOOP
'
' ++++++ ENDE HAUPTPROGRAMMSCHLEIFE +++++++
'
' ++++++++++++++++ P R O Z E D U R E N +++++++++++++
'
' **************************************************
' >>>>>> invertieren eines Buttons
'
PROCEDURE inv_button(nummer&)
i|=nummer& MOD 2
j|=nummer& DIV 2
x1&=ADD(ADD(MUL(i|,abstand_x&),oben_x&),1)
y1&=ADD(ADD(MUL(j|,abstand_y&),oben_y&),1)
x2&=SUB(ADD(x1&,breite&),2)
y2&=SUB(ADD(y1&,hoehe&),2)
DEFFILL 1,1,0
GRAPHMODE 3
PBOX x1&,y1&,x2&,y2&
GRAPHMODE 1
SHOWM
RETURN
'
' **************************************************
' >>>>>> Test ob Button angeklickt
'
PROCEDURE test_mouse(VAR nummer&)
mx&=MOUSEX
my&=MOUSEY
IF mx&>oben_x& AND mx&<ADD(oben_x&,180)
IF my&>oben_y& AND my&<ADD(MUL(abstand_y&,9),oben_y&)
handle_x|=DIV(SUB(mx&,oben_x&),abstand_x&)
handle_x2|=DIV(SUB(mx&,SUB(oben_x&,leer_x&)),abstand_x&)
handle_y|=DIV(SUB(my&,oben_y&),abstand_y&)
handle_y2|=DIV(SUB(my&,SUB(oben_y&,leer_y&)),abstand_y&)
IF handle_x|=handle_x2| AND handle_y|=handle_y2|
nummer&=ADD(MUL(handle_y|,2),handle_x|)
ELSE
nummer&=-1
ENDIF
IF nummer&=14 OR nummer&=15
nummer&=-1
ENDIF
ELSE
nummer&=-1
ENDIF
ELSE
nummer&=-1
ENDIF
maus_button_an!=TRUE
RETURN
'
' **************************************************
' >>>>>> einrichten eines größeren MIDI-Buffers
'
PROCEDURE init_buffer
buffer%=V:buff%(0) ! hole Pointer auf neuen MIDI-Buffer
buff_adr«=XBIOS(14,2) ! hole Adresse der System MIDI-Zeiger
buffer_alt%=LPEEK(buff_adr%) ! alten Bufferzeiger merken
buf_len_alt%=DPEEK(buff_adr%+4) ! alte Bufferlänge merken
LPOKE buff_adr%,buffer% ! neue MIDI-Bufferadresse
DPOKE buff_adr%+4,32768 ! neue MIDI-Bufferlänge
RETURN
'
' **************************************************
' >>>>>> restaurieren des alten MIDI-Buffers
PROCEDURE old_buffer
LPOKE buff_adr%,buffer_alt% ! alten MIDI-Bufferzeiger restaurieren
DPOKE buff_adr%+4,buf_len_alt% ! dto. Länge des Buffers
RETURN
'
' **************************************************
' >>>>>> löschen des MIDI-Buffers
'
PROCEDURE clear_midi_buffer
LPOKE buff_adr%+6,0 ! nächste Schreib/Lesepos auf Null
LPOKE buff_adr%+10,0 ! obere/untere Wassermarke=Null
RETURN
'
' **************************************************
' >>>>>> Bearbeiten von System Realtime Nachr.
'
PROCEDURE system_realtime
SELECT buffer|
CASE &HF2
' ------------- Song Position Nachricht
s_array|=8
stelle|=8
CASE &HF3
' ------------- Song Select Nachricht
s_array|=9
stelle|=9
CASE &HF6
' ------------- Tune Request Nachricht
s_array|=10
stelle|=10
CASE &HF8
' ------------- Timing Clock Nachricht
s_array|=11
stelle|=11
CASE &HFA TO &HFC
' ------------- Tape start/Cont/Stop Nachricht
s_array|=SUB(buffer|,238)
stelle|=12
CASE &HFE
' ------------- Active Sensing Nachricht
s_array|=15
stelle|=13
DEFAULT
' ------------- sonstige, nicht Implementierte
s_array|=16
ENDSELECT
RETURN
PROCEDURE system_exclusive
' ------------- System Exclusive Nachrichten
CLR count|
t=TIMER
DO
IF BIOS(1,3)
buffer|=INP(3)
INC count|
ELSE IF SUB(TIMER,t)>200
timeout|=TRUE
status$="T I M E O U T"
ENDIF
LOOP UNTIL buffer|=&HF7 OR timeout!=TRUE
timeout!=FALSE
IF NOT BTST(filter%,7)
status$=status$(7)+" "+STR$(buffer|)+" Bytes lang"
ELSE
status$=""
ENDIF
RETURN
'
' **************************************************
' >>>>>> Aufbau der Bedienoberfläche
'
PROCEDURE aufbau_screen
'
button_text:
'
DATA Note off
DATA Note on
DATA Polypres
DATA Ctrl Ch
DATA Prog Ch
DATA After T
DATA Pitch B
DATA Sys Excl
DATA Song Pos
DATA Song Sei
DATA Tune Req
DATA Clock
DATA Tape Fun
DATA Active S
DATA Clear
DATA QUIT
'
'
breite&=80 ! Button Breite
hoehe&=26 ! Button Höhe
offset|=2
oben_x&=424 ! erster Button links oben X-Koordinate
oben_Y&=78 ! dto. Y-Koordinate
abstand_x&=100 ! Abstand zweier linker/rechter Eckpunkte X-Koordinate
abstand_y&=34 ! dto. Y-Koordinate
offx_text&=10 ! Abstand Text. zum linken Rand eines Buttons
offY_text&=20 ! Abstand Text zum oberen Rand eines Buttons
leer_x&=SUB(abstand_x&,breite&) ! Lücke in Pixel X-Koordinate
leer_y&=SUB(abstand_y&,hoehe&) ! Lücke in Pixel Y-Koordinate
'
' ----- Bildschirm mit Desktop Muster füllen
GRAPHMODE 1
DEFFILL 1,2,4
PBOX 0,0,639,399
DEFFILL 1,2,1
PBOX 400,50,630,390 ! Box um Filterbereich
DEFFILL 1,0,0
PBOX 4,30,380,390 ! Box um Anzeigebereich
DEFFILL 1,1,0
PBOX 414,11,420,42 ! Box für "Midi Monitor"-Name
' ------------------ Programmname
DEFTEXT ,,,32
GRAPHMODE 4
TEXT 420,38,"Midi Monitor"
GRAPHMODE 3
TEXT 418,36,"Midi Monitor"
DEFTEXT ,1,,13
GRAPHMODE 2
' -------- Überschrift Anzeigebox
TEXT 44,20,"Text Status Byte1 Byte2"
GRAPHMODE 1
'
TEXT 410,70,"Filter"
TEXT 410,342,"Functions"
' -------- zeichne Buttons
RESTORE button_text
FOR j|=0 TO 8
IF j|=7
INC j|
ENDIF
FOR i|=0 TO 1
READ button$
x1&=ADD(MUL(i|,abstand_x&),oben_x&)
y1&=ADD(MUL(j|,abstand_y&),oben_y&)
x2&=ADD(x1&,breite&)
y2&=ADD(y1&,hoehe&)
DEFFILL 1,1,0
PBOX ADD(x1&,2),ADD(y1&,2),ADD(x2&,2),ADD(y2&,2)
DEFFILL 1,0,0
PBOX x1&,y1&,x2&,y2&
TEXT ADD(x1&,offx_text&),ADD(y1&,offy_text&),button$
@inv_button(ADD(MUL(j|,2),i|))
NEXT i|
NEXT j|
DEFTEXT ,0,,6
RETURN