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 <cinttypes>
17#include <cmath>
18namespace ossia
19{
20static constexpr auto get_samplerate_preset(ossia::audio_stretch_mode mode)
21{
22 int qmode = SRC_SINC_BEST_QUALITY;
23 if(mode == audio_stretch_mode::RepitchMediumQ)
24 qmode = SRC_SINC_FASTEST;
25 if(mode == audio_stretch_mode::RepitchFastestQ)
26 qmode = SRC_LINEAR;
27 return qmode;
28}
29
30struct repitch_stretcher
31{
32 struct resample_channel
33 {
34 explicit resample_channel(int preset, int buffersize) noexcept
35 : resampler{src_new(preset, 1, nullptr)}
36 , data(10 * buffersize)
37 {
38 }
39 resample_channel(resample_channel&& other) noexcept
40 : resampler{other.resampler}
41 , data{std::move(other.data)}
42 {
43 other.resampler = nullptr;
44 }
45 resample_channel& operator=(resample_channel&& other) noexcept
46 {
47 resampler = other.resampler;
48 data = std::move(other.data);
49 other.resampler = nullptr;
50 return *this;
51 }
52
53 resample_channel(const resample_channel&) = delete;
54 resample_channel& operator=(const resample_channel&) = delete;
55
56 ~resample_channel()
57 {
58 if(resampler)
59 src_delete(resampler);
60 }
61
62 std::vector<float> input_buffer;
63 SRC_STATE* resampler{};
64 boost::circular_buffer<float> data;
65 };
66
67 repitch_stretcher(int preset, int channels, int bufferSize, int64_t pos)
68 : next_sample_to_read{pos}
69 , preset{preset}
70 {
71 repitchers.reserve(channels);
72 while(int(repitchers.size()) < channels)
73 {
74 repitchers.emplace_back(preset, bufferSize);
75 }
76 }
77
78 std::vector<float*> input_channels;
79 std::vector<float> output_buffer;
80 std::vector<resample_channel> repitchers;
81 int64_t next_sample_to_read{};
82 int preset{};
83
84 // libsamplerate auto-pads its FIR ring; see src_sinc.c:1175.
85 [[nodiscard]] static constexpr int64_t start_delay() noexcept { return 0; }
86
87 void transport(int64_t date) { next_sample_to_read = date; }
88
89 template <typename T>
90 void
91 run(T& audio_fetcher, const ossia::token_request& t, ossia::exec_state_facade e,
92 double tempo_ratio, const std::size_t chan, const int64_t len,
93 int64_t samples_to_read, const int64_t samples_to_write,
94 const int64_t samples_offset, const ossia::mutable_audio_span<double>& ap) noexcept
95 {
96 assert(chan > 0);
97
98 input_channels.resize(chan);
99 for(std::size_t i = 0; i < chan; i++)
100 {
101 repitchers[i].input_buffer.resize(std::max((int64_t)16, samples_to_read));
102 input_channels[i] = repitchers[i].input_buffer.data();
103 }
104 output_buffer.resize(samples_to_write);
105 auto output = output_buffer.data();
106
107 int64_t num_samples_available = repitchers[0].data.size();
108
109 if(t.forward())
110 {
111 while(num_samples_available < samples_to_write)
112 {
113 audio_fetcher.fetch_audio(
114 next_sample_to_read, samples_to_read, input_channels.data());
115
116 SRC_DATA data;
117 for(std::size_t i = 0; i < chan; ++i)
118 {
119 data.data_in = repitchers[i].input_buffer.data();
120 data.data_out = output;
121 data.input_frames = samples_to_read;
122 data.output_frames = samples_to_write - num_samples_available;
123 data.input_frames_used = 0;
124 data.output_frames_gen = 0;
125 data.src_ratio = std::min(70., tempo_ratio);
126 data.end_of_input = 0;
127
128 // Resample
129 src_process(repitchers[i].resampler, &data);
130
131 for(int j = 0; j < data.output_frames_gen; j++)
132 repitchers[i].data.push_back(output[j]);
133 }
134 next_sample_to_read += data.input_frames_used;
135 samples_to_read = 16;
136 num_samples_available = repitchers[0].data.size();
137 }
138
139 for(std::size_t i = 0; i < chan; ++i)
140 {
141 auto it = repitchers[i].data.begin();
142 for(int j = 0; j < samples_to_write; j++)
143 {
144 ap[i][j + samples_offset] = double(*it);
145 ++it;
146 }
147
148 repitchers[i].data.erase_begin(samples_to_write);
149 }
150 }
151 else
152 {
153 // Backward playback
154 while(num_samples_available < samples_to_write)
155 {
156 audio_fetcher.fetch_audio_backward(
157 next_sample_to_read, samples_to_read, input_channels.data());
158
159 SRC_DATA data;
160 for(std::size_t i = 0; i < chan; ++i)
161 {
162 data.data_in = repitchers[i].input_buffer.data();
163 data.data_out = output;
164 data.input_frames = samples_to_read;
165 data.output_frames = samples_to_write - num_samples_available;
166 data.input_frames_used = 0;
167 data.output_frames_gen = 0;
168 data.src_ratio = std::min(70., std::abs(tempo_ratio));
169 data.end_of_input = 0;
170
171 src_process(repitchers[i].resampler, &data);
172
173 for(int j = 0; j < data.output_frames_gen; j++)
174 repitchers[i].data.push_back(output[j]);
175 }
176 next_sample_to_read -= data.input_frames_used;
177 samples_to_read = 16;
178 num_samples_available = repitchers[0].data.size();
179 }
180
181 for(std::size_t i = 0; i < chan; ++i)
182 {
183 auto it = repitchers[i].data.begin();
184 for(int j = 0; j < samples_to_write; j++)
185 {
186 ap[i][j + samples_offset] = double(*it);
187 ++it;
188 }
189
190 repitchers[i].data.erase_begin(samples_to_write);
191 }
192 }
193 }
194};
195}
196
197#else
198
199#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
200
201namespace ossia
202{
203static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
204{
205 return 0;
206}
207using repitch_stretcher = raw_stretcher;
208}
209#endif
210
211#else
212
213#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
214
215namespace ossia
216{
217static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
218{
219 return 0;
220}
221using repitch_stretcher = raw_stretcher;
222}
223#endif
Definition git_info.h:7