{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.
