amspec.html

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
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>

<head>
 <title>Advanced Module Specification</title>
 <meta name="Author" content="Chris Dragan">
 <meta name="Generator" content="manual">
 <meta name="Description" content="Software created in assembly, its
				   sources and music in XM format">
 <meta name="Keywords" content="Chris Dragan, assembler, assembly, asm, win32,
				programming, freeware, software, sources,
				music, XM, modules, module player, benchmark">
</head>

<body
   background="yellow.jpg"
   bgcolor="#FFFFBF"
   text="#00005F"
   link="#0000CF"
   alink="#0000CF"
   vlink="#CF00CF">

<font face="Verdana,Arial" size=1><a href="index.html" target="_top">
Back to Main Page</a></font>

<font face="Arial" size=5><b><center>
Advanced Module Specification
</center></b></font>
<font face="Verdana,Arial" size=1 color="#000000"><center>
Version 9, Jun.12 2000</center></font>
<p align="justify"><font face="Verdana,Arial">
This document describes a flexible musical file format, called "Advanced
  Module". The format is a compilation of MIDI and XM, and contains the
  best features from both of them. This is a final specification, that
  can be extended in the future, though.

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
Introduction
</center></b></i></font>

<p align="justify">
The main idea while creating the AM format was to discard the limitations
  of current musical formats.

<p align="justify">
Almost all variable features have been moved to tracker (i.e. compositional
  software). This means that note control, sample and envelope editing
  is tracker dependent.

<p align="justify">
An AM file is a library of the following object types:

<ul> <li>samples, <li>envelopes, <li>instruments and <li>songs.</ul>

<p align="justify">
There is also room for special effects, like reverb or
  IIR filters. Effects will be defined in the next version of the format.

<p align="justify">
An AM file can contain any number of object types enumerated above.
  This allows multiple songs to refer to the same instruments/envelopes/
  samples, what can make the overall file size smaller. An AM file can
  also serve as a handy library with composer's commonly used instruments.

<p align="justify">
An AM file can exist in one of two stages: development or release stage.
  In development stage the file contains additional tracker-specific
  information describing composer's settings, like time subdivision into
  tacts and parts, for instance, as well as instrument and track names,
  etc. In release stage only the minimal amount of information is saved,
  what makes its contents hardly readable by others, but makes the file
  smaller, thus more suitable for distribution. Files in development stage
  can be converted into release stage. The opposite conversion is impossible
  from obvious reason. Instead of saving tunes in release stage, the
  composer may also choose to use a different format, like MP3 for instance
  (depending on whether a tracker allows this).

<p align="justify">
Further information in this document concerns only release stage of AM files.

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
File structure
</center></b></i></font>

<p align="justify">
List of chunks (identifiers and contents' names):

<ul>
   <li>AMOD - AM file
   <ul>
      <li>SAMP - sample
      <li>ENVL - envelope
      <ul>
	 <li>EDIM - single envelope dimension
      </ul>
      <li>INST - instrument
      <li>SONG - song
      <ul>
	 <li>INFO - composer's info
	 <li>TRAK - track
      </ul>
   </ul>
</ul>

<p align="justify">
It is easy to figure out from the above list, that AMOD, ENVL and SONG
  are container-chunks. They contain sequences of other chunks,
  namely AMOD - SAMP, ENVL, INST and SONG; ENVL - EDIM and SONG - INFO and TRAK.
  An AM file is one big AMOD chunk, with its all sub-chunks.

<p align="justify">
All chunk types have the following header:

<font face="Courier New" color="#007F00"><pre>
struct CHUNK {
   byte  name[4];	// Chunk identifier
   dword size;		// Body size (chunk size - 8)
   word  identifier;	// Unique chunk identifier
   byte  type;		// Object type
   byte  version;	// Chunk version
   byte  body[size-4];	// Chunk data
};
</font></pre>

<p align="justify">
The unique chunk identifier corresponds to chunk type. For example,
  there can be an ENVL chunk in the file with the same identifier as
  one of SAMP chunks, but no two ENVL chunks can have the same identifier.
  This is because the chunks within one chunk type are distinguished by
  their identifiers.

<p align="justify">
Object type field defines more exact type of the chunk. For example the
  format can support compressed samples, which have different object type
  than typical, uncompressed samples. Version field corresponds to object
  type and changes when the format of the chunk changes while the type
  remains unmodified.

<p align="justify">
It is easy to notice that all fields of chunks described below
  are correctly aligned. This feature allows players to load some chunks
  without additional changes, and guarantees maximum field access speed.
  A further requirement is that all chunks should have sizes divisible by
  eight (i.e. be aligned on an 8-byte boundary). The spare bytes resulting
  from the alignment should be set to '0'.

<p align="justify">
The container-chunk AMOD has the following format:

<font face="Courier New" color="#007F00"><pre>
struct CHUNK_AMOD {
   byte  name[4];	// "AMOD"
   dword size;		// Body size
   word  identifier;	// 0
   byte  type;		// 0
   byte  version;	// 1
   byte  description[20]; // Short file description (NOT song name!)
   byte  body[size];	// The sub-chunks
};
</font></pre>

<font face="Courier New" color="#007F00"><pre>
struct CHUNK_SAMP {
   byte  name[4];	// "SAMP"
   dword size;		// Body size
   word  identifier;	// Unique sample identifier
   byte  type;		// 0
   byte  version;	// 1
   byte  volume;	// Default volume (0..128)
   byte  flags; 	// b0 - 16bit, b2 - bidi_sustain, b3 - bidi_decay
   byte  note;		// Note relative to C-4 (-126..126)
   byte  pitch; 	// Note pitch (-128..127, 128=one halftone)
   dword sustain_start; // Sustain loop start (index to sampel, not byte)
   dword sustain_length;// Sustain loop length in sampels, not bytes
   dword decay_start;	// Decay loop start (index to sampel, not byte)
   dword decay_length;	// Decay loop length in sampels, not bytes
   dword length;	// Sample length in sampels, not bytes
   byte  data[N*length];// Sample data
};

struct CHUNK_SAMP_EXTN {
   byte  name[4];	// "SAMP"
   dword size;		// Body size
   word  identifier;	// Unique sample identifier
   byte  type;		// <font color="#00007F">1 - external</font>
   byte  version;	// 1 <font color="#00007F">
   word  original_id;	// Identifier in the external file
   byte  filename[?];	// Zero-terminated file name</font>
};
</font></pre>

<p align="justify">
If the type field contains value 1, the sample is stored in an external
  file (original_id specifies identifier of the actual sample in the
  external file).

<p align="justify">
Flags field defines some sample properties: 8/16-bitness and forward
  / bidirectional sustain and decay loops. Stereo samples (bit 1) aren't
  currently supported by the format.

<p align="justify">
Sample volume ranges between 0 and 128. It is simply a linear multiplier.
  128 is the normal sample volume, while 0 makes the sample muted.
  This volume is used when no volume is specified in a track, otherwise
  it is overriden by the requested volume.

<p align="justify">
Actual notes range between 0 (C-0) and 126 (F#10). Value 127 is reserved.
  Real sample frequency is calculated from the following formula:<br>
  <i>freq = 8363 * 2^(((note<<7)+pitch)/(12*128) - 4)</i>

<p align="justify">
A note played on a track can be in one of two stages: sustain or decay.
  In sustain stage the note is fully controllable, i.e. its properties
  (like volume or pitch) can be modified. After releasing the note, it
  enters the decay stage. In decay stage there are no means to control
  the note. A sample can have two different loops, used in different note
  stages (i.e. sustain loop and decay loop). If any of the loops is
  off, its length is set to 0. Normally the loops are forward (the sample
  pointer restarts from the loop beginning when reaching the loop end).
  Any of the loops can be bidirectional (ping-pong, sample pointer goes
  forwards then backwards) - this is specified in the flags.

<font face="Courier New" color="#007F00"><pre>
struct CHUNK_ENVL {
   byte  name[4];	// "ENVL"
   dword size;		// Body size
   word  identifier;	// Unique envelope identifier
   byte  type;		// 0
   byte  version;	// 1
   byte  align[4];	// Missing alignment
   byte  chunks[size-8]; // Chunks (EDIM) contained within ENVL
};

struct CHUNK_EDIM {
   byte  name[4];	// "EDIM"
   dword size;		// Body size
   word  identifier;	// Envelope type
   byte  type;		// 0
   byte  version;	// 1
   word  num_points;	// Number of points in the dimension
   word  sustain_start; // Sustain loop start tick
   word  sustain_end;	// Sustain loop end tick
   word  decay_start;	// Decay loop start tick
   word  decay_end;	// Decay loop end tick
   word  decay_rate;	// Speed of returning to 0 level
   ENVLPOINT positions[num_points];
};

struct ENVLPOINT {
   word delta_pos;	// Delta tick position since last point
   word value;		// Value at the point (signed)
   word period; 	// Period of the variation
   word start_amp;	// Starting amplitude of the variation
   word end_amp;	// Ending amplitude of the variation
};
</font></pre>

<p align="justify">
Envelopes control multiple sound properties. A single envelope stored
  in a chunk can control one or more properties at a time. Each "dimension"
  corresponds to some property, specified by the identifier field in
  EDIM chunk. Below there are listed properties (and ids) currently
  defined:

<ul>
   <li> 0 - Volume (0..128),
   <li> 1 - Horizontal Angle of Position (Asimuth, Panning) (-128..127),
   <li> 2 - Vertical Angle of Position (Elevation) (-128..127),
   <li> 3 - Pitch (-128..127)
</ul>

<p align="justify">
An envelope is in fact a chart of a property. Generally its values change
  with time (according to current effect processing tempo) and are expressed
  with a signed word (saturated to the values specified above). The chart
  is created from control points with a cubic (Hermite) spline. To the
  resulting curve, a variation is added, which is simply a sine wave
  with the given parameters (period in 2*pi/ticks, unsigned starting and
  ending amplitude). There is also a decay rate given, which is used in
  decay stage, and causes the value of the envelope to go to 0.

<font face="Courier New" color="#007F00"><pre>
struct CHUNK_INST {
   byte  name[4];	// "INST"
   dword size;		// Body size
   word  identifier;	// Unique instrument identifier
   byte  type;		// 0
   byte  version;	// 1
   word  envelope_id;	// Identifier of default envelope
   byte  start_note;	// First note in sample[] array
   byte  end_note;	// Last note in sample[] array
   word  sample[end_note-start_note+1]; // Sample dispatch table
};
</pre></font>

<p align="justify">
The instrument chunk contains information of default envelope used with
  notes played with it, as well as sample identifies assigned to particular
  note channels. There should be at least one sample identifier specified.
  All notes below start_note and above end_note use samples corresponding
  to the respective bounding notes.

<font face="Courier New" color="#007F00"><pre>
struct CHUNK_SONG {
   byte  name[4];	// "SONG"
   dword size;		// Body size
   word  identifier;	// Unique song identifier
   byte  type;		// 0
   byte  version;	// 1
   byte  title[60];	// Song title
   byte  composer[32];	// Composer's name
   byte  chunks[size-96]; // TRAK and INFO as well as other chunks
};

struct CHUNK_INFO {
   byte  name[4];	// "INFO"
   dword size;		// Body size
   word  identifier;	// 0
   byte  type;		// 0
   byte  version;	// 1
   byte  info[size-4];	// User text
};

struct CHUNK_TRAK {
   byte  name[4];	// "TRAK"
   dword size;		// Body size
   word  identifier;	// Identifier of track's instrument
   byte  type;		// 0
   byte  version;	// 1
   dword restart_pos;	// Track restart position (see EndTrack())
   byte  stream[size-8]; // Byte stream of events
};
</pre></font>

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
Tracks
</center></b></i></font>

<p align="justify">
Each track is a sequence of commands. These commands control how notes
  are played.

<p align="justify">
A track uses one and only one instrument. All notes that appear on the track
  correspond to that instrument. There are 127 channels on each track.
  The default note pitch on each channel corresponds to note with the
  channel number (for example on channel 0 note C-0 is played by default).
  It is possible to change the pitch. Each channel has assigned a sample
  (in the sample dispatch table in instrument chunk). This sample is used
  for notes played on the channel.

<p align="justify">
These channels can correspond to what actually is known from real trackers,
  but they in fact do not exist. The player logic developed for AM format
  includes a mechanism called VCA - virtual channel allocation. When a
  note with NoteOn() command appears on a track, it looks like the channel
  corresponding to the note number is used for sample playback. All
  subsequent commands coming with the same note correspond to that channel.
  There can be no other note with the same number at the same time, but other
  notes can be played with the same pitch using Pitch() command. When the
  note is released with NoteOff() command, the actual sample in the virtual
  channel enters decay stage, and new commands cannot control it anymore.
  The "channel" number can then be used for another notes, though the sample
  of the released note still plays (depending on volume envelope and
  sample decay loop).

<p align="justify">
This is maybe difficult to understand, but this is all going about terminology.
  In practice, a track consists of a sequence of events occuring at some
  time. Each event bears a subset of notes within, which are affected at
  the time of the event's occurence. Each note comes with a few commands
  that control it. That is the idea of a track - nothing more, nothing less.
  As a matter of fact, if a note is playing in sustain stage, for example
  F#4, another F#4 cannot be issued. When the playing note is release, i.e.
  enters decay stage, there is room for another F#4. The trick is that one
  can play a few notes at the same pitch (using Pitch() command), and from
  this point it looks like the notes were channels (though they are not).

<p align="justify">
Events can happen at fixed moments in time, called ticks. How often the
  ticks occur depends on tempo. Tempo is like BPM in modules (2*tempo/5
  gives frequency in Hz). However there is no Speed parameter, like in
  modules, therefore the control abilities are exactly like in MIDI.
  All tracks have the same tempo, i.e. they are synchronous. Effects and
  envelopes have their own tempo setting which is independent of current
  track tempo.

<p align="justify">
An event consists of delta time (in ticks) and a sequence of notes. Delta
  time tells how many ticks have passed since the last event. It is either
  one or two bytes long, in big endian order. If this is one byte, the MSb is 0.
  If MSb is 1, the next, low byte follows. A sample code that loads delta
  time from a stream of events:

<font face="Courier New" color="#007F00"><pre>
   int track_index;
   unsigned char track[];
   int delta_time = track[track_index++];
   if (delta_time & 0x80 != 0)
      delta_time = ((delta_time & 0x7F) << 8) | track[track_index++];
</pre></font>

<p align="justify">
Each note in the list contains commands that control it. The MSb of the
  note byte specifies whether this is the last note (=0) or not (=1).

<p align="justify">
Following a note byte there are its commands (at least one). Each command
  contains a command byte and optional parameters. The MSb of command
  byte has the same meaning as MSb of note byte (i.e. specifies whether
  it is the last command concerning the note). The command field is on
  bits 0..3 and a 3-bit field (4..6) specifies the number of parameter
  bytes coming with the command (0..7). The following code loads notes
  and commands from a stream:

<font face="Courier New" color="#007F00"><pre>
   int track_index;
   unsigned char track[];

   do {
      int note_byte = track[track_index++];
      do {
	 int command_byte = track[track_index++];
	 int num_params = (command_byte >> 4) & 0x07;
	 execute_command (note_byte & 0x7F, command_byte & 0x0F,
			  num_params, &track[track_index]);
      } while (command_byte & 0x80 != 0);
   } while (note_byte & 0x80 != 0);
</pre></font>

<p align="justify">
As mentioned before, note 127 is a special note. Commands coming with
  this note correspond to all notes on the track.
  Global commands (i.e. commands that do not correspond to any particular
  notes) can come with any note, even with 127.

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
Commands
</center></b></i></font>

<p align="justify">
GC tells that the command is a global command.

<p><center><table border=1>
<tr>
<th align="center" valign="top"><font face="Verdana,Arial">Code</font>
<th align="center" valign="top"><font face="Verdana,Arial">Command</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">0</font>
<td align="left" valign="top"><font face="Verdana,Arial">EndTrack()<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   This is a fail-safe command. When encountered, it indicates end of track.
   It is enough for a player to put eight 0 bytes at end of each track,
   and for sure the 0 command will be encountered, even if the track is
   corrupted.
   <br>
   No matter if there are any parameters with this command, they are ignored.
   This command should be put in track far enough (with delta time big enough)
   to be sure that all instruments are quiet (their envelopes reached
   a suitable point). In looping songs this command causes restart of a
   track from its restart position. Events at the restart position are
   executed in the same tick as the EndTrack() command.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">1</font>
<td align="left" valign="top"><font face="Verdana,Arial">NoteOn(Delay)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Starts playing the note. The actual sample is automatically assigned
   to some free channel using VCA mechanism. The command retriggers
   already playing note, i.e. starts it on the same channel it was playing on.
   <br>
   The optional parameter (one or two bytes, little endian) specifies after
   how many ticks the note is going to enter decay stage. If this parameter
   exists, the note is not controllable, i.e. it appears like the command
   was followed by NoteOff() with the Delay parameter.
   <br>
   This command has not meaning if issued with note 127, it is simply ignored
   in this case.
   </font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">2</font>
<td align="left" valign="top"><font face="Verdana,Arial">NoteOff(Delay)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Starts ending the note. The instrument envelopes and sample loops are
   exiting sustain stage and entering decay stage. The actual sample is not
   removed from the channel until the volume becomes 0 (it is 0 if the
   instrument has no volume envelope).
   <br>
   The optional Delay parameter (one or two bytes, little endian) prevents
   immediate removal of the note - it may remain in sustain stage for the
   number of ticks specified by Delay, and then enter decay stage. Even if
   the decay stage is delayed, the note is not accessible any more by any
   command, and another same note can be started and managed.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">3</font>
<td align="left" valign="top"><font face="Verdana,Arial">Volume(Volume,Rate)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Starts sliding volume. The Volume parameter specifies destination volume
   and varies between 0 (not audible) and 255 (max volume), where volume of
   128 is the normal sample volume (255 is almost 2x amplification).
   Rate is a positive number (0..255) that specifies the amount of steps
   per tick that will be added to the volume, until it reaches the specified
   destination level. (Example:
   <font face="Courier New">sample.volume += Rate/8</font>) If Rate is 0 or
   does not exist (it is possible that there is only Volume parameter) the
   volume is set at once.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">4</font>
<td align="left" valign="top"><font face="Verdana,Arial">Pitch(Note,Rate)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Starts sliding pitch. The Note parameter specifies destination pitch.
   The frequency of the note is altered, but the note remains the same.
   This means that when Pitch(C-5) is issued for C-4 note, this note
   will be played as it was C-5 (of course after reaching that level).
   Rate (0..255) is the finetune that is added
   or substracted to the note in one tick (each note has 128 finetune levels).
   (Example: <font face="Courier New">sample.pitch += Rate*4</font>) If Rate
   is 0 or does not exist, the pitch is adjusted immediately. If note is
   >=127 (invalid) the sliding of the pitch is stopped at once; this can
   be used to cancel previous Pitch() command.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">5</font>
<td align="left" valign="top"><font face="Verdana,Arial">Position(A,E,RateA,RateE)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Starts sliding position. The possible variations of this command are:
   <ul>
   <li>Position(A) - set asimuth (horizontal angle, like panning)
   <li>Position(A,E) - set asimuth and elevation (vertical angle)
   <li>Position(A,E,RateA,RateE) - slide position to A and E at RateA and RateE, respectively.
   </ul>
   <p align="justify">
   Sound position can be treated as two-dimensional panning. Rate is like for
   Volume() command (except that elevation is modified by Rate/16).
   If Rate is 0 or does not exist, the position is adjusted at once.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">6</font>
<td align="left" valign="top"><font face="Verdana,Arial">Vibrato(Rate,Amp,Type)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Alters vibrato (pitch vibration) parameters. The possible variations
   of the command are:
   <ul>
   <li>Vibrato(Rate) - set rate
   <li>Vibrato(Rate,Amp) - set rate and amplitude
   <li>Vibrato(Rate,Amp,Type) - set all parameters
   </ul>
   <p align="justify">
   Rate is the amount that is added to the vibrato waveform position every tick.
   Example:<br>
   <font face="Courier New">wavepos = (wavepos + Rate) & 255</font><br>
   Amp is the vibrato amplitude. Example:<br>
   <font face="Courier New">pitch = basepitch + (Waveform[wavepos]*Amplitude>>8)</font><br>
   Type specifies waveform type (like for envelope variation): 0=sine,
   1=square, 2=ramp up, 3=ramp down.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">7</font>
<td align="left" valign="top"><font face="Verdana,Arial">Tremolo(Rate,Amp,Type)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Alters tremolo parameters. Works the same as vibrato, but on volume, not on
   pitch.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">8</font>
<td align="left" valign="top"><font face="Verdana,Arial">Envelope(loPos,hiPos,loID,hiID)</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Sets envelope position. The optional two-byte ID parameter causes using
   another envelope (with the specified indentifier). If there is unsufficient
   number of dimensions in the specified optional envelope, only the existing
   dimensions are replaced.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">9</font>
<td align="left" valign="top"><font face="Verdana,Arial">Tempo(Tempo,Rate)<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Slides tempo of all tracks. Tempo*2/5 is tick frequency (in Hz).
   Default tempo (at song startup) is 125. The optional Rate parameter
   specifies rate at which the tempo is slided.
   (Example: <font face="Courier New">tempo += Rate/8</font>). If Rate is
   0 or does not exist, the tempo is set at once.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">10</font>
<td align="left" valign="top"><font face="Verdana,Arial">EffectTempo(Tempo,Rate)<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Works similarly to Tempo(), but adjust effect/envelope tempo. As mentioned
   above, effect tempo is asynchronous to track tempo.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">11</font>
<td align="left" valign="top"><font face="Verdana,Arial">GlobalVolume(Volume,Rate)<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Works similarly to Volume(), but adjusts global volume for all tracks.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">12</font>
<td align="left" valign="top"><font face="Verdana,Arial">Tremor(Rate,Amp,Type)<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   Works similarly to Tremolo(), but adjusts global volume vibration.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">13</font>
<td align="left" valign="top"><font face="Verdana,Arial">ExternalSync(ID,...)<font size="-2" color="#AA0000"><b>GC</b></font></font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   A command that does nothing. It can be used for external synchronization.
   This means that when the ID parameter exists, it is passed by the player
   to an external application. The ID parameter can have any number of
   bytes (if there is no ID parameter a 0 is passed).</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">14</font>
<td align="left" valign="top"><font face="Verdana,Arial">Undefined</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   This command is currently unused. It shouldn't be used for any
   special purposes.</font>
<tr>
<td align="left" valign="top"><font face="Verdana,Arial">15</font>
<td align="left" valign="top"><font face="Verdana,Arial">Meta-command</font>
<tr><td>
<td colspan="3" align="left" valign="top"><font face="Verdana,Arial"><p align="justify">
   The first byte (parameter) specifies the meta-command type. Meta-commands
   0 through 15 are reserved for future use. Meta-commands 16 through
   255 can be freely used by compositional utilities (trackers).
   </font>

</table></center>

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
Scripting Samples
</center></b></i></font>

<p align="justify">
A simple scripting language shows the general idea behind tracks. Keep in
mind that commands are grouped in packets, which either come with notes
or as global commands (note 127). Groups of notes are called time-events.
A track can be assigned only to one instrument, and this cannot be changed
at run-time. Of course many tracks can play the same instrument.

<font face="Courier New" color="#007F00"><pre>
Event(0) {		// delta time 0 ticks
   C-4: NoteOn();	// NoteOn() with note C-4 starts that note
};
Event(4) {		// 4 ticks after previous event
   C-4: NoteOff();	// the note C-4 enters decay state
};
Event(1) {		// 1 tick after
   C-4: NoteOn();	// these 3 notes make up a C-minor chord
   D#4: NoteOn();	//  /
   G-4: NoteOn();	// /
};
Event(4) {
   C-4: Pitch(C-5,32);	// slide to C-5, one halftone per tick
   D#4: NoteOff();
   G-4: Volume(0,88);	// slide volume to 0, 11 levels per tick
};
Event(12) {
   C-4: NoteOff();	// C-4 enters decay state
   C-4: NoteOn();	// start C-4 once again (on another channel)
   C-4: Volume(0x40);	// set C-4 volume (0x80 is 100%)
   D#4: NoteOff();	// D#4 off
   G-4: NoteOff();	// G-4 off
};
Event(1) {
   C-4: NoteOff(1);	// C-4 will enter decay state in next tick
   C-4: NoteOn();	// start C-4 again
};
Event(1) {
   C-4: NoteOff();	// turn C-4 off
};
</pre></font>

<hr><p><font face="Arial" size=5 color="#007F00"><i><b><center>
Credits
</center></b></i></font>

Special thanks to TAD, Fabled, berk and others who helped me to develop
this format.

</body></html>