2 #include <ossia/dataflow/audio_stretch_mode.hpp>
3 #include <ossia/dataflow/node_process.hpp>
4 #include <ossia/dataflow/nodes/media.hpp>
5 #include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
6 #include <ossia/dataflow/nodes/timestretch/repitch_stretcher.hpp>
7 #include <ossia/dataflow/nodes/timestretch/rubberband_stretcher.hpp>
8 #include <ossia/dataflow/port.hpp>
9 #include <ossia/dataflow/sample_to_float.hpp>
10 #include <ossia/detail/pod_vector.hpp>
11 #include <ossia/detail/variant.hpp>
17 struct sample_read_info
19 int64_t samples_to_read{};
20 int64_t samples_to_write{};
24 sample_info(int64_t bufferSize,
double durationRatio,
const ossia::token_request& t)
33 _.samples_to_read = t.physical_read_duration(durationRatio);
35 t.physical_write_duration(durationRatio),
36 t.safe_physical_write_duration(durationRatio, bufferSize));
42 perform_upmix(
const std::size_t upmix,
const std::size_t chan, ossia::audio_port& ap)
75 for(std::size_t chan = 1; chan < upmix; ++chan)
76 ap.channel(chan).assign(ap.channel(0).begin(), ap.channel(0).end());
87 inline void perform_start_offset(
const std::size_t start, ossia::audio_port& ap)
91 ap.get().insert(ap.get().begin(), start, ossia::audio_channel{});
97 bool start_discontinuous,
bool end_discontinuous, audio_channel& ap,
98 std::size_t start, std::size_t end);
102 template <
typename T>
110 ~at_end() { func(); }
118 RubberbandStretcher = 1,
121 [[nodiscard]] int64_t next_sample_to_read() const noexcept
124 [](
auto& stretcher) noexcept {
return stretcher.next_sample_to_read; },
128 void transport(int64_t date)
131 [=](
auto& stretcher) noexcept {
return stretcher.transport(date); }, m_stretch);
135 int64_t date, ossia::audio_stretch_mode mode, std::size_t channels,
136 std::size_t fileSampleRate)
142 case ossia::audio_stretch_mode::None: {
143 if(
auto s = ossia::get_if<RawStretcher>(&m_stretch))
149 m_stretch.emplace<RawStretcher>(date);
154 #if defined(OSSIA_ENABLE_RUBBERBAND)
155 case ossia::audio_stretch_mode::RubberBandStandard:
156 case ossia::audio_stretch_mode::RubberBandPercussive:
157 case ossia::audio_stretch_mode::RubberBandStandardHQ:
158 case ossia::audio_stretch_mode::RubberBandPercussiveHQ: {
159 const auto preset = get_rubberband_preset(mode);
160 if(
auto s = ossia::get_if<RubberbandStretcher>(&m_stretch);
161 s && s->options == preset)
167 m_stretch.emplace<rubberband_stretcher>(
168 preset, channels, fileSampleRate, date);
174 #if defined(OSSIA_ENABLE_LIBSAMPLERATE)
175 case ossia::audio_stretch_mode::Repitch: {
176 if(
auto s = ossia::get_if<RepitchStretcher>(&m_stretch);
177 s && s->repitchers.size() == channels)
184 m_stretch.emplace<repitch_stretcher>(channels, 1024, date);
192 template <
typename T>
194 run(T& audio_fetcher,
const ossia::token_request& t, ossia::exec_state_facade e,
195 double tempo_ratio, std::size_t chan, std::size_t len, int64_t samples_to_read,
196 int64_t samples_to_write, int64_t samples_offset,
197 const ossia::mutable_audio_span<double>& ap)
200 [&](
auto& stretcher) {
202 audio_fetcher, t, e, tempo_ratio, chan, len, samples_to_read, samples_to_write,
208 [[nodiscard]]
bool stretch() const noexcept {
return m_stretch.index() != 0; }
213 #if defined(OSSIA_ENABLE_RUBBERBAND)
217 #if defined(OSSIA_ENABLE_LIBSAMPLERATE)
225 struct sound_processing_info
227 time_value m_prev_date{time_value::infinite_min};
229 time_value m_loop_duration{};
230 time_value m_start_offset{};
234 int64_t m_loop_duration_samples{};
235 int64_t m_start_offset_samples{};
237 ossia::resampler m_resampler{};
244 m_loop_duration = loop_duration;
245 m_start_offset = start_offset;
249 void set_resampler(ossia::resampler&& r)
251 auto date = m_resampler.next_sample_to_read();
252 m_resampler = std::move(r);
253 m_resampler.transport(date);
256 void set_native_tempo(
double v) { tempo = v; }
258 double update_stretch(
259 const ossia::token_request& t,
const ossia::exec_state_facade& e) noexcept
261 double stretch_ratio = 1.;
262 double model_ratio = 1.;
265 if(m_resampler.stretch())
267 model_ratio = ossia::root_tempo / this->tempo;
268 stretch_ratio = this->tempo / t.tempo;
272 model_ratio = ossia::root_tempo / t.tempo;
276 m_loop_duration_samples = m_loop_duration.impl * e.modelToSamples() * model_ratio;
277 m_start_offset_samples = m_start_offset.impl * e.modelToSamples() * model_ratio;
278 return stretch_ratio;
283 :
public ossia::nonowning_graph_node
284 ,
public sound_processing_info
287 virtual void transport(time_value date) = 0;
290 class dummy_sound_node final :
public sound_node
293 ossia::audio_outlet audio_out;
297 m_outlets.push_back(&audio_out);
300 void transport(time_value date)
override { }
302 void run(
const ossia::token_request& t, ossia::exec_state_facade e) noexcept
override
307 #if defined(OSSIA_SCENARIO_DATAFLOW)
308 class sound_process final :
public ossia::node_process
311 using ossia::node_process::node_process;
314 void state(
const ossia::token_request& req)
override
319 static_cast<sound_node&
>(*this->node)
320 .set_loop_info(m_loop_duration, m_start_offset, m_loops);
328 void offset_impl(time_value date)
override
330 static_cast<sound_node&
>(*this->node).transport(date);
332 void transport_impl(time_value date)
override
334 static_cast<sound_node&
>(*this->node).transport(date);
constexpr OSSIA_INLINE auto min(const T a, const U b) noexcept -> typename std::conditional<(sizeof(T) > sizeof(U)), T, U >::type
min function tailored for values
Definition: math.hpp:125
The time_value class.
Definition: ossia/editor/scenario/time_value.hpp:28