Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
miniaudio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
miniaudio
Commits
1449edf4
Commit
1449edf4
authored
Jan 18, 2020
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for the Speex resampler.
parent
7d377894
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2110 additions
and
16 deletions
+2110
-16
extras/speex_resampler/README.md
extras/speex_resampler/README.md
+32
-0
extras/speex_resampler/ma_speex_resampler.h
extras/speex_resampler/ma_speex_resampler.h
+25
-0
extras/speex_resampler/thirdparty/arch.h
extras/speex_resampler/thirdparty/arch.h
+219
-0
extras/speex_resampler/thirdparty/resample.c
extras/speex_resampler/thirdparty/resample.c
+1239
-0
extras/speex_resampler/thirdparty/resample_sse.h
extras/speex_resampler/thirdparty/resample_sse.h
+128
-0
extras/speex_resampler/thirdparty/speex_resampler.h
extras/speex_resampler/thirdparty/speex_resampler.h
+343
-0
research/ma_resampler.h
research/ma_resampler.h
+124
-16
No files found.
extras/speex_resampler/README.md
0 → 100644
View file @
1449edf4
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.
extras/speex_resampler/ma_speex_resampler.h
0 → 100644
View file @
1449edf4
#ifndef ma_speex_resampler_h
#define ma_speex_resampler_h
#define OUTSIDE_SPEEX
#define RANDOM_PREFIX ma_speex
#include "thirdparty/speex_resampler.h"
#endif
/* ma_speex_resampler_h */
#if defined(MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION)
#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 */
#else
#pragma GCC diagnostic push
#endif
#include "thirdparty/resample.c"
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
#endif
\ No newline at end of file
extras/speex_resampler/thirdparty/arch.h
0 → 100644
View file @
1449edf4
/* 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
extras/speex_resampler/thirdparty/resample.c
0 → 100644
View file @
1449edf4
/* 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
.
07063
950
,
0
.
060756
85
,
0
.
051
93064
,
0
.
0440
9466
,
0
.
0371
8069
,
0
.
03111
947
,
0
.
025
84161
,
0
.
02127
838
,
0
.
01736250
,
0
.
01402
878
,
0
.
01121463
,
0
.
00
886058
,
0
.
006
91064
,
0
.
00531256
,
0
.
00401
805
,
0
.
002
98291
,
0
.
00216702
,
0
.
0015343
8
,
0
.
001052
97
,
0
.
0006
9463
,
0
.
000434
89
,
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
.
041
93980
,
0
.
02
979584
,
0
.
02044510
,
0
.
01345224
,
0
.
00
839739
,
0
.
004
88951
,
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
.
047240
88
,
0
.
03412321
,
0
.
0236
9490
,
0
.
015630
93
,
0
.
00
959968
,
0
.
00527363
,
0
.
00233
883
,
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
.
05031
820
,
0
.
03607231
,
0
.
02432151
,
0
.
014
87334
,
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
.
830
f
,
0
.
860
f
,
KAISER6
},
/* Q0 */
{
16
,
4
,
0
.
850
f
,
0
.
880
f
,
KAISER6
},
/* Q1 */
{
32
,
4
,
0
.
882
f
,
0
.
910
f
,
KAISER6
},
/* Q2 */
/* 82.3% cutoff ( ~60 dB stop) 6 */
{
48
,
8
,
0
.
895
f
,
0
.
917
f
,
KAISER8
},
/* Q3 */
/* 84.9% cutoff ( ~80 dB stop) 8 */
{
64
,
8
,
0
.
921
f
,
0
.
940
f
,
KAISER8
},
/* Q4 */
/* 88.7% cutoff ( ~80 dB stop) 8 */
{
80
,
16
,
0
.
922
f
,
0
.
940
f
,
KAISER10
},
/* Q5 */
/* 89.1% cutoff (~100 dB stop) 10 */
{
96
,
16
,
0
.
940
f
,
0
.
945
f
,
KAISER10
},
/* Q6 */
/* 91.5% cutoff (~100 dB stop) 10 */
{
128
,
16
,
0
.
950
f
,
0
.
950
f
,
KAISER10
},
/* Q7 */
/* 93.1% cutoff (~100 dB stop) 10 */
{
160
,
16
,
0
.
960
f
,
0
.
960
f
,
KAISER10
},
/* Q8 */
/* 94.5% cutoff (~100 dB stop) 10 */
{
192
,
32
,
0
.
968
f
,
0
.
968
f
,
KAISER12
},
/* Q9 */
/* 95.5% cutoff (~100 dB stop) 10 */
{
256
,
32
,
0
.
975
f
,
0
.
975
f
,
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-6
f
)
return
WORD2INT
(
32768
.
*
cutoff
);
else
if
(
fabs
(
x
)
>
.
5
f
*
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
.
16667
f
,
15
),
x
)
+
MULT16_16
(
QCONST16
(
0
.
16667
f
,
15
),
x3
),
15
);
interp
[
1
]
=
EXTRACT16
(
EXTEND32
(
x
)
+
SHR32
(
SUB32
(
EXTEND32
(
x2
),
EXTEND32
(
x3
)),
1
));
interp
[
3
]
=
PSHR32
(
MULT16_16
(
QCONST16
(
-
0
.
33333
f
,
15
),
x
)
+
MULT16_16
(
QCONST16
(.
5
f
,
15
),
x2
)
-
MULT16_16
(
QCONST16
(
0
.
16667
f
,
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
.
16667
f
*
frac
+
0
.
16667
f
*
frac
*
frac
*
frac
;
interp
[
1
]
=
frac
+
0
.
5
f
*
frac
*
frac
-
0
.
5
f
*
frac
*
frac
*
frac
;
/*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
interp
[
3
]
=
-
0
.
33333
f
*
frac
+
0
.
5
f
*
frac
*
frac
-
0
.
16667
f
*
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."
;
}
}
extras/speex_resampler/thirdparty/resample_sse.h
0 → 100644
View file @
1449edf4
/* 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
extras/speex_resampler/thirdparty/speex_resampler.h
0 → 100644
View file @
1449edf4
/* 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
research/ma_resampler.h
View file @
1449edf4
...
@@ -9,6 +9,7 @@ typedef enum
...
@@ -9,6 +9,7 @@ typedef enum
{
{
ma_resample_algorithm_linear_lpf
=
0
,
/* Linear with a biquad low pass filter. Default. */
ma_resample_algorithm_linear_lpf
=
0
,
/* Linear with a biquad low pass filter. Default. */
ma_resample_algorithm_linear
,
/* Fastest, lowest quality. */
ma_resample_algorithm_linear
,
/* Fastest, lowest quality. */
ma_resample_algorithm_speex
}
ma_resample_algorithm
;
}
ma_resample_algorithm
;
typedef
struct
typedef
struct
...
@@ -26,6 +27,10 @@ typedef struct
...
@@ -26,6 +27,10 @@ typedef struct
{
{
ma_uint32
cutoffFrequency
;
ma_uint32
cutoffFrequency
;
}
linearLPF
;
}
linearLPF
;
struct
{
int
quality
;
/* 0 to 10. Defaults to 3. */
}
speex
;
}
ma_resampler_config
;
}
ma_resampler_config
;
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
ma_resampler_config_init
(
ma_format
format
,
ma_uint32
channels
,
ma_uint32
sampleRateIn
,
ma_uint32
sampleRateOut
,
ma_resample_algorithm
algorithm
);
...
@@ -44,6 +49,7 @@ typedef struct
...
@@ -44,6 +49,7 @@ typedef struct
float
x1
[
MA_MAX_CHANNELS
];
/* The next input frame. */
float
x1
[
MA_MAX_CHANNELS
];
/* The next input frame. */
ma_lpf
lpf
;
ma_lpf
lpf
;
}
linear
;
}
linear
;
void
*
pSpeex
;
/* SpeexResamplerState* */
}
state
;
}
state
;
}
ma_resampler
;
}
ma_resampler
;
...
@@ -72,7 +78,7 @@ It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
...
@@ -72,7 +78,7 @@ It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
*/
*/
ma_result
ma_resampler_process
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
);
ma_result
ma_resampler_process
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
);
/*
/*
...
@@ -96,6 +102,24 @@ Implementation
...
@@ -96,6 +102,24 @@ Implementation
#define MA_RESAMPLER_MAX_RATIO 48.0
#define MA_RESAMPLER_MAX_RATIO 48.0
#endif
#endif
#if defined(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_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
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
;
...
@@ -106,6 +130,7 @@ ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channel
...
@@ -106,6 +130,7 @@ ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channel
config
.
sampleRateIn
=
sampleRateIn
;
config
.
sampleRateIn
=
sampleRateIn
;
config
.
sampleRateOut
=
sampleRateOut
;
config
.
sampleRateOut
=
sampleRateOut
;
config
.
algorithm
=
algorithm
;
config
.
algorithm
=
algorithm
;
config
.
speex
.
quality
=
3
;
/* Cannot leave this as 0 as that is actually a valid value for Speex resampling quality. */
return
config
;
return
config
;
}
}
...
@@ -141,13 +166,27 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
...
@@ -141,13 +166,27 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
}
}
}
break
;
}
break
;
case
ma_resample_algorithm_speex
:
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
int
speexErr
;
pResampler
->
state
.
pSpeex
=
speex_resampler_init
(
pConfig
->
channels
,
pConfig
->
sampleRateIn
,
pConfig
->
sampleRateOut
,
pConfig
->
speex
.
quality
,
&
speexErr
);
if
(
pResampler
->
state
.
pSpeex
==
NULL
)
{
return
ma_result_from_speex_err
(
speexErr
);
}
#else
/* Speex resampler not available. */
return
MA_INVALID_ARGS
;
#endif
}
break
;
default:
return
MA_INVALID_ARGS
;
default:
return
MA_INVALID_ARGS
;
}
}
return
MA_SUCCESS
;
return
MA_SUCCESS
;
}
}
static
ma_result
ma_resampler_process__seek__linear
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
static
ma_result
ma_resampler_process__seek__linear
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pResampler
!=
NULL
);
...
@@ -172,13 +211,13 @@ static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, ma
...
@@ -172,13 +211,13 @@ static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, ma
return
MA_SUCCESS
;
return
MA_SUCCESS
;
}
}
static
ma_result
ma_resampler_process__seek__linear_lpf
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
static
ma_result
ma_resampler_process__seek__linear_lpf
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
/* TODO: Proper linear LPF implementation. */
/* TODO: Proper linear LPF implementation. */
return
ma_resampler_process__seek__linear
(
pResampler
,
pFrameCountOut
,
pFrame
CountIn
,
pFrames
In
);
return
ma_resampler_process__seek__linear
(
pResampler
,
pFrameCountOut
,
pFrame
sIn
,
pFrameCount
In
);
}
}
static
ma_result
ma_resampler_process__seek
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
static
ma_result
ma_resampler_process__seek
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pResampler
!=
NULL
);
...
@@ -186,12 +225,12 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, ma_uint64*
...
@@ -186,12 +225,12 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, ma_uint64*
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
{
{
return
ma_resampler_process__seek__linear
(
pResampler
,
pFrameCountOut
,
pFrame
CountIn
,
pFrames
In
);
return
ma_resampler_process__seek__linear
(
pResampler
,
pFrameCountOut
,
pFrame
sIn
,
pFrameCount
In
);
}
break
;
}
break
;
case
ma_resample_algorithm_linear_lpf
:
case
ma_resample_algorithm_linear_lpf
:
{
{
return
ma_resampler_process__seek__linear_lpf
(
pResampler
,
pFrameCountOut
,
pFrame
CountIn
,
pFrames
In
);
return
ma_resampler_process__seek__linear_lpf
(
pResampler
,
pFrameCountOut
,
pFrame
sIn
,
pFrameCount
In
);
}
break
;
}
break
;
default:
return
MA_INVALID_ARGS
;
/* Should never hit this. */
default:
return
MA_INVALID_ARGS
;
/* Should never hit this. */
...
@@ -199,7 +238,7 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, ma_uint64*
...
@@ -199,7 +238,7 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, ma_uint64*
}
}
static
ma_result
ma_resampler_process__read__linear
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
static
ma_result
ma_resampler_process__read__linear
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
ma_uint64
frameCountOut
;
ma_uint64
frameCountOut
;
ma_uint64
frameCountIn
;
ma_uint64
frameCountIn
;
...
@@ -307,7 +346,7 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
...
@@ -307,7 +346,7 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
return
MA_SUCCESS
;
return
MA_SUCCESS
;
}
}
static
ma_result
ma_resampler_process__read__linear_lpf
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
static
ma_result
ma_resampler_process__read__linear_lpf
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
/* To do this we just read using the non-filtered linear pipeline, and then do an in-place filter on the output buffer. */
/* To do this we just read using the non-filtered linear pipeline, and then do an in-place filter on the output buffer. */
ma_result
result
;
ma_result
result
;
...
@@ -318,7 +357,7 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
...
@@ -318,7 +357,7 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
MA_ASSERT
(
pFramesIn
!=
NULL
);
MA_ASSERT
(
pFramesIn
!=
NULL
);
MA_ASSERT
(
pFrameCountIn
!=
NULL
);
MA_ASSERT
(
pFrameCountIn
!=
NULL
);
result
=
ma_resampler_process__read__linear
(
pResampler
,
pFrame
CountOut
,
pFramesOut
,
pFrameCountIn
,
pFrames
In
);
result
=
ma_resampler_process__read__linear
(
pResampler
,
pFrame
sOut
,
pFrameCountOut
,
pFramesIn
,
pFrameCount
In
);
if
(
result
!=
MA_SUCCESS
)
{
if
(
result
!=
MA_SUCCESS
)
{
return
result
;
return
result
;
}
}
...
@@ -331,7 +370,71 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
...
@@ -331,7 +370,71 @@ static ma_result ma_resampler_process__read__linear_lpf(ma_resampler* pResampler
}
}
}
}
static
ma_result
ma_resampler_process__read
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFramesIn
)
static
ma_result
ma_resampler_process__read__speex
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
)
{
/* To do this we just read using the non-filtered linear pipeline, and then do an in-place filter on the output buffer. */
int
speexErr
;
ma_uint64
frameCountOut
;
ma_uint64
frameCountIn
;
ma_uint64
framesProcessedOut
;
ma_uint64
framesProcessedIn
;
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pFramesOut
!=
NULL
);
MA_ASSERT
(
pFrameCountOut
!=
NULL
);
MA_ASSERT
(
pFramesIn
!=
NULL
);
MA_ASSERT
(
pFrameCountIn
!=
NULL
);
/* Speex uses unsigned int counts, whereas miniaudio uses 64-bit. We'll need to process in a loop. */
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
=
UINT_MAX
;
if
((
ma_uint64
)
frameCountInThisIteration
>
(
frameCountIn
-
framesProcessedIn
))
{
frameCountInThisIteration
=
(
unsigned
int
)(
frameCountIn
-
framesProcessedIn
);
}
frameCountOutThisIteration
=
UINT_MAX
;
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
.
pSpeex
,
pFramesInThisIteration
,
&
frameCountInThisIteration
,
pFramesOutThisIteration
,
&
frameCountOutThisIteration
);
}
else
if
(
pResampler
->
config
.
format
==
ma_format_s16
)
{
speexErr
=
speex_resampler_process_interleaved_int
((
SpeexResamplerState
*
)
pResampler
->
state
.
pSpeex
,
pFramesInThisIteration
,
&
frameCountInThisIteration
,
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
;
}
static
ma_result
ma_resampler_process__read
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
)
{
{
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pFramesOut
!=
NULL
);
MA_ASSERT
(
pFramesOut
!=
NULL
);
...
@@ -350,19 +453,24 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, ma_uint64*
...
@@ -350,19 +453,24 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, ma_uint64*
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
{
{
return
ma_resampler_process__read__linear
(
pResampler
,
pFrame
CountOut
,
pFramesOut
,
pFrameCountIn
,
pFrames
In
);
return
ma_resampler_process__read__linear
(
pResampler
,
pFrame
sOut
,
pFrameCountOut
,
pFramesIn
,
pFrameCount
In
);
}
break
;
}
break
;
case
ma_resample_algorithm_linear_lpf
:
case
ma_resample_algorithm_linear_lpf
:
{
{
return
ma_resampler_process__read__linear_lpf
(
pResampler
,
pFrameCountOut
,
pFramesOut
,
pFrameCountIn
,
pFramesIn
);
return
ma_resampler_process__read__linear_lpf
(
pResampler
,
pFramesOut
,
pFrameCountOut
,
pFramesIn
,
pFrameCountIn
);
}
break
;
case
ma_resample_algorithm_speex
:
{
return
ma_resampler_process__read__speex
(
pResampler
,
pFramesOut
,
pFrameCountOut
,
pFramesIn
,
pFrameCountIn
);
}
break
;
}
break
;
default:
return
MA_INVALID_ARGS
;
/* Should never hit this. */
default:
return
MA_INVALID_ARGS
;
/* Should never hit this. */
}
}
}
}
ma_result
ma_resampler_process
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFrames
In
)
ma_result
ma_resampler_process
(
ma_resampler
*
pResampler
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCount
In
)
{
{
if
(
pResampler
==
NULL
)
{
if
(
pResampler
==
NULL
)
{
return
MA_INVALID_ARGS
;
return
MA_INVALID_ARGS
;
...
@@ -374,10 +482,10 @@ ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountO
...
@@ -374,10 +482,10 @@ ma_result ma_resampler_process(ma_resampler* pResampler, ma_uint64* pFrameCountO
if
(
pFramesOut
!=
NULL
)
{
if
(
pFramesOut
!=
NULL
)
{
/* Reading. */
/* Reading. */
return
ma_resampler_process__read
(
pResampler
,
pFrame
CountOut
,
pFramesOut
,
pFrameCountIn
,
pFrames
In
);
return
ma_resampler_process__read
(
pResampler
,
pFrame
sOut
,
pFrameCountOut
,
pFramesIn
,
pFrameCount
In
);
}
else
{
}
else
{
/* Seeking. */
/* Seeking. */
return
ma_resampler_process__seek
(
pResampler
,
pFrameCountOut
,
pFrame
CountIn
,
pFrames
In
);
return
ma_resampler_process__seek
(
pResampler
,
pFrameCountOut
,
pFrame
sIn
,
pFrameCount
In
);
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment