|
Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
|
The drum kit records: for each of 128 keys, which tone sounds and how it is levelled, tuned, panned and grouped. More...
#include <tabulasonora/drum_kit_table.hpp>
Public Member Functions | |
| DrumKitTable (const RomImage &rom) | |
| Creates the table by reading the drum regions out of the ROM image. | |
| int | kit_count () const noexcept |
| Number of kit records reachable through the program map. | |
| int | map_row (int internal_bank) const noexcept |
| Maps an internal bank code to a drum map row; standard GM/GS drum parts use 0x04. | |
| std::optional< int > | kit_for_program (int program, int row=0) const noexcept |
| Resolves a drum program to its kit record index, or nothing for an undefined program. | |
| std::string | kit_name (int kit) const |
| The kit's name, as the ROM record carries it, trimmed of trailing spaces. | |
| DrumKey | key (int note, int kit=0) const |
| Reads one key's settings from a kit; kit 0 is GM Standard. | |
Static Public Member Functions | |
| static std::optional< int > | row_for_map (ToneMap map) noexcept |
| The drum map row a vintage's tone map selects, or nothing where no measurement pins one. | |
| static double | coarse_pitch_ratio (int pitch) noexcept |
| The playback ratio the kit's coarse-pitch plane implies. | |
| static constexpr double | level_gain (int level) noexcept |
| The gain the kit level contributes, as amplitude. | |
Static Public Attributes | |
| static constexpr int | kit_stride = 0x50C |
| Bytes per kit record. | |
| static constexpr int | key_count = 128 |
| Keys per kit. | |
| static constexpr int | map_row_count = 6 |
| Rows in the drum program map. | |
| static constexpr std::int64_t | kit_index_offset = 0x19F21B0 |
| File offset of the table mapping a level-two index to a kit record index. | |
The drum kit records: for each of 128 keys, which tone sounds and how it is levelled, tuned, panned and grouped.
Three things about drums are easy to get wrong, and all three are settled by measurement:
|
explicit |
Creates the table by reading the drum regions out of the ROM image.
|
inlinenodiscardnoexcept |
Number of kit records reachable through the program map.
|
staticnodiscardnoexcept |
The drum map row a vintage's tone map selects, or nothing where no measurement pins one.
The module takes this from the part's internal bank code, and that translation is not reversed — but which row each vintage ends up on is observable. Driving the DLL with each tone map in turn and reading back the tone it resolves for program 0 on the drum part: SC-55 selects row 3 (kit 59), SC-88 row 2 (kit 47), SC-88Pro row 1 and SC-8820 row 0.
Rows 4 and 5 are not vintages: row 4 is the XG kit set — Standard 1/2, Room, Rock, Electro, Analog, Jazz, Brush, Classic on programs 0/1/8/16/24/25/32/40/48, plus SFX 1 and 2 on 120 and 121 — and row 5 is GM2's. ToneMap::xg selects row 4; a host that wants row 5 sets it itself.
|
nodiscardnoexcept |
Maps an internal bank code to a drum map row; standard GM/GS drum parts use 0x04.
|
nodiscardnoexcept |
Resolves a drum program to its kit record index, or nothing for an undefined program.
The engine leaves the kit unchanged rather than silencing the part, which is why this is an absence rather than a zero.
|
nodiscard |
The kit's name, as the ROM record carries it, trimmed of trailing spaces.
Twelve bytes at +0x500 of the record, the same field the module's rhythm-set name dump reads. A mixer that shows "kit 73" is showing an index nobody chose; the record has been carrying "Analog Kit" all along.
Three things not to assume about the result, all measured and written up in FINDINGS under "Kit names, and three things not to assume about them":
Empty for a kit index outside the table.
|
nodiscard |
Reads one key's settings from a kit; kit 0 is GM Standard.
Throws std::out_of_range if the note is outside 0–127.
|
staticnodiscardnoexcept |
The playback ratio the kit's coarse-pitch plane implies.
Only correct for a tone that key-follows at 50%, which is what the SC-88 and SC-8820 standard kits use and what this constant was measured from. The general rule is per-tone: the SC-55 kits and the Electronic family follow at 100% and move a whole semitone per unit, so they come out half as far under this reading. The engine paths use PitchChain::drum_pitch_milli_semitones; this remains for callers that want the old scalar.
|
inlinestaticnodiscardconstexprnoexcept |
The gain the kit level contributes, as amplitude.
The kit level is not part of the per-voice amplitude — that matches the engine exactly without it. It enters downstream in the part-volume computation, where the result is squared, so it acts as (level / 127)^2.
|
staticconstexpr |
Bytes per kit record.
|
staticconstexpr |
Keys per kit.
|
staticconstexpr |
Rows in the drum program map.
Six, not the two this read for a long time. Each row maps a program to a kit, and program 0 resolves to kit 0, 38, 47, 59, 68 and 79 across rows 0 to 5. Reading two rows put every kit from 47 up out of reach entirely.
Measured against the DLL: driven with the SC-55 tone map on program 0 of the drum part, the engine resolves a tone for all 61 sounding keys that kit 59 reproduces exactly, 61 of 61 — and kit 59 is only reachable through row 3. The best any kit the two-row read could see managed was 30 of 61.
|
staticconstexpr |
File offset of the table mapping a level-two index to a kit record index.
Unlike the other three drum regions this one is not recorded in the manifest, so it is pinned here. It is DAT_1819f31b0 in the decompile.