Anonymous

Changes

From Final Fantasy Inside

FF7/PSX/Sound/AKAO sequence

4,640 bytes added, 22:24, 31 October 2020
no edit summary
== Introduction ==
AKAO frames are sequence is the most complicated frames in part of the FF7 sound system. ("AKAO" is frame magicthe signature string, probably which implies that the sound format has developed by Minoru Akao, Square Enix sound programmer :) Minoru Akao.)
Frame AKAO sequence is similar to MIDI sequence - it's custom tracker format for playing sequence sound, well tuned specially for PSX.
This frames are The sequence data can be found in all FF7 game modules: Field, Battle, Worldmap and in minigames.
All files with exension *.SND are AKAO.
* '''MOVIE/OVER2.SND''' - same game over sequence, don't know, why to duplicate data
Other AKAO frames sequences are hard-wired in other files.
== AKAO frame structure File Structure ==
=== Header (size: 16 bytes) ===
struct AkaoHeaderAkaoSeqHeader
{
static const uint8_t magic[4]; // "AKAO" C-string aka frame *MAGIC* uint16_t id; // frame song ID, used for playing sequence uint16_t length; // frame data length - sizeof(header)
uint16_t reverb_type; // reverb type (range from 0 to 9)
struct AkaoTimeStamp timestamp; // creation time }; 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 Info (size: 4 bytes + 2 bytes * <channels count>) ===
struct AkaoChannelInfo
{
uint32_t mask; // represents bitmask of used channels in this framesong uint32_t uint16_t start_offsets[num_channels]; // offsets to channel opcode data
};
int num_channels = 0;
while (int bit = 0; bit < 24; bit++) if ((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 song.
First After this bitmask, there is 32-bit number <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. (offset 0x10), which represents bitmask This is a general rule for early versions of used channels in this frameAKAO to interpret relative offsets.)
After this frame=== Channel Commands [AKAO Opcodes] ===For every channels in an AKAO sequence, there is <channels count> offsets a set of commands to channel opcode data counting from current offsetperform. Each offsets This is a relative offset based on the address *next similar to* the offset itselfField opcodes. Here I will call this sound commands "opcodes". Every opcode has its own number of arguments (This is a general rule for early versions of AKAO from no-arguments, to interpret relative offsets3 arguments).)
=== Drum Instrument Map Table ===
=== Channel Commands When a song uses a drum kit with [AKAO [FF7/PSX/Sound/Opcodes/0xeced|opcode 0xEC]] ===Most complicated part, 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.
For every channel in AKAO frame there is set The table consists 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 a repetition of arguments (from no5-arguments, to 3 arguments)byte items.
struct AkaoDrumKeyAttr { uint8_t instrument; // corresponding to opcode 0xA1 uint8_t key; // note number when playing the drum note uint16_t volume; // corresponding to opcode 0xA8 (lower 8bit is a fractional part of the volume) uint8_t pan; // corresponding to opcode 0xAA } == Example (homeHome-created Created AKAO frameSequence): ==
=== Header ===
'''41 4b 41 4f''' - AKAO string
'''34 12''' - frame song ID: 0x1234
'''16 00''' - frame data length 0x16 in hex or 22 in decimal
'''04 00''' - reverb type: 4 (large studio)
'''96 12 18 22 46 28''' - unknown datacreated at 1996-12-18 22:46:28
=== Channel info Info ===
'''01 00 00 00''' - this indicates, that used only one channel
=== Channel commands Commands ===
'''e8 a8 66''' - sets tempo, parameter 0x66a8
== Sound Opcode list List ==
{| class="wikitable"
|1
|
|The opcode indicates the note key and length. The note will be keyed off 2 ticks before the next note.
|-
|0x9A-0x9F
|Unimplemented
|n/a1|
|Should not be used.
|The opcode indicates the note key and length.
|-
|[[FF7/PSX/Sound/Opcodes/0xa0|0xA0]]
|Load Instrument
|2
|instrument: byte(0-127)
|
|-
|Channel Master Volume
|2
|volume: byte(0-127)
|
|-
|3
|length: byte, semitones: signed byte
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xa5|0xA5]]
|Set Octave
|2
|octave: byte(0-15)
|
|-
|Channel Volume
|2
|volume: byte(0-127)
|
|-
|Channel Volume Slide
|3
|length: byte, volume: byte(0-127)|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xa8aa|0xAA]]
|Channel Pan
|2
|pan: byte(0-127)|64 is the center.
|-
|[[FF7/PSX/Sound/Opcodes/0xab|0xAB]]
|Channel Pan Slide
|3
|length: byte, pan: byte(0-127)|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xac|0xAC]]
|Noise Clock Frequency
|2
|clock: byte(0x00-0x3f)
|
|-
|ADSR: Attack Rate
|2
|attack_rate: byte(0x00-0x7f)
|
|-
|ADSR: Decay Rate
|2
|decay_rate: byte(0x00-0x0f)
|
|-
|ADSR: Sustain Level
|2
|sustain_level: byte(0x00-0x0f)
|
|-
|ADSR: Decay Rate & Sustain Level
|3
|decay_rate: byte(0x00-0x0f), sustain_level: byte(0x00-0x0f)
|
|-
|ADSR: Sustain Rate
|2
|sustain_rate: byte(0x00-0x7f)
|
|-
|ADSR: Release Rate
|2
|release_rate: byte(0x00-0x1f)
|
|-
|-
|[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB4]]
|Vibrato (Channel Pitch LFO (Vibrato)
|4
|delay: byte, rate: byte, type: byte(0-15)|When <code>rate</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB5]]
|Channel Pitch LFO Vibrato Depth
|2
|depth: byte
|The most significant bit of the <code>depth</code> 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.
|-
|[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB6]]
|Turn Off Channel Pitch LFOVibrato
|1
|
|ADSR: Attack Mode
|2
|attack_mode: byte(1 or 5)
|
|-
|[[FF7/PSX/Sound/Opcodes/0xb8b9|0xB8]]
|Tremolo (Channel Volume LFO (Tremolo)
|4
|delay: byte, rate: byte, type: byte(0-15)|When <code>rate</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xb8b9|0xB9]]
|Channel Volume LFO Tremolo Depth
|2
|depth: byte
|-
|[[FF7/PSX/Sound/Opcodes/0xb8b9|0xBA]]
|Turn Off Channel Volume LFOTremolo
|1
|
|ADSR: Sustain Mode
|2
|sustain_mode: byte(1, 3, 5 or 7)
|
|-
|Channel Pan LFO
|3
|rate: byte, type: byte(0-15)|When <code>rate</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xbcbd|0xBD]]
|ADSR: Release Mode
|2
|release_mode: byte(3 or 7)
|
|-
|1
|
|Remember the current offset as a loop point and increase the nesting level of the loop.
|-
|[[FF7/PSX/Sound/Opcodes/0xc8c9|0xC9]]
|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 <code>times</code> is 0, it will be translated to 256 times.
|-
|[[FF7/PSX/Sound/Opcodes/0xc8c9|0xCA]]
|1
|
|This instruction will increment the loop counter.
|-
|[[FF7/PSX/Sound/Opcodes/0xcb|0xCB]]
|2
|delay: byte
|When <code>delay</code> is 0, it will be translated to 257 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xcecf|0xCF]]
|2
|delay: byte
|When <code>delay</code> is 0, it will be translated to 257 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xd0d1|0xD0]]
|Turn On Full-Length Note Mode
|1
|
|This instruction will stop the regular key off performance of the subsequent notes.
|
|-
|[[FF7/PSX/Sound/Opcodes/0xd0d1|0xD1]]
|2
|delay: byte
|When <code>delay</code> is 0, it will be translated to 257 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xd2d3|0xD3]]
|2
|delay: byte
|When <code>delay</code> is 0, it will be translated to 257 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xd4d5|0xD4]]
|2
|amount: signed byte
|The pitch can be changed in the range of one octave above and below.The <code>amount</code> is the multiplicand for the playback frequency, not a log-based parameter like cents. For example, when the <code>amount</code> is 127, the pitch will be multiplied by 127/128 (about +1 octave). Negative value ​​will lower the pitch.
|-
|[[FF7/PSX/Sound/Opcodes/0xd8d9|0xD9]]
|2
|amount: signed byte
|The <code>amount</code> will be added to the current tuning amount.
|-
|[[FF7/PSX/Sound/Opcodes/0xdadb|0xDA]]
|2
|speed: byte
|When <code>speed</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xdadb|0xDB]]
|2
|length_to_add: signed byte
|Ignore the regular length (delta-time) of subsequent notes and set to the fixed length. The length <code>length_to_add</code> parameter is a value based on will be added to the current lengthvalue. (default the initial value is 0)
|-
|[[FF7/PSX/Sound/Opcodes/0xdd|0xDD]]
|Channel Pitch LFO Vibrato Depth Slide
|3
|length: byte, depth: byte
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xde|0xDE]]
|Channel Volume LFO Tremolo Depth Slide
|3
|length: byte, depth: byte
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xdf|0xDF]]
|3
|length: byte, depth: byte
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xa0|0xE0-0xE7]]
|tempo: uint16
|bpm = tempo / 214.998204 (approximate)
More strictly, <code>bpm = 60.0 / (48 * (65536.0 / tempo) * (0x44E8 / (33868800.0 / 8)))</code>
 
Note that this coefficient is different from other games with PlayStation AKAO.
|-
|[[FF7/PSX/Sound/Opcodes/0xe8e9|0xE9]]
|4
|length: byte, tempo: uint16
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xeaeb|0xEA]]
|4
|length: byte, depth: uint16
|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xeced|0xEC]]
|3
|drum_map_offset: signed int16
|The <code>drum_map_offset</code> is a relative offset pointing to the drum instrument map table, which determines the instrument for each keys.
|-
|[[FF7/PSX/Sound/Opcodes/0xeced|0xED]]
|3
|destination_offset: signed int16
|Can This instruction can be used to make an infinite loop.
|-
|[[FF7/PSX/Sound/Opcodes/0xef|0xEF]]
|4
|times: byte, destination_offset: signed int16
|When <code>times</code> is 0, it will be translated to 256 times.
|-
|[[FF7/PSX/Sound/Opcodes/0xf0f1|0xF1]]
|times: byte, destination_offset: signed int16
|Unlike 0xF0, this instruction will end the current loop by decrementing the nesting level of the loop.
When <code>times</code> is 0, it will be translated to 256 times.
|-
|[[FF7/PSX/Sound/Opcodes/0xa1|0xF2]]
|2
|instrument: byte
|The Unlike 0xA1, the sample before the loop point is replaced by a short, silence sample.
|-
|[[FF7/PSX/Sound/Opcodes/0xf3|0xF3]]
|Use No Delay for Channel Pitch/Volume LFOUnknown
|1
|
|Specific to FF7. This instruction There is not used for no actual use in music.
|-
|[[FF7/PSX/Sound/Opcodes/0xf40xf4f5|0xF4]]|Turn On Overlay Voice
|3
|instrument: byte, instrument2: byte|Play the same melody by different instruments on two voice channels. A free voice channel is required to work. Note that the two channels share the playback rate, and the pitch is not calculated for each instruments.Used in the song "Anxious Heart".
|-
|[[FF7/PSX/Sound/Opcodes/0xf50xf4f5|0xF5]]|Turn Off Overlay Voice
|1
|
|
|-
|[[FF7/PSX/Sound/Opcodes/0xf60xf4f5|0xF6]]|Overlay Volume Balance
|2
|balance: byte (0-127)|When the balance is 0, the volume of the primary voice will be 100% (127/128) of original and that of the secondary voice will be 0%. 127 is the opposite.
|-
|[[FF7/PSX/Sound/Opcodes/0xf70xf4f5|0xF7]]|Overlay Volume Balance Slide
|3
|length: byte, balance: byte (0-127)|When <code>length</code> is 0, it will be translated to 256 ticks.
|-
|[[FF7/PSX/Sound/Opcodes/0xf80xf8f9|0xF8]]|Turn On Alternate Voice
|2
|release_rate: byte (0x00-0x1f)|This instruction allows subsequent notes to be played on two alternating channels. At the same time, the ADSR release rate will be set to the specified value. A free voice channel is required to work. Check "Opening - Bombing Mission", "Tifa's Theme" and "Fortress of the Condor" for actual usage.
|-
|[[FF7/PSX/Sound/Opcodes/0xf90xf8f9|0xF9]]|Turn Off Alternate Voice
|1
|
|
|-
|[[FF7/PSX/Sound/Opcodes/0xfa0xa0|0xFA]]|||||-|[[FF7/PSX/Sound/Opcodes/0xfb|0xFB]]|||||-|[[FF7/PSX/Sound/Opcodes/0xfc|0xFC]]|Unimplemented|3|1
|
|Code-referenced to 0xA0. Should not be used.
|-
|[[FF7/PSX/Sound/Opcodes/0xfd|0xFD]]
|Time Signature
|3
|ticks_per_beat: byte, beats_per_measure: byte|Note that two parameters can be 0. This pattern is used for initialization.
|-
|[[FF7/PSX/Sound/Opcodes/0xfe|0xFE]]
|Measure Number|23|measure: short
|
|-
|[[FF7/PSX/Sound/Opcodes/0xff0xa0|0xFF]]|Unimplemented||1
|
|Code-referenced to 0xA0. Should not be used.
|}
17
edits