OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
rubberband_stretcher.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
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>
10
11#if __has_include(<RubberBandStretcher.h>)
12#include <RubberBandStretcher.h>
13#elif __has_include(<rubberband/RubberBandStretcher.h>)
14#include <rubberband/RubberBandStretcher.h>
15#endif
16
17#include <cmath>
18
19namespace ossia
20{
21static constexpr auto get_rubberband_preset(ossia::audio_stretch_mode mode)
22{
23 using opt_t = RubberBand::RubberBandStretcher::Option;
24 using preset_t = RubberBand::RubberBandStretcher::PresetOption;
25 uint32_t preset = opt_t::OptionProcessRealTime | opt_t::OptionThreadingNever;
26 switch(mode)
27 {
28 case ossia::audio_stretch_mode::RubberBandStandard:
29 break;
30
31 case ossia::audio_stretch_mode::RubberBandPercussive:
32 preset |= preset_t::PercussiveOptions;
33 break;
34
35 case ossia::audio_stretch_mode::RubberBandStandardHQ:
36 preset |= RubberBand::RubberBandStretcher::OptionEngineFiner;
37 preset |= RubberBand::RubberBandStretcher::OptionPitchHighConsistency;
38 break;
39
40 case ossia::audio_stretch_mode::RubberBandPercussiveHQ:
41 preset |= preset_t::PercussiveOptions;
42 preset |= RubberBand::RubberBandStretcher::OptionEngineFiner;
43 preset |= RubberBand::RubberBandStretcher::OptionPitchHighConsistency;
44 break;
45
46 default:
47 break;
48 }
49
50 return preset;
51}
52
53struct rubberband_stretcher
54{
55 rubberband_stretcher(
56 uint32_t opt, std::size_t channels, std::size_t sampleRate, int64_t pos)
57 : m_rubberBand{std::make_unique<RubberBand::RubberBandStretcher>(
58 sampleRate, channels, opt)}
59 , next_sample_to_read{pos}
60 , options{opt}
61
62 {
63 }
64
65 rubberband_stretcher(const rubberband_stretcher&) = delete;
66 rubberband_stretcher& operator=(const rubberband_stretcher&) = delete;
67 rubberband_stretcher(rubberband_stretcher&&) = default;
68 rubberband_stretcher& operator=(rubberband_stretcher&&) = default;
69
70 std::unique_ptr<RubberBand::RubberBandStretcher> m_rubberBand;
71 int64_t next_sample_to_read = 0;
72 uint32_t options{};
73
74 void transport(int64_t date)
75 {
76 m_rubberBand->reset();
77 next_sample_to_read = date;
78 }
79
80 template <typename T>
81 void
82 run(T& audio_fetcher, const ossia::token_request& t, ossia::exec_state_facade e,
83 double tempo_ratio, const std::size_t chan, const std::size_t len,
84 int64_t samples_to_read, const int64_t samples_to_write,
85 const int64_t samples_offset, const ossia::mutable_audio_span<double>& ap) noexcept
86 {
87 const double abs_tempo_ratio = std::abs(tempo_ratio);
88 if(abs_tempo_ratio != m_rubberBand->getTimeRatio())
89 {
90 m_rubberBand->setTimeRatio(abs_tempo_ratio);
91 }
92
93 // TODO : if T::sample_type == float we could leverage it directly as
94 // input
95 const int max_chan = std::max(chan, m_rubberBand->getChannelCount());
96 const int frames = std::max((int64_t)16, samples_to_read);
97 float** const input = (float**)alloca(sizeof(float*) * max_chan);
98 float** const output = (float**)alloca(sizeof(float*) * max_chan);
99 for(std::size_t i = 0; i < chan; i++)
100 {
101 input[i] = (float*)alloca(sizeof(float) * frames);
102 output[i] = (float*)alloca(sizeof(float) * samples_to_write);
103 }
104 for(std::size_t i = chan; i < m_rubberBand->getChannelCount(); i++)
105 {
106 input[i] = (float*)alloca(sizeof(float) * frames);
107 std::fill_n(input[i], frames, 0.f);
108 output[i] = (float*)alloca(sizeof(float) * samples_to_write);
109 }
110
111 if(t.forward())
112 {
113 while(m_rubberBand->available() < samples_to_write)
114 {
115 audio_fetcher.fetch_audio(next_sample_to_read, samples_to_read, input);
116
117 m_rubberBand->process(input, samples_to_read, false);
118
119 next_sample_to_read += samples_to_read;
120 samples_to_read = 16;
121 }
122
123 m_rubberBand->retrieve(
124 output, std::min((int)samples_to_write, m_rubberBand->available()));
125
126 for(std::size_t i = 0; i < chan; i++)
127 {
128 for(int64_t j = 0; j < samples_to_write; j++)
129 {
130 ap[i][j + samples_offset] = double(output[i][j]);
131 }
132 }
133 }
134 else
135 {
136 // Backward playback:
137 while(m_rubberBand->available() < samples_to_write)
138 {
139 audio_fetcher.fetch_audio_backward(next_sample_to_read, samples_to_read, input);
140
141 m_rubberBand->process(input, samples_to_read, false);
142
143 next_sample_to_read -= samples_to_read;
144 samples_to_read = 16;
145 }
146
147 const int retrieved = m_rubberBand->retrieve(
148 output, std::min((int)samples_to_write, m_rubberBand->available()));
149
150 for(std::size_t i = 0; i < chan; i++)
151 {
152 for(int64_t j = 0; j < samples_to_write; j++)
153 {
154 ap[i][j + samples_offset] = double(output[i][j]);
155 }
156 }
157 }
158 }
159};
160}
161#else
162#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
163
164namespace ossia
165{
166static constexpr uint32_t get_rubberband_preset(ossia::audio_stretch_mode mode)
167{
168 return 0;
169}
170using rubberband_stretcher = raw_stretcher;
171}
172#endif
Definition git_info.h:7