|
Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
|
A four-segment envelope with a release, evaluated at any sample position. More...
#include <tabulasonora/segment_envelope.hpp>
Public Member Functions | |
| SegmentEnvelope (const EnvelopeMachine &machine, std::span< const double > targets, std::span< const double > segment_samples, std::span< const bool > linear, double release_target, double release_samples, bool release_linear, double after_release, std::int64_t control_tick_samples) | |
| Builds an envelope from decoded segment parameters. | |
| std::int64_t | note_off_sample () const noexcept |
| Where the release starts, or -1 while the note is still held. | |
| std::int64_t | release_samples () const noexcept |
| The release duration in samples, at least one. | |
| void | note_off (std::int64_t sample, int damper=0) |
| Starts the release at a sample position. | |
| double | value_at (std::int64_t sample) const noexcept |
| The envelope's value at a sample position relative to note-on. | |
| bool | is_finished (std::int64_t sample) const noexcept |
| Whether the envelope has reached silence and cannot leave it. | |
| std::span< const double, segment_count > | targets () const noexcept |
| The gain each segment ramps to, in the order they run. | |
| std::span< const std::int64_t, segment_count > | segment_ends () const noexcept |
| Where each segment ends, in samples from note-on. | |
Static Public Member Functions | |
| static std::int64_t | defer_to_control_tick (std::int64_t sample, std::int64_t control_tick_samples) noexcept |
| The control tick an event landing at a sample is acted on. | |
Static Public Attributes | |
| static constexpr int | segment_count = 4 |
| Segments before the release. | |
A four-segment envelope with a release, evaluated at any sample position.
The TVA and TVF envelopes have the same shape — four segments that run from note-on, a hold at the last target, then a release that starts wherever note-off caught it. What differs is only how the targets and durations are decoded, which is the job of TvaChain and TvfChain.
The trajectory is a pure function of the sample index and the note-off position, so the offline renderer and the real-time voice loop read the same envelope by construction rather than by agreement between two transcriptions. Nothing here is advanced by rendering: a block renderer evaluates it per sample, and an offline one fills an array.
| ts::SegmentEnvelope::SegmentEnvelope | ( | const EnvelopeMachine & | machine, |
| std::span< const double > | targets, | ||
| std::span< const double > | segment_samples, | ||
| std::span< const bool > | linear, | ||
| double | release_target, | ||
| double | release_samples, | ||
| bool | release_linear, | ||
| double | after_release, | ||
| std::int64_t | control_tick_samples ) |
Builds an envelope from decoded segment parameters.
control_tick_samples is the grid note-off is acted on. A tick of one releases at the note after note-off, which is as close to releasing immediately as this gets and is not what the engine does; see note_off.
Throws std::invalid_argument if a parameter array is not four long.
|
inlinenodiscardnoexcept |
Where the release starts, or -1 while the note is still held.
|
inlinenodiscardnoexcept |
The release duration in samples, at least one.
| void ts::SegmentEnvelope::note_off | ( | std::int64_t | sample, |
| int | damper = 0 ) |
Starts the release at a sample position.
Later calls are ignored.
The release departs from the value the envelope had reached, not from a segment target, which is what makes a note released mid-attack decay from where it actually was.
Note-off does not take effect at the sample it lands on: the engine sees it at its next control tick, so the envelope holds for the rest of the current tick first. Measured by sweeping the hold time past a tick boundary — note-off anywhere in 1000–1008 ms produced the same release, which then stepped a whole tick later at 1010 ms. Releasing immediately instead runs the tail 0–10 ms early; that is inaudible on a pad but a large fraction of a short release, and the Accordion reaches -20 dB in 9 ms against the engine's 23 ms.
damper is the CC64 value at release, 1–0x3f for a half-pressed pedal on a half-damper tone, else zero. The engine writes it into the release ramp's rate-scale byte, which multiplies the rate word by roughly 1 - v/128, so a half-pressed pedal lengthens the release by the reciprocal. Only the 57 piano tones carry the capability; every other tone's pedal value is quantised to 0 or 0x7f before it can get here.
|
staticnodiscardnoexcept |
The control tick an event landing at a sample is acted on.
The following tick, even when the event lands exactly on a boundary: that tick's update has already run by the time the event is latched. Measured on the DLL — a note-off at 1010 ms, exactly a tick, released at 1020 ms, while one at 1008 ms released at 1010 ms. So the deferral spans one full tick and is never zero.
|
nodiscardnoexcept |
The envelope's value at a sample position relative to note-on.
|
inlinenodiscardnoexcept |
Whether the envelope has reached silence and cannot leave it.
Two ways to get there. The release running out is the usual one. The other is an envelope whose last segment ends at zero: it has nowhere left to go, and a voice that never receives a note-off — every drum key but a handful — would otherwise sit in the pool forever holding silence. The module frees those when the gain reaches zero, which is the same rule.
|
inlinenodiscardnoexcept |
The gain each segment ramps to, in the order they run.
Exposed so a built envelope can be read back and compared against the module's own, which scdec tvatrace lifts out of voice+0x16/0x1d2/0x1d4/0x1d6 after note-on. Inferring an envelope error from the audio it produces is guesswork; this makes it arithmetic.
|
inlinenodiscardnoexcept |
Where each segment ends, in samples from note-on.
The module's counterpart is a duration in milliseconds at voice+0x12/0x1c6/0x1c8/0x1ca, so these are cumulative and those are not.
|
staticconstexpr |
Segments before the release.