|
Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
|
The two raw streams that make up one wave: a signed delta per sample, and a shift exponent per 16-sample block packed two to a byte. More...
#include <tabulasonora/wave_rom.hpp>
Public Attributes | |
| std::vector< std::uint8_t > | delta |
| One signed byte per sample, sample_count + 1 long — the ping-pong sampler turns around on index sample_count, so that extra step is read. | |
| std::vector< std::uint8_t > | preamble_delta |
| The deltas between the 32-sample exponent-block boundary and the data start — scale_phase of them, empty for an aligned wave. | |
| std::vector< std::uint8_t > | scale |
| Shift-exponent nibbles: byte i >> 5 holds the exponents for samples i, with the low nibble used when (i >> 4) & 1 is zero and the high nibble otherwise. | |
| std::int32_t | sample_count = 0 |
| Number of decodable samples. | |
| std::int32_t | data_start = 0 |
| The descriptor's data start, exactly as given — not rounded to a block. | |
| std::int32_t | scale_phase = 0 |
| Where data_start falls inside its 32-sample exponent block, i.e. | |
The two raw streams that make up one wave: a signed delta per sample, and a shift exponent per 16-sample block packed two to a byte.
| std::vector<std::uint8_t> ts::WaveStreams::delta |
One signed byte per sample, sample_count + 1 long — the ping-pong sampler turns around on index sample_count, so that extra step is read.
| std::vector<std::uint8_t> ts::WaveStreams::preamble_delta |
The deltas between the 32-sample exponent-block boundary and the data start — scale_phase of them, empty for an aligned wave.
The engine's decoder does not begin at the data start: its predictor is zero at the block boundary below it, and these deltas are integrated in on the way. Their sum rides under every sample of the wave as a constant — measured live, Crash Cym.1's eleven preamble deltas sum to exactly −0.041015625, and subtracting the module's predictor trace from this engine's decode of the same wave leaves precisely that constant at correlation 1.0. This is the in-signal DC the verification article documents on the crashes and the sine kick.
| std::vector<std::uint8_t> ts::WaveStreams::scale |
Shift-exponent nibbles: byte i >> 5 holds the exponents for samples i, with the low nibble used when (i >> 4) & 1 is zero and the high nibble otherwise.
| std::int32_t ts::WaveStreams::sample_count = 0 |
Number of decodable samples.
| std::int32_t ts::WaveStreams::data_start = 0 |
The descriptor's data start, exactly as given — not rounded to a block.
| std::int32_t ts::WaveStreams::scale_phase = 0 |
Where data_start falls inside its 32-sample exponent block, i.e.
loop & 0x1F.
The codec carries no absolute value per block, only differences, so a wave may legitimately begin partway into an exponent block and end partway through another. Decoding just has to index the exponents by the absolute sample position rather than by the offset into what was read, which is what this carries.