-
Notifications
You must be signed in to change notification settings - Fork 1
/
MMD_FileFormat.txt
869 lines (698 loc) · 32.7 KB
/
MMD_FileFormat.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
-----------------------------------------------------------------------------
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~