[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]