MINI - MIDI - MAXI: Der Mini-MIDI-Monitor

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:

  1. Channel-Nachrichten: das sind die Nachrichten, die sich auf einen MIDI-Kanal beziehen. Dazu gehören

a) Voice-Nachrichten (Note on usw.)
b) Modus-Nachrichten

  1. System-Nachrichten: das sind die Nachrichten, die sich nicht auf einen MIDI-Kanal beziehen. Dazu gehören

a) Common (Song Position usw.)
b) Real_Time (Clock usw.)
c) System Exclusive

Alle MIDI-Daten werden nach dem gleichen Prinzip übertragen:

  1. Byte: Status (das ist das Befehls-Byte)

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.

CHANNEL-VOICE-NACHRICHTEN

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

Das Programm

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.

Init Buffer

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.

Adresse Beschreibung
buff_adr% Zeiger auf den Buffer
buff_adr%+4 Länge des Buffers
buff_adr%+6 nächste Schreibposition
buff_adr%+8 nächste Leseposition
buff_adr%+10 "untere Wassermarke"
buff_adr%+12 “obere Wassermarke"

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

Berthold Dettlaff
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]