Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
Loading...
Searching...
No Matches
ts::WaveRom Class Reference

The 24 MB wave ROM embedded in SCCore.dll — the literal Sound Canvas hardware mask ROM, two banks addressed in 1 MB regions. More...

#include <tabulasonora/wave_rom.hpp>

Classes

struct  SplitRegion
 Splits a descriptor's region byte into a bank and a bank-relative region index. More...

Public Member Functions

 WaveRom (const RomImage &rom)
 Creates a wave-ROM view over an open image, which must outlive it.
std::int64_t bank_base (int bank) const noexcept
 File offset of the first byte of a bank.
std::int64_t region_base (int region) const noexcept
 File offset of the base of a region, from a descriptor's region byte.
std::optional< WaveStreamsread_streams (int region, int loop, int start) const
 Reads the delta and scale streams for one wave.

Static Public Member Functions

static constexpr int region_count (int bank) noexcept
 Number of regions of real ROM data in a bank: 0 for bank A, 1 for bank B.
static constexpr SplitRegion split_region (int region) noexcept

Static Public Attributes

static constexpr std::int32_t region_size = 0x100000
 Size of one addressable ROM region, 1 MB.
static constexpr std::int32_t max_sample_count = 2'000'000
 Largest wave this class will attempt to read, as a sanity bound.
static constexpr int bank_a_region_count = 16
 Regions bank A spans: sixteen, the full width of the region nibble.
static constexpr int bank_b_region_count = 8
 Regions of real ROM data in bank B — eight, not twelve.

Detailed Description

The 24 MB wave ROM embedded in SCCore.dll — the literal Sound Canvas hardware mask ROM, two banks addressed in 1 MB regions.

Bank A is the stacked SC-88/SC-88Pro ROM, bank B the SC-8820 ROM. Within a region the shift exponents live at the bottom (one byte per 32 samples, so 32 KB covers the whole 1 MB) while the deltas are addressed directly by sample index.

Constructor & Destructor Documentation

◆ WaveRom()

ts::WaveRom::WaveRom ( const RomImage & rom)
explicit

Creates a wave-ROM view over an open image, which must outlive it.

Member Function Documentation

◆ region_count()

constexpr int ts::WaveRom::region_count ( int bank)
inlinestaticnodiscardconstexprnoexcept

Number of regions of real ROM data in a bank: 0 for bank A, 1 for bank B.

◆ split_region()

constexpr SplitRegion ts::WaveRom::split_region ( int region)
inlinestaticnodiscardconstexprnoexcept

◆ bank_base()

std::int64_t ts::WaveRom::bank_base ( int bank) const
inlinenodiscardnoexcept

File offset of the first byte of a bank.

◆ region_base()

std::int64_t ts::WaveRom::region_base ( int region) const
nodiscardnoexcept

File offset of the base of a region, from a descriptor's region byte.

◆ read_streams()

std::optional< WaveStreams > ts::WaveRom::read_streams ( int region,
int loop,
int start ) const
nodiscard

Reads the delta and scale streams for one wave.

Returns nothing when the descriptor describes no usable data — a non-positive or implausibly large length.

The descriptor's field names are as recovered, and they are confusing: loop is the data start, end is the loop point, and start is the physical end.

Decoding starts at loop exactly. An unaligned loop is not a malformed descriptor: the codec stores no absolute value per block, only differences, so a wave can begin partway into an exponent block and end partway through another, and the decoder simply has to index the exponents absolutely. This used to round the start down with loop & ~0x1F, which began the integration up to 31 samples early — and because the predictor has no leak and nothing downstream blocks DC, those extra deltas displaced the whole wave for its entire length rather than adding a moment of lead-in.

Member Data Documentation

◆ region_size

std::int32_t ts::WaveRom::region_size = 0x100000
staticconstexpr

Size of one addressable ROM region, 1 MB.

◆ max_sample_count

std::int32_t ts::WaveRom::max_sample_count = 2'000'000
staticconstexpr

Largest wave this class will attempt to read, as a sanity bound.

◆ bank_a_region_count

int ts::WaveRom::bank_a_region_count = 16
staticconstexpr

Regions bank A spans: sixteen, the full width of the region nibble.

This read twelve for a long time, and descriptors reach past that — wave 4010 (TR-808 open hat) is region 12, wave 2092 (Bim Hit) region 14. That looked like a bug and is not: the two bank bases sit 0x1000030 apart, exactly 16 MB plus bank B's own 0x30 header, so regions 12–15 land at 0xc927000xf92700, well inside bank A and nowhere near bank B. The mapping bank_a_base + region * 1 MB is right for every region a descriptor can name, and nothing here bounds or clamps it.

Bank B is the one that genuinely stops early, at eight — see below.

◆ bank_b_region_count

int ts::WaveRom::bank_b_region_count = 8
staticconstexpr

Regions of real ROM data in bank B — eight, not twelve.

The manifest records both banks as 12 MB, but bank B's data ends at file offset 0x1892730: regions 0–3 are the tail of the 1996 rom_make image and regions 4–7 are the 1999 8820_wv0 image. The declared 12 MB span runs past the end of the file altogether, so the nominal size cannot be used as a bound.


The documentation for this class was generated from the following file: