{-----------------------------------------------------------------------------}
{  Test program for the MVInt94 unit.                                         }
{  This is a very simple program to test the functionality of the MVInt94     }
{  unit.  If PCM.COM is loaded, it will play the file specified on the        }
{  command line.  If the file is not a wave file, it defaults to 44.1khz, 16  }
{  bit mono.  You can change that using the constants below.                  }
{                                                                             }
{ Copyright 1993, Peter W. Cervasio                                           }
{-----------------------------------------------------------------------------}
PROGRAM TestPCM;
{$I mvInt94.def}    { compiler flags }

USES
    Dos,            { need some dos functionality }
    Crt,            { want our screen writes to be faster }
    Strings,        { using some nul terminated strings }
    MVInt94;        { the int 94 interface unit }

CONST
    DMADivisions = 4;       { number of partitions in our DMA buffer }
    DMADivSize   = 8192;    { size of each DMA buffer partition }

    DefaultRate = 44100;    { default values used if the file to play is not }
    DefaultBits = 16;       { a wave file.  If you want this to play .voc files }
    DefaultChan = 1;        { you'll have to make it do so yourself... this is }
                            { just a demonstration of how to use PCM.COM }

TYPE
    Partition = Array[1..DMADivSize] of byte;
    MemArray = Array[1..DMADivisions] of Partition;
    MPtr = ^MemArray;

VAR
    MyDMA       : DMABufType;       { record to pass to DMABuffer() }
    MyBuff      : MPtr;             { Pointer to my buffer }
    I           : Word;             { General counter variable }
    OldCounter  : Word;             { local counter to compare with DMACounter }
    InpFile     : File;             { our input file }
    Done        : Boolean;          { says we're done reading from inpfile }
    JunkPtr     : Pointer;          { general handy pointer for stuff }
    TotalBlocks : LongInt;          { Total number of blocks in file }
    WaveHeader  : WaveHeaderType;   { wavefile header }

{ Read in a DMA partition.  NumRead will return the number of bytes read }
{ from the file.  If it's less than what we asked for, then we're done   }
{ with the file, and can wait for the PCM routines to be finished with   }
{ what's already been read.                                              }
PROCEDURE FillDMASection (Counter: Word; VAR Done: Boolean);
VAR
    NumRead : Word;
BEGIN
    BlockRead (InpFile, MyBuff^[Counter], DmaDivSize, NumRead);
    if NumRead < DmaDivSize then
    begin
        Done := True;           { flag that we're done and }
                                { fill the rest of the buffer with silence }
        if WaveHeader.SampleSize = 16 then
            FillChar (MyBuff^[Counter,NumRead], DMADivSize - NumRead, $00)
        else
            FillChar (MyBuff^[Counter,NumRead], DMADivSize - NumRead, $80);
    end;
END;

{ Quick way out... free our buffer if needed, do a RemovePCM and halt }
PROCEDURE GetOutOfProgram;
BEGIN
    if assigned(MyBuff) then FreeMem(MyBuff, DMADivisions * DMADivSize);
    RemovePCM;
    Halt(1);
END;

{ Open the input file.  If it isn't found, then exit the program.  If we're }
{ playing a wave file, get the information from the file regarding sample   }
{ rate and size, and whether it mono or stereo.  If it's not, assume it a   }
{ raw PCM file and use the defaults defined above for sample rate, etc.     }
{ We use the WaveHeader record in either case to hold the information.      }
PROCEDURE OpenInputFile (S : String);
VAR
    TmpBuff : Array[0..4] of char;
    Result  : Word;
BEGIN
    Assign (InpFile, S);
    {$I-}
    Reset (InpFile, 1);
    {$I+}
    IF IOResult <> 0 then
    begin
        Writeln ('Input file not found');
        GetOutOfProgram;
    end;
    BlockRead (InpFile, TmpBuff, 4, Result);
    if Result < 4 then
    begin
        Writeln ('Unexpected end of file.');
        Close (InpFile);
        GetOutOfProgram;
    end;
    TmpBuff[4] := #0;

    if StrComp(TmpBuff, 'RIFF') <> 0 then
    begin
        seek (InpFile, 0);
        FillChar (WaveHeader, SizeOF(WaveHeader), 0);
        WaveHeader.SampleRate := DefaultRate;
        WaveHeader.SampleSize := DefaultBits;
        WaveHeader.Channels := DefaultChan;
        WaveHeader.WaveLength := FileSize (InpFile);
    end
    else
    begin
        seek (InpFile, 0);
        { This isn't a good way to do it... there may be extra junk in the }
        { header because of other programs messing it up.  This is just a  }
        { quick and dirty example program, though, so I'm not bothering to }
        { do checks and all that stuff.                                    }
        BlockRead (InpFile, WaveHeader, SizeOF(WaveHeader), Result);
        if Result <> SizeOF (WaveHeader) then
        begin
            Writeln ('Unexpected end of file.');
            Close (InpFile);
            GetOutOfProgram;
        end;
    end;
    TotalBlocks := WaveHeader.Wavelength div DMADivSize;
    if WaveHeader.Wavelength mod DMADivSize <> 0 then inc (TotalBlocks);
END;

{===============================[ Fly or Die ]================================}
{            -|-                                               -|-            }
{    -----===<*>===----- MAIN PROGRAM LOOP STARTS HERE -----===<*>===-----    }
{         o/     \o                                          o/   \o          }
{=============================================================================}

BEGIN
    { Check to see if we were given a file name to play }
    if ParamCount < 1 then
    begin
        Writeln ('TESTPCM filename.ext');
        Writeln ('   Please supply a file name to play.  Thanks.');
        halt(1);
    end;

    { Okay, try to open it and get sample rate, etc. }
    OpenInputFile (ParamStr(1));

    { According to Bart Crane at Media Vision, this isn't needed, but I am }
    { using it anyway.  It didn't hurt when I left it out, though...       }
    JunkPtr := InitMVSound;
    if not Assigned(JunkPtr) then
    begin
        Writeln ('InitMVSound failed...');
        Halt(1);            { don't need to use GetOutOfProgram yet }
    end;

    I := InitPCM;
    if I = 0 then
    begin
        Writeln ('InitPCM failed...');
        Halt(1);            { or here either }
    end;

    { Allocate our DMA buffer, making sure not to cross a 64k boundary }
    AllocateDMABuffer (JunkPtr, DMADivisions * DMADivSize);

    { Okay, tell the tsr about it }
    MyBuff := MPtr(JunkPtr);
    MyDMA.DMABuffer := MyBuff;
    MyDMA.BufKBSize := DMADivisions * DMADivSize div 1024;
    MyDMA.Partitions := DMADivisions;
    JunkPtr := DMABuffer(MyDMA);
    if not assigned(JunkPtr) then
    begin
        Writeln ('DMA Buffer setup failed');
        GetOutOfProgram;
    end;

    { Okay, fill up our DMA partitions.  We'll set the counter to 0 when we }
    { get ready to play, so when it starts incrementing it will tell us the }
    { #1 partition needs to be filled.                                      }
    Done := False;
    For I := 1 to DMADivisions do
        if not Done then FillDMASection (I, Done);
    DMACounter := 0; OldCounter := 0;

    { Set up the DMA interrupt callback routine, using one defined in the  }
    { MVInt94 unit.  Due to the way I'm accessing the pointer in the unit  }
    { we have to pass UserFunc a pointer that points to the procedure, not }
    { just the address of the function.  That may be fixed in the future.  }
    JunkPtr := UserFunc (EZDMA);
    if not assigned(JunkPtr) then
    begin
        Writeln ('User Function setup failed');
        GetOutOfProgram;
    end;

    PausePCM;           { Tell the PCM routines that we're paused. }


    { Set the sample rate, size, etc. }
    I := SetPCMInfo(WaveHeader.SampleSize, WaveHeader.Channels-1,
                    WaveHeader.SampleRate);
    if I <> 0 then
    begin
        Writeln ('SetPCMInfo failed.');
        GetOutOfProgram;
    end;


    I := PCMPlay;       { Okay, now tell it we're going to be playing }
    if I <> 0 then
    begin
        Writeln ('PCMPlay failed... it returned ', I);
        GetOutOfProgram;
    end;

    
    ResumePCM;          { Let's have some sound come out of the card now... }

    repeat
        if KeyPressed then if ReadKey = #27 then    { if ESC pressed }
        begin
            Close (Inpfile);                        { close our input file }
            StopPCM;                                { stop playing the data }
            GetOutOfProgram;                        { and get out of the program }
        end;
        if DMACounter <> OldCounter then            { if it's changed }
        begin
            Dec (TotalBlocks);                      { decrement total counter }
            if DMACounter > DMADivisions then
                DMACounter := 1;                    { handle loop around }
            OldCounter := DMACounter;               { save it for next time }
            if not done then
                FillDMASection (DMACounter, Done);  { fill buffer if some left }
        end;
    until TotalBlocks = 0;                          { we've played all of it! }

    StopPCM;                                        { stop the playback }
    Close (InpFile);                                { Close the file }
    RemovePCM;                                      { and we're done }
    Freemem (MyBuff, DMADivSize * DMADivisions);    { free up our DMA buffer }
END.

