3#include "detail/audio_range.hpp"
20template <std::
size_t Inputs, std::
size_t Outputs>
23 Inputs <= std::numeric_limits<std::size_t>::max() / Outputs;
41template <
typename SampleType = float, std::size_t MaxInputs = 16, std::size_t MaxOutputs = 16,
42 typename Allocator = std::allocator<SampleType>>
46 static_assert(std::is_floating_point_v<SampleType>);
51 explicit AudioMatrixMixerT(
const Allocator& allocator = Allocator{}) : scratch_(allocator) {}
55 max_block_size > std::numeric_limits<std::size_t>::max() / MaxInputs)
58 if (scratch_samples > scratch_.max_size())
60 std::vector<SampleType, Allocator> replacement(scratch_.get_allocator());
61#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
64 replacement.assign(scratch_samples, SampleType{0});
65#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
66 }
catch (
const std::bad_alloc&) {
70 scratch_.swap(replacement);
77 if (inputs == 0 || outputs == 0 || inputs > MaxInputs || outputs > MaxOutputs)
79 if (inputs == inputs_ && outputs == outputs_)
96 bool set_gain(std::size_t output, std::size_t input, SampleType
gain)
noexcept {
105 std::size_t ramp_samples)
noexcept {
106 if (output >= outputs_ || input >= inputs_)
108 if (!std::isfinite(
gain)) {
109 gain = SampleType{0};
110 ++nonfinite_gain_count_;
113 ++out_of_range_gain_count_;
116 auto& cell = cells_[index(output, input)];
117 cell.start = cell.current;
119 cell.total = ramp_samples;
121 if (ramp_samples == 0) {
128 SampleType
gain(std::size_t output, std::size_t input)
const noexcept {
129 return output < outputs_ && input < inputs_ ? cells_[index(output, input)].current
132 SampleType
target_gain(std::size_t output, std::size_t input)
const noexcept {
133 return output < outputs_ && input < inputs_ ? cells_[index(output, input)].target
143 return max_block_size_;
146 return nonfinite_gain_count_;
149 return out_of_range_gain_count_;
155 std::fill(scratch_.begin(), scratch_.end(), SampleType{0});
161 std::size_t frames)
noexcept {
162 if (inputs ==
nullptr || outputs ==
nullptr ||
input_count != inputs_ ||
163 output_count != outputs_ || frames > max_block_size_ || scratch_.empty())
165 for (std::size_t input = 0; input < inputs_; ++input)
166 if (inputs[input] ==
nullptr)
168 for (std::size_t output = 0; output < outputs_; ++output) {
169 if (outputs[output] ==
nullptr)
171 for (std::size_t other = output + 1; other < outputs_; ++other)
172 if (detail::audio_ranges_overlap(outputs[output], outputs[other], frames))
176 for (std::size_t input = 0; input < inputs_; ++input)
177 std::copy_n(inputs[input], frames, scratch_.data() + input * max_block_size_);
180 for (std::size_t frame = 0; frame < frames; ++frame) {
181 for (std::size_t output = 0; output < outputs_; ++output) {
183 for (std::size_t input = 0; input < inputs_; ++input)
184 sum += scratch_[input * max_block_size_ + frame] *
185 cells_[index(output, input)].current;
186 outputs[output][frame] = sum;
193 using AccumulatorType =
194 std::conditional_t<(
sizeof(SampleType) <
sizeof(double)),
double,
long double>;
195 std::array<AccumulatorType, MaxOutputs> row_scales{};
196 for (std::size_t frame = 0; frame < frames; ++frame) {
197 for (std::size_t output = 0; output < outputs_; ++output) {
198 AccumulatorType absolute_sum{};
199 for (std::size_t input = 0; input < inputs_; ++input)
200 absolute_sum +=
static_cast<AccumulatorType
>(
201 std::abs(cells_[index(output, input)].current));
203 AccumulatorType{1} / std::max(AccumulatorType{1}, absolute_sum);
206 for (std::size_t output = 0; output < outputs_; ++output) {
207 AccumulatorType sum{};
208 for (std::size_t input = 0; input < inputs_; ++input)
209 sum +=
static_cast<AccumulatorType
>(
210 scratch_[input * max_block_size_ + frame]) *
211 static_cast<AccumulatorType
>(
212 cells_[index(output, input)].current);
213 outputs[output][frame] =
214 static_cast<SampleType
>(sum * row_scales[output]);
223 SampleType current{};
226 std::size_t total = 0;
227 std::size_t elapsed = 0;
230 static constexpr std::size_t index(std::size_t output, std::size_t input)
noexcept {
231 return output * MaxInputs + input;
234 void advance_ramps() noexcept {
235 for (
auto& cell : cells_) {
236 if (cell.elapsed >= cell.total)
239 if (cell.elapsed == cell.total) {
240 cell.current = cell.target;
244 std::conditional_t<(
sizeof(SampleType) <
sizeof(double)),
double,
long double>;
246 static_cast<ProgressType
>(cell.elapsed) /
static_cast<ProgressType
>(cell.total);
247 const auto start =
static_cast<ProgressType
>(cell.start);
248 const auto target =
static_cast<ProgressType
>(cell.target);
250 if ((cell.start < cell.target && current >= cell.target) ||
251 (cell.start > cell.target && current <= cell.target))
252 current = std::nextafter(cell.target, cell.start);
253 cell.current = current;
257 void settle_ramps() noexcept {
258 for (
auto& cell : cells_) {
259 cell.current = cell.target;
260 cell.start = cell.target;
266 std::size_t inputs_ = 0;
267 std::size_t outputs_ = 0;
268 std::size_t max_block_size_ = 0;
269 std::size_t nonfinite_gain_count_ = 0;
270 std::size_t out_of_range_gain_count_ = 0;
272 std::array<GainCell, MaxInputs * MaxOutputs> cells_{};
273 std::vector<SampleType, Allocator> scratch_;
Fixed-capacity signed audio routing matrix with sample-continuous gain ramps.
Definition audio_matrix_mixer.hpp:44
void reset() noexcept
Clears scratch and settles active ramps at their targets.
Definition audio_matrix_mixer.hpp:154
float gain(std::size_t output, std::size_t input) const noexcept
Definition audio_matrix_mixer.hpp:128
static constexpr std::size_t max_inputs
Definition audio_matrix_mixer.hpp:47
bool set_gain(std::size_t output, std::size_t input, SampleType gain) noexcept
Definition audio_matrix_mixer.hpp:96
bool set_gain_ramped(std::size_t output, std::size_t input, SampleType gain, std::size_t ramp_samples) noexcept
The old value is used for the first sample.
Definition audio_matrix_mixer.hpp:104
void set_headroom_policy(MatrixHeadroomPolicy policy) noexcept
Headroom policy is structural, not automatable: change it while stopped or accept a block-boundary le...
Definition audio_matrix_mixer.hpp:89
AudioMatrixMixerT(const Allocator &allocator=Allocator{})
Definition audio_matrix_mixer.hpp:51
std::size_t max_block_size() const noexcept
Definition audio_matrix_mixer.hpp:142
SampleType target_gain(std::size_t output, std::size_t input) const noexcept
Definition audio_matrix_mixer.hpp:132
std::size_t output_count() const noexcept
Definition audio_matrix_mixer.hpp:139
bool set_dimensions(std::size_t inputs, std::size_t outputs) noexcept
Definition audio_matrix_mixer.hpp:76
bool process(const SampleType *const *inputs, std::size_t input_count, SampleType *const *outputs, std::size_t output_count, std::size_t frames) noexcept
Definition audio_matrix_mixer.hpp:159
static constexpr float max_abs_gain
Definition audio_matrix_mixer.hpp:49
std::size_t input_count() const noexcept
Definition audio_matrix_mixer.hpp:136
bool prepare(std::size_t max_block_size)
Definition audio_matrix_mixer.hpp:53
std::size_t out_of_range_gain_count() const noexcept
Definition audio_matrix_mixer.hpp:148
static constexpr std::size_t max_outputs
Definition audio_matrix_mixer.hpp:48
MatrixHeadroomPolicy headroom_policy() const noexcept
Definition audio_matrix_mixer.hpp:92
std::size_t nonfinite_gain_count() const noexcept
Definition audio_matrix_mixer.hpp:145
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
constexpr bool valid_audio_matrix_capacity
Definition audio_matrix_mixer.hpp:22
Definition additive_bank.hpp:218
AudioMatrixMixerT< double > AudioMatrixMixer64
Definition audio_matrix_mixer.hpp:277
MatrixHeadroomPolicy
Raw applies the signed matrix exactly.
Definition audio_matrix_mixer.hpp:30
@ Raw
Definition audio_matrix_mixer.hpp:30
@ NormalizePeak
Definition audio_matrix_mixer.hpp:30
AudioMatrixMixerT< float > AudioMatrixMixer
Definition audio_matrix_mixer.hpp:276
@ progress
Definition finite_stretch_builder.hpp:30
@ start
Definition geometry.hpp:370