CSNG (File Format): Difference between revisions

Jump to navigation Jump to search
imported>Jackoalan
(Clarify nature of delta time encoding)
imported>Jackoalan
 
(4 intermediate revisions by the same user not shown)
Line 104: Line 104:
| 0x8
| 0x8
| 2
| 2
| '''Region Data Index'''; used to select region data via the ''region data index''; value is negative on a dummy ''region info'' to terminate track regions
| '''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
| {{unknown|'''Unknown'''}}
| '''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 RLE=====
=====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  
Line 163: Line 163:
         next = streamIn.PeekU16()
         next = streamIn.PeekU16()
         if next == 0:
         if next == 0:
            # Automatically consume no-op and continue accumulating time value
             total += 0xffff
             total += 0xffff
             streamIn.seekAhead(2)
             streamIn.seekAhead(2)
Line 169: Line 170:
         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 253: 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 RLE-compressed
If the pitch or mod offsets in a region are non-zero, they point to a buffer of
(delta-tick, delta-value) pairs, decoding to signed 16-bit precision. The decoder must track
(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 byte sequence 0x80 0x00 is a special case that signals the end of the stream.


The algorithm for this RLE is different than the delta-time one for commands. It may
Similar to the command stream, when the delta time exceeds 15 bit range (>32767), extra pairs
scale down to a single byte if able.
with a zeroed value delta are inserted.


<syntaxhighlight lang="python" line="1">
<syntaxhighlight lang="python" line="1">
def DecodeRLE(streamIn):
def SignExtend7(value):
     # high-bit shift-trigger RLE, limited to 2 bytes
     return (value & 0x3f) - (value & 0x40)
    term = streamIn.ReadU8()
    total = term & 0x7f
    if term & 0x80:
        total = total * 256 + streamIn.ReadU8()
    return total


def DecodeContinuousRLE(streamIn, isValue):
def SignExtend15(value):
     total = 0
     return (value & 0x3fff) - (value & 0x4000)
    while True:
 
        # 1-2 byte RLE accumulated within continuable RLE
def DecodeSignedValue(streamIn):
        term = DecodeRLE(streamIn)
    byte0 = streamIn.ReadU8()
        if term == 0x8000:
    if byte0 & 0x80:
            total += 0xffff
        byte1 = streamIn.ReadU8()
            dummy = streamIn.ReadU8()
        return SignExtend15(byte1 | ((byte0 & 0x7f) << 8))
            continue
    else:
         total += term
         return SignExtend7(byte0)


        # values are signed deltas;
def DecodeUnsignedValue(streamIn):
        # extending into the high-half of 15-bit precision
    byte0 = streamIn.ReadU8()
        if isValue:
    if byte0 & 0x80:
            if total >= 0x4000:
        byte1 = streamIn.ReadU8()
                return total - 0xffff
        return byte1 | ((byte0 & 0x7f) << 8)
            else:
    else:
                return total
        return byte0


         # times are always forward-deltas
def DecodeDeltaPair(streamIn):
         return total
    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>


Anonymous user

Navigation menu