712 lines
27 KiB
Plaintext
712 lines
27 KiB
Plaintext
-----------------------------------------------------------------------------
|
|
MED/OctaMED MMD0 and MMD1 file formats
|
|
written by Teijo Kinnunen (25.4.1992)
|
|
Revision 1
|
|
-----------------------------------------------------------------------------
|
|
|
|
Background
|
|
----------
|
|
A couple of years ago, when programming MED V2.1, I needed a file format for
|
|
MED modules. The only "module" format in MED V2.0 was the Sng+samples
|
|
format. Although it produced compact files, it was very difficult and tricky
|
|
to read in. Therefore, I designed a new file format, that would be easy to
|
|
use in module player programs etc. This file format was named 'MMD0' (Med
|
|
MoDule 0). The limitations in MMD0 block format forced me to create a new
|
|
file format for OctaMED Professional, this format is 'MMD1'. It's mostly the
|
|
same as MMD0, except the block structure is different. At the time of this
|
|
writing (when OctaMED Pro is not even released yet), MMD0's are absolutely
|
|
more common than MMD1's.
|
|
|
|
Design concepts
|
|
---------------
|
|
One of the main goals was to make MMD's (MED modules) as extensible as
|
|
possible. This would have been easy to implement with IFF-style chunks.
|
|
However, this method is obviously not the best for play-routine use.
|
|
|
|
Therefore, MMD's are implemented in quite an extraordinary way. They consist
|
|
of structures (similar to C structs), and pointers. In a module file,
|
|
pointers are defined as offsets from the beginning of the module. This way,
|
|
a particular structure can be read just by Seek()'ing using the pointer as
|
|
the offset from the beginning of the file. When a module has been read into
|
|
memory, it has to be relocated before it can be used (the relocation is done
|
|
simply by adding the address of the module to the pointers).
|
|
|
|
As with the Amiga OS, a MMD file does not contain absolute addresses.
|
|
There's a module header structure at the beginning of the file. This
|
|
structure contains pointers to different parts of the module. And you *MUST*
|
|
use these pointers. You may NOT expect that the song structure is at offset
|
|
$00000034, for example. Although it usually is, this may change in future
|
|
releases. In addition, it's possible that a structure even doesn't exist
|
|
(the structure pointer is NULL). Therefore, you *MUST* check the structure
|
|
pointer before accessing the structure. Finally, when writing MMD's you
|
|
*MUST* set undefined/reserved fields to zeros. More finally, you *MUST*
|
|
align all structures to even boundaries! (I forgot the alignment in MED
|
|
V3.00 save routine, resulting Guruing modules under some conditions :-(
|
|
|
|
The module header
|
|
-----------------
|
|
This structure must exist at the beginning of each MED module file. Each of
|
|
the structure members are described below.
|
|
|
|
In multi-modules, there are header structs for each song. (The subsequent
|
|
header pointers can be found from expdata structure. Multi-modules should
|
|
have the same smplarr pointer in every header.) Older MEDs which don't
|
|
recognize multi-modules consider a multi-module as an ordinary module (only
|
|
the first song is loaded).
|
|
|
|
The numbers enclosed in /* */ at the beginning of each line are (decimal)
|
|
offsets of each member (for assembly programmers).
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD0 {
|
|
/* 0 */ ULONG id;
|
|
/* 4 */ ULONG modlen;
|
|
/* 8 */ struct MMD0song *song;
|
|
/* 12 */ ULONG reserved0;
|
|
/* 16 */ struct MMD0Block **blockarr;
|
|
/* 20 */ ULONG reserved1;
|
|
/* 24 */ struct InstrHdr **smplarr;
|
|
/* 28 */ ULONG reserved2;
|
|
/* 32 */ struct MMD0exp *expdata;
|
|
/* 36 */ ULONG reserved3;
|
|
/* 40 */ UWORD pstate; /* some data for the player routine */
|
|
/* 42 */ UWORD pblock;
|
|
/* 44 */ UWORD pline;
|
|
/* 46 */ UWORD pseqnum;
|
|
/* 48 */ WORD actplayline;
|
|
/* 50 */ UBYTE counter;
|
|
/* 51 */ UBYTE extra_songs; /* number of songs - 1 */
|
|
}; /* length = 52 bytes */
|
|
|
|
-----------------------------------------------------------------------------
|
|
id
|
|
--
|
|
This longword is used to identify the MMD and its version. Currently defined
|
|
MMD types are MMD0 (0x4D4D4430) and MMD1 (0x4D4D4431). MMD2 and upwards are
|
|
reserved for future versions.
|
|
|
|
In multi-modules, the following modules usually contain id MCNT, or MCN1.
|
|
The first module always has MMD0 or MMD1 as an id.
|
|
|
|
modlen
|
|
------
|
|
This longword contains the length of the entire module.
|
|
|
|
song
|
|
----
|
|
Pointer to a MMD0song structure. This structure MUST ALWAYS EXIST!
|
|
|
|
blockarr
|
|
--------
|
|
Pointer to a table of block pointers. For example:
|
|
blockarr: $00003000
|
|
block 0 ptr block 1 ptr block 2 ptr
|
|
offset $00003000: $00002000, $00002400, $00002800 ....
|
|
|
|
offset $00002000: block 0 data...
|
|
offset $00002400: block 1 data...
|
|
..
|
|
The size of the table is MMD0song.numblocks longwords.
|
|
|
|
smplarr
|
|
-------
|
|
Pointer to a table of instrument pointers. The size of the table is
|
|
MMD0song.songlen longwords. This pointer is zero in OctaMED Pro MMD1 songs.
|
|
In this case, OctaMED Pro loads the instruments from disk(s).
|
|
|
|
expdata
|
|
-------
|
|
Pointer to an expansion structure. The expansion structure contains a lot of
|
|
extra information. The exp. structure does not exist in all MMD's. (Be
|
|
sure to check the pointer before using it.)
|
|
|
|
pstate, pblock, pline, pseqnum, actplayline, counter
|
|
----------------------------------------------------
|
|
These are variables for the play routine. You can read these fields to get
|
|
the current song position (not all versions of the play routine use these
|
|
fields, however). When writing a MMD, you should leave all fields to zero,
|
|
except the 'actplayline', which ought to be -1 ($FFFF).
|
|
|
|
extra_songs
|
|
-----------
|
|
This field contains the number of songs in the current module. For
|
|
non-multi-modules, this is 0. If there are two songs, extra_songs contains
|
|
1, and so on.
|
|
|
|
reserved0,1,2,3
|
|
---------------
|
|
Not currently defined. Set to zero.
|
|
|
|
|
|
The song structure (MMD0song)
|
|
-----------------------------
|
|
This structure contains the basic information about the song. It must exist
|
|
on every module file.
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD0song {
|
|
struct MMD0sample sample[63]; /* 63 * 8 bytes = 504 bytes */
|
|
UWORD numblocks; /* offs: 504 */
|
|
UWORD songlen; /* offs: 506 */
|
|
UBYTE playseq[256]; /* offs: 508 */
|
|
UWORD deftempo; /* offs: 764 */
|
|
BYTE playtransp; /* offs: 766 */
|
|
UBYTE flags; /* offs: 767 */
|
|
UBYTE flags2; /* offs: 768 */
|
|
UBYTE tempo2; /* offs: 769 */
|
|
UBYTE trkvol[16]; /* offs: 770 */
|
|
UBYTE mastervol; /* offs: 786 */
|
|
UBYTE numsamples; /* offs: 787 */
|
|
}; /* length = 788 bytes */
|
|
|
|
-----------------------------------------------------------------------------
|
|
sample
|
|
------
|
|
Contains some basic info about each sample. The structure looks like this:
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD0sample {
|
|
UWORD rep,replen; /* offs: 0(s), 2(s) */
|
|
UBYTE midich; /* offs: 4(s) */
|
|
UBYTE midipreset; /* offs: 5(s) */
|
|
UBYTE svol; /* offs: 6(s) */
|
|
BYTE strans; /* offs: 7(s) */
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
rep repeat start offset, shifted right one bit (as in
|
|
Protracker).
|
|
replen repeat length, shifted right one bit.
|
|
midich MIDI channel for current instrument, 0 if not MIDI.
|
|
midipreset MIDI preset number for current instrument, 0 if no
|
|
preset.
|
|
svol default volume for current instrument (0 - 64).
|
|
strans instrument transpose value.
|
|
|
|
More information is defined in expdata structure.
|
|
|
|
numblocks
|
|
---------
|
|
Number of blocks in current song. This field also indicates the length of
|
|
the blockarr table in longwords.
|
|
|
|
songlen
|
|
-------
|
|
Song length (number of sequence numbers in the play sequence list).
|
|
|
|
playseq
|
|
-------
|
|
This is the play sequence list.
|
|
|
|
deftempo
|
|
--------
|
|
Default song tempo (the leftmost tempo slider in MED/OctaMED). If BPM mode
|
|
is on, this value indicates BPM.
|
|
|
|
playtransp
|
|
----------
|
|
The global play transpose value for current song.
|
|
|
|
flags
|
|
-----
|
|
Contains many single-bit flags:
|
|
FLAG_FILTERON 0x1 the hardware audio filter is on
|
|
FLAG_JUMPINGON 0x2 mouse pointer jumping on (not in OctaMED Pro)
|
|
FLAG_JUMP8TH 0x4 jump every 8th line (not in OctaMED Pro)
|
|
FLAG_INSTRSATT 0x8 sng+samples indicator (not useful in MMD's)
|
|
FLAG_VOLHEX 0x10 volumes are HEX
|
|
FLAG_STSLIDE 0x20 use ST/NT/PT compatible sliding
|
|
FLAG_8CHANNEL 0x40 this is OctaMED 5-8 channel song
|
|
|
|
(bit 0x80 is not defined, and must be set to zero)
|
|
|
|
flags2
|
|
------
|
|
More flags, currently only BPM stuff:
|
|
FLAG2_BMASK 0x1F (bits 0-4) BPM beat length (in lines)
|
|
0 = 1 line, $1F = 32 lines.
|
|
(The rightmost slider in OctaMED Pro
|
|
BPM mode.)
|
|
FLAG2_BPM 0x20 BPM mode on
|
|
|
|
(bits 0x40 and 0x80 are not defined, and must be set to zero)
|
|
|
|
tempo2
|
|
------
|
|
This is the "secondary tempo" (the rightmost MED/OctaMED tempo slider),
|
|
indicating the number of timing pulses per line.
|
|
|
|
trkvol[16]
|
|
----------
|
|
The relative track volumes (1 - 64) for each track.
|
|
|
|
mastervol
|
|
---------
|
|
The relative master volume (1 - 64).
|
|
|
|
numsamples
|
|
----------
|
|
Number of instruments (samples/synthsounds) in current song. Also indicates
|
|
the size of the smplarr table in longwords.
|
|
|
|
|
|
The block format
|
|
----------------
|
|
As described above, MMD0 header structure contains a pointer (blockarr) to a
|
|
table of block pointers. These block pointers point to the actual block data
|
|
structures. The format of these data structures differ in MMD0 and MMD1 file
|
|
formats.
|
|
|
|
MMD0 block format
|
|
-----------------
|
|
At the beginning of each block, there's a small header:
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD0Block {
|
|
UBYTE numtracks,lines;
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
numtracks number of tracks (4, 8, 12 or 16) on this block
|
|
lines number of lines on this block; 0 = 1 line,
|
|
255 = 256 lines
|
|
|
|
Following this header, there is the actual note data, consisting of 3-byte
|
|
structures containing a note and its command. The data is arranged
|
|
sequentially a line at a time, i.e. in the following order:
|
|
line 0 track 0
|
|
line 0 track 1
|
|
line 0 track 2
|
|
line 0 track 3
|
|
line 1 track 0
|
|
line 1 track 1
|
|
...
|
|
The 3-byte structure looks like this (each letter corresponds to one bit):
|
|
|
|
xynnnnnn iiiicccc dddddddd
|
|
|
|
n = note number (0 - $3F). 0 = ---, 1 = C-1, 2 = C#1...
|
|
i = the low 4 bits of the instrument number
|
|
x = the 5th bit (#4) of the instrument number
|
|
y = the 6th bit (#5) of the instrument number
|
|
c = command number (0 - $F)
|
|
d = databyte ($00 - $FF)
|
|
|
|
MMD1 block format
|
|
-----------------
|
|
MMD1 block format can contain a lot more information than MMD0's. The block
|
|
header looks like this:
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD1Block {
|
|
UWORD numtracks;
|
|
UWORD lines;
|
|
struct BlockInfo *info;
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
numtracks Number of tracks in this block (4, 8, 12, or 16).
|
|
lines Number of lines in this block (0 = 1 line etc.).
|
|
OctaMED Pro can handle upto 3200 lines/block, so
|
|
this is obviously the practical upper limit.
|
|
info Pointer to structure containing extra information.
|
|
(Can be NULL, if no BlockInfo struct exists).
|
|
|
|
The BlockInfo structure is:
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct BlockInfo {
|
|
ULONG *hlmask;
|
|
UBYTE *blockname;
|
|
ULONG blocknamelen;
|
|
ULONG reserved[6];
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
hlmask Pointer to an array of longwords, containing info
|
|
about line highlighting (TAB key on MED). The number
|
|
of longwords depend on the number of lines on the
|
|
block. (E.g: 1 line -> 1 longword, 32 lines -> 1 lw,
|
|
33 lines -> 2 lws, 256 lines -> 4 lws)
|
|
The bits in the longwords are arranged in reversed
|
|
order (e.g. bit #0 = line 0, bit #31 = line 31).
|
|
|
|
blockname Pointer to the name of the block. Must be null-
|
|
terminated.
|
|
|
|
blocknamelen Length of the block name, including the terminating
|
|
zero. OctaMED Pro currently has the maximum length of
|
|
41 chars (+ zero). However, this may change in the
|
|
future. Don't read blocknames longer than you are
|
|
able to handle!
|
|
|
|
reserved[6] These are reserved for future extensions. Must be set
|
|
to zero.
|
|
|
|
The note structures, which are 4 bytes long in MMD1 modules, are
|
|
arranged exactly as in MMD0 modules.
|
|
|
|
xnnnnnnn xxiiiiii cccccccc dddddddd
|
|
|
|
n = note number (0 - $7F, 0 = ---, 1 = C-1...)
|
|
i = instrument number (0 - $3F)
|
|
c = command ($00 - $FF)
|
|
d = databyte ($00 - $FF)
|
|
x = undefined, reserved for future expansion. MUST BE SET TO ZERO,
|
|
AND MASKED OUT WHEN READING THE NOTE OR INSTRUMENT NUMBER.
|
|
|
|
|
|
The instrument format
|
|
---------------------
|
|
The MMD0 header structure contains a pointer (smplarr) to a table of
|
|
instrument pointers. These pointers point to the actual instrument data
|
|
structures. If an instrument pointer is zero, there's no instrument in that
|
|
slot.
|
|
|
|
Every instrument has a six-byte header structure, which is the same for all
|
|
instrument types (sample/synth/hybrid).
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct InstrHdr {
|
|
ULONG length;
|
|
WORD type;
|
|
/* Followed by actual data */
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
length indicates the length of the instrument (the six byte
|
|
header data length is not included)
|
|
type instrument type - currently the following types are
|
|
defined:
|
|
|
|
HYBRID -2
|
|
SYNTHETIC -1
|
|
SAMPLE 0 (an ordinary 1-octave sample)
|
|
IFF5OCT 1 (5 octaves)
|
|
IFF3OCT 2 (3 octaves)
|
|
(The following ones are recognized by OctaMED Pro only)
|
|
IFF2OCT 3 (2 octaves)
|
|
IFF4OCT 4 (4 octaves)
|
|
IFF6OCT 5 (6 octaves)
|
|
IFF7OCT 6 (7 octaves)
|
|
|
|
The sample-type instruments (>= 0) contain the actual sample data straight
|
|
after the header structure.
|
|
|
|
Synthetic instruments
|
|
---------------------
|
|
Synthsounds have a special structure of their own. They also contain
|
|
waveforms and pointers to them. Therefore, relocation is required. However,
|
|
there's an important difference: pointers are expressed as an offset from
|
|
the beginning of the synthsound, NOT the beginning of the module. The
|
|
'reloc.a' routine provided with MED/OctaMED automatically handles this.
|
|
|
|
The synthsound structure is as follows (note that this structure contains the
|
|
header structure):
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct SynthInstr {
|
|
ULONG length; /* length of this struct */
|
|
WORD type; /* -1 or -2 (offs: 4) */
|
|
UBYTE defaultdecay;
|
|
UBYTE reserved[3];
|
|
UWORD rep;
|
|
UWORD replen;
|
|
UWORD voltbllen; /* offs: 14 */
|
|
UWORD wftbllen; /* offs: 16 */
|
|
UBYTE volspeed; /* offs: 18 */
|
|
UBYTE wfspeed; /* offs: 19 */
|
|
UWORD wforms; /* offs: 20 */
|
|
UBYTE voltbl[128]; /* offs: 22 */
|
|
UBYTE wftbl[128]; /* offs: 150 */
|
|
struct SynthWF *wf[64]; /* offs: 278 */
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
defaultdecay = the default decay of the current synthsound. This
|
|
is NOT used in modules. It's significant only when
|
|
saving a single synthsound.
|
|
|
|
reserved[3] = set to zero.
|
|
|
|
rep, replen = repeat/rep. length for hybrid sounds. Used only
|
|
when saving a single hybrid sound.
|
|
|
|
voltbllen = the length of the volume sequence table.
|
|
|
|
wftbllen = the length of the waveform sequence table.
|
|
|
|
volspeed = the initial volume table execution speed.
|
|
|
|
wfspeed = the initial waveform table execution speed.
|
|
|
|
wforms = the number of waveforms in the current synthsound.
|
|
|
|
voltbl = the actual volume sequence table. Values $00-$7F
|
|
are volumes or command arguments. Values >= $80 are
|
|
commands. The following commands are currently
|
|
defined:
|
|
$FF END $F4 EN1
|
|
$FE JMP $F3 CHU
|
|
$FB HLT $F2 CHD
|
|
$FA JWS $F1 WAI
|
|
$F6 EST $F0 SPD
|
|
$F5 EN2
|
|
|
|
wftbl = the actual waveform sequence table. Values $00-$7F
|
|
are waveform numbers or command arguments. Values
|
|
>= $80 are commands. The following commands are
|
|
currently defined:
|
|
$FF END $F6 RES
|
|
$FE JMP $F5 VBS
|
|
$FD ARE $F4 VBD
|
|
$FC ARP $F3 CHU
|
|
$FB HLT $F2 CHD
|
|
$FA JVS $F1 WAI
|
|
$F7 VWF $F0 SPD
|
|
|
|
wf = pointers to waveforms. (Once again: relative to the
|
|
beginning of the synthsound!) A waveform structure
|
|
is as follows:
|
|
|
|
struct SynthWF {
|
|
UWORD length; /* length in words */
|
|
BYTE wfdata[xx]; /* the waveform */
|
|
};
|
|
|
|
(where xx = length in bytes)
|
|
|
|
In hybrid sounds, however, wf[0] is different.
|
|
|
|
Hybrid instruments
|
|
------------------
|
|
Hybrid sounds use the same structure as synthsounds, except that the first
|
|
waveform (wf[0]) pointer points to a sample. (The sample header structure
|
|
exists, as usual.)
|
|
|
|
|
|
MMD0exp - the key to future expansions
|
|
--------------------------------------
|
|
For possible future expansions, I designed a structure for carrying the
|
|
miscellaneous things that were added to MED/OctaMED now and then. (MED V3.00
|
|
was the first version, which wrote this structure.) Most of its fields are in
|
|
use now, but it's possible to even expand this structure (things will get a
|
|
bit more tricky, though).
|
|
|
|
In multi-modules, you should extract all data from the expansion structure of
|
|
the first song. The only exceptions are currently the 'nextmod' and
|
|
'songname' fields, which are song-specific.
|
|
|
|
Also, there has been need for extending the MMD0sample structure. Both
|
|
InstrExt and MMDInstrInfo provide extra information about the instruments.
|
|
These are defined as structure arrays (exp_smp and iinfo point to the first
|
|
structure). The extension structures don't have a constant size, instead you
|
|
have to read s_ext_entrsz or i_ext_entrsz to get the structure sizes. When
|
|
reading, you have to check the entrsz fields to see which structure members
|
|
do exist.
|
|
|
|
The difference between InstrExt and MMDInstrInfo is that InstrExt contains
|
|
information the play-routine is interested in (e.g. finetune). MMDInstrInfo
|
|
contains "secondary" information, which is of no use to the player routine
|
|
(e.g. instrument name).
|
|
|
|
The expansion structure follows:
|
|
|
|
-----------------------------------------------------------------------------
|
|
struct MMD0exp {
|
|
struct MMD0 *nextmod;
|
|
struct InstrExt *exp_smp;
|
|
UWORD s_ext_entries;
|
|
UWORD s_ext_entrsz;
|
|
UBYTE *annotxt;
|
|
ULONG annolen;
|
|
struct MMDInstrInfo *iinfo;
|
|
UWORD i_ext_entries;
|
|
UWORD i_ext_entrsz;
|
|
ULONG jumpmask;
|
|
UWORD *rgbtable;
|
|
UBYTE channelsplit[4];
|
|
struct NotationInfo *n_info;
|
|
UBYTE *songname;
|
|
ULONG songnamelen;
|
|
struct MMDDumpData *dumps;
|
|
ULONG reserved2[7];
|
|
};
|
|
|
|
-----------------------------------------------------------------------------
|
|
nextmod = pointer to the next module (or zero). (Only used in
|
|
multi-modules!)
|
|
|
|
exp_smp = pointer to InstrExt. Currently the first four bytes
|
|
of InstrExt have been defined:
|
|
|
|
struct InstrExt {
|
|
UBYTE hold;
|
|
UBYTE decay;
|
|
UBYTE suppress_midi_off;
|
|
BYTE finetune;
|
|
};
|
|
|
|
hold, decay = hold/decay values of the
|
|
instrument
|
|
|
|
suppress_midi_off = 0 (FALSE) or not (TRUE)
|
|
|
|
finetune = instrument finetune (-8-+7)
|
|
|
|
s_ext_entries = the size of InstrExt structure array (i.e. the
|
|
number of InstrExt structures).
|
|
|
|
s_ext_entrsz = the size of each InstrExt structure (in bytes).
|
|
|
|
annotxt = pointer to the annotation text (null-terminated).
|
|
|
|
annolen = length of 'annotxt', including the terminating \0.
|
|
|
|
iinfo = pointer to MMDInstrInfo. Currently the first forty
|
|
bytes have been defined:
|
|
|
|
struct MMDInstrInfo {
|
|
UBYTE name[40];
|
|
};
|
|
|
|
name = null-terminated instrument name
|
|
|
|
i_ext_entries = the size of the MMDInstrInfo struct array (i.e. the
|
|
number of MMDInstrInfo structures).
|
|
|
|
i_ext_entrsz = the size of each MMDInstrInfo struct in bytes.
|
|
|
|
jumpmask = a mask controlling which instruments cause the
|
|
mouse pointer to jump. E.g. bit #1 = instr. #1.
|
|
This field has become obsolete in OctaMED Pro.
|
|
|
|
rgbtable = pointer to eight UWORDs (screen colors) to be
|
|
passed to LoadRGB4() routine.
|
|
|
|
channelsplit = this longword is divided to four boolean bytes,
|
|
controlling channel splitting in OctaMED 5 - 8 chnl
|
|
modes. (A non-zero byte means that the channel is
|
|
NOT splitted.) Currently only the following
|
|
combinations should be used:
|
|
|
|
0x00000000 (8 channels (or normal 4 channel mode))
|
|
0x000000FF (7 channels)
|
|
0x0000FFFF (6 channels)
|
|
0x00FFFFFF (5 channels)
|
|
|
|
n_info = pointer to NotationInfo structure (used only in
|
|
OctaMED V2.0 and later). It contains some info for
|
|
the notation editor.
|
|
|
|
struct NotationInfo {
|
|
UBYTE n_of_sharps;
|
|
UBYTE flags;
|
|
WORD trksel[5];
|
|
UBYTE trkshow[16];
|
|
UBYTE trkghost[16];
|
|
BYTE notetr[63];
|
|
UBYTE pad;
|
|
};
|
|
|
|
n_of_sharps = number of sharps or flats (0 - 6)
|
|
flags = misc. bits, these are defined:
|
|
NFLG_FLAT 1
|
|
(= use flats instead of sharps)
|
|
NFLG_3_4 2
|
|
(= display 12 lines instead of 16)
|
|
|
|
trksel = the number of the selected track,
|
|
for each display preset
|
|
(-1 = no track selected)
|
|
|
|
trkshow = tracks shown (five bits used in
|
|
each byte, bit #0 = preset 1, etc.)
|
|
|
|
trkghost = tracks ghosted (as in 'trkshow')
|
|
|
|
notetr = note transpose value for each
|
|
instrument (-24 - +24). If bit 6 is
|
|
negated, the instrument is hidden.
|
|
|
|
pad = possibly holding info about struct
|
|
expansions in the future. Don't
|
|
touch!
|
|
|
|
songname = song name of the current song (0-terminated).
|
|
Each song of a multi-module can have a different
|
|
name.
|
|
|
|
songnamelen = song name length (including the terminating zero).
|
|
|
|
dumps = MIDI dump data (created using OctaMED Pro MIDI
|
|
message editor). The 'dumps' field points to the
|
|
following struct:
|
|
|
|
struct MMDDumpData {
|
|
UWORD numdumps;
|
|
UWORD reserved[3];
|
|
};
|
|
|
|
Immediately after this struct, there are 'numdumps'
|
|
pointers to the following struct:
|
|
|
|
struct MMDDump {
|
|
ULONG length;
|
|
UBYTE *data;
|
|
UWORD ext_len;
|
|
/* if ext_len >= 20: */
|
|
UBYTE name[20];
|
|
};
|
|
|
|
length = length of the MIDI message dump.
|
|
|
|
data = pointer to the actual MIDI dump
|
|
data.
|
|
|
|
ext_len = MMDDump struct extension length.
|
|
For flexible future expansion.
|
|
|
|
(if ext_len >= 20, the following fields exist)
|
|
|
|
name = name of the dump.
|
|
|
|
reserved2 = future expansion fields, that MUST be zero now.
|
|
|
|
-----------------------------------------------------------------------------
|
|
Finally, here is a collection of the most important rules you should obey
|
|
when handling MMD's:
|
|
|
|
* ALWAYS USE POINTERS, NOT ABSOLUTE OFFSETS.
|
|
|
|
* CHECK THAT A POINTER IS NONZERO BEFORE ACCESSING ANYTHING IT
|
|
POINTS TO.
|
|
|
|
* WHEN WRITING, SET UNDEFINED/RESERVED BITS AND BYTES TO ZERO.
|
|
WHEN READING, MASK OUT UNDEFINED BITS, AND DON'T USE UNDEFINED
|
|
FIELDS.
|
|
|
|
* WHEN WRITING, ALWAYS ALIGN EVERYTHING TO EVEN BOUNDARIES.
|
|
|
|
* WHEN WRITING, ALWAYS WRITE THE SONG STRUCTURE.
|
|
|
|
* REMEMBER TO HANDLE ERROR SITUATIONS CORRECTLY (IN ALL PROGRAMS,
|
|
NOT ONLY WHEN HANDLING MMDs ;^)
|
|
|
|
If you don't understand some part of this file completely, try saving a
|
|
module using MED, and then examine the file with a file editor. This way you
|
|
can learn easily about the file format of MED/OctaMED.
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
I hope this document will help programmers who wish to be able to handle
|
|
modules in their programs. NOTE! This text file is PUBLIC DOMAIN. All
|
|
distribution of this file,via the pd is strongly encouraged. Thank you!
|
|
|
|
Teijo Kinnunen
|
|
Oksantie 19
|
|
SF-86300 OULAINEN
|
|
FINLAND
|
|
|
|
----------------------------------------------------------------------------
|