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
11d752ad
Commit
11d752ad
authored
Jan 14, 2020
by
David Reid
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Untested work in progress on the improved resampler.
parent
44d2605a
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
116 additions
and
90 deletions
+116
-90
research/ma_lpf.h
research/ma_lpf.h
+6
-1
research/ma_resampler.h
research/ma_resampler.h
+102
-88
tests/ma_test_0.vcxproj
tests/ma_test_0.vcxproj
+8
-1
No files found.
research/ma_lpf.h
View file @
11d752ad
...
...
@@ -133,10 +133,15 @@ ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFrame
/* Fast path for passthrough. */
if
(
pBQ
->
isPassthrough
)
{
if
(
pFramesOut
!=
pFramesIn
)
{
/* <-- The output buffer is allowed to be the same as the input buffer. */
ma_copy_memory_64
(
pFramesOut
,
pFramesIn
,
frameCount
*
ma_get_bytes_per_frame
(
pBQ
->
config
.
format
,
pBQ
->
config
.
channels
));
}
return
MA_SUCCESS
;
}
/* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */
/* Currently only supporting f32. */
if
(
pBQ
->
config
.
format
==
ma_format_f32
)
{
float
*
pY
=
(
float
*
)
pFramesOut
;
...
...
research/ma_resampler.h
View file @
11d752ad
...
...
@@ -39,6 +39,9 @@ typedef struct
{
struct
{
float
t
;
/* Input time, relative to x0. */
float
x0
[
MA_MAX_CHANNELS
];
/* The previous input frame. */
float
x1
[
MA_MAX_CHANNELS
];
/* The next input frame. */
ma_lpf
lpf
;
}
linear
;
}
state
;
...
...
@@ -117,19 +120,16 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
MA_ZERO_OBJECT
(
pResampler
);
pResampler
->
config
=
*
pConfig
;
pResampler
->
timeX
=
0
.
0
f
;
pResampler
->
timeY
=
0
.
0
f
;
switch
(
pConfig
->
algorithm
)
{
case
ma_resample_algorithm_linear
:
{
}
break
;
case
ma_resample_algorithm_linear_lpf
:
{
ma_lpf_config
lpfConfig
;
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
(
pConfig
->
format
,
pConfig
->
channels
,
pConfig
->
sampleRateOut
,
pConfig
->
linearLPF
.
cutoffFrequency
);
if
(
lpfConfig
.
cutoffFrequency
==
0
)
{
lpfConfig
.
cutoffFrequency
=
ma_min
(
pConfig
->
sampleRateIn
,
pConfig
->
sampleRateOut
)
/
2
;
...
...
@@ -201,14 +201,91 @@ 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
*
pFramesIn
)
{
ma_uint64
frameCountOut
;
ma_uint64
frameCountIn
;
float
ratioInOut
;
MA_ASSERT
(
pResampler
!=
NULL
);
MA_ASSERT
(
pFramesOut
!=
NULL
);
MA_ASSERT
(
pFrameCountOut
!=
NULL
);
MA_ASSERT
(
pFrameCountIn
!=
NULL
);
frameCountOut
=
*
pFrameCountOut
;
frameCountIn
=
*
pFrameCountIn
;
ratioInOut
=
(
float
)
pResampler
->
config
.
sampleRateIn
/
(
float
)
pResampler
->
config
.
sampleRateOut
;
if
(
pFramesIn
!=
NULL
)
{
/* Pass in data from the input buffer. */
ma_uint64
iFrameOut
;
ma_uint64
iFrameIn
;
ma_uint64
iChannel
;
iFrameOut
=
0
;
iFrameIn
=
0
;
if
(
pResampler
->
config
.
format
==
ma_format_f32
)
{
float
*
pY
=
(
float
*
)
pFramesOut
;
const
float
*
pX
=
(
const
float
*
)
pFramesIn
;
/*
We need to do an initial load of input data so that the first output frame is the same as the input frame. We can know whether or not to do this by
checking whether or not the current time is < 0 (it will be initialized to -1).
*/
if
(
pResampler
->
state
.
linear
.
t
<
0
)
{
if
(
frameCountIn
>
0
)
{
for
(
iChannel
=
0
;
iChannel
<
pResampler
->
config
.
channels
;
iChannel
+=
1
)
{
pResampler
->
state
.
linear
.
x1
[
iChannel
]
=
pX
[
iChannel
];
}
iFrameIn
+=
1
;
pResampler
->
state
.
linear
.
t
=
1
;
/* Important that we set this to 1. This will cause the logic below to load the _second_ frame so we can do correct interpolation. */
}
}
for
(;;)
{
float
t0
;
float
t1
;
float
y
;
if
(
iFrameOut
>=
frameCountOut
||
iFrameIn
>=
frameCountIn
)
{
break
;
}
for
(
iChannel
=
0
;
iChannel
<
pResampler
->
config
.
channels
;
iChannel
+=
1
)
{
y
=
ma_mix_f32_fast
(
pResampler
->
state
.
linear
.
x0
[
iChannel
],
pResampler
->
state
.
linear
.
x1
[
iChannel
],
pResampler
->
state
.
linear
.
t
);
}
t0
=
pResampler
->
state
.
linear
.
t
;
t1
=
t0
+
ratioInOut
;
if
(
t1
>=
1
)
{
/* Need to load the next input frame. */
iFrameIn
+=
(
ma_uint64
)
t1
;
if
(
iFrameIn
>
0
)
{
for
(
iChannel
=
0
;
iChannel
<
pResampler
->
config
.
channels
;
iChannel
+=
1
)
{
pResampler
->
state
.
linear
.
x0
[
iChannel
]
=
pX
[(
iFrameIn
-
1
)
*
pResampler
->
config
.
channels
+
iChannel
];
pResampler
->
state
.
linear
.
x1
[
iChannel
]
=
pX
[(
iFrameIn
-
0
)
*
pResampler
->
config
.
channels
+
iChannel
];
}
}
else
{
for
(
iChannel
=
0
;
iChannel
<
pResampler
->
config
.
channels
;
iChannel
+=
1
)
{
pResampler
->
state
.
linear
.
x0
[
iChannel
]
=
pResampler
->
state
.
linear
.
x1
[
iChannel
];
pResampler
->
state
.
linear
.
x1
[
iChannel
]
=
pX
[
iFrameIn
*
pResampler
->
config
.
channels
+
iChannel
];
}
}
}
pResampler
->
state
.
linear
.
t
=
t1
-
floorf
(
t1
);
/* The time should always be relative to x0, and should not be greater than 1. */
iFrameOut
+=
1
;
}
}
else
{
/* Format not supported. */
return
MA_INVALID_OPERATION
;
}
}
else
{
/* Pass in zeroes. */
return
MA_INVALID_OPERATION
;
}
return
MA_SUCCESS
;
...
...
@@ -216,8 +293,25 @@ static ma_result ma_resampler_process__read__linear(ma_resampler* pResampler, ma
static
ma_result
ma_resampler_process__read__linear_lpf
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFramesIn
)
{
/* TODO: Proper linear LPF implementation. */
return
ma_resampler_process__read__linear
(
pResampler
,
pFrameCountOut
,
pFramesOut
,
pFrameCountIn
,
pFramesIn
);
/* 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
,
pFrameCountOut
,
pFramesOut
,
pFrameCountIn
,
pFramesIn
);
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
);
}
else
{
return
MA_SUCCESS
;
}
}
static
ma_result
ma_resampler_process__read
(
ma_resampler
*
pResampler
,
ma_uint64
*
pFrameCountOut
,
void
*
pFramesOut
,
ma_uint64
*
pFrameCountIn
,
const
void
*
pFramesIn
)
...
...
@@ -334,85 +428,6 @@ ma_result ma_resample_f32__linear(ma_uint32 sampleRateOut, ma_uint32 sampleRateI
return
MA_INVALID_ARGS
;
}
double
ma_sinc_hann
(
double
n
,
int
N
)
{
double
s
=
sin
(
MA_PI_D
*
n
/
N
);
return
s
*
s
;
}
ma_result
ma_resample_f32__sinc
(
ma_uint32
sampleRateOut
,
ma_uint32
sampleRateIn
,
ma_uint64
sampleCountOut
,
float
*
pSamplesOut
,
ma_uint64
sampleCountIn
,
float
*
pSamplesIn
)
{
double
ratio
=
(
double
)
sampleRateIn
/
(
double
)
sampleRateOut
;
double
timeIn
=
0
;
double
timeOut
=
0
;
double
samplingPeriodIn
=
1
.
0
/
sampleRateIn
;
int
windowWidth
=
32
;
/* Fast path if the sample rates are the same. */
if
(
sampleRateOut
==
sampleRateIn
)
{
MA_COPY_MEMORY
(
pSamplesOut
,
pSamplesIn
,
(
size_t
)
ma_min
(
sampleCountOut
,
sampleCountIn
)
*
sizeof
(
float
));
return
MA_SUCCESS
;
}
/* Do nothing if there's no input. */
if
(
sampleCountOut
==
0
||
sampleCountIn
==
0
)
{
return
MA_SUCCESS
;
}
/* The first output sample should always be the same as the input sample. */
pSamplesOut
[
0
]
=
pSamplesIn
[
0
];
timeIn
+=
ratio
;
timeOut
+=
1
;
for
(;;)
{
ma_uint64
iTimeIn
;
ma_uint64
iTimeOut
;
iTimeIn
=
(
ma_uint64
)
timeIn
;
if
(
iTimeIn
>=
sampleCountIn
)
{
break
;
}
iTimeOut
=
(
ma_uint64
)
timeOut
;
if
(
iTimeOut
>=
sampleCountOut
)
{
break
;
}
{
ma_uint64
iTimeInPrev
=
iTimeIn
;
ma_uint64
iTimeInNext
=
(
ma_uint64
)
ceil
(
timeIn
);
if
(
iTimeInNext
>=
sampleCountIn
)
{
iTimeInNext
=
iTimeInPrev
;
/* <-- We could instead terminate here which would make the output a few samples shorter. */
}
#if 0
pSamplesOut[iTimeOut] = ma_mix_f32_fast(pSamplesIn[iTimeInPrev], pSamplesIn[iTimeInNext], (float)(timeIn - iTimeIn));
#else
pSamplesOut
[
iTimeOut
]
=
0
;
for
(
int
i
=
-
windowWidth
;
i
<
windowWidth
;
i
+=
1
)
{
if
((
i
<
0
&&
-
i
<
iTimeIn
)
||
i
>
0
&&
i
<
sampleCountIn
)
{
#if 0
double t = (iTimeIn+n);
double s = ma_sinc(sampleRateIn*t);
pSamplesOut[iTimeOut] += pSamplesIn[(ma_uint64)(iTimeIn+n)] * s;
#endif
}
}
#endif
}
timeIn
+=
ratio
;
timeOut
+=
1
;
}
return
MA_INVALID_ARGS
;
}
ma_result
ma_resample_f32
(
ma_resample_algorithm
algorithm
,
ma_uint32
sampleRateOut
,
ma_uint32
sampleRateIn
,
ma_uint64
sampleCountOut
,
float
*
pSamplesOut
,
ma_uint64
sampleCountIn
,
float
*
pSamplesIn
)
{
if
(
pSamplesOut
==
NULL
||
pSamplesIn
==
NULL
)
{
...
...
@@ -422,7 +437,6 @@ ma_result ma_resample_f32(ma_resample_algorithm algorithm, ma_uint32 sampleRateO
switch
(
algorithm
)
{
case
ma_resample_algorithm_linear
:
return
ma_resample_f32__linear
(
sampleRateOut
,
sampleRateIn
,
sampleCountOut
,
pSamplesOut
,
sampleCountIn
,
pSamplesIn
);
case
ma_resample_algorithm_sinc
:
return
ma_resample_f32__sinc
(
sampleRateOut
,
sampleRateIn
,
sampleCountOut
,
pSamplesOut
,
sampleCountIn
,
pSamplesIn
);
default:
return
MA_INVALID_ARGS
;
}
}
...
...
tests/ma_test_0.vcxproj
View file @
11d752ad
...
...
@@ -327,6 +327,14 @@
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|x64'"
>
true
</ExcludedFromBuild>
</ClCompile>
<ClCompile
Include=
"..\examples\simple_playback_emscripten.c"
>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|Win32'"
>
false
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|Win32'"
>
false
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|ARM'"
>
false
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|ARM'"
>
false
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|x64'"
>
false
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|x64'"
>
false
</ExcludedFromBuild>
</ClCompile>
<ClCompile
Include=
"..\research\tests\ma_lpf_test_0.c"
>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|Win32'"
>
true
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|Win32'"
>
true
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|ARM'"
>
true
</ExcludedFromBuild>
...
...
@@ -334,7 +342,6 @@
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|x64'"
>
true
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|x64'"
>
true
</ExcludedFromBuild>
</ClCompile>
<ClCompile
Include=
"..\research\tests\ma_lpf_test_0.c"
/>
<ClCompile
Include=
"..\research\tests\ma_resampler_test_0.c"
>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Debug|Win32'"
>
true
</ExcludedFromBuild>
<ExcludedFromBuild
Condition=
"'$(Configuration)|$(Platform)'=='Release|Win32'"
>
true
</ExcludedFromBuild>
...
...
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