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

A native C++20 implementation of the Roland Sound Canvas VA synth voice. It reads the wave ROM and synth tables out of SCCore.dll as a data file — the DLL is never loaded as code — so the engine is portable and has no Windows dependency at all.

MIDI file in, audio out, on one core.

auto rom = ts::RomImage::open("SCCore.dll");
ts::NoteRenderer notes(rom);
ts::ToneGenerator engine(notes, {
.map = ts::ToneMap::sc55, // the vintage to resolve programs against
});
auto player = ts::SequencePlayer::from_file(engine, "song.mid");
auto result = player.render_to_end(/*tail_seconds=*/2.2);
Renders one note by assembling the whole voice chain: sampler, pitch, filter, amplitude and pan.
Definition note_renderer.hpp:42
static RomImage open(const std::string &path, RomVerification verification=RomVerification::full, const TableManifest *manifest=nullptr)
Opens and verifies an SCCore.dll.
static SequencePlayer from_file(ToneGenerator &generator, const std::filesystem::path &path)
Reads a music file and creates a player for it.
The real-time engine: MIDI in, audio out, rendered a block at a time.
Definition tone_generator.hpp:100
@ sc55
Definition patch_directory.hpp:23
static constexpr int unlimited_polyphony
Asks for polyphony that grows on demand instead of stealing.
Definition tone_generator.hpp:19

result.left and result.right are the finished mix at ts::ToneGenerator::sample_rate.

Where to start

Getting started Supplying the DLL, the first render, driving the engine live
Architecture How a note becomes sound, and where the clock domains sit
In the browser The engine compiled to WebAssembly, and the app around it
Verification What is proven, how, and against which oracle
Specification The normative specification this engine is built to
API reference Every public type

Or hear it first. The same engine is live at tabula-sonora.kddlb.cl — a player for Standard MIDI Files and a live instrument, running in the browser with nothing to build. It needs the same DLL this library does, and reads it in the page.

The library, by area

The headers under include/tabulasonora divide the way the engine does. Everything is in namespace ts.

area what it does start at
ROM Opens the DLL as data and slices the tables out of it ts::RomImage, ts::TableManifest, ts::TableSet, ts::WaveRom
Patches Turns a program change into a tone, and a note into a wave ts::PatchDirectory, ts::Tone, ts::DrumKitTable, ts::WaveDescriptor
DSP The per-voice signal path ts::Sampler, ts::Interpolator, ts::StateVariableFilter, ts::PitchChain, ts::TvaChain, ts::TvfChain, ts::LfoEngine
Modulation What the stream can move while a note sounds ts::ControlMatrix, ts::PartModifiers, ts::PitchRamp, ts::ControlDecode
Effects The three send effects, the part EQ, and their coefficient tables ts::Reverb, ts::Chorus, ts::SystemDelay, ts::Equalizer, ts::EffectPresets, ts::EffectProgrammer
MIDI Reading a Standard MIDI File into something renderable ts::MidiEvent, ts::Sequence
The engine The block loop, 32 samples at a time, and the parts it drives ts::ToneGenerator, ts::Part, ts::VoicePool, ts::FrameRing
Render Playing a file through it, live or into a buffer ts::SequencePlayer, ts::RenderOptions, ts::NoteRenderer, ts::wav::write

You must supply the DLL

This engine is inert on its own. Roland's wave ROM and tables are not redistributed here — see NOTICE.md, which also covers what little is. You need a legally obtained SCCore.dll from a Sound Canvas VA installation you have licensed, pinned to one exact build: the one shipped in SOUND Canvas VA 1.1.6.

field value
SCVA release 1.1.6
size 27,347,456 bytes
SHA-256 117e6aa147a96fbde5e10d2caf16c89965acc1e44235fd245992216cc620bdb1
PE timestamp 2019-10-30

A different build moves every table offset, so ts::RomImage refuses to open one.

The release number tells you which installer to look in and nothing more. The DLL has no version resource, so 1.1.6 cannot be read from the file and is not verified; ts::DllIdentity records it as provenance while the hash does the identifying.

Nothing Roland-derived is committed to this repository. assets/manifest.json is the offset map, not the data. The effect coefficients are no exception: the reverb and chorus numbers, once thought to exist only in the running engine's state, are encoded in the DLL, and ts::EffectProgrammer decodes them from your own copy.

Why it exists

Sound Canvas VA was withdrawn from sale in September 2024. This is a preservation and interoperability effort: music written for the Sound Canvas should keep playing after the software that played it stops being available, on platforms the original plugin never supported.

This repository is the reference implementation. It began as a port of the C# engine, which is now archived and kept as a record; both are built to the specification recovered from the DLL. Being C++ with a BSD 3-Clause licence means a host that cannot take a .NET runtime — including GPL software such as Cog — can embed it directly.

And the plugin is itself a port: SCCore.dll carries the SC-8820's own mask ROMs and reproduces its voice down to the fixed-point arithmetic. The lineage runs hardware → plugin → here, and this end of it is not confined to what the previous two could do — it will run 64 parts over four ports where the module has 32 and the shipped DLL reaches 16, and will grow its voice pool past the hardware's 64 rather than steal. Everything of that kind is opt-in, because the default has a job: match the module, and exceed it only on request.

How this was written

The bulk of this code was generated by large language models, working from the specification and under review. That is not a footnote: it is how every commit in the repository was made, and git log is the record — each one carries a Co-Authored-By trailer naming the model that wrote it, so git blame answers the question for any particular line. Claude Fable 5 and Claude Opus 5, to date.

git log --format='%(trailers:key=Co-Authored-By,valueonly,unfold)' | sort | uniq -c

What makes that acceptable is the property the rest of these pages are about: nothing in this engine is trusted because it looks right. A port whose bar is a SHA-256 match against another implementation's output is checkable by machine, and the phases were gated that way — a phase did not start until the previous one was byte-exact. Verification sets out what is proven, against which oracle, and where this engine knowingly differs. The constants were recovered by measurement, and the claims resting on thin evidence are tagged as such rather than smoothed over.