home *** CD-ROM | disk | FTP | other *** search
- -----------------------------------------------------------------------------
-
- MED/OctaMED MMD0/MMD1/MMD2 file formats
- written by Teijo Kinnunen (18.05.1995)
- Revision 4
-
- $VER: MMD_documentation 4 (18.05.1995)
-
- -----------------------------------------------------------------------------
-
- 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. Another new format is called
- 'MMD2', this extends some of the old limits and provides a couple of new
- features
-
- 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 was 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.
-
- 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 */ UWORD psecnum; /* for the player routine, MMD2 only */
- /* 14 */ UWORD pseq; /* " " " " */
- /* 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), MMD1 (0x4D4D4431) and MMD2 (0x4D4D4432).
- MMD3 and upwards are reserved for future versions.
-
- In multi-modules, the following modules usually contain id MCNT, MCN1 or
- MCN2. The first module always has MMD0 - MMD2 as an id.
-
- modlen
- ------
- This longword contains the length of the entire module.
-
- song
- ----
- Pointer to a MMD0song/MMD2song 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, psecnum, pseq
- -------------------------------------------------------------------
- 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. This structure is for MMD0/MMD1 only! The MMD2 struct
- is documented below.
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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 main 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
- FLAG_SLOWHQ 0X80 HQ V2-4 compatibility mode
-
- flags2
- ------
- More flags, currently only BPM stuff:
- FLAG2_BMASK 0x1F (bits 0-4) BPM beat length (in lines)
- 0 = 1 line, $1F = 32 lines.
- (OctaMED Pro Lines Per Beat slider.)
- 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 MMD2 song structure
- ~~~~~~~~~~~~~~~~~~~~~~~
- struct MMD2song {
- struct MMD0sample sample[63];
- UWORD numblocks;
- UWORD songlen; /* NOTE: number of sections in MMD2 */
- struct PlaySeq **playseqtable;
- UWORD *sectiontable; /* UWORD section numbers */
- UBYTE *trackvols; /* UBYTE track volumes */
- UWORD numtracks; /* max. number of tracks in the song
- (also the number of entries in
- 'trackvols' table) */
- UWORD numpseqs; /* number of PlaySeqs in 'playseqtable' */
- UBYTE pad0[240]; /* reserved for future expansion */
- /* Below fields are MMD0/MMD1-compatible (except pad1[]) */
- UWORD deftempo;
- BYTE playtransp;
- UBYTE flags;
- UBYTE flags2;
- UBYTE tempo2;
- UBYTE pad1[16]; /* used to be trackvols, in MMD2 reserved */
- UBYTE mastervol;
- UBYTE numsamples;
- };
-
- This structure is exactly as long as the MMDsong structure. Common fields are
- located at same offsets. You can also see, that there's a lot of room for
- expansion in this structure. The new/changed fields are:
-
- songlen
- -------
- Now expresses the number of sections (i.e. the length of 'sectiontable').
-
- playseqtable
- ------------
- Points to a table of PlaySeq structures. This structure is:
-
- struct PlaySeq {
- char name[32]; /* (0) 31 chars + \0 */
- ULONG reserved[2]; /* (32) for possible extensions */
- UWORD length; /* (40) # of entries */
- /* Commented out, not all compilers may like it... */
- /* UWORD seq[0]; */ /* (42) block numbers.. */
- };
-
- This should be self-explaining. Note: block numbers in seq[] may not be
- greater than 0x7FFF. These are reserved for future expansions. The current
- player routines skip these numbers.
-
- trackvols
- ---------
- Points to a table of 'numtracks' UBYTEs, which contain values 1 - 64 for
- relative track volumes.
-
- numtracks
- ---------
- The number of tracks this song uses (i.e. the width of the widest block).
-
- numpseqs
- --------
- The number of PlaySeqs in 'playseqtable'.
-
- pad[]
- -----
- Must be zero! Don't attempt to read.
-
- 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/MMD2 block format
- -----------------
- MMD1/MMD2 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).
- (MMD2: All values between 1 - 64 are valid.)
- 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;
- struct BlockCmdPageTable *pagetable;
- ULONG reserved[5];
- };
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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!
-
- pagetable OctaMED V6 supports multiple player commands per note.
- This pointer points to the BlockCmdPageTable structure,
- if this block has more than one pages. (See below.)
-
- reserved[5] These are reserved for future extensions. Must be set
- to zero.
-
- This is the BlockCmdPageTable structure:
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- struct BlockCmdPageTable {
- UWORD num_pages;
- UWORD reserved;
- UWORD *page[0];
- };
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- num_pages Specifies the number of additional command pages. The first
- command page that always exists is not included in this.
-
- reserved Some room for future expansions, you never know..
-
- page Here begins the table of command page pointers. (The table
- has num_pages entries.) Each page contains two-byte entries
- (command and data byte), which are arranged just as notes
- in blocks. (Line 0 Track 0, Line 0 Track 1...)
-
- The note structures, which are 4 bytes long in MMD1 modules, are
- arranged exactly as in MMD0 modules (i.e. L0T0, L0T1... L1T0, L1T1..).
-
- xnnnnnnn xxiiiiii cccccccc dddddddd
-
- n = note number (0 - $7F, 0 = ---, 1 = C-1...)
- i = instrument number (0 - $3F)
- c = command ($00 - $FF)
- d = data byte ($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)
- (OctaMED Pro V5 + later)
- EXTSAMPLE 7 (two extra-low octaves)
-
- The sample-type instruments (>= 0) contain the actual sample data straight
- after the header structure.
-
- Since OctaMED V6, there are also two flag bits in the type field:
- S_16 0x10
- STEREO 0x20
-
- S_16 means that this is a 16-bit sample. Length still indicates the sample
- length in bytes, so there are actually length / 2 16-bit samples.
-
- STEREO means that this is a stereo sample. The sample is not interleaved. The
- left channel comes first, followed by the right channel. Important: Length
- specifies the size of one channel only! The actual memory usage for both
- samples is length * 2 bytes.
-
- Plus an obsolete item:
- OBSOLETE_MD16 0x18
-
- This instrument type is obsolete, and was used for V5.04 Aura support. It
- should be treated as type S_16|SAMPLE (0x10), with instrument's output device
- set to Aura.
-
- 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;
- struct MMDInfo *mmdinfo;
- ULONG reserved2[6];
- };
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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;
- /* Below fields saved by >= V5 */
- UBYTE default_pitch;
- UBYTE instr_flags;
- UWORD long_midi_preset;
- /* Below fields saved by >= V5.02 */
- UBYTE output_device;
- UBYTE reserved;
- };
-
- hold, decay = hold/decay values of the
- instrument
-
- suppress_midi_off = 0 (FALSE) or not (TRUE)
-
- finetune = instrument finetune (-8-+7)
-
- default_pitch = instrument's default pitch
- (0 = ---, 1 = C-1, 2 = C#1...)
-
- instr_flags = some miscellaneous flags:
- SSFLG_LOOP 0x01 (Loop On/Off)
- SSFLG_EXTPSET 0x02 (Ext. Preset)
- SSFLG_DISABLED 0x04 (Disabled)
-
- long_midi_preset = overrides the preset in the
- song structure (allows ext.
- presets)
-
- output_device = instrument's output device,
- currently the following exist:
- OUTPUT_STD 0 (standard Amiga audio)
- OUTPUT_MD16 1 (Aura)
- OUTPUT_TOCC 2 (Toccata)
-
- reserved = zero, at the moment
-
- 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.
-
- mmdinfo = provides more information about the song that annotxt.
- Currently allows a text file to be attached. The
- structure MMDInfo is:
-
- struct MMDInfo {
- struct MMDInfo *next;
- UWORD reserved;
- UWORD type;
- ULONG length;
- /* data follows... */
- };
-
- next = pointer to the next MMDInfo structure. However,
- OctaMED 6 only supports one attachment, so this
- field is not used at the moment. May change in
- the future.
-
- reserved = 0
-
- type = data type. This is for maximum flexibility for
- future expansions. Currently only type 1 (ASCII
- text) is supported. Other types should be ignored.
-
- ASCII is standard Amiga ISO ASCII. Lines terminated
- by a single '\n'. The text is null terminated. No
- tabs allowed (ListView gadgets can't display them).
-
- length = data length in bytes.
-
- 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 and examine the file with a file editor. This way you can learn easily
- about the file format of MED/OctaMED.
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-