Commit 98792cae authored by David Reid's avatar David Reid

Resampling testing.

parent b69f3667
/* Resampling research. Public domain. */
#ifndef ma_resampler_h
#define ma_resampler_h
typedef enum
{
ma_resample_algorithm_linear = 0, /* Default. Fastest. */
ma_resample_algorithm_sinc /* Slower. */
} ma_resample_algorithm;
/*
Simple high-level API for resampling 32-bit floating point samples.
Use ma_calculate_frame_count_after_src() to determine the required output buffer size.
*/
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);
#endif /* ma_resampler_h */
/*
Implementation
*/
#ifdef MINIAUDIO_IMPLEMENTATION
#ifndef MA_RESAMPLER_MIN_RATIO
#define MA_RESAMPLER_MIN_RATIO 0.02083333
#endif
#ifndef MA_RESAMPLER_MAX_RATIO
#define MA_RESAMPLER_MAX_RATIO 48.0
#endif
ma_result ma_resample_f32__linear(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;
/* 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;
}
/* To linearly interpolate we need the previous and next input samples. */
{
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. */
}
pSamplesOut[iTimeOut] = ma_mix_f32_fast(pSamplesIn[iTimeInPrev], pSamplesIn[iTimeInNext], (float)(timeIn - iTimeIn));
/* Try some kind of low-pass filter. */
#if 1
{
double cutoff = ma_min(sampleRateIn, sampleRateOut) * 0.5;
double RC = 1.0/(cutoff*MA_TAU_D);
double dt = 1.0/sampleRateOut;
float alpha = (float)(dt/(RC+dt));
pSamplesOut[iTimeOut] = pSamplesOut[iTimeOut-1] + (alpha*(pSamplesOut[iTimeOut] - pSamplesOut[iTimeOut-1]));
}
#endif
}
timeIn += ratio;
timeOut += 1;
}
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;
}
/* To linearly interpolate we need the previous and next input samples. */
{
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) {
return MA_INVALID_ARGS;
}
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;
}
}
#endif /* MINIAUDIO_IMPLEMENTATION */
#if 0
/* /*
Consider this code public domain. Consider this code public domain.
...@@ -1204,3 +1401,4 @@ ma_uint64 ma_resampler_seek__sinc(ma_resampler* pResampler, ma_uint64 frameCount ...@@ -1204,3 +1401,4 @@ ma_uint64 ma_resampler_seek__sinc(ma_resampler* pResampler, ma_uint64 frameCount
} }
#endif #endif
#endif /* 0 (Old Implementation)*/
\ No newline at end of file
#define DR_FLAC_IMPLEMENTATION
#include "../../extras/dr_flac.h" /* Enables FLAC decoding. */
#define DR_MP3_IMPLEMENTATION
#include "../../extras/dr_mp3.h" /* Enables MP3 decoding. */
#define DR_WAV_IMPLEMENTATION #define DR_WAV_IMPLEMENTATION
#include "../../../../dr_libs/dr_wav.h" #include "../../extras/dr_wav.h" /* Enables WAV decoding. */
#define MA_DEBUG_OUTPUT #define MA_DEBUG_OUTPUT
#define MINIAUDIO_IMPLEMENTATION #define MINIAUDIO_IMPLEMENTATION
#include "../../miniaudio.h" #include "../../miniaudio.h"
#include "../ma_resampler.h" #include "../ma_resampler.h"
#define USE_NEW_RESAMPLER 1
ma_uint64 g_outputFrameCount;
void* g_pRunningFrameData;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_uint32 framesToCopy;
framesToCopy = frameCount;
if (framesToCopy > (ma_uint32)g_outputFrameCount) {
framesToCopy = (ma_uint32)g_outputFrameCount;
}
MA_COPY_MEMORY(pOutput, g_pRunningFrameData, framesToCopy * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels));
g_pRunningFrameData = ma_offset_ptr(g_pRunningFrameData, framesToCopy * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels));
g_outputFrameCount -= framesToCopy;
(void)pInput;
}
int main(int argc, char** argv)
{
ma_result result;
ma_decoder_config decoderConfig;
ma_uint64 inputFrameCount;
void* pInputFrameData;
ma_uint64 outputFrameCount = 0;
void* pOutputFrameData = NULL;
ma_device_config deviceConfig;
ma_device device;
ma_backend backend;
/* This example just resamples the input file to an exclusive device's native sample rate. */
if (argc < 2) {
printf("No input file.\n");
return -1;
}
decoderConfig = ma_decoder_config_init(ma_format_f32, 1, 0);
result = ma_decode_file(argv[1], &decoderConfig, &inputFrameCount, &pInputFrameData);
if (result != MA_SUCCESS) {
return (int)result;
}
backend = ma_backend_wasapi;
deviceConfig = ma_device_config_init(ma_device_type_playback);
#if USE_NEW_RESAMPLER
deviceConfig.playback.shareMode = ma_share_mode_exclusive; /* <-- We need to use exclusive mode to ensure there's no resampling going on by the OS. */
deviceConfig.sampleRate = 0; /* <-- Always use the device's native sample rate. */
#else
deviceConfig.playback.shareMode = ma_share_mode_shared; /* <-- We need to use exclusive mode to ensure there's no resampling going on by the OS. */
deviceConfig.sampleRate = decoderConfig.sampleRate;
#endif
deviceConfig.playback.format = decoderConfig.format;
deviceConfig.playback.channels = decoderConfig.channels;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = NULL;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
printf("Failed to open playback device.\n");
ma_free(pInputFrameData);
return -3;
}
#if USE_NEW_RESAMPLER
/* Resample. */
outputFrameCount = ma_calculate_frame_count_after_src(device.sampleRate, decoderConfig.sampleRate, inputFrameCount);
pOutputFrameData = ma_malloc((size_t)(outputFrameCount * ma_get_bytes_per_frame(device.playback.format, device.playback.channels)));
if (pOutputFrameData == NULL) {
printf("Out of memory.\n");
ma_free(pInputFrameData);
ma_device_uninit(&device);
}
ma_resample_f32(ma_resample_algorithm_sinc, device.playback.internalSampleRate, decoderConfig.sampleRate, outputFrameCount, pOutputFrameData, inputFrameCount, pInputFrameData);
g_pRunningFrameData = pOutputFrameData;
g_outputFrameCount = outputFrameCount;
#else
g_pRunningFrameData = pInputFrameData;
g_outputFrameCount = inputFrameCount;
#endif
if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&device);
ma_free(pInputFrameData);
ma_free(pOutputFrameData);
return -4;
}
printf("Press Enter to quit...");
getchar();
ma_device_uninit(&device);
ma_free(pInputFrameData);
ma_free(pOutputFrameData);
return 0;
}
#if 0
#define SAMPLE_RATE_IN 44100 #define SAMPLE_RATE_IN 44100
#define SAMPLE_RATE_OUT 44100 #define SAMPLE_RATE_OUT 44100
#define CHANNELS 1 #define CHANNELS 1
...@@ -71,4 +182,5 @@ int main(int argc, char** argv) ...@@ -71,4 +182,5 @@ int main(int argc, char** argv)
(void)argc; (void)argc;
(void)argv; (void)argv;
return 0; return 0;
} }
\ No newline at end of file #endif
...@@ -319,14 +319,6 @@ ...@@ -319,14 +319,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_playback.c"> <ClCompile Include="..\examples\simple_playback.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="..\examples\simple_playback_emscripten.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
...@@ -334,7 +326,7 @@ ...@@ -334,7 +326,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\research\tests\ma_resampler_test_0.c"> <ClCompile Include="..\examples\simple_playback_emscripten.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
...@@ -342,6 +334,14 @@ ...@@ -342,6 +334,14 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\research\tests\ma_resampler_test_0.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="ma_dithering.c"> <ClCompile Include="ma_dithering.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment