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
31086c5d
Commit
31086c5d
authored
Jan 19, 2020
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix bugs in ma_resampler_get_required_input_frame_count().
parent
5df3c0ce
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
54 additions
and
86 deletions
+54
-86
miniaudio.h
miniaudio.h
+1
-1
research/ma_resampler.h
research/ma_resampler.h
+53
-85
No files found.
miniaudio.h
View file @
31086c5d
...
@@ -4285,7 +4285,7 @@ static MA_INLINE ma_int32 ma_mix_s32_fast(ma_int32 x, ma_int32 y, float a)
...
@@ -4285,7 +4285,7 @@ static MA_INLINE ma_int32 ma_mix_s32_fast(ma_int32 x, ma_int32 y, float a)
return x + r1;
return x + r1;
}
}
static MA_INLINE ma_int
32
ma_mix_s16_fast(ma_int32 x, ma_int32 y, float a)
static MA_INLINE ma_int
16
ma_mix_s16_fast(ma_int32 x, ma_int32 y, float a)
{
{
return (ma_int16)ma_mix_s32_fast(x, y, a);
return (ma_int16)ma_mix_s32_fast(x, y, a);
}
}
research/ma_resampler.h
View file @
31086c5d
/* Resampling research. Public domain. */
/* Resampling research. Public domain. */
/*
TODO:
- Add documentation about Speex and how initialization will fail if it's unavailable.
*/
#ifndef ma_resampler_h
#ifndef ma_resampler_h
#define ma_resampler_h
#define ma_resampler_h
...
@@ -7,8 +12,7 @@
...
@@ -7,8 +12,7 @@
typedef
enum
typedef
enum
{
{
ma_resample_algorithm_linear_lpf
=
0
,
/* Linear with a biquad low pass filter. Default. */
ma_resample_algorithm_linear
,
/* Fastest, lowest quality. Optional low-pass filtering. Default. */
ma_resample_algorithm_linear
,
/* Fastest, lowest quality. */
ma_resample_algorithm_speex
ma_resample_algorithm_speex
}
ma_resample_algorithm
;
}
ma_resample_algorithm
;
...
@@ -21,13 +25,10 @@ typedef struct
...
@@ -21,13 +25,10 @@ typedef struct
ma_resample_algorithm
algorithm
;
ma_resample_algorithm
algorithm
;
struct
struct
{
{
int
_unused
;
ma_bool32
enableLPF
;
ma_uint32
lpfCutoffFrequency
;
}
linear
;
}
linear
;
struct
struct
{
ma_uint32
cutoffFrequency
;
}
linearLPF
;
struct
{
{
int
quality
;
/* 0 to 10. Defaults to 3. */
int
quality
;
/* 0 to 10. Defaults to 3. */
}
speex
;
}
speex
;
...
@@ -110,24 +111,12 @@ number of output frames.
...
@@ -110,24 +111,12 @@ number of output frames.
The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
read from the client in order to output the specified number of output frames.
read from the client in order to output the specified number of output frames.
When the end of input mode is set to ma_resampler_end_of_input_mode_no_consume, the input frames sitting in the filter
window are not included in the calculation.
*/
*/
ma_uint64
ma_resampler_get_required_input_frame_count
(
ma_resampler
*
pResampler
,
ma_uint64
outputFrameCount
);
ma_uint64
ma_resampler_get_required_input_frame_count
(
ma_resampler
*
pResampler
,
ma_uint64
outputFrameCount
);
/*
/*
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
input frames from the client.
input frames from the client.
A detail to keep in mind is how cached input frames are handled. This function calculates the output frame count based on
inputFrameCount + ma_resampler_get_cached_input_time(). It essentially calcualtes how many output frames will be returned
if an additional inputFrameCount frames were read from the client and consumed by the resampler. You can adjust the return
value by ma_resampler_get_cached_output_frame_count() which calculates the number of output frames that can be output from
the currently cached input.
When the end of input mode is set to ma_resampler_end_of_input_mode_no_consume, the input frames sitting in the filter
window are not included in the calculation.
*/
*/
ma_uint64
ma_resampler_get_expected_output_frame_count
(
ma_resampler
*
pResampler
,
ma_uint64
inputFrameCount
);
ma_uint64
ma_resampler_get_expected_output_frame_count
(
ma_resampler
*
pResampler
,
ma_uint64
inputFrameCount
);
...
@@ -185,7 +174,7 @@ static ma_result ma_resampler__init_lpf(ma_resampler* pResampler)
...
@@ -185,7 +174,7 @@ static ma_result ma_resampler__init_lpf(ma_resampler* pResampler)
pResampler
->
state
.
linear
.
t
=
-
1
;
/* This must be set to -1 for the linear backend. It's used to indicate that the first frame needs to be loaded. */
pResampler
->
state
.
linear
.
t
=
-
1
;
/* This must be set to -1 for the linear backend. It's used to indicate that the first frame needs to be loaded. */
lpfConfig
=
ma_lpf_config_init
(
pResampler
->
config
.
format
,
pResampler
->
config
.
channels
,
pResampler
->
config
.
sampleRateOut
,
pResampler
->
config
.
linear
LPF
.
c
utoffFrequency
);
lpfConfig
=
ma_lpf_config_init
(
pResampler
->
config
.
format
,
pResampler
->
config
.
channels
,
pResampler
->
config
.
sampleRateOut
,
pResampler
->
config
.
linear
.
lpfC
utoffFrequency
);
if
(
lpfConfig
.
cutoffFrequency
==
0
)
{
if
(
lpfConfig
.
cutoffFrequency
==
0
)
{
lpfConfig
.
cutoffFrequency
=
ma_min
(
pResampler
->
config
.
sampleRateIn
,
pResampler
->
config
.
sampleRateOut
)
/
2
;
lpfConfig
.
cutoffFrequency
=
ma_min
(
pResampler
->
config
.
sampleRateIn
,
pResampler
->
config
.
sampleRateOut
)
/
2
;
}
}
...
@@ -212,12 +201,16 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
...
@@ -212,12 +201,16 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
switch
(
pConfig
->
algorithm
)
switch
(
pConfig
->
algorithm
)
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear_lpf
:
{
{
/* We need to initialize the time to -1 so that ma_resampler_process() can know that it needs to load the buffer with an initial frame. */
pResampler
->
state
.
linear
.
t
=
-
1
;
if
(
pConfig
->
linear
.
enableLPF
)
{
result
=
ma_resampler__init_lpf
(
pResampler
);
result
=
ma_resampler__init_lpf
(
pResampler
);
if
(
result
!=
MA_SUCCESS
)
{
if
(
result
!=
MA_SUCCESS
)
{
return
result
;
return
result
;
}
}
}
}
break
;
}
break
;
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
...
@@ -278,12 +271,6 @@ static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, co
...
@@ -278,12 +271,6 @@ static ma_result ma_resampler_process__seek__linear(ma_resampler* pResampler, co
return
MA_SUCCESS
;
return
MA_SUCCESS
;
}
}
static
ma_result
ma_resampler_process__seek__linear_lpf
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
ma_uint64
*
pFrameCountOut
)
{
/* TODO: Proper linear LPF implementation. */
return
ma_resampler_process__seek__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
}
#if defined(MA_HAS_SPEEX_RESAMPLER)
#if defined(MA_HAS_SPEEX_RESAMPLER)
static
ma_result
ma_resampler_process__seek__speex
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
ma_uint64
*
pFrameCountOut
)
static
ma_result
ma_resampler_process__seek__speex
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
ma_uint64
*
pFrameCountOut
)
{
{
...
@@ -308,20 +295,21 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, const void
...
@@ -308,20 +295,21 @@ static ma_result ma_resampler_process__seek(ma_resampler* pResampler, const void
return
ma_resampler_process__seek__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
return
ma_resampler_process__seek__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
}
break
;
}
break
;
case
ma_resample_algorithm_linear_lpf
:
{
return
ma_resampler_process__seek__linear_lpf
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
}
break
;
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
{
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
#if defined(MA_HAS_SPEEX_RESAMPLER)
return
ma_resampler_process__seek__speex
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
return
ma_resampler_process__seek__speex
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFrameCountOut
);
#else
break
;
#endif
#endif
}
break
;
};
default:
return
MA_INVALID_ARGS
;
/* Should never hit this. */
default:
break
;
}
}
/* Should never hit this. */
MA_ASSERT
(
MA_FALSE
);
return
MA_INVALID_ARGS
;
}
}
static
ma_result
ma_resampler_process__read__linear
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
)
static
ma_result
ma_resampler_process__read__linear
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
)
...
@@ -347,7 +335,7 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
...
@@ -347,7 +335,7 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
frameCountOut
=
*
pFrameCountOut
;
frameCountOut
=
*
pFrameCountOut
;
frameCountIn
=
*
pFrameCountIn
;
frameCountIn
=
*
pFrameCountIn
;
if
(
frameCountOut
==
0
||
frameCountIn
==
0
)
{
if
(
frameCountOut
==
0
)
{
return
MA_INVALID_ARGS
;
/* Nothing to do. */
return
MA_INVALID_ARGS
;
/* Nothing to do. */
}
}
...
@@ -384,10 +372,6 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
...
@@ -384,10 +372,6 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
}
}
for
(;;)
{
for
(;;)
{
if
(
iFrameOut
>=
frameCountOut
||
iFrameIn
>=
frameCountIn
)
{
break
;
}
/* We can't interpolate if our interpolation factor (time relative to x0) is greater than 1. */
/* We can't interpolate if our interpolation factor (time relative to x0) is greater than 1. */
if
(
pResampler
->
state
.
linear
.
t
>
1
)
{
if
(
pResampler
->
state
.
linear
.
t
>
1
)
{
/* Need to load the next input frame. */
/* Need to load the next input frame. */
...
@@ -489,32 +473,18 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
...
@@ -489,32 +473,18 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, co
/* Move time forward. */
/* Move time forward. */
pResampler
->
state
.
linear
.
t
+=
ratioInOut
;
pResampler
->
state
.
linear
.
t
+=
ratioInOut
;
iFrameOut
+=
1
;
iFrameOut
+=
1
;
if
(
iFrameOut
>=
frameCountOut
||
iFrameIn
>=
frameCountIn
)
{
break
;
}
}
}
/* Here is where we set the number of frames that were consumed. */
/* Here is where we set the number of frames that were consumed. */
*
pFrameCountOut
=
iFrameOut
;
*
pFrameCountOut
=
iFrameOut
;
*
pFrameCountIn
=
iFrameIn
;
*
pFrameCountIn
=
iFrameIn
;
return
MA_SUCCESS
;
/* Low-pass filter if it's enabled. */
}
if
(
pResampler
->
config
.
linear
.
enableLPF
&&
pResampler
->
config
.
sampleRateIn
!=
pResampler
->
config
.
sampleRateOut
)
{
static
ma_result
ma_resampler_process__read__linear_lpf
(
ma_resampler
*
pResampler
,
const
void
*
pFramesIn
,
ma_uint64
*
pFrameCountIn
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountOut
)
{
/* 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_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pFramesOut
!=
NULL
);
MA_ASSERT
(
pFrameCountOut
!=
NULL
);
MA_ASSERT
(
pFrameCountIn
!=
NULL
);
result
=
ma_resampler_process__read__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFramesOut
,
pFrameCountOut
);
if
(
result
!=
MA_SUCCESS
)
{
return
result
;
}
/* Now just do an in-place low-pass filter. No need to spend time filtering if the sample rates are the same. */
if
(
pResampler
->
config
.
sampleRateIn
!=
pResampler
->
config
.
sampleRateOut
)
{
return
ma_lpf_process
(
&
pResampler
->
state
.
linear
.
lpf
,
pFramesOut
,
pFramesOut
,
*
pFrameCountOut
);
return
ma_lpf_process
(
&
pResampler
->
state
.
linear
.
lpf
,
pFramesOut
,
pFramesOut
,
*
pFrameCountOut
);
}
else
{
}
else
{
return
MA_SUCCESS
;
return
MA_SUCCESS
;
...
@@ -608,11 +578,6 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, const void
...
@@ -608,11 +578,6 @@ static ma_result ma_resampler_process__read(ma_resampler* pResampler, const void
return
ma_resampler_process__read__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFramesOut
,
pFrameCountOut
);
return
ma_resampler_process__read__linear
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFramesOut
,
pFrameCountOut
);
}
}
case
ma_resample_algorithm_linear_lpf
:
{
return
ma_resampler_process__read__linear_lpf
(
pResampler
,
pFramesIn
,
pFrameCountIn
,
pFramesOut
,
pFrameCountOut
);
}
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
{
{
#if defined(MA_HAS_SPEEX_RESAMPLER)
#if defined(MA_HAS_SPEEX_RESAMPLER)
...
@@ -666,12 +631,10 @@ ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn
...
@@ -666,12 +631,10 @@ ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
{
{
}
break
;
/* If we are using low-pass filtering we need to reinitialize the filter since it depends on the sample rate. */
if
(
pResampler
->
config
.
linear
.
enableLPF
)
{
case
ma_resample_algorithm_linear_lpf
:
{
/* We need to reinitialize the low-pass filter. */
ma_resampler__init_lpf
(
pResampler
);
ma_resampler__init_lpf
(
pResampler
);
}
}
break
;
}
break
;
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
...
@@ -732,23 +695,29 @@ ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler,
...
@@ -732,23 +695,29 @@ ma_uint64 ma_resampler_get_required_input_frame_count(ma_resampler* pResampler,
switch
(
pResampler
->
config
.
algorithm
)
switch
(
pResampler
->
config
.
algorithm
)
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear_lpf
:
{
{
/*
/*
All we're doing is calculating the number of whole input frames that'll be processed after outputFrameCount frames are returned. We can
The first output frame is treated a little different to the rest because it is never interpolated - the first output frame is always the
determine this by just looking at where the input time will be at the end of it.
same as the first input frame. We can know if we're loading the first frame by checking if the input time is < 0.
*/
/*
The linear backend initializes t to -1 at start up to trigger the initial load of the first sample. In this case we will always load at
least two whole input frames before outputting the first output frame.
*/
*/
ma_uint64
count
=
0
;
double
t
=
pResampler
->
state
.
linear
.
t
;
double
t
=
pResampler
->
state
.
linear
.
t
;
if
(
t
<
0
)
{
if
(
t
<
0
)
{
t
=
2
;
count
=
1
;
t
=
1
;
}
/* If the input time is greater than 1 we consume any whole input frames. */
if
(
t
>
1
)
{
count
=
(
ma_uint64
)
t
;
t
-=
count
;
}
}
return
(
ma_uint64
)
ceil
(
pResampler
->
state
.
linear
.
t
+
(
outputFrameCount
*
ratioInOut
))
-
1
;
/* At this point we are guaranteed to get at least one output frame from the cached input (not requiring an additional input). */
outputFrameCount
-=
1
;
count
+=
(
ma_uint64
)
ceil
(
t
+
(
outputFrameCount
*
ratioInOut
))
-
1
;
return
count
;
}
}
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
...
@@ -785,7 +754,6 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler,
...
@@ -785,7 +754,6 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler,
switch
(
pResampler
->
config
.
algorithm
)
switch
(
pResampler
->
config
.
algorithm
)
{
{
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear
:
case
ma_resample_algorithm_linear_lpf
:
{
{
/*
/*
The linear backend initializes t to -1 at start up to trigger the initial load of the first sample. In this case we will always load at
The linear backend initializes t to -1 at start up to trigger the initial load of the first sample. In this case we will always load at
...
@@ -796,7 +764,7 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler,
...
@@ -796,7 +764,7 @@ ma_uint64 ma_resampler_get_expected_output_frame_count(ma_resampler* pResampler,
t
=
2
;
t
=
2
;
}
}
return
(
ma_uint64
)
ceil
((
pResampler
->
state
.
linear
.
t
+
inputFrameCount
)
/
ratioInOut
)
-
1
;
return
(
ma_uint64
)
ceil
((
t
+
inputFrameCount
)
/
ratioInOut
)
-
1
;
}
}
case
ma_resample_algorithm_speex
:
case
ma_resample_algorithm_speex
:
...
...
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