Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
track_mixer_program.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <algorithm>
8#include <cmath>
9#include <cstddef>
10#include <cstdint>
11
12namespace pulp::playback {
13
17inline constexpr float kMaximumTrackMixerGain = 64.0f;
19 "the render path must bound an automated fader by exactly the range the "
20 "document accepts for a static one, or a curve could ask for a gain the "
21 "model refuses to store");
22
29 float gain_linear = 1.0f;
30 float pan = 0.0f;
33
37 bool transparent() const noexcept {
38 return gain_automation == nullptr && pan_automation == nullptr && gain_linear == 1.0f &&
39 pan == 0.0f;
40 }
41};
42
50constexpr float clamped_track_gain(float value) noexcept {
51 if (!(value >= 0.0f))
52 return value <= 0.0f ? 0.0f : 1.0f;
53 return value > kMaximumTrackMixerGain ? kMaximumTrackMixerGain : value;
54}
55
56constexpr float clamped_track_pan(float value) noexcept {
57 if (!(value >= -1.0f))
58 return value <= -1.0f ? -1.0f : 0.0f;
59 return value > 1.0f ? 1.0f : value;
60}
61
66 float left = 1.0f;
67 float right = 1.0f;
68};
69
71 return {pan > 0.0f ? 1.0f - pan : 1.0f, pan < 0.0f ? 1.0f + pan : 1.0f};
72}
73
77constexpr float track_mixer_channel_gain(float pan, std::size_t channel,
78 std::size_t channel_count) noexcept {
79 if (channel_count < 2)
80 return 1.0f;
81 const auto gains = track_mixer_channel_gains(pan);
82 return channel % 2u == 0u ? gains.left : gains.right;
83}
84
92 public:
93 void reset(const AutomationProgram* program, float constant) noexcept {
94 program_ = program && !program->empty() ? program : nullptr;
95 constant_ = constant;
96 segment_index_ = 0;
97 cold_ = true;
98 }
99
101 bool automated() const noexcept {
102 return program_ != nullptr;
103 }
104
108 void restart(const timebase::CompiledTempoMap& map) noexcept {
109 if (!program_)
110 return;
111 tempo_.reset(map);
112 segment_index_ = 0;
113 cold_ = true;
114 }
115
116 float value_at(timebase::SamplePosition sample) noexcept {
117 if (!program_)
118 return constant_;
119 segment_index_ = select_automation_segment(*program_, sample, cold_, segment_index_);
120 const auto tick = cold_ ? tempo_.seek(sample).tick : tempo_.advance(sample).tick;
121 cold_ = false;
122 if (sample < program_->segments().front().start_sample)
123 return program_->leading_value();
124 return evaluate_automation_segment(*program_, segment_index_, sample, tick);
125 }
126
129 float value_at_tick(long double tick) noexcept {
130 if (!program_)
131 return constant_;
132 const auto segments = program_->segments();
133 if (cold_) {
134 const auto found = std::upper_bound(
135 segments.begin(), segments.end(), tick,
136 [](long double value, const AutomationProgramSegment& segment) {
137 return value <
138 static_cast<long double>(segment.end_tick.value);
139 });
140 segment_index_ =
141 found == segments.end()
142 ? segments.size() - 1u
143 : static_cast<std::size_t>(found - segments.begin());
144 cold_ = false;
145 } else {
146 while (segment_index_ + 1u < segments.size() &&
147 tick >= static_cast<long double>(
148 segments[segment_index_].end_tick.value))
149 ++segment_index_;
150 }
151 if (tick < static_cast<long double>(segments.front().start_tick.value))
152 return program_->leading_value();
153 const auto& segment = segments[segment_index_];
154 if (segment.start_tick == segment.end_tick)
155 return segment.end_value;
156 const auto start = static_cast<long double>(segment.start_tick.value);
157 const auto end = static_cast<long double>(segment.end_tick.value);
158 if (tick <= start)
159 return segment.start_value;
160 if (tick >= end)
161 return segment.end_value;
162 if (segment.interpolation == timeline::AutomationInterpolation::Hold)
163 return segment.start_value;
164 const auto fraction = std::clamp((tick - start) / (end - start), 0.0L, 1.0L);
165 const auto bend = static_cast<long double>(segment.curvature);
166 const auto quadratic =
167 bend >= 0.0L ? fraction * fraction : std::fma(-fraction, fraction, 2.0L * fraction);
168 const auto shaped = std::fma(std::abs(bend), quadratic - fraction, fraction);
169 return static_cast<float>(std::fma(static_cast<long double>(segment.end_value) -
170 static_cast<long double>(segment.start_value),
171 shaped, static_cast<long double>(segment.start_value)));
172 }
173
174 private:
175 const AutomationProgram* program_ = nullptr;
176 float constant_ = 1.0f;
178 std::size_t segment_index_ = 0;
179 bool cold_ = true;
180};
181
182} // namespace pulp::playback
Immutable tick- and sample-domain form of one automation lane.
Definition automation_program.hpp:55
Streams one mixer control's value in timeline-sample order without allocating.
Definition track_mixer_program.hpp:91
bool automated() const noexcept
Whether any position can yield something other than the constant.
Definition track_mixer_program.hpp:101
float value_at_tick(long double tick) noexcept
Evaluates a host-projected fractional document tick without first quantizing it through the integer s...
Definition track_mixer_program.hpp:129
float value_at(timebase::SamplePosition sample) noexcept
Definition track_mixer_program.hpp:116
void restart(const timebase::CompiledTempoMap &map) noexcept
Begins a fresh monotonic run.
Definition track_mixer_program.hpp:108
void reset(const AutomationProgram *program, float constant) noexcept
Definition track_mixer_program.hpp:93
Definition compiled_tempo_map.hpp:91
Definition compiled_tempo_map.hpp:164
constexpr float kMaximumTrackGainLinear
Largest admitted authored linear track gain.
Definition recording.hpp:146
@ Hold
Definition automation_curve.hpp:21
Definition audio_renderer.hpp:20
constexpr TrackMixerChannelGains track_mixer_channel_gains(float pan) noexcept
Definition track_mixer_program.hpp:70
constexpr float clamped_track_pan(float value) noexcept
Definition track_mixer_program.hpp:56
std::size_t select_automation_segment(const AutomationProgram &program, timebase::SamplePosition sample, bool cold, std::size_t segment_index) noexcept
Selects the segment governing an absolute timeline sample.
float evaluate_automation_segment(const AutomationProgram &program, std::size_t segment_index, timebase::SamplePosition sample, timebase::TickPosition tick) noexcept
The authored value of one already-selected segment at an exact tick.
constexpr float track_mixer_channel_gain(float pan, std::size_t channel, std::size_t channel_count) noexcept
The balance multiplier for one output channel.
Definition track_mixer_program.hpp:77
constexpr float kMaximumTrackMixerGain
Mirrors timeline::kMaximumTrackGainLinear.
Definition track_mixer_program.hpp:17
constexpr float clamped_track_gain(float value) noexcept
Constrains a streamed control value to the range the document accepts for the same control as a const...
Definition track_mixer_program.hpp:50
Definition automation_program.hpp:38
Per-side multipliers for a stereo balance.
Definition track_mixer_program.hpp:65
float right
Definition track_mixer_program.hpp:67
float left
Definition track_mixer_program.hpp:66
The compiled form of a track's own level and stereo placement.
Definition track_mixer_program.hpp:28
const AutomationProgram * gain_automation
Definition track_mixer_program.hpp:31
float gain_linear
Definition track_mixer_program.hpp:29
const AutomationProgram * pan_automation
Definition track_mixer_program.hpp:32
bool transparent() const noexcept
Whether this mixer can be skipped entirely.
Definition track_mixer_program.hpp:37
float pan
Definition track_mixer_program.hpp:30
Definition rational_time.hpp:9