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 * )

struct AkaoChannelInfo
{
  uint32_t start_offsets[num_channels];  // offsets to channel opcode data
};

AkaoSeqHeader *header;
int num_channels = 0;
while (int bit = 0; bit < 32; bit++)
  if (info.mask & (1 << bit)) != 0)
    num_channels++;

There is 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.

Custom Instrument Map Table

When a song uses custom instruments with opcode 0xFE 0x14, a custom instrument map table will be placed after the end of AKAO opcode sequence.

This table consists of a collection of zones representing performance settings for a particular key range. A zone is an 8-byte item, and an instrument can have one or more regions.

struct AkaoInstrumentZoneAttr
{
  uint8_t instrument;   // corresponding to opcode 0xA1
  uint8_t low_key;      // lowest key (in note number)
  uint8_t high_key;     // highest key (in note number)
  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-31)
  uint8_t volume;       // adjust the note volume to n/128 of the original volume (0 will keep the original volume)
}

- Zones must be ordered from low key to high key - The final zone of the instrument must be a terminator filled with 0s (more precisely, a zone with ADSR sustain mode of 0 is considered a terminator)

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-31)
  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.

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

Vibrato (Channel Pitch LFO)

4

delay: byte, rate: byte, type: byte (0-15)

When rate is 0, it will be translated to 256 ticks.

0xB5

Vibrato 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 Vibrato

1

0xB7

ADSR: Attack Mode

2

attack_mode: byte (1 or 5)

0xB8

Tremolo (Channel Volume LFO)

4

delay: byte, rate: byte, type: byte (0-15)

When rate is 0, it will be translated to 256 ticks.

0xB9

Tremolo Depth

2

depth: byte

0xBA

Turn Off Tremolo

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

Vibrato Depth Slide

3

length: byte, depth: byte

When length is 0, it will be translated to 256 ticks.

0xDE

Tremolo 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

Vibrato Rate Slide

3

length: byte, rate: byte

When length is 0, it will be translated to 256 ticks.

0xE5

Tremolo 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)))

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 (No Attack Sample)

3

instrument: byte

Unlike 0xA1, the sample before the loop point is replaced by a short, silence sample.

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

Pattern

4

destination_offset: signed int16

Remember the return address and jump to the specified location. It cannot be nested.

0xFE 0x0F

End Pattern

2

Return from opcode 0xFE 0x0E.

0xFE 0x10

Allocate Reserved Voices

3

count: byte

Reserve the specified count of hardware voices. Reserved voices will not be used unless explicitly specified in opcode 0xFE 0x1D.

0xFE 0x11

Free Reserved Voices

2

Set the count of reserved voices to 0.

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

Channel Volume Slide Per Note On

4

length: byte, volume: byte (0-127)

Applies volume slide (opcode 0xA9) for each notes. When length is 0, it will be translated to 256 ticks.

0xFE 0x1A

Unknown

2

Turn something on.

0xFE 0x1B

Unknown

2

Clears the effect of opcode 0xFE 0x1A.

0xFE 0x1C

Unknown

3

value: byte

Obsoleted opcode? As far as I learned from the assembly code, it probably does nothing.

0xFE 0x1D

Use Reserved Voices

2

Allow using the reserved voices to play the sound of the current track. If they are all used, the remaining voices will instead be used normally.

Use opcode 0xFE 0x10 to allocate reserved voices.

0xFE 0x1E

Use No Reserved Voices

2

Disable the effect of opcode 0xFE 0x1D.

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.