19template <
typename SampleType =
float, std::
size_t MaximumVoices = 16>
22 static_assert(std::is_floating_point_v<SampleType>);
27 if (!std::isfinite(sample_rate) || sample_rate <= SampleType{0})
return false;
30 sample_rate_ = sample_rate;
40 bool trigger(SampleType frequency, std::uint64_t seed,
41 std::uint64_t note_instance_id)
noexcept {
42 if (!prepared_ || !std::isfinite(frequency) || frequency <= SampleType{0})
45 if (!next_layout.
configure(spec_, seed, note_instance_id))
return false;
46 const double worst_cents = spec_.detune_cents + spec_.
drift_cents;
47 const double maximum_frequency =
static_cast<double>(frequency) *
48 std::exp2(worst_cents / 1200.0);
49 if (!std::isfinite(maximum_frequency) || maximum_frequency >= sample_rate_ * 0.5)
51 auto next_oscillators = oscillators_;
52 for (std::size_t
i = 0;
i < next_layout.
size(); ++
i) {
53 next_oscillators[
i].set_sample_rate(sample_rate_);
55 if (!next_oscillators[
i].reset_phase(
56 static_cast<SampleType
>(next_layout[
i].phase)))
return false;
58 layout_ = next_layout;
59 oscillators_ = next_oscillators;
60 base_frequency_ = frequency;
70 if (!active_)
return output;
71 constexpr double half_pi = 1.57079632679489661923;
72 for (std::size_t
i = 0;
i < layout_.size(); ++
i) {
73 const auto& voice = layout_[
i];
74 const double cents = voice.detune_cents + layout_.drift_cents(
i, absolute_frame);
75 oscillators_[
i].set_frequency(
static_cast<SampleType
>(
76 static_cast<double>(base_frequency_) * std::exp2(cents / 1200.0)));
77 const double sample = oscillators_[
i].next() * voice.gain;
78 const double angle = (voice.pan + 1.0) * 0.5 * half_pi;
79 output.
left +=
static_cast<SampleType
>(sample * std::cos(angle));
80 output.
right +=
static_cast<SampleType
>(sample * std::sin(angle));
88 for (
auto& oscillator : oscillators_) oscillator.reset();
92 bool prepared() const noexcept {
return prepared_; }
93 bool active() const noexcept {
return active_; }
97 std::array<OscillatorT<SampleType>, MaximumVoices> oscillators_{};
98 UnisonLayout<MaximumVoices> layout_{};
100 SampleType sample_rate_ = SampleType{0};
101 SampleType base_frequency_ = SampleType{0};
102 bool prepared_ =
false;
103 bool active_ =
false;
@ saw
Definition oscillator.hpp:21
Definition supersaw.hpp:20
bool trigger(SampleType frequency, std::uint64_t seed, std::uint64_t note_instance_id) noexcept
Audio-thread-safe note start.
Definition supersaw.hpp:40
StereoSampleT< SampleType > next(std::uint64_t absolute_frame) noexcept
Render one stereo sample for the supplied absolute audio-frame index.
Definition supersaw.hpp:68
void reset() noexcept
Audio-thread-safe hard reset.
Definition supersaw.hpp:87
const UnisonLayout< MaximumVoices > & layout() const noexcept
Definition supersaw.hpp:94
bool active() const noexcept
Definition supersaw.hpp:93
bool prepare(SampleType sample_rate, const UnisonSpec &spec) noexcept
Control-side preparation.
Definition supersaw.hpp:26
bool prepared() const noexcept
Definition supersaw.hpp:92
std::size_t size() const noexcept
Definition unison.hpp:76
double drift_cents(std::size_t voice, std::uint64_t absolute_frame) const noexcept
Definition unison.hpp:84
bool configure(const UnisonSpec &spec, std::uint64_t seed=0, std::uint64_t note_instance_id=0) noexcept
Definition unison.hpp:42
Definition additive_bank.hpp:218
SupersawT< float > Supersaw
Definition supersaw.hpp:106
@ i
Definition formant_filter_bank.hpp:20
SupersawT< double > Supersaw64
Definition supersaw.hpp:107
Definition supersaw.hpp:14
SampleType left
Definition supersaw.hpp:15
SampleType right
Definition supersaw.hpp:16
Control-side configuration for a fixed unison layout.
Definition unison.hpp:20