Difference between revisions of "FF7/PSX/Sound/AKAO sequence"
(31 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
== Introduction == | == Introduction == | ||
− | AKAO | + | AKAO sequence is the most complicated part of the FF7 sound system. ("AKAO" is the signature string, which implies that the sound format has developed by Square Enix sound programmer Minoru Akao.) |
− | + | AKAO sequence is similar to MIDI sequence - it's custom tracker format for playing sequence sound, well tuned specially for PSX. | |
− | + | The sequence data can be found in all FF7 game modules: Field, Battle, Worldmap and in minigames. | |
All files with exension *.SND are AKAO. | All files with exension *.SND are AKAO. | ||
Line 14: | Line 14: | ||
* '''MOVIE/OVER2.SND''' - same game over sequence, don't know, why to duplicate data | * '''MOVIE/OVER2.SND''' - same game over sequence, don't know, why to duplicate data | ||
− | Other AKAO | + | Other AKAO sequences are hard-wired in other files. |
− | == | + | == File Structure == |
=== Header (size: 16 bytes) === | === Header (size: 16 bytes) === | ||
− | struct | + | struct AkaoSeqHeader |
{ | { | ||
− | static const uint8_t magic[4]; // "AKAO" C-string | + | static const uint8_t magic[4]; // "AKAO" C-string |
− | uint16_t id; // | + | uint16_t id; // song ID, used for playing sequence |
− | uint16_t length; // | + | uint16_t length; // data length - sizeof(header) |
uint16_t reverb_type; // reverb type (range from 0 to 9) | uint16_t reverb_type; // reverb type (range from 0 to 9) | ||
− | struct AkaoTimeStamp | + | 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) | ||
}; | }; | ||
− | === Channel | + | === Channel Info (size: 4 bytes + 2 bytes * <channels count>) === |
struct AkaoChannelInfo | struct AkaoChannelInfo | ||
{ | { | ||
− | uint32_t mask; // represents bitmask of used channels in this | + | uint32_t mask; // represents bitmask of used channels in this song |
− | + | uint16_t start_offsets[num_channels]; // offsets to channel opcode data | |
}; | }; | ||
int num_channels = 0; | int num_channels = 0; | ||
− | while (int bit = 0; ((info.mask & 0xFFFFFF) & (1 << bit)) != 0 | + | while (int bit = 0; bit < 24; bit++) |
− | + | if ((info.mask & 0xFFFFFF) & (1 << bit)) != 0) | |
+ | num_channels++; | ||
− | First there is 32-bit number (offset 0x10), which represents bitmask of used channels in this | + | First there is 32-bit number (offset 0x10), which represents bitmask of used channels in this song. |
− | After this | + | After this bitmask, 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] === | ||
+ | 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 [[FF7/PSX/Sound/Opcodes/0xeced|opcode 0xEC]], 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 5-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 ( | + | == Example (Home-Created AKAO Sequence): == |
=== Header === | === Header === | ||
'''41 4b 41 4f''' - AKAO string | '''41 4b 41 4f''' - AKAO string | ||
− | '''34 12''' - | + | '''34 12''' - song ID: 0x1234 |
− | '''16 00''' - | + | '''16 00''' - data length 0x16 in hex or 22 in decimal |
'''04 00''' - reverb type: 4 (large studio) | '''04 00''' - reverb type: 4 (large studio) | ||
− | '''96 12 18 22 46 28''' - | + | '''96 12 18 22 46 28''' - created at 1996-12-18 22:46:28 |
− | === Channel | + | === Channel Info === |
'''01 00 00 00''' - this indicates, that used only one channel | '''01 00 00 00''' - this indicates, that used only one channel | ||
Line 77: | Line 91: | ||
− | === Channel | + | === Channel Commands === |
'''e8 a8 66''' - sets tempo, parameter 0x66a8 | '''e8 a8 66''' - sets tempo, parameter 0x66a8 | ||
Line 100: | Line 114: | ||
− | == Sound Opcode | + | == Sound Opcode List == |
{| class="wikitable" | {| class="wikitable" | ||
Line 108: | Line 122: | ||
! Operands | ! Operands | ||
! Note | ! Note | ||
+ | |- | ||
+ | |[[FF7/PSX/Sound/Opcodes/0x0099|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. | ||
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xa0|0xA0]] | |[[FF7/PSX/Sound/Opcodes/0xa0|0xA0]] | ||
Line 118: | Line 144: | ||
|Load Instrument | |Load Instrument | ||
|2 | |2 | ||
− | |instrument: byte | + | |instrument: byte (0-127) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0x0099|0xA2]] |
− | |Overwrite Next | + | |Overwrite Next Note Length |
|2 | |2 | ||
− | | | + | |length: byte |
− | | | + | |Ignores the regular length (delta-time) of the next note and overwrites it with the specified length. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xa8aa|0xA3]] | |[[FF7/PSX/Sound/Opcodes/0xa8aa|0xA3]] | ||
|Channel Master Volume | |Channel Master Volume | ||
|2 | |2 | ||
− | |volume: byte | + | |volume: byte (0-127) |
| | | | ||
|- | |- | ||
Line 137: | Line 163: | ||
|3 | |3 | ||
|length: byte, semitones: signed byte | |length: byte, semitones: signed byte | ||
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xa5|0xA5]] | |[[FF7/PSX/Sound/Opcodes/0xa5|0xA5]] | ||
|Set Octave | |Set Octave | ||
|2 | |2 | ||
− | |octave: byte | + | |octave: byte (0-15) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xa5|0xA6]] |
|Increase Octave | |Increase Octave | ||
|1 | |1 | ||
Line 151: | Line 177: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xa5|0xA7]] |
|Decrease Octave | |Decrease Octave | ||
|1 | |1 | ||
Line 160: | Line 186: | ||
|Channel Volume | |Channel Volume | ||
|2 | |2 | ||
− | |volume: byte | + | |volume: byte (0-127) |
| | | | ||
|- | |- | ||
Line 166: | Line 192: | ||
|Channel Volume Slide | |Channel Volume Slide | ||
|3 | |3 | ||
− | |length: byte, volume: byte | + | |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]] | |[[FF7/PSX/Sound/Opcodes/0xa8aa|0xAA]] | ||
|Channel Pan | |Channel Pan | ||
|2 | |2 | ||
− | |pan: byte | + | |pan: byte (0-127) |
− | |64 is the center | + | |64 is the center. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xab|0xAB]] | |[[FF7/PSX/Sound/Opcodes/0xab|0xAB]] | ||
|Channel Pan Slide | |Channel Pan Slide | ||
|3 | |3 | ||
− | |length: byte, pan: byte | + | |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]] | |[[FF7/PSX/Sound/Opcodes/0xac|0xAC]] | ||
|Noise Clock Frequency | |Noise Clock Frequency | ||
|2 | |2 | ||
− | |clock: byte | + | |clock: byte (0x00-0x3f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xAD]] |
|ADSR: Attack Rate | |ADSR: Attack Rate | ||
|2 | |2 | ||
− | |attack_rate: byte | + | |attack_rate: byte (0x00-0x7f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xAE]] |
|ADSR: Decay Rate | |ADSR: Decay Rate | ||
|2 | |2 | ||
− | |decay_rate: byte | + | |decay_rate: byte (0x00-0x0f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xAF]] |
|ADSR: Sustain Level | |ADSR: Sustain Level | ||
|2 | |2 | ||
− | |sustain_level: byte | + | |sustain_level: byte (0x00-0x0f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xB0]] |
|ADSR: Decay Rate & Sustain Level | |ADSR: Decay Rate & Sustain Level | ||
|3 | |3 | ||
− | |decay_rate: byte, sustain_level: byte | + | |decay_rate: byte (0x00-0x0f), sustain_level: byte (0x00-0x0f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xB1]] |
|ADSR: Sustain Rate | |ADSR: Sustain Rate | ||
|2 | |2 | ||
− | |sustain_rate: byte | + | |sustain_rate: byte (0x00-0x7f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xB2]] |
|ADSR: Release Rate | |ADSR: Release Rate | ||
|2 | |2 | ||
− | |release_rate: byte | + | |release_rate: byte (0x00-0x1f) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xB3]] |
|ADSR: Reset ADSR | |ADSR: Reset ADSR | ||
|1 | |1 | ||
Line 229: | Line 255: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB4]] |
− | |Channel Pitch LFO | + | |Vibrato (Channel Pitch LFO) |
|4 | |4 | ||
− | |delay: byte, rate: byte, type: byte | + | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB5]] |
− | | | + | |Vibrato Depth |
|2 | |2 | ||
|depth: byte | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xb4b5|0xB6]] |
− | |Turn Off | + | |Turn Off Vibrato |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xB7]] |
|ADSR: Attack Mode | |ADSR: Attack Mode | ||
|2 | |2 | ||
− | |attack_mode: byte | + | |attack_mode: byte (1 or 5) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xb8b9|0xB8]] |
− | |Channel Volume LFO | + | |Tremolo (Channel Volume LFO) |
|4 | |4 | ||
− | |delay: byte, rate: byte, type: byte | + | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xb8b9|0xB9]] |
− | | | + | |Tremolo Depth |
|2 | |2 | ||
|depth: byte | |depth: byte | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xb8b9|0xBA]] |
− | |Turn Off | + | |Turn Off Tremolo |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/ADSR|0xBB]] |
|ADSR: Sustain Mode | |ADSR: Sustain Mode | ||
|2 | |2 | ||
− | |sustain_mode: byte | + | |sustain_mode: byte (1, 3, 5 or 7) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xbcbd|0xBC]] |
|Channel Pan LFO | |Channel Pan LFO | ||
|3 | |3 | ||
− | |rate: byte, type: byte | + | |rate: byte, type: byte (0-15) |
− | | | + | |When <code>rate</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xbcbd|0xBD]] |
|Channel Pan LFO Depth | |Channel Pan LFO Depth | ||
|2 | |2 | ||
Line 289: | Line 319: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xbcbd|0xBE]] |
|Turn Off Channel Pan LFO | |Turn Off Channel Pan LFO | ||
|1 | |1 | ||
Line 298: | Line 328: | ||
|ADSR: Release Mode | |ADSR: Release Mode | ||
|2 | |2 | ||
− | |release_mode: byte | + | |release_mode: byte (3 or 7) |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc0c1|0xC0]] |
|Channel Transpose (Absolute) | |Channel Transpose (Absolute) | ||
|2 | |2 | ||
Line 307: | Line 337: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc0c1|0xC1]] |
|Channel Transpose (Relative) | |Channel Transpose (Relative) | ||
|2 | |2 | ||
Line 313: | Line 343: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc2c3|0xC2]] |
|Turn On Reverb | |Turn On Reverb | ||
|1 | |1 | ||
Line 319: | Line 349: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc2c3|0xC3]] |
|Turn Off Reverb | |Turn Off Reverb | ||
|1 | |1 | ||
Line 325: | Line 355: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc4c5|0xC4]] |
|Turn On Noise | |Turn On Noise | ||
|1 | |1 | ||
Line 331: | Line 361: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc4c5|0xC5]] |
|Turn Off Noise | |Turn Off Noise | ||
|1 | |1 | ||
Line 337: | Line 367: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc6c7|0xC6]] |
|Turn On Frequency Modulation | |Turn On Frequency Modulation | ||
|1 | |1 | ||
Line 343: | Line 373: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc6c7|0xC7]] |
|Turn Off Frequency Modulation | |Turn Off Frequency Modulation | ||
|1 | |1 | ||
Line 349: | Line 379: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc8c9|0xC8]] |
|Loop Point | |Loop Point | ||
|1 | |1 | ||
| | | | ||
− | | | + | |Remember the current offset as a loop point and increase the nesting level of the loop. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xc8c9|0xC9]] |
|Return to Loop Point Up to N Times | |Return to Loop Point Up to N Times | ||
|2 | |2 | ||
|times: byte | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xc8c9|0xCA]] |
|Return to Loop Point | |Return to Loop Point | ||
|1 | |1 | ||
| | | | ||
− | | | + | |This instruction will increment the loop counter. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xcb|0xCB]] | |[[FF7/PSX/Sound/Opcodes/0xcb|0xCB]] | ||
Line 373: | Line 404: | ||
|Reset sound effects such as noise, frequency modulation, reverb, overlay voice and alternate voice. | |Reset sound effects such as noise, frequency modulation, reverb, overlay voice and alternate voice. | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xcccd|0xCC]] |
|Turn On Legato | |Turn On Legato | ||
|1 | |1 | ||
| | | | ||
− | |This instruction will stop the key on and key off performance of the subsequent notes and update the pitch only. | + | |This instruction will stop the regular key on and key off performance of the subsequent notes and update the pitch only. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xcccd|0xCD]] |
|Turn Off Legato | |Turn Off Legato | ||
|1 | |1 | ||
Line 385: | Line 416: | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xcecf|0xCE]] |
|Turn On Noise and Toggle Noise On/Off after a Period of Time | |Turn On Noise and Toggle Noise On/Off after a Period of Time | ||
|2 | |2 | ||
|delay: byte | |delay: byte | ||
− | | | + | |When <code>delay</code> is 0, it will be translated to 257 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xcecf|0xCF]] |
|Toggle Noise On/Off after a Period of Time | |Toggle Noise On/Off after a Period of Time | ||
|2 | |2 | ||
|delay: byte | |delay: byte | ||
− | | | + | |When <code>delay</code> is 0, it will be translated to 257 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd0d1|0xD0]] |
− | | | + | |Turn On Full-Length Note Mode |
|1 | |1 | ||
| | | | ||
− | | | + | |This instruction will stop the regular key off performance of the subsequent notes. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd0d1|0xD1]] |
− | | | + | |Turn Off Full-Length Note Mode |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd2d3|0xD2]] |
− | | | + | |Turn On Frequency Modulation and Toggle Frequency Modulation On/Off after a Period of Time |
− | | | + | |2 |
− | | | + | |delay: byte |
− | | | + | |When <code>delay</code> is 0, it will be translated to 257 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd2d3|0xD3]] |
− | | | + | |Toggle Frequency Modulation On/Off after a Period of Time |
− | | | + | |2 |
− | | | + | |delay: byte |
− | | | + | |When <code>delay</code> is 0, it will be translated to 257 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd4d5|0xD4]] |
− | | | + | |Turn On Playback Rate Side Chain |
|1 | |1 | ||
| | | | ||
− | | | + | |Duplicate and use the playback frequency of the previous voice channel. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd4d5|0xD5]] |
− | | | + | |Turn Off Playback Rate Side Chain |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd6d7|0xD6]] |
− | | | + | |Turn On Pitch-Volume Side Chain |
|1 | |1 | ||
| | | | ||
− | | | + | |Multiply the playback frequency of the previous voice channel to the output volume. Lower pitch will make the volume smaller. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd6d7|0xD7]] |
− | | | + | |Turn Off Pitch-Volume Side Chain |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xd8d9|0xD8]] |
− | | | + | |Channel Fine Tuning (Absolute) |
|2 | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xd8d9|0xD9]] |
− | | | + | |Channel Fine Tuning (Relative) |
|2 | |2 | ||
− | | | + | |amount: signed byte |
− | | | + | |The <code>amount</code> will be added to the current tuning amount. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xdadb|0xDA]] |
− | | | + | |Turn On Portamento |
|2 | |2 | ||
− | | | + | |speed: byte |
− | | | + | |When <code>speed</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xdadb|0xDB]] |
− | | | + | |Turn Off Portamento |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0x0099|0xDC]] |
− | | | + | |Fix Note Length |
|2 | |2 | ||
− | | | + | |length_to_add: signed byte |
− | | | + | |Ignore the regular length (delta-time) of subsequent notes and set to the fixed length. The <code>length_to_add</code> parameter will be added to the current length value. (the initial value is 0) |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xdd|0xDD]] | |[[FF7/PSX/Sound/Opcodes/0xdd|0xDD]] | ||
− | | | + | |Vibrato Depth Slide |
|3 | |3 | ||
− | | | + | |length: byte, depth: byte |
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xde|0xDE]] | |[[FF7/PSX/Sound/Opcodes/0xde|0xDE]] | ||
− | | | + | |Tremolo Depth Slide |
|3 | |3 | ||
− | | | + | |length: byte, depth: byte |
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xdf|0xDF]] | |[[FF7/PSX/Sound/Opcodes/0xdf|0xDF]] | ||
− | | | + | |Channel Pan LFO Depth Slide |
|3 | |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]] | ||
+ | |Unimplemented | ||
+ | |1 | ||
| | | | ||
− | | | + | |Code-referenced to 0xA0. Should not be used. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xe8e9|0xE8]] |
|Tempo | |Tempo | ||
|3 | |3 | ||
− | | | + | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xe8e9|0xE9]] |
− | | | + | |Tempo Slide |
|4 | |4 | ||
− | | | + | |length: byte, tempo: uint16 |
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xeaeb|0xEA]] |
|Reverb Depth | |Reverb Depth | ||
|3 | |3 | ||
− | | | + | |depth: uint16 |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xeaeb|0xEB]] |
− | | | + | |Reverb Depth Slide |
|4 | |4 | ||
− | | | + | |length: byte, depth: uint16 |
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xeced|0xEC]] |
− | | | + | |Turn On Drum Mode |
|3 | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xeced|0xED]] |
− | | | + | |Turn Off Drum Mode |
|1 | |1 | ||
| | | | ||
Line 530: | Line 571: | ||
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xee|0xEE]] | |[[FF7/PSX/Sound/Opcodes/0xee|0xEE]] | ||
− | | | + | |Unconditional Jump |
|3 | |3 | ||
− | | | + | |destination_offset: signed int16 |
− | | | + | |This instruction can be used to make an infinite loop. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xef|0xEF]] | |[[FF7/PSX/Sound/Opcodes/0xef|0xEF]] | ||
− | | | + | |CPU-Conditional Jump |
− | | | + | |4 |
− | | | + | |condition: byte, destination_offset: signed int16 |
− | | | + | |Jump if the condition variable matches <code>condition</code>. The value of the condition variable can be set from the game program. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xf0f1|0xF0]] |
− | | | + | |Jump on the Nth Repeat |
|4 | |4 | ||
− | | | + | |times: byte, destination_offset: signed int16 |
− | | | + | |When <code>times</code> is 0, it will be translated to 256 times. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xf0f1|0xF1]] |
− | | | + | |Break the Loop on the Nth Repeat |
|4 | |4 | ||
− | | | + | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xa1|0xF2]] |
− | | | + | |Load Instrument (No Attack Sample) |
|2 | |2 | ||
− | | | + | |instrument: byte |
− | | | + | |Unlike 0xA1, the sample before the loop point is replaced by a short, silence sample. |
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xf3|0xF3]] | |[[FF7/PSX/Sound/Opcodes/0xf3|0xF3]] | ||
− | | | + | |Unknown |
|1 | |1 | ||
| | | | ||
− | | | + | |There is no actual use in music. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xf4f5|0xF4]] |
− | | | + | |Turn On Overlay Voice |
|3 | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xf4f5|0xF5]] |
− | | | + | |Turn Off Overlay Voice |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xf4f5|0xF6]] |
− | | | + | |Overlay Volume Balance |
|2 | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xf4f5|0xF7]] |
− | | | + | |Overlay Volume Balance Slide |
|3 | |3 | ||
− | | | + | |length: byte, balance: byte (0-127) |
− | | | + | |When <code>length</code> is 0, it will be translated to 256 ticks. |
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xf8f9|0xF8]] |
− | | | + | |Turn On Alternate Voice |
|2 | |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/ | + | |[[FF7/PSX/Sound/Opcodes/0xf8f9|0xF9]] |
− | | | + | |Turn Off Alternate Voice |
|1 | |1 | ||
| | | | ||
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xa0|0xFA-0xFC]] |
− | + | |Unimplemented | |
− | + | |1 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | | | ||
− | | | ||
− | |||
| | | | ||
+ | |Code-referenced to 0xA0. Should not be used. | ||
|- | |- | ||
|[[FF7/PSX/Sound/Opcodes/0xfd|0xFD]] | |[[FF7/PSX/Sound/Opcodes/0xfd|0xFD]] | ||
− | | | + | |Time Signature |
|3 | |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]] | |[[FF7/PSX/Sound/Opcodes/0xfe|0xFE]] | ||
− | | | + | |Measure Number |
− | | | + | |3 |
− | | | + | |measure: short |
| | | | ||
|- | |- | ||
− | |[[FF7/PSX/Sound/Opcodes/ | + | |[[FF7/PSX/Sound/Opcodes/0xa0|0xFF]] |
− | | | + | |Unimplemented |
− | | | + | |1 |
− | |||
| | | | ||
+ | |Code-referenced to 0xA0. Should not be used. | ||
|} | |} |
Latest revision as of 22:24, 31 October 2020
Contents
Introduction
AKAO sequence is the most complicated part of the FF7 sound system. ("AKAO" is the signature string, which implies that the sound format has developed by Square Enix sound programmer Minoru Akao.)
AKAO sequence is similar to MIDI sequence - it's custom tracker format for playing sequence sound, well tuned specially for PSX.
The sequence data can be found 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 sequences are hard-wired in other files.
File Structure
Header (size: 16 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 - 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) };
Channel Info (size: 4 bytes + 2 bytes * <channels count>)
struct AkaoChannelInfo { uint32_t mask; // represents bitmask of used channels in this song 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) num_channels++;
First there is 32-bit number (offset 0x10), which represents bitmask of used channels in this song.
After this bitmask, 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]
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 0xEC, 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 5-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 (Home-Created AKAO Sequence):
Header
41 4b 41 4f - AKAO string
34 12 - song ID: 0x1234
16 00 - data length 0x16 in hex or 22 in decimal
04 00 - reverb type: 4 (large studio)
96 12 18 22 46 28 - created at 1996-12-18 22:46:28
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 |
---|---|---|---|---|
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 | 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 |
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 |
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-0xE7 | Unimplemented | 1 | Code-referenced to 0xA0. Should not be used. | |
0xE8 | Tempo | 3 | tempo: uint16 | bpm = tempo / 214.998204 (approximate)
More strictly, Note that this coefficient is different from other games with PlayStation AKAO. |
0xE9 | Tempo Slide | 4 | length: byte, tempo: uint16 | When length is 0, it will be translated to 256 ticks.
|
0xEA | Reverb Depth | 3 | depth: uint16 | |
0xEB | Reverb Depth Slide | 4 | length: byte, depth: uint16 | When length is 0, it will be translated to 256 ticks.
|
0xEC | Turn On Drum Mode | 3 | drum_map_offset: signed int16 | The drum_map_offset is a relative offset pointing to the drum instrument map table, which determines the instrument for each keys.
|
0xED | Turn Off Drum Mode | 1 | ||
0xEE | Unconditional Jump | 3 | destination_offset: signed int16 | This instruction can be used to make an infinite loop. |
0xEF | CPU-Conditional Jump | 4 | 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.
|
0xF0 | Jump on the Nth Repeat | 4 | times: byte, destination_offset: signed int16 | When times is 0, it will be translated to 256 times.
|
0xF1 | Break the Loop on the Nth Repeat | 4 | times: byte, destination_offset: signed int16 | Unlike 0xF0, this instruction will end the current loop by decrementing the nesting level of the loop.
When |
0xF2 | Load Instrument (No Attack Sample) | 2 | instrument: byte | Unlike 0xA1, the sample before the loop point is replaced by a short, silence sample. |
0xF3 | Unknown | 1 | There is no actual use in music. | |
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". |
0xF5 | Turn Off Overlay Voice | 1 | ||
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. |
0xF7 | Overlay Volume Balance Slide | 3 | length: byte, balance: byte (0-127) | When length is 0, it will be translated to 256 ticks.
|
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. |
0xF9 | Turn Off Alternate Voice | 1 | ||
0xFA-0xFC | Unimplemented | 1 | Code-referenced to 0xA0. Should not be used. | |
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. |
0xFE | Measure Number | 3 | measure: short | |
0xFF | Unimplemented | 1 | Code-referenced to 0xA0. Should not be used. |