From BROWNVM.BROWN.EDU!BROWNVM.BROWN.EDU!LISTSERV Sun May 16 13:48:11 1993
Received: from BROWNVM.brown.edu ([128.148.128.40]) by niksula.hut.fi with SMTP id <61842-12>; Sun, 16 May 1993 13:48:02 +0300
Received: from BROWNVM.BROWN.EDU by BROWNVM.brown.edu (IBM VM SMTP V2R2)
with BSMTP id 1856; Sun, 16 May 93 06:47:31 EDT
Received: from BROWNVM.BROWN.EDU (NJE origin LISTSERV@BROWNVM) by BROWNVM.BROWN.EDU (LMail V1.1d/1.7f) with BSMTP id 0653; Sun, 16 May 1993 06:47:30 -0400
Date: Sun, 16 May 1993 13:47:16 +0300
From: BITNET list server at BROWNVM (1.7f)
Subject: Rejected posting to IBMSND-L@BROWNVM
To: [email protected]
Message-Id: <[email protected]>
Status: OR
You are not authorized to mail to list IBMSND-L from your [email protected]
account. You might be authorized to post to the list from another of your
accounts or under a slightly different address, but LISTSERV has no way to
associate this other account or address with you and is thus rejecting your
message. Your message is being returned to you unprocessed. If you have any
question regarding authorization to use the IBMSND-L list, please contact one
of the list owners, whose names and addresses are listed below:
[email protected]
[email protected]
------------------------ Rejected message (766 lines) -------------------------
Return-Path: <@BROWNVM.BROWN.EDU:[email protected]>
Received: from BROWNVM (NJE origin SMTP@BROWNVM) by BROWNVM.BROWN.EDU (LMail V1.1d/1.7f) with BSMTP id 0651; Sun, 16 May 1993 06:47:18 -0400
Received: from joker.cs.hut.fi by BROWNVM.brown.edu (IBM VM SMTP V2R2) with TCP;
Sun, 16 May 93 06:47:13 EDT
Received: from zelda.cs.hut.fi by niksula.hut.fi id <61823-8>; Sun, 16 May 1993 13:40:18 +0300
Subject: Re: .MOD file format
From: Tomi Holger Engdahl
To: [email protected]
Date: Sun, 16 May 1993 14:39:53 +0300
In-Reply-To: <[email protected]> from "Bill Gerrard" at May 14, 93 08:01:46 pm
X-Mailer: ELM [version 2.4 PL17]
MIME-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Length: 27600
Message-Id: <[email protected]>
> I am looking for info on .MOD file format. I would like to
> extract info from .MOD files (i.e. name, instruments, etc.) and was wondering
> if the file format info was floating around on the net somewhere.
Here are two articles that I have found about .mod format.
Those articles should tell you how .mod format really works.
----------------------------------------------------------------------------
Protracker V1.1B Effect Commands
----------------------------------------------------------------------------
0 - Normal play or Arpeggio 0xy : x-first halfnote add, y-second
1 - Slide Up 1xx : upspeed
2 - Slide Down 2xx : downspeed
3 - Tone Portamento 3xx : up/down speed
4 - Vibrato 4xy : x-speed, y-depth
5 - Tone Portamento + Volume Slide 5xy : x-upspeed, y-downspeed
6 - Vibrato + Volume Slide 6xy : x-upspeed, y-downspeed
7 - Tremolo 7xy : x-speed, y-depth
8 - NOT USED
9 - Set SampleOffset 9xx : offset (23 -> 2300)
A - VolumeSlide Axy : x-upspeed, y-downspeed
B - Position Jump Bxx : songposition
C - Set Volume Cxx : volume, 00-40
D - Pattern Break Dxx : break position in next patt
E - E-Commands Exy : see below...
F - Set Speed Fxx : speed (00-1F) / tempo (20-FF)
----------------------------------------------------------------------------
E0- Set Filter E0x : 0-filter on, 1-filter off
E1- FineSlide Up E1x : value
E2- FineSlide Down E2x : value
E3- Glissando Control E3x : 0-off, 1-on (use with tonep.)
E4- Set Vibrato Waveform E4x : 0-sine, 1-ramp down, 2-square
E5- Set Loop E5x : set loop point
E6- Jump to Loop E6x : jump to loop, play x times
E7- Set Tremolo Waveform E7x : 0-sine, 1-ramp down. 2-square
E8- NOT USED
E9- Retrig Note E9x : retrig from note + x vblanks
EA- Fine VolumeSlide Up EAx : add x to volume
EB- Fine VolumeSlide Down EBx : subtract x from volume
EC- NoteCut ECx : cut from note + x vblanks
ED- NoteDelay EDx : delay note x vblanks
EE- PatternDelay EEx : delay pattern x notes
EF- Invert Loop EFx : speed
----------------------------------------------------------------------------
Have you ever wondered how a Protracker 1.1B module is built up?
Well, here's the...
Protracker 1.1B Song/Module Format:
-----------------------------------
Offset Bytes Description
------ ----- -----------
0 20 Songname. Remember to put trailing null bytes at the end...
Information for sample 1-31:
Offset Bytes Description
------ ----- -----------
20 22 Samplename for sample 1. Pad with null bytes.
42 2 Samplelength for sample 1. Stored as number of words.
Multiply by two to get real sample length in bytes.
44 1 Lower four bits are the finetune value, stored as a signed
four bit number. The upper four bits are not used, and
should be set to zero.
Value: Finetune:
0 0
1 +1
2 +2
3 +3
4 +4
5 +5
6 +6
7 +7
8 -8
9 -7
A -6
B -5
C -4
D -3
E -2
F -1
45 1 Volume for sample 1. Range is $00-$40, or 0-64 decimal.
46 2 Repeat point for sample 1. Stored as number of words offset
from start of sample. Multiply by two to get offset in bytes.
48 2 Repeat Length for sample 1. Stored as number of words in
loop. Multiply by two to get replen in bytes.
Information for the next 30 samples starts here. It's just like the info for
sample 1.
Offset Bytes Description
------ ----- -----------
50 30 Sample 2...
80 30 Sample 3...
.
.
.
890 30 Sample 30...
920 30 Sample 31...
Offset Bytes Description
------ ----- -----------
950 1 Songlength. Range is 1-128.
951 1 Well... this little byte here is set to 127, so that old
trackers will search through all patterns when loading.
Noisetracker uses this byte for restart, but we don't.
952 128 Song positions 0-127. Each hold a number from 0-63 that
tells the tracker what pattern to play at that position.
1080 4 The four letters "M.K." - This is something Mahoney & Kaktus
inserted when they increased the number of samples from
15 to 31. If it's not there, the module/song uses 15 samples
or the text has been removed to make the module harder to
rip. Startrekker puts "FLT4" or "FLT8" there instead.
Offset Bytes Description
------ ----- -----------
1084 1024 Data for pattern 00.
.
.
.
xxxx Number of patterns stored is equal to the highest patternnumber
in the song position table (at offset 952-1079).
Each note is stored as 4 bytes, and all four notes at each position in
the pattern are stored after each other.
00 - chan1 chan2 chan3 chan4
01 - chan1 chan2 chan3 chan4
02 - chan1 chan2 chan3 chan4
etc.
Info for each note:
_____byte 1_____ byte2_ _____byte 3_____ byte4_
/ \ / \ / \ / \
0000 0000-00000000 0000 0000-00000000
Upper four 12 bits for Lower four Effect command.
bits of sam- note period. bits of sam-
ple number. ple number.
Periodtable for Tuning 0, Normal
C-1 to B-1 : 856,808,762,720,678,640,604,570,538,508,480,453
C-2 to B-2 : 428,404,381,360,339,320,302,285,269,254,240,226
C-3 to B-3 : 214,202,190,180,170,160,151,143,135,127,120,113
To determine what note to show, scan through the table until you find
the same period as the one stored in byte 1-2. Use the index to look
up in a notenames table.
This is the data stored in a normal song. A packed song starts with the
four letters "PACK", but i don't know how the song is packed: You can
get the source code for the cruncher/decruncher from us if you need it,
but I don't understand it; I've just ripped it from another tracker...
In a module, all the samples are stored right after the patterndata.
To determine where a sample starts and stops, you use the sampleinfo
structures in the beginning of the file (from offset 20). Take a look
at the mt_init routine in the playroutine, and you'll see just how it
is done.
Lars "ZAP" Hamre/Amiga Freelancers
Protracker CIA (Complex Interface Adapter) Timer Tempo Calculations:
--------------------------------------------------------------------
Fcolor = 4.43361825 MHz (PAL color carrier frequency)
CPU Clock = Fcolor * 1.6 = 7.0937892 MHz
CIA Clock = Cpu Clock / 10 = 709.37892 kHz
50 Hz Timer = CIA Clock / 50 = 14187.5784
Tempo num. = 50 Hz Timer*125 = 1773447
For NTSC: CPU Clock = 7.1590905 MHz --> Tempo num. = 1789773
To calculate tempo we use the formula: TimerValue = 1773447 / Tempo
The timer is only a word, so the available tempo range is 28-255 (++).
Tempo 125 will give a normal 50 Hz timer (VBlank).
A normal Protracker VBlank song tempo can be calculated as follows:
We want to know the tempo in BPM (Beats Per Minute), or rather quarter-
notes per minute. Four notes makes up a quarternote.
First find interrupts per minute: 60 seconds * 50 per second = 3000
Divide by interrupts per quarter note = 4 notes * speed
This gives: Tempo = 3000/(4*speed)
simplified: Tempo = 750/speed
For a normal song in speed 6 this formula gives: 750/6 = 125 BPM
Lars "ZAP" Hamre/Amiga Freelancers 1990
---------------------------------------------------------------------------
Newsgroups: comp.sys.ibm.pc.soundcard
From: Dmitry Boldyrev
Subject: Sources for simple .mod player. .mod file format (answer)
X-Xxmessage-Id:
X-Xxdate: Sun, 2 Feb 92 09:14:48 GMT
Organization: University of Utah
X-Useragent: Nuntius v1.1.1d17
Date: Tue, 2 Feb 93 19:11:54 GMT
Many people were asking me about .mod player: How it works? What is the
.mod file
format ?
I decided to *show* you one simple .mod player which can be easily
modified for
any computer, it can be Macintosh, PC or etc. The player was originally
written
for Sparc station, but as I told you before, can be modified for any
computer,
even for SoundBlaster, AdLib or any other sound card. What you have to
know is
initialization and how to write a byte..
Before I saw this source, I had a very poor notion of how it's works, but
two dwo
days with printed-out code made my notion very clear. So, if you're going
to write
a mod player and don't know how to start, try to start with reading the
source..
The code demonstrate how to read .mod files and how to playback them.'
Very clear and useful code..
/***********************************************************************/
/* */
/* str.c - plays sound/noisetracker files on a SparcStation */
/* */
/* Authors : Liam Corner - [email protected] */
/* Marc Espie - [email protected] */
/* Version : 1.20 - 3 November 1991 */
/* */
/* Usage : str32 */
/* [f|z]cat filename | str32 */
/* */
/***********************************************************************/
#include
#include
/**********************************************************/
/* uS is the number of uSeconds that a byte is played for */
/* Sparc plays at 8000 bytes/sec => 1 byte = 125 uSec */
/* VSYNC is the number of bytes played in 1/50 sec */
/* ie 0.02/(uS * 10**-6) */
/**********************************************************/
#define uS 125
#define VSYNC 160
#define AUDIO "/dev/audio"
#define MIN(A,B) ((A)<(B) ? (A) : (B))
#define MAX(A,B) ((A)>(B) ? (A) : (B))
typedef struct { /***********************************/
char *info; /* Sample */
int length; /* Length of sample */
float volume; /* Fractional volume 0-1 (min-max) */
int rep_start; /* Byte offset of repeat start */
int rep_end; /* Byte offset of repeat end */
} Voice; /***********************************/
typedef struct { /**************************/
char sample [64][4]; /* Sample number */
char effect [64][4]; /* Effect number */
unsigned char params [64][4]; /* Effect parameters */
int period [64][4]; /* Period (pitch) of note */
} Pattern; /**************************/
typedef struct { /***********************************************/
char samp; /* Sample number of current note */
int pitch; /* Current channel pitch (index to step_table) */
int slide; /* Step size of pitch slide (if any) */
int doslide;
unsigned int pointer; /* Current sample position */
unsigned int step; /* Sample offset increment (gives pitch) */
float volume; /* Fractional volume of current note */
float volslide;
int doslidevol;
int doporta;
int pitchgoal;
int portarate;
} Channel; /***********************************************/
/**********************************************************************
******/
/* Skips the next 'n' input bytes - because fseek won't work on stdin
*/
/**********************************************************************
******/
void byteskip (fp, bytes)
FILE *fp;
int bytes;
{
int loop;
for (loop = 0; loop < bytes; loop++)
getc(fp);
}
/************************************************************************/
/* For routine 'cvt' only */
/************************************************************************/
/* Copyright 1989 by Rich Gopstein and Harris Corporation */
/************************************************************************/
unsigned int cvt(ch)
int ch;
{
int mask;
if (ch < 0)
{
ch = -ch;
mask = 0x7f;
}
else
mask = 0xff;
if (ch < 32)
{
ch = 0xF0 | 15 - (ch / 2);
}
else if (ch < 96)
{
ch = 0xE0 | 15 - (ch - 32) / 4;
}
else if (ch < 224)
{
ch = 0xD0 | 15 - (ch - 96) / 8;
}
else if (ch < 480)
{
ch = 0xC0 | 15 - (ch - 224) / 16;
}
else if (ch < 992)
{
ch = 0xB0 | 15 - (ch - 480) / 32;
}
else if (ch < 2016)
{
ch = 0xA0 | 15 - (ch - 992) / 64;
}
else if (ch < 4064)
{
ch = 0x90 | 15 - (ch - 2016) / 128;
}
else if (ch < 8160)
{
ch = 0x80 | 15 - (ch - 4064) / 256;
}
else
{
ch = 0x80;
}
return (mask & ch);
}
char *getstring(f, len)
FILE *f;
int len;
{
static char s[150];
int i;
for (i = 0; i < len; i++)
s[i] = fgetc(f);
s[len] = '\0';
return s;
}
#define OLD 0
#define NEW 1
int main (argc, argv)
int argc;
char **argv;
{
FILE *fp, *audio;
int loop;
int notes, note, channel, vsync;
int pat, pat_num;
int byte, bytes;
int step_table[1024];
int speed=6; /* Default speed is 6 */
int end_pattern=0;
char songlength;
char tune[128];
char num_patterns=0;
unsigned char ulaw;
float dummy1, dummy2;
Voice voices[32];
Pattern patterns[64];
Channel ch[4];
int nvoices;
int effect;
int type; /* module type: old or new */
char *command; /* the actual command name used */
command = argv[0];
if (strcmp(argv[0], "str32") == 0)
type = NEW;
else if (strcmp(argv[0], "str15") == 0)
type = OLD;
else
{
fprintf(stderr,
"Error: command should be named either str15 or str32\n");
exit(1);
}
if (type == OLD)
nvoices = 15;
else
nvoices = 31;
if (argc>2)
{
fprintf(stderr,"Usage: %s []\n", command);
exit(1);
}
/***********************************************************************/
/* Creates a table of the byte_step << 16 for a given pitch */
/* The step and pointer are stored << 16 to get accuracy without floats*/
/* eg to get double pitch only play every other byte */
/* so step of 0x10000 is normal pitch, 0x8000 is half, */
/* 0x20000 is double. Pointer is >> 16 when accessed, */
/* so 0x10000 is 1st byte, 0x20000 2nd etc */
/* I have no idea where the other numbers are from, I copied them from */
/* a SoundTracker player for the Acorn Archimedes */
/* */
/* Actually, these other numbers are highly dependent on the amiga hw. */
/***********************************************************************/
step_table[0] = 0;
for (loop = 1; loop < 1024; loop++)
{
dummy1 = 3575872 / loop;
dummy2 = (dummy1 / (1000000 /uS) ) * 60000;
step_table[loop] = (int)dummy2;
}
if (argc < 2)
fp = stdin;
else
fp = fopen(argv[1], "r");
if (fp == NULL)
{
fprintf(stderr, "%s: unable to open tune file %s\n",
command, argv[1]);
exit(1);
}
/* read song name */
printf("Module : %s\n\n", getstring(fp, 20));
/* Reads in the sample-information tables */
for (loop = 1; loop <= nvoices; loop++)
{
printf("%6d : %s\n", loop, getstring(fp, 22));
voices[loop].length = ( (getc(fp) << 8) | getc(fp) ) * 2;
getc(fp);
voices[loop].volume = getc(fp);
voices[loop].volume = MIN(voices[loop].volume, 64);
voices[loop].volume /= 64; /* Volume is a fraction */
voices[loop].rep_start = ( (getc(fp) << 8) | getc(fp) ) * 2;
voices[loop].rep_end = ( (getc(fp) << 8) | getc(fp) ) * 2;
if (voices[loop].rep_end <= 4)
voices[loop].rep_end = 0;
else
{
/* If there is a repeat then end=start+length, but must
be */
/* less than the sample length. Not sure if this is 100%
*/
/* correct, but it seems to work OK :-)
*/
if (voices[loop].rep_end + voices[loop].rep_start - 1
> voices[loop].length)
voices[loop].rep_start >>= 1;
voices[loop].rep_end += voices[loop].rep_start;
voices[loop].rep_end = MIN(voices[loop].rep_end,
voices[loop].length);
}
}
voices[0].length = 0;
songlength = getc(fp);
byteskip(fp, 1);
/* Reads in the tune */
for (loop = 0; loop < 128; loop++)
{
tune[loop] = getc(fp);
if (tune[loop] > num_patterns)
num_patterns = tune[loop];
}
num_patterns++;
/* skip over sig (usually M.K.) */
if (type == NEW)
byteskip(fp,4);
/* Reads in the patterns */
for (pat_num = 0; pat_num < num_patterns; pat_num++)
{
/* 64 notes per pattern */
for (notes = 0; notes < 64; notes++)
{
/* 4 channels per note */
for (channel = 0; channel < 4; channel++)
{
note = (getc(fp) << 24) | (getc(fp) << 16) |
(getc(fp) << 8) | getc(fp);
(patterns[pat_num]).effect[notes][channel] =
(note & 0xF00) >> 8;
(patterns[pat_num]).params[notes][channel] = note & 0xFF;
(patterns[pat_num]).sample[notes][channel] =
( (note & 0xF000) >> 12) | ( (note >> 24) & 0x10);
(patterns[pat_num]).period[notes][channel] =
MIN( (note & 0xFFF0000) >> 16, 1023);
}
}
}
/* Stores the samples voices as an array of char */
for (loop = 1; loop <= nvoices; loop++)
{
voices[loop].info = malloc(voices[loop].length);
if (voices[loop].info == NULL)
{
fprintf(stderr, "%s: unable to allocate memory\n, command");
exit(1);
}
fread(voices[loop].info, 1, voices[loop].length, fp);
}
audio = fopen(AUDIO, "w");
if (audio == NULL)
{
fprintf(stderr, "%s: unable to access %s\n", command, AUDIO);
exit(1);
}
for (loop = 0; loop < 4; loop++)
{
ch[loop].pointer = 0;
ch[loop].step = 0;
ch[loop].volume = 0;
ch[loop].pitch = 0;
}
printf("\nPosition (%d):", songlength);
fflush(stdout);
for (pat_num = 0; pat_num < songlength; pat_num++)
{
printf("\r\t\t%3d", pat_num);
fflush(stdout);
pat = tune[pat_num];
end_pattern = 0;
for (notes = 0; notes < 64; notes++)
{
for (channel = 0; channel < 4; channel++)
{
int samp, pitch, cmd, para;
samp = patterns[pat].sample[notes][channel];
pitch = patterns[pat].period[notes][channel];
cmd = patterns[pat].effect[notes][channel];
para = patterns[pat].params[notes][channel];
if (samp)
{
ch[channel].samp = samp;
/* load new instrument */
ch[channel].volume = voices[ch[channel].samp].volume;
}
/* If sample number=0 and no new period */
/* continue last note */
if (pitch && cmd != 3)
{
ch[channel].pointer = 0;
ch[channel].step = step_table[pitch];
ch[channel].pitch = pitch;
}
ch[channel].doslide = 0;
ch[channel].doslidevol = 0;
ch[channel].doporta = 0;
switch(cmd) /* Do effects */
{
case 0xF :
speed = para;
break;
case 0xD :
end_pattern = 1;
break;
case 0xC :
ch[channel].volume= MIN(para, 64);
ch[channel].volume /= 64;
break;
/* volume_slide */
case 0xB :
pat_num = (para & 0xF) + (10 * (para >> 4));
break;
case 0xA :
ch[channel].doslidevol = 1;
if (para)
{
if (para & 15)
ch[channel].volslide = - para / 64;
else
ch[channel].volslide = (para >> 4)/64;
}
break;
case 3 :
ch[channel].doporta = 1;
if (para)
ch[channel].portarate = para;
if (pitch)
ch[channel].pitchgoal = pitch;
break;
case 2 :
ch[channel].doslide = 1;
if (para)
ch[channel].slide = para;
break;
case 1 :
ch[channel].doslide = 1;
if (para)
ch[channel].slide = -para;
break;
case 0 :
break;
default :
/* printf(" [%d][%d] ", cmd, para); */
break;
}
}
/* 1 vsync = 0.02 sec */
for (vsync = 0; vsync < speed; vsync++)
{
/* 160*125uSec = 0.02 */
for (bytes = 0; bytes < VSYNC; bytes++)
{
byte = 0;
for (channel = 0; channel < 4; channel++)
{
if (ch[channel].samp == 0)
continue;
/* If at end of sample jump to rep_start
position */
if (voices[ch[channel].samp].rep_end)
{
if ((ch[channel].pointer >> 16) >=
voices[ch[channel].samp].rep_end)
ch[channel].pointer +=
(voices[ch[channel].samp].rep_start -
voices[ch[channel].samp].length)<< 16;
}
else
if ((ch[channel].pointer >> 16) >=
voices[ch[channel].samp].length)
continue;
/* byte = sum of (sample byte * volume) for each
*/
/* of 4 channels which mixes the sounds
*/
if (ch[channel].pointer >> 16 <
voices[ch[channel].samp].length)
{
byte += (int) ( (voices[ch[channel].samp]
.info[ch[channel].pointer >> 16])
* (ch[channel].volume));
ch[channel].pointer += ch[channel].step;
}
}
/* Divide by 4 to get the correct volume */
byte /= 4;
ulaw = (unsigned char) cvt(byte * 16);/* Convert byte
*/
fputc(ulaw, audio); /* and play the
note */
}
/* Do end of vsync */
if (vsync == 0)
continue;
for (channel = 0; channel < 4; channel++)
{
if (ch[channel].doslide) /* effects */
{
ch[channel].pitch += ch[channel].slide;
ch[channel].pitch = MIN(ch[channel].pitch, 1023);
ch[channel].pitch = MAX(ch[channel].pitch, 113);
ch[channel].step = step_table[ch[channel].pitch];
}
if (ch[channel].doslidevol)
{
ch[channel].volume += ch[channel].volslide;
if (ch[channel].volume < 0.0)
ch[channel].volume = 0.0;
else if (ch[channel].volume >= 1.0)
ch[channel].volume = 1.0;
}
if (ch[channel].doporta)
{
if (ch[channel].pitch < ch[channel].pitchgoal)
{
ch[channel].pitch += ch[channel].portarate;
if (ch[channel].pitch > ch[channel].pitchgoal)
ch[channel].pitch = ch[channel].pitchgoal;
}
else if (ch[channel].pitch >
ch[channel].pitchgoal)
{
ch[channel].pitch -= ch[channel].portarate;
if (ch[channel].pitch < ch[channel].pitchgoal)
ch[channel].pitch = ch[channel].pitchgoal;
}
}
}
}
if (end_pattern == 1)
break;
}
}
fclose(audio);
printf("\n");
return (0);
}
o=======================================o
I Dmitry Boldyrev. I
I Department of Chemistry, I
I University of Utah SLC Utah. I
I Inter. [email protected] I
o=======================================o
--
[email protected] ! Kovalevysi jumissa ?
[email protected] ! - ravista sit{