OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
repitch_stretcher.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
4#if defined(OSSIA_ENABLE_LIBSAMPLERATE)
5#if __has_include(<samplerate.h>)
6#include <ossia/dataflow/audio_port.hpp>
7#include <ossia/dataflow/audio_stretch_mode.hpp>
8#include <ossia/dataflow/graph_node.hpp>
9#include <ossia/dataflow/nodes/media.hpp>
10#include <ossia/dataflow/token_request.hpp>
11
12#include <boost/circular_buffer.hpp>
13
14#include <samplerate.h>
15
16#include <algorithm>
17#include <cinttypes>
18#include <cmath>
19namespace ossia
20{
21static constexpr auto get_samplerate_preset(ossia::audio_stretch_mode mode)
22{
23 int qmode = SRC_SINC_BEST_QUALITY;
24 if(mode == audio_stretch_mode::RepitchMediumQ)
25 qmode = SRC_SINC_FASTEST;
26 if(mode == audio_stretch_mode::RepitchFastestQ)
27 qmode = SRC_LINEAR;
28 return qmode;
29}
30
31struct repitch_stretcher
32{
33 struct resample_channel
34 {
35 explicit resample_channel(int preset, int buffersize) noexcept
36 : resampler{src_new(preset, 1, nullptr)}
37 , data(10 * buffersize)
38 {
39 }
40 resample_channel(resample_channel&& other) noexcept
41 : resampler{other.resampler}
42 , data{std::move(other.data)}
43 {
44 other.resampler = nullptr;
45 }
46 resample_channel& operator=(resample_channel&& other) noexcept
47 {
48 resampler = other.resampler;
49 data = std::move(other.data);
50 other.resampler = nullptr;
51 return *this;
52 }
53
54 resample_channel(const resample_channel&) = delete;
55 resample_channel& operator=(const resample_channel&) = delete;
56
57 ~resample_channel()
58 {
59 if(resampler)
60 src_delete(resampler);
61 }
62
63 std::vector<float> input_buffer;
64 SRC_STATE* resampler{};
65 boost::circular_buffer<float> data;
66 };
67
68 repitch_stretcher(int preset, int channels, int bufferSize, int64_t pos)
69 : next_sample_to_read{pos}
70 , preset{preset}
71 {
72 repitchers.reserve(channels);
73 while(int(repitchers.size()) < channels)
74 {
75 repitchers.emplace_back(preset, bufferSize);
76 }
77 }
78
79 std::vector<float*> input_channels;
80 std::vector<float> output_buffer;
81 std::vector<resample_channel> repitchers;
82 int64_t next_sample_to_read{};
83 int preset{};
84
85 // libsamplerate auto-pads its FIR ring; see src_sinc.c:1175.
86 [[nodiscard]] static constexpr int64_t start_delay() noexcept { return 0; }
87
88 void transport(int64_t date) { next_sample_to_read = date; }
89
90 template <typename T>
91 void
92 run(T& audio_fetcher, const ossia::token_request& t, ossia::exec_state_facade e,
93 double tempo_ratio, double rate_ratio, const std::size_t chan, const int64_t len,
94 int64_t samples_to_read, const int64_t samples_to_write,
95 const int64_t samples_offset, const ossia::mutable_audio_span<double>& ap) noexcept
96 {
97 assert(chan > 0);
98
99 // Output frames per input frame, so rate and tempo multiply. Outside
100 // [1/256, 256] src_process fails without writing input_frames_used, and the
101 // loops below never end.
102 const double src_ratio
103 = std::clamp(std::abs(rate_ratio * tempo_ratio), 1. / 256., 70.);
104
105 input_channels.resize(chan);
106 for(std::size_t i = 0; i < chan; i++)
107 {
108 repitchers[i].input_buffer.resize(std::max((int64_t)16, samples_to_read));
109 input_channels[i] = repitchers[i].input_buffer.data();
110 }
111 output_buffer.resize(samples_to_write);
112 auto output = output_buffer.data();
113
114 int64_t num_samples_available = repitchers[0].data.size();
115
116 if(t.forward())
117 {
118 while(num_samples_available < samples_to_write)
119 {
120 audio_fetcher.fetch_audio(
121 next_sample_to_read, samples_to_read, input_channels.data());
122
123 SRC_DATA data;
124 for(std::size_t i = 0; i < chan; ++i)
125 {
126 data.data_in = repitchers[i].input_buffer.data();
127 data.data_out = output;
128 data.input_frames = samples_to_read;
129 data.output_frames = samples_to_write - num_samples_available;
130 data.input_frames_used = 0;
131 data.output_frames_gen = 0;
132 data.src_ratio = src_ratio;
133 data.end_of_input = 0;
134
135 // Resample
136 src_process(repitchers[i].resampler, &data);
137
138 for(int j = 0; j < data.output_frames_gen; j++)
139 repitchers[i].data.push_back(output[j]);
140 }
141 next_sample_to_read += data.input_frames_used;
142 samples_to_read = 16;
143 num_samples_available = repitchers[0].data.size();
144 // src_process emits nothing while its FIR history fills, so only a call
145 // that neither consumed nor produced will never end.
146 if(data.input_frames_used == 0 && data.output_frames_gen == 0)
147 break;
148 }
149
150 const int64_t got
151 = std::min<int64_t>(samples_to_write, repitchers[0].data.size());
152 for(std::size_t i = 0; i < chan; ++i)
153 {
154 auto it = repitchers[i].data.begin();
155 for(int64_t j = 0; j < got; j++)
156 {
157 ap[i][j + samples_offset] = double(*it);
158 ++it;
159 }
160 for(int64_t j = got; j < samples_to_write; j++)
161 ap[i][j + samples_offset] = 0.;
162
163 repitchers[i].data.erase_begin(got);
164 }
165 }
166 else
167 {
168 // Backward playback
169 while(num_samples_available < samples_to_write)
170 {
171 audio_fetcher.fetch_audio_backward(
172 next_sample_to_read, samples_to_read, input_channels.data());
173
174 SRC_DATA data;
175 for(std::size_t i = 0; i < chan; ++i)
176 {
177 data.data_in = repitchers[i].input_buffer.data();
178 data.data_out = output;
179 data.input_frames = samples_to_read;
180 data.output_frames = samples_to_write - num_samples_available;
181 data.input_frames_used = 0;
182 data.output_frames_gen = 0;
183 data.src_ratio = src_ratio;
184 data.end_of_input = 0;
185
186 src_process(repitchers[i].resampler, &data);
187
188 for(int j = 0; j < data.output_frames_gen; j++)
189 repitchers[i].data.push_back(output[j]);
190 }
191 next_sample_to_read -= data.input_frames_used;
192 samples_to_read = 16;
193 num_samples_available = repitchers[0].data.size();
194 // src_process emits nothing while its FIR history fills, so only a call
195 // that neither consumed nor produced will never end.
196 if(data.input_frames_used == 0 && data.output_frames_gen == 0)
197 break;
198 }
199
200 const int64_t got
201 = std::min<int64_t>(samples_to_write, repitchers[0].data.size());
202 for(std::size_t i = 0; i < chan; ++i)
203 {
204 auto it = repitchers[i].data.begin();
205 for(int64_t j = 0; j < got; j++)
206 {
207 ap[i][j + samples_offset] = double(*it);
208 ++it;
209 }
210 for(int64_t j = got; j < samples_to_write; j++)
211 ap[i][j + samples_offset] = 0.;
212
213 repitchers[i].data.erase_begin(got);
214 }
215 }
216 }
217};
218}
219
220#else
221
222#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
223
224namespace ossia
225{
226static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
227{
228 return 0;
229}
230using repitch_stretcher = raw_stretcher;
231}
232#endif
233
234#else
235
236#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
237
238namespace ossia
239{
240static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
241{
242 return 0;
243}
244using repitch_stretcher = raw_stretcher;
245}
246#endif
Definition git_info.h:7