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 void transport(int64_t date) { next_sample_to_read = date; }
85
86 template <typename T>
87 void
88 run(T& audio_fetcher, const ossia::token_request& t, ossia::exec_state_facade e,
89 double tempo_ratio, const std::size_t chan, const int64_t len,
90 int64_t samples_to_read, const int64_t samples_to_write,
91 const int64_t samples_offset, const ossia::mutable_audio_span<double>& ap) noexcept
92 {
93 assert(chan > 0);
94
95 input_channels.resize(chan);
96 for(std::size_t i = 0; i < chan; i++)
97 {
98 repitchers[i].input_buffer.resize(std::max((int64_t)16, samples_to_read));
99 input_channels[i] = repitchers[i].input_buffer.data();
100 }
101 output_buffer.resize(samples_to_write);
102 auto output = output_buffer.data();
103
104 int64_t num_samples_available = repitchers[0].data.size();
105
106 if(t.forward())
107 {
108 while(num_samples_available < samples_to_write)
109 {
110 audio_fetcher.fetch_audio(
111 next_sample_to_read, samples_to_read, input_channels.data());
112
113 SRC_DATA data;
114 for(std::size_t i = 0; i < chan; ++i)
115 {
116 data.data_in = repitchers[i].input_buffer.data();
117 data.data_out = output;
118 data.input_frames = samples_to_read;
119 data.output_frames = samples_to_write - num_samples_available;
120 data.input_frames_used = 0;
121 data.output_frames_gen = 0;
122 data.src_ratio = std::min(70., tempo_ratio);
123 data.end_of_input = 0;
124
125 // Resample
126 src_process(repitchers[i].resampler, &data);
127
128 for(int j = 0; j < data.output_frames_gen; j++)
129 repitchers[i].data.push_back(output[j]);
130 }
131 next_sample_to_read += data.input_frames_used;
132 samples_to_read = 16;
133 num_samples_available = repitchers[0].data.size();
134 }
135
136 for(std::size_t i = 0; i < chan; ++i)
137 {
138 auto it = repitchers[i].data.begin();
139 for(int j = 0; j < samples_to_write; j++)
140 {
141 ap[i][j + samples_offset] = double(*it);
142 ++it;
143 }
144
145 repitchers[i].data.erase_begin(samples_to_write);
146 }
147 }
148 else
149 {
150 // Backward playback
151 while(num_samples_available < samples_to_write)
152 {
153 audio_fetcher.fetch_audio_backward(
154 next_sample_to_read, samples_to_read, input_channels.data());
155
156 SRC_DATA data;
157 for(std::size_t i = 0; i < chan; ++i)
158 {
159 data.data_in = repitchers[i].input_buffer.data();
160 data.data_out = output;
161 data.input_frames = samples_to_read;
162 data.output_frames = samples_to_write - num_samples_available;
163 data.input_frames_used = 0;
164 data.output_frames_gen = 0;
165 data.src_ratio = std::min(70., std::abs(tempo_ratio));
166 data.end_of_input = 0;
167
168 src_process(repitchers[i].resampler, &data);
169
170 for(int j = 0; j < data.output_frames_gen; j++)
171 repitchers[i].data.push_back(output[j]);
172 }
173 next_sample_to_read -= data.input_frames_used;
174 samples_to_read = 16;
175 num_samples_available = repitchers[0].data.size();
176 }
177
178 for(std::size_t i = 0; i < chan; ++i)
179 {
180 auto it = repitchers[i].data.begin();
181 for(int j = 0; j < samples_to_write; j++)
182 {
183 ap[i][j + samples_offset] = double(*it);
184 ++it;
185 }
186
187 repitchers[i].data.erase_begin(samples_to_write);
188 }
189 }
190 }
191};
192}
193
194#else
195
196#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
197
198namespace ossia
199{
200static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
201{
202 return 0;
203}
204using repitch_stretcher = raw_stretcher;
205}
206#endif
207
208#else
209
210#include <ossia/dataflow/nodes/timestretch/raw_stretcher.hpp>
211
212namespace ossia
213{
214static constexpr int get_samplerate_preset(ossia::audio_stretch_mode mode)
215{
216 return 0;
217}
218using repitch_stretcher = raw_stretcher;
219}
220#endif
Definition git_info.h:7