From zappe@sietec.sietec.de Wed Apr  6 13:51:35 1994
Received: from geech.gnu.ai.mit.edu by albert.gnu.ai.mit.edu (5.65/4.0) with SMTP
	id <AA11165@albert.gnu.ai.mit.edu>; Wed, 6 Apr 94 13:51:29 -0400
Received: from [141.73.252.1] by geech.gnu.ai.mit.edu (5.65/4.0) with SMTP
	id <AA04158@geech.gnu.ai.mit.edu>; Wed, 6 Apr 94 13:51:19 -0400
Received: from gaea.sietec.de by sietec.sietec.de with SMTP id AA19492
  (5.67b/IDA-1.5 for <jamal@gnu.ai.mit.edu>); Wed, 6 Apr 1994 19:55:09 +0200
From: Harald Zappe <zappe@gaea.sietec.de>
Date: Wed, 6 Apr 94 19:48:56 +0200
Message-Id: <9404061748.AA01255@gaea.sietec.de>
To: jamal@gnu.ai.mit.edu
Subject: oktafmt.txt (? final)
Status: OR


Thanks to all of those mentioned below for the additional infos
and sources. It looks pretty complete now.

The effects seem to be complete. All, VT, Multiplayer and the
Amiga Oktalyzer 1.1 Player use the same values. But I didn't see
the effect 12 anywhere (Arp 5), as mentioned by Peter Kunath.

-----------------------------------------------------------------

[C.3.3]     Oktalyzer
            ---------

Thanks to Frank Seide (seide@pfa.philips.de) for the first hints,
Bryan Ford (baford@schirf.cs.utah.edu) for most of the detailed comments
below, the effects, and the (GPL) free source code of his Multiplayer,
the Vangelis Team, which is Juan Carlos Arevalo (jarevalo@moises.ls.fi.upm.es),
Felix Sanz, and Luis Crespo for the Freeware sources of the Vangelis Tracker,
Armin Sander for the (?)first Oktalyzer Player on an Amiga,
Peter Kunath (kunath@informatik.tu-muenchen.de) for several hints,
and Jamal Hannah (jamal@gnu.ai.mit.edu) for coordinating us all.

There are two different "Oktalyzer" formats. The following description
only refers to the IFF-like style. The other one (a memory dump model)
seems to have no popularity.

(All numbers below are given in hex unless specified as 't'ecimal.)

MSB first

offset |bytes| contents   | meaning
-------+-----+------------+-------------------------------------------------
000000 |   8 | "OKTASONG" | char Magic[8]
       |     |            |   /* If you support different music file types
       |     |            |      check these letters. */
-------+-----+------------+-------------------------------------------------
       |     |            | Channel_Modes {
000008 |   4 | "CMOD"     |   char chunk_name[4]
00000C |   4 | 8          |   long chunk_len
000010 |   8 |            |   short chan_flags[4]
       |     |            |   /* 0: normal (Amiga) sound channel         */
       |     |            |   /* 1: 'tied' or 'splitted' channel: two
       |     |            |         sounds are played through this channel
       |     |            |         at the same time (mixed at run time) */
       |     |            |   /* eg. 0 1 0 1 => 6 channel: 1: normal,
       |     |            |      2/3: tied, 4: normal, 5/6: tied         */
       |     |            | }
-------+-----+------------+-------------------------------------------------
       |     |            | Sample_directory {
000018 |   4 | "SAMP"     |   char chunk_name[4]
00001C |   4 | (00000480) |   long Sample_dir_len /*==chunk_len*/
       |     |            |        /* Nr_of_samples = Sample_dir_len / 32 */
       |     |            |
000020 |  20t|            |   char Sample_Name[20]             \
000034 |   4 |            |     unsigned long  Sample_Len       )
000038 |   2 |            |     unsigned short Repeat_Start    (   up to 36x
00003A |   2 |            |     unsigned short Repeat_Len       > (or more?)
00003C |   1 | (00)       |     char           pad1            (
00003D |   1 | (40)       |     unsigned char  Volume           )
00003E |   2 | (0001)     |     short          pad2            /
...    |     |            | /*
       |     |            | If 'Repeat_Len' is zero, it is a simple one-shot
       |     |            | sample: ignore 'Repeat_Start', just play the whole
       |     |            | 'Sample_Len' bytes and stop the sound.
       |     |            | If 'Repeat_Len' is nonzero, it is a repeating sample
       |     |            | consisting of three parts: attack, sustain, and re-
       |     |            | lease. (Most other tracker formats don't support re-
       |     |            | lease.) The attack part starts at 0 and ends at
       |     |            | Repeat_Start-1, the sustain part starts at 'Repeat_
       |     |            | Start' and ends at Repeat_Start+Repeat_Len-1, and
       |     |            | the release part starts at Repeat_Start+Repeat_Len
       |     |            | and ends at Sample_Length-1.
       |     |            | The attack part should be played once, followed by
       |     |            | the repeat part an arbitrary number of times until
       |     |            | another note is played or a "release" command is
       |     |            | seen. If the "release" command is seen, then switch
       |     |            | to the release part of the sample when the current
       |     |            | repeat run is finished, and only play it once, fol-
       |     |            | lowed by silence.
       |     |            | 'Volume' is the default volume for notes played with
       |     |            | this sample: 0 to 64 (0x40) inclusive. */
       |     |            | }
-------+-----+------------+-------------------------------------------------
       |     |            | Speed {
0004A0 |   4 | "SPEE"     |   char  chunk_name[4]
0004A4 |   4 | 2          |   long  chunk_len
0004A8 |   2 | (3)        |   short AmigaVBLDivisor /* InitialTempo */
       |     |            | }
-------+-----+------------+-------------------------------------------------
       |     |            | Song_Length {
0004AA |   4 | "SLEN"     |   char  chunk_name[4]
0004B2 |   4 | 2          |   long  chunk_len
0004B6 |   2 | (60t)      |   short value
       |     |            |         /* it specifies the number of different
       |     |            |            patterns this module has.
       |     |            |            (can be used as counter for the "PBOD"
       |     |            |            chunks) */
       |     |            | }
-------+-----+------------+-------------------------------------------------
       |     |            | Num_Pattern_Positions { /* "PatternLength" */
0004B8 |   4 | "PLEN"     |   char  chunk_name[4]
0004BC |   4 | 2          |   long  chunk_len
0004BE |   2 | (15t)      |   short num_positions
       |     |            |         /* it specifies the number of entries in
       |     |            |            the pattern table (see "PATT" below) */
       |     |            | }
-------+-----+------------+- - - - - - - - - - - - - - - - - - - - - - - - -
       |     |            | Pattern_Positions {
0004C0 |   4 | "PATT"     |   char  chunk_name[4]
0004C4 |   4 | (128t ?)   |   long  chunk_len
       |     |            |         /* (it seems that the length of this chunk
       |     |            |             is always set to 128) */
0004C8 | 128t|            |   byte  position[*]
       |     |            |         /* zero *is* a valid value in this field.
       |     |            |            it means that pattern number 0 should
       |     |            |            be played. the number of valid positions
       |     |            |            is specified by the "PLEN" chunk. */
       |     |            | }
=======+=====+============+=================================================
       |     |            | Pattern1 {
000548 |   4 | "PBOD"     |   char  chunk_name[4]            )
00054C |   4 | (0702      |   long  chunk_len               (  up to 64
       |     |  or 0602)  |                                  > patterns are
00054E |   2 | (64t)      |   short num_pattern_lines       (  supported
000550 |  ...|            |   byte  Pattern1_Line[*]         )
       |     |            |         /* see below */
       |     |            | }
...    |     |            |
=======+=====+============+=================================================
       |     |            | Sample1 {
0..... |   4 | "SBOD"     |   char  chunk_name[4]            ) up to 255* is
0..... |   4 |            |   long  chunk_len               (_ possible but
0..... |  ...|            |   byte  sample_data[*]          (  mostly limited
...    |     |            |         /* 8 bit signed data */  ) to 36*
       |     |            | }
...    |     |            |
=======+=====+============+=================================================
Values in parentheses are examples and may vary.
(If you choose the faster methode to check the chunk types using a 'long'-
value, don't forget to exchange the byte order on LSB-systems.)

There are 36 effects, instruments and notes. In the original Oktalyzer
editor they are entered using the 10 digits and the 26 letters, that's
why 36.
____

A pattern line (PBOD chunks) looks like follows:

After the 16-bit num_pattern_lines are that many lines of pattern data,
each line containing four bytes for each active channel.
For example, in a 6-channel module, each line is 24 bytes.

The four bytes of one channel are:

    unsigned char newnote,
    unsigned char instrument, /* sample */
    unsigned char effect,
    unsigned char data        /* effect parameter */

If newnote is nonzero, start playing a different note. There are 36 pitches,
1-36 (see pertab below).

Set the current channel's volume to the sample's volume.
'instrument' indicates which sample to use.

Whether or not newnote is nonzero, process 'effect' and 'data' (see effects
below).
___

Oktalyzer uses the following period table, which is the same as for ST/NT/PT-
Mod-Files. (converted to C actually, but the same numbers)

static short pertab[] =
{
/*  C    C#     D    D#      E     F    F#     G    G#      A    A#     B */
 0x358,0x328,0x2FA,0x2D0, 0x2A6,0x280,0x25C,0x23A, 0x21A,0x1FC,0x1E0,0x1C5,
 0x1AC,0x194,0x17D,0x168, 0x153,0x140,0x12E,0x11D, 0x10D, 0xFE, 0xF0, 0xE2,
  0xD6, 0xCA, 0xBE, 0xB4,  0xAA, 0xA0, 0x97, 0x8F,  0x87, 0x7F, 0x78, 0x71
};

The extended octaves 0 and 4 which might be found in other formats are not
used here.
____

The Oktalyzer format defines the following effects (decimal):

 1  Portamento down: decrease period of current sample by 'data',
                     once every 50Hz clock tick.
 2  Portamento up:   increase period of current sample by 'data',
                     once every 50Hz clock tick.

10  Arpeggio 3: Change note every 50Hz tick between L,N,H
11  Arpeggio 4: Change note every 50Hz tick between N,H,N,L
12  Arpeggio 5: Change note every 50Hz tick between H,H,N
                N = normal note being played in this channel (1-36)
                L = normal note number minus upper four bits of 'data'.
                H = normal note number plus  lower four bits of 'data'.

13  Decrease note number by 'data' once per tick.
17  Increase note number by 'data' once per tick.
21  Decrease note number by 'data' once per line.
30  Increase note number by 'data' once per line.

15  Amiga low-pass filter control: 'data' indicates the new setting.

25  Position jump: Instead of going to the next line after this one,
          instead jump to the beginning of pattern number 'data'.

27  Release: start playing the release phase of the currently playing
             sample.

28  Set speed (number of 50Hz ticks between advancing lines) to 'data'.

31  Volume control:
      If 'data' <= 0x40, set the volume of this channel to 'data'.
      If 0x41 <= 'data' <= 0x50, decrease volume by 'data' - 0x40
                                 every 50Hz clock tick (fast fade out).
      If 0x51 <= 'data' <= 0x60, increase volume by 'data' - 0x50
                                 every 50Hz clock tick (fast fade in).
      If 0x61 <= 'data' <= 0x70, decrease volume by 'data' - 0x60
                                 at the beginning of every line (slow fade out).
      If 0x71 <= 'data' <= 0x80, increase volume by 'data' - 0x70
                                 at the beginning of every line (slow fade in).

--
There seems to be much room for future extentions, eg. panning.

... now it's complete? (HZ)

-----------------------------------------------------------------

Harald

--
>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<
 zappe@gaea.sietec.de              |  |
                                   |--+-              everything is relative
 Harald Zappe                      |  |/              nothing is for infinity
 work: +49-30-386-28328/29            /               quantity is not quality
 home: +49-30-ASK-ME                 /___              


