From f6a66171bcb9254b8a88906530fddfdcd9f72cfb Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 10 May 2021 18:11:35 +0100 Subject: [PATCH] Initial experimental import of bq resampler --- meson.build | 8 +- meson_options.txt | 2 +- src/dsp/BQResampler.cpp | 633 ++++++++++++++++++++++++++++++++++++++++ src/dsp/BQResampler.h | 165 +++++++++++ src/dsp/Resampler.cpp | 418 ++++++++++++++++++-------- src/dsp/Resampler.h | 23 +- src/system/Allocators.h | 92 ++++++ src/system/VectorOps.h | 52 ++++ 8 files changed, 1269 insertions(+), 124 deletions(-) create mode 100644 src/dsp/BQResampler.cpp create mode 100644 src/dsp/BQResampler.h diff --git a/meson.build b/meson.build index 1585b51..65d49eb 100644 --- a/meson.build +++ b/meson.build @@ -199,7 +199,13 @@ else endif # fft -if resampler == 'libsamplerate' +if resampler == 'builtin' + config_summary += { 'Resampler': 'Built-in' } + message('For resampler: using built-in implementation') + library_sources += 'src/dsp/BQResampler.cpp' + feature_defines += ['-DUSE_BQRESAMPLER'] + +elif resampler == 'libsamplerate' if samplerate_dep.found() config_summary += { 'Resampler': 'libsamplerate' } message('For resampler: using libsamplerate') diff --git a/meson_options.txt b/meson_options.txt index 86bf686..8e65fa0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -7,7 +7,7 @@ option('fft', option('resampler', type: 'combo', - choices: ['auto', 'libsamplerate', 'speex', 'ipp'], + choices: ['auto', 'builtin', 'libsamplerate', 'speex', 'ipp'], value: 'auto', description: 'Resampler library to use. Recommended is libsamplerate. The default (auto) will use libsamplerate if available, speex otherwise.') diff --git a/src/dsp/BQResampler.cpp b/src/dsp/BQResampler.cpp new file mode 100644 index 0000000..2a628f8 --- /dev/null +++ b/src/dsp/BQResampler.cpp @@ -0,0 +1,633 @@ +//* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band Library + An audio time-stretching and pitch-shifting library. + Copyright 2007-2021 Particular Programs Ltd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. + + Alternatively, if you have a valid commercial licence for the + Rubber Band Library obtained by agreement with the copyright + holders, you may redistribute and/or modify it under the terms + described in that licence. + + If you wish to distribute code using the Rubber Band Library + under terms other than those of the GNU General Public License, + you must obtain a valid commercial licence before doing so. +*/ + +#include "BQResampler.h" + +#include + +#include + +#include "system/Allocators.h" +#include "system/VectorOps.h" + +#define BQ_R__ R__ + +using std::vector; +using std::cerr; +using std::endl; +using std::min; +using std::max; + +namespace RubberBand { + +BQResampler::BQResampler(Parameters parameters, int channels) : + m_qparams(parameters.quality), + m_dynamism(parameters.dynamism), + m_ratio_change(parameters.ratioChange), + m_debug_level(parameters.debugLevel), + m_initial_rate(parameters.referenceSampleRate), + m_channels(channels), + m_fade_count(0), + m_initialised(false) +{ + if (m_debug_level > 0) { + cerr << "BQResampler::BQResampler: " + << (m_dynamism == RatioOftenChanging ? "often-changing" : "mostly-fixed") + << ", " + << (m_ratio_change == SmoothRatioChange ? "smooth" : "sudden") + << " ratio changes, ref " << m_initial_rate << " Hz" << endl; + } + + if (m_dynamism == RatioOftenChanging) { + m_proto_length = m_qparams.proto_p * m_qparams.p_multiple + 1; + if (m_debug_level > 0) { + cerr << "BQResampler: creating prototype filter of length " + << m_proto_length << endl; + } + m_prototype = make_filter(m_proto_length, m_qparams.proto_p); + m_prototype.push_back(0.0); // interpolate without fear + } + + int phase_reserve = 2 * int(round(m_initial_rate)); + int buffer_reserve = 1000 * m_channels; + m_state_a.phase_info.reserve(phase_reserve); + m_state_a.buffer.reserve(buffer_reserve); + + if (m_dynamism == RatioOftenChanging) { + m_state_b.phase_info.reserve(phase_reserve); + m_state_b.buffer.reserve(buffer_reserve); + } + + m_s = &m_state_a; + m_fade = &m_state_b; +} + +BQResampler::BQResampler(const BQResampler &other) : + m_qparams(other.m_qparams), + m_dynamism(other.m_dynamism), + m_ratio_change(other.m_ratio_change), + m_debug_level(other.m_debug_level), + m_initial_rate(other.m_initial_rate), + m_channels(other.m_channels), + m_state_a(other.m_state_a), + m_state_b(other.m_state_b), + m_fade_count(other.m_fade_count), + m_prototype(other.m_prototype), + m_proto_length(other.m_proto_length), + m_initialised(other.m_initialised) +{ + if (other.m_s == &(other.m_state_a)) { + m_s = &m_state_a; + m_fade = &m_state_b; + } else { + m_s = &m_state_b; + m_fade = &m_state_a; + } +} + +void +BQResampler::reset() +{ + m_initialised = false; + m_fade_count = 0; +} + +BQResampler::QualityParams::QualityParams(Quality q) +{ + switch (q) { + case Fastest: + p_multiple = 12; + proto_p = 160; + k_snr = 70.0; + k_transition = 0.2; + cut = 0.9; + break; + case FastestTolerable: + p_multiple = 62; + proto_p = 160; + k_snr = 90.0; + k_transition = 0.05; + cut = 0.975; + break; + case Best: + p_multiple = 122; + proto_p = 800; + k_snr = 100.0; + k_transition = 0.01; + cut = 0.995; + break; + } +} + +int +BQResampler::resampleInterleaved(float *const out, + int outspace, + const float *const in, + int incount, + double ratio, + bool final) { + + int fade_length = round(m_initial_rate / 1000.0); + if (fade_length < 6) { + fade_length = 6; + } + int max_fade = min(outspace, int(floor(incount * ratio))) / 2; + if (fade_length > max_fade) { + fade_length = max_fade; + } + + if (!m_initialised) { + state_for_ratio(*m_s, ratio, *m_fade); + m_initialised = true; + } else if (ratio != m_s->parameters.ratio) { + state *tmp = m_fade; + m_fade = m_s; + m_s = tmp; + state_for_ratio(*m_s, ratio, *m_fade); + if (m_ratio_change == SmoothRatioChange) { + if (m_debug_level > 0) { + cerr << "BQResampler: ratio changed, beginning fade of length " + << fade_length << endl; + } + m_fade_count = fade_length; + } + } + + int i = 0, o = 0; + int bufsize = m_s->buffer.size(); + + int incount_samples = incount * m_channels; + int outspace_samples = outspace * m_channels; + + while (o < outspace_samples) { + while (i < incount_samples && m_s->fill < bufsize) { + m_s->buffer[m_s->fill++] = in[i++]; + } + if (m_s->fill == bufsize) { + out[o++] = reconstruct_one(m_s); + } else if (final && m_s->fill > m_s->centre) { + out[o++] = reconstruct_one(m_s); + } else if (final && m_s->fill == m_s->centre && + m_s->current_phase != m_s->initial_phase) { + out[o++] = reconstruct_one(m_s); + } else { + break; + } + } + + int fbufsize = m_fade->buffer.size(); + int fi = 0, fo = 0; + while (fo < o && m_fade_count > 0) { + while (fi < incount_samples && m_fade->fill < fbufsize) { + m_fade->buffer[m_fade->fill++] = in[fi++]; + } + if (m_fade->fill == fbufsize) { + double r = reconstruct_one(m_fade); + double fadeWith = out[fo]; + double extent = double(m_fade_count - 1) / double(fade_length); + double mixture = 0.5 * (1.0 - cos(M_PI * extent)); + double mixed = r * mixture + fadeWith * (1.0 - mixture); + out[fo] = mixed; + ++fo; + if (m_fade->current_channel == 0) { + --m_fade_count; + } + } else { + break; + } + } + + return o / m_channels; +} + +int +BQResampler::gcd(int a, int b) const +{ + int c = a % b; + if (c == 0) return b; + else return gcd(b, c); +} + +double +BQResampler::bessel0(double x) const +{ + static double facsquared[] = { + 0.0, 1.0, 4.0, 36.0, + 576.0, 14400.0, 518400.0, 25401600.0, + 1625702400.0, 131681894400.0, 1.316818944E13, 1.59335092224E15, + 2.29442532803E17, 3.87757880436E19, 7.60005445655E21, + 1.71001225272E24, 4.37763136697E26, 1.26513546506E29, + 4.09903890678E31, 1.47975304535E34 + }; + static int nterms = sizeof(facsquared) / sizeof(facsquared[0]); + double b = 1.0; + for (int n = 1; n < nterms; ++n) { + double ff = facsquared[n]; + double term = pow(x / 2.0, n * 2.0) / ff; + b += term; + } + return b; +} + +vector +BQResampler::kaiser(double beta, int len) const +{ + double denominator = bessel0(beta); + int half = (len % 2 == 0 ? len/2 : (len+1)/2); + vector v(len, 0.0); + for (int n = 0; n < half; ++n) { + double k = (2.0 * n) / (len-1) - 1.0; + v[n] = bessel0 (beta * sqrt(1.0 - k*k)) / denominator; + } + for (int n = half; n < len; ++n) { + v[n] = v[len-1 - n]; + } + return v; +} + +void +BQResampler::kaiser_params(double attenuation, + double transition, + double &beta, + int &len) const +{ + if (attenuation > 21.0) { + len = 1 + ceil((attenuation - 7.95) / (2.285 * transition)); + } else { + len = 1 + ceil(5.79 / transition); + } + beta = 0.0; + if (attenuation > 50.0) { + beta = 0.1102 * (attenuation - 8.7); + } else if (attenuation > 21.0) { + beta = 0.5842 * (pow (attenuation - 21.0, 0.4)) + + 0.07886 * (attenuation - 21.0); + } +} + +vector +BQResampler::kaiser_for(double attenuation, + double transition, + int minlen, + int maxlen) const +{ + double beta; + int m; + kaiser_params(attenuation, transition, beta, m); + int mb = m; + if (maxlen > 0 && mb > maxlen - 1) { + mb = maxlen - 1; + } else if (minlen > 0 && mb < minlen) { + mb = minlen; + } + if (mb % 2 == 0) ++mb; + if (m_debug_level > 0) { + cerr << "BQResampler: window attenuation " << attenuation + << ", transition " << transition + << " -> length " << m << " adjusted to " << mb + << ", beta " << beta << endl; + } + return kaiser(beta, mb); +} + +void +BQResampler::sinc_multiply(double peak_to_zero, vector &buf) const +{ + int len = int(buf.size()); + if (len < 2) return; + + int left = len / 2; + int right = (len + 1) / 2; + double m = M_PI / peak_to_zero; + + for (int i = 1; i <= right; ++i) { + double x = i * m; + double sinc = sin(x) / x; + if (i <= left) { + buf[left - i] *= sinc; + } + if (i < right) { + buf[i + left] *= sinc; + } + } +} + +BQResampler::params +BQResampler::fill_params(double ratio, int num, int denom) const +{ + params p; + int g = gcd (num, denom); + p.ratio = ratio; + p.numerator = num / g; + p.denominator = denom / g; + p.effective = double(p.numerator) / double(p.denominator); + p.peak_to_zero = max(p.denominator, p.numerator); + p.peak_to_zero /= m_qparams.cut; + p.scale = double(p.numerator) / double(p.peak_to_zero); + + if (m_debug_level > 0) { + cerr << "BQResampler: ratio " << p.ratio + << " -> fraction " << p.numerator << "/" << p.denominator + << " with error " << p.effective - p.ratio + << endl; + cerr << "BQResampler: peak-to-zero " << p.peak_to_zero + << ", scale " << p.scale + << endl; + } + + return p; +} + +BQResampler::params +BQResampler::pick_params(double ratio) const +{ + // Farey algorithm, see + // https://www.johndcook.com/blog/2010/10/20/best-rational-approximation/ + int max_denom = 192000; + double a = 0.0, b = 1.0, c = 1.0, d = 0.0; + double pa = a, pb = b, pc = c, pd = d; + double eps = 1e-9; + while (b <= max_denom && d <= max_denom) { + double mediant = (a + c) / (b + d); + if (fabs(ratio - mediant) < eps) { + if (b + d <= max_denom) { + return fill_params(ratio, a + c, b + d); + } else if (d > b) { + return fill_params(ratio, c, d); + } else { + return fill_params(ratio, a, b); + } + } + if (ratio > mediant) { + pa = a; pb = b; + a += c; b += d; + } else { + pc = c; pd = d; + c += a; d += b; + } + } + if (fabs(ratio - (pc / pd)) < fabs(ratio - (pa / pb))) { + return fill_params(ratio, pc, pd); + } else { + return fill_params(ratio, pa, pb); + } +} + +void +BQResampler::phase_data_for(vector &target_phase_data, + floatbuf &target_phase_sorted_filter, + int filter_length, + const vector *filter, + int initial_phase, + int input_spacing, + int output_spacing) const +{ + target_phase_data.clear(); + target_phase_data.reserve(input_spacing); + + for (int p = 0; p < input_spacing; ++p) { + int next_phase = p - output_spacing; + while (next_phase < 0) next_phase += input_spacing; + next_phase %= input_spacing; + double dspace = double(input_spacing); + int zip_length = ceil(double(filter_length - p) / dspace); + int drop = ceil(double(max(0, output_spacing - p)) / dspace); + phase_rec phase; + phase.next_phase = next_phase; + phase.drop = drop; + phase.length = zip_length; + phase.start_index = 0; // we fill this in below if needed + target_phase_data.push_back(phase); + } + + if (m_dynamism == RatioMostlyFixed) { + if (!filter) throw std::logic_error("filter required at phase_data_for in RatioMostlyFixed mode"); + target_phase_sorted_filter.clear(); + target_phase_sorted_filter.reserve(filter_length); + for (int p = initial_phase; ; ) { + phase_rec &phase = target_phase_data[p]; + phase.start_index = target_phase_sorted_filter.size(); + for (int i = 0; i < phase.length; ++i) { + target_phase_sorted_filter.push_back + ((*filter)[i * input_spacing + p]); + } + p = phase.next_phase; + if (p == initial_phase) { + break; + } + } + } +} + +vector +BQResampler::make_filter(int filter_length, double peak_to_zero) const +{ + vector filter; + filter.reserve(filter_length); + + vector kaiser = kaiser_for(m_qparams.k_snr, m_qparams.k_transition, + 1, filter_length); + int k_length = kaiser.size(); + + if (k_length == filter_length) { + sinc_multiply(peak_to_zero, kaiser); + return kaiser; + } else { + kaiser.push_back(0.0); + double m = double(k_length - 1) / double(filter_length - 1); + for (int i = 0; i < filter_length; ++i) { + double ix = i * m; + int iix = floor(ix); + double remainder = ix - iix; + double value = 0.0; + value += kaiser[iix] * (1.0 - remainder); + value += kaiser[iix+1] * remainder; + filter.push_back(value); + } + sinc_multiply(peak_to_zero, filter); + return filter; + } +} + +void +BQResampler::state_for_ratio(BQResampler::state &target_state, + double ratio, + const BQResampler::state &BQ_R__ prev_state) const +{ + params parameters = pick_params(ratio); + target_state.parameters = parameters; + + target_state.filter_length = + int(parameters.peak_to_zero * m_qparams.p_multiple + 1); + + if (target_state.filter_length % 2 == 0) { + ++target_state.filter_length; + } + + int half_length = target_state.filter_length / 2; // nb length is odd + int input_spacing = parameters.numerator; + int initial_phase = half_length % input_spacing; + + target_state.initial_phase = initial_phase; + target_state.current_phase = initial_phase; + + if (m_dynamism == RatioMostlyFixed) { + + if (m_debug_level > 0) { + cerr << "BQResampler: creating filter of length " + << target_state.filter_length << endl; + } + + vector filter = + make_filter(target_state.filter_length, parameters.peak_to_zero); + + phase_data_for(target_state.phase_info, + target_state.phase_sorted_filter, + target_state.filter_length, &filter, + target_state.initial_phase, + input_spacing, + parameters.denominator); + } else { + phase_data_for(target_state.phase_info, + target_state.phase_sorted_filter, + target_state.filter_length, 0, + target_state.initial_phase, + input_spacing, + parameters.denominator); + } + + int buffer_left = half_length / input_spacing; + int buffer_right = buffer_left + 1; + + int buffer_length = buffer_left + buffer_right; + buffer_length = max(buffer_length, + int(prev_state.buffer.size() / m_channels)); + + target_state.centre = buffer_length / 2; + target_state.left = target_state.centre - buffer_left; + target_state.fill = target_state.centre; + + buffer_length *= m_channels; + target_state.centre *= m_channels; + target_state.left *= m_channels; + target_state.fill *= m_channels; + + int n_phases = int(target_state.phase_info.size()); + + if (m_debug_level > 0) { + cerr << "BQResampler: " << m_channels << " channel(s) interleaved" + << ", buffer left " << buffer_left + << ", right " << buffer_right + << ", total " << buffer_length << endl; + + cerr << "BQResampler: input spacing " << input_spacing + << ", output spacing " << parameters.denominator + << ", initial phase " << initial_phase + << " of " << n_phases << endl; + } + + if (prev_state.buffer.size() > 0) { + if (int(prev_state.buffer.size()) == buffer_length) { + target_state.buffer = prev_state.buffer; + target_state.fill = prev_state.fill; + } else { + target_state.buffer = floatbuf(buffer_length, 0.0); + for (int i = 0; i < prev_state.fill; ++i) { + int offset = i - prev_state.centre; + int new_ix = offset + target_state.centre; + if (new_ix >= 0 && new_ix < buffer_length) { + target_state.buffer[new_ix] = prev_state.buffer[i]; + target_state.fill = new_ix + 1; + } + } + } + + int phases_then = int(prev_state.phase_info.size()); + double distance_through = + double(prev_state.current_phase) / double(phases_then); + target_state.current_phase = round(n_phases * distance_through); + if (target_state.current_phase >= n_phases) { + target_state.current_phase = n_phases - 1; + } + } else { + target_state.buffer = floatbuf(buffer_length, 0.0); + } +} + +double +BQResampler::reconstruct_one(state *s) const +{ + const phase_rec &pr = s->phase_info[s->current_phase]; + int phase_length = pr.length; + double result = 0.0; + + if (m_dynamism == RatioMostlyFixed) { + int phase_start = pr.start_index; + if (m_channels == 1) { + result = v_multiply_and_sum + (s->phase_sorted_filter.data() + phase_start, + s->buffer.data() + s->left, + phase_length); + } else { + for (int i = 0; i < phase_length; ++i) { + result += + s->phase_sorted_filter[phase_start + i] * + s->buffer[s->left + i * m_channels + s->current_channel]; + } + } + } else { + double m = double(m_proto_length - 1) / double(s->filter_length - 1); + for (int i = 0; i < phase_length; ++i) { + double sample = + s->buffer[s->left + i * m_channels + s->current_channel]; + int filter_index = i * s->parameters.numerator + s->current_phase; + double proto_index = m * filter_index; + int iix = floor(proto_index); + double remainder = proto_index - iix; + double filter_value = m_prototype[iix] * (1.0 - remainder); + filter_value += m_prototype[iix+1] * remainder; + result += filter_value * sample; + } + } + + s->current_channel = (s->current_channel + 1) % m_channels; + + if (s->current_channel == 0) { + + if (pr.drop > 0) { + int drop = pr.drop * m_channels; + v_move(s->buffer.data(), s->buffer.data() + drop, + int(s->buffer.size()) - drop); + for (int i = 1; i <= drop; ++i) { + s->buffer[s->buffer.size() - i] = 0.0; + } + s->fill -= drop; + } + + s->current_phase = pr.next_phase; + } + + return result * s->parameters.scale; +} + +} diff --git a/src/dsp/BQResampler.h b/src/dsp/BQResampler.h new file mode 100644 index 0000000..c05af1d --- /dev/null +++ b/src/dsp/BQResampler.h @@ -0,0 +1,165 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band Library + An audio time-stretching and pitch-shifting library. + Copyright 2007-2021 Particular Programs Ltd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. + + Alternatively, if you have a valid commercial licence for the + Rubber Band Library obtained by agreement with the copyright + holders, you may redistribute and/or modify it under the terms + described in that licence. + + If you wish to distribute code using the Rubber Band Library + under terms other than those of the GNU General Public License, + you must obtain a valid commercial licence before doing so. +*/ + +#ifndef BQ_BQRESAMPLER_H +#define BQ_BQRESAMPLER_H + +#include + +#include "system/Allocators.h" +#include "system/VectorOps.h" + +namespace RubberBand { + +class BQResampler +{ +public: + enum Quality { Best, FastestTolerable, Fastest }; + enum Dynamism { RatioOftenChanging, RatioMostlyFixed }; + enum RatioChange { SmoothRatioChange, SuddenRatioChange }; + + struct Parameters { + Quality quality; + Dynamism dynamism; + RatioChange ratioChange; + double referenceSampleRate; + int debugLevel; + + Parameters() : + quality(FastestTolerable), + dynamism(RatioMostlyFixed), + ratioChange(SmoothRatioChange), + referenceSampleRate(44100), + debugLevel(0) { } + }; + + BQResampler(Parameters parameters, int channels); + BQResampler(const BQResampler &); + + int resampleInterleaved(float *const out, int outspace, + const float *const in, int incount, + double ratio, bool final); + + void reset(); + +private: + struct QualityParams { + int p_multiple; + int proto_p; + double k_snr; + double k_transition; + double cut; + QualityParams(Quality); + }; + + const QualityParams m_qparams; + const Dynamism m_dynamism; + const RatioChange m_ratio_change; + const int m_debug_level; + const double m_initial_rate; + const int m_channels; + + struct params { + double ratio; + int numerator; + int denominator; + double effective; + double peak_to_zero; + double scale; + params() : ratio(1.0), numerator(1), denominator(1), + effective(1.0), peak_to_zero(0), scale(1.0) { } + }; + + struct phase_rec { + int next_phase; + int length; + int start_index; + int drop; + phase_rec() : next_phase(0), length(0), start_index(0), drop(0) { } + }; + + typedef std::vector > floatbuf; + + struct state { + params parameters; + int initial_phase; + int current_phase; + int current_channel; + int filter_length; + std::vector phase_info; + floatbuf phase_sorted_filter; + floatbuf buffer; + int left; + int centre; + int fill; + state() : initial_phase(0), current_phase(0), current_channel(0), + filter_length(0), left(0), centre(0), fill(0) { } + }; + + state m_state_a; + state m_state_b; + + state *m_s; // points at either m_state_a or m_state_b + state *m_fade; // whichever one m_s does not point to + + int m_fade_count; + + std::vector m_prototype; + int m_proto_length; + bool m_initialised; + + int gcd(int a, int b) const; + double bessel0(double x) const; + std::vector kaiser(double beta, int len) const; + void kaiser_params(double attenuation, double transition, + double &beta, int &len) const; + std::vector kaiser_for(double attenuation, double transition, + int minlen, int maxlen) const; + void sinc_multiply(double peak_to_zero, std::vector &buf) const; + + params fill_params(double ratio, int num, int denom) const; + params pick_params(double ratio) const; + + std::vector make_filter(int filter_length, + double peak_to_zero) const; + + void phase_data_for(std::vector &target_phase_data, + floatbuf &target_phase_sorted_filter, + int filter_length, + const std::vector *filter, + int initial_phase, + int input_spacing, + int output_spacing) const; + + void state_for_ratio(state &target_state, + double ratio, + const state &R__ prev_state) const; + + double reconstruct_one(state *s) const; + + BQResampler &operator=(const BQResampler &); // not provided +}; + +} + +#endif diff --git a/src/dsp/Resampler.cpp b/src/dsp/Resampler.cpp index 06199a4..dda917d 100644 --- a/src/dsp/Resampler.cpp +++ b/src/dsp/Resampler.cpp @@ -1,4 +1,4 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rubber Band Library @@ -22,7 +22,9 @@ */ #include "Resampler.h" -#include "base/Profiler.h" + +#include "system/Allocators.h" +#include "system/VectorOps.h" #include #include @@ -30,9 +32,6 @@ #include #include -#include "system/Allocators.h" -#include "system/VectorOps.h" - #ifdef HAVE_IPP #include #if (IPP_VERSION_MAJOR < 7) @@ -42,6 +41,10 @@ #endif #endif +#ifdef HAVE_SAMPLERATE +#define HAVE_LIBSAMPLERATE 1 +#endif + #ifdef HAVE_LIBSAMPLERATE #include #endif @@ -51,18 +54,26 @@ #endif #ifdef USE_SPEEX -#include "speex/speex_resampler.h" +#include "../speex/speex_resampler.h" +#endif + +#ifdef USE_BQRESAMPLER +#include "BQResampler.h" #endif #ifndef HAVE_IPP #ifndef HAVE_LIBSAMPLERATE #ifndef HAVE_LIBRESAMPLE #ifndef USE_SPEEX +#ifndef USE_BQRESAMPLER #error No resampler implementation selected! #endif #endif #endif #endif +#endif + +#define BQ_R__ R__ using namespace std; @@ -73,16 +84,16 @@ class Resampler::Impl public: virtual ~Impl() { } - virtual int resample(float *const R__ *const R__ out, + virtual int resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) = 0; - virtual int resampleInterleaved(float *const R__ out, + virtual int resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) = 0; @@ -99,20 +110,21 @@ namespace Resamplers { class D_IPP : public Resampler::Impl { public: - D_IPP(Resampler::Quality quality, int channels, double initialSampleRate, + D_IPP(Resampler::Quality quality, Resampler::RatioChange, + int channels, double initialSampleRate, int maxBufferSize, int debugLevel); ~D_IPP(); - int resample(float *const R__ *const R__ out, + int resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final); - int resampleInterleaved(float *const R__ out, + int resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final = false); @@ -144,6 +156,7 @@ protected: }; D_IPP::D_IPP(Resampler::Quality /* quality */, + Resampler::RatioChange /* ratioChange */, int channels, double initialSampleRate, int maxBufferSize, int debugLevel) : m_state(0), @@ -152,7 +165,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */, m_debugLevel(debugLevel) { if (m_debugLevel > 0) { - cerr << "Resampler::Resampler: using IPP implementation" << endl; + cerr << "Resampler::Resampler: using implementation: IPP" << endl; } m_window = 32; @@ -190,7 +203,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */, setBufSize(maxBufferSize + m_history); if (m_debugLevel > 1) { - cerr << "D_IPP: bufsize = " << m_bufsize << ", window = " << m_window << ", nStep = " << nStep << ", history = " << m_history << endl; + cerr << "bufsize = " << m_bufsize << ", window = " << m_window << ", nStep = " << nStep << ", history = " << m_history << endl; } int specSize = 0; @@ -214,18 +227,13 @@ D_IPP::D_IPP(Resampler::Quality /* quality */, 9.0f, m_state[c], hint); - - if (m_debugLevel > 1) { - cerr << "D_IPP: Resampler state size = " << specSize << ", allocated at " - << m_state[c] << endl; - } m_lastread[c] = m_history; m_time[c] = m_history; } if (m_debugLevel > 1) { - cerr << "D_IPP: Resampler init done" << endl; + cerr << "Resampler init done" << endl; } } @@ -248,9 +256,9 @@ D_IPP::setBufSize(int sz) { if (m_debugLevel > 1) { if (m_bufsize > 0) { - cerr << "D_IPP: resize bufsize " << m_bufsize << " -> "; + cerr << "resize bufsize " << m_bufsize << " -> "; } else { - cerr << "D_IPP: initialise bufsize to "; + cerr << "initialise bufsize to "; } } @@ -263,13 +271,13 @@ D_IPP::setBufSize(int sz) int n1 = m_bufsize + m_history + 2; if (m_debugLevel > 1) { - cerr << "D_IPP: inbuf allocating " << m_bufsize << " + " << m_history << " + 2 = " << n1 << endl; + cerr << "inbuf allocating " << m_bufsize << " + " << m_history << " + 2 = " << n1 << endl; } int n2 = (int)lrintf(ceil((m_bufsize - m_history) * m_factor + 2)); if (m_debugLevel > 1) { - cerr << "D_IPP: outbuf allocating (" << m_bufsize << " - " << m_history << ") * " << m_factor << " + 2 = " << n2 << endl; + cerr << "outbuf allocating (" << m_bufsize << " - " << m_history << ") * " << m_factor << " + 2 = " << n2 << endl; } m_inbuf = reallocate_and_zero_extend_channels @@ -277,30 +285,15 @@ D_IPP::setBufSize(int sz) m_outbuf = reallocate_and_zero_extend_channels (m_outbuf, m_channels, m_outbufsz, m_channels, n2); - + m_inbufsz = n1; m_outbufsz = n2; - - if (m_debugLevel > 2) { - - cerr << "D_IPP: inbuf ptr = " << m_inbuf << ", channel inbufs "; - for (int c = 0; c < m_channels; ++c) { - cerr << m_inbuf[c] << " "; - } - cerr << "at " << m_inbufsz * sizeof(float) << " bytes each" << endl; - - cerr << "D_IPP: outbuf ptr = " << m_outbuf << ", channel outbufs "; - for (int c = 0; c < m_channels; ++c) { - cerr << m_outbuf[c] << " "; - } - cerr << "at " << m_outbufsz * sizeof(float) << " bytes each" << endl; - } } int -D_IPP::resample(float *const R__ *const R__ out, +D_IPP::resample(float *const BQ_R__ *const BQ_R__ out, int outspace, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) @@ -311,7 +304,7 @@ D_IPP::resample(float *const R__ *const R__ out, } if (m_debugLevel > 2) { - cerr << "D_IPP: incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl; + cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl; } for (int c = 0; c < m_channels; ++c) { @@ -328,7 +321,7 @@ D_IPP::resample(float *const R__ *const R__ out, } if (m_debugLevel > 2) { - cerr << "D_IPP: lastread advanced to " << m_lastread[0] << endl; + cerr << "lastread advanced to " << m_lastread[0] << endl; } int got = doResample(outspace, ratio, final); @@ -341,9 +334,9 @@ D_IPP::resample(float *const R__ *const R__ out, } int -D_IPP::resampleInterleaved(float *const R__ out, +D_IPP::resampleInterleaved(float *const BQ_R__ out, int outspace, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) @@ -354,7 +347,7 @@ D_IPP::resampleInterleaved(float *const R__ out, } if (m_debugLevel > 2) { - cerr << "D_IPP: incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl; + cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl; } for (int c = 0; c < m_channels; ++c) { @@ -371,7 +364,7 @@ D_IPP::resampleInterleaved(float *const R__ out, } if (m_debugLevel > 2) { - cerr << "D_IPP: lastread advanced to " << m_lastread[0] << " after injection of " + cerr << "lastread advanced to " << m_lastread[0] << " after injection of " << incount << " samples" << endl; } @@ -392,20 +385,20 @@ D_IPP::doResample(int outspace, double ratio, bool final) int n = m_lastread[c] - m_history - int(m_time[c]); if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: at start, lastread = " << m_lastread[c] << ", history = " + cerr << "at start, lastread = " << m_lastread[c] << ", history = " << m_history << ", time = " << m_time[c] << ", therefore n = " << n << endl; } if (n <= 0) { if (c == 0 && m_debugLevel > 1) { - cerr << "D_IPP: not enough input samples to do anything" << endl; + cerr << "not enough input samples to do anything" << endl; } continue; } if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: before resample call, time = " << m_time[c] << endl; + cerr << "before resample call, time = " << m_time[c] << endl; } // We're committed to not overrunning outspace, so we need to @@ -414,7 +407,7 @@ D_IPP::doResample(int outspace, double ratio, bool final) int limit = int(floor(outspace / ratio)); if (n > limit) { if (c == 0 && m_debugLevel > 1) { - cerr << "D_IPP: trimming input samples from " << n << " to " << limit + cerr << "trimming input samples from " << n << " to " << limit << " to avoid overrunning " << outspace << " at output" << endl; } @@ -431,26 +424,26 @@ D_IPP::doResample(int outspace, double ratio, bool final) m_state[c]); int t = int(floor(m_time[c])); - + int moveFrom = t - m_history; - + if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: converted " << n << " samples to " << outcount + cerr << "converted " << n << " samples to " << outcount << " (nb outbufsz = " << m_outbufsz << "), time advanced to " << m_time[c] << endl; - cerr << "D_IPP: rounding time to " << t << ", lastread = " + cerr << "rounding time to " << t << ", lastread = " << m_lastread[c] << ", history = " << m_history << endl; - cerr << "D_IPP: will move " << m_lastread[c] - moveFrom + cerr << "will move " << m_lastread[c] - moveFrom << " unconverted samples back from index " << moveFrom << " to 0" << endl; } - + if (moveFrom >= m_lastread[c]) { moveFrom = m_lastread[c]; if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: number of samples to move is <= 0, " + cerr << "number of samples to move is <= 0, " << "not actually moving any" << endl; } } else { @@ -464,7 +457,7 @@ D_IPP::doResample(int outspace, double ratio, bool final) m_time[c] -= moveFrom; if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: lastread reduced to " << m_lastread[c] + cerr << "lastread reduced to " << m_lastread[c] << ", time reduced to " << m_time[c] << endl; } @@ -483,7 +476,7 @@ D_IPP::doResample(int outspace, double ratio, bool final) int additionalcount = 0; if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: final call, padding input with " << m_history + cerr << "final call, padding input with " << m_history << " zeros (symmetrical with m_history)" << endl; } @@ -492,14 +485,14 @@ D_IPP::doResample(int outspace, double ratio, bool final) } if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: before resample call, time = " << m_time[c] << endl; + cerr << "before resample call, time = " << m_time[c] << endl; } int nAdditional = m_lastread[c] - int(m_time[c]); if (n + nAdditional > limit) { if (c == 0 && m_debugLevel > 1) { - cerr << "D_IPP: trimming final input samples from " << nAdditional + cerr << "trimming final input samples from " << nAdditional << " to " << (limit - n) << " to avoid overrunning " << outspace << " at output" << endl; @@ -517,9 +510,9 @@ D_IPP::doResample(int outspace, double ratio, bool final) m_state[c]); if (c == 0 && m_debugLevel > 2) { - cerr << "D_IPP: converted " << n << " samples to " << additionalcount + cerr << "converted " << n << " samples to " << additionalcount << ", time advanced to " << m_time[c] << endl; - cerr << "D_IPP: outcount = " << outcount << ", additionalcount = " << additionalcount << ", sum " << outcount + additionalcount << endl; + cerr << "outcount = " << outcount << ", additionalcount = " << additionalcount << ", sum " << outcount + additionalcount << endl; } if (c == 0) { @@ -529,7 +522,7 @@ D_IPP::doResample(int outspace, double ratio, bool final) } if (m_debugLevel > 2) { - cerr << "D_IPP: returning " << outcount << " samples" << endl; + cerr << "returning " << outcount << " samples" << endl; } return outcount; @@ -548,20 +541,21 @@ D_IPP::reset() class D_SRC : public Resampler::Impl { public: - D_SRC(Resampler::Quality quality, int channels, double initialSampleRate, + D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange, + int channels, double initialSampleRate, int maxBufferSize, int m_debugLevel); ~D_SRC(); - int resample(float *const R__ *const R__ out, + int resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final); - int resampleInterleaved(float *const R__ out, + int resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final = false); @@ -579,11 +573,12 @@ protected: int m_ioutsize; double m_prevRatio; bool m_ratioUnset; + bool m_smoothRatios; int m_debugLevel; }; -D_SRC::D_SRC(Resampler::Quality quality, int channels, double, - int maxBufferSize, int debugLevel) : +D_SRC::D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange, + int channels, double, int maxBufferSize, int debugLevel) : m_src(0), m_iin(0), m_iout(0), @@ -592,25 +587,41 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, double, m_ioutsize(0), m_prevRatio(1.0), m_ratioUnset(true), + m_smoothRatios(ratioChange == Resampler::SmoothRatioChange), m_debugLevel(debugLevel) { if (m_debugLevel > 0) { - cerr << "Resampler::Resampler: using libsamplerate implementation" - << endl; + cerr << "Resampler::Resampler: using implementation: libsamplerate" + << endl; } + if (channels < 1) { + cerr << "Resampler::Resampler: unable to create resampler: invalid channel count " << channels << " supplied" << endl; +#ifdef NO_EXCEPTIONS + throw Resampler::ImplementationError; +#endif + return; + } + int err = 0; m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY : - quality == Resampler::Fastest ? SRC_LINEAR : - SRC_SINC_FASTEST, + quality == Resampler::Fastest ? SRC_SINC_FASTEST : + SRC_SINC_MEDIUM_QUALITY, channels, &err); if (err) { cerr << "Resampler::Resampler: failed to create libsamplerate resampler: " - << src_strerror(err) << endl; + << src_strerror(err) << endl; #ifndef NO_EXCEPTIONS throw Resampler::ImplementationError; #endif + return; + } else if (!m_src) { + cerr << "Resampler::Resampler: failed to create libsamplerate resampler, but no error reported?" << endl; +#ifndef NO_EXCEPTIONS + throw Resampler::ImplementationError; +#endif + return; } if (maxBufferSize > 0 && m_channels > 1) { @@ -631,9 +642,9 @@ D_SRC::~D_SRC() } int -D_SRC::resample(float *const R__ *const R__ out, +D_SRC::resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) @@ -661,15 +672,15 @@ D_SRC::resample(float *const R__ *const R__ out, } int -D_SRC::resampleInterleaved(float *const R__ out, +D_SRC::resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) { SRC_DATA data; - + // libsamplerate smooths the filter change over the duration of // the processing block to avoid artifacts due to sudden changes, // and it uses outcount to determine how long to smooth the change @@ -682,7 +693,7 @@ D_SRC::resampleInterleaved(float *const R__ out, outcount = int(ceil(incount * ratio) + 5); } - if (m_ratioUnset) { + if (m_ratioUnset || !m_smoothRatios) { // The first time we set a ratio, we want to do it directly src_set_ratio(m_src, ratio); @@ -724,10 +735,9 @@ D_SRC::resampleInterleaved(float *const R__ out, data.input_frames = incount; data.output_frames = outcount; - data.src_ratio = ratio; data.end_of_input = (final ? 1 : 0); - + int err = src_process(m_src, &data); if (err) { @@ -737,7 +747,7 @@ D_SRC::resampleInterleaved(float *const R__ out, throw Resampler::ImplementationError; #endif } - + return (int)data.output_frames_gen; } @@ -755,20 +765,21 @@ D_SRC::reset() class D_Resample : public Resampler::Impl { public: - D_Resample(Resampler::Quality quality, int channels, double initialSampleRate, + D_Resample(Resampler::Quality quality, Resampler::RatioChange, + int channels, double initialSampleRate, int maxBufferSize, int m_debugLevel); ~D_Resample(); - int resample(float *const R__ *const R__ out, + int resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final); - int resampleInterleaved(float *const R__ out, + int resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final); @@ -799,7 +810,7 @@ D_Resample::D_Resample(Resampler::Quality quality, m_debugLevel(debugLevel) { if (m_debugLevel > 0) { - cerr << "Resampler::Resampler: using libresample implementation" + cerr << "Resampler::Resampler: using implementation: libresample" << endl; } @@ -836,9 +847,9 @@ D_Resample::~D_Resample() } int -D_Resample::resample(float *const R__ *const R__ out, +D_Resample::resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) @@ -895,9 +906,9 @@ D_Resample::resample(float *const R__ *const R__ out, } int -D_Resample::resampleInterleaved(float *const R__ out, +D_Resample::resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) @@ -937,25 +948,174 @@ D_Resample::reset() #endif /* HAVE_LIBRESAMPLE */ +#ifdef USE_BQRESAMPLER + +class D_BQResampler : public Resampler::Impl +{ +public: + D_BQResampler(Resampler::Parameters params, int channels); + ~D_BQResampler(); + + int resample(float *const BQ_R__ *const BQ_R__ out, + int outcount, + const float *const BQ_R__ *const BQ_R__ in, + int incount, + double ratio, + bool final); + + int resampleInterleaved(float *const BQ_R__ out, + int outcount, + const float *const BQ_R__ in, + int incount, + double ratio, + bool final = false); + + int getChannelCount() const { return m_channels; } + + void reset(); + +protected: + BQResampler *m_resampler; + float *m_iin; + float *m_iout; + int m_channels; + int m_iinsize; + int m_ioutsize; + int m_debugLevel; +}; + +D_BQResampler::D_BQResampler(Resampler::Parameters params, int channels) : + m_resampler(0), + m_iin(0), + m_iout(0), + m_channels(channels), + m_iinsize(0), + m_ioutsize(0), + m_debugLevel(params.debugLevel) +{ + if (m_debugLevel > 0) { + cerr << "Resampler::Resampler: using implementation: BQResampler" << endl; + } + + BQResampler::Parameters rparams; + switch (params.quality) { + case Resampler::Best: + rparams.quality = BQResampler::Best; + break; + case Resampler::FastestTolerable: + rparams.quality = BQResampler::FastestTolerable; + break; + case Resampler::Fastest: + rparams.quality = BQResampler::Fastest; + break; + } + switch (params.dynamism) { + case Resampler::RatioOftenChanging: + rparams.dynamism = BQResampler::RatioOftenChanging; + break; + case Resampler::RatioMostlyFixed: + rparams.dynamism = BQResampler::RatioMostlyFixed; + break; + } + switch (params.ratioChange) { + case Resampler::SmoothRatioChange: + rparams.ratioChange = BQResampler::SmoothRatioChange; + break; + case Resampler::SuddenRatioChange: + rparams.ratioChange = BQResampler::SuddenRatioChange; + break; + } + rparams.referenceSampleRate = params.initialSampleRate; + rparams.debugLevel = params.debugLevel; + + m_resampler = new BQResampler(rparams, m_channels); + + if (params.maxBufferSize > 0 && m_channels > 1) { + m_iinsize = params.maxBufferSize * m_channels; + m_ioutsize = params.maxBufferSize * m_channels * 2; + m_iin = allocate(m_iinsize); + m_iout = allocate(m_ioutsize); + } +} + +D_BQResampler::~D_BQResampler() +{ + delete m_resampler; + deallocate(m_iin); + deallocate(m_iout); +} + +int +D_BQResampler::resample(float *const BQ_R__ *const BQ_R__ out, + int outcount, + const float *const BQ_R__ *const BQ_R__ in, + int incount, + double ratio, + bool final) +{ + if (m_channels == 1) { + return resampleInterleaved(*out, outcount, *in, incount, ratio, final); + } + + if (incount * m_channels > m_iinsize) { + m_iin = reallocate(m_iin, m_iinsize, incount * m_channels); + m_iinsize = incount * m_channels; + } + if (outcount * m_channels > m_ioutsize) { + m_iout = reallocate(m_iout, m_ioutsize, outcount * m_channels); + m_ioutsize = outcount * m_channels; + } + + v_interleave(m_iin, in, m_channels, incount); + + int n = resampleInterleaved(m_iout, outcount, m_iin, incount, ratio, final); + + v_deinterleave(out, m_iout, m_channels, n); + + return n; +} + +int +D_BQResampler::resampleInterleaved(float *const BQ_R__ out, + int outcount, + const float *const BQ_R__ in, + int incount, + double ratio, + bool final) +{ + return m_resampler->resampleInterleaved(out, outcount, + in, incount, + ratio, final); +} + +void +D_BQResampler::reset() +{ + m_resampler->reset(); +} + +#endif /* USE_BQRESAMPLER */ + #ifdef USE_SPEEX class D_Speex : public Resampler::Impl { public: - D_Speex(Resampler::Quality quality, int channels, double initialSampleRate, + D_Speex(Resampler::Quality quality, Resampler::RatioChange, + int channels, double initialSampleRate, int maxBufferSize, int debugLevel); ~D_Speex(); - int resample(float *const R__ *const R__ out, + int resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final); - int resampleInterleaved(float *const R__ out, + int resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final = false); @@ -982,7 +1142,7 @@ protected: double ratio, bool final); }; -D_Speex::D_Speex(Resampler::Quality quality, +D_Speex::D_Speex(Resampler::Quality quality, Resampler::RatioChange, int channels, double initialSampleRate, int maxBufferSize, int debugLevel) : m_resampler(0), @@ -1000,7 +1160,7 @@ D_Speex::D_Speex(Resampler::Quality quality, quality == Resampler::Fastest ? 0 : 4); if (m_debugLevel > 0) { - cerr << "Resampler::Resampler: using Speex implementation with q = " + cerr << "Resampler::Resampler: using implementation: Speex with q = " << q << endl; } @@ -1093,9 +1253,9 @@ D_Speex::setRatio(double ratio) } int -D_Speex::resample(float *const R__ *const R__ out, +D_Speex::resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) @@ -1136,9 +1296,9 @@ D_Speex::resample(float *const R__ *const R__ out, } int -D_Speex::resampleInterleaved(float *const R__ out, +D_Speex::resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) @@ -1236,6 +1396,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels) #ifdef HAVE_LIBRESAMPLE m_method = 3; #endif +#ifdef USE_BQRESAMPLER + m_method = 4; +#endif #ifdef HAVE_LIBSAMPLERATE m_method = 1; #endif @@ -1251,6 +1414,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels) #ifdef USE_SPEEX m_method = 2; #endif +#ifdef USE_BQRESAMPLER + m_method = 4; +#endif #ifdef HAVE_LIBSAMPLERATE m_method = 1; #endif @@ -1266,6 +1432,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels) #ifdef USE_SPEEX m_method = 2; #endif +#ifdef USE_BQRESAMPLER + m_method = 4; +#endif #ifdef HAVE_LIBSAMPLERATE m_method = 1; #endif @@ -1281,7 +1450,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels) case 0: #ifdef HAVE_IPP d = new Resamplers::D_IPP - (params.quality, + (params.quality, params.ratioChange, channels, params.initialSampleRate, params.maxBufferSize, params.debugLevel); #else @@ -1293,7 +1462,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels) case 1: #ifdef HAVE_LIBSAMPLERATE d = new Resamplers::D_SRC - (params.quality, + (params.quality, params.ratioChange, channels, params.initialSampleRate, params.maxBufferSize, params.debugLevel); #else @@ -1305,7 +1474,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels) case 2: #ifdef USE_SPEEX d = new Resamplers::D_Speex - (params.quality, + (params.quality, params.ratioChange, channels, params.initialSampleRate, params.maxBufferSize, params.debugLevel); #else @@ -1317,12 +1486,21 @@ Resampler::Resampler(Resampler::Parameters params, int channels) case 3: #ifdef HAVE_LIBRESAMPLE d = new Resamplers::D_Resample - (params.quality, + (params.quality, params.ratioChange, channels, params.initialSampleRate, params.maxBufferSize, params.debugLevel); #else cerr << "Resampler::Resampler: No implementation available!" << endl; abort(); +#endif + break; + + case 4: +#ifdef USE_BQRESAMPLER + d = new Resamplers::D_BQResampler(params, channels); +#else + cerr << "Resampler::Resampler: No implementation available!" << endl; + abort(); #endif break; } @@ -1340,26 +1518,24 @@ Resampler::~Resampler() } int -Resampler::resample(float *const R__ *const R__ out, +Resampler::resample(float *const BQ_R__ *const BQ_R__ out, int outcount, - const float *const R__ *const R__ in, + const float *const BQ_R__ *const BQ_R__ in, int incount, double ratio, bool final) { - Profiler profiler("Resampler::resample"); return d->resample(out, outcount, in, incount, ratio, final); } int -Resampler::resampleInterleaved(float *const R__ out, +Resampler::resampleInterleaved(float *const BQ_R__ out, int outcount, - const float *const R__ in, + const float *const BQ_R__ in, int incount, double ratio, bool final) { - Profiler profiler("Resampler::resampleInterleaved"); return d->resampleInterleaved(out, outcount, in, incount, ratio, final); } diff --git a/src/dsp/Resampler.h b/src/dsp/Resampler.h index 4a1f723..1bb28a8 100644 --- a/src/dsp/Resampler.h +++ b/src/dsp/Resampler.h @@ -1,4 +1,4 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +//* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rubber Band Library @@ -32,6 +32,9 @@ class Resampler { public: enum Quality { Best, FastestTolerable, Fastest }; + enum Dynamism { RatioOftenChanging, RatioMostlyFixed }; + enum RatioChange { SmoothRatioChange, SuddenRatioChange }; + enum Exception { ImplementationError }; struct Parameters { @@ -41,6 +44,22 @@ public: */ Quality quality; + /** + * Performance hint indicating whether the ratio is expected + * to change regularly or not. If not, more work may happen on + * ratio changes to reduce work when ratio is unchanged. + */ + Dynamism dynamism; + + /** + * Hint indicating whether to smooth transitions, via filter + * interpolation or some such method, at ratio change + * boundaries, or whether to make a precise switch to the new + * ratio without regard to audible artifacts. The actual + * effect of this depends on the implementation in use. + */ + RatioChange ratioChange; + /** * Rate of expected input prior to resampling: may be used to * determine the filter bandwidth for the quality setting. If @@ -67,6 +86,8 @@ public: Parameters() : quality(FastestTolerable), + dynamism(RatioMostlyFixed), + ratioChange(SmoothRatioChange), initialSampleRate(44100), maxBufferSize(0), debugLevel(0) { } diff --git a/src/system/Allocators.h b/src/system/Allocators.h index 355bf29..1536954 100644 --- a/src/system/Allocators.h +++ b/src/system/Allocators.h @@ -29,6 +29,8 @@ #include // for std::bad_alloc #include +#include + #ifndef HAVE_POSIX_MEMALIGN #ifndef _WIN32 #ifndef __APPLE__ @@ -309,6 +311,96 @@ private: T *m_t; }; +/** Allocator for use with STL classes, e.g. vector, to ensure + * alignment. Based on example code by Stephan T. Lavavej. + * + * e.g. std::vector > v; + */ +template +class StlAllocator +{ +public: + typedef T *pointer; + typedef const T *const_pointer; + typedef T &reference; + typedef const T &const_reference; + typedef T value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + StlAllocator() { } + StlAllocator(const StlAllocator&) { } + template StlAllocator(const StlAllocator&) { } + ~StlAllocator() { } + + T * + allocate(const size_t n) const { + if (n == 0) return 0; + if (n > max_size()) { +#ifndef NO_EXCEPTIONS + throw std::length_error("Size overflow in StlAllocator::allocate()"); +#else + abort(); +#endif + } + return ::RubberBand::allocate(n); + } + + void + deallocate(T *const p, const size_t) const { + ::RubberBand::deallocate(p); + } + + template + T * + allocate(const size_t n, const U *) const { + return allocate(n); + } + + T * + address(T &r) const { + return &r; + } + + const T * + address(const T &s) const { + return &s; + } + + size_t + max_size() const { + return (static_cast(0) - static_cast(1)) / sizeof(T); + } + + template struct rebind { + typedef StlAllocator other; + }; + + bool + operator==(const StlAllocator &) const { + return true; + } + + bool + operator!=(const StlAllocator &) const { + return false; + } + + void + construct(T *const p, const T &t) const { + void *const pv = static_cast(p); + new (pv) T(t); + } + + void + destroy(T *const p) const { + p->~T(); + } + +private: + StlAllocator& operator=(const StlAllocator&); +}; + } #endif diff --git a/src/system/VectorOps.h b/src/system/VectorOps.h index ced40fc..5287be7 100644 --- a/src/system/VectorOps.h +++ b/src/system/VectorOps.h @@ -491,6 +491,58 @@ inline T v_sum(const T *const R__ src, return result; } +template +inline T v_multiply_and_sum(const T *const R__ src1, + const T *const R__ src2, + const int count) +{ + T result = T(); + for (int i = 0; i < count; ++i) { + result += src1[i] * src2[i]; + } + return result; +} + +#if defined HAVE_IPP +template<> +inline float v_multiply_and_sum(const float *const R__ src1, + const float *const R__ src2, + const int count) +{ + float dp; + ippsDotProd_32f(src1, src2, count, &dp); + return dp; +} +template<> +inline double v_multiply_and_sum(const double *const R__ src1, + const double *const R__ src2, + const int count) +{ + double dp; + ippsDotProd_64f(src1, src2, count, &dp); + return dp; +} +#elif defined HAVE_VDSP +template<> +inline float v_multiply_and_sum(const float *const R__ src1, + const float *const R__ src2, + const int count) +{ + float dp; + vDSP_dotpr(src1, 1, src2, 1, &dp, count); + return dp; +} +template<> +inline double v_multiply_and_sum(const double *const R__ src1, + const double *const R__ src2, + const int count) +{ + double dp; + vDSP_dotprD(src1, 1, src2, 1, &dp, count); + return dp; +} +#endif + template inline void v_log(T *const R__ dst, const int count)