Copyright (c) Hyperion Entertainment and contributors.

SAMP IFF Sampled Sound

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

SAMP

                       IFF FORM "SAMP" Sampled Sound

 Date:   Dec 3,1989
 From:   Jim Fiore and Jeff Glatt, dissidents

   The form "SAMP" is a file format used to store sampled sound data in some
ways like the current standard, "8SVX". Unlike "8SVX", this new format is not
restricted to 8 bit sample data. There can be more than one waveform per
octave, and the lengths of different waveforms do not have to be factors of
2. In fact, the lengths (waveform size) and playback mapping (which musical
notes each waveform will "play") are independently determined for each wave-
form. Furthermore, this format takes into account the MIDI sample dump stan-
dard (the defacto standard for musical sample storage), while also incorpo-
rating the ability to store Amiga specific info (for example, the sample data
that might be sent to an audio channel which is modulating another channel).
Although this form can be used to store "sound effects" (typically oneShot
sounds played at a set pitch), it is primarily intended to correct the many
deficiencies of the "8SVX" form in regards to musical sampling. Because the
emphasis is on musical sampling, this format relies on the MIDI (Musical
Instrument Digital Interface) method of describing "sound events" as does
virtually all currently manufactured, musical samplers. In addition, it at-
tempts to incorporate features found on many professional music samplers, in
anticipation that future Amiga models will implement 16 bit sampling, and
thus be able to achieve this level of performance. Because this format is 
more complex than "8SVX", programming examples to demonstrate the use of this
format have been included in both C and assembly. Also, a library of func-
tions to read and write SAMP files is available, with example applications.

  SEMANTICS: When MIDI literature talks about a sample, usually it means a
collection of many sample points that make up what we call a "wave".


        =====SIMILARITIES AND DIFFERENCES FROM THE "8SVX" FORM=======

   Like "8SVX", this new format uses headers to separate the various sections
of the sound file into chunks. Some of the chunks are exactly the same since
there wasn't a need to improve them. The chunks that remain unchanged are as
follows:

   "(c) "
   "AUTH"
   "ANNO"

  Since these properties are all described in the original "8SVX" document,
please refer to that for a description of these chunks and their uses. Like
the "8SVX" form, none of these chunks are required to be in a sound file.
If they do appear, they must be padded out to an even number of bytes.

  Furthermore, two "8SVX" chunks no longer exist as they have been incorpo-
rated into the "BODY" chunk. They are:

   "ATAK"
   "RLSE"

  Since each wave can be completely different than the other waves in the 
sound file (one wave might be middle C on a piano, and another might be a
snare drum hit), it is necessary for each wave to have its own envelope de-
scription, and name.

  The major changes from the "8SVX" format are in the "MHDR", "NAME", and
"BODY" chunks.


          =================THE "SAMP" HEADER================

 At the very beginning of a sound file is the "SAMP" header. This is used to
determine if the disk file is indeed a SAMP sound file. It's attributes are
as follows:

#define ID_SAMP MakeID('S','A','M','P')

  In assembly, this looks like:

      CNOP 0,2  ;word-align

SAMP         dc.b  'SAMP'
sizeOfChunks dc.l  [sizes of all subsequent chunks summed]


             =================THE "MHDR" CHUNK=================

  The required "MHDR" chunk immediately follows the "SAMP" header and consists
of the following components:

#define ID_MHDR MakeID('M','H','D','R')

  /* MHDR size is dependant on the size of the imbedded PlayMap. */

  typedef struct{
   UBYTE NumOfWaves,      /* The number of waves in this file */
     Format,              /* # of ORIGINAL significant bits from 8-28 */
     Flags,               /* Various bits indicate various functions */
     PlayMode,            /* determines play MODE of the PlayMap */
     NumOfChans,
     Pad,
     PlayMap[128*4],  /* a map of which wave numbers to use for
                          each of 128 possible Midi Notes. Default to 4 */
   } MHDRChunk;

   The PlayMap is an array of bytes representing wave numbers. There can be a
total of 255 waves in a "SAMP" file. They are numbered from 1 to 255. A wave
number of 0 is reserved to indicate "NO WAVE". The Midi Spec 1.0 designates
that there are 128 possible note numbers (pitches), 0 to 127. The size of an
MHDR's PlayMap is determined by (NumOfChans * 128). For example, if NumOfChans
= 4, then an MHDR's PlayMap is 512 bytes. There are 4 bytes in the PlayMap
for EACH of the 128 Midi Note numbers. For example, the first 4 bytes
in PlayMap pertain to Midi Note #0. Of those 4 bytes, the first byte is the
wave number to play back on Amiga audio channel 0. The second byte is the
wave number to play back on Amiga audio channel 1, etc. In this way, a single
Midi Note Number could simultaneously trigger a sound event on each of the 4
Amiga audio channels. If NumOfChans is 1, then the PlayMap is 128 bytes and
each midi note has only 1 byte in the PlayMap. The first byte pertains to midi
note #0, the second pertains to midi note #1, etc. In this case, a player
program might elect to simply play back the PlayMap wave number on any
available amiga audio channel. If NumOfChans = 0, then there is no imbedded
PlayMap in the MHDR, no midi note assignments for the waves, and an application
should play back waves on any channel at their default sampleRates.
  In effect, the purpose of the PlayMap array is to determine which (if any)
waves are to be played back for each of the 128 possible Midi Note Numbers.
Usually, the MHDR's NumOfChans will be set to 4 since the Amiga has 4 audio
channels. For the rest of this document, the NumOfChans is assumed to be 4.
  As mentioned, there can be a total of 255 waves in a "SAMP" file, numbered
from 1 to 255. A PlayMap wave number of 0 is reserved to indicate that NO WAVE
number should be played back. Consider the following example:

  The first 4 bytes of PlayMap are  1,3,0,200.

  If a sample playing program receives (from the serial port or another task
perhaps) Midi Note Number 0, the following should occur:

  1) The sampler plays back wave 1 on Amiga audio channel
     number 0 (because the first PlayMap byte is 1).
  2) The sampler plays back wave 3 on Amiga audio channel
     number 1 (because the second PlayMap byte is 3).
  3) The sampler does not effect Amiga audio channel 2 in
     any way (because the third PlayMap byte is a 0).
  4) The sampler plays back wave 200 on Amiga audio channel
     number 4 (because the fourth PlayMap byte is 200).

 (This assumes INDEPENDANT CHANNEL play MODE to be discussed later in this
  document.)

   All four of the PlayMap bytes could even be the same wave number. This would
cause that wave to be output of all 4 Amiga channels simultaneously.

  NumOfWaves is simply the number of waves in the sound file.

  Format is the number of significant bits in every sample of a wave.
For example, if Format = 8, then this means that the sample data is an
8 bit format, and that every sample of the wave can be expressed by a single
BYTE. (A 16 bit sample would need a WORD for every sample point).

  Each bit of the Flags byte, when set, means the following:

Bit #0 - File continued on another disc. This might occur if the SAMP file
         was too large to fit on 1 floppy. The accepted practice (as incor-
         porated by Yamaha's TX sampler and Casio's FZ-1 for example) is to
         dump as much as possible onto one disc and set a flag to indicate
         that more is on another disc's file. The name of the files must
         be the related. The continuation file should have its own SAMP header
         MHDR, and BODY chunks. This file could even have its continuation
         bit set, etc. Never chop a sample wave in half. Always close the
         file on 1 disc after the last wave which can be completely saved.
         Resume with the next wave within the BODY of the continuation file.
         Also, the NumOfWaves in each file's BODY should be the number saved
         on that disc (not the total number in all combined disk files).
         See the end of this document for filename conventions.

  In C, here is how the PlayMap is used when receiving a midi note-on event:

  MapOffset = (UBYTE) MidiNoteNumber * numOfChans;
  /* MidiNoteNumber is the received note number (i.e. the second byte of a
     midi note-on event. numOfChans is from the SAMP MHDR. */
  chan0waveNum = (UBYTE) playMap[MapOffset];
  chan1waveNum = (UBYTE) playMap[MapOffset+1];
  chan2waveNum = (UBYTE) playMap[MapOffset+2];
  chan3waveNum = (UBYTE) playMap[MapOffset+3];

  if (chan0waveNum != 0)
  { /* get the pointer to wave #1's data, determine the values
       that need to be passed to the audio device, and play this
       wave on Amiga audio channel #0 (if INDEPENDANT PlayMode) */
  }

   /* do the same with the other 3 channel's wave numbers */

  In assembly, the "MHDR" structure looks like this:

             CNOP 0,2
MHDR        dc.b 'MHDR'
sizeOfMHDR  dc.l [this is 6 + (NumOfChans * 128) ]
NumOfWaves  dc.b [a byte count of the # of waves in the file]
Format      dc.b [a byte count of the # of significant bits in a sample point]
Flags       dc.b [bit mask]
PlayMode    dc.b [play MODE discussed later]
NumOfChans  dc.b [# of bytes per midi note for PlayMap]
PlayMap     ds.b [128 x NumOfChans bytes of initialized values]

   and a received MidiNoteNumber is interpreted as follows:

   moveq   #0,d0
   move.b  MidiNoteNumber,d0  ;this is the received midi note #
   bmi.s   Illegal_Number     ;exit, as this is an illegal midi note #
   moveq   #0,d1
   move.b  NumOfChans,d1
   mulu.w  d1,d0              ;MidiNoteNumber x NumOfChans
   lea     PlayMap,a0
   adda.l  d0,a0
   move.b  (a0)+,chan0waveNum
   move.b  (a0)+,chan1waveNum
   move.b  (a0)+,chan2waveNum
   move.b  (a0),chan3waveNum

      tst.b   chan0waveNum
      beq.s   Chan1
 ;Now get the address of this wave number's sample data, determine the
 ;values that need to be passed to the audio device, and output the wave's
 ;data on Amiga chan 0 (assuming INDEPENDANT PlayMode).

Chan1 tst.b chan1waveNum
      beq.s Chan2
 ;do the same for the other wave numbers, etc.


    =====================THE "NAME" CHUNK=========================

  #define ID_NAME MakeID('N','A','M','E')

  If a NAME chunk is included in the file, then EVERY wave must have a name.
Each name is NULL-terminated. The first name is for the first wave, and it
is immediately followed by the second wave's name, etc. It is legal for a
wave's name to be simply a NULL byte. For example, if a file contained 4
waves and a name chunk, the chunk might look like this:

           CNOP 0,2

Name       dc.b 'NAME'
sizeOfName dc.l 30
           dc.b 'Snare Drum',0  ;wave 1
           dc.b 'Piano 1',0     ;wave 2
           dc.b 'Piano A4',0    ;wave 3
           dc.b 0               ;wave 4
           dc.b 0

  NAME chunks should ALWAYS be padded out to an even number of bytes. (Hence
the extra NULL byte in this example). The chunk's size should ALWAYS be even
consequently. DO NOT USE the typical IFF method of padding a chunk out to an
even number of bytes, but allowing an odd number size in the header.


            ==============THE "BODY" CHUNK===============

 The "BODY" chunk is CONSIDERABLY different than the "8SVX" form. Like all
chunks it has an ID.

   #define ID_BODY MakeID('B','O','D','Y')

Every wave has an 80 byte waveHeader, followed by its data. The waveHeader
structure is as follows:

 typedef struct {
   ULONG  WaveSize;        /* total # of BYTES in the wave (MUST be even) */
   UWORD  MidiSampNum;     /* ONLY USED for Midi Dumps */
   UBYTE  LoopType,        /* ONLY USED for Midi Dumps */
   InsType;         /* Used for searching for a certain instrument */
   ULONG  Period,    /* in nanoseconds at original pitch */
   Rate,             /* # of samples per second at original pitch */
   LoopStart,         /* an offset in BYTES (from the beginning of the
                              of the wave) where the looping portion of the
                              wave begins. Set to WaveSize if no loop. */
   LoopEnd;           /* an offset in BYTES (from the beginning of the
                              of the wave) where the looping portion of the
                              wave ends. Set to WaveSize if no loop. */
   UBYTE  RootNote,        /* the Midi Note # that plays back original pitch */
   VelStart;            /* 0 = NO velocity effect, 128 =
                                negative direction, 64 = positive
                                direction (it must be one of these 3) */
   UWORD VelTable[16];  /* contains 16 successive offset values
                                in BYTES from the beginning of the wave */

  /* The ATAK and RLSE segments contain an EGPoint[] piece-wise
     linear envelope just like 8SVX. The structure of an EGPoint[]
     is the same as 8SVX. See that document for details. */

   ULONG  ATAKsize,     /* # of BYTES in subsequent ATAK envelope.
                             If 0, then no ATAK data for this wave. */
   RLSEsize,            /* # of BYTES in subsequent RLSE envelope
                             If 0, then no RLSE envelope follows */

  /* The FATK and FRLS segments contain an EGPoint[] piece-wise
     linear envelope for filtering purposes. This is included in
     the hope that future Amiga audio will incorporate a VCF
     (Voltage Controlled Filter). Until then, if you are doing any
     non-realtime digital filtering, you could store info here. */

  sizeOfFATK,            /* # of BYTES in FATK segment */
  sizeOfFRLS,            /* # of BYTES in FRLS segment */

  USERsize;        /*   # of BYTES in the following data
                              segment (not including USERtype).
                              If zero, then no user data */
  UWORD  USERtype;     /* See explanation below. If USERsize
                             = 0, then ignore this. */

 /* End of the waveHeader. */

 /* The data for any ATAK, RLSE, FATK, FRLS, USER, and the actual wave
    data for wave #1 follows in this order:
    Now list each EGPoint[] (if any) for the VCA's (Voltage Controlled Amp)
    attack portion.
    Now list each EGPoint[] for the VCA's (Voltage Controlled Amp)
    release portion.
    List EGPoints[] (if any) for FATK.
    List EGPoints[] if any for FRLS */
    Now include the user data here if there is any. Just pad it out
    to an even number of bytes and have USERsize reflect that.
    Finally, here is the actual sample data for the wave. The size (in BYTES)
    of this data is WaveSize. It MUST be padded out to an even number of bytes. */

 } WaveFormInfo;

 /* END OF WAVE #1 */

 /* The waveHeader and data for the next wave would now follow. It is
    the same form as the first wave */


  In assembly,  the BODY chunk looks like this:

           CNOP 0,2
BodyHEADER dc.b 'BODY'
sizeOfBody dc.l  [total bytes in the BODY chunk not counting 8 byte header]

   ; Now for the first wave
WaveSize       dc.l  ;[total # of BYTES in this wave (MUST be even)]
MidiSampNum    dc.w  ;[from Midi Sample Dump]  ; ONLY USED for Midi Dumps
LoopType       dc.b  ;[0 or 1]                 ; ONLY USED for Midi Dumps
InsType        dc.b  0
Period         dc.l  ;[period in nanoseconds at original pitch]
Rate           dc.l  ;[# of samples per second at original pitch]
LoopStart      dc.l    ;[an offset in BYTES (from the beginning of the
                       ; of the wave) to where the looping
                       ; portion of the wave begins.]
LoopEnd        dc.l    ;[an offset in BYTES (from the beginning of the
                       ; of the wave) to where the looping
                       ; portion of the wave ends]
RootNote       dc.b    ;[the Midi Note # that plays back original pitch]
VelStart       dc.b    ;[0, 64, or 128]
VelTable       dc.w    ;[first velocity offset]
               dc.w    ;[second velocity offset]...etc
               ds.w 14 ;...for a TOTAL of 16 velocity offsets

ATAKsize       dc.l  ;# of BYTES in subsequent ATAK envelope.
                     ;If 0, then no ATAK data for this wave.
RLSEsize       dc.l  ;# of BYTES in subsequent RLSE envelope
                     ;If 0, then no RLSE data
FATKsize       dc.l  ;# of BYTES in FATK segment
FRLSsize       dc.l  ;# of BYTES in FRLS segment
USERsize       dc.l  ;# of BYTES in the following User data
                     ;segment (not including USERtype).
                     ;If zero, then no user data
USERtype       dc.w  ; See explanation below. If USERsize
                  ; = 0, then ignore this.

  ;Now include the EGpoints[] (data) for the ATAK if any
  ;Now the EGpoints for the RLSE
  ;Now the EGpoints for the FATK
  ;Now the EGpoints for the FLSR
  ;Now include the user data here if there is any. Just pad
  ;it out to an even number of bytes.
  ;After the userdata (if any) is the actual sample data for
  ;the wave. The size (in BYTES) of this segment is WaveSize.
  ;It MUST be padded out to an even number of bytes.

  ; END OF WAVE #1


      =============STRUCTURE OF AN INDIVIDUAL SAMPLE POINT=============

   Even though the next generation of computers will probably have 16 bit
audio, and 8 bit sampling will quickly disappear, this spec has sizes expressed
in BYTES. (ie LoopStart, WaveSize, etc.) This is because each successive
address in RAM is a byte to the 68000, and so calculating address offsets
will be much easier with all sizes in BYTES. The Midi sample dump, on the 
other hand, has sizes expressed in WORDS. What this means is that if you
have a 16 bit wave, for example, the WaveSize is the total number of BYTES,
not WORDS, in the wave.
  Also, there is no facility for storing a compression type. This is because
sample data should be stored in linear format (as per the MIDI spec). Currently,
all music samplers, regardless of their internal method of playing sample data
must transmit and expect to receive sample dumps in a linear format. It is
up to each device to translate the linear format into its own compression
scheme. For example, if you are using an 8 bit compression scheme that yields
a 14 bit linear range, you should convert each sample data BYTE to a decom-
pressed linear WORD when you save a sound file. Set the MHDR's Format
to 14. It is up to the application to do its own compression upon loading
a file. The midi spec was set up this way because musical samplers need to
pass sample data between each other, and computers (via a midi interface).
Since there are almost as many data compression schemes on the market as
there are musical products, it was decided that all samplers should expect
data received over midi to be in LINEAR format. It seems logical to store it
this way on disc as well. Therefore, any software program "need not know" how
to decompress another software program's SAMP file. When 16 bit sampling is
eventually implemented there won't be much need for compression on playback
anyway. The continuation Flag solves the problem of disc storage as well.
  Since the 68000 can only perform math on BYTES, WORDS, or LONGS, it has
been decided that a sample point should be converted to one of these sizes
when saved in SAMP as follows:

 ORIGINAL significant bits          SAMP sample point size
 ­­­­­­­­­­­­­­­­­­­­­­­­­          ­­­­­­­­­­­­­­­­­­­­­­
            8                               BYTE
          9 to 16                           WORD
          17 to 28                          LONG

  Furthermore, the significant bits should be left-justified since it is
easier to perform math on the samples.

  So, for example, an 8 bit sample point (like 8SVX) would be saved as a
BYTE with all 8 bits being significant. The MHDR's Format = 8. No
conversion is necessary.

  A 12 bit sample point should be stored as a WORD with the significant bits
being numbers 4 to 15. (i.e shift the 12-bit WORD 4 places to the left). Bits
0, 1, 2 and 3 may be zero (unless some 16-bit math was performed and you wish to
save these results). The MHDR's Format = 12. In this way, the sample
may be loaded and manipulated as a 16-bit wave, but when transmitted via
midi, it can be converted back to 12 bits (rounded and shifted right by 4).

  A 16 bit sample point would be saved as a WORD with all 16 bits being
significant. The MHDR's Format = 16. No conversion is necessary.


          ============== The waveHeader explained ==============

   The WaveSize is, as stated, the number of BYTES in the wave's sample table.
If your sample data consisted of the following 8 bit samples:

    BYTE  100,-90,80,-60,30,35,40,-30,-35,-40,00,12,12,10

 then WaveSize = 14. (PAD THE DATA OUT TO AN EVEN NUMBER OF BYTES!)

  The MidiSampNum is ONLY used to hold the sample number received from a MIDI
Sample Dump. It has no bearing on where the wave should be placed in a SAMP
file. Also, the wave numbers in the PlayMap are between 1 to 255, with 1 being
the number of the first wave in the file. Remember that a wave number of 0 is
reserved to mean "no wave to play back". Likewise, the LoopType is only used
to hold info from a MIDI sample dump.

   The InsType is explained at the end of this document. Often it will be set
to 0.

   The RootNote is the Midi Note number that will play the wave back at it's
original, recorded pitch. For example, consider the following excerpt of a
PlayMap:

  PlayMap  {2,0,0,4       /* Midi Note #0 channel assignment */
            4,100,1,0     /* Midi Note #1    "        "  */
            1,4,0,0       /* Midi Note #2    "        "  */
            60,2,1,1...}  /* Midi Note #3    "        "  */

  Notice that Midi Notes 0, 1, and 2 are all set to play wave number 4 (on
Amiga channels 3, 0, and 1 respectively). If we set wave 4's RootNote = 1,
then receiving Midi Note number 1 would play back wave 4 (on Amiga channel 0)
at it's original pitch. If we receive a Midi Note number 0, then wave 4 would
be played back on channel 3) a half step lower than it's original pitch. If we
receive Midi Note number 2, then wave 4 would be played (on channel 1) a half
step higher than it's original pitch. If we receive Midi Note number 3, then
wave 4 would not be played at all because it isn't specified in the PlayMap
bytes for Midi Note number 3.

  The Rate is the number of samples per second of the original pitch.
For example, if Rate = 20000, then to play the wave at it's original
pitch, the sampling period would be:

     (1/20000)/.279365 = .000178977

#define AUDIO_HARDWARE_FUDGE .279365

where .279365 is the Amiga Fudge Factor (a hardware limitation). Since the
amiga needs to see the period in terms of microseconds, move the decimal place
to the right 6 places and our sampling period = 179 (rounded to an integer).
In order to play the wave at higher or lower pitches, one would need to
"transpose" this period value. By specifying a higher period value, the Amiga
will play back the samples slower, and a lower pitch will be achieved. By
specifying a lower period value, the amiga will play back the sample faster,
and a higher pitch will be achieved. By specifying this exact period, the wave
will be played back exactly "as it was recorded (sampled)". ("This period is
JUST RIGHT!", exclaimed GoldiLocks.) Later, a method of transposing pitch will
be shown using a "look up" table of periods. This should prove to be the
fastest way to transpose pitch, though there is nothing in the SAMP format
that compels you to do it this way.

  The LoopStart is a BYTE offset from the beginning of the wave to where
the looping portion of the wave begins. For example, if SampleData points to
the start of the wave, then SampleData + LoopStart is the start address
of the looping portion. In 8SVX, the looping portion was referred to as
repeatHiSamples. The data from the start of the wave up to the start of the
looping portion is the oneShot portion of the wave. LoopEnd is a BYTE
offset from the beginning of the wave to where the looping portion ends. This
might be the very end of the wave in memory, or perhaps there might be still
more data after this point. You can choose to ignore this "trailing" data and
play back the two other portions of the wave just like an 8SVX file (except
that there are no other interpolated octaves of this wave).

  VelTable contains 16 BYTE offsets from the beginning of the wave. Each
successive value should be greater (or equal to) the preceding value. If
VelStart = POSITIVE (64), then for each 8 increments in Midi Velocity
above 0, you move UP in the table, add this offset to the wave's beginning
address (start of oneShot), and start playback at that address. Here is a 
table relating received midi note-on velocity vs. start playback address for
POSITIVE VelStart. SamplePtr points to the beginning of the sample.

 If midi velocity = 0, then don't play a sample, this is a note off
 If midi velocity = 1 to 7, then start play at SamplePtr + VelTable[0]
 If midi velocity = 8 to 15, then start at SamplePtr + VelTable[1]
 If midi velocity = 16 to 23, then start at SamplePtr + VelTable[2]
 If midi velocity = 24 to 31, then start at SamplePtr + VelTable[3]
 If midi velocity = 32 to 39, then start at SamplePtr + VelTable[4]
 If midi velocity = 40 to 47, then start at SamplePtr + VelTable[5]
 If midi velocity = 48 to 55, then start at SamplePtr + VelTable[6]
 If midi velocity = 56 to 63, then start at SamplePtr + VelTable[7]
 If midi velocity = 64 to 71, then start at SamplePtr + VelTable[8]
 If midi velocity = 72 to 79, then start at SamplePtr + VelTable[9]
 If midi velocity = 80 to 87, then start at SamplePtr + VelTable[10]
 If midi velocity = 88 to 95, then start at SamplePtr + VelTable[11]
 If midi velocity = 96 to 103, then start at SamplePtr + VelTable[12]
 If midi velocity = 104 to 111, then start at SamplePtr + VelTable[13]
 If midi velocity = 112 to 119, then start at SamplePtr + VelTable[14]
 If midi velocity = 120 to 127, then start at SamplePtr + VelTable[15]

We don't want to specify a scale factor and use integer division to find the
sample start. This would not only be slow, but also, it could never be certain
that the resulting sample would be a zero crossing if the start point is calcu-
lated "on the fly". The reason for having a table is so that the offsets can be
be initially set on zero crossings via an editor. This way, no audio "clicks"
guaranteed. This table should provide enough resolution.

   If VelStart = NEGATIVE (128), then for each 8 increments in midi
velocity, you start from the END of VelTable, and work backwards. Here
is a table for NEGATIVE velocity start.

 If midi velocity = 0, then don't play a sample, this is a note off
 If midi velocity = 1 to 7, then start play at SamplePtr + VelTable[15]
 If midi velocity = 8 to 15, then start at SamplePtr + VelTable[14]
 If midi velocity = 16 to 23, then start at SamplePtr + VelTable[13]
 If midi velocity = 24 to 31, then start at SamplePtr + VelTable[12]
 If midi velocity = 32 to 39, then start at SamplePtr + VelTable[11]
 If midi velocity = 40 to 47, then start at SamplePtr + VelTable[10]
 If midi velocity = 48 to 55, then start at SamplePtr + VelTable[9]
 If midi velocity = 56 to 63, then start at SamplePtr + VelTable[8]
 If midi velocity = 64 to 71, then start at SamplePtr + VelTable[7]
 If midi velocity = 72 to 81, then start at SamplePtr + VelTable[6]
 If midi velocity = 80 to 87, then start at SamplePtr + VelTable[5]
 If midi velocity = 88 to 95, then start at SamplePtr + VelTable[4]
 If midi velocity = 96 to 103, then start at SamplePtr + VelTable[3]
 If midi velocity = 104 to 111, then start at SamplePtr + VelTable[2]
 If midi velocity = 112 to 119, then start at SamplePtr + VelTable[1]
 If midi velocity = 120 to 127, then start at SamplePtr + VelTable[0]

 In essence, increasing midi velocity starts playback "farther into" the wave
for POSITIVE VelStart. Increasing midi velocity "brings the start point
back" toward the beginning of the wave for NEGATIVE VelStart.

 If VelStart is set to NONE (0), then the wave's playback start should
not be affected by the table of offsets.

 What is the use of this feature? As an example, when a snare drum is hit with
a soft volume, its initial attack is less pronounced than when it is struck
hard. You might record a snare being hit hard. By setting VelStart to a
NEGATIVE value and setting up the offsets in the Table, a lower midi velocity
will "skip" the beginning samples and thereby tend to soften the initial
attack. In this way, one wave yields a true representation of its instrument
throughout its volume range. Furthermore, stringed and plucked instruments
(violins, guitars, pianos, etc) exhibit different attacks at different
volumes. VelStart makes these kinds of waves more realistic via a software
implementation. Also, an application program can allow the user to enable/
disable this feature. See the section "Making the Velocity Table" for info on
how to best choose the 16 table values.


        =========MIDI VELOCITY vs. AMIGA CHANNEL VOLUME============

 The legal range for Midi Velocity bytes is 0 to 127. (A midi velocity of 0
 should ALWAYS be interpreted as a note off).

 The legal range for Amiga channel volume is 0 to 64. Since this is half of
 the midi range, a received midi velocity should be divided by 2 and add 1
 (but only AFTER checking for a received midi velocity of 0).

  An example of how to implement a received midi velocity in C:

  If ( ReceivedVelocity != 0 && ReceivedVelocity < 128 )
  {   /* the velocity byte of a midi message */
      If (velStart != 0)
      {
          tableEntry = ReceivedVelocity / 8;
          If (velStart == 64)
          {    /* Is it POSITIVE */
               startOfWave = SamplePtr + velTable[tableEntry];
                           /* ^where to find the sample start point */
          }
          If (velStart == 128)
          {    /* Is it NEGATIVE */
               startOfWave = SamplePtr + velTable[15 - tableEntry];
          }
          volume = (receivedVelocity/2 + 1;  /* playback volume */
          /* Now playback the wave */
      }
  }

  In assembly,

  lea      SampleData,a0        ;the start addr of the sample data
  moveq    #0,d0
  move.b   ReceivedVelocity,d0  ;the velocity byte of a midi message
  beq      A_NoteOff            ;If zero, branch to a routine to
                                ;process a note-off message.

  bmi      Illegal_Vol          ;exit if received velocity > 127
  ;---Check for velocity start feature ON, and direction
  move.b   VelStart,d1
  beq.s    Volume               ;skip the velocity offset routine if 0
  bmi.s    NegativeVel          ;is it NEGATIVE? (128)

  ;---Positive velocity offset
  move.l   d0,d1                ;duplicate velocity
  lsr.b    #3,d1                ;divide by 8
  add.b    d1,d1                ;x 2 because we need to fetch a word
  lea      VelTable,a1     ;start at table's HEAD
  adda.l   d1,a1                ;go forward
  move.w   (a1),d1              ;get the velocity offet
  adda.l   d1,a0          ;where to start actual playback
  bra.s    Volume

NegativeVel:
  ;---Negative velocity offset
  move.l   d0,d1                ;duplicate velocity
  lsr.b    #3,d1                ;divide by 8
  add.b    d1,d1                ;x 2 because we need to fetch a word
  lea      VelTable+30,a1  ;start at table's END
  suba.l   d1,a1                ;go backwards
  move.w   (a1),d1              ;get the velocity offset
  adda.l   d1,a0          ;where to start actual playback

  ;---Convert Midi velocity to an Amiga volume
Volume  lsr.b    #1,d0          ;divide by 2
        addq.b   #1,d0          ;an equivalent Amiga volume

 ;---Now a0 and d0 are the address of sample start, and volume


     ================= AN EGpoint (envelope generator) ================

 A single EGpoint is a 6 byte structure as follows:

EGpoint1: dc.w ;[the duration in milliseconds]
          dc.l ;[the volume factor - fixed point, 16 bits to the left of the
               ;decimal point and 16 to the right.]

  The volume factor is a fixed point where 1.0 ($00010000) represents the
  MAXIMUM volume possible. (i.e. No volume factor should exceed this value.)
  The last EGpoint in the ATAK is always the sustain point. Each EG's volume
  is determined from 0.0, not as a difference from the previous EG's volume.
  I hope that this clears up the ambiguity in the original 8SVX document.
  So, to recreate an amplifier envelope like this:

    /\
   /  \____
  /        \
 /          \

 |  | |   |  |
  1  2  3   4

  Stages 1, 2, and 3 would be in the ATAK data, like so:

  ;Stage 1
  dc.w  100       ;take 100ms
  dc.l  $00004000 ;go to this volume
  dc.w  100
  dc.l  $00008000
  dc.w  100
  dc.l  $0000C000
  dc.w  100
  dc.l  $00010000 ;the "peak" of our attack is full volume
  ;Stage 2
  dc.w  100
  dc.l  $0000C000 ;back off to this level
  dc.l  100
  dc.l  $00008000 ;this is where we hold (SUSTAIN) until the note is turned
                  ;off. (We are now holding at stage 3)

  Now the RLSE data would specify stage 4 as follows:
  dc.w  100
  dc.l  $00004000
  dc.w  100
  dc.l  $00000000 ;the volume is 0


        ===============ADDITIONAL USER DATA SECTION=================

  There is a provision for storing user data for each wave. This is where an
application can store Amiga hardware info, or other, application specific info.
The waveHeader's USERtype tells what kind of data is stored. The current
types are:

#define SPECIFIC 0
#define VOLMOD   1
#define PERMOD   2
#define LOOPING  3

 SPECIFIC (0) - application specific data. It should be stored
                in a format that some application can immediately
                recognize. (i.e. a "format within" the SAMP format)
                If the USERtype is SPECIFIC, and an application
                doesn't find some sort of header that it can re-
                cognize, it should conclude that this data was
                put there by "someone else", and ignore the data.

 VOLMOD (1) -   This data is for volume modulation of an Amiga
                channel as described by the ADKCON register. This
                data will be sent to the modulator channel of the
                channel set to play the wave.

 PERMOD (2) -   This data is for period modulation of an Amiga
                channel as described by the ADKCON register. This
                data will be sent to the modulator channel of the
                channel set to play the wave.

 LOOPING (3) -  This contains more looping points for the sample.
                There are some samplers that allow more than just
                one loop (Casio products primarily). Additional
                looping info can be stored in this format:

               UWORD numOfLoops;  /* number of loop points to follow */

               ULONG StartLoop1,  /* BYTE offset from the beginning of
                                    the sample to the start of loop1 */
               EndLoop1,          /* BYTE offset from the beginning of
                                    the sample to the end of loop1 */

               StartLoop2,        /* ...etc */


          =========Converting Midi Sample Dump to SAMP=========

  SEMANTICS: When MIDI literature talks about a sample, usually it means a
collection of many sample points that make up what we call "a wave".
Therefore, a Midi Sample Dump sends all the sample data that makes up ONE
wave. A SAMP file is designed to hold up to 255 of these waves (midi dumps).

  The Midi Sample Dump specifies playback rate only in terms of a sample
PERIOD in nanoseconds. SAMP also expresses playback in terms of samples per
second (frequency). The Amiga needs to see its period rounded to the nearest
microsecond. If you take the sample period field of a Midi sample Dump (the
8th, 9th, and 10th bytes of the Dump Header LSB first) which we will call
MidiSamplePer, and the Rate of a SAMP file, here is the relationship:

    Rate = (1/MidiSamplePer) x 10E9

  Also the number of samples (wave's length) in a Midi Sample Dump (the 11th,
12th, and 13th bytes of the Dump header) is expressed in WORDS. SAMP's
WaveSize is expressed in the number of BYTES. (For the incredibly stupid),
the relationship is:

   WaveSize = MidiSampleLength x 2

  A Midi sample dump's LoopStart point and LoopEnd point are also in WORDS as
versus the SAMP equivalents expressed in BYTES.

   A Midi sample dump's sample number can be 0 to 65535. A SAMP file can hold
up to 255 waves, and their numbers in the playmap must be 1 to 255. (A single,
Midi Sample Dump only sends info on one wave.) When recieving a Midi Sample
Dump, just store the sample number (5th and 6th bytes of the Dump Header LSB
first) in SAMP's MidiSampNum field. Then forget about this number until you
need to send the wave back to the Midi instrument from whence it came.

  A Midi Dump's loop type can be forward, or forward/backward. Amiga hardware
supports forward only. You should store the Midi Dump's LoopType byte here,
but ignore it otherwise until/unless Amiga hardware supports "reading audio
data" in various ways. If so, then the looptype is as follows:

    forward = 0, backward/forward = 1

  A Midi Dump's sample format byte is the same as SAMP's.


  ===================== INTERPRETING THE PLAYMODE ==========================

  PlayMode specifies how the bytes in the PlayMap are to be interpreted.
  Remember that a PlayMap byte of 0 means "No Wave to Play".

#define INDEPENDANT 0
#define MULTI       1
#define STEREO      2
#define PAN         3

  PlayMode types:

 INDEPENDANT (0) - The wave #s for a midi note are to be output on
                   Amiga audio channels 0, 1, 2, and 3 respectively.
                   If the NumOfChans is < 4, then only use that many channels.

 MULTI       (1) - The first wave # (first of the PlayMap bytes) for a
                   midi note is to be output on any free channel. The other
                   wave numbers are ignored. If all four channels are in
                   play, the application can decide whether to "steal" a
                   channel.

 STEREO     (2) -  The first wave # (first of the PlayMap bytes) is to be
                   output of the Left stereo jack (channel 1 or 3) and if
                   there is a second wave number (the second of the PlayMap
                   bytes), it is to be output the Right jack (channel 2 or 4).
                   The other wave numbers are ignored.

 PAN        (3) -  This is just like STEREO except that the volume of wave 1
                   should start at its initial volume (midi velocity) and
                   fade to 0. At the same rate, wave 2 should start at 0
                   volume and rise to wave #1's initial level. The net
                   effect is that the waves "cross" from Left to Right in
                   the stereo field. This is most effective when the wave
                   numbers are the same. (ie the same wave) The application
                   program should set the rate. Also, the application can
                   reverse the stereo direction (ie Right to Left fade).

  The most important wave # to be played back by a midi note should be the
first of the PlayMap bytes. If the NumOfChans > 1, the second PlayMap byte
should be a defined wave number as well (even if it is deliberately set to the
same value as the first byte). This insures that all 4 PlayModes will have some
effect on a given SAMP file. Also, an application should allow the user to
change the PlayMode at will. The PlayMode stored in the SAMP file is only a
default or initial set-up condition.


  =================== MAKING A TRANSPOSE TABLE =====================

 In order to allow a wave to playback over a range of musical notes, (+/-
semitones), its playback rate must be raised or lowered by a set amount.
From one semitone to the next, this set amount is by a factor of the 12th
root of 2 (assuming a western, equal-tempered scale). Here is a table that
shows what factor would need to be multiplied by the sampling rate in order
to transpose the wave's pitch.

  Pitch in relation to the Root     Multiply Rate by this amount
 -------------------------------   ------------------------------
   DOWN 6     semitones              0.5
   DOWN 5 1/2 semitones              0.529731547
   DOWN 5     semitones              0.561231024
   DOWN 4 1/2 semitones              0.594603557
   DOWN 4     semitones              0.629960525
   DOWN 3 1/2 semitones              0.667419927
   DOWN 3     semitones              0.707106781
   DOWN 2 1/2 semitones              0.749153538
   DOWN 2     semitones              0.793700526
   DOWN 1 1/2 semitones              0.840896415
   DOWN 1     semitones              0.890898718
   DOWN 1/2   semitone               0.943874312
ORIGINAL_PITCH                       1.0           /* rootnote's pitch */
   UP   1/2   semitone               1.059463094
   UP   1     semitones              1.122562048
   UP   1 1/2 semitones              1.189207115
   UP   2     semitones              1.259921050
   UP   2 1/2 semitones              1.334839854
   UP   3     semitones              1.414213562
   UP   3 1/2 semitones              1.498307077
   UP   4     semitones              1.587401052
   UP   4 1/2 semitones              1.681792830
   UP   5     semitones              1.781797436
   UP   5 1/2 semitones              1.887748625
   UP   6     semitones              2

  For example, if the wave's Rate is 18000 hz, and you wish to play
the wave UP 1 semitone, then the playback rate is:

   18000 x 1.122562048 = 20206.11686 hz

  The sampling period for the Amiga is therefore:

     (1/20206.11686)/.279365 = .000177151

 and to send it to the Audio Device, it is rounded and expressed in micro-
seconds: 177

  Obviously, this involves floating point math which can be time consuming
and impractical for outputing sound in real-time. A better method is to con-
struct a transpose table that contains the actual periods already calculated
for every semitone. The drawback of this method is that you need a table for
EVERY DIFFERENT Rate in the SAMP file. If all the Rates in the
file happened to be the same, then only one table would be needed. Let's
assume that this is the case, and that the Rate = 18000 hz. Here is a
table containing enough entries to transpose the waves +/- 6 semitones.

  Pitch in relation to the Root     The Amiga Period (assuming rate = 18000 hz)
 -------------------------------   ------------------------------
Transposition_table[TRANS_TABLE_SIZE]={
/* DOWN 6     semitones  */            398,
/* DOWN 5 1/2 semitones  */            375,
/* DOWN 5     semitones  */            354,
/* DOWN 4 1/2 semitones  */            334,
/* DOWN 4     semitones  */            316,
/* DOWN 3 1/2 semitones  */            298,
/* DOWN 3     semitones  */            281,
/* DOWN 2 1/2 semitones  */            265,
/* DOWN 2     semitones  */            251,
/* DOWN 1 1/2 semitones  */            236,
/* DOWN 1     semitones  */            223,
/* DOWN 1/2   semitone   */            211,
/* ORIGINAL_PITCH        */            199,      /* rootnote's pitch */
/* UP   1/2   semitone   */            187,
/* UP   1     semitones  */            177,
/* UP   1 1/2 semitones  */            167,
/* UP   2     semitones  */            157,
/* UP   2 1/2 semitones  */            148,
/* UP   3     semitones  */            141,
/* UP   3 1/2 semitones  */            133,
 /* Since the minimum Amiga period = 127 the following
    are actually out of range. */
/* UP   4     semitones  */            125,
/* UP   4 1/2 semitones  */            118,
/* UP   5     semitones  */            112,
/* UP   5 1/2 semitones  */            105,
/* UP   6     semitones  */            99   };


  Let's assume that (according to the PlayMap) midi note #40 is set to play
wave number 3. Upon examining wave 3's structure, we discover that the
Rate = 18000, and the RootNote = 38. Here is how the Amiga sampling
period is calulated using the above 18000hz "transpose chart" in C:
  /* MidiNoteNumber is the received midi note's number (here 40) */

  #define ORIGINAL_PITCH     TRANS_TABLE_SIZE/2 + 1
/* TRANS_TABLE_SIZE is the number of entries in the transposition table
   (dynamic, ie this can change with the application) */

  transposeAmount = (LONG) (MidiNoteNumber - rootNote); /* make it a SIGNED LONG */
  amigaPeriod     = Transposition_table[ORIGINAL_PITCH + transposeAmount];


  In assembly, the 18000hz transpose chart and above example would be:

Table       dc.w  398
            dc.w  375
            dc.w  354
            dc.w  334
            dc.w  316
            dc.w  298
            dc.w  281
            dc.w  265
            dc.w  251
            dc.w  236
            dc.w  223
            dc.w  211
ORIGINAL_PITCH  dc.w  199   ; rootnote's pitch
            dc.w  187
            dc.w  177
            dc.w  167
            dc.w  157
            dc.w  148
            dc.w  141
            dc.w  133
 ; Since the minimum Amiga period = 127, the following
 ; are actually out of range.
            dc.w  125
            dc.w  118
            dc.w  112
            dc.w  105
            dc.w  99

  lea     ORIGINAL_PITCH,a0
  move.b  MidiNoteNumber,d0  ;the received note number
  sub.b   RootNote,d0        ;subtract the wave's root note
  ext.w   d0
  ext.l   d0                 ;make it a signed LONG
  add.l   d0,d0              ;x 2 in order to fetch a WORD
  adda.l  d0,a0
  move.w  (a0),d0            ;the Amiga Period (WORD)

  Note that these examples don't check to see if the transpose amount is
beyond the number of entries in the transpose table. Nor do they check if
the periods in the table are out of range of the Amiga hardware.


    ===================== MAKING THE VELOCITY TABLE ======================

  The 16 entries in the velocity table should be within the oneShot portion of
the sample (ie not in the looping portion). THe first offset, VelTable[0]
should be set to zero (in order to play back from the beginning of the data).
The subsequent values should be increasing numbers. If you are using a graphic
editor, try choosing offsets that will keep you within the initial attack
portion of the wave. In practice, these values will be relatively close
together within the wave. Always set the offsets so that when they are added
to the sample start point, the resulting address points to a sample value of
zero (a zero crossing point). This will eliminate pops and clicks at the
beginning of the playback.

  In addition, the start of the wave should be on a sample with a value of
zero. The last sample of the oneShot portion and the first sample of the
looping portion should be approximately equal, (or zero points). The same is
true of the first and last samples of the looping portion. Finally, try to
keep the slopes of the end of the oneShot, the beginning of the looping, and
the end of the looping section, approximately equal. All this will eliminate
noise on the audio output and provide "seamless" looping.


  ======================== THE INSTRUMENT TYPE ==========================

  Many SMUS players search for certain instruments by name. Not only is this
slow (comparing strings), but if the exact name can't be found, then it is
very difficult and time-consuming to search for a suitable replacement. For
this reason, many SMUS players resort to "default" instruments even if these
are nothing like the desired instruments. The InsType byte in each
waveHeader is meant to be a numeric code which will tell an SMUS player
exactly what the instrument is. In this way, the SMUS player can search for
the correct "type" of instrument if it can't find the desired name. The type
byte is divided into 2 nibbles (4 bits for you C programmers) with the low
4 bits representing the instrument "family" as follows:

 1 = STRING, 2 = WOODWIND, 3 = KEYBOARD, 4 = GUITAR, 5 = VOICE, 6 = DRUM1,
 7 = DRUM2,  8 = PERCUSSION1, 9 = BRASS1, A = BRASS2, B = CYMBAL, C = EFFECT1,
 D = EFFECT2, E = SYNTH, F is undefined at this time

 Now, the high nibble describes the particular type within that family.

 For the STRING family, the high nibble is as follows:

 1 = VIOLIN BOW, 2 = VIOLIN PLUCK, 3 = VIOLIN GLISSANDO, 4 = VIOLIN TREMULO,
 5 = VIOLA BOW, 6 = VIOLA PLUCK, 7 = VIOLA GLIS, 8 = VIOLA TREM, 9 = CELLO
 BOW, A = CELLO PLUCK, B = CELLO GLIS, C = CELLO TREM, D = BASS BOW, E =
 BASS PLUCK (jazz bass), F = BASS TREM

 For the BRASS1 family, the high nibble is as follows:

 1 = BARITONE SAX, 2 = BARI GROWL, 3 = TENOR SAX, 4 = TENOR GROWL, 5 = ALTO
 SAX, 6 = ALTO GROWL, 7 = SOPRANO SAX, 8 = SOPRANO GROWL, 9 = TRUMPET, A =
 MUTED TRUMPET, B = TRUMPET DROP, C = TROMBONE, D = TROMBONE SLIDE, E =
 TROMBONE MUTE

 For the BRASS2 family, the high nibble is as follows:

 1 = FRENCH HORN, 2 = TUBA, 3 = FLUGAL HORN, 4 = ENGLISH HORN

 For the WOODWIND family, the high nibble is as follows:

 1 = CLARINET, 2 = FLUTE, 3 = PAN FLUTE, 4 = OBOE, 5 = PICCOLO, 6 = RECORDER,
 7 = BASSOON, 8 = BASS CLARINET, 9 = HARMONICA

 For the KEYBOARD family, the high nibble is as follows:

 1 = GRAND PIANO, 2 = ELEC. PIANO, 3 = HONKYTONK PIANO, 4 = TOY PIANO, 5 =
 HARPSICHORD, 6 = CLAVINET, 7 = PIPE ORGAN, 8 = HAMMOND B-3, 9 = FARFISA
 ORGAN, A = HARP

 For the DRUM1 family, the high nibble is as follows:

 1 = KICK, 2 = SNARE, 3 = TOM, 4 = TIMBALES, 5 = CONGA HIT, 6 = CONGA SLAP,
 7 = BRUSH SNARE, 8 = ELEC SNARE, 9 = ELEC KICK, A = ELEC TOM, B = RIMSHOT,
 C = CROSS STICK, D = BONGO, E = STEEL DRUM, F = DOUBLE TOM

 For the DRUM2 family, the high nibble is as follows:

 1 = TIMPANI, 2 = TIMPANI ROLL, 3 = LOG DRUM

 For the PERCUSSION1 family, the high nibble is as follows:

 1 = BLOCK, 2 = COWBELL, 3 = TRIANGLE, 4 = TAMBOURINE, 5 = WHISTLE, 6 =
 MARACAS, 7 = BELL, 8 = VIBES, 9 = MARIMBA, A = XYLOPHONE, B = TUBULAR BELLS,
 C = GLOCKENSPEIL

 For the CYMBAL family, the high nibble is as follows:

 1 = CLOSED HIHAT, 2 = OPEN HIHAT, 3 = STEP HIHAT, 4 = RIDE, 5 = BELL CYMBAL,
 6 = CRASH, 7 = CHOKE CRASH, 8 = GONG, 9 = BELL TREE, A = CYMBAL ROLL

 For the GUITAR family, the high nibble is as follows:

 1 = ELECTRIC, 2 = MUTED ELECTRIC, 3 = DISTORTED, 4 = ACOUSTIC, 5 = 12-STRING,
 6 = NYLON STRING, 7 = POWER CHORD, 8 = HARMONICS, 9 = CHORD STRUM, A = BANJO,
 B = ELEC. BASS, C = SLAPPED BASS, D = POPPED BASS, E = SITAR, F = MANDOLIN
 (Note that an acoustic picked bass is found in the STRINGS - Bass Pluck)

 For the VOICE family, the high nibble is as follows:

 1 = MALE AHH, 2 = FEMALE AHH, 3 = MALE OOO, 4 = FEMALE OOO, 5 = FEMALE
 BREATHY, 6 = LAUGH, 7 = WHISTLE

 For the EFFECTS1 family, the high nibble is as follows:

 1 = EXPLOSION, 2 = GUNSHOT, 3 = CREAKING DOOR OPEN, 4 = DOOR SLAM, 5 = DOOR
 CLOSE, 6 = SPACEGUN, 7 = JET ENGINE, 8 = PROPELLER, 9 = HELOCOPTER, A =
 BROKEN GLASS, B = THUNDER, C = RAIN, D = BIRDS, E = JUNGLE NOISES, F =
 FOOTSTEP

 For the EFFECTS2 family, the high nibble is as follows:

 1 = MACHINE GUN, 2 = TELEPHONE, 3 = DOG BARK, 4 = DOG GROWL, 5 = BOAT
 WHISTLE, 6 = OCEAN, 7 = WIND, 8 = CROWD BOOS, 9 = APPLAUSE, A = ROARING
 CROWDS, B = SCREAM, C = SWORD CLASH, D = AVALANCE, E = BOUNCING BALL,
 F = BALL AGAINST BAT OR CLUB

 For the SYNTH family, the high nibble is as follows:

 1 = STRINGS, 2 = SQUARE, 3 = SAWTOOTH, 4 = TRIANGLE, 5 = SINE, 6 = NOISE

  So, for example if a wave's type byte was 0x26, this would be a SNARE DRUM.
If a wave's type byte is 0, then this means "UNKNOWN" instrument.


  ===================== THE ORDER OF THE CHUNKS =========================

 The SAMP header obviously must be first in the file, followed by the MHDR
chunk. After this, the ANNO, (c), AUTH and NAME chunks may follow in any
order, though none of these need appear in the file at all. The BODY chunk
must be last.


        ================= FILENAME CONVENTIONS =================

   For when it becomes necessary to split a SAMP file between floppies using
the Continuation feature, the filenames should be related. The method is the
following:

   The "root" file has the name that the user chose to save under. Subsequent
files have an ascii number appended to the name to indicate what sublevel the
file is in. In this way, a program can reload the files in the proper order.

   For example, if a user saved a file called "Gurgle", the first continuation
file should be named "Gurgle1", etc.


  ============ WHY DOES ANYONE NEED SUCH A COMPLICATED FILE? ==============
                 (or "What's wrong with 8SVX anyway?")

  In a nutshell, 8SVX is not adequate for professional music sampling. First
of all, it is nearly impossible to use multi-sampling (utilizing several,
different samples of any instrument throughout its musical range). This very
reason alone makes it impossible to realistically reproduce a musical in-
strument, as none in existance (aside from an electronic organ) uses inter-
polations of a single wave to create its musical note range.
  Also, stretching a sample out over an entire octave range does grotesque
(and VERY unmusical) things to such elements as the overtone structure,
wind/percussive noises, the instrument's amplitude envelope, etc. The 8SVX
format is designed to stretch the playback in exactly this manner.
  8SVX ignores MIDI which is the de facto standard of musical data transmission.
  8SVX does not allow storing data for features that are commonplace to pro-
fessional music samplers. Such features are: velocity sample start, separate
filter and envelopes for each sample, separate sampling rates, and various
playback modes like stereo sampling and panning.
  SAMP attempts to remedy all of these problems with a format that can be
used by a program that simulates these professional features in software. The
format was inspired by the capabilities of the following musical products:

  EMU's                 EMAX, EMULATOR
  SEQUENTIAL CIRCUIT's  PROPHET 2000, STUDIO 440
  ENSONIQ's             MIRAGE
  CASIO's               FZ-1
  OBERHEIM's            DPX
  YAMAHA                TX series

   So why does the Amiga need the SAMP format? Because professional musician's
are buying computers. With the firm establishment of MIDI, musician's are
buying and using a variety of sequencers, patch editors, and scoring programs.
It is now common knowledge amoung professional musicians that the Amiga
lags far behind IBM clones, Macintosh, and Atari ST computers in both music
software and hardware support. It is important for music software to exploit whatever capabili-
ties the Amiga offers before the paint and animation programs, genlocks,
frame-grabbers, and video breadboxes are the only applications selling
for the Amiga. Hopefully, this format, with the SAMP disk I/O library will
make it possible for Amiga software to attain the level of professionalism
that the other machines now boast, and the Amiga lacks.