FF9/Sound/AKAO sequence

From Final Fantasy Inside
< FF9‎ | Sound
Revision as of 07:03, 11 July 2020 by Loveemu (talk | contribs) (Created page with "== Introduction == AKAO sequence is similar to MIDI sequence - it's custom tracker format for playing sequence sound, well tuned specially for PSX. == File Structure == ===...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

AKAO sequence is similar to MIDI sequence - it's custom tracker format for playing sequence sound, well tuned specially for PSX.


File Structure

Header (size: 64 bytes)

struct AkaoSeqHeader
{
  static const uint8_t magic[4];  // "AKAO" C-string
  uint16_t id;                    // song ID, used for playing sequence
  uint16_t length;                // data length (including this header)
  uint16_t reverb_type;           // reverb type (range from 0 to 9)
  uint8_t field_0A[6];
  uint32_t field_10;
  uint16_t sample_set_id;         // associated sample set ID
  uint16_t field_12;
  uint32_t field_14;
  uint32_t field_18;
  uint32_t field_1C;
  uint32_t mask;                  // represents bitmask of used channels in this song
  uint32_t field_24;
  uint32_t field_28;
  uint32_t field_2C;
  uint32_t instrument_map_offset; // relative offset to custom instrument map (0 if unused)
  uint32_t drum_map_offset;       // relative offset to custom drum map (0 if unused)
  uint32_t field_38;
  uint32_t field_3C;
};

Channel Offsets (size: 2 bytes * <channels count>)

struct AkaoChannelInfo
{
  uint32_t start_offsets[num_channels];  // offsets to channel opcode data
};
AkaoSeqHeader *header;
int num_channels = 0;
while (int bit = 0; header->mask & (1 << bit)) != 0; bit++)
  num_channels++;

There is <channels count> offsets to channel opcode data counting from current offset. Each offsets is a relative offset based on the address the offset itself.


Channel Commands [AKAO Opcodes]

For every channels in an AKAO sequence, there is a set of commands to perform. This is similar to Field opcodes. Here I will call this sound commands "opcodes". Every opcode has its own number of arguments (from no-arguments, to 3 arguments).

Drum Instrument Map Table

When a song uses a drum kit with opcode 0xFE 0x04, a drum instrument map table will be placed at the end of the sequence. The table determines the instrument, channel volume and pan for each keys.

The table consists of a repetition of 8-byte items.

struct AkaoDrumKeyAttr
{
  uint8_t instrument;   // corresponding to opcode 0xA1
  uint8_t key;          // note number when playing the drum note
  uint8_t ar;           // ADSR: attack rate (0-127)
  uint8_t sr;           // ADSR: sustain rate (0-127)
  uint8_t s_mode;       // ADSR: sustain mode (1: linear increase, 3: linear decrease, 5: exponential increase, 7: exponential decrease)
  uint8_t rr;           // ADSR: release rate (0-127)
  uint8_t volume;       // adjust the percussion volume to n/128 of the original volume (0 will keep the original volume)
  uint8_t pan : 7;      // corresponding to opcode 0xAA
  uint8_t reverb : 1;   // reverb on/off (0: off, 1: on)
}


Sound Opcode List

Opcode Summary Length Operands Note
0x00-0x99 Note, Tie, Rest 1 The opcode indicates the note key and length. The note will be keyed off 2 ticks before the next note.
0x9A-0x9F Unimplemented 1 Should not be used.
0xA0 Finish Channel 1
0xA1 Load Instrument 2 instrument: byte (0-127)
0xA2 Overwrite Next Note Length 2 length: byte Ignores the regular length (delta-time) of the next note and overwrites it with the specified length.
0xA3 Channel Master Volume 2 volume: byte (0-127)
0xA4 Pitch Bend Slide 3 length: byte, semitones: signed byte When length is 0, it will be translated to 256 ticks.
0xA5 Set Octave 2 octave: byte (0-15)
0xA6 Increase Octave 1
0xA7 Decrease Octave 1
0xA8 Channel Volume 2 volume: byte (0-127)
0xA9 Channel Volume Slide 3 length: byte, volume: byte (0-127) When length is 0, it will be translated to 256 ticks.
0xAA Channel Pan 2 pan: byte (0-127) 64 is the center.
0xAB Channel Pan Slide 3 length: byte, pan: byte (0-127) When length is 0, it will be translated to 256 ticks.
0xAC Noise Clock Frequency 2 clock: byte (0x00-0x3f)
0xAD ADSR: Attack Rate 2 attack_rate: byte (0x00-0x7f)
0xAE ADSR: Decay Rate 2 decay_rate: byte (0x00-0x0f)
0xAF ADSR: Sustain Level 2 sustain_level: byte (0x00-0x0f)
0xB0 ADSR: Decay Rate & Sustain Level 3 decay_rate: byte (0x00-0x0f), sustain_level: byte (0x00-0x0f)
0xB1 ADSR: Sustain Rate 2 sustain_rate: byte (0x00-0x7f)
0xB2 ADSR: Release Rate 2 release_rate: byte (0x00-0x1f)
0xB3 ADSR: Reset ADSR 1
0xB4 Channel Pitch LFO (Vibrato) 4 delay: byte, rate: byte, type: byte (0-15) When rate is 0, it will be translated to 256 ticks.
0xB5 Channel Pitch LFO Depth 2 depth: byte The most significant bit of the depth determines the amplitude range.

When it is 0, the range is up to about ± 50 cents (amplitude is 15/256 compared with range type 1).

When it is 1, the range is up to about ± 700 cents.

0xB6 Turn Off Channel Pitch LFO 1
0xB7 ADSR: Attack Mode 2 attack_mode: byte (1 or 5)
0xB8 Channel Volume LFO (Tremolo) 4 delay: byte, rate: byte, type: byte (0-15) When rate is 0, it will be translated to 256 ticks.
0xB9 Channel Volume LFO Depth 2 depth: byte
0xBA Turn Off Channel Volume LFO 1
0xBB ADSR: Sustain Mode 2 sustain_mode: byte (1, 3, 5 or 7)
0xBC Channel Pan LFO 3 rate: byte, type: byte (0-15) When rate is 0, it will be translated to 256 ticks.
0xBD Channel Pan LFO Depth 2 depth: byte
0xBE Turn Off Channel Pan LFO 1
0xBF ADSR: Release Mode 2 release_mode: byte (3 or 7)
0xC0 Channel Transpose (Absolute) 2 semitones: signed byte
0xC1 Channel Transpose (Relative) 2 semitones: signed byte
0xC2 Turn On Reverb 1
0xC3 Turn Off Reverb 1
0xC4 Turn On Noise 1
0xC5 Turn Off Noise 1
0xC6 Turn On Frequency Modulation 1
0xC7 Turn Off Frequency Modulation 1
0xC8 Loop Point 1 Remember the current offset as a loop point and increase the nesting level of the loop.
0xC9 Return to Loop Point Up to N Times 2 times: byte On the Nth repeat, this instruction will end the current loop by decrementing the nesting level of the loop. Otherwise, it will increment the loop counter and return to the loop point.

When times is 0, it will be translated to 256 times.

0xCA Return to Loop Point 1 This instruction will increment the loop counter.
0xCB Reset Sound Effects 1 Reset sound effects such as noise, frequency modulation, reverb, overlay voice and alternate voice.
0xCC Turn On Legato 1 This instruction will stop the regular key on and key off performance of the subsequent notes and update the pitch only.
0xCD Turn Off Legato 1
0xCE Turn On Noise and Toggle Noise On/Off after a Period of Time 2 delay: byte When delay is 0, it will be translated to 257 ticks.
0xCF Toggle Noise On/Off after a Period of Time 2 delay: byte When delay is 0, it will be translated to 257 ticks.
0xD0 Turn On Full-Length Note Mode 1 This instruction will stop the regular key off performance of the subsequent notes.
0xD1 Turn Off Full-Length Note Mode 1
0xD2 Turn On Frequency Modulation and Toggle Frequency Modulation On/Off after a Period of Time 2 delay: byte When delay is 0, it will be translated to 257 ticks.
0xD3 Toggle Frequency Modulation On/Off after a Period of Time 2 delay: byte When delay is 0, it will be translated to 257 ticks.
0xD4 Turn On Playback Rate Side Chain 1 Duplicate and use the playback frequency of the previous voice channel.
0xD5 Turn Off Playback Rate Side Chain 1
0xD6 Turn On Pitch-Volume Side Chain 1 Multiply the playback frequency of the previous voice channel to the output volume. Lower pitch will make the volume smaller.
0xD7 Turn Off Pitch-Volume Side Chain 1
0xD8 Channel Fine Tuning (Absolute) 2 amount: signed byte The pitch can be changed in the range of one octave above and below.

The amount is the multiplicand for the playback frequency, not a log-based parameter like cents. For example, when the amount is 127, the pitch will be multiplied by 127/128 (about +1 octave). Negative value ​​will lower the pitch.

0xD9 Channel Fine Tuning (Relative) 2 amount: signed byte The amount will be added to the current tuning amount.
0xDA Turn On Portamento 2 speed: byte When speed is 0, it will be translated to 256 ticks.
0xDB Turn Off Portamento 1
0xDC Fix Note Length 2 length_to_add: signed byte Ignore the regular length (delta-time) of subsequent notes and set to the fixed length. The length_to_add parameter will be added to the current length value. (the initial value is 0)
0xDD Channel Pitch LFO Depth Slide 3 length: byte, depth: byte When length is 0, it will be translated to 256 ticks.
0xDE Channel Volume LFO Depth Slide 3 length: byte, depth: byte When length is 0, it will be translated to 256 ticks.
0xDF Channel Pan LFO Depth Slide 3 length: byte, depth: byte When length is 0, it will be translated to 256 ticks.
0xE0 Unknown 1
0xE1 Unknown 2 value: byte
0xE2 Unknown 1 Clears the effect of opcode 0xE1
0xE3 Unimplemented 1 Code-referenced to 0xA0. Should not be used.
0xE4 Channel Pitch LFO Rate Slide 3 length: byte, rate: byte When length is 0, it will be translated to 256 ticks.
0xE5 Channel Volume LFO Rate Slide 3 length: byte, rate: byte When length is 0, it will be translated to 256 ticks.
0xE6 Channel Pan LFO Rate Slide 3 length: byte, rate: byte When length is 0, it will be translated to 256 ticks.
0xE7-0xEF Unimplemented 1 Code-referenced to 0xA0. Should not be used.
0xF0-0xFD Note, Tie, Rest with Length 2 length: byte
0xFE 0x00 Tempo 4 tempo: uint16 bpm = tempo / 218.453333 (approximate)

More strictly, bpm = 60.0 / (48 * (65536.0 / tempo) * (0x43D1 / (33868800.0 / 8)))

Note that this coefficient is different from other games with PlayStation AKAO.

0xFE 0x01 Tempo Slide 5 length: byte, tempo: uint16 When length is 0, it will be translated to 256 ticks.
0xFE 0x02 Reverb Depth 4 depth: uint16
0xFE 0x03 Reverb Depth Slide 5 length: byte, depth: uint16 When length is 0, it will be translated to 256 ticks.
0xFE 0x04 Turn On Drum Mode 2 The drum map offset is recorded in the file header.
0xFE 0x05 Turn Off Drum Mode 2
0xFE 0x06 Unconditional Jump 4 destination_offset: signed int16 This instruction can be used to make an infinite loop.
0xFE 0x07 CPU-Conditional Jump 5 condition: byte, destination_offset: signed int16 Jump if the condition variable matches condition. The value of the condition variable can be set from the game program.
0xFE 0x08 Jump on the Nth Repeat 5 times: byte, destination_offset: signed int16 When times is 0, it will be translated to 256 times.
0xFE 0x09 Break the Loop on the Nth Repeat 5 times: byte, destination_offset: signed int16 Unlike 0xF0, this instruction will end the current loop by decrementing the nesting level of the loop.

When times is 0, it will be translated to 256 times.

0xFE 0x0A Load Instrument (With Some Unknown Effect) 3 instrument: byte
0xFE 0x0B Unknown 6 offset: signed int16, offset2: signed int16
0xFE 0x0C-0x0D Unimplemented 2 Code-referenced to 0xA0. Should not be used.
0xFE 0x0E Subroutine Jump 4 destination_offset: signed int16 The subroutine jump cannot be nested. It will overwrite the return address.
0xFE 0x0F Return from Subroutine 2
0xFE 0x10 Unknown 3 value: byte
0xFE 0x11 Unknown 2 Clears the effect of opcode 0xFE 0x10
0xFE 0x12 Channel Master Volume Slide 3 length: byte, volume: byte (0-127) When length is 0, it will be translated to 256 ticks.
0xFE 0x13 Unimplemented 2 Code-referenced to 0xA0. Should not be used.
0xFE 0x14 Load Custom Instrument (Key-Split Instrument) 3 instrument: byte The custom instrument number corresponds to the custom instrument map pointed from the file header. Note that the number is different from the regular sample number used in opcode 0xA1.
0xFE 0x15 Time Signature 4 ticks_per_beat: byte, beats_per_measure: byte Note that two parameters can be 0. This pattern is used for initialization.
0xFE 0x16 Measure Number 3 measure: byte
0xFE 0x17-0x18 Unimplemented 2 Code-referenced to 0xA0. Should not be used.
0xFE 0x19 Unknown ? ?
0xFE 0x1A Unknown ? ?
0xFE 0x1B Unknown ? ?
0xFE 0x1C Unknown ? ?
0xFE 0x1D Unknown ? ?
0xFE 0x1E Unknown ? ?
0xFE 0x1F Unimplemented 2 Code-referenced to 0xA0. Should not be used.
0xFE 0x20-0xFF Unimplemented (Out of Range) n/a Do not use.
0xFF Unimplemented 1 Code-referenced to 0xA0. Should not be used.