Description of FFF file format
-------------------------------
Version 2.0, (C)1996-7 by ChAr, from Sources of Kernel 1.02
(English revision translated from Czech original, sorry for terrible english)

Any comments, suggestions, questons to 
  ardour@usa.net  or  cibulka@server-y.sh.cvut.cz


0) Definition of used data types
For description is used TurboPascal syntax with following notes:
  a) All items of structures are alligned to byte (byte-packed)
  b) SizeOf all types (in bytes):
      char, byte 	 = 1
      shortint   	 = 1 
      smallint, word	 = 2
      longint	         = 4
		
1) Base structure of FFF file
FFF file is in binary form, for number encoding is used Intel-formating.
It contains "chunks", i.e. blocks for description of Patch, Wave, Layer, Text...
Every chunk is identified by 4-byte length char array, followed by
4 byte length of this chunk without this header (-8 byte => chunk data len),
header names are case sensitive ('FFFF' <> 'ffff')

a) Known types of chunks:
  - FFFF: header of FFFF chunk, contains definition of patches,envelopes...
  - PTCH: header of Patch chunk, header for ID of program, version of PROG section
  - PROG: header of Program chunk, definition of 1 midi instrument 
      (constructed from layers and waves)
  - LAYR: header of Layer chunk, definition of 1 layer (contains waves)
  - WAVE: header of Wave chunk, definition of 1 wave (by 'DATA' it references
      .DAT file with wave data)
  - ENVP: header of Envelope chunk, referenced by ID from Layers
  - TEXT: header of Text chunk, no further information on this chunk (not used)
  - DATA: header of Data chunk, used for storing filename of .DAT (from wave)
  - CPRT: header of Copyright chunk, used for storing (C)

 
b) structure on FFF level is following:

1..n: FFFF_Header: THeader; { THeader.tag='FFFF'; }
        0..n: ????_Header: THeader; { not THeader.tag in ['PROG','DATA','ENVP'] }
        FFFF_Data; { !sizeof(FFFF_Data) < 65535 } {!!! in drivers !!!}
  
- one FFF file can contain 1-n FFFF chunks, in which can be some patch data.
- all chunks before 1st 'PROG','DATA','ENVP' are ignored (in parsing & loading).
- data length from 1st 'PROG','DATA','ENVP' are loaded from file into memory
  as one block (simplified drivers, Driver Kernel written for 16bit segments) =>
  maximum length of block 64KB.

c) structure of FFFF_Data block (not single chunk, but group of chunks):

FFFF_Data:
0..n: { ENVP, DATA or PROG chunks (no particular order needed), there can be also other type of chunk }
      { which have in start of header TID record for numerical ID }	
      DATA_Chunk: TDataChunk; { iw_data - used for refering .DAT files }	
      ENVP_Chunk: TEnvelopeChunk; { struct iw_envp }
      PROG_Chunk: TProgramChunk; { .tag='PROG', .version>=MINIMAL_VERSION }
        PTCH_Chunk: TPatchChunk; { struct iw_patch }
	  PTCH.nlayers: LAYR_Chunk: TLayerChunk; { struct iw_layer } 
	              { .penv a .venv refers on 'ENVP' chunks }
             LAYR.waves: WAVE_Chunk: TWaveChunk; { struct iw_wave }
		      { .data_id references 'DATA' chunk with filename of .DAT with wave data }

- every chunk <> 'PROG' must start with record TID header,
  kernel stores table of ID numbers, which are later conected to real
  pointers into memory (after loading and parsing) at coresponding adresses of chunks
- in this data block, there are on top level recognized only PROG chunks, which 
  are later processed, rest (ENVP chunks by default) of chunks are linked with
  their ID number for later linking (see above)
- ENVP_Chunk: contains TEnvpHeader followed by array of .num_envelopes*TEnvpRecord. 
  Directly after every TEnvpRecord (inside them) is array (.nattack+.nrelease) [word,word] 
  containing offsets and amplitudes of envelope points.
- PROG_Chunk: contain definition of program, when loading it is controlled 
  version of chunk (.version >= IW_PATCH_VERION else ignored). 
  After this must follow PTCH_Chunk with patch definition.
- PTCH_Chunk: definition of patch (1 MIDI instrument), identified by .bank (GS bank number) and
  .program (MIDI program: melodic 0-127, drums 128-255). 
  After PTCH_Chunk must follow .nalyers x LAYR_Chunks with layer defs.
- LAYR_Chunk: definition of layer (part of midi instrument, played on note_on/note_off event or
  defined note pitch/velocity). Contains links on volume envelope (.venv) and 
  pitch envelope (.penv) (ENVP chunks). 
  After LAYR_Chunk must follow .nwaves x WAVE_Chunks with wave defs. for this layer.
- WAVE_Chunk: definition of wave (part of layer played in particular freq. range <lowNote,highNote>.
  Contains base definitions refering binary wave data, eg.
  length, loops, wave format. Field .data_id contains an ID of 'DATA' chunk, 
  with filename of .DAT with wave data. Maximum length of name is
  80 characters (by internal architecture of kernel) including trailing #0. 
  Offset of physical start of wave in .DAT file is stored in field .start.


2) Structure for chunk header
THeader = record 
    tag: array[0..3] of char;
    length: longint;	 { Length of following datas in bytes }
  end;

3) Structure for ENVP Chunk
TEnvelopeHeader = record
    header: THeader; { .tag = 'ENVP' }
    id: TID; { id number of ENVP }
    num_envelopes: byte; { number of envelopes in ENVP (>0) }
    flags: byte;	 { retrigger & retrigger_layer }
			 { bit0 = retrigger flag, retrigger of layer (must be enabled in layer flags) }
    mode: byte;	         { envelope type: 1 shot, sustained, non sustained }
			 { 1 = one shot, envelope runs one without sustain, note_off has no effect on playing }
			 {     -> must be used for KEY_UP layers }
			 { 2 = sustain, envelope has sustain point }
			 { 3 = no sustain, envelope hasn't sustain point, but on note_off jumps to release point}
    index_type: byte;    { envelope index by: none, velocity, frequency }
			 { 0 = none index, every time will be used 1st envelope record }
			 { 1 = velocity, envelope used by note velocity }						
			 { 2 = freq, ... used by index of midi note (freq) }
    envp_records: array[1..num_envelopes] of TEnvelopeRecord; { envelopes here }
  end;    

TEnvelopeRecord = record
    nattack: smallint; 	 { count of attack points in envelope (note_on) (>=0) }
    nrelease: smallint;	 { count of release points in envelope (note_off) (>=0) }
    sustain_offset: word; { offset in sustain point (pitch offset/volume) }
    sustain_rate: word;  { length of sustain - look at IW.Rate registers }
    release_rate: word;  { length of start on release }
    hirange: byte; 	 { max. size of index. variable of this ENVP (vel/freq) }
			 { if searched index<hirange then use this envelope else try next envelope }
			 { -> envelopes must be SORTED by hirange ascendently! }
    pad: byte;		 { not used }
    envp_points_attack: array[1..nattack] of TEnvelopePoint; { points for attack section }
    envp_points_release: array[1..nrelease] of TEnvelopePoint; { points for release section }
  end;

TEnvelopePoint = record
    case integer of
      0: (next, rate: word);  { volume: next(volume amplitude) rate(length volume) }
      1: (next, time: word);  { pitch: next(freq change) time(length of change) }
    end;
  end; 


4) Structure of PROG chunk
TProgramChunk = record
    header: THeader; { .id = 'PROG' }
    id: TID; { id of PROG }
    version: TID; { version of PROG chunk (1=major, 0=minor in current version of midi-engine }
  end;


5) Structure of PTCH chunk
TPatchChunk = record
    header: THeader; { .id = 'PTCH' }
    id: TID; { id of PTCH }
    nlayers: smallint; 		{ count of layers in patch (>0) }
    layer_mode: byte;		{ activation of layer, look at note_on }
				{ 0 - none layering (layers off ?) }
				{ 1 - layered }
				{ 2 - layered by note velocity }
				{ 3 - layered by note freq }
    exclusion_mode: byte;	{ exclusion mode (only for 1 chanel): _NONE, _SINGLE, _MULTIPLE }
				{ 0 - none }
				{ 1 - single, all notes of this group are stopped }
				{ 2 - multiple, only the same notes from this group are stopped }
    exclusion_group: smallint;	{ exclusion group number }
    effect1: byte;		{ effect type 1 }
				{ 0 - none }
				{ 1 - reverb }
				{ 2 - chorus }
				{ 3 - echo (not implemented) }
    effect1_depth: byte;	{ effect volume 0 - 127 }
    effect2: byte;		{ effect type 2, look at effect1, if same as 1st, override }
    effect2_depth: byte;	{ effect volume 0 - 127 }
    bank: byte;			{ GS bank number of patch }
    program: byte;		{ program number melodic 0-127, drums 128-255 (only there, all other note numbers are 0-127) }
    iw_layer: PLayerChunk;      { on disk not used, in RAM pointer of 1st layer }
  end;


6) Structure of LAYR chunk
TLayerChunk = record
    header: THeader; { .id = 'LAYR' }
    id: TID; { id of LAYR }
    nwaves: byte;		{ number of waves in layer (>0) }
    flags: byte;		{ rettriger mode flags: retrigger }
				{ bit0 - enable retrigger of layer (if used in envelopes) }
    high_range: byte;		{ range of layer for which is used }
    low_range: byte;		{ veloc. or freq., look at layer_mode in parent's patch chunk }
				{ layer used for low_range<= velocity/midi_note <= high_range }
				{ if not true, no layers are playing }
    pan: byte;			{ pan offset from CC1 (0 left - 64 center - 127 right) }
    pan_freq_scale: byte;	{ position = f(freq) (0 - 127) }
				{ note_pan(-64..63)=((MIDI_note-64)*pan_freq_scale)>>7 + pan-64 }
				{ voice_pan = note_pan + MIDI_chanel_panning }
    tremolo: TLFO;		{ tremolo LFO }
    vibrato: TLFO;		{ vibrato LFO }
    velocity_mode: byte;	{ velocity offsets = f(time/freq): IW_LAYER_TIME, IW_LAYER_RATE }
				{ 0 - time, velocity controls length (?) }
				{ 1 - rate, velocity controls volume, default }
    attenuation: byte;		{ attenuation of layeru 0 - 127 }
    freq_scale: smallint;       { short! freq scaling 0 - 2048, 1024 = default, half-cent scale}
				{ played_note=(note-freq_center)*freq_scale/1024 + freq_center }
    freq_center: byte;		{ middle for freq scaling (midi note) }
    layer_event: byte;		{ layer is used for event: }
				{ 0 - layer key up }
				{ 1 - layer key down }
				{ 2 - layer key retrigger }
				{ 3 - layer key legato }
    penv: TID;	    		{ ID number/pointer on pitch ENVP (>=0) }
    venv: TID;	    		{ ID number/pointer on volume ENVP (MUST EXIST, eg. >0) }
    waves: PWaveChunk;  	{ on disk not used }
  end;


7) Structure of WAVE chunk
TWaveChunk = record
    header: THeader; { .id = 'WAVE' }
    id: TID;			{ id of Wave }
    size: longint;		{ length of wave in samples (16bit:1sample=2bytes), max. length 4MB (HW - InterWave)! }
    start: longint;		{ offset of wave in .DAT file in bytes }
    loopstart: longint;		{ start of loop in samples (ofs from start, (adress21bits+8bits fraction)}
    loopend: longint;		{ end of loop in samples }
				{ For loops are used 32bit adress, where bits are: }
				{ bit00-bit03 - unused fraction part (IW don't use) }
				{ bit04-bit07 - fraction part of adress }
				{ bit30-bit08 - adress in IW RAM (offset) }
				{ bit31       - reserved }
    m_start: longint;		{ offset of sample in IW-RAM, not used on disk }
    sample_ratio: longint;	{ sample ratio (44100 * 1024 / rate) <- IW FreqRate }
    attenuation: byte;		{ wave attenuation 0 - 127 }
    low_note: byte;		{ low border of range for using this wave - MIDI note }
    high_note: byte;		{ high border if range for using this wave - MIDI note }
				{ playable: low_note<= midi_note(scaled from layer) <= high_note }
    format: byte;		{ wave format: 8/16/Bidi/Uni... }
				{ bit0 - 8bit wave }
				{ bit1 - signed wave }
				{ bit2 - forward playing }
				{ bit3 - loop enabled }
				{ bit4 - bidirectional loop }
				{ bit5 - uLaw compressed wave }
				{ bit6 - ROM wave (don't use on disk) }
    m_format: byte;		{ disk don't use }
    data_id: TID;		{ id of 'DATA', later ptr on 'DATA' structure }	
				{ containing filename of .DAT (max.80 characters) }
  end;


8) Structure of TID union
TID = record
    case integer of
      0: (record major_id, minor_id: word; end);
      1: (p: pointer); { 32bits! }
    end;
  end;
	

9) Structure of TLFO record
TLFO = record { all is LFO registers of IW chip, but sweep not }
    freq: word;			{ freq LFO (1 - 2047) ($01)0.01Hz - ($7FF)21.5Hz }
				{ LFO_FREQ = 44100/(64*65536)*freq [Hz] }
    depth: smallint;		{ depth LFO(+/-): volume +-(0 - 255) 0.48675dB/step } 
    				{ pitch +-(0 - 255(215)) #cents }
    sweep: smallint;		{ sweep of depth: 0 - 950 deciseconds }
				{ <>0 => ramping of depth: depth_inc=(final-curent)*512/(sweep*4410)
    shape: byte;		{ type of LFO: LFO_TRIANGLE, LFO_POSTRIANGLE }
				{ 0 - LFO triangle (-/\/-) }
				{ 1 - LFO post triangle (_/\_) }
    delay: byte;		{ not used(?): delaying of start LFO(?): 0 - 255 deciseconds }
  end;


10) Structure of DATA Chunk
TDataChunk = record { type of DATA chunk, which is reffered by WAVE chunks }
    header: THeader; { .id = 'DATA' }
    id: TID; { id which is used for linking to waves }
    filename: array[0..79] of char; { ZeroEnded $ Name of .DAT file, !length }
  end;	



11) Structure of .DAT file
- no restrictions on format of file.
- .DAT files are reffered from special DATA sections (1-n) in FFF file, 
  these DATA sections are reffered by their IDs from WAVE chunks.
- data of waves are stored in same format in which will be later loaded by midi-engine
  into soundcard's RAM (no conversions used), for better access they can be
  alligned to 4byte boundaries.
- start of wave is in TWaveChunk.start in bytes from start of .DAT file, length
  is computed from TWaveChunk.size && TWaveStart.format (8/16bits)
- GIPC uses "pseudo header" of .DAT file, it reserves 32 bytes on begining 
  of file for text information, midi-engine don't use it

