2#include <ossia/detail/config.hpp>
4#if defined(OSSIA_ENABLE_RUBBERBAND)
5#include <ossia/dataflow/audio_port.hpp>
6#include <ossia/dataflow/audio_stretch_mode.hpp>
7#include <ossia/dataflow/graph_node.hpp>
8#include <ossia/dataflow/nodes/media.hpp>
9#include <ossia/dataflow/token_request.hpp>
11#if __has_include(<RubberBandStretcher.h>)
12#include <RubberBandStretcher.h>
13#elif __has_include(<rubberband/RubberBandStretcher.h>)
14#include <rubberband/RubberBandStretcher.h>
23static constexpr auto get_rubberband_preset(ossia::audio_stretch_mode mode)
25 using opt_t = RubberBand::RubberBandStretcher::Option;
26 using preset_t = RubberBand::RubberBandStretcher::PresetOption;
27 uint32_t preset = opt_t::OptionProcessRealTime | opt_t::OptionThreadingNever;
30 case ossia::audio_stretch_mode::RubberBandStandard:
33 case ossia::audio_stretch_mode::RubberBandPercussive:
34 preset |= preset_t::PercussiveOptions;
37 case ossia::audio_stretch_mode::RubberBandStandardHQ:
38 preset |= RubberBand::RubberBandStretcher::OptionEngineFiner;
39 preset |= RubberBand::RubberBandStretcher::OptionPitchHighConsistency;
42 case ossia::audio_stretch_mode::RubberBandPercussiveHQ:
43 preset |= preset_t::PercussiveOptions;
44 preset |= RubberBand::RubberBandStretcher::OptionEngineFiner;
45 preset |= RubberBand::RubberBandStretcher::OptionPitchHighConsistency;
55struct rubberband_stretcher
65 enum class prime_strategy : uint8_t
73 static inline prime_strategy s_prime_strategy{prime_strategy::BareRecipe};
76 uint32_t opt, std::size_t channels, std::size_t sampleRate, int64_t pos)
77 : m_rubberBand{std::make_unique<RubberBand::RubberBandStretcher>(
78 sampleRate, channels, opt)}
79 , next_sample_to_read{pos}
85 = m_rubberBand ? m_rubberBand->getPreferredStartPad() : 0;
86 if(channels > 0 && pad > 0)
88 m_prime_zero_storage.assign(channels * pad, 0.f);
89 m_prime_zero_ptrs.resize(channels);
90 for(std::size_t c = 0; c < channels; ++c)
92 m_prime_zero_ptrs[c] = m_prime_zero_storage.data() + c * pad;
97 rubberband_stretcher(
const rubberband_stretcher&) =
delete;
98 rubberband_stretcher& operator=(
const rubberband_stretcher&) =
delete;
99 rubberband_stretcher(rubberband_stretcher&&) =
default;
100 rubberband_stretcher& operator=(rubberband_stretcher&&) =
default;
102 std::unique_ptr<RubberBand::RubberBandStretcher> m_rubberBand;
103 int64_t next_sample_to_read = 0;
105 bool m_needs_prime{
true};
108 std::vector<float> m_prime_zero_storage;
109 std::vector<float*> m_prime_zero_ptrs;
111 [[nodiscard]] int64_t start_delay() const noexcept
113 return m_rubberBand ? int64_t(m_rubberBand->getStartDelay()) : 0;
116 void transport(int64_t date)
118 m_rubberBand->reset();
119 next_sample_to_read = date;
120 m_needs_prime =
true;
123 template <
typename T>
125 run(T& audio_fetcher,
const ossia::token_request& t, ossia::exec_state_facade e,
126 double tempo_ratio,
const std::size_t chan,
const std::size_t len,
127 int64_t samples_to_read,
const int64_t samples_to_write,
128 const int64_t samples_offset,
const ossia::mutable_audio_span<double>& ap)
noexcept
130 const double abs_tempo_ratio = std::min(70., std::abs(tempo_ratio));
131 if(abs_tempo_ratio != m_rubberBand->getTimeRatio())
133 m_rubberBand->setTimeRatio(abs_tempo_ratio);
137 if(m_needs_prime) [[unlikely]]
139 prime(audio_fetcher, chan, t.forward());
140 m_needs_prime =
false;
145 const int max_chan = std::max(chan, m_rubberBand->getChannelCount());
146 const int frames = std::max((int64_t)16, samples_to_read);
147 float**
const input = (
float**)alloca(
sizeof(
float*) * max_chan);
148 float**
const output = (
float**)alloca(
sizeof(
float*) * max_chan);
149 for(std::size_t i = 0; i < chan; i++)
151 input[i] = (
float*)alloca(
sizeof(
float) * frames);
152 output[i] = (
float*)alloca(
sizeof(
float) * samples_to_write);
154 for(std::size_t i = chan; i < m_rubberBand->getChannelCount(); i++)
156 input[i] = (
float*)alloca(
sizeof(
float) * frames);
157 std::fill_n(input[i], frames, 0.f);
158 output[i] = (
float*)alloca(
sizeof(
float) * samples_to_write);
163 while(m_rubberBand->available() < samples_to_write)
165 audio_fetcher.fetch_audio(next_sample_to_read, samples_to_read, input);
167 m_rubberBand->process(input, samples_to_read,
false);
169 next_sample_to_read += samples_to_read;
170 samples_to_read = 16;
173 m_rubberBand->retrieve(
174 output, std::min((
int)samples_to_write, m_rubberBand->available()));
176 for(std::size_t i = 0; i < chan; i++)
178 for(int64_t j = 0; j < samples_to_write; j++)
180 ap[i][j + samples_offset] = double(output[i][j]);
187 while(m_rubberBand->available() < samples_to_write)
189 audio_fetcher.fetch_audio_backward(next_sample_to_read, samples_to_read, input);
191 m_rubberBand->process(input, samples_to_read,
false);
193 next_sample_to_read -= samples_to_read;
194 samples_to_read = 16;
197 const int retrieved = m_rubberBand->retrieve(
198 output, std::min((
int)samples_to_write, m_rubberBand->available()));
200 for(std::size_t i = 0; i < chan; i++)
202 for(int64_t j = 0; j < samples_to_write; j++)
204 ap[i][j + samples_offset] = double(output[i][j]);
212 template <
typename T>
213 void prime(T& audio_fetcher, std::size_t chan,
bool forward)
noexcept
215 if(!m_rubberBand || chan == 0)
218 const auto strategy = s_prime_strategy;
219 if(strategy == prime_strategy::NoPrime)
222 if(strategy == prime_strategy::PreRollRealAudio)
225 const int64_t window = 2 * int64_t(m_rubberBand->getPreferredStartPad());
228 if(window > 0 && (forward ? next_sample_to_read >= window : true))
230 constexpr int prime_fetch_chunk = 256;
232 = (
float**)alloca(
sizeof(
float*) * m_rubberBand->getChannelCount());
234 = (
float**)alloca(
sizeof(
float*) * m_rubberBand->getChannelCount());
235 for(std::size_t i = 0; i < m_rubberBand->getChannelCount(); i++)
237 input[i] = (
float*)alloca(
sizeof(
float) * prime_fetch_chunk);
238 output[i] = (
float*)alloca(
sizeof(
float) * prime_fetch_chunk);
242 next_sample_to_read -= window;
244 next_sample_to_read += window;
250 = std::min<int64_t>(prime_fetch_chunk, window - fed);
253 audio_fetcher.fetch_audio(next_sample_to_read, chunk, input);
254 next_sample_to_read += chunk;
258 audio_fetcher.fetch_audio_backward(next_sample_to_read, chunk, input);
259 next_sample_to_read -= chunk;
261 m_rubberBand->process(input,
int(chunk),
false);
267 const int64_t target_drain
268 = std::max<int64_t>(0, window - int64_t(m_rubberBand->getStartDelay()));
271 while(drained < target_drain && safety++ < 4096)
273 while(m_rubberBand->available() <= 0)
275 std::fill_n(input[0], prime_fetch_chunk, 0.f);
276 for(std::size_t i = 1; i < m_rubberBand->getChannelCount(); i++)
277 std::fill_n(input[i], prime_fetch_chunk, 0.f);
278 m_rubberBand->process(input, prime_fetch_chunk,
false);
280 const int avail = m_rubberBand->available();
281 const int to_take = int(std::min<int64_t>(
282 target_drain - drained,
283 std::min<int64_t>(avail, prime_fetch_chunk)));
284 m_rubberBand->retrieve(output, to_take);
291 const int64_t toPad = int64_t(m_rubberBand->getPreferredStartPad());
296 if(int64_t(m_prime_zero_storage.size()) < int64_t(chan) * toPad
297 || int64_t(m_prime_zero_ptrs.size()) < int64_t(chan))
302 m_rubberBand->process(
303 m_prime_zero_ptrs.data(),
int(toPad),
false);
305 if(strategy == prime_strategy::ZeroPadOnly
306 || strategy == prime_strategy::PreRollRealAudio)
309 int64_t drain_target;
310 if(strategy == prime_strategy::ExtendedDrain)
312 drain_target = 2 * toPad;
316 drain_target = int64_t(m_rubberBand->getStartDelay());
325 if(!(options & RubberBand::RubberBandStretcher::OptionEngineFiner))
327 drain_target += int64_t(std::llround(
328 0.375 *
double(toPad) * (1.0 - m_rubberBand->getTimeRatio())));
333 if(drain_target <= 0)
336 constexpr int prime_fetch_chunk = 256;
338 = (
float**)alloca(
sizeof(
float*) * m_rubberBand->getChannelCount());
340 = (
float**)alloca(
sizeof(
float*) * m_rubberBand->getChannelCount());
341 for(std::size_t i = 0; i < m_rubberBand->getChannelCount(); i++)
343 input[i] = (
float*)alloca(
sizeof(
float) * prime_fetch_chunk);
344 output[i] = (
float*)alloca(
sizeof(
float) * prime_fetch_chunk);
345 std::fill_n(input[i], prime_fetch_chunk, 0.f);
350 while(drained < drain_target && safety++ < 4096)
352 while(m_rubberBand->available() <= 0)
356 audio_fetcher.fetch_audio(
357 next_sample_to_read, prime_fetch_chunk, input);
358 next_sample_to_read += prime_fetch_chunk;
362 audio_fetcher.fetch_audio_backward(
363 next_sample_to_read, prime_fetch_chunk, input);
364 next_sample_to_read -= prime_fetch_chunk;
366 m_rubberBand->process(input, prime_fetch_chunk,
false);
369 const int available = m_rubberBand->available();
370 const int to_take = int(std::min<int64_t>(
371 drain_target - drained, std::min<int64_t>(available, prime_fetch_chunk)));
372 m_rubberBand->retrieve(output, to_take);
379#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
383static constexpr uint32_t get_rubberband_preset(ossia::audio_stretch_mode mode)
387using rubberband_stretcher = raw_stretcher;