54 std::size_t channel_count)
noexcept {
64 channels_ = channel_count;
65 exact_bypass_ =
factor == 1.0;
70 history_.assign(channels_ * kHistoryFrames, 0.0f);
84 std::vector<float>().swap(history_);
91 exact_bypass_ =
false;
95 source_fraction_ = 0.0;
101 if (!prepared_)
return;
102 std::fill(history_.begin(), history_.end(), 0.0f);
103 accepted_frames_ = 0;
105 source_fraction_ = 0.0;
110 bool prepared() const noexcept {
return prepared_; }
113 return fixed_clock_ && !exact_bypass_;
116 double factor() const noexcept {
return factor_; }
129 if (fixed_clock_) set_step(factor_);
130 exact_bypass_ = factor_ == 1.0 &&
132 (source_index_ >= 0 && source_fraction_ == 0.0 &&
133 static_cast<std::uint64_t
>(source_index_) == accepted_frames_));
154 if (!prepared_)
return result;
161 auto index = source_index_;
162 auto fraction = source_fraction_;
163 std::int64_t highest = -1;
164 for (std::size_t frame = 0; frame < output_frames; ++frame) {
165 auto required = index;
167 if (required == std::numeric_limits<std::int64_t>::max()) {
173 highest = std::max(highest, required);
174 if (!advance(index, fraction)) {
179 if (highest < 0)
return result;
180 const auto required_through =
static_cast<std::uint64_t
>(highest) + 1u;
181 if (required_through > accepted_frames_) {
182 const auto needed = required_through - accepted_frames_;
183 if (needed > std::numeric_limits<std::size_t>::max()) {
194 const auto expected =
plan(output.num_samples());
195 if (!expected.valid())
return expected.status;
196 if (input.num_channels() != channels_ || output.num_channels() != channels_)
198 if (input.num_samples() != expected.input_frames)
203 (input.num_samples() >
204 std::numeric_limits<std::uint64_t>::max() - accepted_frames_ ||
205 input.num_samples() >
static_cast<std::size_t
>(
206 std::numeric_limits<std::int64_t>::max() - source_index_))) {
209 for (std::size_t channel = 0; channel < channels_; ++channel)
210 std::copy(input.channel(channel).begin(), input.channel(channel).end(),
211 output.channel(channel).begin());
213 append_history(input);
214 accepted_frames_ += input.num_samples();
215 source_index_ +=
static_cast<std::int64_t
>(input.num_samples());
219 if (input.num_samples() >
220 std::numeric_limits<std::uint64_t>::max() - accepted_frames_) {
224 const auto new_input_start = accepted_frames_;
225 auto index = source_index_;
226 auto fraction = source_fraction_;
227 if (output.num_samples() != 0 && index >= 0 &&
228 static_cast<std::uint64_t
>(index) < new_input_start &&
229 static_cast<std::uint64_t
>(index) < history_start_) {
233 for (std::size_t frame = 0; frame < output.num_samples(); ++frame) {
234 for (std::size_t channel = 0; channel < channels_; ++channel) {
235 const auto first = source_sample(channel, index, input, new_input_start);
238 const auto second = source_sample(channel, index + 1, input,
240 output.channel(channel)[frame] =
static_cast<float>(
241 first + (second - first) * fraction);
243 output.channel(channel)[frame] = first;
246 if (!advance(index, fraction))
250 append_history(input);
251 accepted_frames_ += input.num_samples();
252 source_index_ = index;
253 source_fraction_ = fraction;
261 static constexpr std::size_t kHistoryFrames = 2;
269 void set_step(
double factor)
noexcept {
270 const auto integer = std::floor(
factor);
271 step_integer_ =
static_cast<std::int64_t
>(integer);
272 step_fraction_ =
factor - integer;
275 bool advance(std::int64_t& index,
double& fraction)
const noexcept {
276 auto next_fraction = fraction + step_fraction_;
277 std::int64_t carry = 0;
278 if (next_fraction >= 1.0) {
279 next_fraction -= 1.0;
282 if (step_integer_ > std::numeric_limits<std::int64_t>::max() - carry ||
283 index > std::numeric_limits<std::int64_t>::max() -
284 (step_integer_ + carry)) {
287 index += step_integer_ + carry;
288 fraction = next_fraction;
292 float source_sample(std::size_t channel,
293 std::int64_t source_index,
294 const BufferView<const float>& input,
295 std::uint64_t new_input_start)
const noexcept {
296 if (source_index < 0)
return 0.0f;
297 const auto absolute =
static_cast<std::uint64_t
>(source_index);
298 if (absolute >= new_input_start) {
299 const auto offset = absolute - new_input_start;
300 if (offset < input.num_samples())
301 return input.channel(channel)[
static_cast<std::size_t
>(offset)];
304 if (absolute < history_start_ ||
305 absolute - history_start_ >= history_count_) {
308 const auto slot =
static_cast<std::size_t
>(absolute - history_start_);
309 return history_[channel * kHistoryFrames + slot];
312 void append_history(
const BufferView<const float>& input)
noexcept {
313 const auto retained = std::min(kHistoryFrames, input.num_samples());
314 const auto previous_needed = kHistoryFrames -
retained;
315 const auto previous_retained = std::min(previous_needed, history_count_);
316 for (std::size_t channel = 0; channel < channels_; ++channel) {
317 auto* destination = history_.data() + channel * kHistoryFrames;
318 if (previous_retained != 0) {
319 const auto previous_offset = history_count_ - previous_retained;
320 std::move(destination + previous_offset,
321 destination + history_count_, destination);
323 const auto source_offset = input.num_samples() -
retained;
324 std::copy(input.channel(channel).begin() +
325 static_cast<std::ptrdiff_t
>(source_offset),
326 input.channel(channel).end(),
327 destination + previous_retained);
329 history_count_ = previous_retained +
retained;
330 history_start_ = accepted_frames_ + input.num_samples() - history_count_;
333 std::vector<float> history_;
335 double factor_ = 0.0;
336 double step_fraction_ = 0.0;
337 std::int64_t step_integer_ = 0;
338 std::size_t channels_ = 0;
339 bool prepared_ =
false;
340 bool exact_bypass_ =
false;
341 bool fixed_clock_ =
false;
342 std::uint64_t accepted_frames_ = 0;
343 std::int64_t source_index_ = 0;
344 double source_fraction_ = 0.0;
345 std::size_t history_count_ = 0;
346 std::uint64_t history_start_ = 0;
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
SampleHeritagePitchProcessor(const SampleHeritagePitchProcessor &)=delete
SampleHeritagePitchProcessor & operator=(const SampleHeritagePitchProcessor &)=delete
void reset() noexcept
Definition sample_heritage_pitch.hpp:100
bool fixed_clock_processing() const noexcept
Definition sample_heritage_pitch.hpp:112
double machine_clock_multiplier() const noexcept
Multiplier applied to the voice machine clock.
Definition sample_heritage_pitch.hpp:138
double source_frames_per_machine_frame() const noexcept
Source advance per fixed-rate machine frame.
Definition sample_heritage_pitch.hpp:145
SampleHeritagePitchProcessor(SampleHeritagePitchProcessor &&)=delete
SampleHeritagePitchStatus prepare(SampleHeritagePitchFamily family, double factor, std::size_t channel_count) noexcept
Definition sample_heritage_pitch.hpp:52
SampleHeritagePitchPlan plan(std::size_t output_frames) const noexcept
Definition sample_heritage_pitch.hpp:151
void release() noexcept
Definition sample_heritage_pitch.hpp:83
SampleHeritagePitchProcessor()=default
bool prepared() const noexcept
Definition sample_heritage_pitch.hpp:110
SampleHeritagePitchStatus process(const BufferView< const float > &input, BufferView< float > output) noexcept
Definition sample_heritage_pitch.hpp:192
SampleHeritagePitchProcessor & operator=(SampleHeritagePitchProcessor &&)=delete
SampleHeritagePitchStatus set_factor(double factor) noexcept
Updates note pitch without reallocating or discontinuously resetting the source cursor.
Definition sample_heritage_pitch.hpp:122
static constexpr double kMaximumFactor
Definition sample_heritage_pitch.hpp:258
SampleHeritagePitchFamily family() const noexcept
Definition sample_heritage_pitch.hpp:115
bool exact_bypass() const noexcept
Definition sample_heritage_pitch.hpp:111
double factor() const noexcept
Definition sample_heritage_pitch.hpp:116
static constexpr double kMinimumFactor
Definition sample_heritage_pitch.hpp:257
Definition analyzer_provider.hpp:12
SampleHeritagePitchFamily
Definition sample_heritage_schema.hpp:36
@ EarlyLinear
Definition sample_heritage_schema.hpp:39
@ DropRepeat
Definition sample_heritage_schema.hpp:38
@ VariableClock
Definition sample_heritage_schema.hpp:37
@ AllocationFailed
Definition finite_time_stretch.hpp:27
SampleHeritagePitchStatus
Definition sample_heritage_pitch.hpp:15
@ SizeOverflow
Definition sample_heritage_pitch.hpp:19
@ HistoryUnavailable
Definition sample_heritage_pitch.hpp:23
@ InvalidDimensions
Definition sample_heritage_pitch.hpp:21
@ NotPrepared
Definition sample_heritage_pitch.hpp:17
@ InputFrameMismatch
Definition sample_heritage_pitch.hpp:22
@ Ok
Definition sample_heritage_pitch.hpp:16
@ AllocationFailed
Definition sample_heritage_pitch.hpp:20
@ InvalidConfiguration
Definition sample_heritage_pitch.hpp:18
@ SizeOverflow
Definition sample_heritage_engine.hpp:44
@ InvalidDimensions
Definition sample_heritage_bus_dsp.hpp:23
@ NotPrepared
Definition sample_heritage_bus_dsp.hpp:21
@ Ok
Definition loop_types.hpp:34
@ InputFrameMismatch
Definition sample_heritage_engine.hpp:86
@ InvalidConfiguration
Definition sample_heritage_live_cyclic.hpp:24
constexpr std::size_t kSampleHeritageMaximumChannels
Definition sample_heritage_schema.hpp:27
@ index
Definition gpu_pass.hpp:140
@ retained
Definition design_update.hpp:10
Definition sample_heritage_pitch.hpp:26
bool valid() const noexcept
Definition sample_heritage_pitch.hpp:31
std::size_t input_frames
Definition sample_heritage_pitch.hpp:29
SampleHeritagePitchStatus status
Definition sample_heritage_pitch.hpp:27
std::size_t output_frames
Definition sample_heritage_pitch.hpp:28