Difference between revisions of "FF7/PSX/Sound/AKAO sequence"

From Final Fantasy Inside
< FF7
Jump to navigation Jump to search
Line 118: Line 118:
 
|Load Instrument
 
|Load Instrument
 
|2
 
|2
|u8 instrument
+
|instrument: byte
 
|
 
|
 
|-
 
|-
Line 124: Line 124:
 
|Overwrite Next Delta-Time Length
 
|Overwrite Next Delta-Time Length
 
|2
 
|2
|u8 delta_time
+
|delta_time: byte
 
|
 
|
 
|-
 
|-
Line 130: Line 130:
 
|Channel Master Volume
 
|Channel Master Volume
 
|2
 
|2
|u8 volume
+
|volume: byte
 
|
 
|
 
|-
 
|-
Line 136: Line 136:
 
|Pitch Bend Slide
 
|Pitch Bend Slide
 
|3
 
|3
|u8 length, s8 semitones
+
|length: byte, semitones: signed byte
 
|
 
|
 
|-
 
|-
Line 142: Line 142:
 
|Set Octave
 
|Set Octave
 
|2
 
|2
|u8 octave
+
|octave: byte
 
|
 
|
 
|-
 
|-
Line 160: Line 160:
 
|Channel Volume
 
|Channel Volume
 
|2
 
|2
|u8 volume
+
|volume: byte
 
|
 
|
 
|-
 
|-
Line 166: Line 166:
 
|Channel Volume Slide
 
|Channel Volume Slide
 
|3
 
|3
|u8 length, u8 volume
+
|length: byte, volume: byte
 
|
 
|
 
|-
 
|-
Line 172: Line 172:
 
|Channel Pan
 
|Channel Pan
 
|2
 
|2
|u8 pan
+
|pan: byte
 
|64 is the center
 
|64 is the center
 
|-
 
|-
Line 178: Line 178:
 
|Channel Pan Slide
 
|Channel Pan Slide
 
|3
 
|3
|u8 length, u8 pan
+
|length: byte, pan: byte
 
|
 
|
 
|-
 
|-
Line 184: Line 184:
 
|Noise Clock Frequency
 
|Noise Clock Frequency
 
|2
 
|2
|u8 clock
+
|clock: byte
 
|
 
|
 
|-
 
|-
Line 190: Line 190:
 
|ADSR: Attack Rate
 
|ADSR: Attack Rate
 
|2
 
|2
|u8 attack_rate
+
|attack_rate: byte
 
|
 
|
 
|-
 
|-
Line 196: Line 196:
 
|ADSR: Decay Rate
 
|ADSR: Decay Rate
 
|2
 
|2
|u8 decay_rate
+
|decay_rate: byte
 
|
 
|
 
|-
 
|-
Line 202: Line 202:
 
|ADSR: Sustain Level
 
|ADSR: Sustain Level
 
|2
 
|2
|u8 sustain_level
+
|sustain_level: byte
 
|
 
|
 
|-
 
|-
Line 208: Line 208:
 
|ADSR: Decay Rate & Sustain Level
 
|ADSR: Decay Rate & Sustain Level
 
|3
 
|3
|u8 decay_rate, u8 sustain_level
+
|decay_rate: byte, sustain_level: byte
 
|
 
|
 
|-
 
|-
Line 214: Line 214:
 
|ADSR: Sustain Rate
 
|ADSR: Sustain Rate
 
|2
 
|2
|u8 sustain_rate
+
|sustain_rate: byte
 
|
 
|
 
|-
 
|-
Line 220: Line 220:
 
|ADSR: Release Rate
 
|ADSR: Release Rate
 
|2
 
|2
|u8 release_rate
+
|release_rate: byte
 
|
 
|
 
|-
 
|-
Line 232: Line 232:
 
|Channel Pitch LFO (Vibrato)
 
|Channel Pitch LFO (Vibrato)
 
|4
 
|4
|u8 delay, u8 rate, u8 type
+
|delay: byte, rate: byte, type: byte
 
|
 
|
 
|-
 
|-
Line 238: Line 238:
 
|Channel Pitch LFO Depth
 
|Channel Pitch LFO Depth
 
|2
 
|2
|u8 depth
+
|depth: byte
 
|
 
|
 
|-
 
|-
Line 250: Line 250:
 
|ADSR: Attack Mode
 
|ADSR: Attack Mode
 
|2
 
|2
|u8 attack_mode
+
|attack_mode: byte
 
|
 
|
 
|-
 
|-
Line 256: Line 256:
 
|Channel Volume LFO (Tremolo)
 
|Channel Volume LFO (Tremolo)
 
|4
 
|4
|u8 delay, u8 rate, u8 type
+
|delay: byte, rate: byte, type: byte
 
|
 
|
 
|-
 
|-
Line 262: Line 262:
 
|Channel Volume LFO Depth
 
|Channel Volume LFO Depth
 
|2
 
|2
|u8 depth
+
|depth: byte
 
|
 
|
 
|-
 
|-
Line 274: Line 274:
 
|ADSR: Sustain Mode
 
|ADSR: Sustain Mode
 
|2
 
|2
|u8 sustain_mode
+
|sustain_mode: byte
 
|
 
|
 
|-
 
|-
Line 280: Line 280:
 
|Channel Pan LFO
 
|Channel Pan LFO
 
|3
 
|3
|u8 rate, u8 type
+
|rate: byte, type: byte
 
|
 
|
 
|-
 
|-
Line 286: Line 286:
 
|Channel Pan LFO Depth
 
|Channel Pan LFO Depth
 
|2
 
|2
|u8 depth
+
|depth: byte
 
|
 
|
 
|-
 
|-
Line 298: Line 298:
 
|ADSR: Release Mode
 
|ADSR: Release Mode
 
|2
 
|2
|u8 release_mode
+
|release_mode: byte
 
|
 
|
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc0|0xC0]]
 
|[[FF7/PSX/Sound/Opcodes/0xc0|0xC0]]
|
+
|Transpose (Absolute)
 
|2
 
|2
|
+
|semitones: signed byte
 
|
 
|
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc1|0xC1]]
 
|[[FF7/PSX/Sound/Opcodes/0xc1|0xC1]]
|
+
|Transpose (Relative)
 
|2
 
|2
|
+
|semitones: signed byte
 
|
 
|
 
|-
 
|-
Line 320: Line 320:
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc3|0xC3]]
 
|[[FF7/PSX/Sound/Opcodes/0xc3|0xC3]]
|
+
|Turn Off Reverb
 
|1
 
|1
 
|
 
|
Line 326: Line 326:
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc4|0xC4]]
 
|[[FF7/PSX/Sound/Opcodes/0xc4|0xC4]]
|
+
|Turn On Noise
 
|1
 
|1
 
|
 
|
Line 332: Line 332:
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc5|0xC5]]
 
|[[FF7/PSX/Sound/Opcodes/0xc5|0xC5]]
|
+
|Turn Off Noise
 
|1
 
|1
 
|
 
|
Line 338: Line 338:
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc6|0xC6]]
 
|[[FF7/PSX/Sound/Opcodes/0xc6|0xC6]]
|
+
|Turn On Frequency Modulation
 
|1
 
|1
 
|
 
|
Line 344: Line 344:
 
|-
 
|-
 
|[[FF7/PSX/Sound/Opcodes/0xc7|0xC7]]
 
|[[FF7/PSX/Sound/Opcodes/0xc7|0xC7]]
|
+
|Turn Off Frequency Modulation
 
|1
 
|1
 
|
 
|

Revision as of 06:05, 31 May 2020

Introduction

AKAO frames are most complicated frames in FF7 sound system. ("AKAO" is frame magic, probably developed by Minoru Akao, Square Enix sound programmer :) )

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

This frames are in all FF7 game modules: Field, Battle, Worldmap and in minigames.

All files with exension *.SND are AKAO.

  • MINI/ASERI2.SND - Battle Arena theme
  • MINI/SENSUI.SND - used in Submarine minigame
  • ENEMY6/OVER2.SND - game over sequence
  • ENEMY6/FAN2.SND - battle win "fanfare" sequence
  • MOVIE/OVER2.SND - same game over sequence, don't know, why to duplicate data

Other AKAO frames are hard-wired in other files.


AKAO frame structure

Header (size: 16 bytes)

struct AkaoHeader
{
  static const uint8_t magic[4];  // "AKAO" C-string aka frame *MAGIC*
  uint16_t id;                    // frame ID, used for playing sequence
  uint16_t length;                // frame length - sizeof(header)
  uint16_t reverb_type;           // reverb type (range from 0 to 9)
  struct AkaoTimeStamp
  {
    uint8_t year_bcd;             // year (in binary coded decimal)
    uint8_t month_bcd;            // month (in binary coded decimal, between 0x01 - 0x12)
    uint8_t day_bcd;              // day (in binary coded decimal, between 0x01 - 0x31)
    uint8_t hours_bcd;            // hours (in binary coded decimal, between 0x00 - 0x23)
    uint8_t minutes_bcd;          // minutes (in binary coded decimal, between 0x00 - 0x59)
    uint8_t seconds_bcd;          // seconds (in binary coded decimal, between 0x00 - 0x59)
  } timestamp;
};

Channel info (size: 4 bytes + 2 bytes * <channels count>)

struct AkaoChannelInfo
{
  uint32_t mask;                         // represents bitmask of used channels in this frame
  uint32_t start_offsets[num_channels];  // offsets to channel opcode data
};
int num_channels = 0;
while (int bit = 0; ((info.mask & 0xFFFFFF) & (1 << bit)) != 0; bit++)
  num_channels++;

First there is 32-bit number (offset 0x10), which represents bitmask of used channels in this frame.

After this frame, there is <channels count> offsets to channel opcode data counting from current offset. Each offsets is a relative offset based on the address *next to* the offset itself. (This is a general rule for early versions of AKAO to interpret relative offsets.)


Channel Commands [AKAO Opcodes]

Most complicated part.

For every channel in AKAO frame there is set of commands to perform. This is similar to Field opcodes. Here I'll call this sound commands "opcodes". Every opcode has it's own number of arguments (from no-arguments, to 3 arguments).

Example (home-created AKAO frame):

Header

41 4b 41 4f - AKAO string

34 12 - frame ID: 0x1234

16 00 - frame length 0x16 in hex or 22 in decimal

04 00 - reverb type: 4 (large studio)

96 12 18 22 46 28 - unknown data


Channel info

01 00 00 00 - this indicates, that used only one channel

00 00 - offset to first channel opcodes: in our example 0x00 means that next to this offset is opcodes for first channel


Channel commands

e8 a8 66 - sets tempo, parameter 0x66a8

ea 00 50 - sets reverb depth

a8 55 - load sample 0x55 from INSTR.ALL to channel

aa 40 - sets channel volume

c2 - turns on reverb effect

a1 0c - sets volume pan

c8 - sets loop point

66 - 0x66 % 11 = 3 (3 means to take 3rd number from play length table), 0x66 / 11 = 9 (9 means to take pitch[9] from loaded instrument record index)

ca - returns to saved loop point with opcode c8


This example plays Chocobo "Whoo-Hoo" (instrument number 0x55) repeatedly.


Sound Opcode list

Opcode Summary Length Operands Note
0xA0 Finish Channel 1
0xA1 Load Instrument 2 instrument: byte
0xA2 Overwrite Next Delta-Time Length 2 delta_time: byte
0xA3 Channel Master Volume 2 volume: byte
0xA4 Pitch Bend Slide 3 length: byte, semitones: signed byte
0xA5 Set Octave 2 octave: byte
0xA6 Increase Octave 1
0xA7 Decrease Octave 1
0xA8 Channel Volume 2 volume: byte
0xA9 Channel Volume Slide 3 length: byte, volume: byte
0xAA Channel Pan 2 pan: byte 64 is the center
0xAB Channel Pan Slide 3 length: byte, pan: byte
0xAC Noise Clock Frequency 2 clock: byte
0xAD ADSR: Attack Rate 2 attack_rate: byte
0xAE ADSR: Decay Rate 2 decay_rate: byte
0xAF ADSR: Sustain Level 2 sustain_level: byte
0xB0 ADSR: Decay Rate & Sustain Level 3 decay_rate: byte, sustain_level: byte
0xB1 ADSR: Sustain Rate 2 sustain_rate: byte
0xB2 ADSR: Release Rate 2 release_rate: byte
0xB3 ADSR: Reset ADSR 1
0xB4 Channel Pitch LFO (Vibrato) 4 delay: byte, rate: byte, type: byte
0xB5 Channel Pitch LFO Depth 2 depth: byte
0xB6 Turn Off Channel Pitch LFO 1
0xB7 ADSR: Attack Mode 2 attack_mode: byte
0xB8 Channel Volume LFO (Tremolo) 4 delay: byte, rate: byte, type: byte
0xB9 Channel Volume LFO Depth 2 depth: byte
0xBA Turn Off Channel Volume LFO 1
0xBB ADSR: Sustain Mode 2 sustain_mode: byte
0xBC Channel Pan LFO 3 rate: byte, type: byte
0xBD Channel Pan LFO Depth 2 depth: byte
0xBE Turn Off Channel Pan LFO 1
0xBF ADSR: Release Mode 2 release_mode: byte
0xC0 Transpose (Absolute) 2 semitones: signed byte
0xC1 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
0xC9 2
0xCA Return to Loop Point 1
0xCB 1
0xCC 1
0xCD 1
0xCE 1
0xCF 1
0xD0 1
0xD1 1
0xD2 1
0xD3 1
0xD4 1
0xD5 1
0xD6 1
0xD7 1
0xD8 2
0xD9 2
0xDA 2
0xDB 1
0xDC 2
0xDD 3
0xDE 3
0xDF 3
0xE8 Tempo 3 u16 tempo
0xE9 4
0xEA Reverb Depth 3 u16 depth
0xEB 4
0xEC 3
0xED 1
0xEE 3
0xEF 3
0xF0 4
0xF1 4
0xF2 2
0xF3 1
0xF4 3
0xF5 1
0xF6 2
0xF7 3
0xF8 2
0xF9 1
0xFA
0xFB
0xFC 3
0xFD 3
0xFE 2
0xFF