Deluge Firmware 1.3.0
Build date: 2026.11.13
Loading...
Searching...
No Matches
envelope_curve.h
1/*
2 * Copyright © 2026 Synthstrom Audible Limited
3 *
4 * This file is part of The Synthstrom Audible Deluge Firmware.
5 *
6 * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software Foundation,
8 * either version 3 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with this program.
15 * If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include <algorithm>
21#include <bit>
22#include <cmath>
23#include <cstdint>
24
25namespace deluge::modulation {
26
29[[gnu::always_inline]] constexpr float fastExp2(float x) {
30 int32_t whole = static_cast<int32_t>(x); // truncates toward zero
31 float frac = x - static_cast<float>(whole);
32 if (frac < 0.f) { // floor for negative inputs, so frac is in [0, 1)
33 frac += 1.f;
34 whole -= 1;
35 }
36 // Weighted least-squares fit of 2^f to polynomial on [0, 1]
37 float p = 0.00185888436f;
38 p = p * frac + 0.00903089405f;
39 p = p * frac + 0.0557939352f;
40 p = p * frac + 0.240164314f;
41 p = p * frac + 0.693151594f;
42 p = p * frac + 1.f;
43 return std::bit_cast<float>(std::bit_cast<int32_t>(p) + (whole << 23));
44}
45
53class EnvelopeCurve {
54public:
55 constexpr EnvelopeCurve() : EnvelopeCurve(0.f) {}
56 constexpr explicit EnvelopeCurve(float k)
57 : k_(k), floor_(fastExp2(-k)), scale_((k == 0.f) ? 1.f : 1.f / (1.f - fastExp2(-k))) {}
58
60 [[gnu::always_inline]] constexpr float falling(float x) const {
61 if (k_ == 0.f) {
62 return 1.f - x;
63 }
64 return (fastExp2(-k_ * x) - floor_) * scale_;
65 }
66
68 [[gnu::always_inline]] constexpr float rising(float x) const { return 1.f - falling(x); }
69
71 float risingInverse(float value) const {
72 value = std::clamp(value, 0.f, 1.f);
73 if (k_ == 0.f) {
74 return value;
75 }
76 float x = -std::log2((1.f - value) / scale_ + floor_) / k_;
77 return std::clamp(x, 0.f, 1.f);
78 }
79
80 constexpr float k() const { return k_; }
81
82private:
83 float k_;
84 float floor_; // 2^-k, the unnormalised curve's value at x = 1
85 float scale_; // 1 / (1 - 2^-k)
86};
87
90 EnvelopeCurve attack{4.f};
91 EnvelopeCurve decay{8.f};
92 EnvelopeCurve release{8.f};
93
97 static constexpr int32_t kMinCurve = -50;
98 static constexpr int32_t kMaxCurve = 50;
99 static constexpr int32_t kClassicCurve = 32;
100 static constexpr EnvelopeShape fromCurve(int32_t curve) {
101 float k = static_cast<float>(curve) * 0.25f;
102 return {.attack = EnvelopeCurve{k * 0.5f}, .decay = EnvelopeCurve{k}, .release = EnvelopeCurve{k}};
103 }
104};
105
107inline constexpr EnvelopeShape kClassicEnvelopeShape{};
109inline constexpr EnvelopeShape kAudioClipEnvelopeShape{.release = EnvelopeCurve{4.f}};
110
111enum class EnvelopeMode : uint8_t {
113 ADSR,
117 AHD,
121 AR,
124 MPE,
125};
126constexpr int32_t kNumEnvelopeModes = 4;
127
131enum class EnvelopeFollow : uint8_t {
132 Z,
133 Y,
134};
135constexpr int32_t kNumEnvelopeFollows = 2;
136
139 EnvelopeMode mode{EnvelopeMode::ADSR};
140 EnvelopeFollow follow{EnvelopeFollow::Z};
141 int8_t curve{EnvelopeShape::kClassicCurve};
142 EnvelopeShape shape{}; // derived from curve; call setCurve() rather than writing either directly
143
144 constexpr void setCurve(int32_t newCurve) {
145 curve = static_cast<int8_t>(std::clamp(newCurve, EnvelopeShape::kMinCurve, EnvelopeShape::kMaxCurve));
146 shape = EnvelopeShape::fromCurve(curve);
147 }
148 constexpr bool isDefault() const {
149 return mode == EnvelopeMode::ADSR && follow == EnvelopeFollow::Z && curve == EnvelopeShape::kClassicCurve;
150 }
151};
152
153inline constexpr EnvelopeConfig kAudioClipEnvelopeConfig{.shape = kAudioClipEnvelopeShape};
154
155char const* envelopeModeToString(EnvelopeMode mode);
156EnvelopeMode stringToEnvelopeMode(char const* string);
157char const* envelopeFollowToString(EnvelopeFollow follow);
158EnvelopeFollow stringToEnvelopeFollow(char const* string);
159
160} // namespace deluge::modulation
Definition envelope_curve.h:53
constexpr float falling(float x) const
x in [0, 1]
Definition envelope_curve.h:60
constexpr float rising(float x) const
The rising mirror image: 0 at x = 0, 1 at x = 1. With k > 0 it's fast at the start,...
Definition envelope_curve.h:68
float risingInverse(float value) const
Inverse of rising(): where on the segment the curve reaches value. Not for the render path (uses log2...
Definition envelope_curve.h:71
The shape of each stage of an envelope.
Definition envelope_curve.h:89
A sound's settings for one of its envelopes. Not a param: it doesn't vary per clip and can't be modul...
Definition envelope_curve.h:138
The shape of each stage of an envelope.
Definition envelope_curve.h:89
static constexpr int32_t kMinCurve
Definition envelope_curve.h:97