Commit 3dc522e1 authored by David Reid's avatar David Reid

Remove the Speex resampler.

parent b2ed5ab0
This code in the `thirdparty` directory is taken from opus-tools (https://github.com/xiph/opus-tools). Note
that unlike miniaudio, this code is _not_ public domain. The opus-tools license is below:
```
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
Note that miniaudio does not use any of this code by default and is strictly opt-in. While miniaudio reproduces
this license text in it's source redistributions (in this file, and in each source file), it does not have any
control over binary distributions. When opting-in to use the Speex resampler you will need to consider this if
you redistribute a binary.
#ifndef ma_speex_resampler_h
#define ma_speex_resampler_h
#define OUTSIDE_SPEEX
#define RANDOM_PREFIX ma_speex
#include "thirdparty/speex_resampler.h"
#if defined(_MSC_VER)
typedef unsigned __int64 spx_uint64_t;
#else
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
#endif
typedef unsigned long long spx_uint64_t;
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#endif
int ma_speex_resampler_get_required_input_frame_count(const SpeexResamplerState* st, spx_uint64_t out_len, spx_uint64_t* in_len);
int ma_speex_resampler_get_expected_output_frame_count(const SpeexResamplerState* st, spx_uint64_t in_len, spx_uint64_t* out_len);
#endif /* ma_speex_resampler_h */
#if defined(MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION)
/* The Speex resampler uses "inline", which is not defined for C89. We need to define it here. */
#if !defined(__cplusplus)
#if defined(__GNUC__) && !defined(_MSC_VER)
#if defined(__STRICT_ANSI__)
#if !defined(inline)
#define inline __inline__ __attribute__((always_inline))
#define MA_SPEEX_INLINE_DEFINED
#endif
#endif
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1400 /* 1400 = Visual Studio 2005 */
#define inline _inline
#define MA_SPEEX_INLINE_DEFINED
#endif
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable:4244) /* conversion from 'x' to 'y', possible loss of data */
#pragma warning(disable:4018) /* signed/unsigned mismatch */
#pragma warning(disable:4706) /* assignment within conditional expression */
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare" /* comparison between signed and unsigned integer expressions */
#endif
#include "thirdparty/resample.c"
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
#pragma GCC diagnostic pop
#endif
#if defined(MA_SPEEX_INLINE_DEFINED)
#undef inline
#undef MA_SPEEX_INLINE_DEFINED
#endif
EXPORT int ma_speex_resampler_get_required_input_frame_count(const SpeexResamplerState* st, spx_uint64_t out_len, spx_uint64_t* in_len)
{
spx_uint64_t count;
if (st == NULL || in_len == NULL) {
return RESAMPLER_ERR_INVALID_ARG;
}
*in_len = 0;
if (out_len == 0) {
return RESAMPLER_ERR_SUCCESS; /* Nothing to do. */
}
/* miniaudio only uses interleaved APIs so we can safely just use channel index 0 for the calculations. */
if (st->nb_channels == 0) {
return RESAMPLER_ERR_BAD_STATE;
}
count = out_len * st->int_advance;
count += (st->samp_frac_num[0] + (out_len * st->frac_advance)) / st->den_rate;
*in_len = count;
return RESAMPLER_ERR_SUCCESS;
}
EXPORT int ma_speex_resampler_get_expected_output_frame_count(const SpeexResamplerState* st, spx_uint64_t in_len, spx_uint64_t* out_len)
{
spx_uint64_t count;
spx_uint64_t last_sample;
spx_uint32_t samp_frac_num;
if (st == NULL || out_len == NULL) {
return RESAMPLER_ERR_INVALID_ARG;
}
*out_len = 0;
if (out_len == 0) {
return RESAMPLER_ERR_SUCCESS; /* Nothing to do. */
}
/* miniaudio only uses interleaved APIs so we can safely just use channel index 0 for the calculations. */
if (st->nb_channels == 0) {
return RESAMPLER_ERR_BAD_STATE;
}
count = 0;
last_sample = st->last_sample[0];
samp_frac_num = st->samp_frac_num[0];
while (!(last_sample >= in_len)) {
count += 1;
last_sample += st->int_advance;
samp_frac_num += st->frac_advance;
if (samp_frac_num >= st->den_rate) {
samp_frac_num -= st->den_rate;
last_sample += 1;
}
}
*out_len = count;
return RESAMPLER_ERR_SUCCESS;
}
#endif
/* Copyright (C) 2003 Jean-Marc Valin */
/**
@file arch.h
@brief Various architecture definitions Speex
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCH_H
#define ARCH_H
/* A couple test to catch stupid option combinations */
#ifdef FIXED_POINT
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#error Make up your mind. What CPU do you have?
#endif
#else
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#endif
#ifndef OUTSIDE_SPEEX
#include "speex/speexdsp_types.h"
#endif
#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#ifdef FIXED_POINT
typedef spx_int16_t spx_word16_t;
typedef spx_int32_t spx_word32_t;
typedef spx_word32_t spx_mem_t;
typedef spx_word16_t spx_coef_t;
typedef spx_word16_t spx_lsp_t;
typedef spx_word32_t spx_sig_t;
#define Q15ONE 32767
#define LPC_SCALING 8192
#define SIG_SCALING 16384
#define LSP_SCALING 8192.
#define GAMMA_SCALING 32768.
#define GAIN_SCALING 64
#define GAIN_SCALING_1 0.015625
#define LPC_SHIFT 13
#define LSP_SHIFT 13
#define SIG_SHIFT 14
#define GAIN_SHIFT 6
#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
#define VERY_SMALL 0
#define VERY_LARGE32 ((spx_word32_t)2147483647)
#define VERY_LARGE16 ((spx_word16_t)32767)
#define Q15_ONE ((spx_word16_t)32767)
#ifdef FIXED_DEBUG
#include "fixed_debug.h"
#else
#include "fixed_generic.h"
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#include "fixed_bfin.h"
#endif
#endif
#else
typedef float spx_mem_t;
typedef float spx_coef_t;
typedef float spx_lsp_t;
typedef float spx_sig_t;
typedef float spx_word16_t;
typedef float spx_word32_t;
#define Q15ONE 1.0f
#define LPC_SCALING 1.f
#define SIG_SCALING 1.f
#define LSP_SCALING 1.f
#define GAMMA_SCALING 1.f
#define GAIN_SCALING 1.f
#define GAIN_SCALING_1 1.f
#define VERY_SMALL 1e-15f
#define VERY_LARGE32 1e15f
#define VERY_LARGE16 1e15f
#define Q15_ONE ((spx_word16_t)1.f)
#define QCONST16(x,bits) (x)
#define QCONST32(x,bits) (x)
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define EXTRACT16(x) (x)
#define EXTEND32(x) (x)
#define SHR16(a,shift) (a)
#define SHL16(a,shift) (a)
#define SHR32(a,shift) (a)
#define SHL32(a,shift) (a)
#define PSHR16(a,shift) (a)
#define PSHR32(a,shift) (a)
#define VSHR32(a,shift) (a)
#define SATURATE16(x,a) (x)
#define SATURATE32(x,a) (x)
#define SATURATE32PSHR(x,shift,a) (x)
#define PSHR(a,shift) (a)
#define SHR(a,shift) (a)
#define SHL(a,shift) (a)
#define SATURATE(x,a) (x)
#define ADD16(a,b) ((a)+(b))
#define SUB16(a,b) ((a)-(b))
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
#define MULT16_16_Q11_32(a,b) ((a)*(b))
#define MULT16_16_Q13(a,b) ((a)*(b))
#define MULT16_16_Q14(a,b) ((a)*(b))
#define MULT16_16_Q15(a,b) ((a)*(b))
#define MULT16_16_P15(a,b) ((a)*(b))
#define MULT16_16_P13(a,b) ((a)*(b))
#define MULT16_16_P14(a,b) ((a)*(b))
#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define WORD2INT(x) ((x) < -32767.5f ? -32768 : \
((x) > 32766.5f ? 32767 : (spx_int16_t)floor(.5 + (x))))
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2
#define BITS_PER_CHAR 16
#define LOG2_BITS_PER_CHAR 4
#else
#define BYTES_PER_CHAR 1
#define BITS_PER_CHAR 8
#define LOG2_BITS_PER_CHAR 3
#endif
#ifdef FIXED_DEBUG
extern long long spx_mips;
#endif
#endif
/* Copyright (C) 2007-2008 Jean-Marc Valin
Copyright (C) 2008 Thorvald Natvig
File: resample.c
Arbitrary resampling code
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
The design goals of this code are:
- Very fast algorithm
- SIMD-friendly algorithm
- Low memory requirement
- Good *perceptual* quality (and not best SNR)
Warning: This resampler is relatively new. Although I think I got rid of
all the major bugs and I don't expect the API to change anymore, there
may be something I've missed. So use with caution.
This algorithm is based on this original resampling algorithm:
Smith, Julius O. Digital Audio Resampling Home Page
Center for Computer Research in Music and Acoustics (CCRMA),
Stanford University, 2007.
Web published at https://ccrma.stanford.edu/~jos/resample/.
There is one main difference, though. This resampler uses cubic
interpolation instead of linear interpolation in the above paper. This
makes the table much smaller and makes it possible to compute that table
on a per-stream basis. In turn, being able to tweak the table for each
stream makes it possible to both reduce complexity on simple ratios
(e.g. 2/3), and get rid of the rounding operations in the inner loop.
The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
static void *speex_alloc(int size) {return calloc(size,1);}
static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
static void speex_free(void *ptr) {free(ptr);}
#ifndef EXPORT
#define EXPORT
#endif
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
#include "speex/speex_resampler.h"
#include "arch.h"
#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include <math.h>
#include <limits.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define IMAX(a,b) ((a) > (b) ? (a) : (b))
#define IMIN(a,b) ((a) < (b) ? (a) : (b))
#ifndef NULL
#define NULL 0
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967295U
#endif
#if defined(__SSE__) && !defined(FIXED_POINT)
#include "resample_sse.h"
#endif
#ifdef USE_NEON
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else
#define FIXED_STACK_ALLOC 1024
#endif
typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
struct SpeexResamplerState_ {
spx_uint32_t in_rate;
spx_uint32_t out_rate;
spx_uint32_t num_rate;
spx_uint32_t den_rate;
int quality;
spx_uint32_t nb_channels;
spx_uint32_t filt_len;
spx_uint32_t mem_alloc_size;
spx_uint32_t buffer_size;
int int_advance;
int frac_advance;
float cutoff;
spx_uint32_t oversample;
int initialised;
int started;
/* These are per-channel */
spx_int32_t *last_sample;
spx_uint32_t *samp_frac_num;
spx_uint32_t *magic_samples;
spx_word16_t *mem;
spx_word16_t *sinc_table;
spx_uint32_t sinc_table_length;
resampler_basic_func resampler_ptr;
int in_stride;
int out_stride;
} ;
static const double kaiser12_table[68] = {
0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
0.00001000, 0.00000000};
/*
static const double kaiser12_table[36] = {
0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741,
0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762,
0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274,
0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466,
0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291,
0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000};
*/
static const double kaiser10_table[36] = {
0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000};
static const double kaiser8_table[36] = {
0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000};
static const double kaiser6_table[36] = {
0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000};
struct FuncDef {
const double *table;
int oversample;
};
static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
#define KAISER12 (&kaiser12_funcdef)
static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
#define KAISER10 (&kaiser10_funcdef)
static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
#define KAISER8 (&kaiser8_funcdef)
static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
#define KAISER6 (&kaiser6_funcdef)
struct QualityMapping {
int base_length;
int oversample;
float downsample_bandwidth;
float upsample_bandwidth;
const struct FuncDef *window_func;
};
/* This table maps conversion quality to internal parameters. There are two
reasons that explain why the up-sampling bandwidth is larger than the
down-sampling bandwidth:
1) When up-sampling, we can assume that the spectrum is already attenuated
close to the Nyquist rate (from an A/D or a previous resampling filter)
2) Any aliasing that occurs very close to the Nyquist rate will be masked
by the sinusoids/noise just below the Nyquist rate (guaranteed only for
up-sampling).
*/
static const struct QualityMapping quality_map[11] = {
{ 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */
{ 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */
{ 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */
{ 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */
{ 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */
{ 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */
{ 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */
{128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */
{160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */
{192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */
{256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
};
/*8,24,40,56,80,104,128,160,200,256,320*/
static double compute_func(float x, const struct FuncDef *func)
{
float y, frac;
double interp[4];
int ind;
y = x*func->oversample;
ind = (int)floor(y);
frac = (y-ind);
/* CSE with handle the repeated powers */
interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac);
interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac);
/*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac);
/* Just to make sure we don't have rounding problems */
interp[1] = 1.f-interp[3]-interp[2]-interp[0];
/*sum = frac*accum[1] + (1-frac)*accum[2];*/
return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3];
}
#if 0
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
for (i=0;i<256;i++)
{
printf ("%f\n", compute_func(i/256., KAISER12));
}
return 0;
}
#endif
#ifdef FIXED_POINT
/* The slow way of computing a sinc for the table. Should improve that some day */
static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func)
{
/*fprintf (stderr, "%f ", x);*/
float xx = x * cutoff;
if (fabs(x)<1e-6f)
return WORD2INT(32768.*cutoff);
else if (fabs(x) > .5f*N)
return 0;
/*FIXME: Can it really be any slower than this? */
return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func));
}
#else
/* The slow way of computing a sinc for the table. Should improve that some day */
static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func)
{
/*fprintf (stderr, "%f ", x);*/
float xx = x * cutoff;
if (fabs(x)<1e-6)
return cutoff;
else if (fabs(x) > .5*N)
return 0;
/*FIXME: Can it really be any slower than this? */
return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func);
}
#endif
#ifdef FIXED_POINT
static void cubic_coef(spx_word16_t x, spx_word16_t interp[4])
{
/* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
but I know it's MMSE-optimal on a sinc */
spx_word16_t x2, x3;
x2 = MULT16_16_P15(x, x);
x3 = MULT16_16_P15(x, x2);
interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15);
interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1));
interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15);
/* Just to make sure we don't have rounding problems */
interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3];
if (interp[2]<32767)
interp[2]+=1;
}
#else
static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4])
{
/* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
but I know it's MMSE-optimal on a sinc */
interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac;
interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
/*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
/* Just to make sure we don't have rounding problems */
interp[2] = 1.-interp[0]-interp[1]-interp[3];
}
#endif
static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
const int N = st->filt_len;
int out_sample = 0;
int last_sample = st->last_sample[channel_index];
spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
const spx_word16_t *sinc_table = st->sinc_table;
const int out_stride = st->out_stride;
const int int_advance = st->int_advance;
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
spx_word32_t sum;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
const spx_word16_t *iptr = & in[last_sample];
#ifndef OVERRIDE_INNER_PRODUCT_SINGLE
int j;
sum = 0;
for(j=0;j<N;j++) sum += MULT16_16(sinct[j], iptr[j]);
/* This code is slower on most DSPs which have only 2 accumulators.
Plus this this forces truncation to 32 bits and you lose the HW guard bits.
I think we can trust the compiler and let it vectorize and/or unroll itself.
spx_word32_t accum[4] = {0,0,0,0};
for(j=0;j<N;j+=4) {
accum[0] += MULT16_16(sinct[j], iptr[j]);
accum[1] += MULT16_16(sinct[j+1], iptr[j+1]);
accum[2] += MULT16_16(sinct[j+2], iptr[j+2]);
accum[3] += MULT16_16(sinct[j+3], iptr[j+3]);
}
sum = accum[0] + accum[1] + accum[2] + accum[3];
*/
sum = SATURATE32PSHR(sum, 15, 32767);
#else
sum = inner_product_single(sinct, iptr, N);
#endif
out[out_stride * out_sample++] = sum;
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate)
{
samp_frac_num -= den_rate;
last_sample++;
}
}
st->last_sample[channel_index] = last_sample;
st->samp_frac_num[channel_index] = samp_frac_num;
return out_sample;
}
#ifdef FIXED_POINT
#else
/* This is the same as the previous function, except with a double-precision accumulator */
static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
const int N = st->filt_len;
int out_sample = 0;
int last_sample = st->last_sample[channel_index];
spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
const spx_word16_t *sinc_table = st->sinc_table;
const int out_stride = st->out_stride;
const int int_advance = st->int_advance;
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
double sum;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
const spx_word16_t *sinct = & sinc_table[samp_frac_num*N];
const spx_word16_t *iptr = & in[last_sample];
#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE
int j;
double accum[4] = {0,0,0,0};
for(j=0;j<N;j+=4) {
accum[0] += sinct[j]*iptr[j];
accum[1] += sinct[j+1]*iptr[j+1];
accum[2] += sinct[j+2]*iptr[j+2];
accum[3] += sinct[j+3]*iptr[j+3];
}
sum = accum[0] + accum[1] + accum[2] + accum[3];
#else
sum = inner_product_double(sinct, iptr, N);
#endif
out[out_stride * out_sample++] = PSHR32(sum, 15);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate)
{
samp_frac_num -= den_rate;
last_sample++;
}
}
st->last_sample[channel_index] = last_sample;
st->samp_frac_num[channel_index] = samp_frac_num;
return out_sample;
}
#endif
static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
const int N = st->filt_len;
int out_sample = 0;
int last_sample = st->last_sample[channel_index];
spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
const int out_stride = st->out_stride;
const int int_advance = st->int_advance;
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
spx_word32_t sum;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
const spx_word16_t *iptr = & in[last_sample];
const int offset = samp_frac_num*st->oversample/st->den_rate;
#ifdef FIXED_POINT
const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
#else
const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
#endif
spx_word16_t interp[4];
#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
int j;
spx_word32_t accum[4] = {0,0,0,0};
for(j=0;j<N;j++) {
const spx_word16_t curr_in=iptr[j];
accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
sum = SATURATE32PSHR(sum, 15, 32767);
#else
cubic_coef(frac, interp);
sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
#endif
out[out_stride * out_sample++] = sum;
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate)
{
samp_frac_num -= den_rate;
last_sample++;
}
}
st->last_sample[channel_index] = last_sample;
st->samp_frac_num[channel_index] = samp_frac_num;
return out_sample;
}
#ifdef FIXED_POINT
#else
/* This is the same as the previous function, except with a double-precision accumulator */
static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
const int N = st->filt_len;
int out_sample = 0;
int last_sample = st->last_sample[channel_index];
spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
const int out_stride = st->out_stride;
const int int_advance = st->int_advance;
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
spx_word32_t sum;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
const spx_word16_t *iptr = & in[last_sample];
const int offset = samp_frac_num*st->oversample/st->den_rate;
#ifdef FIXED_POINT
const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate);
#else
const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate;
#endif
spx_word16_t interp[4];
#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
int j;
double accum[4] = {0,0,0,0};
for(j=0;j<N;j++) {
const double curr_in=iptr[j];
accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
#else
cubic_coef(frac, interp);
sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp);
#endif
out[out_stride * out_sample++] = PSHR32(sum,15);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate)
{
samp_frac_num -= den_rate;
last_sample++;
}
}
st->last_sample[channel_index] = last_sample;
st->samp_frac_num[channel_index] = samp_frac_num;
return out_sample;
}
#endif
/* This resampler is used to produce zero output in situations where memory
for the filter could not be allocated. The expected numbers of input and
output samples are still processed so that callers failing to check error
codes are not surprised, possibly getting into infinite loops. */
static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
int out_sample = 0;
int last_sample = st->last_sample[channel_index];
spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index];
const int out_stride = st->out_stride;
const int int_advance = st->int_advance;
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
(void)in;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
out[out_stride * out_sample++] = 0;
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate)
{
samp_frac_num -= den_rate;
last_sample++;
}
}
st->last_sample[channel_index] = last_sample;
st->samp_frac_num[channel_index] = samp_frac_num;
return out_sample;
}
static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
{
spx_uint32_t major = value / den;
spx_uint32_t remain = value % den;
/* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
if (remain > UINT32_MAX / num || major > UINT32_MAX / num
|| major * num > UINT32_MAX - remain * num / den)
return RESAMPLER_ERR_OVERFLOW;
*result = remain * num / den + major * num;
return RESAMPLER_ERR_SUCCESS;
}
static int update_filter(SpeexResamplerState *st)
{
spx_uint32_t old_length = st->filt_len;
spx_uint32_t old_alloc_size = st->mem_alloc_size;
int use_direct;
spx_uint32_t min_sinc_table_length;
spx_uint32_t min_alloc_size;
st->int_advance = st->num_rate/st->den_rate;
st->frac_advance = st->num_rate%st->den_rate;
st->oversample = quality_map[st->quality].oversample;
st->filt_len = quality_map[st->quality].base_length;
if (st->num_rate > st->den_rate)
{
/* down-sampling */
st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
goto fail;
/* Round up to make sure we have a multiple of 8 for SSE */
st->filt_len = ((st->filt_len-1)&(~0x7))+8;
if (2*st->den_rate < st->num_rate)
st->oversample >>= 1;
if (4*st->den_rate < st->num_rate)
st->oversample >>= 1;
if (8*st->den_rate < st->num_rate)
st->oversample >>= 1;
if (16*st->den_rate < st->num_rate)
st->oversample >>= 1;
if (st->oversample < 1)
st->oversample = 1;
} else {
/* up-sampling */
st->cutoff = quality_map[st->quality].upsample_bandwidth;
}
#ifdef RESAMPLE_FULL_SINC_TABLE
use_direct = 1;
if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
goto fail;
#else
/* Choose the resampling type that requires the least amount of memory */
use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
&& INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
#endif
if (use_direct)
{
min_sinc_table_length = st->filt_len*st->den_rate;
} else {
if ((INT_MAX/sizeof(spx_word16_t)-8)/st->oversample < st->filt_len)
goto fail;
min_sinc_table_length = st->filt_len*st->oversample+8;
}
if (st->sinc_table_length < min_sinc_table_length)
{
spx_word16_t *sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,min_sinc_table_length*sizeof(spx_word16_t));
if (!sinc_table)
goto fail;
st->sinc_table = sinc_table;
st->sinc_table_length = min_sinc_table_length;
}
if (use_direct)
{
spx_uint32_t i;
for (i=0;i<st->den_rate;i++)
{
spx_int32_t j;
for (j=0;j<st->filt_len;j++)
{
st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func);
}
}
#ifdef FIXED_POINT
st->resampler_ptr = resampler_basic_direct_single;
#else
if (st->quality>8)
st->resampler_ptr = resampler_basic_direct_double;
else
st->resampler_ptr = resampler_basic_direct_single;
#endif
/*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
} else {
spx_int32_t i;
for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++)
st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func);
#ifdef FIXED_POINT
st->resampler_ptr = resampler_basic_interpolate_single;
#else
if (st->quality>8)
st->resampler_ptr = resampler_basic_interpolate_double;
else
st->resampler_ptr = resampler_basic_interpolate_single;
#endif
/*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
}
/* Here's the place where we update the filter memory to take into account
the change in filter length. It's probably the messiest part of the code
due to handling of lots of corner cases. */
/* Adding buffer_size to filt_len won't overflow here because filt_len
could be multiplied by sizeof(spx_word16_t) above. */
min_alloc_size = st->filt_len-1 + st->buffer_size;
if (min_alloc_size > st->mem_alloc_size)
{
spx_word16_t *mem;
if (INT_MAX/sizeof(spx_word16_t)/st->nb_channels < min_alloc_size)
goto fail;
else if (!(mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*min_alloc_size * sizeof(*mem))))
goto fail;
st->mem = mem;
st->mem_alloc_size = min_alloc_size;
}
if (!st->started)
{
spx_uint32_t i;
for (i=0;i<st->nb_channels*st->mem_alloc_size;i++)
st->mem[i] = 0;
/*speex_warning("reinit filter");*/
} else if (st->filt_len > old_length)
{
spx_uint32_t i;
/* Increase the filter length */
/*speex_warning("increase filter size");*/
for (i=st->nb_channels;i--;)
{
spx_uint32_t j;
spx_uint32_t olen = old_length;
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-1+st->magic_samples[i];j--;)
st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
for (j=0;j<st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = 0;
st->magic_samples[i] = 0;
}
if (st->filt_len > olen)
{
/* If the new filter length is still bigger than the "augmented" length */
/* Copy data going backward */
for (j=0;j<olen-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
/* Then put zeros for lack of anything better */
for (;j<st->filt_len-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
/* Adjust last_sample */
st->last_sample[i] += (st->filt_len - olen)/2;
} else {
/* Put back some of the magic! */
st->magic_samples[i] = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
}
}
} else if (st->filt_len < old_length)
{
spx_uint32_t i;
/* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
samples so they can be used directly as input the next time(s) */
for (i=0;i<st->nb_channels;i++)
{
spx_uint32_t j;
spx_uint32_t old_magic = st->magic_samples[i];
st->magic_samples[i] = (old_length - st->filt_len)/2;
/* We must copy some of the memory that's no longer used */
/* Copy data going backward */
for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
st->magic_samples[i] += old_magic;
}
}
return RESAMPLER_ERR_SUCCESS;
fail:
st->resampler_ptr = resampler_basic_zero;
/* st->mem may still contain consumed input samples for the filter.
Restore filt_len so that filt_len - 1 still points to the position after
the last of these samples. */
st->filt_len = old_length;
return RESAMPLER_ERR_ALLOC_FAILED;
}
EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
{
return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
}
EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
{
SpeexResamplerState *st;
int filter_err;
if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0)
{
if (err)
*err = RESAMPLER_ERR_INVALID_ARG;
return NULL;
}
st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
if (!st)
{
if (err)
*err = RESAMPLER_ERR_ALLOC_FAILED;
return NULL;
}
st->initialised = 0;
st->started = 0;
st->in_rate = 0;
st->out_rate = 0;
st->num_rate = 0;
st->den_rate = 0;
st->quality = -1;
st->sinc_table_length = 0;
st->mem_alloc_size = 0;
st->filt_len = 0;
st->mem = 0;
st->resampler_ptr = 0;
st->cutoff = 1.f;
st->nb_channels = nb_channels;
st->in_stride = 1;
st->out_stride = 1;
st->buffer_size = 160;
/* Per channel data */
if (!(st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t))))
goto fail;
if (!(st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t))))
goto fail;
if (!(st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t))))
goto fail;
speex_resampler_set_quality(st, quality);
speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);
filter_err = update_filter(st);
if (filter_err == RESAMPLER_ERR_SUCCESS)
{
st->initialised = 1;
} else {
speex_resampler_destroy(st);
st = NULL;
}
if (err)
*err = filter_err;
return st;
fail:
if (err)
*err = RESAMPLER_ERR_ALLOC_FAILED;
speex_resampler_destroy(st);
return NULL;
}
EXPORT void speex_resampler_destroy(SpeexResamplerState *st)
{
speex_free(st->mem);
speex_free(st->sinc_table);
speex_free(st->last_sample);
speex_free(st->magic_samples);
speex_free(st->samp_frac_num);
speex_free(st);
}
static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len)
{
int j=0;
const int N = st->filt_len;
int out_sample = 0;
spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
spx_uint32_t ilen;
st->started = 1;
/* Call the right resampler through the function ptr */
out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len);
if (st->last_sample[channel_index] < (spx_int32_t)*in_len)
*in_len = st->last_sample[channel_index];
*out_len = out_sample;
st->last_sample[channel_index] -= *in_len;
ilen = *in_len;
for(j=0;j<N-1;++j)
mem[j] = mem[j+ilen];
return RESAMPLER_ERR_SUCCESS;
}
static int speex_resampler_magic(SpeexResamplerState *st, spx_uint32_t channel_index, spx_word16_t **out, spx_uint32_t out_len) {
spx_uint32_t tmp_in_len = st->magic_samples[channel_index];
spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size;
const int N = st->filt_len;
speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len);
st->magic_samples[channel_index] -= tmp_in_len;
/* If we couldn't process all "magic" input samples, save the rest for next time */
if (st->magic_samples[channel_index])
{
spx_uint32_t i;
for (i=0;i<st->magic_samples[channel_index];i++)
mem[N-1+i]=mem[N-1+i+tmp_in_len];
}
*out += out_len*st->out_stride;
return out_len;
}
#ifdef FIXED_POINT
EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
#else
EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
#endif
{
int j;
spx_uint32_t ilen = *in_len;
spx_uint32_t olen = *out_len;
spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
const int filt_offs = st->filt_len - 1;
const spx_uint32_t xlen = st->mem_alloc_size - filt_offs;
const int istride = st->in_stride;
if (st->magic_samples[channel_index])
olen -= speex_resampler_magic(st, channel_index, &out, olen);
if (! st->magic_samples[channel_index]) {
while (ilen && olen) {
spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
spx_uint32_t ochunk = olen;
if (in) {
for(j=0;j<ichunk;++j)
x[j+filt_offs]=in[j*istride];
} else {
for(j=0;j<ichunk;++j)
x[j+filt_offs]=0;
}
speex_resampler_process_native(st, channel_index, &ichunk, out, &ochunk);
ilen -= ichunk;
olen -= ochunk;
out += ochunk * st->out_stride;
if (in)
in += ichunk * istride;
}
}
*in_len -= ilen;
*out_len -= olen;
return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
}
#ifdef FIXED_POINT
EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
#else
EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
#endif
{
int j;
const int istride_save = st->in_stride;
const int ostride_save = st->out_stride;
spx_uint32_t ilen = *in_len;
spx_uint32_t olen = *out_len;
spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size;
const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
#ifdef VAR_ARRAYS
const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
spx_word16_t ystack[ylen];
#else
const unsigned int ylen = FIXED_STACK_ALLOC;
spx_word16_t ystack[FIXED_STACK_ALLOC];
#endif
st->out_stride = 1;
while (ilen && olen) {
spx_word16_t *y = ystack;
spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen;
spx_uint32_t ochunk = (olen > ylen) ? ylen : olen;
spx_uint32_t omagic = 0;
if (st->magic_samples[channel_index]) {
omagic = speex_resampler_magic(st, channel_index, &y, ochunk);
ochunk -= omagic;
olen -= omagic;
}
if (! st->magic_samples[channel_index]) {
if (in) {
for(j=0;j<ichunk;++j)
#ifdef FIXED_POINT
x[j+st->filt_len-1]=WORD2INT(in[j*istride_save]);
#else
x[j+st->filt_len-1]=in[j*istride_save];
#endif
} else {
for(j=0;j<ichunk;++j)
x[j+st->filt_len-1]=0;
}
speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk);
} else {
ichunk = 0;
ochunk = 0;
}
for (j=0;j<ochunk+omagic;++j)
#ifdef FIXED_POINT
out[j*ostride_save] = ystack[j];
#else
out[j*ostride_save] = WORD2INT(ystack[j]);
#endif
ilen -= ichunk;
olen -= ochunk;
out += (ochunk+omagic) * ostride_save;
if (in)
in += ichunk * istride_save;
}
st->out_stride = ostride_save;
*in_len -= ilen;
*out_len -= olen;
return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
}
EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len)
{
spx_uint32_t i;
int istride_save, ostride_save;
spx_uint32_t bak_out_len = *out_len;
spx_uint32_t bak_in_len = *in_len;
istride_save = st->in_stride;
ostride_save = st->out_stride;
st->in_stride = st->out_stride = st->nb_channels;
for (i=0;i<st->nb_channels;i++)
{
*out_len = bak_out_len;
*in_len = bak_in_len;
if (in != NULL)
speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
else
speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len);
}
st->in_stride = istride_save;
st->out_stride = ostride_save;
return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
}
EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len)
{
spx_uint32_t i;
int istride_save, ostride_save;
spx_uint32_t bak_out_len = *out_len;
spx_uint32_t bak_in_len = *in_len;
istride_save = st->in_stride;
ostride_save = st->out_stride;
st->in_stride = st->out_stride = st->nb_channels;
for (i=0;i<st->nb_channels;i++)
{
*out_len = bak_out_len;
*in_len = bak_in_len;
if (in != NULL)
speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len);
else
speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len);
}
st->in_stride = istride_save;
st->out_stride = ostride_save;
return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS;
}
EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate)
{
return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
}
EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate)
{
*in_rate = st->in_rate;
*out_rate = st->out_rate;
}
static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
{
while (b != 0)
{
spx_uint32_t temp = a;
a = b;
b = temp % b;
}
return a;
}
EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate)
{
spx_uint32_t fact;
spx_uint32_t old_den;
spx_uint32_t i;
if (ratio_num == 0 || ratio_den == 0)
return RESAMPLER_ERR_INVALID_ARG;
if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
return RESAMPLER_ERR_SUCCESS;
old_den = st->den_rate;
st->in_rate = in_rate;
st->out_rate = out_rate;
st->num_rate = ratio_num;
st->den_rate = ratio_den;
fact = compute_gcd(st->num_rate, st->den_rate);
st->num_rate /= fact;
st->den_rate /= fact;
if (old_den > 0)
{
for (i=0;i<st->nb_channels;i++)
{
if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
return RESAMPLER_ERR_OVERFLOW;
/* Safety net */
if (st->samp_frac_num[i] >= st->den_rate)
st->samp_frac_num[i] = st->den_rate-1;
}
}
if (st->initialised)
return update_filter(st);
return RESAMPLER_ERR_SUCCESS;
}
EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den)
{
*ratio_num = st->num_rate;
*ratio_den = st->den_rate;
}
EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality)
{
if (quality > 10 || quality < 0)
return RESAMPLER_ERR_INVALID_ARG;
if (st->quality == quality)
return RESAMPLER_ERR_SUCCESS;
st->quality = quality;
if (st->initialised)
return update_filter(st);
return RESAMPLER_ERR_SUCCESS;
}
EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality)
{
*quality = st->quality;
}
EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride)
{
st->in_stride = stride;
}
EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride)
{
*stride = st->in_stride;
}
EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride)
{
st->out_stride = stride;
}
EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride)
{
*stride = st->out_stride;
}
EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st)
{
return st->filt_len / 2;
}
EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st)
{
return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate;
}
EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st)
{
spx_uint32_t i;
for (i=0;i<st->nb_channels;i++)
st->last_sample[i] = st->filt_len/2;
return RESAMPLER_ERR_SUCCESS;
}
EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st)
{
spx_uint32_t i;
for (i=0;i<st->nb_channels;i++)
{
st->last_sample[i] = 0;
st->magic_samples[i] = 0;
st->samp_frac_num[i] = 0;
}
for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
st->mem[i] = 0;
return RESAMPLER_ERR_SUCCESS;
}
EXPORT const char *speex_resampler_strerror(int err)
{
switch (err)
{
case RESAMPLER_ERR_SUCCESS:
return "Success.";
case RESAMPLER_ERR_ALLOC_FAILED:
return "Memory allocation failed.";
case RESAMPLER_ERR_BAD_STATE:
return "Bad resampler state.";
case RESAMPLER_ERR_INVALID_ARG:
return "Invalid argument.";
case RESAMPLER_ERR_PTR_OVERLAP:
return "Input and output buffers overlap.";
default:
return "Unknown error. Bad error code or strange version mismatch.";
}
}
/* Copyright (C) 2007-2008 Jean-Marc Valin
* Copyright (C) 2008 Thorvald Natvig
*/
/**
@file resample_sse.h
@brief Resampler functions (SSE version)
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <xmmintrin.h>
#define OVERRIDE_INNER_PRODUCT_SINGLE
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
int i;
float ret;
__m128 sum = _mm_setzero_ps();
for (i=0;i<len;i+=8)
{
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
}
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
_mm_store_ss(&ret, sum);
return ret;
}
#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
int i;
float ret;
__m128 sum = _mm_setzero_ps();
__m128 f = _mm_loadu_ps(frac);
for(i=0;i<len;i+=2)
{
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
}
sum = _mm_mul_ps(f, sum);
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
_mm_store_ss(&ret, sum);
return ret;
}
#ifdef __SSE2__
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE
static inline double inner_product_double(const float *a, const float *b, unsigned int len)
{
int i;
double ret;
__m128d sum = _mm_setzero_pd();
__m128 t;
for (i=0;i<len;i+=8)
{
t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
}
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
int i;
double ret;
__m128d sum;
__m128d sum1 = _mm_setzero_pd();
__m128d sum2 = _mm_setzero_pd();
__m128 f = _mm_loadu_ps(frac);
__m128d f1 = _mm_cvtps_pd(f);
__m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
__m128 t;
for(i=0;i<len;i+=2)
{
t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
}
sum1 = _mm_mul_pd(f1, sum1);
sum2 = _mm_mul_pd(f2, sum2);
sum = _mm_add_pd(sum1, sum2);
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#endif
/* Copyright (C) 2007 Jean-Marc Valin
File: speex_resampler.h
Resampling code
The design goals of this code are:
- Very fast algorithm
- Low memory requirement
- Good *perceptual* quality (and not best SNR)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPEEX_RESAMPLER_H
#define SPEEX_RESAMPLER_H
#ifdef OUTSIDE_SPEEX
/********* WARNING: MENTAL SANITY ENDS HERE *************/
/* If the resampler is defined outside of Speex, we change the symbol names so that
there won't be any clash if linking with Speex later on. */
/* #define RANDOM_PREFIX your software name here */
#ifndef RANDOM_PREFIX
#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
#endif
#define CAT_PREFIX2(a,b) a ## b
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
#define spx_int16_t short
#define spx_int32_t int
#define spx_uint16_t unsigned short
#define spx_uint32_t unsigned int
#define speex_assert(cond)
#else /* OUTSIDE_SPEEX */
#include "speexdsp_types.h"
#endif /* OUTSIDE_SPEEX */
#ifdef __cplusplus
extern "C" {
#endif
#define SPEEX_RESAMPLER_QUALITY_MAX 10
#define SPEEX_RESAMPLER_QUALITY_MIN 0
#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
enum {
RESAMPLER_ERR_SUCCESS = 0,
RESAMPLER_ERR_ALLOC_FAILED = 1,
RESAMPLER_ERR_BAD_STATE = 2,
RESAMPLER_ERR_INVALID_ARG = 3,
RESAMPLER_ERR_PTR_OVERLAP = 4,
RESAMPLER_ERR_OVERFLOW = 5,
RESAMPLER_ERR_MAX_ERROR
};
struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState;
/** Create a new resampler with integer input and output rates.
* @param nb_channels Number of channels to be processed
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Create a new resampler with fractional input/output rates. The sampling
* rate ratio is an arbitrary rational number with both the numerator and
* denominator being 32-bit integers.
* @param nb_channels Number of channels to be processed
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Destroy a resampler state.
* @param st Resampler state
*/
void speex_resampler_destroy(SpeexResamplerState *st);
/** Resample a float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the
* number of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_float(SpeexResamplerState *st,
spx_uint32_t channel_index,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_int(SpeexResamplerState *st,
spx_uint32_t channel_index,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Set (change) the input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
*/
int speex_resampler_set_rate(SpeexResamplerState *st,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz) copied.
* @param out_rate Output sampling rate (integer number of Hz) copied.
*/
void speex_resampler_get_rate(SpeexResamplerState *st,
spx_uint32_t *in_rate,
spx_uint32_t *out_rate);
/** Set (change) the input/output sampling rates and resampling ratio
* (fractional values in Hz supported).
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
*/
int speex_resampler_set_rate_frac(SpeexResamplerState *st,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current resampling ratio. This will be reduced to the least
* common denominator.
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio copied
* @param ratio_den Denominator of the sampling rate ratio copied
*/
void speex_resampler_get_ratio(SpeexResamplerState *st,
spx_uint32_t *ratio_num,
spx_uint32_t *ratio_den);
/** Set (change) the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
int speex_resampler_set_quality(SpeexResamplerState *st,
int quality);
/** Get the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
void speex_resampler_get_quality(SpeexResamplerState *st,
int *quality);
/** Set (change) the input stride.
* @param st Resampler state
* @param stride Input stride
*/
void speex_resampler_set_input_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the input stride.
* @param st Resampler state
* @param stride Input stride copied
*/
void speex_resampler_get_input_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Set (change) the output stride.
* @param st Resampler state
* @param stride Output stride
*/
void speex_resampler_set_output_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the output stride.
* @param st Resampler state copied
* @param stride Output stride
*/
void speex_resampler_get_output_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Get the latency introduced by the resampler measured in input samples.
* @param st Resampler state
*/
int speex_resampler_get_input_latency(SpeexResamplerState *st);
/** Get the latency introduced by the resampler measured in output samples.
* @param st Resampler state
*/
int speex_resampler_get_output_latency(SpeexResamplerState *st);
/** Make sure that the first samples to go out of the resamplers don't have
* leading zeros. This is only useful before starting to use a newly created
* resampler. It is recommended to use that when resampling an audio file, as
* it will generate a file with the same length. For real-time processing,
* it is probably easier not to use this call (so that the output duration
* is the same for the first frame).
* @param st Resampler state
*/
int speex_resampler_skip_zeros(SpeexResamplerState *st);
/** Reset a resampler so a new (unrelated) stream can be processed.
* @param st Resampler state
*/
int speex_resampler_reset_mem(SpeexResamplerState *st);
/** Returns the English meaning for an error code
* @param err Error code
* @return English string
*/
const char *speex_resampler_strerror(int err);
#ifdef __cplusplus
}
#endif
#endif
...@@ -826,19 +826,14 @@ The resampler supports multiple channels and is always interleaved (both input a ...@@ -826,19 +826,14 @@ The resampler supports multiple channels and is always interleaved (both input a
The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the
only configuration property that can be changed after initialization. only configuration property that can be changed after initialization.
The miniaudio resampler supports multiple algorithms: The miniaudio resampler has built-in support for the following algorithms:
+-----------+------------------------------+ +-----------+------------------------------+
| Algorithm | Enum Token | | Algorithm | Enum Token |
+-----------+------------------------------+ +-----------+------------------------------+
| Linear | ma_resample_algorithm_linear | | Linear | ma_resample_algorithm_linear |
| Speex | ma_resample_algorithm_speex |
+-----------+------------------------------+ +-----------+------------------------------+
Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
the Speex Resampler section below.
The algorithm cannot be changed after initialization. The algorithm cannot be changed after initialization.
Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process
...@@ -860,9 +855,7 @@ with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. ...@@ -860,9 +855,7 @@ with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
6.3.1. Resampling Algorithms 6.3.1. Resampling Algorithms
---------------------------- ----------------------------
The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency, The choice of resampling algorithm depends on your situation and requirements.
but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
for memory management.
6.3.1.1. Linear Resampling 6.3.1.1. Linear Resampling
...@@ -883,30 +876,6 @@ and is a purely perceptual configuration. ...@@ -883,30 +876,6 @@ and is a purely perceptual configuration.
The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`. The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
6.3.1.2. Speex Resampling
-------------------------
The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
source files. To opt-in, you must first `#include` the following file before the implementation of miniaudio.h:
```c
#include "extras/speex_resampler/ma_speex_resampler.h"
```
Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
```c
#define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
#include "extras/speex_resampler/ma_speex_resampler.h"
```
Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being
the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3.
6.4. General Data Conversion 6.4. General Data Conversion
---------------------------- ----------------------------
...@@ -2503,8 +2472,7 @@ MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resample ...@@ -2503,8 +2472,7 @@ MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resample
typedef enum typedef enum
{ {
ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ ma_resample_algorithm_linear = 0 /* Fastest, lowest quality. Optional low-pass filtering. Default. */
ma_resample_algorithm_speex
} ma_resample_algorithm; } ma_resample_algorithm;
typedef struct typedef struct
...@@ -2519,10 +2487,6 @@ typedef struct ...@@ -2519,10 +2487,6 @@ typedef struct
ma_uint32 lpfOrder; ma_uint32 lpfOrder;
double lpfNyquistFactor; double lpfNyquistFactor;
} linear; } linear;
struct
{
int quality; /* 0 to 10. Defaults to 3. */
} speex;
} ma_resampler_config; } ma_resampler_config;
MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
...@@ -2533,10 +2497,6 @@ typedef struct ...@@ -2533,10 +2497,6 @@ typedef struct
union union
{ {
ma_linear_resampler linear; ma_linear_resampler linear;
struct
{
void* pSpeexResamplerState; /* SpeexResamplerState* */
} speex;
} state; } state;
} ma_resampler; } ma_resampler;
...@@ -2686,10 +2646,6 @@ typedef struct ...@@ -2686,10 +2646,6 @@ typedef struct
ma_uint32 lpfOrder; ma_uint32 lpfOrder;
double lpfNyquistFactor; double lpfNyquistFactor;
} linear; } linear;
struct
{
int quality;
} speex;
} resampling; } resampling;
} ma_data_converter_config; } ma_data_converter_config;
...@@ -3398,10 +3354,6 @@ struct ma_device_config ...@@ -3398,10 +3354,6 @@ struct ma_device_config
{ {
ma_uint32 lpfOrder; ma_uint32 lpfOrder;
} linear; } linear;
struct
{
int quality;
} speex;
} resampling; } resampling;
struct struct
{ {
...@@ -4071,10 +4023,6 @@ struct ma_device ...@@ -4071,10 +4023,6 @@ struct ma_device
{ {
ma_uint32 lpfOrder; ma_uint32 lpfOrder;
} linear; } linear;
struct
{
int quality;
} speex;
} resampling; } resampling;
struct struct
{ {
...@@ -6171,10 +6119,6 @@ typedef struct ...@@ -6171,10 +6119,6 @@ typedef struct
{ {
ma_uint32 lpfOrder; ma_uint32 lpfOrder;
} linear; } linear;
struct
{
int quality;
} speex;
} resampling; } resampling;
ma_allocation_callbacks allocationCallbacks; ma_allocation_callbacks allocationCallbacks;
ma_encoding_format encodingFormat; ma_encoding_format encodingFormat;
...@@ -32620,7 +32564,6 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ...@@ -32620,7 +32564,6 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter); result = ma_data_converter_init(&converterConfig, &pDevice->capture.converter);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -32643,7 +32586,6 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ...@@ -32643,7 +32586,6 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;
converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
converterConfig.resampling.speex.quality = pDevice->resampling.speex.quality;
result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter); result = ma_data_converter_init(&converterConfig, &pDevice->playback.converter);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -33350,7 +33292,6 @@ MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) ...@@ -33350,7 +33292,6 @@ MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
/* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */ /* Resampling defaults. We must never use the Speex backend by default because it uses licensed third party code. */
config.resampling.algorithm = ma_resample_algorithm_linear; config.resampling.algorithm = ma_resample_algorithm_linear;
config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
config.resampling.speex.quality = 3;
return config; return config;
} }
...@@ -33427,7 +33368,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ...@@ -33427,7 +33368,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
pDevice->sampleRate = pConfig->sampleRate; pDevice->sampleRate = pConfig->sampleRate;
pDevice->resampling.algorithm = pConfig->resampling.algorithm; pDevice->resampling.algorithm = pConfig->resampling.algorithm;
pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
pDevice->resampling.speex.quality = pConfig->resampling.speex.quality;
pDevice->capture.shareMode = pConfig->capture.shareMode; pDevice->capture.shareMode = pConfig->capture.shareMode;
pDevice->capture.format = pConfig->capture.format; pDevice->capture.format = pConfig->capture.format;
...@@ -39361,24 +39301,6 @@ MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resample ...@@ -39361,24 +39301,6 @@ MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resample
} }
#if defined(ma_speex_resampler_h)
#define MA_HAS_SPEEX_RESAMPLER
static ma_result ma_result_from_speex_err(int err)
{
switch (err)
{
case RESAMPLER_ERR_SUCCESS: return MA_SUCCESS;
case RESAMPLER_ERR_ALLOC_FAILED: return MA_OUT_OF_MEMORY;
case RESAMPLER_ERR_BAD_STATE: return MA_ERROR;
case RESAMPLER_ERR_INVALID_ARG: return MA_INVALID_ARGS;
case RESAMPLER_ERR_PTR_OVERLAP: return MA_INVALID_ARGS;
case RESAMPLER_ERR_OVERFLOW: return MA_ERROR;
default: return MA_ERROR;
}
}
#endif /* ma_speex_resampler_h */
MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
{ {
ma_resampler_config config; ma_resampler_config config;
...@@ -39394,9 +39316,6 @@ MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 ...@@ -39394,9 +39316,6 @@ MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32
config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
config.linear.lpfNyquistFactor = 1; config.linear.lpfNyquistFactor = 1;
/* Speex. */
config.speex.quality = 3; /* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
return config; return config;
} }
...@@ -39435,20 +39354,6 @@ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resamp ...@@ -39435,20 +39354,6 @@ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resamp
} }
} break; } break;
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
int speexErr;
pResampler->state.speex.pSpeexResamplerState = speex_resampler_init(pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->speex.quality, &speexErr);
if (pResampler->state.speex.pSpeexResamplerState == NULL) {
return ma_result_from_speex_err(speexErr);
}
#else
/* Speex resampler not available. */
return MA_NO_BACKEND;
#endif
} break;
default: return MA_INVALID_ARGS; default: return MA_INVALID_ARGS;
} }
...@@ -39464,12 +39369,6 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler) ...@@ -39464,12 +39369,6 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler)
if (pResampler->config.algorithm == ma_resample_algorithm_linear) { if (pResampler->config.algorithm == ma_resample_algorithm_linear) {
ma_linear_resampler_uninit(&pResampler->state.linear); ma_linear_resampler_uninit(&pResampler->state.linear);
} }
#if defined(MA_HAS_SPEEX_RESAMPLER)
if (pResampler->config.algorithm == ma_resample_algorithm_speex) {
speex_resampler_destroy((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
}
#endif
} }
static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
...@@ -39477,76 +39376,6 @@ static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pRe ...@@ -39477,76 +39376,6 @@ static ma_result ma_resampler_process_pcm_frames__read__linear(ma_resampler* pRe
return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
} }
#if defined(MA_HAS_SPEEX_RESAMPLER)
static ma_result ma_resampler_process_pcm_frames__read__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
int speexErr;
ma_uint64 frameCountOut;
ma_uint64 frameCountIn;
ma_uint64 framesProcessedOut;
ma_uint64 framesProcessedIn;
unsigned int framesPerIteration = UINT_MAX;
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFrameCountOut != NULL);
MA_ASSERT(pFrameCountIn != NULL);
/*
Reading from the Speex resampler requires a bit of dancing around for a few reasons. The first thing is that it's frame counts
are in unsigned int's whereas ours is in ma_uint64. We therefore need to run the conversion in a loop. The other, more complicated
problem, is that we need to keep track of the input time, similar to what we do with the linear resampler. The reason we need to
do this is for ma_resampler_get_required_input_frame_count() and ma_resampler_get_expected_output_frame_count().
*/
frameCountOut = *pFrameCountOut;
frameCountIn = *pFrameCountIn;
framesProcessedOut = 0;
framesProcessedIn = 0;
while (framesProcessedOut < frameCountOut && framesProcessedIn < frameCountIn) {
unsigned int frameCountInThisIteration;
unsigned int frameCountOutThisIteration;
const void* pFramesInThisIteration;
void* pFramesOutThisIteration;
frameCountInThisIteration = framesPerIteration;
if ((ma_uint64)frameCountInThisIteration > (frameCountIn - framesProcessedIn)) {
frameCountInThisIteration = (unsigned int)(frameCountIn - framesProcessedIn);
}
frameCountOutThisIteration = framesPerIteration;
if ((ma_uint64)frameCountOutThisIteration > (frameCountOut - framesProcessedOut)) {
frameCountOutThisIteration = (unsigned int)(frameCountOut - framesProcessedOut);
}
pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels));
if (pResampler->config.format == ma_format_f32) {
speexErr = speex_resampler_process_interleaved_float((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const float*)pFramesInThisIteration, &frameCountInThisIteration, (float*)pFramesOutThisIteration, &frameCountOutThisIteration);
} else if (pResampler->config.format == ma_format_s16) {
speexErr = speex_resampler_process_interleaved_int((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, (const spx_int16_t*)pFramesInThisIteration, &frameCountInThisIteration, (spx_int16_t*)pFramesOutThisIteration, &frameCountOutThisIteration);
} else {
/* Format not supported. Should never get here. */
MA_ASSERT(MA_FALSE);
return MA_INVALID_OPERATION;
}
if (speexErr != RESAMPLER_ERR_SUCCESS) {
return ma_result_from_speex_err(speexErr);
}
framesProcessedIn += frameCountInThisIteration;
framesProcessedOut += frameCountOutThisIteration;
}
*pFrameCountOut = framesProcessedOut;
*pFrameCountIn = framesProcessedIn;
return MA_SUCCESS;
}
#endif
static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{ {
MA_ASSERT(pResampler != NULL); MA_ASSERT(pResampler != NULL);
...@@ -39569,15 +39398,6 @@ static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler, ...@@ -39569,15 +39398,6 @@ static ma_result ma_resampler_process_pcm_frames__read(ma_resampler* pResampler,
return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); return ma_resampler_process_pcm_frames__read__linear(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
} }
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
return ma_resampler_process_pcm_frames__read__speex(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
#else
break;
#endif
}
default: break; default: break;
} }
...@@ -39595,81 +39415,6 @@ static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pRe ...@@ -39595,81 +39415,6 @@ static ma_result ma_resampler_process_pcm_frames__seek__linear(ma_resampler* pRe
return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut); return ma_linear_resampler_process_pcm_frames(&pResampler->state.linear, pFramesIn, pFrameCountIn, NULL, pFrameCountOut);
} }
#if defined(MA_HAS_SPEEX_RESAMPLER)
static ma_result ma_resampler_process_pcm_frames__seek__speex(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
{
/* The generic seek method is implemented in on top of ma_resampler_process_pcm_frames__read() by just processing into a dummy buffer. */
float devnull[4096];
ma_uint64 totalOutputFramesToProcess;
ma_uint64 totalOutputFramesProcessed;
ma_uint64 totalInputFramesProcessed;
ma_uint32 bpf;
ma_result result;
MA_ASSERT(pResampler != NULL);
totalOutputFramesProcessed = 0;
totalInputFramesProcessed = 0;
bpf = ma_get_bytes_per_frame(pResampler->config.format, pResampler->config.channels);
if (pFrameCountOut != NULL) {
/* Seek by output frames. */
totalOutputFramesToProcess = *pFrameCountOut;
} else {
/* Seek by input frames. */
MA_ASSERT(pFrameCountIn != NULL);
totalOutputFramesToProcess = ma_resampler_get_expected_output_frame_count(pResampler, *pFrameCountIn);
}
if (pFramesIn != NULL) {
/* Process input data. */
MA_ASSERT(pFrameCountIn != NULL);
while (totalOutputFramesProcessed < totalOutputFramesToProcess && totalInputFramesProcessed < *pFrameCountIn) {
ma_uint64 inputFramesToProcessThisIteration = (*pFrameCountIn - totalInputFramesProcessed);
ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
}
result = ma_resampler_process_pcm_frames__read(pResampler, ma_offset_ptr(pFramesIn, totalInputFramesProcessed*bpf), &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
if (result != MA_SUCCESS) {
return result;
}
totalOutputFramesProcessed += outputFramesToProcessThisIteration;
totalInputFramesProcessed += inputFramesToProcessThisIteration;
}
} else {
/* Don't process input data - just update timing and filter state as if zeroes were passed in. */
while (totalOutputFramesProcessed < totalOutputFramesToProcess) {
ma_uint64 inputFramesToProcessThisIteration = 16384;
ma_uint64 outputFramesToProcessThisIteration = (totalOutputFramesToProcess - totalOutputFramesProcessed);
if (outputFramesToProcessThisIteration > sizeof(devnull) / bpf) {
outputFramesToProcessThisIteration = sizeof(devnull) / bpf;
}
result = ma_resampler_process_pcm_frames__read(pResampler, NULL, &inputFramesToProcessThisIteration, ma_offset_ptr(devnull, totalOutputFramesProcessed*bpf), &outputFramesToProcessThisIteration);
if (result != MA_SUCCESS) {
return result;
}
totalOutputFramesProcessed += outputFramesToProcessThisIteration;
totalInputFramesProcessed += inputFramesToProcessThisIteration;
}
}
if (pFrameCountIn != NULL) {
*pFrameCountIn = totalInputFramesProcessed;
}
if (pFrameCountOut != NULL) {
*pFrameCountOut = totalOutputFramesProcessed;
}
return MA_SUCCESS;
}
#endif
static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut) static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, ma_uint64* pFrameCountOut)
{ {
MA_ASSERT(pResampler != NULL); MA_ASSERT(pResampler != NULL);
...@@ -39681,15 +39426,6 @@ static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler, ...@@ -39681,15 +39426,6 @@ static ma_result ma_resampler_process_pcm_frames__seek(ma_resampler* pResampler,
return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut); return ma_resampler_process_pcm_frames__seek__linear(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
} break; } break;
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
return ma_resampler_process_pcm_frames__seek__speex(pResampler, pFramesIn, pFrameCountIn, pFrameCountOut);
#else
break;
#endif
};
default: break; default: break;
} }
...@@ -39738,15 +39474,6 @@ MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampl ...@@ -39738,15 +39474,6 @@ MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampl
return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut); return ma_linear_resampler_set_rate(&pResampler->state.linear, sampleRateIn, sampleRateOut);
} break; } break;
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
return ma_result_from_speex_err(speex_resampler_set_rate((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, sampleRateIn, sampleRateOut));
#else
break;
#endif
};
default: break; default: break;
} }
...@@ -39798,21 +39525,6 @@ MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler* ...@@ -39798,21 +39525,6 @@ MA_API ma_uint64 ma_resampler_get_required_input_frame_count(const ma_resampler*
return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount); return ma_linear_resampler_get_required_input_frame_count(&pResampler->state.linear, outputFrameCount);
} }
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
spx_uint64_t count;
int speexErr = ma_speex_resampler_get_required_input_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, outputFrameCount, &count);
if (speexErr != RESAMPLER_ERR_SUCCESS) {
return 0;
}
return (ma_uint64)count;
#else
break;
#endif
}
default: break; default: break;
} }
...@@ -39838,21 +39550,6 @@ MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler ...@@ -39838,21 +39550,6 @@ MA_API ma_uint64 ma_resampler_get_expected_output_frame_count(const ma_resampler
return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount); return ma_linear_resampler_get_expected_output_frame_count(&pResampler->state.linear, inputFrameCount);
} }
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
spx_uint64_t count;
int speexErr = ma_speex_resampler_get_expected_output_frame_count((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState, inputFrameCount, &count);
if (speexErr != RESAMPLER_ERR_SUCCESS) {
return 0;
}
return (ma_uint64)count;
#else
break;
#endif
}
default: break; default: break;
} }
...@@ -39874,15 +39571,6 @@ MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) ...@@ -39874,15 +39571,6 @@ MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
return ma_linear_resampler_get_input_latency(&pResampler->state.linear); return ma_linear_resampler_get_input_latency(&pResampler->state.linear);
} }
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
return (ma_uint64)ma_speex_resampler_get_input_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
#else
break;
#endif
}
default: break; default: break;
} }
...@@ -39904,15 +39592,6 @@ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) ...@@ -39904,15 +39592,6 @@ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
return ma_linear_resampler_get_output_latency(&pResampler->state.linear); return ma_linear_resampler_get_output_latency(&pResampler->state.linear);
} }
case ma_resample_algorithm_speex:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
return (ma_uint64)ma_speex_resampler_get_output_latency((SpeexResamplerState*)pResampler->state.speex.pSpeexResamplerState);
#else
break;
#endif
}
default: break; default: break;
} }
...@@ -40825,9 +40504,6 @@ MA_API ma_data_converter_config ma_data_converter_config_init_default() ...@@ -40825,9 +40504,6 @@ MA_API ma_data_converter_config ma_data_converter_config_init_default()
config.resampling.linear.lpfOrder = 1; config.resampling.linear.lpfOrder = 1;
config.resampling.linear.lpfNyquistFactor = 1; config.resampling.linear.lpfNyquistFactor = 1;
/* Speex resampling defaults. */
config.resampling.speex.quality = 3;
return config; return config;
} }
...@@ -40928,7 +40604,6 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ...@@ -40928,7 +40604,6 @@ MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig,
resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm); resamplerConfig = ma_resampler_config_init(midFormat, resamplerChannels, pConverter->config.sampleRateIn, pConverter->config.sampleRateOut, pConverter->config.resampling.algorithm);
resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder; resamplerConfig.linear.lpfOrder = pConverter->config.resampling.linear.lpfOrder;
resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor; resamplerConfig.linear.lpfNyquistFactor = pConverter->config.resampling.linear.lpfNyquistFactor;
resamplerConfig.speex.quality = pConverter->config.resampling.speex.quality;
result = ma_resampler_init(&resamplerConfig, &pConverter->resampler); result = ma_resampler_init(&resamplerConfig, &pConverter->resampler);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -46793,7 +46468,6 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3 ...@@ -46793,7 +46468,6 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3
config.sampleRate = outputSampleRate; config.sampleRate = outputSampleRate;
config.resampling.algorithm = ma_resample_algorithm_linear; config.resampling.algorithm = ma_resample_algorithm_linear;
config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
config.resampling.speex.quality = 3;
config.encodingFormat = ma_encoding_format_unknown; config.encodingFormat = ma_encoding_format_unknown;
/* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
...@@ -46885,7 +46559,6 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ ...@@ -46885,7 +46559,6 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_
converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ converterConfig.resampling.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
converterConfig.resampling.algorithm = pConfig->resampling.algorithm; converterConfig.resampling.algorithm = pConfig->resampling.algorithm;
converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
converterConfig.resampling.speex.quality = pConfig->resampling.speex.quality;
return ma_data_converter_init(&converterConfig, &pDecoder->converter); return ma_data_converter_init(&converterConfig, &pDecoder->converter);
} }
...@@ -4,25 +4,12 @@ USAGE: audioconverter [input file] [output file] [format] [channels] [rate] ...@@ -4,25 +4,12 @@ USAGE: audioconverter [input file] [output file] [format] [channels] [rate]
EXAMPLES: EXAMPLES:
audioconverter my_file.flac my_file.wav audioconverter my_file.flac my_file.wav
audioconverter my_file.flac my_file.wav f32 44100 linear --linear-order 8 audioconverter my_file.flac my_file.wav f32 44100 linear --linear-order 8
audioconverter my_file.flac my_file.wav s16 2 44100 speex --speex-quality 10
*/
/*
Note about Speex resampling. If you decide to enable the Speex resampler with ENABLE_SPEEX, this program will use licensed third party code. If you compile and
redistribute this program you need to include a copy of the license which can be found at https://github.com/xiph/opus-tools/blob/master/COPYING. You can also
find a copy of this text in extras/speex_resampler/README.md in the miniaudio repository.
*/ */
#define _CRT_SECURE_NO_WARNINGS /* For stb_vorbis' usage of fopen() instead of fopen_s(). */ #define _CRT_SECURE_NO_WARNINGS /* For stb_vorbis' usage of fopen() instead of fopen_s(). */
#define STB_VORBIS_HEADER_ONLY #define STB_VORBIS_HEADER_ONLY
#include "../../extras/stb_vorbis.c" /* Enables Vorbis decoding. */ #include "../../extras/stb_vorbis.c" /* Enables Vorbis decoding. */
/* Enable Speex resampling, but only if requested on the command line at build time. */
#if defined(ENABLE_SPEEX)
#define MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
#include "../../extras/speex_resampler/ma_speex_resampler.h"
#endif
#define MA_NO_DEVICE_IO #define MA_NO_DEVICE_IO
#define MA_NO_THREADING #define MA_NO_THREADING
#define MINIAUDIO_IMPLEMENTATION #define MINIAUDIO_IMPLEMENTATION
...@@ -44,7 +31,6 @@ void print_usage() ...@@ -44,7 +31,6 @@ void print_usage()
printf("\n"); printf("\n");
printf("PARAMETERS:\n"); printf("PARAMETERS:\n");
printf(" --linear-order [0..%d]\n", MA_MAX_FILTER_ORDER); printf(" --linear-order [0..%d]\n", MA_MAX_FILTER_ORDER);
printf(" --speex-quality [0..10]\n");
} }
ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder) ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder)
...@@ -64,9 +50,9 @@ ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder) ...@@ -64,9 +50,9 @@ ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder)
ma_uint64 framesToReadThisIteration; ma_uint64 framesToReadThisIteration;
framesToReadThisIteration = sizeof(pRawData) / ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); framesToReadThisIteration = sizeof(pRawData) / ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
framesReadThisIteration = ma_decoder_read_pcm_frames(pDecoder, pRawData, framesToReadThisIteration); result = ma_decoder_read_pcm_frames(pDecoder, pRawData, framesToReadThisIteration, &framesReadThisIteration);
if (framesReadThisIteration == 0) { if (result != MA_SUCCESS) {
break; /* Reached the end. */ break; /* Reached the end, or an error occurred. */
} }
/* At this point we have the raw data from the decoder. We now just need to write it to the encoder. */ /* At this point we have the raw data from the decoder. We now just need to write it to the encoder. */
...@@ -159,8 +145,6 @@ ma_bool32 try_parse_resample_algorithm(const char* str, ma_resample_algorithm* p ...@@ -159,8 +145,6 @@ ma_bool32 try_parse_resample_algorithm(const char* str, ma_resample_algorithm* p
/* */ if (strcmp(str, "linear") == 0) { /* */ if (strcmp(str, "linear") == 0) {
algorithm = ma_resample_algorithm_linear; algorithm = ma_resample_algorithm_linear;
} else if (strcmp(str, "speex") == 0) {
algorithm = ma_resample_algorithm_speex;
} else { } else {
return MA_FALSE; /* Not a valid algorithm */ return MA_FALSE; /* Not a valid algorithm */
} }
...@@ -179,13 +163,12 @@ int main(int argc, char** argv) ...@@ -179,13 +163,12 @@ int main(int argc, char** argv)
ma_decoder decoder; ma_decoder decoder;
ma_encoder_config encoderConfig; ma_encoder_config encoderConfig;
ma_encoder encoder; ma_encoder encoder;
ma_resource_format outputResourceFormat; ma_encoding_format outputEncodingFormat;
ma_format format = ma_format_unknown; ma_format format = ma_format_unknown;
ma_uint32 channels = 0; ma_uint32 channels = 0;
ma_uint32 rate = 0; ma_uint32 rate = 0;
ma_resample_algorithm resampleAlgorithm; ma_resample_algorithm resampleAlgorithm;
ma_uint32 linearOrder = 8; ma_uint32 linearOrder = 8;
ma_uint32 speexQuality = 3;
int iarg; int iarg;
const char* pOutputFilePath; const char* pOutputFilePath;
...@@ -204,12 +187,7 @@ int main(int argc, char** argv) ...@@ -204,12 +187,7 @@ int main(int argc, char** argv)
return -1; return -1;
} }
/* Default to Speex if it's enabled. */
#if defined(ENABLE_SPEEX)
resampleAlgorithm = ma_resample_algorithm_speex;
#else
resampleAlgorithm = ma_resample_algorithm_linear; resampleAlgorithm = ma_resample_algorithm_linear;
#endif
/* /*
The fourth and fifth arguments can be a format and/or rate specifier. It doesn't matter which order they are in as we can identify them by whether or The fourth and fifth arguments can be a format and/or rate specifier. It doesn't matter which order they are in as we can identify them by whether or
...@@ -230,20 +208,6 @@ int main(int argc, char** argv) ...@@ -230,20 +208,6 @@ int main(int argc, char** argv)
continue; continue;
} }
if (strcmp(argv[iarg], "--speex-quality") == 0) {
iarg += 1;
if (iarg >= argc) {
break;
}
if (!try_parse_uint32_in_range(argv[iarg], &speexQuality, 0, 10)) {
printf("Expecting a number between 0 and 10 for --speex-quality.\n");
return -1;
}
continue;
}
if (try_parse_resample_algorithm(argv[iarg], &resampleAlgorithm)) { if (try_parse_resample_algorithm(argv[iarg], &resampleAlgorithm)) {
continue; continue;
} }
...@@ -268,9 +232,6 @@ int main(int argc, char** argv) ...@@ -268,9 +232,6 @@ int main(int argc, char** argv)
decoderConfig = ma_decoder_config_init(format, channels, rate); decoderConfig = ma_decoder_config_init(format, channels, rate);
decoderConfig.resampling.algorithm = resampleAlgorithm; decoderConfig.resampling.algorithm = resampleAlgorithm;
decoderConfig.resampling.linear.lpfOrder = linearOrder; decoderConfig.resampling.linear.lpfOrder = linearOrder;
#if defined(ENABLE_SPEEX)
decoderConfig.resampling.speex.quality = speexQuality;
#endif
result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder); result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -296,15 +257,15 @@ int main(int argc, char** argv) ...@@ -296,15 +257,15 @@ int main(int argc, char** argv)
pOutputFilePath = argv[2]; pOutputFilePath = argv[2];
outputResourceFormat = ma_resource_format_wav; /* Wave by default in case we don't know the file extension. */ outputEncodingFormat = ma_encoding_format_wav; /* Wave by default in case we don't know the file extension. */
if (ma_path_extension_equal(pOutputFilePath, "wav")) { if (ma_path_extension_equal(pOutputFilePath, "wav")) {
outputResourceFormat = ma_resource_format_wav; outputEncodingFormat = ma_encoding_format_wav;
} else { } else {
printf("Warning: Unknown file extension \"%s\". Encoding as WAV.\n", ma_path_extension(pOutputFilePath)); printf("Warning: Unknown file extension \"%s\". Encoding as WAV.\n", ma_path_extension(pOutputFilePath));
} }
/* Initialize the encoder for the output file. */ /* Initialize the encoder for the output file. */
encoderConfig = ma_encoder_config_init(ma_resource_format_wav, decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate); encoderConfig = ma_encoder_config_init(outputEncodingFormat, decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate);
result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, &encoder); result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, &encoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_decoder_uninit(&decoder); ma_decoder_uninit(&decoder);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment