Dr.Godfried-Willem RAES
Kursus Experimentele Muziek: Boekdeel 1: Algoritmische kompositie
Hogeschool Gent : Departement Muziek & Drama
<Terug naar inhoudstafel kursus> |
1063:
M.I.D.I.- PROCEDURES & FUNKTIES
1.Universele procedure voor de sturing van Logos midi-interfaces
Deze procedures werken alleen met de interfaces zoals beschreven in boekdeel 2 van kursus. Vanuit het hoofdprogramma of vanuit een ander subprogramma over te dragen variabelen (of konstanten) zijn:
DP het I/O adres van de midi-poort
b% de waarde van het uit te sturen midi-getal
CODE:
SUB Uit (b%)
' dit is een subprogramma dat een byte 'b%' naar het Logos midi-interface doorstuurt het I/O adres van het gebruikte interface is opgeslagen in de variabele of konstante DP
IF DP=&H320 THEN
OUT &H320,b% : OUT &H322,0
Z#=ZZ#^ZZ# :' dit is een dummy op de strobe-puls te verlengen .Het kan ook in hardware geimplementeerd worden met een monoflop.
OUT &H322,1
WHILE INP(&H321) AND 128 : WEND
' wacht tot de UART klaar is met de verwerking
EXIT SUB
ENDIF
IF DP<> &H2FA THEN
OUT DP,b% : OUT DP+2,0: A$="_": OUT DP+2,1
WHILE INP(DP+1) AND 128 : WEND
ELSE
OUT &H2FA,b%
DO : LOOP UNTIL INP(&H2FB) AND 64
ENDIF
END SUB
Heb je slechts 1 machine met slechts 1 interface, dan kan je voor DP uiteraard ook een konstante waarde invoeren en deze procedure sterk inkorten. Heb je bijvoorbeeld je midi-interface geinstalleerd op adres &H380 dan wordt dit :
SUB Uit (b%)
OUT &H380, b% : OUT &H382, 0 : A$="***" : OUT &H382, 1
WHILE INP(&H381) AND 128 : WEND
END SUB
Een subprogramma dat zelf het adres van een logos-midi-interface op een willekeurige machine opzoekt en uittest, staat ter beschikking op de harddisk in de klas, zoals overigens ook alle nog aktuele procedures en programmas (en de laatste versies daarvan) waarvan sprake doorheen deze kursus.
2. Universeel subprogramma voor het weergeven van P(i,j) arrays via midi
Door het hoofdprogramma over te dragen variabelen :
P(I,J) het partituur-array zelf
EINDE de grootste waarde van I ( dit is ook te vinden als EINDE=UBOUND(P%,1)
CODE:
SUB MPLAY
DEFLNG K,T
CLS
DO :K$=INKEY$ : LOOP UNTIL K$="" : ' maak de keyboard buffer leeg...
LOCATE 15, 10 : PRINT " Want to hear this piece ? (Y/N) "; : K$ = INPUT$(1)
PRINT K$
K$= UCASE$(K$)
IF K$="Y" THEN
CLS
LOCATE 16, 10 : PRINT " Tempo ( M.M.-getal) ?:"; INPUT MM
TMP# = 60 / (MM*8) :' dit klopt alleen wanneer de teleenheid in het array de tweeendertigste noot is !
FOR I= 0 TO EINDE
T0#= TIMER ' kijk op de klok
FOR J = 1 TO 8 STEP 2
IF P(I, J) > 0 THEN ' als er een midi-code staat in het array ...
Uit (144 + (( J - 1) \ 2))
Uit (P(I, J))
Uit (P(I, J + 1)) ' hier wordt 3x de midi-uit procedure gebruikt
ENDIF
NEXT J
DO UNTIL TIMER - T0# >= TMP# : LOOP ' kijk op de klok tot de gevraagde duur verstreken is...
NEXT I
ENDIF
ENDSUB
Hoewel ik een programma schreef en ter beschikking stel dat arrays en files op een meer gesofistikeerde wijze kan weergeven en in realtime beinvloeden, raad ik de studenten toch aan deze of een soortgelijke beperkte procedure in elk komponeerprogramma op te nemen, omdat dit het debuggen erg kan versnellen en omdat de opname van het gehele 'Player' programma teveel geheugenruimte zou innemen.
De aanpassing van de hierboven gegeven kode voor het extended-P formaat zal de studenten allicht geen grote moeite kosten. Doe het eens, als oefening.
3. Procedure voor de initialisatie in UART-modus van MPU401 en MusicQuest interfaces
' ****************
' * MPUUART.BAS
' ****************
' Deze programmamodule verzorgt de noodzakelijk instelling van het MPU401,of MusicQuest interface in 'domme' UART-modus. Het moet gerund worden na eventueel gebruik van MBATCH, Windows of CakePro...
DEFINT A-Z
CONST DP = &H330
CONST CP = &H331: ' bit 7= byte to be read - negative logic
' bit 6= mcc ready - negative logic
' bits 5-0= not used (always 1!)
INITUART:
' als bit 7 1 is, is er geen te lezen byte:
IF INP(CP) AND 128 THEN
WAIT &H331, 64, 64 'check bit 6 - must be 0
OUT CP, &H3F: ' = 0011 1111 (63) =UART-command
ELSE
' als bit 7 0 is moet er een byte ingelezen worden:
WHILE INP(CP) < 128
dummy = INP(DP)
WEND
GOTO INITUART
END IF
END
4. Universele Midi-out routines in Basic voor MPU401 e.d.
COMMON SHARED Dp%
DECLARE SUB Uit (byte%)
SUB Uit (byte%)
' routine te gebruiken wanneer er ook MIDI-in verwacht kan worden:
IF Dp% = &H330 THEN
IF INP(&H331) AND 128 THEN
WAIT &H331, 64, 64: OUT &H330, byte%: EXIT SUB
ELSE
WHILE INP(&H331) < 128: dummy%= INP(&H330): WEND :' het midi-in byte gooien we weg...
WAIT &H331, 64, 64: OUT &H330, byte%: EXIT SUB
END IF
END IF
' Logotronics interfaces:
IF Dp% = &H2FA THEN
OUT &H2FA, byte%: DO: LOOP UNTIL INP(&H2FB) AND 64: EXIT SUB
WHILE INP(&H2FB) < 64: WEND: ' werkt ook zonder op de T1000 of andere trage XT komputers!
END IF
IF Dp% = &H378 THEN
' this is for 80386SX + 80486DX
OUT &H378, byte%: OUT &H37A, 0: ZZ# = ZZ# ^ ZZ#: OUT &H37A, 1
WHILE INP(&H379) AND 128: WEND
EXIT SUB
END IF
IF Dp% = &H320 THEN
' this is for Abulafia (80386DX -33MHz) and faster machines
OUT &H320, byte%: OUT &H322, 0: ZZ# = TIMER
DO: LOOP UNTIL TIMER - ZZ# > .00001
OUT &H322, 1
WHILE INP(&H321) AND 128: WEND
EXIT SUB
ELSE
OUT Dp%, byte%: OUT Dp% + 2, 0: t$ = "_": OUT Dp% + 2, 1
WHILE INP(Dp% + 1) AND 128: WEND
END IF
END SUB
Merk op dat deze procedure in een programma net zo eenvoudig te gebruiken is als de midi-sturing op een oude Atari ST. We kunnen immers -zonder gebruik te maken van CALL- rechtstreeks schrijven:
Uit 144 : Uit 60 : Uit 127
en onze centrale do klinkt in ff. We brengen haar tot stilte via:
Uit 144 : Uit 60 : Uit 0
of, wat op hetzelfde neerkomt:
Uit 128 : Uit 60 : Uit 0
6. Procedures voor Soundblaster midi interfaces en assembler procedures in Power Basic
De hier gegeven procedures zijn geschreven voor opname in Power Basic programma's. In deze Basic versie is het mogelijk assembler taal rechtstreeks in de bronkode op te nemen. Dit laat de hoogst haalbare kommunikatiesnelheden toe. We hebben deze kodevoorbeelden uitvoerig getest op soundblasterkaarten voorzien van midi-interfaces. Vanaf de soundblaster kaart 'SB16' kan het midi interface gebruikt en geadresseerd worden zoals hiervoor getoond , en op dezelfde adressen als gebruikelijk voor MPU-kompatibele kaarten. Oudere versies moeten gebruik maken van de SBuit procedure die we verder eveneens laten volgen. Deze SBuit procedure werkt ook op de nieuwste Soundblasterkaarten. Wanneer je dus schrijft voor machines waarin je een soundblasterkaart als aanwezig veronderstelt, dan kan je volstaan met uitsluitend deze procedures en vervalt bovendien de noodzaak om het interface eerst in een MPU401 kompatibele UART modus om te schakelen. Bovendien zijn er nog enkele extra voordelen verbonden aan het gebruik van het soundblaster midi-interface in 'native' mode: het laat 'time-stamping' toe en het is sneller dan de MPU-mode. Voorbeelden van het gebruik met time-stamping zullen binnenkort aan deze paragrafen worden toegevoegd. Ze zijn trouwens sedert 1998 standaard opgenomen in de nieuwste versies van <GMT>. (Godfried's multitasker).
6.1: Een in assembler gekodeerde Uit procedure voor midi , in MPU-UART mode:
Deze procedure werkt alleen in UART-mode! (zie verder voor een procedure waarmee UART mode wordt ingesteld). Ze is minstens 20 maal sneller dan eerder gegeven procedures in Basic. Wees echter uiterst aandachtig bij het overtypen van deze kode. De uitroeptekens gaan telkens de assemblerinstrukties vooraf. Kommentaren toegevoegd aan regels in assembler moeten beginnen met ; (punt-komma) en niet met ' of :' zoals in Basic. Er is geen syntax-help beschikbaar voor inline assembler. Een foutje leidt dan ook bijna steeds tot een gecrashte komputer... Wie graag de mogelijkheden van assembler en/of de how and why's van deze procedures wil onderzoeken, verwijzen we naar de Professional developper kit for programmers voor de Soundblaster kaarten evenals naar -uiteraard- een goed boek over 80xx86 processoren en hun instruktieset. Deze materie valt echter beslist buiten het bestek van deze kursus.
%Madr = &H330 :' konstante voor het I/O adres van het midi interface
SUB Uit (BYVAL b%) PUBLIC
' code
for MPU in UART mode - tested. 1998 o.k.for PB3.5
! push ax
! push dx
! mov dx, %Madr ; numbers follow basic conventions
! inc dx ;
Madr + 1 = status port
UitBusy:
' we set
labels here as in Basic ...
! in al, dx ; read status port
! test al, &H40
! jnz UitBusy ; jump if not ready
! mov ax, b% ; we do: mov ax, b% and further use only lsb
in al. This serves as a filter
! dec dx ;
set back to base port adress
! out dx, al ; this outputs the byte
! pop dx
! pop ax
END SUB
%SBadr = &H220 :' konstante voor het
basis I/O adres van de soundblaster kaart
SUB SBuit (BYVAL b%)
' code
for soundblaster midi-out - this does not require entering UART
mode prior to a first call.
! push ax
! push dx
! mov dx, %SBadr
! add dl, &H0C ; status port/ data port
SbBusy0:
! in al, dx
! test al, &H80
! jnz SbBusy0 ; dsp should be ready to accept a command. The
highest bit should become zero
! mov al, &H38 ; midi-out command
! out dx, al ; send midi-out command
SbBusy1:
! in al, dx
! test al, &H80
! jnz SbBusy1 ; again, check dsp busy...
! mov al, b%
! out dx, al ; output the byte
! pop dx
! pop ax
END SUB
6.3: Een
procedure om een MPU-kompatibel midi interface in of uit
UART-modus te schakelen:
Deze procedure vraagt een parameter: 1 schakelt UART modus in, 0
schakelt dit weer uit.
SUB MpuUart (BYVAL
state%)
' state%
sets ON (1) or OFF (0)
! push cx ; we only use cx here. ax, dx are used in
MpuCommand
! cmp state%, 0 ; compare parameter to 0 (OFF-state)
! je TurnOff ; if we have to leave UART, jump to the
corresponding label in the procedure
MpuCommand &H03F ' basic call to the assembler procedure (zie verder)
! sub cx, cx ; try max.65536 times...
! jmp short ExitSetUart ; jump to a nearby label
TurnOff:
MpuCommand &HFF
UARTmodeOK:
! mov ax, 0 ; return 0 in ax as a sign of success.
However, we cannot retrieve this value on exit
ExitSetUART:
! pop cx
END SUB
In deze procedure komt een call voor naar een
algemenere procedure -volledig in assembler- waarmee
kommando-bytes naar het MPU interface moeten worden gestuurd:
SUB MpuCommand (BYVAL
b%)
' this
procedure sends a command byte to the MPU midi-port.
! push ax
! push dx
! mov dx, %Madr
! inc dx
Busy1:
! in al, dx
! test al, &H40
! jnz Busy1
! mov al, b%
! out dx, al
! pop dx
! pop ax
END SUB
De hier gegeven procedure kodeert een funktie die een via de midi-ingang aangeboden midi-byte retourneert. Ze kan -eens opgenomen in je eigen programma- gebruikt worden zoals elke andere funktie eigen aan Basic zelf en volgt ook geheel eenzelfde syntax. De funktie hier gegeven werkt alleen voor MPU401 en daarmee kompatibele interfaces (MusicQuest en latere Soundblasterkaarten) die eerst in UART modus werden geschakeld met de hiervoor gegeven programmaprocedure.
COMMON SHARED DP AS INTEGER
DECLARE FUNCTION Min% (b%)
DP = &H330
Voorbeeld van kode zoals die in een hoofdprogrammamodule zou kunnen voorkomen, en waarmee de funktie kan worden getest:
DO
byte = Min%
IF byte >= 0 AND byte < &HFF THEN PRINT byte; " ";
LOOP
'en hier volgt de kode van de funktie zelf:
FUNCTION Min%
IF DP = &H330 THEN
IF INP(&H331) < 128 THEN Min% = INP(&H330) ELSE Min% = -1
EXIT FUNCTION
END IF
END FUNCTION
Met deze funktie zijn geen hoge doorvoersnelheden te halen. Wanneer dat nodig is zullen we een buffer in software moeten implementeren.Dit wordt uitvoerig behandeld wanneer we aan het ontwerpen van echte multitaskers toe zullen zijn. Daarbij zal overigens het gebruiken van assembler procedures erg nuttig blijken.
Kode voor gebruik in kombinatie met Power Basic en in-line assembler geven we alvast in twee varianten:
1.- een versie voor gebruik van een interface in MPU-kompatibele UART modus (dit kan dus ook een soundblaster inferface zijn, geinitialiseerd in MPU-UART modus zoals hoger gezien).
FUNCTION MiIn%() PUBLIC
' Read a
byte from the midi port
! push ax ; we need ax to receive the return value
! push dx
! mov dx, %Madr
! inc dx ;
set port to &H331
BusyMiIn1:
! in al, dx
! test al, &H080 ; o.k. to read? = IF al AND &H80 THEN
! jnz NoMiIn ; jump if not zero- nothing to read
! dec dx ;
set back to data port
! in al, dx ; read data byte, clear midi interrupt
! jmp short EindeMiIn
NoMiIn:
! mov ax, -1
EindeMiIn:
! mov FUNCTION, ax
! pop dx
! pop ax
END FUNCTION
2.- een versie voor gebruik met een Soundblaster kaart in de voor die kaart gewone midi-modus. (geen UART, en bereikbaar via het basis I/O adres van de kaart, bijna steeds &H220)
FUNCTION SBmidiRead%()
PUBLIC
' reads
a byte from a soundblaster
' midi input port, in polling mode.
! push ax
! push dx
! mov dx, %SBadr ; %SBadr is a declared constant. &H220
! add dl, &H0C ; status port/ data port
SbBusy00:
! in al, dx ; read status port
! test al, &H80 ; ready for output?
! jnz SbBusy00 ; dsp should be ready to accept a command
! mov al, &H30 ; midi-in command: read command for polling
mode
! out dx, al ; send midi-in command
! mov dx, %SBadr ; reset to base adress
! add dl, &H0E ; status port
SbBusy01:
! in al, dx ; read status port
! test al, &H080 ; midi data available?
! jz SbBusy01 ; if not, ...
! mov dx, %SBadr ; reset to base adress
! add dl, &H0A ; set to data port
! in al, dx ; read data byte
! mov ah, al ; save it...
! mov dx, %SBadr ; reset to base adress
! add dl, &H0C ; set to status / data port
sbBusy02:
! in al, dx ; read status port
! test al, &H080 ; ready for output ?
! jnz sbBusy02
! mov al, &H030 ; this is the midi-in command
! out dx, al ; turn off the midi-in command
! mov al, ah ; retrieve data byte read
! mov ah, 0 ; mask msb
! mov FUNCTION, ax ; this is the method to return a register
value in the function
! pop dx
! pop ax
END FUNCTION
8.Gekombineerde Midi-IN en Midi-Out procedure:
' MAIN module:
COMMON SHARED i%
COMMON SHARED Fifo%()
DECLARE SUB IO (b%)
DIM SHARED Fifo%(0 TO &H7FF)
CONST dummybyte = &H100
' PROCEDURE:
SUB IO (b%)
IF (NOT (INP(&H331) OR 127) AND 128) THEN
DO
Fifo%(i%) = INP(&H330):
i% = ABS(NOT i%) AND &H7FF
LOOP UNTIL INP(&H331) AND 128
END IF
IF NOT b% AND dummybyte THEN WAIT &H331, 64, 64: OUT &H330, b%
END SUB
9. Midi- I/O procedure met dubbele fifo-buffers:
' Dit is Microsoft Basic PDS kode.
' in de MAIN-module:
REM $DYNAMIC
CONST DP= &H330
CONST SP=&H331
CONST MidiBuffer = &H3FF
COMMON SHARED FifoIn%()
COMMON SHARED FifoUit%()
COMMON SHARED i%,j%,ii%,jj%
DECLARE SUB Midi ()
DIM SHARED FifoIn%(0 TO MidiBuffer)
DIM SHARED FifoUit%(0 TO MidiBuffer)
'Procedure:
SUB Midi
DO
WHILE INP(SP) < 128
FifoIn%(ii%)= INP(DP) :
ii%= (ii%+1) AND MidiBuffer
WEND
IF jj% <> j% THEN
WAIT SP,64,64
OUT DP, FifoUit%(jj%): jj%= (jj%+1) AND MidiBuffer
LOOP UNTIL jj%=j%
END SUB
10. Midi- I/O procedures met dubbele fifo-buffers voor PBcc:
Kodevoorbeelden geschreven en getest in Power Basic Console Compiler versie 1.0 (07/1998).
Sedert 1998 moeten we de studenten eigenlijk afraden nog langer naar kompatibiliteit met het hoogbejaarde Roland MPU401 midi interface te zoeken. Wanneer gelijktijdig midi-input en output nodig is in een programma -en dit is nu eenmaal zo goed als steeds het geval in programma die we schrijven voor interaktieve kompositie en uitvoeringspraktijk- , dan laat het MPU interface -dat intern gebruik maakt van een voor hedendaagse normen erg trage en primitieve 8-bit microprocessor, verstek gaan.
De moderne Soundblaster-kaarten hebben een DSP processor aan boord evenals een heleboel geheugen, waarmee in hardware FIFO's werden voorzien voor midi I/O, maar ook -uiteraard- voor digitale audio I/O.
De eerste taak die we krijgen bij het opzetten van een eigen programma, bestaat er veelal in de DSP op de soundcard te resetten. We gaan er immers van uit dat onder Windows95, 98 of NT wordt gewerkt vandaag en dat dus de mogelijkheid bestaat dat het interface zich in een willekeurige toestand bevindt op het moment dat we er in ons eigen programma gebruik van willen maken.
De nieuwste 32-bit compiler van Power Basic beschikt echter niet over enkele traditionele Basic kommandos waarmee we rechtstreeks naar de registers van de DSP chip kunnen schrijven. Daarom hebben we onderstaande kode welhaast volledig geschreven in inline assembler.
10.1.: Resetting the SoundBlaster DSP chip:
' Dr.Godfried-Willem RAES: SoundBlaster RESET procedure written in assembler... ' This can be compiled under PowerBasic Console Compiler V1.0 $DIM ALL %SBadr = &H220 :' make sure this corresponds to your hardware settings... DECLARE FUNCTION DSPreset? () AS BYTE FUNCTION PBMAIN () AS LONG LOCAL a? DO a? = DSPreset? PRINT a? IF INKEY$ <> "" THEN EXIT DO LOOP UNTIL a? = 170 END FUNCTION FUNCTION DSPreset? () LOCAL t! ' sends a RESET to the SoundBlaster DSP chip ! push ax ! push cx ! push dx ' first we check the DSP for a ready to write... ! mov dx, %SBadr ! add dl, &H0C rstlabel1: ! in al, dx ! test al, &H80 ! jnz rstlabel1 ! mov dx, %SBadr ! add dl, 6 ; set to DSP reset register ! mov al, 1 ; prepare to write a 1 to the reset port ! out dx, al ; output it ! sub al, al ; delay loop SbDSPdelay: ! dec al ! jnz SbDSPdelay ; should be 3 microseconds... ! mov ax, 0 ! out dx, al ; output a zero now ! sub cx,cx ; max. 65536 tries SbDSPempt: ! mov dx, %SBadr ; set I/O adres back ! add dl, &H0E ; set to read-buffer status port ! in al, dx ; read it ! or al, al ; data available? ! jns SbDSPnxt ; bit 7 clear, try again ! sub dl,4 ; set to data-port ! in al,dx ; read data returned ! cmp al,&HAA ; receive code for successfull reset ? (=&HAA) ! je SbDSPresetOK ; if al = &HAA then goto SbDSPresetOK SbDSPnxt: ! loop SbDSPempt ; if no success, try again... SbDSPresetOK: ! mov FUNCTION, al ; return the value for success in the basic function ! pop dx ! pop cx ! pop ax END FUNCTION
10.2.: Midi I/O using the SoundBlaster DSP chip:
In de hieronderstaande kode, ontleend aan de PBcc versie van <GMT> maken we gebruik van de intrinsieke DSP UART funktie van de Soundblaster kaart. Dit mag niet worden verward met de MPU-UART modus, die door deze kaarten nog steeds wordt ondersteund.
' first we declare all constants:
%Madr = &H330 :' base I/O adres for mpu interfaces
%SBadr = &H220 :' base adress for soundblaster cards
%Mirq = &H5 :' Midi hardware interrupt number
%MidiBuffer = &H1FFF :' size for midi fifo-buffers
GLOBAL MiBuf AS STRING * %MidiBuffer :' buffer for raw incoming midi data GLOBAL MuBuf AS STRING * %MidiBuffer :' buffer for midi output data
GLOBAL MidiMode AS BYTE :' 0 = MPU401 mode :' 1 = SB-Mode UART :' 2 = SB-standard mode :' 3 = Use RS232-port
SUB InitMidi LOCAL k$, retval? MuBuf = STRING$(255, %MidiBuffer) MiBuf = STRING$(255, %MidiBuffer) 'MidiMode = :' sets UART mode - global variable :' 2 = soundblaster normal mode :' 1 = soundblaster midi UART mode :' 0 = MPU-UART mode PRINT "MidiMode? (Choose 1 with SB-cards)"; DO: k$=INKEY$: LOOP UNTIL k$>="0" AND k$<="2" MidiMode = VAL(k$) SELECT CASE MidiMode CASE 0 ' MPU-UART mode - midi-input is shaky in this mode... MidiTimeStamp = 0 SHELL "mpuuart.exe", 1 ' uses shell command to external programm CLS CASE 1 ' Soundblaster DSP polling UART-mode ' THIS IS THE ONLY MODE WHERIN WE DO NOT MISS BYTES... retval? = DSPreset? SELECT CASE retval? CASE 170 ' 170 = &HAA = &B10101010 $DEBUG PRINT "Soundblaster DSP reset O.K." CASE ELSE PRINT "SB reset failed... "; retval? END SELECT ' SB UART polling mode midi I/O ' DSPcommand &H34 ' sends a command to the SoundBlaster DSP chip ! push ax ! push dx ! mov dx, %SBadr ! add dl, &H0C ; set to DSP write command/data adres SbDSPstIm10: ! in al, dx ! test al, &H80 ! jnz SbDSPstIm10 ! mov al, &H34 ; send the DSP-command ! out dx, al ! pop dx ! pop ax $DEBUG PRINT "Soundblaster DSP midi I/O in use - mode &H34" CASE 2 ' Soundblaster normal mode : requires In/Out switching ' DSPcommand &H30 :' standard SB polling mode midi IN ' DSPcommand &H38 :' standard midi output mode retval? = DSPreset? SELECT CASE retval? CASE &HAA $DEBUG PRINT "Soundblaster DSP reset O.K." CASE ELSE PRINT "SB reset failed... "; retval? END SELECT MidiTimeStamp = 0 CASE ELSE ' other modes to be implemented later... END SELECT END SUB
' ---------------------------------------------------------------------- SUB Uit(b%) ' writes midioutput to the output buffer in string-format: IF b% > 254 THEN EXIT SUB MuBuf = CHR$(b%) + MuBuf END SUB ' ---------------------------------------------------------------------- SUB MidiUit LOCAL b?, wpp% ' this is a Task in a multitasker. It should be called periodically. ' writes the midi output buffer MuBuf to the midi-port. ' Search the first occurence of the ASC(255) character. ' This is one byte beyond the last byte to send out. ' If the buffer is empty wpp% would be 1 wpp% = INSTR (1,MuBuf,CHR$(255)) ' if wpp%=0 than we have buffer-overflow (ni ASC(255) character ' found in the entire buffer! ' we now have the pointer to the first byte to send out through midi... wpp% = wpp% - 1 'IF wpp% < 0 THEN ' LOCATE 18,1: PRINT "Buffer overflow" ' EXIT SUB :' buffer overflow error 'END IF DO IF wpp% THEN b? = ASC(MID$(MuBuf, wpp%,1)) MID$(MuBuf, wpp%, 1) = CHR$(255) wpp% = wpp% - 1 ELSE EXIT SUB END IF SELECT CASE MidiMode CASE 0 ! push ax ! push dx ! mov dx, %Madr ; numbers follow basic conventions ! inc dx ; Madr + 1 = status port UitBusy: ! in al, dx ; read status port ! test al, &H40 ! jnz UitBusy ; jump if not ready ! mov al, b? ; and further use only lsb in al ! dec dx ; set back to base port adress ! out dx, al ; this outputs the byte ! pop dx ! pop ax CASE 1 ! push ax ! push dx ! mov dx, %SBadr ! add dl, &H0C ; set to dsp read/write port Sbrdy: ! in al, dx ; check bit 7 - writebuffer status ! test al, &H80 ! jnz Sbrdy ; jump if not 0 ! mov al, b? ; read the data into ax ! out dx, al ; output the byte to the dsp port ! pop dx ! pop ax CASE 2 ' prepare DSP for output - we send DSPcommand &H38 ' DSPcommand &H38 ! push ax ! push dx ! mov dx, %SBadr ! add dl, &H0C ; status port/ data port SbBusy20: ! in al, dx ! test al, &H80 ! jnz SbBusy20 ; dsp should be ready to accept a command ! mov al, &H38 ; midi-out command ! out dx, al ; send midi-out command SbBusy21: ! in al, dx ! test al, &H80 ! jnz SbBusy21 ! mov al, b? ; now we send the midi byte ! out dx, al ! pop dx ! pop ax CASE ELSE ' not implemented yet END SELECT LOOP UNTIL wpp% = %False END SUB '----------------------------------------------------------------------- SUB MidiIn ' this task polls the midi port and writes incoming data to Mibuf. LOCAL Inbyte? Inbyte? = 255 SELECT CASE MidiMode CASE 0 ' The polling frequency should correspond to the midi Baudrate ' If we would write the adress of this procedure to the ' interrupt vector table, we would have a real interrupt handler... ' The poke- adress for this interrupt vektor can be calculated ' as : MidiIrqPointer% = %IrqNr * 4 ' The adres of PolMidiIn is :.... ' So we can do: Poke MidiIrqPointer, PolMidiIn_adrespointer ! push ax ! push dx ! mov dx, %Madr ; set to MPU adres ! inc dx ; set to status port adres ! in al,dx ; read status ! test al, &H80 ; if bit 7 is 0, we have data to read, if 1 there is no data available ! jnz polmidiend ; jump if not zero: jump out if test returns 1, or no-data to read ! dec dx ; set to data-port again ! in al,dx ; read input byte, clear interrupt ! mov Inbyte?, al ; put the byte into the Basic variable polmidiend: ! pop dx ! pop ax CASE 1 ' soundblaster mode - this works fine ! push ax ! push dx ! mov dx, %SBadr ! add dl, &H0E ; set to read-buffer status port ! in al, dx ; read the readbuffer status port ! test al, &H80 ; als bit 7 = 1 = data, 0= geen data ! jz polSBmidiend ; indien 0, verlaat procedure ! sub dl, 4 ; set to read data port &HE-4=&HA ! in al, dx ; read inbound DSP data ! mov Inbyte?, al ; move data into Basic variable polSBmidiend: ! pop dx ! pop ax CASE 2 ' first we send DSPCommand &H30 ' This works, but we are missing bytes... ! push ax ! push dx ! mov dx, %SBadr ! add dl, &H0C ; set to DSP write command/data adres SbDSP2Min: ! in al, dx ! test al, &H80 ! jnz SbDSP2Min ; wait until ready ! mov al, &H30 ; send the DSP-command ! out dx, al ' now we can wait until the DSP is ready again... ! mov dx, %SBadr ! add dl, &H0E ; set to read-buffer status port ! in al, dx ; read the readbuffer status port ! test al, &H80 ! jz polSB2mnd ! sub dl, 4 ; set to read data port &HE-4=&HA ! in al, dx ; read inbound DSP data ! mov Inbyte?, al polSB2mnd: ! pop ax ! pop dx CASE ELSE END SELECT ' now, if a byte was received, we can write in to MiBuf IF Inbyte? < 248 THEN Mibuf = CHR$(Inbyte?) + Mibuf END IF IF MidiTimeStamp AND 1 THEN ' in MPU mode we could write the midi-timing bytes here... TiBuf = RIGHT$("000000"+ HEX$(msbTime?)+ HEX$(midTime?) + HEX$(lsbTime?),6)+ TiBuf END IF END SUB '---------------------------------------------------------------------------
Algemene opmerking:
Deze midifunkties, waarbij de hardware rechtstreeks aangesproken wordt zijn zelfs in gekompileerd Basic uiterst snel. Het programmeren in andere hoge talen zoals C levert geen substantiele tijdwinst op. Alleen assembler kan hier winst opleveren. Onder Windows in protected mode en onder OS2 leveren zij echter grote problemen op, omdat daar elk rechtstreeks aanspreken van de hardware zowat taboe is. Bovendien, zelfs al zou het toch kunnen, dan nog zijn deze multitasking operating systems nog steeds aanzienlijk trager dan het rechtstreeks werken onder DOS.
Wie werkt onder QBX V7.1 kan weliswaar zijn programmas kompileren voor gebruik op OS2 machines, maar moet dan wel instrukties zoals WAIT, INP, OUT, PEEK, POKE, VARPTR, BLOAD, BSAVE grotendeels missen! Het zelf ontwerpen van midi-software is dan niet meer eenvoudig doenbaar. Wanneer de hardware leverancier van de midi-kaart een bruikbare library ter beschikking stelt, is het nog enigszins doenbaar. Zonder zoiets vergt een en ander het doorworstelen van zo'n 20000 paginas technische dokumentatie rond Windows DLL's en OS2-references.
Sedert 1997 raden we studenten dan ook aan over te schakelen op het erg degelijke Power Basic, dat hardware access ook in protected mode en onder Windows ten volle mogelijk maakt. De diefstal die Bill Gates met zijn Windows en Visual Basic pleegde door zich het unieke gebruiksrecht van al onze PC hardware toe te eigenen, kan op deze wijze wat worden gekompenseerd.
In 1998 kwam van Power Basic een nieuwe 32-bit compiler beschikbaar, die in ruime mate de snelheid van C++ overtreft. De hiermee gekompileerde software draait uitsluitend onder 32-bit OS's zoals Win95, Win98 en WinNT. De resultaten doen de hiervoor gemaakte opmerkingen wel wat vervagen.
In de nieuwste versies van ons <GMT> programmeertaal zijn zowat alle denkbare procedures en funkties in verband met de aansturing van midi hardware opgenomen. Voor up to date informatie verwijzen we dan ook naar deze software en de dokumentatie erbij.
Filedate: [950501] /2005-10-17
Terug naar inhoudstafel kursus: <Index Kursus> | Naar homepage dr.Godfried-Willem RAES | Naar midi-standaard beschrijving |