SonaStream is the format for sound tracks in Sona. Each track is a sequence of
"events" (commands) which consist of an "opcode" byte and possibly extra bytes
as arguments (see below for the format of each event). The recommended file
extension is ".sona".
The documentation below is for Sona 0.50.
*** Load commands ***
$00 ... Load FM ch1 instrument
$01 ... Load FM ch2 instrument
$02 ... Load FM ch3 instrument
$03 ... Load FM ch3 instrument
$04 ... Load FM ch4 instrument
$05 ... Load FM ch5 instrument
$06 ... Load FM ch6 instrument
Loads a new instrument into the given FM channel. The following byte is
the instrument ID. There isn't any difference between $02 and $03.
$08 ... Load square ch1 instrument
$09 ... Load square ch2 instrument
$0A ... Load square ch3 instrument
Loads a new instrument into the given square channel. The following byte
is the instrument ID.
$0B ... Load noise instrument
Loads a new instrument into the noise channel. The following byte is the
instrument ID.
*** Key-on commands ***
$10 ... FM ch1 key-on
$11 ... FM ch2 key-on
$12 ... FM ch3 key-on (normal)
$13 ... FM ch3 key-on (special)
$14 ... FM ch4 key-on
$15 ... FM ch5 key-on
$16 ... FM ch6 key-on
Starts a new note in the given FM channel. This is followed by one pitch
value (except $13, which is followed by four pitch values in S1, S2, S3,
S4 order).
$18 ... square ch1 key-on
$19 ... square ch2 key-on
$1A ... square ch3 key-on
Starts a new note in the given square wave channel. This is followed by
one pitch value.
$1B ... noise key-on
Starts a new note in the noise channel. This is followed by one byte:
$00 = periodic noise, high pitch
$01 = periodic noise, medium pitch
$02 = periodic noise, low pitch
$03 = periodic noise, square ch3 pitch
$04 = white noise, high pitch
$05 = white noise, medium pitch
$06 = white noise, low pitch
$07 = white noise, square ch3 pitch
$1E ... PCM ch1 key-on
$1F ... PCM ch2 key-on
Plays a sample in the given PCM channel. This is followed by one byte,
which is the instrument ID.
*** Key-off commands ***
$20 ... FM ch1 key-off
$21 ... FM ch2 key-off
$22 ... FM ch3 key-off
$23 ... FM ch3 key-off
$24 ... FM ch4 key-off
$25 ... FM ch5 key-off
$26 ... FM ch6 key-off
Stops the current note in the given FM channel (starts release). There
isn't any difference between $22 and $23.
$28 ... square ch1 key-off
$29 ... square ch2 key-off
$2A ... square ch3 key-off
Stops the current note in the given square channel (starts release).
$2B ... noise key-off
Stops the current note in the noise channel (starts release).
$2E ... PCM ch1 key-off
$2F ... PCM ch2 key-off
Stops playback on the given PCM channel.
*** Set pitch commands ***
$30 ... FM ch1 set pitch
$31 ... FM ch2 set pitch
$32 ... FM ch3 set pitch (normal)
$33 ... FM ch3 set pitch (special)
$34 ... FM ch4 set pitch
$35 ... FM ch5 set pitch
$36 ... FM ch6 set pitch
Changes the pitch of the given FM channel (without starting a new note).
This is followed by one pitch value (except $33, which is followed by four
pitch values in S1, S2, S3, S4 order).
$38 ... square ch1 set pitch
$39 ... square ch2 set pitch
$3A ... square ch3 set pitch
Changes the pitch of the given square channel (without starting a new
note). This is followed by one pitch value.
$3B ... noise set pitch
Changes the pitch of the noise channel (without starting a new note). This
is followed by one byte, in the same format as for $1B.
*** Volume commands ***
$40 ... FM ch1 set volume
$41 ... FM ch2 set volume
$42 ... FM ch3 set volume
$43 ... FM ch3 set volume
$44 ... FM ch4 set volume
$45 ... FM ch5 set volume
$46 ... FM ch6 set volume
Changes the volume of the given FM channel. There isn't any difference
between $42 and $43. This is followed by a volume value.
$48 ... square ch1 set volume
$49 ... square ch2 set volume
$4A ... square ch3 set volume
Changes the volume of the given square channel. This is followed by a
volume value.
$4B ... noise set volume
Changes the volume of the noise channel. This is followed by a
volume value.
*** Panning commands ***
$50 ... FM ch1 set panning
$51 ... FM ch2 set panning
$52 ... FM ch3 set panning
$53 ... FM ch3 set panning
$54 ... FM ch4 set panning
$55 ... FM ch5 set panning
$56 ... FM ch6 set panning
Changes the panning of the given FM channel. There isn't any difference
between $52 and $53. The following byte is the new panning:
$00 = mute
$40 = right speaker only
$80 = left speaker only
$C0 = both speakers
Note: FM ch6's panning also affects PCM output (since it's the same
hardware channel).
*** LFO commands ***
$FB ... Set LFO
Changes the LFO setting. The following byte is the new setting (same
format as YM2612 register $22):
$00 = LFO off
$08 = 3.82Hz $0C = 6.60Hz
$09 = 5.33Hz $0D = 9.23Hz
$0A = 5.77Hz $0E = 46.11Hz
$0B = 6.11Hz $0F = 69.22Hz
$58 ... FM ch1 set PMS/AMS value
$59 ... FM ch2 set PMS/AMS value
$5A ... FM ch3 set PMS/AMS value
$5B ... FM ch3 set PMS/AMS value
$5C ... FM ch4 set PMS/AMS value
$5D ... FM ch5 set PMS/AMS value
$5E ... FM ch6 set PMS/AMS value
Changes the PMS and AMS values for the given FM channel. There isn't any
difference between $5A and $5B. The following byte is the PMS/AMS value,
in this format:
00aa0ppp
aa = AMS 00 = off
01 = 1.4 dB
10 = 5.9 dB
11 = 11.8 dB
ppp = PMS 000 = off
001 = ±3.4 cent
010 = ±6.7 cent
011 = ±10.0 cent
100 = ±14.0 cent
101 = ±20.0 cent
110 = ±40.0 cent
111 = ±80.0 cent
You can use PMS to do vibrato and AMS to do tremolo. Only operators with
their AMON bit set will be affected.
*** Control commands ***
$F6 ... Replace instrument
Replaces an instrument in the instrument list with an instrument embedded
in the stream data itself. The format is as follows:
3 bytes: size of instrument (in bytes)
1 bytes: instrument ID
?? bytes: instrument data
$F8 ... Write YM2612 register (bank 1)
$F9 ... Write YM2612 register (bank 2)
Writes directly to a YM2612 register. Two bytes follow: the register
number and the value to write.
$FA ... Set BGM speed
Changes the BGM speed. One byte follows, which specifies the speed
multiplier divided by 32:
60Hz × (value ÷ 32)
*60Hz if you didn't change Timer B's value.
$FC ... Set loop point
Sets this stream's loop point here.
$FD ... Go to loop point
Jumps to this stream's loop point.
$FE ... Wait
Waits ("rest"). The following byte is the number of ticks to wait (0 is
interpreted as waiting for 256 ticks).
$FF ... Stop
End of stream data.
*** VM commands ***
$C0 ... Move immediate (src) into variable (dest)
$C1 ... Move variable (src) into variable (dest)
dest ← src
Copies a value into a VM variable.
Two bytes follow: the first byte is the destination (dest), the second
byte is the source (src). The destination is always a variable number.
For $C0 the source is the value to copy, for $C1 the source is another
variable number.
$C2 ... Add immediate (src) to variable (dest)
$C3 ... Add variable (src) to variable (dest)
dest ← dest + src
Adds a value to a VM variable.
Two bytes follow, in the same format as for $C0/$C1.
$C4 ... Substract immediate (src) from variable (dest)
$C5 ... Substract variable (src) from variable (dest)
dest ← dest - src
Substracts a value from a VM variable.
Two bytes follow, in the same format as for $C0/$C1.
$C6 ... AND immediate (src) with variable (dest)
$C7 ... AND variable (src) with variable (dest)
dest ← dest & src
Does a binary AND between a value and a VM variable.
Two bytes follow, in the same format as for $C0/$C1.
$C8 ... OR immediate (src) with variable (dest)
$C9 ... OR variable (src) with variable (dest)
dest ← dest | src
Does a binary OR between a value and a VM variable.
Two bytes follow, in the same format as for $C0/$C1.
$CA ... XOR immediate (src) with variable (dest)
$CB ... XOR variable (src) with variable (dest)
dest ← dest ^ src
Does a binary XOR between a value and a VM variable.
Two bytes follow, in the same format as for $C0/$C1.
$CC ... Negate variable
dest ← -dest
Flips the sign of a VM variable (two's complement). One byte follows,
which is the variable number. This is the same as doing $CD nn $CE nn
(nn = variable number).
$CD ... NOT variable
dest ← ~dest
Flips all the bits of a VM variable. One byte follows, which is the
variable number. This is the same as doing $CA nn $FF (nn = variable
number).
$CE ... Increment variable by 1
dest ← dest + 1
Increments a VM variable by 1. One byte follows, which is the variable
number. This is the same as doing $C2 nn $01 (nn = variable number).
$CF ... Decrement variable by 1
dest ← dest - 1
Decrements a VM variable by 1. One byte follows, which is the variable
number. This is the same as doing $C4 nn $01 (nn = variable number).
*** Pitch argument format ***
Pitch value format is one or two bytes as follows:
$00~$5F = 0yyyyxxx Absolute pitch (coarse)
$70~$7F = 0111zzzz 0yyyyxxx Absolute pitch (fine)
$80~$DF = 1yyyy0xx Relative pitch (coarse, up)
1yyyy1xx Relative pitch (coarse, down)
$F0~$FF = 1111zzzz 0yyyyxxx Relative pitch (fine, up)
1111zzzz 1yyyyxxx Relative pitch (fine, down)
xxx = octave (0~*)
yyyy = semitone (0~11)
zzzz = semitone fraction (0~15)
* Octaves for FM are 0~7, for square are 0~5.
* Fraction is measured in 1/16th semitone units (6.25 cents).
* $80 can be used for key-on and set pitch with FM3 special to avoid
changing the pitch for some operators.
*** Volume argument format ***
Volume value format is one byte as follows:
$00~$7F = 0vvvvvvv Absolute volume
$80~$BF = 10vvvvvv Relative volume (increment attenuation)
$C0~$FF = 11vvvvvv Relative volume (decrement attenuation)
vvvvvvv = attenuation
Attenuation is always in -0.75dB steps, even for PSG channels.