UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
resampler.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2011-2022 CESNET, z. s. p. o.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#include "config_unix.h"
42#include "config_win32.h"
43#endif // HAVE_CONFIG_H
44
45#include "audio/resampler.hpp"
46#include "audio/types.h"
47#include "audio/utils.h"
48#include "debug.h"
49#include "host.h"
50#include "ug_runtime_error.hpp"
51#include "utils/macros.h"
53#include "utils/worker.h"
54
55#ifdef HAVE_SPEEXDSP
56#include <speex/speex_resampler.h>
57#endif // HAVE_SPEEXDSP
58
59#ifdef HAVE_SOXR
60#include <soxr.h>
61#endif // HAVE_SOXR
62
63#define DEFAULT_SPEEX_RESAMPLE_QUALITY 10 // in range [0,10] - 10 best
64#define MOD_NAME "[audio_resampler] "
65
66using namespace std;
67
69 public:
70 virtual std::tuple<bool, audio_frame2> resample(audio_frame2 &a, std::vector<audio_frame2::channel> &out, int new_sample_rate_num, int new_sample_rate_den) = 0;
72 virtual const int *get_supported_bps() = 0;
73 virtual ~impl() {}
74};
75
76tuple<bool, audio_frame2> audio_frame2_resampler::resample(audio_frame2 &a, vector<audio_frame2::channel> &out, int new_sample_rate_num, int new_sample_rate_den)
77{
78 if (!m_impl) {
79 LOG(LOG_LEVEL_ERROR) << "Audio frame resampler: cannot resample, Soxr/SpeexDSP was not compiled in!\n";
80 return { false, audio_frame2{} };
81 }
82 return m_impl->resample(a, out, new_sample_rate_num, new_sample_rate_den);
83}
84
86 unsigned rate_from{0};
87 unsigned rate_to_num{0};
88 unsigned rate_to_den{1};
89 unsigned ch_count{0};
90 unsigned bps{0};
91};
92
93#ifdef HAVE_SOXR
94class soxr_resampler : public audio_frame2_resampler::impl {
95public:
96 tuple<bool, audio_frame2> resample(audio_frame2 &a, vector<audio_frame2::channel> &new_channels, int new_sample_rate_num, int new_sample_rate_den) override;
97 const int *get_supported_bps() override {
98 static const int ret[] = { 2, 4, 0 };
99 return ret;
100 }
101 ~soxr_resampler() {
102 if (resampler) {
103 soxr_delete(resampler);
104 }
105 }
106
107private:
108 bool check_reconfigure(uint32_t original_sample_rate, uint32_t new_sample_rate_num, uint32_t new_sample_rate_den, size_t nb_channels, unsigned bps);
109
110 soxr_t resampler{nullptr};
111 struct resample_prop prop;
112};
113
126bool soxr_resampler::check_reconfigure(uint32_t original_sample_rate, uint32_t new_sample_rate_num, uint32_t new_sample_rate_den, size_t nb_channels, unsigned bps) {
127 if (resampler != nullptr && nb_channels == prop.ch_count && bps == prop.bps) {
128 if (original_sample_rate != prop.rate_from
129 || new_sample_rate_num != prop.rate_to_num
130 || new_sample_rate_den != prop.rate_to_den) {
131 // Update the resampler numerator and denomintors
132 prop.rate_from = original_sample_rate;
133 prop.rate_to_num = new_sample_rate_num;
134 prop.rate_to_den = new_sample_rate_den;
135 soxr_set_io_ratio(resampler, ((double)prop.rate_from / ((double)new_sample_rate_num / (double)new_sample_rate_den)), 0);
136 }
137 return true;
138 }
139
140 if (this->resampler) {
141 soxr_delete((soxr_t)this->resampler);
142 }
143 this->resampler = nullptr;
144
145 /* When creating a var-rate resampler, q_spec must be set as follows: */
146 soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, SOXR_VR);
147 soxr_runtime_spec_t const runtime_spec = soxr_runtime_spec(1);
148 soxr_io_spec_t io_spec;
149 if(bps == 2) {
150 io_spec = soxr_io_spec(SOXR_INT16_S, SOXR_INT16_S);
151 }
152 else if (bps == 4) {
153 io_spec = soxr_io_spec(SOXR_INT32_S, SOXR_INT32_S);
154 }
155 else {
156 LOG(LOG_LEVEL_ERROR) << "[audio_frame2_resampler] Unsupported BPS of: " << bps << "\n";
157 return false;
158 }
159
160 soxr_error_t error;
161 /* The ratio of the given input rate and output rates must equate to the
162 * maximum I/O ratio that will be used. A resample rate of 2 to 1 would be excessive,
163 but provides a sensible ceiling */
164 this->resampler = soxr_create(2, 1, nb_channels, &error, &io_spec, &q_spec, &runtime_spec);
165
166 if (error) {
167 LOG(LOG_LEVEL_ERROR) << "[audio_frame2_resampler] Cannot initialize resampler: " << soxr_strerror(error) << "\n";
168 return false;
169 }
170 // Immediately change the resample rate to be the correct value for the audio frame
171 soxr_set_io_ratio((soxr_t)this->resampler, ((double)original_sample_rate / ((double)new_sample_rate_num / (double)new_sample_rate_den)), 0);
172
173 // Setup resampler values
174 this->prop.rate_from = original_sample_rate;
175 this->prop.rate_to_num = new_sample_rate_num;
176 this->prop.rate_to_den = new_sample_rate_den;
177 this->prop.ch_count = nb_channels;
178 this->prop.bps = bps;
179 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "Soxr resampler (re)made at " << new_sample_rate_num / new_sample_rate_den << "\n";
180 return true;
181}
182
183tuple<bool, audio_frame2> soxr_resampler::resample(audio_frame2 &a, vector<audio_frame2::channel> &new_channels, int new_sample_rate_num, int new_sample_rate_den) {
184 std::chrono::high_resolution_clock::time_point funcBegin = std::chrono::high_resolution_clock::now();
185
186 bool ret = check_reconfigure(a.get_sample_rate(), new_sample_rate_num, new_sample_rate_den, a.get_channel_count(), a.get_bps());
187 if (!ret) {
188 return {false, audio_frame2{}};
189 }
190
191 // Initialise the new channels that the resampler is going to write into
192 void * * const obuf_ptrs = (void * *) malloc(sizeof(void *) * a.get_channel_count());
193 void * * ibuf_ptrs = (void * *) malloc(sizeof(void *) * a.get_channel_count());
194
195 for (size_t i = 0; i < new_channels.size(); i++) {
196 // Setup the buffers
197 obuf_ptrs[i] = new_channels[i].data.get();
198 ibuf_ptrs[i] = a.get_data(i);
199 }
200
201 size_t inlen = a.get_data_len(0) / a.get_bps();
202 size_t outlen = new_channels[0].len / a.get_bps();
203 size_t odone = 0;
204 soxr_error_t error;
205 error = soxr_process(resampler, ibuf_ptrs, inlen, NULL, obuf_ptrs, outlen, &odone);
206 if (error) {
207 LOG(LOG_LEVEL_ERROR) << "[audio_frame2_resampler] resampler failed: " << soxr_strerror(error) << "\n";
208 return {false, audio_frame2{}};
209 }
210 for (unsigned int i = 0; i < new_channels.size(); i++) {
211 new_channels[i].len = odone * a.get_bps();
212 }
213
214 free(obuf_ptrs); free(ibuf_ptrs);
215
216 std::chrono::high_resolution_clock::time_point funcEnd = std::chrono::high_resolution_clock::now();
217 long long resamplerDuration = std::chrono::duration_cast<std::chrono::milliseconds>(funcEnd - funcBegin).count();
218 LOG(LOG_LEVEL_DEBUG) << "[audio_frame2_resampler] resampler_duration " << resamplerDuration << "\n";
219
220 // Remainders aren't as relevant when using SOXR
221 audio_frame2 remainder = {};
222 return {true, std::move(remainder)};
223}
224#endif
225
226#ifdef HAVE_SPEEXDSP
227class speex_resampler : public audio_frame2_resampler::impl {
228public:
229 tuple<bool, audio_frame2> resample(audio_frame2 &a, vector<audio_frame2::channel> &new_channels, int new_sample_rate_num, int new_sample_rate_den) override;
230
231 speex_resampler(int q) : quality(q) {}
232
233 const int *get_supported_bps() override {
234 static const int ret[] = { 2, 4, 0 };
235 return ret;
236 }
237
238 ~speex_resampler() {
239 if (state) {
240 speex_resampler_destroy(state);
241 }
242 }
243private:
244 bool check_reconfigure(unsigned original_sample_rate, unsigned new_sample_rate_num, unsigned new_sample_rate_den, unsigned channel_size, unsigned bps);
245
246 int quality;
247 SpeexResamplerState *state{nullptr};
248 struct resample_prop prop;
249};
250
251bool speex_resampler::check_reconfigure(unsigned original_sample_rate, unsigned new_sample_rate_num, unsigned new_sample_rate_den, unsigned nb_channels, unsigned bps) {
252 if (state != nullptr && original_sample_rate == prop.rate_from
253 && new_sample_rate_num == prop.rate_to_num
254 && new_sample_rate_den == prop.rate_to_den
255 && nb_channels == prop.ch_count
256 && bps == prop.bps) {
257 return true;
258 }
259 if (bps != 2 && bps != 4) {
260 throw logic_error("Only 16 or 32 bits per sample are supported for resampling!");
261 }
262
263 if (state) {
264 speex_resampler_destroy(state);
265 }
266 state = nullptr;
267 int err = 0;
268 state = speex_resampler_init_frac(nb_channels, original_sample_rate * new_sample_rate_den,
269 new_sample_rate_num, original_sample_rate, new_sample_rate_num, quality, &err);
270 if (err) {
271 LOG(LOG_LEVEL_ERROR) << MOD_NAME "Cannot initialize SpeexDSP resampler: " << speex_resampler_strerror(err) << "\n";
272 return false;
273 }
274 prop.rate_from = original_sample_rate;
275 prop.rate_to_num = new_sample_rate_num;
276 prop.rate_to_den = new_sample_rate_den;
277 prop.ch_count = nb_channels;
278 prop.bps = bps;
279 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "SpeexDSP resampler (re)made at " << new_sample_rate_num / new_sample_rate_den << "\n";
280 return true;
281}
282
283struct speex_process_channel_data {
284 SpeexResamplerState *state;
285 int channel_idx;
286 void *in;
287 void *out;
288 uint32_t in_frames;
289 uint32_t in_frames_orig;
290 uint32_t write_frames;
291};
292
293static void *speex_process_channel_short(void *arg) {
294 auto *d = (speex_process_channel_data *) arg;
295 speex_resampler_process_int(d->state,
296 d->channel_idx,
297 (spx_int16_t *) d->in, &d->in_frames,
298 (spx_int16_t *) d->out, &d->write_frames);
299 return NULL;
300}
301
302static void *speex_process_channel_int(void *arg) {
303 auto *d = (speex_process_channel_data *) arg;
304 int2float((char *) d->in, (char *) d->in, sizeof(float) * d->in_frames);
305 speex_resampler_process_float(d->state,
306 d->channel_idx,
307 (float *) d->in, &d->in_frames,
308 (float *) d->out, &d->write_frames);
309 float2int((char *) d->out, (char *) d->out, sizeof(float) * d->write_frames);
310 return NULL;
311}
312
313tuple<bool, audio_frame2> speex_resampler::resample(audio_frame2 &a, vector<audio_frame2::channel> &new_channels, int new_sample_rate_num, int new_sample_rate_den) {
314 bool ret = check_reconfigure(a.get_sample_rate(), new_sample_rate_num, new_sample_rate_den, a.get_channel_count(), a.get_bps());
315 if (!ret) {
316 return {false, audio_frame2{}};
317 }
318
319 audio_frame2 remainder;
320 remainder.init(new_channels.size(), AC_PCM, prop.bps, prop.rate_from);
321
322 vector <speex_process_channel_data> speex_worker_data(new_channels.size());
323 for (size_t i = 0; i < new_channels.size(); i++) {
324 speex_worker_data.at(i).state = state;
325 speex_worker_data.at(i).channel_idx = i;
326 speex_worker_data.at(i).in = (void *) a.get_data(i);
327 speex_worker_data.at(i).out = (void *) new_channels[i].data.get();
328 speex_worker_data.at(i).in_frames_orig =
329 speex_worker_data.at(i).in_frames = a.get_data_len(i) / a.get_bps();
330 speex_worker_data.at(i).write_frames = new_channels[i].len / a.get_bps();
331 }
332 if (a.get_bps() == 2) {
333 task_run_parallel(speex_process_channel_short, new_channels.size(), speex_worker_data.data(), sizeof speex_worker_data[0], NULL);
334 } else {
335 task_run_parallel(speex_process_channel_int, new_channels.size(), speex_worker_data.data(), sizeof speex_worker_data[0], NULL);
336 }
337 for (size_t i = 0; i < new_channels.size(); i++) {
338 if (speex_worker_data.at(i).in_frames != speex_worker_data.at(i).in_frames_orig) {
339 remainder.append(i, a.get_data(i) + speex_worker_data.at(i).in_frames * a.get_bps(),
340 speex_worker_data.at(i).in_frames_orig - speex_worker_data.at(i).in_frames);
341 }
342 new_channels[i].len = speex_worker_data.at(i).write_frames * a.get_bps();
343 }
344
345 if (remainder.get_data_len() == 0) {
346 remainder = {};
347 }
348 return {true, std::move(remainder)};
349}
350#endif // defined HAVE_SPEEXDSP
351
352ADD_TO_PARAM("resampler", "* resampler=[speex|soxr][[:]quality=[0-10]]\n"
353 " Select resampler; set quality for Speex in range 0 (worst) and 10 (best), default " TOSTRING(DEFAULT_SPEEX_RESAMPLE_QUALITY) "\n");
354
356{
357 enum { RESAMPLER_DEFAULT, RESAMPLER_SPEEX, RESAMPLER_SOXR } resampler_type = RESAMPLER_DEFAULT;
358 const char *cfg_c = get_commandline_param("resampler");
359 std::string_view sv = cfg_c ? cfg_c : "";
360 int quality = DEFAULT_SPEEX_RESAMPLE_QUALITY;
361 while (!sv.empty()) {
362 const auto &tok = tokenize(sv, ':');
363 if (tok == "speex") {
364 resampler_type = RESAMPLER_SPEEX;
365 } else if (tok == "soxr") {
366 resampler_type = RESAMPLER_SOXR;
367 } else if (tok.compare(0, "quality="sv.length(), "quality=") == 0) {
368 quality = stoi(string(tok.substr("quality="sv.length())));
369 if (quality < 0 || quality > 10) {
370 throw ug_runtime_error("Quality " + to_string(quality) + " out of range 0-10"s);
371 }
372 } else {
373 throw ug_runtime_error("Unknown resampler option: "s + string(tok));
374 }
375 }
376 switch (resampler_type) {
377 case RESAMPLER_DEFAULT:
378#ifdef HAVE_SPEEXDSP
379 m_impl = unique_ptr<audio_frame2_resampler::impl>(new speex_resampler(quality));
380#elif defined HAVE_SOXR
381 m_impl = unique_ptr<audio_frame2_resampler::impl>(new soxr_resampler());
382#endif
383 break;
384 case RESAMPLER_SPEEX:
385#ifdef HAVE_SPEEXDSP
386 m_impl = unique_ptr<audio_frame2_resampler::impl>(new speex_resampler(quality));
387#else
388 throw ug_runtime_error("SpeexDSP not compiled in!");
389#endif
390 break;
391 case RESAMPLER_SOXR:
392#if defined HAVE_SOXR
393 m_impl = unique_ptr<audio_frame2_resampler::impl>(new soxr_resampler());
394 break;
395#else
396 throw ug_runtime_error("Soxr not compiled in!");
397#endif
398 }
399}
400
405 if (!m_impl) {
406 LOG(LOG_LEVEL_ERROR) << "Audio frame resampler: cannot resample, Soxr/SpeexDSP was not compiled in!\n";
407 return 0;
408 }
409 const int *sup = m_impl->get_supported_bps();
410 int last = 0;
411 while (*sup != 0) {
412 if (orig <= *sup) {
413 return *sup;
414 }
415 last = *sup++;
416 }
417 return last;
418}
419
423
void ** state
This file contains definition of some common audio types.
@ AC_PCM
Definition types.h:53
virtual std::tuple< bool, audio_frame2 > resample(audio_frame2 &a, std::vector< audio_frame2::channel > &out, int new_sample_rate_num, int new_sample_rate_den)=0
virtual const int * get_supported_bps()=0
std::tuple< bool, audio_frame2 > resample(audio_frame2 &a, std::vector< audio_frame2::channel > &out, int new_sample_rate_num, int new_sample_rate_den)
Definition resampler.cpp:76
std::unique_ptr< impl > m_impl
Definition resampler.hpp:59
audio_frame2_resampler & operator=(audio_frame2_resampler &&)
int align_bps(int orig)
char * get_data(int channel)
Definition types.cpp:218
void init(int nr_channels, audio_codec_t codec, int bps, int sample_rate)
Initializes audio_frame2 for use. If already initialized, data are dropped.
Definition types.cpp:115
int get_channel_count() const
Definition types.cpp:260
size_t get_data_len(int channel) const
Definition types.cpp:228
int get_sample_rate() const
Definition types.cpp:275
int get_bps() const
Definition types.cpp:200
void append(audio_frame2 const &frame)
Definition types.cpp:125
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG(level)
Definition debug.h:313
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
#define TOSTRING(A)
Definition macros.h:49
#define DEFAULT_SPEEX_RESAMPLE_QUALITY
Definition resampler.cpp:63
#define MOD_NAME
Definition resampler.cpp:64
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
unsigned bps
Definition resampler.cpp:90
unsigned rate_to_den
Definition resampler.cpp:88
unsigned ch_count
Definition resampler.cpp:89
unsigned rate_to_num
Definition resampler.cpp:87
unsigned rate_from
Definition resampler.cpp:86
void float2int(char *out, const char *in, int len)
Definition utils.cpp:482
void int2float(char *out, const char *in, int len)
Definition utils.cpp:496
void task_run_parallel(runnable_t task, int worker_count, void *data, size_t data_size, void **res)
Definition worker.cpp:294