[Back to SOUND SWAG index] [Back to Main SWAG index] [Original]

    {
    > Umm, I don't think you understood my question...  I don't know how
    > to play 8 or 16 bit sound  (Like a mod file), I CAN do FM...
    > I just want help on making the routines to play things like a
    > mod...
    > Gotta, go...  Could you please see if you could help me on this,
    > though? Justin Greer

     Here is some Pascal source code to Read a (Standard) MOD 4 Tracks:

     READ it's not play, that is another story...

     Here is some example files that you sould see:
     ==============================================
     DEMOVT15.ZIP    MOD's 8 Borland C/ MS C/ (Real Mode/Protected Mode)
                     PAS/C/Assembly
     GOLDPALY.ZIP    MOD's 4 tracks Assembly/Pascal
     MDSS031A.ZIP    MOD's e S3M's  C/Pascal/Assembly
     PPS.ZIP         MOD's 4 tracks Assembly
     TNYPL212.ZIP    MOD's 8 tracks C/Assembly (Real Mode/Protected Mode)
     VTSRC12B.ZIP    MOD's 8 Pascal source of a player
    }

    Unit MODTool;
    {* Reads information from a Soundtracker module. *}
    INTERFACE
    CONST
       MODToolVersion = 'v1.0';
       MaxIns = 31;
       Octaves : ARRAY[1..36] OF WORD =
          (856,808,762,720,678,640,604,570,538,508,480,453,
           428,404,381,360,339,320,302,285,269,254,240,226,
           214,202,190,180,170,160,151,143,135,127,120,113);
    TYPE
       InstrumentType = RECORD
          SampName : ARRAY[0..21] OF CHAR;
          SampLen  : WORD;
          SampTune : BYTE;
          SampAmp  : BYTE;
          SampRepS : WORD;
          SampRepL : WORD;
          END;
       MODHeaderType = RECORD
          MODName  : ARRAY[0..19] OF CHAR;
          MODInstr : ARRAY[1..MaxIns] OF InstrumentType;
          MODLen   : BYTE;
          MODMisc  : BYTE;
          MODPattr : ARRAY[1..128] OF BYTE;
          MODSign  : ARRAY[1..4] OF CHAR;
          END;
       NoteType    = ARRAY[1..4] OF BYTE;
       PatternLine = RECORD
          Channel1, Channel2,
          Channel3, Channel4  : NoteType;
          END;
       PatternType = ARRAY[1..64] OF PatternLine;
    PROCEDURE BuildModScript(ModFilename, ScriptFilename : STRING);
    IMPLEMENTATION
    FUNCTION ConvertString(Source : Pointer; Size : BYTE):String;
    {* INPUT   : Pointer to an ARRAY OF CHAR, length in BYTES
     * OUTPUT  : Pascal string in Size bytes length
     * PURPOSE : Convertor, e.g., converts string ending in NULL to a Pascal
                  string. This routine can convert any other memory range
                  into a string. }
    VAR
       WorkStr : String;
    BEGIN
       Move(Source^,WorkStr[1],Size);
       WorkStr[0] := CHR(Size);
       ConvertString := WorkStr;
       END;
    FUNCTION Words(FalseWord : WORD):WORD;
    {* INPUT   : Word variable with rotated high/low-byte
     * OUTPUT  : Restored Word, multiplied by 2
     * PURPOSE : Gets a Word value from the MOD file and restores the proper
                 high-byte/low-byte sequence; also multiplies the result by
                 2, as this function must store the information as Word units
                 to coincide with sample length. }
    BEGIN
       Words := (Hi(FalseWord)+Lo(FalseWord)*256)*2;
       END;

    FUNCTION NoteName(Period : WORD):String;
    {* INPUT   : Note length value as WORD
     * OUTPUT  : Note in text as string
     * PURPOSE : Using tuning table, converts pitch values to note names. }
    CONST
       NNames : ARRAY[0..11] OF String[2] =
          ('C-','C#','D-','D#','E-','F-','F#','G-','G#','A-','A#','B-');
    VAR
       WorkStr : String;
       NCount  : BYTE;
    BEGIN
       NCount := 1;
       IF (Period = 0) THEN BEGIN
          NCount := 37;
          NoteName := '----';
          END;
       WHILE (NCount <= 36) DO BEGIN
          IF (Period = Octaves[NCount]) THEN BEGIN
             Dec(Ncount);
             Str((NCount DIV 12)+1:2,WorkStr);
             NoteName := NNames[NCount-(NCount DIV 12)*12]+WorkStr;
             NCount := 37;
             END;
          Inc(NCount);
          END;
       END;
    PROCEDURE BuildModScript(ModFilename, ScriptFilename : STRING);
    {* INPUT   : Module name and name of desired script file
     * OUTPUT  : None
     * PURPOSE : Reads a SoundTracker module and writes the most important
                 information to a text file.  The module _must_ contain 31
                 instruments, or incorrect results will occur.
                 Patterns are stored in sequence. }
    VAR
       ModFile  : File;
       ScrFile  : TEXT;
       Result   : WORD;
       Header   : ModHeaderType;
       DummyStr : String;
       InsCount : BYTE;
       PatCount : BYTE;
       Pattern  : PatternType;
       HiPatt   : BYTE;
       Counter  : WORD;
    BEGIN
    { Make sure that desired MOD file is available }
    {$I-}
       Assign(ModFile,ModFilename);
       Reset(ModFile);
       Close(ModFile);
    {$I+}
       IF (IOResult <> 0) THEN BEGIN
          Writeln('Cannot find MOD file: ',ModFilename:12,'. Sorry.');
          HALT(100);
          END;
    { Read MOD header data into Header variable }
       Reset(ModFile,1);
       BlockRead(ModFile,Header,SizeOf(Header),Result);
    { Analyze data in Header }
       WITH Header DO BEGIN
          Assign(ScrFile,ScriptFilename);
          ReWrite(ScrFile);
          WriteLn(ScrFile,'Soundtracker module script file ',ModFilename);
          WriteLn(ScrFile);
    { Write module name }
          DummyStr := ConvertString(Addr(MODName),SizeOf(MODName));
          WriteLn(ScrFile,'Module name  : ',DummyStr);
    { Write module length (valid numbers = 1 - 128) }
          Str(MODLen,DummyStr);
          Write  (ScrFile,'Module length  : ',DummyStr, ' Pattern(s),');
    { Search for highest pattern number }
          HiPatt := 0;
          FOR PatCount := 1 TO MODLen DO
             IF ModPattr[PatCount] >= HiPatt THEN
                HiPatt := ModPattr[PatCount];
          Str(HiPatt,DummyStr);
          WriteLn(ScrFile,' - highest pattern number is ',DummyStr);
          WriteLn(ScrFile);
    { Write instrument information }
          FOR InsCount := 1 TO MaxIns DO BEGIN
             WITH MODInstr[InsCount] DO BEGIN
                DummyStr := ConvertString(Addr(SampName),SizeOf(SampName));
                WriteLn(ScrFile,'Instrument # ',InsCount:2,' = ',DummyStr);
                Str(Words(SampLen):6,DummyStr);
                WriteLn(ScrFile,'Length in bytes = ',DummyStr);
                Str(SampTune:6,DummyStr);
                WriteLn(ScrFile,'Fine tune       = ',DummyStr);
                Str(SampAmp:6,DummyStr);
                WriteLn(ScrFile,'Volume          = ',DummyStr);
                Str(Words(SampRepS):6,DummyStr);
                WriteLn(ScrFile,'Repeat start    = ',DummyStr);
                Str(Words(SampRepL):6,DummyStr);
                WriteLn(ScrFile,'Repeat length   = ',DummyStr);
                WriteLn(ScrFile,'----------------------------------------');
                END;
          END;
    { Read patterns and write note values into script file }
    { (parentheses following pitch name contain sample number of note) }
          FOR PatCount := 1 TO HiPatt+1 DO BEGIN
             IF NOT(EOF(ModFile)) THEN
                Blockread(ModFile,Pattern,SizeOf(Pattern),Result);
             WriteLn('Read pattern ',PatCount-1:3);
             WriteLn(ScrFile,'Pattern number : ',PatCount-1:3);
             WriteLn(ScrFile,'Lines #   Chan.1     Chan.2     Chan.3     Chan.4');
             FOR Counter := 1 TO 64 DO BEGIN
                Write(ScrFile,'  ',Counter:2,'    ');
                WITH Pattern[Counter] DO BEGIN
    { Display note and sample number for channel 1 }
                   DummyStr := NoteName((Channel1[1] AND $0F)*256+(Channel1[2]));
                   Write(ScrFile,' ',DummyStr);
                   Write(ScrFile,'(',((Channel1[1] AND $F0)+
                                      (Channel1[3] SHR 4)):2,')  ');
    { Display note and sample number for channel 2 }
                   DummyStr := NoteName((Channel2[1] AND $0F)*256+(Channel2[2]));
                   Write(ScrFile,' ',DummyStr);
                   Write(ScrFile,'(',((Channel2[1] AND $F0)+
                                      (Channel2[3] SHR 4)):2,')  ');
    { Display note and sample number for channel 3 }
                   DummyStr := NoteName((Channel3[1] AND $0F)*256+(Channel3[2]));
                   Write(ScrFile,' ',DummyStr);
                   Write(ScrFile,'(',((Channel3[1] AND $F0)+
                                      (Channel3[3] SHR 4)):2,')  ');
    { Display note and sample number for channel 4 }
                   DummyStr := NoteName((Channel4[1] AND $0F)*256+(Channel4[2]));
                   Write(ScrFile,' ',DummyStr);
                   Write(ScrFile,'(',((Channel4[1] AND $F0)+
                                      (Channel4[3] SHR 4)):2,')  ');
                   WriteLn(ScrFile);
                   END;
                END;
             WriteLn(ScrFile,'-------------------------------------------------');
             WriteLn(ScrFile);
             END;
    { Close script file and MOD file }
          Close(ScrFile);
          CLose(ModFile);
          END;
       END;
    END.

    {

    MAIN FILE to test MODTOOL.PAS
    -------------------------------- MODSCRIP.MOD ------------------------------
    }
    Program MODScript;
    {* Demo program for MODTOOL unit: Creating a script file *}
    Uses MODTool; { Use MODTool unit }
    VAR
       WorkStr : String; { String for processing provided filename }
    BEGIN
       WriteLn('MODScript v1.0 (C) 1992 Abacus -  Author: Axel Stolz');
    { No command line parameter given? display syntax }
       IF (ParamCount = 0) THEN BEGIN
          WriteLn('Syntax : MODSCRIP module[.MOD]');
          HALT(0);
          END;
    { Pass command line paramters }
       WorkStr := ParamStr(1);
    { If user enters filename and extension, replace extension with ".MOD"}
       IF Pos('.',WorkStr) > 0 THEN
          WorkStr := Copy(WorkStr,1,Pos('.',WorkStr)-1);
       WriteLn('Create a script file from a sound module ',WorkStr,'.MOD !');
    { Create script file }
       BuildModScript(WorkStr+'.MOD',WorkStr+'.TXT');
       WriteLn('Ready. Result stored in ',WorkStr,'.TXT.');
    END.

[Back to SOUND SWAG index] [Back to Main SWAG index] [Original]