{$A-,B+,D+,E+,F+,G+,I+,L+,N-,O+,P+,Q+,R+,S+,T-,V+,X+}

{$M 16384,0,655360}

program NewMID;



(*

 * 2. Versuch fuer einen MID-Player

 *

 * laeuft wegen Timing-Problemen nicht komplett im Interrupt (wenn sehr

viele

 * Events hintereinander kommen, gibts Haenger)

 * Aber: ich arbeite daran :-)

 *

 * Hat jemand eine Idee oder ein System (Unit?), die 2 unterschiedlich

 * schnell getaktete Interrupts bietet (z.B. einer 768 mal pro sek., der

 * andere z.B. 50 mal pro sek., der erste muss aber einer hoehere

Prioritaet

 * wie der zweite haben. Noch besser waere: der 1. muss den 2. ausloesen

koennen)

 * [ Ich hoffe, das hat jetzt jeder verstanden :-) ]

 * Wer also sowas hat oder kennt, meldet sich mal bitte bei mir ;)

 *

 * Aber trotz allem: Verbesserungsvorschlaege, Lobesgesaenge,

Dankesschreiben

 * Kritik etc. an: Rene Werner@2:249/4060.3 oder hier im Echo (PASCAL.GER)

 *

 * (C) 1994,1995 by Rene Werner, Freeware

 * Dieser Source ist Freeware! Er darf in Sachen verwendet werden, fuer

 * die kein Geld verlangt wird (also nicht in Shareware oder gar

 * kommerziellen Programmen). Wenn jemand den Player in seinen eigenen

 * Programmen verwendet, wuerde ich mich ueber eine Kopie des Programms

sehr

 * freuen.

 * Fall jemand den Source aendert, dann moechte ich eine Kopie der

veraenderten

 * Version haben!

 *

 * 01.05.95 : 1.0 (RW) : fertig :-)

 *)



uses

    crt,dos;



type

    BPTR        = ^byte;



const

     { Fehlermeldungen fuer loadMidiFile() }

     lmf_ok             : string[2]  = 'OK';

     lmf_no_file        : string[35] = 'kann Datei nicht oeffnen';

     lmf_out_of_mem     : string[35] = 'nicht genug Speicher frei';

     lmf_no_mid_file    : string[35] = 'keine MIDI-Datei (''MTrk''fehlt)';

     lmf_mid_format_err : string[35] = 'unerwartete Daten in MIDI-Datei';

     lmf_not_typ01      : string[35] = 'MID-Datei ist nicht Typ 0 oder 1';

     lmf_missing_mtrk   : string[35] = 'kein ''MTrk'' am Track-Anfang';

     lmf_track_too_large: string[35] = 'Track ist zu gross ( > 65528 bytes)';

     lmf_too_many_tracks: string[35] = 'zu viele Tracks (max. 64)';



     { auf 'false' setzen um Ausgabe zu unterdruecken }

     debug              : boolean = true;



     { standard values for MPU-401: }

     Dataport           = $330; {Standard MPU-401 port NoteVals}

     Statusport         = dataport+1;

     Commandport        = dataport+1;

     DRR                = $40;  {Data Ready to Receive}

     DRS                = $80;  {Data Ready to Send}

     ACKmsg             = $FE;

     ResetCmd           = $FF;

     UartModeCmd        = $3F;



     hexChars: array [0..$F] of Char ='0123456789ABCDEF';



     { die Laengen der Midi-Events }

     event_lens : array[$80..$FF] of longint = (

                3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, { $8x }

                3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, { $9x }

                3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, { $Ax }

                3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, { $Bx }

                2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, { $Cx }

                2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, { $Dx }

                3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, { $Ex }

                1,1,3,2,1,1,1,1,1,1,1,1,1,1,1,1  { $Fx }

     );



var

   s              : string;

   tracks         : word;

   track_ptr      : array[1..64] of pointer;

   track_size     : array[1..64] of longint;

   play_ptr       : array[1..64] of BPTR;

   bp             : BPTR;

   play_delta     : array[1..64] of longint;

   old_cmds       : array[1..64] of byte;

   trk_finished   : array[1..64] of boolean;

   deltaticks     : word;

   bpm            : word;

   ints           : word;

   i              : word;

   il,jl          : longint;

   c              : char;

   b,meta         : byte;

   finished       : boolean;

   finished_t     : word;

   tick           : boolean;

   absDelta       : longint;



   call08         : boolean;

   oldint08       : procedure;



{ modint: Frequenz des Interrupts aendern }

procedure modint(frames:longint);

var

   Factor : longint;

begin

     asm cli end;

     port[$43]:=$36;                    { Steuerbefehl }

     Factor:=1193180 DIV frames;        { frequenz in Hz }

     port[$40]:=Lo(Factor);

     port[$40]:=Hi(Factor);

     asm sti end;

end;



{ sendet ein Midi-Byte an den MPU-Port }

procedure sendMidiByte(b : byte);

var

   junk : byte;

begin

     {Warten, bis MPU bereit zum empfangen ist...}

     while port[StatusPort] and DRR <> 0 do begin

           { Solange uns die MPU was senden will, holen wir es uns ;-) }

           while port[StatusPort] and DRS = 0 do

                 junk := port[Dataport];

     end;

     { Jetzt ist sie soweit ;) }

     port[Dataport] := b;

END;



{ Hex4: Umwandlung word in Hex-String }

function Hex4(w: Word):string;

var

   s : string;

begin

     s:=hexChars[Hi(w) shr 4]+hexChars[Hi(w) and $F]+hexChars[Lo(w) shr 4]+

        hexChars[Lo(w) and $F];

     Hex4:=s;

end;



{$F+}

{ der Timer-Interrupt }

procedure int08Handler(Flags, rCS, rIP, rAX, rBX, rCX, rDX,

                          rSI, rDI, rDS, rES, rBP: Word);

interrupt;

var

   i       : word;

begin

   tick:=true;

   inc(absDelta);



   if call08 then

   { call original system int 08 vector }

   begin

        asm

           pushf

        end;

        oldint08;

   end

   else

       port[$20]:=$20;

end;

{$F-}



{ verbiegt Timer-Int 08 und stellt Freuqenz ein }

procedure setint(frames:longint);

var

   Factor : longint;

begin

     port[$43]:=$36;                    { Steuerbefehl }

     Factor:=1193180 DIV frames;        { frequenz in Hz }

     port[$40]:=Lo(Factor);

     port[$40]:=Hi(Factor);



     call08 := false;

     GetIntVec($08, @oldint08);

     SetIntVec($08, Addr(int08Handler));

end;



{ Setzt Int 08 zurueck }

procedure endint;

var

   Factor : longint;

begin

     asm

        cli

     end;

     Factor := $FFFF;

     Port[$40] := Lo(Factor);    { Low-Byte fuer die Timer-Frequenz }

     Port[$40] := Hi(Factor);    { High-Byte fuer den Timer-Frequenz }



     SetIntVec($08, @oldint08);

     asm

        sti

     end;

end;



{ versucht, die MPU zurueckzusetzen, gibt TRUE zurueck, wenn eine MPU

  vorhanden ist und auf MIDI-Kommandos reagiert }

function MidiReset : boolean;

var

   junk           : byte;

   retries        : integer;

   timeout        : word;

begin

     MidiReset := true;

     { Try to clear pending data }

     timeout := 0;

     while (port[StatusPort] and DRS = 0) and (timeout < 10) do begin

           junk := port[Dataport];

           inc(timeout);

     end;



     for retries := 1 to 2 do begin

         timeout := 0;

         {Give it 3000 chances to say it

         is ready to receive}

         while (port[StatusPort] and DRR <> 0) and(timeout < 3000) do

               inc(timeout);



         if (timeout < 3000) then begin

            {Send the reset command}

            port[StatusPort] := ResetCmd;

            timeout := 0;

            {Give it 3000 tries to RESPOND to the

             reset with an ACKnowledge}

            while (TimeOut < 3000) do begin

                  if (port[StatusPort]and DRS = 0)

                  and (port[Dataport] = ACKmsg) then

                     exit;

                  inc(timeout);

            end;

         end;

     end;

     {If you get here, it failed to initialize}

     MidiReset := false;

end;



{ LoadMidiFile

      E: Dateiname der zu ladenden Datei

      A: Errorcode ('OK', wenn alles i.O. ist) }

function LoadMidiFile(midfn:string):string;

var

   f    : file;

   buf  : array[1..4] of byte;

   ts   : longint;

   i,j  : word;

begin

     { track_ptr und track_size auf 0 }

     for i:=1 to 64 do begin

         track_ptr[i]:=nil;

         track_size[i]:=0;

     end;

     if debug then writeln('DEBUG: versuche, ',midfn,' zu laden');

     FileMode:=0;

     assign(f,midfn);

     {$I-}

     reset(f,1);

     {$I+}

     if IOResult<>0 then begin

        LoadMidiFile:=lmf_no_file;

        exit;       { exit mit 'lmf_no_file' }

     end;

     FileMode:=2;

     { Header lesen und auswerten (MThd) }

     blockread(f,buf,4);

     if not((buf[1]=ord('M')) and (buf[2]=ord('T')) and (buf[3]=ord('h'))

     and (buf[4]=ord('d'))) then begin

         close(f);

         LoadMidiFile:=lmf_no_mid_file;

         exit;              { exit mit 'lmf_no_mid_file' }

     end;

     blockread(f,buf,4);

     ts:=longint(buf[1]) shl 24+longint(buf[2]) shl 16+longint(buf[3]) shl

8+

     longint(buf[4]);

     if ts<>6 then begin

        close(f);

        LoadMidiFile:=lmf_mid_format_err;

        exit; { MThd nicht 6 Bytes gross ! }

     end;

     blockread(f,buf,2);

     if buf[1]<>0 then begin

        close(f);

        LoadMidiFile:=lmf_mid_format_err;

        exit; { format error, muss 0 sein ! }

     end;

     if not((buf[2]=0) or (buf[2]=1)) then begin

        close(f);

        LoadMidiFile:=lmf_not_typ01;

        exit; { nicht typ 0 oder 1, 2 wird nicht unterstuetzt }

     end;

     if debug then writeln('DEBUG: MIDI-Datei Typ ',buf[2]);

     blockread(f,buf,2);

     tracks:=word(buf[1]) shl 8+word(buf[2]);

     if debug then writeln('DEBUG: Anzahl Tracks: ',tracks);

     if tracks>64 then begin

        close(f);

        LoadMidiFile:=lmf_too_many_tracks;

        exit;

     end;

     blockread(f,buf,2);

     deltaticks:=word(buf[1]) shl 8+word(buf[2]);

     if debug then writeln('DEBUG: DeltaTicks pro Viertelnote:',deltaticks);

     if debug then writeln('DEBUG: lade Tracks... (',tracks,')');

     for i:=1 to tracks do begin

         if debug then writeln('DEBUG: lade Spur #',i);

         blockread(f,buf,4);    { MTrk }

         if not((buf[1]=ord('M')) and (buf[2]=ord('T')) and

(buf[3]=ord('r'))

         and (buf[4]=ord('k'))) then begin

             close(f);

             LoadMidiFile:=lmf_missing_mtrk;

             exit;

         end;

         blockread(f,buf,4);

         ts:=longint(buf[1]) shl 24+longint(buf[2]) shl 16+

         longint(buf[3]) shl 8+longint(buf[4]);

         if MemAvail65528 then begin

            close(f);

            LoadMidiFile:=lmf_track_too_large;

            exit;

         end;

         track_size[i]:=ts;

         GetMem(track_ptr[i],ts);

         blockread(f,track_ptr[i]^,ts);

     end;



     loadMidiFile:=lmf_ok;

     close(f);

end;



begin

     clrscr;

     if paramcount<1 then begin

        writeln('Aufruf: ',paramstr(0),' midfilename');

        halt;

     end;



     TextMode(CO80+Font8x8);

     if not MidiReset then begin

       s:=hexChars[Hi(dataport) and $F]+hexChars[Lo(dataport) shr 4]+

          hexChars[Lo(dataport) and $F];

        writeln('Kann MPU an Port '+s+'h nicht initialisieren!');

{        halt;}

 { Dieser Block wird auf meinem Terratec MiniWaveSystem immer ausgel”st.

   Danach funktioniert die MIDI-Ausgabe dennoch einwandfrei.

    - Arno (F_Fleck@compuserve.com) }

     end

     else

         if debug then writeln('DEBUG: MPU ok.');

     repeat { MPU in UART-mode bringen }

     until (port[StatusPort] and DRR) = 0;

     port[CommandPort] := UartModeCmd;

     while port[StatusPort] and DRS <> 0 DO;



     s:=loadMidiFile(paramstr(1));

     if s='OK' then

        writeln('MID-Datei erfolgreich geladen')

     else begin

         writeln('loadMidiFile() brachte folgenden Fehler:');

         writeln(' -> ',s);

         for i:=1 to 64 do

             if track_ptr[i]<>nil then

                FreeMem(track_ptr[i],track_size[i]);

         halt;

     end;



     { player initialisieren }

     for i:=1 to tracks do begin

         bp:=track_ptr[i];

         il:=ord(bp^) AND $7F;

         while ord(bp^)>127 do begin

               il:=il SHL 7;

               inc(bp);

               il:=il+(ord(bp^) AND $7F);

         end;

         inc(bp);

         play_delta[i]:=il+1;

         play_ptr[i]:=bp;  { korrigierter Ptr, zeigt auf 1. Event }

         trk_finished[i]:=false;

     end;



     bpm:=120;  { default }

     ints:=word((longint(bpm)*longint(deltaticks)) div 60);



     if debug then begin

        write('Druecke eine Taste...');

        if readkey=#0 then readkey;



        clrscr;

        asm { Cursor aus }

           xor     cl,cl

           mov     ch,36

           mov     ah,1

           int     10h

        end;

        gotoxy(1,3);

        write('Track     Current');

        for i:=1 to tracks do begin

            gotoxy(1,4+i);

            write('  ',i:2,'  : $',hex4(seg(play_ptr[i]^)),':',

                  hex4(ofs(play_ptr[i]^)));

        end;

        gotoxy(10,1);

        write('Speed: ',bpm:3,' BPM ; ',deltaticks:4,'Ticks/Quarternote');

     end;



     finished:=false;

     finished_t:=0;



     c:=#0;

     tick:=false;

     setint(longint(ints));     { start player }

     while not tick do;

     absDelta:=0;



     repeat

           if not finished then begin

              for i:=1 to tracks do begin

                  while ((not(trk_finished[i])) and

                  (play_delta[i]<=absDelta)) do begin

                        bp:=play_ptr[i];

                        if bp^<$80 then

                           b:=old_cmds[i]

                        else begin

                             b:=bp^;

                             inc(bp);

                        end;

                        old_cmds[i]:=b;

                        if b=$ff then begin { meta event ? }

                           meta:=bp^;

                           inc(bp);

                           { laenge des meta-events holen }

                           il:=ord(bp^) AND $7F;

                           while ord(bp^)>127 do begin

                                 il:=il SHL 7;

                                 inc(bp);

                                 il:=il+(ord(bp^) AND $7F);

                           end;

                           inc(bp);

                           case meta of

                                $2f: begin { track ende }

                                           if debug then begin

                                              gotoxy(15,4+i);

                                              write('---- (track ende)');

                                           end;

                                           trk_finished[i]:=true;

                                           inc(finished_t);

                                           if finished_t=tracks then

                                              finished:=true;

                                     end;

                                $51: begin { set tempo }

                                           il:=ord(bp^);

                                           inc(bp);

                                           il:=(il SHL 8)+ord(bp^);

                                           inc(bp);

                                           il:=(il SHL 8)+ord(bp^);

                                           inc(bp);

                                           bpm:=word(60000000 DIV il);

                                          

                                           ints:=word((longint(deltaticks)*

                                                 longint(bpm)) div 60);

                                           if debug then begin

                                              gotoxy(10,1);

                                              write('Speed: ',bpm:3,

                                                    ' BPM ;',deltaticks:4,

                                                    ' Ticks/Quarternote');

                                           end;

                                           { Interrupt-Frequenz aendern }

                                           modint(longint(ints));

                                     end;

                                $03: begin { Trackname }

                                          if debug then begin

                                             gotoxy(20,4+i);

                                             for jl:=1 to 11 do

                                                 write('     ');

                                             gotoxy(20,4+i);

                                             for jl:=1 to il do begin

                                                 write(char(bp^));

                                                 inc(bp);

                                             end;

                                          end else

                                              for jl:=1 to il do

                                                  inc(bp);

                                     end;

                                else for jl:=1 to il do

                                         inc(bp); { meta-event uebergehen

}

                           end;

                        end { if b=$ff }

                        else begin

                             il:=event_lens[b]; { laenge des events }

                             if ((b=$f0) or (b=$f7)) then begin { sysex ?

}

                                il:=ord(bp^) AND $7F; { laenge holen }

                                while ord(bp^)>127 do begin

                                      il:=il SHL 7;

                                      inc(bp);

                                      il:=il+(ord(bp^) AND $7F);

                                end;

                                inc(bp);

                                { anzahl bytes in il }

                                if b=$f0 then begin { beginn eines sysex ?

}

                                   while port[StatusPort] and DRR <> 0 do;

                                   port[Dataport] := $0f0; {sysex senden}

                                end;

                                for jl:=1 to il do begin

                                    while port[StatusPort] and DRR <> 0

do;

                                    port[Dataport] := bp^;

                                    inc(bp);

                                end;

                             end else begin

                                 { Byte in b }

                                 (*

                                 while port[StatusPort] and DRR <> 0 do;

                                 port[Dataport] := b;

                                 *)

                                 asm

                                    mov dx,StatusPort

                                  @1:

                                    in  al,dx

                                    and al,DRR

                                    jnz @1

                                    mov al,[b]

                                    dec dx   { DataPort = StatusPort - 1 }

                                    out dx,al

                                 end;

                                 if il>=2 then begin {Event mit 2 oder 3

Bytes}

                                    b:=bp^; { Byte holen }

                                    (*

                                      while port[StatusPort] and DRR <> 0

do;

                                      port[Dataport] := bp^;

                                    *)

                                    asm

                                       mov dx,StatusPort

                                    @2:

                                       in  al,dx

                                       and al,DRR

                                       jnz @2

                                       mov al,[b]

                                       dec dx

                                       out dx,al

                                    end;

                                    inc(bp);

                                    if il=3 then begin { 3. Byte ?? }

                                       b:=bp^; { Byte holen }

                                       (*

                                         while port[StatusPort] and DRR<>0

do;

                                         port[Dataport] := bp^;

                                       *)

                                       asm

                                          mov dx,StatusPort

                                        @2:

                                          in  al,dx

                                          and al,DRR

                                          jnz @2

                                          mov al,[b]

                                          dec dx

                                          out dx,al

                                       end;

                                       inc(bp);

                                    end; { if il=3 }

                                 end; { if il>=2 }

                             end; { if ((b=$f0) or (b=$f7)) -- else end }

                        end; { if b=$ff -- else end}

                        if not trk_finished[i] then begin

                           il:=ord(bp^) AND $7F;

                           while ord(bp^)>127 do begin

                                 il:=il SHL 7;

                                 inc(bp);

                                 il:=il+(ord(bp^) AND $7F);

                           end;

                           inc(bp);

                           play_delta[i]:=play_delta[i]+il;

                           play_ptr[i]:=bp;

                           if debug then begin

                              gotoxy(15,4+i);

                              write(hex4(ofs(play_ptr[i]^)));

                           end;

                        end;

                  end; { while play_delta[i]=0 }

              end; { for i:=1 to tracks }

           end; { if not finished }

           if keypressed then begin

              c:=readkey;

              if c=#0 then readkey;

           end;

     until ((c=#27) or (finished));



     { Interrupt wiederherstellen }

     endint;



     { bringen wir das Midi-Equipment mal zur Ruhe }

     for b:=0 to 15 do begin

         sendMidiByte($B0+b);   { All notes off }

         sendMidiByte($7B);

         sendMidiByte($00);

         sendMidiByte($B0+b);   { All sounds off }

         sendMidiByte($78);

         sendMidiByte($00);

         sendMidiByte($B0+b);   { Reset all controllers }

         sendMidiByte($79);

         sendMidiByte($00);

     end;



     { Speicher freigeben }

     for i:=1 to 64 do

         if track_ptr[i]<>nil then

            FreeMem(track_ptr[i],track_size[i]);

     TextMode(CO80);



     { und last but not least: wir bringen die Systemzeit in Ordnung :-) }

     asm

       XOR     AL,AL

       OUT     70h,AL

       IN      AL,71h

       MOV     DH,AL

       AND     DH,15

       SHR     AL,4

       MOV     DL,10

       MUL     DL

       ADD     DH,AL



       MOV     AL,2

       OUT     70h,AL

       IN      AL,71h

       MOV     CL,AL

       AND     CL,15

       SHR     AL,4

       MOV     DL,10

       MUL     DL

       ADD     CL,AL



       MOV     AL,4

       OUT     70h,AL

       IN      AL,71h

       MOV     CH,AL

       AND     CH,15

       SHR     AL,4

       MOV     DL,10

       MUL     DL

       ADD     CH,AL



       XOR     DL,DL

       MOV     AH,2Dh

       INT     21h



       MOV     AL,7

       OUT     70h,AL

       IN      AL,71h

       MOV     DL,AL

       AND     DL,15

       SHR     AL,4

       MOV     CH,10

       MUL     CH

       ADD     DL,AL



       MOV     AL,8

       OUT     70h,AL

       IN      AL,71h

       MOV     DH,AL

       AND     DH,15

       SHR     AL,4

       MOV     CH,10

       MUL     CH

       ADD     DH,AL



       MOV     AL,4

       OUT     70h,AL

       IN      AL,71h

       MOV     CL,AL

       AND     CL,15

       SHR     AL,4

       MOV     CH,10

       MUL     CH

       ADD     CL,AL



       XOR     CH,CH

       ADD     CX,1900

       MOV     AH,2Bh

       INT     21h

     end;



end.




    Source: geocities.com/~franzglaser/tpsrc

               ( geocities.com/~franzglaser)