2021-05-10 18:11:35 +01:00
|
|
|
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
2007-11-06 21:41:16 +00:00
|
|
|
|
|
|
|
|
/*
|
2012-09-09 16:57:42 +01:00
|
|
|
Rubber Band Library
|
2007-11-06 21:41:16 +00:00
|
|
|
An audio time-stretching and pitch-shifting library.
|
2021-01-08 17:13:52 +00:00
|
|
|
Copyright 2007-2021 Particular Programs Ltd.
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
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.
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
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.
|
2007-11-06 21:41:16 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "Resampler.h"
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2021-10-04 12:21:39 +01:00
|
|
|
#include "../system/Allocators.h"
|
|
|
|
|
#include "../system/VectorOps.h"
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
2015-07-09 15:14:47 +01:00
|
|
|
#include <algorithm>
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
2018-05-14 20:41:25 +01:00
|
|
|
#include <ippversion.h>
|
|
|
|
|
#if (IPP_VERSION_MAJOR < 7)
|
2020-10-22 14:00:32 +01:00
|
|
|
#error Unsupported IPP version, must be >= 7
|
2018-05-14 20:41:25 +01:00
|
|
|
#else
|
|
|
|
|
#include <ipps.h>
|
|
|
|
|
#endif
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
#ifdef HAVE_SAMPLERATE
|
|
|
|
|
#define HAVE_LIBSAMPLERATE 1
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
2007-11-06 21:41:16 +00:00
|
|
|
#include <samplerate.h>
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
|
|
|
|
#include <libresample.h>
|
|
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef USE_SPEEX
|
2021-05-10 18:11:35 +01:00
|
|
|
#include "../speex/speex_resampler.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BQRESAMPLER
|
|
|
|
|
#include "BQResampler.h"
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifndef HAVE_IPP
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifndef HAVE_LIBSAMPLERATE
|
|
|
|
|
#ifndef HAVE_LIBRESAMPLE
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifndef USE_SPEEX
|
2021-05-10 18:11:35 +01:00
|
|
|
#ifndef USE_BQRESAMPLER
|
2011-01-07 21:46:36 +00:00
|
|
|
#error No resampler implementation selected!
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
|
|
|
|
#endif
|
2021-05-10 18:11:35 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define BQ_R__ R__
|
2011-01-07 21:46:36 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
using namespace std;
|
|
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
namespace RubberBand {
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
class Resampler::Impl
|
2008-05-22 16:54:27 +00:00
|
|
|
{
|
|
|
|
|
public:
|
2020-10-21 15:05:14 +01:00
|
|
|
virtual ~Impl() { }
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
virtual int resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2008-05-22 16:54:27 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2008-05-22 16:54:27 +00:00
|
|
|
bool final) = 0;
|
2010-03-24 09:44:51 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
virtual int resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2010-03-24 09:44:51 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2010-03-24 09:44:51 +00:00
|
|
|
bool final) = 0;
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2009-09-17 13:01:21 +00:00
|
|
|
virtual int getChannelCount() const = 0;
|
2021-05-13 18:04:43 +01:00
|
|
|
virtual double getEffectiveRatio(double ratio) const = 0;
|
2009-09-17 13:01:21 +00:00
|
|
|
|
2008-05-22 16:54:27 +00:00
|
|
|
virtual void reset() = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace Resamplers {
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
class D_IPP : public Resampler::Impl
|
2012-09-09 16:57:42 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2021-05-10 18:11:35 +01:00
|
|
|
D_IPP(Resampler::Quality quality, Resampler::RatioChange,
|
|
|
|
|
int channels, double initialSampleRate,
|
2020-10-21 15:05:14 +01:00
|
|
|
int maxBufferSize, int debugLevel);
|
2012-09-09 16:57:42 +01:00
|
|
|
~D_IPP();
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final);
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final = false);
|
|
|
|
|
|
|
|
|
|
int getChannelCount() const { return m_channels; }
|
2021-05-13 18:04:43 +01:00
|
|
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
void reset();
|
|
|
|
|
|
|
|
|
|
protected:
|
2018-05-14 20:41:25 +01:00
|
|
|
// to m_outbuf
|
|
|
|
|
int doResample(int outcount, double ratio, bool final);
|
2020-10-21 15:05:14 +01:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
IppsResamplingPolyphase_32f **m_state;
|
2020-10-21 15:05:14 +01:00
|
|
|
double m_initialSampleRate;
|
2012-09-09 16:57:42 +01:00
|
|
|
float **m_inbuf;
|
|
|
|
|
size_t m_inbufsz;
|
|
|
|
|
float **m_outbuf;
|
|
|
|
|
size_t m_outbufsz;
|
|
|
|
|
int m_bufsize;
|
|
|
|
|
int m_channels;
|
|
|
|
|
int m_window;
|
|
|
|
|
float m_factor;
|
|
|
|
|
int m_history;
|
|
|
|
|
int *m_lastread;
|
|
|
|
|
double *m_time;
|
|
|
|
|
int m_debugLevel;
|
|
|
|
|
|
|
|
|
|
void setBufSize(int);
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-22 15:55:48 +01:00
|
|
|
D_IPP::D_IPP(Resampler::Quality /* quality */,
|
2021-05-10 18:11:35 +01:00
|
|
|
Resampler::RatioChange /* ratioChange */,
|
2020-10-22 15:55:48 +01:00
|
|
|
int channels, double initialSampleRate,
|
2020-10-21 15:05:14 +01:00
|
|
|
int maxBufferSize, int debugLevel) :
|
2012-09-09 16:57:42 +01:00
|
|
|
m_state(0),
|
2020-10-21 15:05:14 +01:00
|
|
|
m_initialSampleRate(initialSampleRate),
|
2012-09-09 16:57:42 +01:00
|
|
|
m_channels(channels),
|
|
|
|
|
m_debugLevel(debugLevel)
|
|
|
|
|
{
|
|
|
|
|
if (m_debugLevel > 0) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "Resampler::Resampler: using implementation: IPP" << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
2020-10-22 17:13:55 +01:00
|
|
|
m_window = 32;
|
|
|
|
|
int nStep = 64;
|
2018-05-14 20:41:25 +01:00
|
|
|
IppHintAlgorithm hint = ippAlgHintFast;
|
2012-09-09 16:57:42 +01:00
|
|
|
m_factor = 8; // initial upper bound on m_ratio, may be amended later
|
2020-10-21 15:05:14 +01:00
|
|
|
|
|
|
|
|
// This is largely based on the IPP docs and examples. Adapted
|
|
|
|
|
// from the docs:
|
|
|
|
|
//
|
|
|
|
|
// m_time defines the time value for which the first output
|
|
|
|
|
// sample is calculated. The input vector with indices less
|
|
|
|
|
// than m_time [whose initial value is m_history below]
|
|
|
|
|
// contains the history data of filters.
|
|
|
|
|
//
|
|
|
|
|
// The history length is [(1/2) window * max(1, 1/factor) ]+1
|
|
|
|
|
// where window is the size of the ideal lowpass filter
|
|
|
|
|
// window. The input vector must contain the same number of
|
|
|
|
|
// elements with indices greater than m_time + length for the
|
|
|
|
|
// right filter wing for the last element.
|
|
|
|
|
|
|
|
|
|
m_history = int(m_window * 0.5 * max(1.0, 1.0 / m_factor)) + 1;
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
m_state = new IppsResamplingPolyphase_32f *[m_channels];
|
|
|
|
|
|
|
|
|
|
m_lastread = new int[m_channels];
|
|
|
|
|
m_time = new double[m_channels];
|
|
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
m_inbufsz = 0;
|
|
|
|
|
m_outbufsz = 0;
|
|
|
|
|
m_inbuf = 0;
|
|
|
|
|
m_outbuf = 0;
|
|
|
|
|
m_bufsize = 0;
|
2020-10-21 15:05:14 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
setBufSize(maxBufferSize + m_history);
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "bufsize = " << m_bufsize << ", window = " << m_window << ", nStep = " << nStep << ", history = " << m_history << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
int specSize = 0;
|
|
|
|
|
ippsResamplePolyphaseGetSize_32f(float(m_window),
|
|
|
|
|
nStep,
|
|
|
|
|
&specSize,
|
|
|
|
|
hint);
|
|
|
|
|
if (specSize == 0) {
|
|
|
|
|
#ifndef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#else
|
|
|
|
|
abort();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
2018-05-14 20:41:25 +01:00
|
|
|
m_state[c] = (IppsResamplingPolyphase_32f *)ippsMalloc_8u(specSize);
|
|
|
|
|
ippsResamplePolyphaseInit_32f(float(m_window),
|
|
|
|
|
nStep,
|
|
|
|
|
0.95f,
|
|
|
|
|
9.0f,
|
|
|
|
|
m_state[c],
|
|
|
|
|
hint);
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
m_lastread[c] = m_history;
|
|
|
|
|
m_time[c] = m_history;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "Resampler init done" << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
D_IPP::~D_IPP()
|
|
|
|
|
{
|
2018-05-14 20:41:25 +01:00
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
ippsFree(m_state[c]);
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
deallocate_channels(m_inbuf, m_channels);
|
|
|
|
|
deallocate_channels(m_outbuf, m_channels);
|
|
|
|
|
|
|
|
|
|
delete[] m_lastread;
|
|
|
|
|
delete[] m_time;
|
|
|
|
|
delete[] m_state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
D_IPP::setBufSize(int sz)
|
|
|
|
|
{
|
|
|
|
|
if (m_debugLevel > 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
if (m_bufsize > 0) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "resize bufsize " << m_bufsize << " -> ";
|
2020-10-21 15:05:14 +01:00
|
|
|
} else {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "initialise bufsize to ";
|
2020-10-21 15:05:14 +01:00
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_bufsize = sz;
|
|
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (m_debugLevel > 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << m_bufsize << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
int n1 = m_bufsize + m_history + 2;
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "inbuf allocating " << m_bufsize << " + " << m_history << " + 2 = " << n1 << endl;
|
2020-10-21 15:05:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int n2 = (int)lrintf(ceil((m_bufsize - m_history) * m_factor + 2));
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "outbuf allocating (" << m_bufsize << " - " << m_history << ") * " << m_factor << " + 2 = " << n2 << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_inbuf = reallocate_and_zero_extend_channels
|
|
|
|
|
(m_inbuf, m_channels, m_inbufsz, m_channels, n1);
|
|
|
|
|
|
|
|
|
|
m_outbuf = reallocate_and_zero_extend_channels
|
|
|
|
|
(m_outbuf, m_channels, m_outbufsz, m_channels, n2);
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
m_inbufsz = n1;
|
|
|
|
|
m_outbufsz = n2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_IPP::resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outspace,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
if (ratio > m_factor) {
|
|
|
|
|
m_factor = ratio;
|
2020-10-21 15:05:14 +01:00
|
|
|
m_history = int(m_window * 0.5 * max(1.0, 1.0 / m_factor)) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
if (m_lastread[c] + incount + m_history > m_bufsize) {
|
|
|
|
|
setBufSize(m_lastread[c] + incount + m_history);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
for (int i = 0; i < incount; ++i) {
|
|
|
|
|
m_inbuf[c][m_lastread[c] + i] = in[c][i];
|
|
|
|
|
}
|
|
|
|
|
m_lastread[c] += incount;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
if (m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "lastread advanced to " << m_lastread[0] << endl;
|
2020-10-21 15:05:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int got = doResample(outspace, ratio, final);
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
2018-05-14 20:41:25 +01:00
|
|
|
v_copy(out[c], m_outbuf[c], got);
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
return got;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_IPP::resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outspace,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
if (ratio > m_factor) {
|
|
|
|
|
m_factor = ratio;
|
2020-10-21 15:05:14 +01:00
|
|
|
m_history = int(m_window * 0.5 * max(1.0, 1.0 / m_factor)) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
if (m_lastread[c] + incount + m_history > m_bufsize) {
|
|
|
|
|
setBufSize(m_lastread[c] + incount + m_history);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
for (int i = 0; i < incount; ++i) {
|
|
|
|
|
m_inbuf[c][m_lastread[c] + i] = in[i * m_channels + c];
|
|
|
|
|
}
|
|
|
|
|
m_lastread[c] += incount;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
if (m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "lastread advanced to " << m_lastread[0] << " after injection of "
|
2020-10-21 15:05:14 +01:00
|
|
|
<< incount << " samples" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int got = doResample(outspace, ratio, final);
|
2018-05-14 20:41:25 +01:00
|
|
|
|
|
|
|
|
v_interleave(out, m_outbuf, m_channels, got);
|
|
|
|
|
|
|
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
D_IPP::doResample(int outspace, double ratio, bool final)
|
|
|
|
|
{
|
|
|
|
|
int outcount = 0;
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
|
|
|
|
|
|
int n = m_lastread[c] - m_history - int(m_time[c]);
|
|
|
|
|
|
|
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "at start, lastread = " << m_lastread[c] << ", history = "
|
2018-05-14 20:41:25 +01:00
|
|
|
<< m_history << ", time = " << m_time[c] << ", therefore n = "
|
2020-10-21 15:05:14 +01:00
|
|
|
<< n << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
if (c == 0 && m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "not enough input samples to do anything" << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "before resample call, time = " << m_time[c] << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We're committed to not overrunning outspace, so we need to
|
|
|
|
|
// offer the resampler only enough samples to ensure it won't
|
|
|
|
|
|
|
|
|
|
int limit = int(floor(outspace / ratio));
|
|
|
|
|
if (n > limit) {
|
|
|
|
|
if (c == 0 && m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "trimming input samples from " << n << " to " << limit
|
2018-05-14 20:41:25 +01:00
|
|
|
<< " to avoid overrunning " << outspace << " at output"
|
2020-10-21 15:05:14 +01:00
|
|
|
<< endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
n = limit;
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
ippsResamplePolyphase_32f(m_inbuf[c],
|
|
|
|
|
n,
|
|
|
|
|
m_outbuf[c],
|
|
|
|
|
ratio,
|
|
|
|
|
1.0f,
|
|
|
|
|
&m_time[c],
|
|
|
|
|
&outcount,
|
|
|
|
|
m_state[c]);
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
int t = int(floor(m_time[c]));
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
int moveFrom = t - m_history;
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "converted " << n << " samples to " << outcount
|
2020-10-22 13:55:50 +01:00
|
|
|
<< " (nb outbufsz = " << m_outbufsz
|
|
|
|
|
<< "), time advanced to " << m_time[c] << endl;
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "rounding time to " << t << ", lastread = "
|
2020-10-22 13:55:50 +01:00
|
|
|
<< m_lastread[c] << ", history = " << m_history << endl;
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "will move " << m_lastread[c] - moveFrom
|
2020-10-22 13:55:50 +01:00
|
|
|
<< " unconverted samples back from index " << moveFrom
|
2020-10-21 15:05:14 +01:00
|
|
|
<< " to 0" << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
if (moveFrom >= m_lastread[c]) {
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
moveFrom = m_lastread[c];
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "number of samples to move is <= 0, "
|
2020-10-22 13:55:50 +01:00
|
|
|
<< "not actually moving any" << endl;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
v_move(m_inbuf[c],
|
|
|
|
|
m_inbuf[c] + moveFrom,
|
|
|
|
|
m_lastread[c] - moveFrom);
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2020-10-22 13:55:50 +01:00
|
|
|
m_lastread[c] -= moveFrom;
|
|
|
|
|
m_time[c] -= moveFrom;
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "lastread reduced to " << m_lastread[c]
|
2018-05-14 20:41:25 +01:00
|
|
|
<< ", time reduced to " << m_time[c]
|
2020-10-21 15:05:14 +01:00
|
|
|
<< endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (final && n < limit) {
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
// Looks like this actually produces too many samples
|
|
|
|
|
// (additionalcount is a few samples too large).
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
// Also, we aren't likely to have enough space in the
|
|
|
|
|
// output buffer as the caller won't have allowed for
|
|
|
|
|
// all the samples we're retrieving here.
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
// What to do?
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
int additionalcount = 0;
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "final call, padding input with " << m_history
|
2020-10-21 15:05:14 +01:00
|
|
|
<< " zeros (symmetrical with m_history)" << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
for (int i = 0; i < m_history; ++i) {
|
|
|
|
|
m_inbuf[c][m_lastread[c] + i] = 0.f;
|
|
|
|
|
}
|
2018-05-14 20:41:25 +01:00
|
|
|
|
|
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "before resample call, time = " << m_time[c] << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nAdditional = m_lastread[c] - int(m_time[c]);
|
|
|
|
|
|
|
|
|
|
if (n + nAdditional > limit) {
|
|
|
|
|
if (c == 0 && m_debugLevel > 1) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "trimming final input samples from " << nAdditional
|
2018-05-14 20:41:25 +01:00
|
|
|
<< " to " << (limit - n)
|
|
|
|
|
<< " to avoid overrunning " << outspace << " at output"
|
2020-10-21 15:05:14 +01:00
|
|
|
<< endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
nAdditional = limit - n;
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
ippsResamplePolyphase_32f(m_inbuf[c],
|
|
|
|
|
nAdditional,
|
|
|
|
|
m_outbuf[c],
|
|
|
|
|
ratio,
|
|
|
|
|
1.0f,
|
|
|
|
|
&m_time[c],
|
|
|
|
|
&additionalcount,
|
|
|
|
|
m_state[c]);
|
|
|
|
|
|
|
|
|
|
if (c == 0 && m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "converted " << n << " samples to " << additionalcount
|
2020-10-21 15:05:14 +01:00
|
|
|
<< ", time advanced to " << m_time[c] << endl;
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "outcount = " << outcount << ", additionalcount = " << additionalcount << ", sum " << outcount + additionalcount << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (c == 0) {
|
|
|
|
|
outcount += additionalcount;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-14 20:41:25 +01:00
|
|
|
if (m_debugLevel > 2) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "returning " << outcount << " samples" << endl;
|
2018-05-14 20:41:25 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
return outcount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
D_IPP::reset()
|
|
|
|
|
{
|
|
|
|
|
//!!!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_IPP */
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
class D_SRC : public Resampler::Impl
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
|
|
|
|
public:
|
2021-05-10 18:11:35 +01:00
|
|
|
D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange,
|
|
|
|
|
int channels, double initialSampleRate,
|
2020-10-21 15:05:14 +01:00
|
|
|
int maxBufferSize, int m_debugLevel);
|
2008-05-22 16:54:27 +00:00
|
|
|
~D_SRC();
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2008-05-22 16:54:27 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2008-05-22 16:54:27 +00:00
|
|
|
bool final);
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2010-03-24 09:44:51 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2010-03-24 09:44:51 +00:00
|
|
|
bool final = false);
|
|
|
|
|
|
2009-09-17 13:01:21 +00:00
|
|
|
int getChannelCount() const { return m_channels; }
|
2021-05-13 18:04:43 +01:00
|
|
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
2009-09-17 13:01:21 +00:00
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
void reset();
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
SRC_STATE *m_src;
|
|
|
|
|
float *m_iin;
|
|
|
|
|
float *m_iout;
|
2008-05-22 16:54:27 +00:00
|
|
|
int m_channels;
|
|
|
|
|
int m_iinsize;
|
|
|
|
|
int m_ioutsize;
|
2020-10-21 17:16:15 +01:00
|
|
|
double m_prevRatio;
|
2020-11-16 09:11:12 +00:00
|
|
|
bool m_ratioUnset;
|
2021-05-10 18:11:35 +01:00
|
|
|
bool m_smoothRatios;
|
2008-05-22 16:54:27 +00:00
|
|
|
int m_debugLevel;
|
2007-11-06 21:41:16 +00:00
|
|
|
};
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
D_SRC::D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange,
|
|
|
|
|
int channels, double, int maxBufferSize, int debugLevel) :
|
2007-11-06 21:41:16 +00:00
|
|
|
m_src(0),
|
|
|
|
|
m_iin(0),
|
|
|
|
|
m_iout(0),
|
|
|
|
|
m_channels(channels),
|
|
|
|
|
m_iinsize(0),
|
2008-05-22 16:54:27 +00:00
|
|
|
m_ioutsize(0),
|
2020-10-21 17:16:15 +01:00
|
|
|
m_prevRatio(1.0),
|
2020-11-16 09:11:12 +00:00
|
|
|
m_ratioUnset(true),
|
2021-05-10 18:11:35 +01:00
|
|
|
m_smoothRatios(ratioChange == Resampler::SmoothRatioChange),
|
2008-05-22 16:54:27 +00:00
|
|
|
m_debugLevel(debugLevel)
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
2008-05-22 16:54:27 +00:00
|
|
|
if (m_debugLevel > 0) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "Resampler::Resampler: using implementation: libsamplerate"
|
|
|
|
|
<< endl;
|
2008-05-22 16:54:27 +00:00
|
|
|
}
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
if (channels < 1) {
|
|
|
|
|
cerr << "Resampler::Resampler: unable to create resampler: invalid channel count " << channels << " supplied" << endl;
|
|
|
|
|
#ifdef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
int err = 0;
|
2008-05-22 16:54:27 +00:00
|
|
|
m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY :
|
2021-05-10 18:11:35 +01:00
|
|
|
quality == Resampler::Fastest ? SRC_SINC_FASTEST :
|
|
|
|
|
SRC_SINC_MEDIUM_QUALITY,
|
2007-11-06 21:41:16 +00:00
|
|
|
channels, &err);
|
|
|
|
|
|
2008-07-01 14:54:31 +00:00
|
|
|
if (err) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: failed to create libsamplerate resampler: "
|
2021-05-10 18:11:35 +01:00
|
|
|
<< 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;
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifndef NO_EXCEPTIONS
|
2011-11-25 11:11:59 +00:00
|
|
|
throw Resampler::ImplementationError;
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2021-05-10 18:11:35 +01:00
|
|
|
return;
|
2008-07-01 14:54:31 +00:00
|
|
|
}
|
2007-11-06 21:41:16 +00:00
|
|
|
|
|
|
|
|
if (maxBufferSize > 0 && m_channels > 1) {
|
|
|
|
|
m_iinsize = maxBufferSize * m_channels;
|
|
|
|
|
m_ioutsize = maxBufferSize * m_channels * 2;
|
2009-09-17 13:01:21 +00:00
|
|
|
m_iin = allocate<float>(m_iinsize);
|
|
|
|
|
m_iout = allocate<float>(m_ioutsize);
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
2008-07-04 14:19:32 +00:00
|
|
|
|
|
|
|
|
reset();
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-22 16:54:27 +00:00
|
|
|
D_SRC::~D_SRC()
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
|
|
|
|
src_delete(m_src);
|
2011-01-07 21:46:36 +00:00
|
|
|
deallocate(m_iin);
|
|
|
|
|
deallocate(m_iout);
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-22 16:54:27 +00:00
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_SRC::resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2008-05-22 16:54:27 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2008-05-22 16:54:27 +00:00
|
|
|
bool final)
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
|
|
|
|
if (m_channels == 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
return resampleInterleaved(*out, outcount, *in, incount, ratio, final);
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
if (incount * m_channels > m_iinsize) {
|
|
|
|
|
m_iin = reallocate<float>(m_iin, m_iinsize, incount * m_channels);
|
|
|
|
|
m_iinsize = incount * m_channels;
|
2008-07-01 14:54:31 +00:00
|
|
|
}
|
2020-10-21 15:05:14 +01:00
|
|
|
if (outcount * m_channels > m_ioutsize) {
|
|
|
|
|
m_iout = reallocate<float>(m_iout, m_ioutsize, outcount * m_channels);
|
|
|
|
|
m_ioutsize = outcount * m_channels;
|
2010-03-24 09:44:51 +00:00
|
|
|
}
|
2020-10-21 15:05:14 +01:00
|
|
|
|
|
|
|
|
v_interleave(m_iin, in, m_channels, incount);
|
2010-03-24 09:44:51 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
int n = resampleInterleaved(m_iout, outcount, m_iin, incount, ratio, final);
|
2010-03-24 09:44:51 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
v_deinterleave(out, m_iout, m_channels, n);
|
2020-09-15 13:46:18 +01:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
return n;
|
2010-03-24 09:44:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_SRC::resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2010-03-24 09:44:51 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2010-03-24 09:44:51 +00:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
SRC_DATA data;
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2020-10-21 17:16:15 +01:00
|
|
|
// 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
|
|
|
|
|
// over. This is a good thing, but it does mean (a) we should
|
|
|
|
|
// never pass outcount significantly longer than the actual
|
|
|
|
|
// expected output, and (b) when the ratio has just changed, we
|
|
|
|
|
// should aim to supply a shortish block next
|
|
|
|
|
|
|
|
|
|
if (outcount > int(ceil(incount * ratio) + 5)) {
|
|
|
|
|
outcount = int(ceil(incount * ratio) + 5);
|
|
|
|
|
}
|
2010-03-24 09:44:51 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
if (m_ratioUnset || !m_smoothRatios) {
|
2020-11-16 09:11:12 +00:00
|
|
|
|
|
|
|
|
// The first time we set a ratio, we want to do it directly
|
|
|
|
|
src_set_ratio(m_src, ratio);
|
|
|
|
|
m_ratioUnset = false;
|
|
|
|
|
m_prevRatio = ratio;
|
|
|
|
|
|
|
|
|
|
} else if (ratio != m_prevRatio) {
|
2020-10-21 17:16:15 +01:00
|
|
|
|
|
|
|
|
// If we are processing a block of appreciable length, turn it
|
|
|
|
|
// into two recursive calls, one for the short smoothing block
|
|
|
|
|
// and the other for the rest. Update m_prevRatio before doing
|
|
|
|
|
// this so that the calls don't themselves recurse!
|
|
|
|
|
m_prevRatio = ratio;
|
|
|
|
|
|
|
|
|
|
int shortBlock = 200;
|
|
|
|
|
if (outcount > shortBlock * 2) {
|
|
|
|
|
int shortIn = int(floor(shortBlock / ratio));
|
|
|
|
|
if (shortIn >= 10) {
|
|
|
|
|
int shortOut =
|
|
|
|
|
resampleInterleaved(out, shortBlock,
|
|
|
|
|
in, shortIn,
|
|
|
|
|
ratio, false);
|
|
|
|
|
int remainingOut = 0;
|
|
|
|
|
if (shortOut < outcount) {
|
|
|
|
|
remainingOut =
|
|
|
|
|
resampleInterleaved(out + shortOut * m_channels,
|
|
|
|
|
outcount - shortOut,
|
|
|
|
|
in + shortIn * m_channels,
|
|
|
|
|
incount - shortIn,
|
|
|
|
|
ratio, final);
|
|
|
|
|
}
|
|
|
|
|
return shortOut + remainingOut;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-24 09:44:51 +00:00
|
|
|
|
|
|
|
|
data.data_in = const_cast<float *>(in);
|
|
|
|
|
data.data_out = out;
|
|
|
|
|
|
|
|
|
|
data.input_frames = incount;
|
|
|
|
|
data.output_frames = outcount;
|
|
|
|
|
data.src_ratio = ratio;
|
|
|
|
|
data.end_of_input = (final ? 1 : 0);
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2010-03-24 09:44:51 +00:00
|
|
|
int err = src_process(m_src, &data);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::process: libsamplerate error: "
|
|
|
|
|
<< src_strerror(err) << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifndef NO_EXCEPTIONS
|
2011-11-25 11:11:59 +00:00
|
|
|
throw Resampler::ImplementationError;
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
2021-05-10 18:11:35 +01:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
return (int)data.output_frames_gen;
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2008-05-22 16:54:27 +00:00
|
|
|
D_SRC::reset()
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
|
|
|
|
src_reset(m_src);
|
2020-11-16 09:11:12 +00:00
|
|
|
m_ratioUnset = true;
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif /* HAVE_LIBSAMPLERATE */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
class D_Resample : public Resampler::Impl
|
2011-01-07 21:46:36 +00:00
|
|
|
{
|
|
|
|
|
public:
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Resample(Resampler::Quality quality, Resampler::RatioChange,
|
|
|
|
|
int channels, double initialSampleRate,
|
2020-10-21 15:05:14 +01:00
|
|
|
int maxBufferSize, int m_debugLevel);
|
2011-01-07 21:46:36 +00:00
|
|
|
~D_Resample();
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2011-01-07 21:46:36 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2011-01-07 21:46:36 +00:00
|
|
|
bool final);
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2011-01-07 21:46:36 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2011-01-07 21:46:36 +00:00
|
|
|
bool final);
|
|
|
|
|
|
|
|
|
|
int getChannelCount() const { return m_channels; }
|
2021-05-13 18:04:43 +01:00
|
|
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
2011-01-07 21:46:36 +00:00
|
|
|
|
|
|
|
|
void reset();
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
void *m_src;
|
|
|
|
|
float *m_iin;
|
|
|
|
|
float *m_iout;
|
2020-10-21 15:05:14 +01:00
|
|
|
double m_lastRatio;
|
2011-01-07 21:46:36 +00:00
|
|
|
int m_channels;
|
|
|
|
|
int m_iinsize;
|
|
|
|
|
int m_ioutsize;
|
|
|
|
|
int m_debugLevel;
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
D_Resample::D_Resample(Resampler::Quality quality,
|
|
|
|
|
int channels, double, int maxBufferSize, int debugLevel) :
|
2011-01-07 21:46:36 +00:00
|
|
|
m_src(0),
|
|
|
|
|
m_iin(0),
|
|
|
|
|
m_iout(0),
|
|
|
|
|
m_channels(channels),
|
|
|
|
|
m_iinsize(0),
|
|
|
|
|
m_ioutsize(0),
|
|
|
|
|
m_debugLevel(debugLevel)
|
|
|
|
|
{
|
|
|
|
|
if (m_debugLevel > 0) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "Resampler::Resampler: using implementation: libresample"
|
2020-10-21 15:05:14 +01:00
|
|
|
<< endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float min_factor = 0.125f;
|
|
|
|
|
float max_factor = 8.0f;
|
|
|
|
|
|
|
|
|
|
m_src = resample_open(quality == Resampler::Best ? 1 : 0, min_factor, max_factor);
|
|
|
|
|
|
|
|
|
|
if (!m_src) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: failed to create libresample resampler: "
|
|
|
|
|
<< endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
throw Resampler::ImplementationError; //!!! of course, need to catch this!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxBufferSize > 0 && m_channels > 1) {
|
|
|
|
|
m_iinsize = maxBufferSize * m_channels;
|
|
|
|
|
m_ioutsize = maxBufferSize * m_channels * 2;
|
|
|
|
|
m_iin = allocate<float>(m_iinsize);
|
|
|
|
|
m_iout = allocate<float>(m_ioutsize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
D_Resample::~D_Resample()
|
|
|
|
|
{
|
|
|
|
|
resample_close(m_src);
|
|
|
|
|
if (m_iinsize > 0) {
|
|
|
|
|
deallocate(m_iin);
|
|
|
|
|
}
|
|
|
|
|
if (m_ioutsize > 0) {
|
|
|
|
|
deallocate(m_iout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Resample::resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2011-01-07 21:46:36 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2011-01-07 21:46:36 +00:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
float *data_in;
|
|
|
|
|
float *data_out;
|
|
|
|
|
int input_frames, output_frames, end_of_input, source_used;
|
|
|
|
|
float src_ratio;
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount = (int)lrint(ceil(incount * ratio));
|
2011-01-07 21:46:36 +00:00
|
|
|
|
|
|
|
|
if (m_channels == 1) {
|
|
|
|
|
data_in = const_cast<float *>(*in); //!!!???
|
|
|
|
|
data_out = *out;
|
|
|
|
|
} else {
|
|
|
|
|
if (incount * m_channels > m_iinsize) {
|
|
|
|
|
m_iin = reallocate<float>(m_iin, m_iinsize, incount * m_channels);
|
|
|
|
|
m_iinsize = incount * m_channels;
|
|
|
|
|
}
|
|
|
|
|
if (outcount * m_channels > m_ioutsize) {
|
|
|
|
|
m_iout = reallocate<float>(m_iout, m_ioutsize, outcount * m_channels);
|
|
|
|
|
m_ioutsize = outcount * m_channels;
|
|
|
|
|
}
|
|
|
|
|
v_interleave(m_iin, in, m_channels, incount);
|
|
|
|
|
data_in = m_iin;
|
|
|
|
|
data_out = m_iout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_frames = incount;
|
|
|
|
|
output_frames = outcount;
|
|
|
|
|
src_ratio = ratio;
|
|
|
|
|
end_of_input = (final ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
int output_frames_gen = resample_process(m_src,
|
|
|
|
|
src_ratio,
|
|
|
|
|
data_in,
|
|
|
|
|
input_frames,
|
|
|
|
|
end_of_input,
|
|
|
|
|
&source_used,
|
|
|
|
|
data_out,
|
|
|
|
|
output_frames);
|
|
|
|
|
|
|
|
|
|
if (output_frames_gen < 0) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::process: libresample error: "
|
|
|
|
|
<< endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
throw Resampler::ImplementationError; //!!! of course, need to catch this!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_channels > 1) {
|
|
|
|
|
v_deinterleave(out, m_iout, m_channels, output_frames_gen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output_frames_gen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Resample::resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2011-01-07 21:46:36 +00:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2011-01-07 21:46:36 +00:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
int input_frames, output_frames, end_of_input, source_used;
|
|
|
|
|
float src_ratio;
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount = (int)lrint(ceil(incount * ratio));
|
2011-01-07 21:46:36 +00:00
|
|
|
|
|
|
|
|
input_frames = incount;
|
|
|
|
|
output_frames = outcount;
|
|
|
|
|
src_ratio = ratio;
|
|
|
|
|
end_of_input = (final ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
int output_frames_gen = resample_process(m_src,
|
|
|
|
|
src_ratio,
|
|
|
|
|
const_cast<float *>(in),
|
|
|
|
|
input_frames,
|
|
|
|
|
end_of_input,
|
|
|
|
|
&source_used,
|
|
|
|
|
out,
|
|
|
|
|
output_frames);
|
|
|
|
|
|
|
|
|
|
if (output_frames_gen < 0) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::process: libresample error: "
|
|
|
|
|
<< endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
throw Resampler::ImplementationError; //!!! of course, need to catch this!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output_frames_gen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
D_Resample::reset()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_LIBRESAMPLE */
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
#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);
|
|
|
|
|
|
2021-05-13 18:04:43 +01:00
|
|
|
int getChannelCount() const {
|
|
|
|
|
return m_channels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double getEffectiveRatio(double ratio) const {
|
|
|
|
|
return m_resampler->getEffectiveRatio(ratio);
|
|
|
|
|
}
|
2021-05-10 18:11:35 +01:00
|
|
|
|
|
|
|
|
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<float>(m_iinsize);
|
|
|
|
|
m_iout = allocate<float>(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<float>(m_iin, m_iinsize, incount * m_channels);
|
|
|
|
|
m_iinsize = incount * m_channels;
|
|
|
|
|
}
|
|
|
|
|
if (outcount * m_channels > m_ioutsize) {
|
|
|
|
|
m_iout = reallocate<float>(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 */
|
|
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef USE_SPEEX
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
class D_Speex : public Resampler::Impl
|
2012-09-09 16:57:42 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Speex(Resampler::Quality quality, Resampler::RatioChange,
|
|
|
|
|
int channels, double initialSampleRate,
|
2020-10-21 15:05:14 +01:00
|
|
|
int maxBufferSize, int debugLevel);
|
2012-09-09 16:57:42 +01:00
|
|
|
~D_Speex();
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final);
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
int resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final = false);
|
|
|
|
|
|
|
|
|
|
int getChannelCount() const { return m_channels; }
|
2021-05-13 18:04:43 +01:00
|
|
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
void reset();
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
SpeexResamplerState *m_resampler;
|
2020-10-21 15:05:14 +01:00
|
|
|
double m_initialSampleRate;
|
2012-09-09 16:57:42 +01:00
|
|
|
float *m_iin;
|
|
|
|
|
float *m_iout;
|
|
|
|
|
int m_channels;
|
|
|
|
|
int m_iinsize;
|
|
|
|
|
int m_ioutsize;
|
2020-10-21 15:05:14 +01:00
|
|
|
double m_lastratio;
|
2012-09-09 16:57:42 +01:00
|
|
|
bool m_initial;
|
|
|
|
|
int m_debugLevel;
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
void setRatio(double);
|
|
|
|
|
void doResample(const float *in, unsigned int &incount,
|
|
|
|
|
float *out, unsigned int &outcount,
|
|
|
|
|
double ratio, bool final);
|
2012-09-09 16:57:42 +01:00
|
|
|
};
|
|
|
|
|
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Speex::D_Speex(Resampler::Quality quality, Resampler::RatioChange,
|
2020-10-21 15:05:14 +01:00
|
|
|
int channels, double initialSampleRate,
|
|
|
|
|
int maxBufferSize, int debugLevel) :
|
2012-09-09 16:57:42 +01:00
|
|
|
m_resampler(0),
|
2020-10-21 15:05:14 +01:00
|
|
|
m_initialSampleRate(initialSampleRate),
|
2012-09-09 16:57:42 +01:00
|
|
|
m_iin(0),
|
|
|
|
|
m_iout(0),
|
|
|
|
|
m_channels(channels),
|
|
|
|
|
m_iinsize(0),
|
|
|
|
|
m_ioutsize(0),
|
2020-10-21 15:05:14 +01:00
|
|
|
m_lastratio(-1.0),
|
2012-09-09 16:57:42 +01:00
|
|
|
m_initial(true),
|
|
|
|
|
m_debugLevel(debugLevel)
|
|
|
|
|
{
|
|
|
|
|
int q = (quality == Resampler::Best ? 10 :
|
|
|
|
|
quality == Resampler::Fastest ? 0 : 4);
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 0) {
|
2021-05-10 18:11:35 +01:00
|
|
|
cerr << "Resampler::Resampler: using implementation: Speex with q = "
|
2020-10-21 15:05:14 +01:00
|
|
|
<< q << endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
int rrate = int(round(m_initialSampleRate));
|
2020-09-15 13:46:18 +01:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
int err = 0;
|
|
|
|
|
m_resampler = speex_resampler_init_frac(m_channels,
|
|
|
|
|
1, 1,
|
2020-10-21 15:05:14 +01:00
|
|
|
rrate, rrate,
|
2012-09-09 16:57:42 +01:00
|
|
|
q,
|
|
|
|
|
&err);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (err) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: failed to create Speex resampler"
|
|
|
|
|
<< endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifndef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxBufferSize > 0 && m_channels > 1) {
|
|
|
|
|
m_iinsize = maxBufferSize * m_channels;
|
|
|
|
|
m_ioutsize = maxBufferSize * m_channels * 2;
|
|
|
|
|
m_iin = allocate<float>(m_iinsize);
|
|
|
|
|
m_iout = allocate<float>(m_ioutsize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
D_Speex::~D_Speex()
|
|
|
|
|
{
|
|
|
|
|
speex_resampler_destroy(m_resampler);
|
|
|
|
|
deallocate<float>(m_iin);
|
|
|
|
|
deallocate<float>(m_iout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-10-21 15:05:14 +01:00
|
|
|
D_Speex::setRatio(double ratio)
|
2012-09-09 16:57:42 +01:00
|
|
|
{
|
|
|
|
|
// Speex wants a ratio of two unsigned integers, not a single
|
|
|
|
|
// float. Let's do that.
|
|
|
|
|
|
|
|
|
|
unsigned int big = 272408136U;
|
|
|
|
|
unsigned int denom = 1, num = 1;
|
|
|
|
|
|
|
|
|
|
if (ratio < 1.f) {
|
|
|
|
|
denom = big;
|
|
|
|
|
double dnum = double(big) * double(ratio);
|
|
|
|
|
num = (unsigned int)dnum;
|
|
|
|
|
} else if (ratio > 1.f) {
|
|
|
|
|
num = big;
|
|
|
|
|
double ddenom = double(big) / double(ratio);
|
|
|
|
|
denom = (unsigned int)ddenom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "D_Speex: Desired ratio " << ratio << ", requesting ratio "
|
|
|
|
|
<< num << "/" << denom << " = " << float(double(num)/double(denom))
|
|
|
|
|
<< endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
2020-10-21 15:05:14 +01:00
|
|
|
|
|
|
|
|
int fromRate = int(round(m_initialSampleRate));
|
|
|
|
|
int toRate = int(round(m_initialSampleRate * ratio));
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
int err = speex_resampler_set_rate_frac
|
2020-10-21 15:05:14 +01:00
|
|
|
(m_resampler, denom, num, fromRate, toRate);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
cerr << "Resampler::Resampler: failed to set rate on Speex resampler"
|
|
|
|
|
<< endl;
|
|
|
|
|
#ifndef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
speex_resampler_get_ratio(m_resampler, &denom, &num);
|
|
|
|
|
|
|
|
|
|
if (m_debugLevel > 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "D_Speex: Desired ratio " << ratio << ", got ratio "
|
|
|
|
|
<< num << "/" << denom << " = " << float(double(num)/double(denom))
|
|
|
|
|
<< endl;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_lastratio = ratio;
|
|
|
|
|
|
|
|
|
|
if (m_initial) {
|
|
|
|
|
speex_resampler_skip_zeros(m_resampler);
|
|
|
|
|
m_initial = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Speex::resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
if (ratio != m_lastratio) {
|
|
|
|
|
setRatio(ratio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int uincount = incount;
|
2020-10-21 15:05:14 +01:00
|
|
|
unsigned int uoutcount = outcount;
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
float *data_in, *data_out;
|
|
|
|
|
|
|
|
|
|
if (m_channels == 1) {
|
|
|
|
|
data_in = const_cast<float *>(*in);
|
|
|
|
|
data_out = *out;
|
|
|
|
|
} else {
|
2020-10-21 15:05:14 +01:00
|
|
|
if (int(incount * m_channels) > m_iinsize) {
|
2012-09-09 16:57:42 +01:00
|
|
|
m_iin = reallocate<float>(m_iin, m_iinsize, incount * m_channels);
|
|
|
|
|
m_iinsize = incount * m_channels;
|
|
|
|
|
}
|
2020-10-21 15:05:14 +01:00
|
|
|
if (int(outcount * m_channels) > m_ioutsize) {
|
2012-09-09 16:57:42 +01:00
|
|
|
m_iout = reallocate<float>(m_iout, m_ioutsize, outcount * m_channels);
|
|
|
|
|
m_ioutsize = outcount * m_channels;
|
|
|
|
|
}
|
|
|
|
|
v_interleave(m_iin, in, m_channels, incount);
|
|
|
|
|
data_in = m_iin;
|
|
|
|
|
data_out = m_iout;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
doResample(data_in, uincount, data_out, uoutcount, ratio, final);
|
2013-05-15 10:35:30 +01:00
|
|
|
|
2012-09-09 16:57:42 +01:00
|
|
|
if (m_channels > 1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
v_deinterleave(out, m_iout, m_channels, uoutcount);
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
return uoutcount;
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
D_Speex::resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2012-09-09 16:57:42 +01:00
|
|
|
int incount,
|
2020-10-21 15:05:14 +01:00
|
|
|
double ratio,
|
2012-09-09 16:57:42 +01:00
|
|
|
bool final)
|
|
|
|
|
{
|
|
|
|
|
if (ratio != m_lastratio) {
|
|
|
|
|
setRatio(ratio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int uincount = incount;
|
2020-10-21 15:05:14 +01:00
|
|
|
unsigned int uoutcount = outcount;
|
2012-09-09 16:57:42 +01:00
|
|
|
|
|
|
|
|
float *data_in = const_cast<float *>(in);
|
|
|
|
|
float *data_out = out;
|
|
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
doResample(data_in, uincount, data_out, uoutcount, ratio, final);
|
|
|
|
|
|
|
|
|
|
return uoutcount;
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
void
|
|
|
|
|
D_Speex::doResample(const float *data_in, unsigned int &uincount,
|
|
|
|
|
float *data_out, unsigned int &uoutcount,
|
|
|
|
|
double ratio, bool final)
|
|
|
|
|
{
|
|
|
|
|
int initial_outcount = int(uoutcount);
|
|
|
|
|
|
|
|
|
|
int err = speex_resampler_process_interleaved_float
|
|
|
|
|
(m_resampler,
|
|
|
|
|
data_in, &uincount,
|
|
|
|
|
data_out, &uoutcount);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
cerr << "Resampler::Resampler: Speex resampler returned error "
|
|
|
|
|
<< err << endl;
|
|
|
|
|
#ifndef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2013-05-15 10:35:30 +01:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
if (final) {
|
|
|
|
|
int actual = int(uoutcount);
|
|
|
|
|
int expected = std::min(initial_outcount, int(round(uincount * ratio)));
|
|
|
|
|
if (actual < expected) {
|
|
|
|
|
unsigned int final_out = expected - actual;
|
|
|
|
|
unsigned int final_in = (unsigned int)(round(final_out / ratio));
|
|
|
|
|
if (final_in > 0) {
|
|
|
|
|
float *pad = allocate_and_zero<float>(final_in * m_channels);
|
|
|
|
|
err = speex_resampler_process_interleaved_float
|
|
|
|
|
(m_resampler,
|
|
|
|
|
pad, &final_in,
|
|
|
|
|
data_out + actual * m_channels, &final_out);
|
|
|
|
|
deallocate(pad);
|
|
|
|
|
uoutcount += final_out;
|
|
|
|
|
if (err) {
|
|
|
|
|
cerr << "Resampler::Resampler: Speex resampler returned error "
|
|
|
|
|
<< err << endl;
|
|
|
|
|
#ifndef NO_EXCEPTIONS
|
|
|
|
|
throw Resampler::ImplementationError;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-09 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
D_Speex::reset()
|
|
|
|
|
{
|
2017-09-26 09:51:22 +01:00
|
|
|
m_lastratio = -1.0; // force reset of ratio
|
|
|
|
|
m_initial = true;
|
2012-09-09 16:57:42 +01:00
|
|
|
speex_resampler_reset_mem(m_resampler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2008-05-22 16:54:27 +00:00
|
|
|
} /* end namespace Resamplers */
|
2007-11-06 21:41:16 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
Resampler::Resampler(Resampler::Parameters params, int channels)
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
2008-05-22 16:54:27 +00:00
|
|
|
m_method = -1;
|
2020-10-21 15:05:14 +01:00
|
|
|
|
|
|
|
|
if (params.initialSampleRate == 0) {
|
|
|
|
|
params.initialSampleRate = 44100;
|
|
|
|
|
}
|
2008-05-22 16:54:27 +00:00
|
|
|
|
2020-10-21 15:05:14 +01:00
|
|
|
switch (params.quality) {
|
2008-05-22 16:54:27 +00:00
|
|
|
|
|
|
|
|
case Resampler::Best:
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
|
|
|
|
m_method = 0;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USE_SPEEX
|
|
|
|
|
m_method = 2;
|
|
|
|
|
#endif
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
|
|
|
|
m_method = 3;
|
|
|
|
|
#endif
|
2021-05-10 18:11:35 +01:00
|
|
|
#ifdef USE_BQRESAMPLER
|
|
|
|
|
m_method = 4;
|
|
|
|
|
#endif
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
2008-05-22 16:54:27 +00:00
|
|
|
m_method = 1;
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Resampler::FastestTolerable:
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
|
|
|
|
m_method = 0;
|
|
|
|
|
#endif
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
|
|
|
|
m_method = 3;
|
|
|
|
|
#endif
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef USE_SPEEX
|
|
|
|
|
m_method = 2;
|
2020-09-29 11:04:18 +01:00
|
|
|
#endif
|
2021-05-10 18:11:35 +01:00
|
|
|
#ifdef USE_BQRESAMPLER
|
|
|
|
|
m_method = 4;
|
|
|
|
|
#endif
|
2020-09-29 11:04:18 +01:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
|
|
|
|
m_method = 1;
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Resampler::Fastest:
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
|
|
|
|
m_method = 0;
|
|
|
|
|
#endif
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
|
|
|
|
m_method = 3;
|
|
|
|
|
#endif
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef USE_SPEEX
|
|
|
|
|
m_method = 2;
|
|
|
|
|
#endif
|
2021-05-10 18:11:35 +01:00
|
|
|
#ifdef USE_BQRESAMPLER
|
|
|
|
|
m_method = 4;
|
|
|
|
|
#endif
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
2008-05-22 16:54:27 +00:00
|
|
|
m_method = 1;
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_method == -1) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
2008-05-22 16:54:27 +00:00
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (m_method) {
|
|
|
|
|
case 0:
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef HAVE_IPP
|
2020-10-21 15:05:14 +01:00
|
|
|
d = new Resamplers::D_IPP
|
2021-05-10 18:11:35 +01:00
|
|
|
(params.quality, params.ratioChange,
|
2020-10-21 15:05:14 +01:00
|
|
|
channels,
|
|
|
|
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
2012-09-09 16:57:42 +01:00
|
|
|
#else
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
2008-05-22 16:54:27 +00:00
|
|
|
abort();
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
2011-01-07 21:46:36 +00:00
|
|
|
#ifdef HAVE_LIBSAMPLERATE
|
2020-10-21 15:05:14 +01:00
|
|
|
d = new Resamplers::D_SRC
|
2021-05-10 18:11:35 +01:00
|
|
|
(params.quality, params.ratioChange,
|
2020-10-21 15:05:14 +01:00
|
|
|
channels,
|
|
|
|
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
2011-01-07 21:46:36 +00:00
|
|
|
#else
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
abort();
|
|
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2:
|
2012-09-09 16:57:42 +01:00
|
|
|
#ifdef USE_SPEEX
|
2020-10-21 15:05:14 +01:00
|
|
|
d = new Resamplers::D_Speex
|
2021-05-10 18:11:35 +01:00
|
|
|
(params.quality, params.ratioChange,
|
2020-10-21 15:05:14 +01:00
|
|
|
channels,
|
|
|
|
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
2012-09-09 16:57:42 +01:00
|
|
|
#else
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
2008-05-22 16:54:27 +00:00
|
|
|
abort();
|
2012-09-09 16:57:42 +01:00
|
|
|
#endif
|
2008-05-22 16:54:27 +00:00
|
|
|
break;
|
2011-01-07 21:46:36 +00:00
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
#ifdef HAVE_LIBRESAMPLE
|
2020-10-21 15:05:14 +01:00
|
|
|
d = new Resamplers::D_Resample
|
2021-05-10 18:11:35 +01:00
|
|
|
(params.quality, params.ratioChange,
|
2020-10-21 15:05:14 +01:00
|
|
|
channels,
|
|
|
|
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
2011-01-07 21:46:36 +00:00
|
|
|
#else
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
2011-01-07 21:46:36 +00:00
|
|
|
abort();
|
2021-05-10 18:11:35 +01:00
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
#ifdef USE_BQRESAMPLER
|
|
|
|
|
d = new Resamplers::D_BQResampler(params, channels);
|
|
|
|
|
#else
|
|
|
|
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
|
|
|
|
abort();
|
2011-01-07 21:46:36 +00:00
|
|
|
#endif
|
|
|
|
|
break;
|
2008-05-22 16:54:27 +00:00
|
|
|
}
|
2011-03-19 12:41:38 +00:00
|
|
|
|
|
|
|
|
if (!d) {
|
2020-10-21 15:05:14 +01:00
|
|
|
cerr << "Resampler::Resampler: Internal error: No implementation selected"
|
|
|
|
|
<< endl;
|
2011-03-19 12:41:38 +00:00
|
|
|
abort();
|
|
|
|
|
}
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Resampler::~Resampler()
|
|
|
|
|
{
|
2008-05-22 16:54:27 +00:00
|
|
|
delete d;
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-22 16:54:27 +00:00
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
Resampler::resample(float *const BQ_R__ *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ *const BQ_R__ in,
|
2020-10-21 15:05:14 +01:00
|
|
|
int incount,
|
|
|
|
|
double ratio,
|
|
|
|
|
bool final)
|
2007-11-06 21:41:16 +00:00
|
|
|
{
|
2020-10-21 15:05:14 +01:00
|
|
|
return d->resample(out, outcount, in, incount, ratio, final);
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
2010-03-24 09:44:51 +00:00
|
|
|
int
|
2021-05-10 18:11:35 +01:00
|
|
|
Resampler::resampleInterleaved(float *const BQ_R__ out,
|
2020-10-21 15:05:14 +01:00
|
|
|
int outcount,
|
2021-05-10 18:11:35 +01:00
|
|
|
const float *const BQ_R__ in,
|
2020-10-21 15:05:14 +01:00
|
|
|
int incount,
|
|
|
|
|
double ratio,
|
|
|
|
|
bool final)
|
2010-03-24 09:44:51 +00:00
|
|
|
{
|
2020-10-21 15:05:14 +01:00
|
|
|
return d->resampleInterleaved(out, outcount, in, incount, ratio, final);
|
2010-03-24 09:44:51 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-17 13:01:21 +00:00
|
|
|
int
|
|
|
|
|
Resampler::getChannelCount() const
|
|
|
|
|
{
|
|
|
|
|
return d->getChannelCount();
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-13 18:04:43 +01:00
|
|
|
double
|
|
|
|
|
Resampler::getEffectiveRatio(double ratio) const
|
|
|
|
|
{
|
|
|
|
|
return d->getEffectiveRatio(ratio);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-06 21:41:16 +00:00
|
|
|
void
|
|
|
|
|
Resampler::reset()
|
|
|
|
|
{
|
2008-05-22 16:54:27 +00:00
|
|
|
d->reset();
|
2007-11-06 21:41:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|