{But, if you're set on MIDI, then the following is a unit I made. It's very crude, however, as it has a few serious limitations: 1. To change the port setting you must recompile the unit. 2. In no way does it support SMF (general midi) files...sorry. 3. The procedures I made are just simple note on and note off procedures. 4. I don't know if it will work with anything other than an external MIDI device...I think, if you want FM synthesis, you need another unit. If it does work, and you need more MIDI procedures for doing different things to the MIDI device (more than note on and off), then feel free to ask me, as I have a big reference for all functions. Anyway, here is the unit: Converted into unit format from a general midi program included in SWAG edition November 1994 --- feel free to put this into SWAG as well. New (simple) functions and procedures were added. Freeware. Unit conversion and new procedures made 1995 (converted into unit form by Sam McGrath) } unit gMidi; {---------==========---------} Interface {---------==========---------} var GMPort:word; {Status Port of the GM Device: default is 331h The data port is the statusPort-1} { GetByte returns a byte being output from the general MIDI device writeByte outputs a byte TO the GM device writeByteS outputs a byte to the GM device, but outputs it to the status port as opposed to the data port } function getByte:byte; function getByteS:byte; procedure writeByte(b:byte); procedure writeByteS(b:byte); {ResetGM initializes the GM device setNoteOn plays a note on the specified channel, with a specified note and velocity (velocity is how hard the key on the GM device is pressed. It is similar to volume but different, since some synths will change, for example, the attack time as well as the volume if different velocities are input into the device) setNoteOff stops playing the specified note on the specified channel. The value you give velocity has no effect. } procedure resetGM; procedure setNoteOn(channel,note,velocity:byte); procedure setNoteOff(channel,note,velocity:byte); {--------========----------} Implementation {--------========---------} Const Send = $80; Receive = $40; { Routines from SWAG: } { AL:=Command; } Procedure WriteGMCommand; Assembler; ASM MOV DX,GMPort {;DX:=GMStatusPort; } PUSH AX {;Save AX } XOR AX,AX {;AH:=TimeOutValue; } @@WaitLoop: { ;Prevent Infinite Loop with Timeout } DEC AH {; |If TimeOutCount=0 then } JZ @@TimeOut {;/ TimeOut; } {; Wait until GM is ready } IN AL,DX {; |If Not Ready then } AND AL,Receive {; | WaitLoop; } JNZ @@WaitLoop {;/ } @@TimeOut: POP AX {;Restore AX } OUT DX,AL {;Send Data } End; { ; AL:=Data } Procedure WriteGM; Assembler; ASM MOV DX,GMPort {;DX:=GMStatusPort; } PUSH AX {;Save AX } XOR AX,AX {;AH:=TimeOutValue; } @@WaitLoop: { ; Prevent Infinite Loop with Timeout } DEC AH {; |If TimeOutCount=0 then } JZ @@TimeOut {;/ TimeOut; } { ; Wait until GM is ready } IN AL,DX {; |If Not Ready then } AND AL,Receive {; | WaitLoop; } JNZ @@WaitLoop {;/ } @@TimeOut: POP AX {;Restore AX } DEC DX {;DX:=DataPort } OUT DX,AL {;Send Data } End; { Recieve a byte from the GM device } Function getByte:Byte; Assembler; ASM MOV DX,GMPort {;DX:=GMStatusPort; } PUSH AX {;Save AX } XOR AX,AX {;AH:=TimeOutValue; } @@WaitLoop: { ; Prevent Infinite Loop with Timeout } DEC AH {; |If TimeOutCount=0 then } JZ @@TimeOut {;/ TimeOut; } { ; Wait until GM is ready } IN AL,DX {; |If Not Ready then } AND AL,Send {; | WaitLoop; } JNZ @@WaitLoop {;/ } @@TimeOut: POP AX {;Restore AX } DEC DX {;DX:=DataPort } IN AL,DX {;Receive Data } End; Function getByteS:Byte; Assembler; ASM MOV DX,GMPort {;DX:=GMStatusPort; } PUSH AX {;Save AX } XOR AX,AX {;AH:=TimeOutValue; } @@WaitLoop: { ; Prevent Infinite Loop with Timeout } DEC AH {; |If TimeOutCount=0 then } JZ @@TimeOut {;/ TimeOut; } { ; Wait until GM is ready } IN AL,DX {; |If Not Ready then } AND AL,Send {; | WaitLoop; } JNZ @@WaitLoop {;/ } @@TimeOut: POP AX {;Restore AX } IN AL,DX {;Receive Data } End; {Initialize the GM device} Procedure ResetGM; Assembler; ASM { ;Reset GM } MOV DX,GMPort MOV AL,0FFh OUT DX,AL {; Get ACK } CALL getByte {; UART Mode } MOV AL,03Fh CALL WriteGMCommand End; {Turn on specified note on specified channel} Procedure SetNoteOn(Channel,Note,velocity:Byte); Assembler; ASM MOV AL,[Channel] ADD AL,90h Call WriteGM MOV AL,[Note] CALL WriteGM MOV AL,[velocity] CALL WriteGM End; {Turn off specified note on specified channel} Procedure SetNoteOff(Channel,Note,velocity:Byte); Assembler; ASM MOV AL,[Channel] ADD AL,80h Call WriteGM MOV AL,[Note] CALL WriteGM MOV AL,[velocity] CALL WriteGM End; {Write a single byte to the data port} procedure writeByte(b:byte); assembler; asm mov al, [b] call writeGM end; {Write a single byte to the status port} procedure writeByteS(b:byte);assembler; asm mov al, [b] call writeGMCommand end; begin {initialization} GMPort:=$331 end.