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

The static patch directory: resolves a program change and a played note to the waves that sound, entirely from ROM tables with no engine involved. More...

#include <tabulasonora/patch_directory.hpp>

Public Member Functions

 PatchDirectory (const TableSet &tables)
 Creates a directory over a loaded table set, which must outlive it.
int tone_count () const noexcept
 Number of tone records in the melodic table.
int multisample_count () const noexcept
 Number of multisample records.
int wave_count () const noexcept
 Number of wave descriptors.
int alternate_count () const noexcept
 Number of alternate-articulation records.
std::optional< WaveDescriptorwave (int wave_number) const
 Reads a wave descriptor, or nothing if the number is out of range.
std::optional< Tonetone (int tone_number) const
 Reads a tone record, or nothing if the number is out of range.
int tone_level (int tone_number) const
 Reads a tone's master level from its header.
bool half_damper (int tone_number) const
 Whether a tone responds to half-damper (continuous CC64).
PartialParameters partial_by_slot (int tone_number, int slot) const
 Reads a partial block by its slot rather than its position among the present partials.
std::optional< int > multisample_wave (int multisample, int transposed_key) const
 Selects the wave a multisample yields for a transposed key, or nothing when silent.
int zone_level (int multisample, int key, int key_center) const
 Reads the per-key-zone level, the second attenuation in the TVA base-level chain.
std::vector< MultisampleZonemultisample_zones (int multisample) const
 Lists a multisample's key zones in transposed-key space, low to high.
std::optional< std::string > tone_zones (int tone_number, std::vector< ToneZone > &zones) const
 Builds the full static zone map for a tone, with key bounds converted back to MIDI notes.
std::optional< int > lut3_raw (int program, ToneMap map, int bank) const
 The raw unsigned word the three-level program lookup yields.
std::optional< int > lut3_resolved (int program, ToneMap map, int bank) const
 The three-level lookup with the GS capital-tone fallback applied.
std::optional< AlternateEntryalternate (int index) const
 Decodes an entry of the alternate-articulation table, indexed by tone# - 0x6000.
std::vector< int > program_tones (int program, ToneMap map, int bank) const
 Resolves a program change to the tone numbers that sound for an ordinary poly note.
int alternate_tone (int program, ToneMap map, int bank) const
 The mono/solo alternate-articulation tone for a program, or -1.
int program_to_tone (int program, ToneMap map, int bank) const
 Resolves a program change to a single tone number, or -1 when unassigned.
ResolvedTone resolve (int tone_number, int note, int velocity) const
 Resolves a played note against a tone.
ResolvedTone resolve_midi (int program, int note, int velocity, ToneMap map, int bank) const
 Resolves a MIDI program change and played note in one step.

Static Public Attributes

static constexpr int melodic_space_end = 0x4000
 Highest tone number in the melodic space, exclusive.
static constexpr int drum_space_start = 0x4000
 First tone number in the drum space.
static constexpr int alternate_space_start = 0x6000
 First tone number in the alternate-articulation space.
static constexpr int indirect_only_flag = 0x8000
 Tone numbers at or above this are reachable only indirectly.
static constexpr int multisample_stride = 0x8C
 Bytes per multisample record.
static constexpr int alternate_stride = 0x18
 Bytes per alternate-articulation record.
static constexpr int unassigned = 0xFFFF
 Value meaning "unassigned" in the third lookup level.

Detailed Description

The static patch directory: resolves a program change and a played note to the waves that sound, entirely from ROM tables with no engine involved.

The chain is (map, bank, program) -> tone# -> partial -> multisample -> key/velocity zone -> wave# -> ROM coordinates. The program lookup is three levels deep and its result carries three distinct tone spaces, which is the part most likely to be got wrong.

Constructor & Destructor Documentation

◆ PatchDirectory()

ts::PatchDirectory::PatchDirectory ( const TableSet & tables)
explicit

Creates a directory over a loaded table set, which must outlive it.

Member Function Documentation

◆ tone_count()

int ts::PatchDirectory::tone_count ( ) const
inlinenodiscardnoexcept

Number of tone records in the melodic table.

◆ multisample_count()

int ts::PatchDirectory::multisample_count ( ) const
inlinenodiscardnoexcept

Number of multisample records.

◆ wave_count()

int ts::PatchDirectory::wave_count ( ) const
inlinenodiscardnoexcept

Number of wave descriptors.

◆ alternate_count()

int ts::PatchDirectory::alternate_count ( ) const
inlinenodiscardnoexcept

Number of alternate-articulation records.

◆ wave()

std::optional< WaveDescriptor > ts::PatchDirectory::wave ( int wave_number) const
nodiscard

Reads a wave descriptor, or nothing if the number is out of range.

◆ tone()

std::optional< Tone > ts::PatchDirectory::tone ( int tone_number) const
nodiscard

Reads a tone record, or nothing if the number is out of range.

◆ tone_level()

int ts::PatchDirectory::tone_level ( int tone_number) const
nodiscard

Reads a tone's master level from its header.

◆ half_damper()

bool ts::PatchDirectory::half_damper ( int tone_number) const
nodiscard

Whether a tone responds to half-damper (continuous CC64).

Tone header byte 0x0d bit 2, copied to the part on tone select. Set on exactly 57 of the 2363 tones — the piano family — matching the hardware, where half-pedal is a piano feature. Every other tone's pedal is quantised to fully up or fully down.

◆ partial_by_slot()

PartialParameters ts::PatchDirectory::partial_by_slot ( int tone_number,
int slot ) const
nodiscard

Reads a partial block by its slot rather than its position among the present partials.

Tone::partials drops empty slots, so its indices are not slot numbers. Anything correlating against the reference or a voice dump needs the slot.

◆ multisample_wave()

std::optional< int > ts::PatchDirectory::multisample_wave ( int multisample,
int transposed_key ) const
nodiscard

Selects the wave a multisample yields for a transposed key, or nothing when silent.

An empty bottom zone means the note is below the multisample's sampled range, and the partial is silent — it must not fall through to the neighbouring wave. The engine does statically down-extrapolate, but the resulting voice is terminated, so the audible result is silence. Slap Bass 2 below note 40 is the case that proved it.

◆ zone_level()

int ts::PatchDirectory::zone_level ( int multisample,
int key,
int key_center ) const
nodiscard

Reads the per-key-zone level, the second attenuation in the TVA base-level chain.

◆ multisample_zones()

std::vector< MultisampleZone > ts::PatchDirectory::multisample_zones ( int multisample) const
nodiscard

Lists a multisample's key zones in transposed-key space, low to high.

◆ tone_zones()

std::optional< std::string > ts::PatchDirectory::tone_zones ( int tone_number,
std::vector< ToneZone > & zones ) const
nodiscard

Builds the full static zone map for a tone, with key bounds converted back to MIDI notes.

Returns the tone's name, or nothing when the tone is undefined. This is the static map, so it applies only the key centre — not the partial's key transpose, which belongs to the played-note path in resolve.

◆ lut3_raw()

std::optional< int > ts::PatchDirectory::lut3_raw ( int program,
ToneMap map,
int bank ) const
nodiscard

The raw unsigned word the three-level program lookup yields.

It must be read as unsigned. Reading it signed and bailing on the sign bit is what once made four string patches look unassigned — the 0x8000 bit marks a tone reachable only through an alternate-articulation entry, not a missing one.

◆ lut3_resolved()

std::optional< int > ts::PatchDirectory::lut3_resolved ( int program,
ToneMap map,
int bank ) const
nodiscard

The three-level lookup with the GS capital-tone fallback applied.

A bank select picks a variation. When the selected variation has no entry for the program, a Sound Canvas does not fall silent — it sounds the capital tone at bank 0 instead. Reproducing that is what lets a file play a note on a variation bank the ROM never filled in, which is exactly what one honky-tonk part in passport.mid does: it selects bank 5, whose program-3 slot is empty, and every note would otherwise be dropped.

◆ alternate()

std::optional< AlternateEntry > ts::PatchDirectory::alternate ( int index) const
nodiscard

Decodes an entry of the alternate-articulation table, indexed by tone# - 0x6000.

◆ program_tones()

std::vector< int > ts::PatchDirectory::program_tones ( int program,
ToneMap map,
int bank ) const
nodiscard

Resolves a program change to the tone numbers that sound for an ordinary poly note.

Returned as a list because the caller walks it, but an alternate-articulation entry's two references are not layers played together: the second is a conditional articulation that only the mono/solo path can reach.

◆ alternate_tone()

int ts::PatchDirectory::alternate_tone ( int program,
ToneMap map,
int bank ) const
nodiscard

The mono/solo alternate-articulation tone for a program, or -1.

Not rendered: reproducing it needs the part's mono-mode flag and the inter-note timing test.

◆ program_to_tone()

int ts::PatchDirectory::program_to_tone ( int program,
ToneMap map,
int bank ) const
nodiscard

Resolves a program change to a single tone number, or -1 when unassigned.

◆ resolve()

ResolvedTone ts::PatchDirectory::resolve ( int tone_number,
int note,
int velocity ) const
nodiscard

Resolves a played note against a tone.

◆ resolve_midi()

ResolvedTone ts::PatchDirectory::resolve_midi ( int program,
int note,
int velocity,
ToneMap map,
int bank ) const
nodiscard

Resolves a MIDI program change and played note in one step.

Member Data Documentation

◆ melodic_space_end

int ts::PatchDirectory::melodic_space_end = 0x4000
staticconstexpr

Highest tone number in the melodic space, exclusive.

◆ drum_space_start

int ts::PatchDirectory::drum_space_start = 0x4000
staticconstexpr

First tone number in the drum space.

◆ alternate_space_start

int ts::PatchDirectory::alternate_space_start = 0x6000
staticconstexpr

First tone number in the alternate-articulation space.

◆ indirect_only_flag

int ts::PatchDirectory::indirect_only_flag = 0x8000
staticconstexpr

Tone numbers at or above this are reachable only indirectly.

◆ multisample_stride

int ts::PatchDirectory::multisample_stride = 0x8C
staticconstexpr

Bytes per multisample record.

◆ alternate_stride

int ts::PatchDirectory::alternate_stride = 0x18
staticconstexpr

Bytes per alternate-articulation record.

◆ unassigned

int ts::PatchDirectory::unassigned = 0xFFFF
staticconstexpr

Value meaning "unassigned" in the third lookup level.


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