CSNG (File Format): Difference between revisions
Jump to navigation
Jump to search
→Region Info
imported>Jackoalan |
imported>Jackoalan |
||
(5 intermediate revisions by the same user not shown) | |||
Line 104: | Line 104: | ||
| 0x8 | | 0x8 | ||
| 2 | | 2 | ||
| '''Region Data Index'''; used to select region data | | '''Region Data Index'''; used to select region data when positive; -1 value terminates track; -2 value loops jumps to ''Loop Region Index'' at this region's tick | ||
|- | |- | ||
| 0xA | | 0xA | ||
| 2 | | 2 | ||
| '''Loop Region Index'''; index of region to jump to if ''Region Data Index'' set to -2 | |||
|- | |- | ||
| 0xC | | 0xC | ||
Line 147: | Line 147: | ||
in SNG: ''note'', ''control change'', and ''program change''. | in SNG: ''note'', ''control change'', and ''program change''. | ||
=====Delta Time | =====Delta Time Encoding===== | ||
Just like MIDI, each command starts with a '''delta time''' value telling the sequencer | Just like MIDI, each command starts with a '''delta time''' value telling the sequencer | ||
how many ticks to wait after the previous command. Unlike MIDI, Factor5 uses a | how many ticks to wait after the previous command. Unlike MIDI, Factor5 uses a fixed 16-bit | ||
value to store the delta times. In case of a delta time greater than 65535, no-op commands | |||
with a 65535 delta time are inserted into the command stream until the target time is attained. | |||
In a theoretical Python streaming API, the delta time can be decoded with no-ops automatically consumed like so: | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
def | def DecodeDeltaTime(streamIn): | ||
total = 0 | total = 0 | ||
while True: | while True: | ||
term = streamIn.ReadU16() | term = streamIn.ReadU16() | ||
if | next = streamIn.PeekU16() | ||
if next == 0: | |||
# Automatically consume no-op and continue accumulating time value | |||
total += 0xffff | total += 0xffff | ||
streamIn.seekAhead(2) | |||
continue | continue | ||
total += term | total += term | ||
return total | return total | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=====No-Op Command===== | |||
When the two bytes following the delta-time are both zero, this is a no-op command. | |||
As mentioned before, no-ops are useful for accumulating delta times greater than 65535. | |||
=====Note Command===== | =====Note Command===== | ||
Line 256: | Line 260: | ||
====Continuous Pitch / Modulation Data==== | ====Continuous Pitch / Modulation Data==== | ||
If the pitch or mod offsets in a region are non-zero, they point to a buffer of | If the pitch or mod offsets in a region are non-zero, they point to a buffer of | ||
(delta-tick, delta-value) pairs | (delta-tick, delta-value) pairs. The delta times are encoded as unsigned 7/15 bit values | ||
the absolute time and value, summing each consecutive update for the current time/values. | and the delta values are signed 7/15-bit values. The values are promoted to 15-bit if they | ||
exceed their 7-bit range. In this case, the highest bit is set on the first byte in the pair | |||
(0x80). The decoder must track the absolute time and value, summing each consecutive update | |||
for the current time/values. | |||
The | The byte sequence 0x80 0x00 is a special case that signals the end of the stream. | ||
Similar to the command stream, when the delta time exceeds 15 bit range (>32767), extra pairs | |||
with a zeroed value delta are inserted. | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
def | def SignExtend7(value): | ||
return (value & 0x3f) - (value & 0x40) | |||
def SignExtend15(value): | |||
return (value & 0x3fff) - (value & 0x4000) | |||
return | |||
def | def DecodeSignedValue(streamIn): | ||
byte0 = streamIn.ReadU8() | |||
if byte0 & 0x80: | |||
byte1 = streamIn.ReadU8() | |||
return SignExtend15(byte1 | ((byte0 & 0x7f) << 8)) | |||
else: | |||
return SignExtend7(byte0) | |||
def DecodeUnsignedValue(streamIn): | |||
byte0 = streamIn.ReadU8() | |||
if byte0 & 0x80: | |||
byte1 = streamIn.ReadU8() | |||
return byte1 | ((byte0 & 0x7f) << 8) | |||
else: | |||
return byte0 | |||
# | def DecodeDeltaPair(streamIn): | ||
return | ret = [0,0] | ||
while ret[1] == 0: | |||
if streamIn.PeekU16() == 0x8000: | |||
# The stream has ended | |||
break | |||
ret[0] += DecodeUnsignedValue(streamIn) | |||
ret[1] = DecodeSignedValue(streamIn) | |||
return ret | |||
</syntaxhighlight> | </syntaxhighlight> | ||