build: Merge submodules into repo
This commit is contained in:
parent
540a30f719
commit
4c64279f90
422 changed files with 106715 additions and 8 deletions
377
src/minarch/libretro-common/audio/audio_mix.c
Normal file
377
src/minarch/libretro-common/audio/audio_mix.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_mix.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <memalign.h>
|
||||
|
||||
#include <retro_environment.h>
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__ALTIVEC__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <audio/audio_mix.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <audio/conversion/float_to_s16.h>
|
||||
#include <audio/conversion/s16_to_float.h>
|
||||
|
||||
void audio_mix_volume_C(float *out, const float *in, float vol, size_t samples)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < samples; i++)
|
||||
out[i] += in[i] * vol;
|
||||
}
|
||||
|
||||
#ifdef __SSE2__
|
||||
void audio_mix_volume_SSE2(float *out, const float *in, float vol, size_t samples)
|
||||
{
|
||||
size_t i, remaining_samples;
|
||||
__m128 volume = _mm_set1_ps(vol);
|
||||
|
||||
for (i = 0; i + 16 <= samples; i += 16, out += 16, in += 16)
|
||||
{
|
||||
unsigned j;
|
||||
__m128 input[4];
|
||||
__m128 additive[4];
|
||||
|
||||
input[0] = _mm_loadu_ps(out + 0);
|
||||
input[1] = _mm_loadu_ps(out + 4);
|
||||
input[2] = _mm_loadu_ps(out + 8);
|
||||
input[3] = _mm_loadu_ps(out + 12);
|
||||
|
||||
additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in + 0));
|
||||
additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in + 4));
|
||||
additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in + 8));
|
||||
additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12));
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
_mm_storeu_ps(out + 4 * j, _mm_add_ps(input[j], additive[j]));
|
||||
}
|
||||
|
||||
remaining_samples = samples - i;
|
||||
|
||||
for (i = 0; i < remaining_samples; i++)
|
||||
out[i] += in[i] * vol;
|
||||
}
|
||||
#endif
|
||||
|
||||
void audio_mix_free_chunk(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav && chunk->rwav->samples)
|
||||
{
|
||||
/* rwav_free only frees the samples */
|
||||
rwav_free(chunk->rwav);
|
||||
free(chunk->rwav);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (chunk->buf)
|
||||
free(chunk->buf);
|
||||
|
||||
if (chunk->upsample_buf)
|
||||
memalign_free(chunk->upsample_buf);
|
||||
|
||||
if (chunk->float_buf)
|
||||
memalign_free(chunk->float_buf);
|
||||
|
||||
if (chunk->float_resample_buf)
|
||||
memalign_free(chunk->float_resample_buf);
|
||||
|
||||
if (chunk->resample_buf)
|
||||
memalign_free(chunk->resample_buf);
|
||||
|
||||
if (chunk->resampler && chunk->resampler_data)
|
||||
chunk->resampler->free(chunk->resampler_data);
|
||||
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate,
|
||||
const char *resampler_ident, enum resampler_quality quality)
|
||||
{
|
||||
#ifdef HAVE_RWAV
|
||||
int sample_size;
|
||||
int64_t len = 0;
|
||||
void *buf = NULL;
|
||||
audio_chunk_t *chunk = (audio_chunk_t*)malloc(sizeof(*chunk));
|
||||
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
chunk->buf = NULL;
|
||||
chunk->upsample_buf = NULL;
|
||||
chunk->float_buf = NULL;
|
||||
chunk->float_resample_buf = NULL;
|
||||
chunk->resample_buf = NULL;
|
||||
chunk->len = 0;
|
||||
chunk->resample_len = 0;
|
||||
chunk->sample_rate = sample_rate;
|
||||
chunk->resample = false;
|
||||
chunk->resampler = NULL;
|
||||
chunk->resampler_data = NULL;
|
||||
chunk->ratio = 0.00f;
|
||||
chunk->rwav = (rwav_t*)malloc(sizeof(rwav_t));
|
||||
|
||||
if (!chunk->rwav)
|
||||
goto error;
|
||||
|
||||
chunk->rwav->bitspersample = 0;
|
||||
chunk->rwav->numchannels = 0;
|
||||
chunk->rwav->samplerate = 0;
|
||||
chunk->rwav->numsamples = 0;
|
||||
chunk->rwav->subchunk2size = 0;
|
||||
chunk->rwav->samples = NULL;
|
||||
|
||||
if (!filestream_read_file(path, &buf, &len))
|
||||
goto error;
|
||||
|
||||
chunk->buf = buf;
|
||||
chunk->len = len;
|
||||
|
||||
if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
|
||||
goto error;
|
||||
|
||||
/* numsamples does not know or care about
|
||||
* multiple channels, but we need space for 2 */
|
||||
chunk->upsample_buf = (int16_t*)memalign_alloc(128,
|
||||
chunk->rwav->numsamples * 2 * sizeof(int16_t));
|
||||
|
||||
sample_size = chunk->rwav->bitspersample / 8;
|
||||
|
||||
if (sample_size == 1)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (chunk->rwav->numchannels == 1)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) + i;
|
||||
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[(i * 2) + 1] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
}
|
||||
}
|
||||
else if (chunk->rwav->numchannels == 2)
|
||||
{
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
uint8_t *sample = (
|
||||
(uint8_t*)chunk->rwav->samples) +
|
||||
(i * 2);
|
||||
|
||||
chunk->upsample_buf[i * 2] =
|
||||
(int16_t)((sample[0] - 128) << 8);
|
||||
chunk->upsample_buf[(i * 2) + 1] =
|
||||
(int16_t)((sample[1] - 128) << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sample_size == 2)
|
||||
{
|
||||
if (chunk->rwav->numchannels == 1)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < chunk->rwav->numsamples; i++)
|
||||
{
|
||||
int16_t sample = ((int16_t*)
|
||||
chunk->rwav->samples)[i];
|
||||
|
||||
chunk->upsample_buf[i * 2] = sample;
|
||||
chunk->upsample_buf[(i * 2) + 1] = sample;
|
||||
}
|
||||
}
|
||||
else if (chunk->rwav->numchannels == 2)
|
||||
memcpy(chunk->upsample_buf, chunk->rwav->samples,
|
||||
chunk->rwav->subchunk2size);
|
||||
}
|
||||
else if (sample_size != 2)
|
||||
{
|
||||
/* we don't support any other sample size besides 8 and 16-bit yet */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sample_rate != (int)chunk->rwav->samplerate)
|
||||
{
|
||||
chunk->resample = true;
|
||||
chunk->ratio = (double)sample_rate / chunk->rwav->samplerate;
|
||||
|
||||
retro_resampler_realloc(&chunk->resampler_data,
|
||||
&chunk->resampler,
|
||||
resampler_ident,
|
||||
quality,
|
||||
chunk->ratio);
|
||||
|
||||
if (chunk->resampler && chunk->resampler_data)
|
||||
{
|
||||
struct resampler_data info;
|
||||
|
||||
chunk->float_buf = (float*)memalign_alloc(128,
|
||||
chunk->rwav->numsamples * 2 *
|
||||
chunk->ratio * sizeof(float));
|
||||
|
||||
/* why is *3 needed instead of just *2? Does the
|
||||
* sinc driver require more space than we know about? */
|
||||
chunk->float_resample_buf = (float*)memalign_alloc(128,
|
||||
chunk->rwav->numsamples * 3 *
|
||||
chunk->ratio * sizeof(float));
|
||||
|
||||
convert_s16_to_float(chunk->float_buf,
|
||||
chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0);
|
||||
|
||||
info.data_in = (const float*)chunk->float_buf;
|
||||
info.data_out = chunk->float_resample_buf;
|
||||
/* a 'frame' consists of two channels, so we set this
|
||||
* to the number of samples irrespective of channel count */
|
||||
info.input_frames = chunk->rwav->numsamples;
|
||||
info.output_frames = 0;
|
||||
info.ratio = chunk->ratio;
|
||||
|
||||
chunk->resampler->process(chunk->resampler_data, &info);
|
||||
|
||||
/* number of output_frames does not increase with
|
||||
* multiple channels, but assume we need space for 2 */
|
||||
chunk->resample_buf = (int16_t*)memalign_alloc(128,
|
||||
info.output_frames * 2 * sizeof(int16_t));
|
||||
chunk->resample_len = info.output_frames;
|
||||
convert_float_to_s16(chunk->resample_buf,
|
||||
chunk->float_resample_buf, info.output_frames * 2);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
|
||||
error:
|
||||
audio_mix_free_chunk(chunk);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
if (chunk->resample)
|
||||
return chunk->resample_len;
|
||||
return chunk->rwav->numsamples;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no other filetypes supported yet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_mix_get_chunk_sample:
|
||||
* @chunk : audio chunk instance
|
||||
* @channel : channel of the sample (0=left, 1=right)
|
||||
* @index : index of the sample
|
||||
*
|
||||
* Get a sample from an audio chunk.
|
||||
*
|
||||
* Returns: A signed 16-bit audio sample.
|
||||
**/
|
||||
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk,
|
||||
unsigned channel, size_t index)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
int sample_size = chunk->rwav->bitspersample / 8;
|
||||
int16_t sample_out = 0;
|
||||
|
||||
/* 0 is the first/left channel */
|
||||
uint8_t *sample = NULL;
|
||||
|
||||
if (chunk->resample)
|
||||
sample = (uint8_t*)chunk->resample_buf +
|
||||
(sample_size * index * chunk->rwav->numchannels)
|
||||
+ (channel * sample_size);
|
||||
else
|
||||
sample = (uint8_t*)chunk->upsample_buf +
|
||||
(sample_size * index * chunk->rwav->numchannels)
|
||||
+ (channel * sample_size);
|
||||
|
||||
sample_out = (int16_t)*sample;
|
||||
|
||||
return sample_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no other filetypes supported yet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
{
|
||||
int16_t *sample;
|
||||
|
||||
if (chunk->resample)
|
||||
sample = chunk->resample_buf;
|
||||
else
|
||||
sample = chunk->upsample_buf;
|
||||
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
|
||||
{
|
||||
if (!chunk)
|
||||
return 0;
|
||||
|
||||
#ifdef HAVE_RWAV
|
||||
if (chunk->rwav)
|
||||
return chunk->rwav->numchannels;
|
||||
#endif
|
||||
|
||||
/* don't support other formats yet */
|
||||
return 0;
|
||||
}
|
||||
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
1400
src/minarch/libretro-common/audio/audio_mixer.c
Normal file
File diff suppressed because it is too large
Load diff
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal file
178
src/minarch/libretro-common/audio/conversion/float_to_s16.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/* Copyright (C) 2010-2021 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__ALTIVEC__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
#include <features/features_cpu.h>
|
||||
#include <audio/conversion/float_to_s16.h>
|
||||
|
||||
#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
|
||||
static bool float_to_s16_neon_enabled = false;
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
void convert_float_s16_asm(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
void convert_float_to_s16(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
size_t i = 0;
|
||||
if (float_to_s16_neon_enabled)
|
||||
{
|
||||
float gf = (1<<15);
|
||||
float32x4_t vgf = {gf, gf, gf, gf};
|
||||
while (samples >= 8)
|
||||
{
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
size_t aligned_samples = samples & ~7;
|
||||
if (aligned_samples)
|
||||
convert_float_s16_asm(out, in, aligned_samples);
|
||||
|
||||
out += aligned_samples;
|
||||
in += aligned_samples;
|
||||
samples -= aligned_samples;
|
||||
i = 0;
|
||||
#else
|
||||
int16x4x2_t oreg;
|
||||
int32x4x2_t creg;
|
||||
float32x4x2_t inreg = vld2q_f32(in);
|
||||
creg.val[0] = vcvtq_s32_f32(vmulq_f32(inreg.val[0], vgf));
|
||||
creg.val[1] = vcvtq_s32_f32(vmulq_f32(inreg.val[1], vgf));
|
||||
oreg.val[0] = vqmovn_s32(creg.val[0]);
|
||||
oreg.val[1] = vqmovn_s32(creg.val[1]);
|
||||
vst2_s16(out, oreg);
|
||||
in += 8;
|
||||
out += 8;
|
||||
samples -= 8;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < samples; i++)
|
||||
{
|
||||
int32_t val = (int32_t)(in[i] * 0x8000);
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF :
|
||||
(val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
void convert_float_to_s16_init_simd(void)
|
||||
{
|
||||
uint64_t cpu = cpu_features_get();
|
||||
|
||||
if (cpu & RETRO_SIMD_NEON)
|
||||
float_to_s16_neon_enabled = true;
|
||||
}
|
||||
#else
|
||||
void convert_float_to_s16(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
size_t i = 0;
|
||||
#if defined(__SSE2__)
|
||||
__m128 factor = _mm_set1_ps((float)0x8000);
|
||||
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128 input_l = _mm_loadu_ps(in + 0);
|
||||
__m128 input_r = _mm_loadu_ps(in + 4);
|
||||
__m128 res_l = _mm_mul_ps(input_l, factor);
|
||||
__m128 res_r = _mm_mul_ps(input_r, factor);
|
||||
__m128i ints_l = _mm_cvtps_epi32(res_l);
|
||||
__m128i ints_r = _mm_cvtps_epi32(res_r);
|
||||
__m128i packed = _mm_packs_epi32(ints_l, ints_r);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, packed);
|
||||
}
|
||||
|
||||
samples = samples - i;
|
||||
i = 0;
|
||||
#elif defined(__ALTIVEC__)
|
||||
int samples_in = samples;
|
||||
|
||||
/* Unaligned loads/store is a bit expensive,
|
||||
* so we optimize for the good path (very likely). */
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector float input0 = vec_ld( 0, in);
|
||||
vector float input1 = vec_ld(16, in);
|
||||
vector signed int result0 = vec_cts(input0, 15);
|
||||
vector signed int result1 = vec_cts(input1, 15);
|
||||
vec_st(vec_packs(result0, result1), 0, out);
|
||||
}
|
||||
|
||||
samples_in -= i;
|
||||
}
|
||||
|
||||
samples = samples_in;
|
||||
i = 0;
|
||||
#elif defined(_MIPS_ARCH_ALLEGREX)
|
||||
#ifdef DEBUG
|
||||
/* Make sure the buffers are 16 byte aligned, this should be
|
||||
* the default behaviour of malloc in the PSPSDK.
|
||||
* Assume alignment. */
|
||||
retro_assert(((uintptr_t)in & 0xf) == 0);
|
||||
retro_assert(((uintptr_t)out & 0xf) == 0);
|
||||
#endif
|
||||
|
||||
for (i = 0; i + 8 <= samples; i += 8)
|
||||
{
|
||||
__asm__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
||||
"lv.q c100, 0(%0) \n"
|
||||
"lv.q c110, 16(%0) \n"
|
||||
|
||||
"vf2in.q c100, c100, 31 \n"
|
||||
"vf2in.q c110, c110, 31 \n"
|
||||
"vi2s.q c100, c100 \n"
|
||||
"vi2s.q c102, c110 \n"
|
||||
|
||||
"sv.q c100, 0(%1) \n"
|
||||
|
||||
".set pop \n"
|
||||
:: "r"(in + i), "r"(out + i));
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; i < samples; i++)
|
||||
{
|
||||
int32_t val = (int32_t)(in[i] * 0x8000);
|
||||
out[i] = (val > 0x7FFF)
|
||||
? 0x7FFF
|
||||
: (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
void convert_float_to_s16_init_simd(void) { }
|
||||
#endif
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
.globl convert_float_s16_asm
|
||||
#ifndef __MACH__
|
||||
.type convert_float_s16_asm, %function
|
||||
#endif
|
||||
.globl _convert_float_s16_asm
|
||||
#ifndef __MACH__
|
||||
.type _convert_float_s16_asm, %function
|
||||
#endif
|
||||
# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)
|
||||
convert_float_s16_asm:
|
||||
_convert_float_s16_asm:
|
||||
# Hacky way to get a constant of 2^15.
|
||||
# ((2^4)^2)^2 * 0.5 = 2^15
|
||||
vmov.f32 q8, #16.0
|
||||
vmov.f32 q9, #0.5
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q9
|
||||
|
||||
1:
|
||||
# Preload here?
|
||||
vld1.f32 {q0-q1}, [r1]!
|
||||
|
||||
vmul.f32 q0, q0, q8
|
||||
vmul.f32 q1, q1, q8
|
||||
|
||||
vcvt.s32.f32 q0, q0
|
||||
vcvt.s32.f32 q1, q1
|
||||
|
||||
vqmovn.s32 d4, q0
|
||||
vqmovn.s32 d5, q1
|
||||
|
||||
vst1.f32 {d4-d5}, [r0]!
|
||||
|
||||
# Guaranteed to get samples in multiples of 8.
|
||||
subs r2, r2, #8
|
||||
bne 1b
|
||||
|
||||
bx lr
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (float_to_s16_neon.S).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__thumb__)
|
||||
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||
#else
|
||||
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
|
||||
#endif
|
||||
|
||||
asm(
|
||||
DECL_ARMMODE("convert_float_s16_asm")
|
||||
DECL_ARMMODE("_convert_float_s16_asm")
|
||||
"# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)\n"
|
||||
" # Hacky way to get a constant of 2^15.\n"
|
||||
" # ((2^4)^2)^2 * 0.5 = 2^15\n"
|
||||
" vmov.f32 q8, #16.0\n"
|
||||
" vmov.f32 q9, #0.5\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q9\n"
|
||||
"\n"
|
||||
"1:\n"
|
||||
" # Preload here?\n"
|
||||
" vld1.f32 {q0-q1}, [r1]!\n"
|
||||
"\n"
|
||||
" vmul.f32 q0, q0, q8\n"
|
||||
" vmul.f32 q1, q1, q8\n"
|
||||
"\n"
|
||||
" vcvt.s32.f32 q0, q0\n"
|
||||
" vcvt.s32.f32 q1, q1\n"
|
||||
"\n"
|
||||
" vqmovn.s32 d4, q0\n"
|
||||
" vqmovn.s32 d5, q1\n"
|
||||
"\n"
|
||||
" vst1.f32 {d4-d5}, [r0]!\n"
|
||||
"\n"
|
||||
" # Guaranteed to get samples in multiples of 8.\n"
|
||||
" subs r2, r2, #8\n"
|
||||
" bne 1b\n"
|
||||
"\n"
|
||||
" bx lr\n"
|
||||
"\n"
|
||||
);
|
||||
#endif
|
||||
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal file
205
src/minarch/libretro-common/audio/conversion/s16_to_float.c
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/* Copyright (C) 2010-2021 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__ALTIVEC__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
#include <boolean.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <audio/conversion/s16_to_float.h>
|
||||
|
||||
#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
|
||||
static bool s16_to_float_neon_enabled = false;
|
||||
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
/* Avoid potential hard-float/soft-float ABI issues. */
|
||||
void convert_s16_float_asm(float *out, const int16_t *in,
|
||||
size_t samples, const float *gain);
|
||||
#else
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
void convert_s16_to_float(float *out,
|
||||
const int16_t *in, size_t samples, float gain)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
if (s16_to_float_neon_enabled)
|
||||
{
|
||||
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
|
||||
size_t aligned_samples = samples & ~7;
|
||||
if (aligned_samples)
|
||||
convert_s16_float_asm(out, in, aligned_samples, &gain);
|
||||
|
||||
/* Could do all conversion in ASM, but keep it simple for now. */
|
||||
out += aligned_samples;
|
||||
in += aligned_samples;
|
||||
samples -= aligned_samples;
|
||||
i = 0;
|
||||
#else
|
||||
float gf = gain / (1 << 15);
|
||||
float32x4_t vgf = {gf, gf, gf, gf};
|
||||
while (samples >= 8)
|
||||
{
|
||||
float32x4x2_t oreg;
|
||||
int16x4x2_t inreg = vld2_s16(in);
|
||||
int32x4_t p1 = vmovl_s16(inreg.val[0]);
|
||||
int32x4_t p2 = vmovl_s16(inreg.val[1]);
|
||||
oreg.val[0] = vmulq_f32(vcvtq_f32_s32(p1), vgf);
|
||||
oreg.val[1] = vmulq_f32(vcvtq_f32_s32(p2), vgf);
|
||||
vst2q_f32(out, oreg);
|
||||
in += 8;
|
||||
out += 8;
|
||||
samples -= 8;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gain /= 0x8000;
|
||||
|
||||
for (; i < samples; i++)
|
||||
out[i] = (float)in[i] * gain;
|
||||
}
|
||||
|
||||
void convert_s16_to_float_init_simd(void)
|
||||
{
|
||||
uint64_t cpu = cpu_features_get();
|
||||
|
||||
if (cpu & RETRO_SIMD_NEON)
|
||||
s16_to_float_neon_enabled = true;
|
||||
}
|
||||
#else
|
||||
void convert_s16_to_float(float *out,
|
||||
const int16_t *in, size_t samples, float gain)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
#if defined(__SSE2__)
|
||||
float fgain = gain / UINT32_C(0x80000000);
|
||||
__m128 factor = _mm_set1_ps(fgain);
|
||||
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||
__m128i regs_l = _mm_unpacklo_epi16(_mm_setzero_si128(), input);
|
||||
__m128i regs_r = _mm_unpackhi_epi16(_mm_setzero_si128(), input);
|
||||
__m128 output_l = _mm_mul_ps(_mm_cvtepi32_ps(regs_l), factor);
|
||||
__m128 output_r = _mm_mul_ps(_mm_cvtepi32_ps(regs_r), factor);
|
||||
|
||||
_mm_storeu_ps(out + 0, output_l);
|
||||
_mm_storeu_ps(out + 4, output_r);
|
||||
}
|
||||
|
||||
samples = samples - i;
|
||||
i = 0;
|
||||
#elif defined(__ALTIVEC__)
|
||||
size_t samples_in = samples;
|
||||
|
||||
/* Unaligned loads/store is a bit expensive, so we
|
||||
* optimize for the good path (very likely). */
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
const vector float gain_vec = { gain, gain , gain, gain };
|
||||
const vector float zero_vec = { 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector signed short input = vec_ld(0, in);
|
||||
vector signed int hi = vec_unpackh(input);
|
||||
vector signed int lo = vec_unpackl(input);
|
||||
vector float out_hi = vec_madd(vec_ctf(hi, 15), gain_vec, zero_vec);
|
||||
vector float out_lo = vec_madd(vec_ctf(lo, 15), gain_vec, zero_vec);
|
||||
|
||||
vec_st(out_hi, 0, out);
|
||||
vec_st(out_lo, 16, out);
|
||||
}
|
||||
|
||||
samples_in -= i;
|
||||
}
|
||||
|
||||
samples = samples_in;
|
||||
i = 0;
|
||||
#endif
|
||||
|
||||
gain /= 0x8000;
|
||||
|
||||
#if defined(_MIPS_ARCH_ALLEGREX)
|
||||
#ifdef DEBUG
|
||||
/* Make sure the buffer is 16 byte aligned, this should be the
|
||||
* default behaviour of malloc in the PSPSDK.
|
||||
* Only the output buffer can be assumed to be 16-byte aligned. */
|
||||
retro_assert(((uintptr_t)out & 0xf) == 0);
|
||||
#endif
|
||||
|
||||
__asm__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
"mtv %0, s200 \n"
|
||||
".set pop \n"
|
||||
::"r"(gain));
|
||||
|
||||
for (i = 0; i + 16 <= samples; i += 16)
|
||||
{
|
||||
__asm__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
||||
"lv.s s100, 0(%0) \n"
|
||||
"lv.s s101, 4(%0) \n"
|
||||
"lv.s s110, 8(%0) \n"
|
||||
"lv.s s111, 12(%0) \n"
|
||||
"lv.s s120, 16(%0) \n"
|
||||
"lv.s s121, 20(%0) \n"
|
||||
"lv.s s130, 24(%0) \n"
|
||||
"lv.s s131, 28(%0) \n"
|
||||
|
||||
"vs2i.p c100, c100 \n"
|
||||
"vs2i.p c110, c110 \n"
|
||||
"vs2i.p c120, c120 \n"
|
||||
"vs2i.p c130, c130 \n"
|
||||
|
||||
"vi2f.q c100, c100, 16 \n"
|
||||
"vi2f.q c110, c110, 16 \n"
|
||||
"vi2f.q c120, c120, 16 \n"
|
||||
"vi2f.q c130, c130, 16 \n"
|
||||
|
||||
"vmscl.q e100, e100, s200 \n"
|
||||
|
||||
"sv.q c100, 0(%1) \n"
|
||||
"sv.q c110, 16(%1) \n"
|
||||
"sv.q c120, 32(%1) \n"
|
||||
"sv.q c130, 48(%1) \n"
|
||||
|
||||
".set pop \n"
|
||||
:: "r"(in + i), "r"(out + i));
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; i < samples; i++)
|
||||
out[i] = (float)in[i] * gain;
|
||||
}
|
||||
|
||||
void convert_s16_to_float_init_simd(void) { }
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
.globl convert_s16_float_asm
|
||||
#ifndef __MACH__
|
||||
.type convert_s16_float_asm, %function
|
||||
#endif
|
||||
.globl _convert_s16_float_asm
|
||||
#ifndef __MACH__
|
||||
.type _convert_s16_float_asm, %function
|
||||
#endif
|
||||
# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)
|
||||
convert_s16_float_asm:
|
||||
_convert_s16_float_asm:
|
||||
# Hacky way to get a constant of 2^-15.
|
||||
# Might be faster to just load a constant from memory.
|
||||
# It's just done once however ...
|
||||
vmov.f32 q8, #0.25
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vmul.f32 q8, q8, q8
|
||||
vadd.f32 q8, q8, q8
|
||||
|
||||
# Apply gain
|
||||
vld1.f32 {d6[0]}, [r3]
|
||||
vmul.f32 q8, q8, d6[0]
|
||||
|
||||
1:
|
||||
# Preload here?
|
||||
vld1.s16 {q0}, [r1]!
|
||||
|
||||
# Widen to 32-bit
|
||||
vmovl.s16 q1, d0
|
||||
vmovl.s16 q2, d1
|
||||
|
||||
# Convert to float
|
||||
vcvt.f32.s32 q1, q1
|
||||
vcvt.f32.s32 q2, q2
|
||||
|
||||
vmul.f32 q1, q1, q8
|
||||
vmul.f32 q2, q2, q8
|
||||
|
||||
vst1.f32 {q1-q2}, [r0]!
|
||||
|
||||
# Guaranteed to get samples in multiples of 8.
|
||||
subs r2, r2, #8
|
||||
bne 1b
|
||||
|
||||
bx lr
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (s16_to_float_neon.S).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__thumb__)
|
||||
#define DECL_ARMMODE(x) " .align 2\n" " .global " x "\n" " .thumb\n" " .thumb_func\n" " .type " x ", %function\n" x ":\n"
|
||||
#else
|
||||
#define DECL_ARMMODE(x) " .align 4\n" " .global " x "\n" " .arm\n" x ":\n"
|
||||
#endif
|
||||
|
||||
asm(
|
||||
DECL_ARMMODE("convert_s16_float_asm")
|
||||
DECL_ARMMODE("_convert_s16_float_asm")
|
||||
"# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)\n"
|
||||
" # Hacky way to get a constant of 2^-15.\n"
|
||||
" # Might be faster to just load a constant from memory.\n"
|
||||
" # It's just done once however ...\n"
|
||||
" vmov.f32 q8, #0.25\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vmul.f32 q8, q8, q8\n"
|
||||
" vadd.f32 q8, q8, q8\n"
|
||||
"\n"
|
||||
" # Apply gain\n"
|
||||
" vld1.f32 {d6[0]}, [r3]\n"
|
||||
" vmul.f32 q8, q8, d6[0]\n"
|
||||
"\n"
|
||||
"1:\n"
|
||||
" # Preload here?\n"
|
||||
" vld1.s16 {q0}, [r1]!\n"
|
||||
"\n"
|
||||
" # Widen to 32-bit\n"
|
||||
" vmovl.s16 q1, d0\n"
|
||||
" vmovl.s16 q2, d1\n"
|
||||
"\n"
|
||||
" # Convert to float\n"
|
||||
" vcvt.f32.s32 q1, q1\n"
|
||||
" vcvt.f32.s32 q2, q2\n"
|
||||
"\n"
|
||||
" vmul.f32 q1, q1, q8\n"
|
||||
" vmul.f32 q2, q2, q8\n"
|
||||
"\n"
|
||||
" vst1.f32 {q1-q2}, [r0]!\n"
|
||||
"\n"
|
||||
" # Guaranteed to get samples in multiples of 8.\n"
|
||||
" subs r2, r2, #8\n"
|
||||
" bne 1b\n"
|
||||
"\n"
|
||||
" bx lr\n"
|
||||
"\n"
|
||||
);
|
||||
#endif
|
||||
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal file
324
src/minarch/libretro-common/audio/dsp_filter.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (dsp_filter.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include <compat/posix_string.h>
|
||||
#include <dynamic/dylib.h>
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <file/config_file_userdata.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
#include <audio/dsp_filter.h>
|
||||
|
||||
struct retro_dsp_plug
|
||||
{
|
||||
#ifdef HAVE_DYLIB
|
||||
dylib_t lib;
|
||||
#endif
|
||||
const struct dspfilter_implementation *impl;
|
||||
};
|
||||
|
||||
struct retro_dsp_instance
|
||||
{
|
||||
const struct dspfilter_implementation *impl;
|
||||
void *impl_data;
|
||||
};
|
||||
|
||||
struct retro_dsp_filter
|
||||
{
|
||||
config_file_t *conf;
|
||||
|
||||
struct retro_dsp_plug *plugs;
|
||||
unsigned num_plugs;
|
||||
|
||||
struct retro_dsp_instance *instances;
|
||||
unsigned num_instances;
|
||||
};
|
||||
|
||||
static const struct dspfilter_implementation *find_implementation(
|
||||
retro_dsp_filter_t *dsp, const char *ident)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < dsp->num_plugs; i++)
|
||||
{
|
||||
if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
|
||||
return dsp->plugs[i].impl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct dspfilter_config dspfilter_config = {
|
||||
config_userdata_get_float,
|
||||
config_userdata_get_int,
|
||||
config_userdata_get_float_array,
|
||||
config_userdata_get_int_array,
|
||||
config_userdata_get_string,
|
||||
config_userdata_free,
|
||||
};
|
||||
|
||||
static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
|
||||
{
|
||||
unsigned i;
|
||||
struct retro_dsp_instance *instances = NULL;
|
||||
unsigned filters = 0;
|
||||
|
||||
if (!config_get_uint(dsp->conf, "filters", &filters))
|
||||
return false;
|
||||
|
||||
instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
|
||||
if (!instances)
|
||||
return false;
|
||||
|
||||
dsp->instances = instances;
|
||||
dsp->num_instances = filters;
|
||||
|
||||
for (i = 0; i < filters; i++)
|
||||
{
|
||||
struct config_file_userdata userdata;
|
||||
struct dspfilter_info info;
|
||||
char key[64];
|
||||
char name[64];
|
||||
|
||||
key[0] = name[0] = '\0';
|
||||
|
||||
info.input_rate = sample_rate;
|
||||
|
||||
snprintf(key, sizeof(key), "filter%u", i);
|
||||
|
||||
if (!config_get_array(dsp->conf, key, name, sizeof(name)))
|
||||
return false;
|
||||
|
||||
dsp->instances[i].impl = find_implementation(dsp, name);
|
||||
if (!dsp->instances[i].impl)
|
||||
return false;
|
||||
|
||||
userdata.conf = dsp->conf;
|
||||
/* Index-specific configs take priority over ident-specific. */
|
||||
userdata.prefix[0] = key;
|
||||
userdata.prefix[1] = dsp->instances[i].impl->short_ident;
|
||||
|
||||
dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
|
||||
&dspfilter_config, &userdata);
|
||||
if (!dsp->instances[i].impl_data)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(HAVE_FILTERS_BUILTIN)
|
||||
extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
|
||||
|
||||
static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
|
||||
panning_dspfilter_get_implementation,
|
||||
iir_dspfilter_get_implementation,
|
||||
echo_dspfilter_get_implementation,
|
||||
phaser_dspfilter_get_implementation,
|
||||
wahwah_dspfilter_get_implementation,
|
||||
eq_dspfilter_get_implementation,
|
||||
chorus_dspfilter_get_implementation,
|
||||
};
|
||||
|
||||
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
|
||||
{
|
||||
unsigned i;
|
||||
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
|
||||
struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
|
||||
calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
|
||||
|
||||
if (!plugs)
|
||||
return false;
|
||||
|
||||
dsp->plugs = plugs;
|
||||
dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
|
||||
{
|
||||
dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
|
||||
if (!dsp->plugs[i].impl)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#elif defined(HAVE_DYLIB)
|
||||
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
|
||||
{
|
||||
unsigned i;
|
||||
dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
|
||||
unsigned list_size = list ? (unsigned)list->size : 0;
|
||||
|
||||
for (i = 0; i < list_size; i++)
|
||||
{
|
||||
dspfilter_get_implementation_t cb;
|
||||
const struct dspfilter_implementation *impl = NULL;
|
||||
struct retro_dsp_plug *new_plugs = NULL;
|
||||
dylib_t lib =
|
||||
dylib_load(list->elems[i].data);
|
||||
|
||||
if (!lib)
|
||||
continue;
|
||||
|
||||
cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
|
||||
if (!cb)
|
||||
{
|
||||
dylib_close(lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
impl = cb(mask);
|
||||
if (!impl)
|
||||
{
|
||||
dylib_close(lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (impl->api_version != DSPFILTER_API_VERSION)
|
||||
{
|
||||
dylib_close(lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
new_plugs = (struct retro_dsp_plug*)
|
||||
realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
|
||||
if (!new_plugs)
|
||||
{
|
||||
dylib_close(lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Found plug. */
|
||||
|
||||
dsp->plugs = new_plugs;
|
||||
dsp->plugs[dsp->num_plugs].lib = lib;
|
||||
dsp->plugs[dsp->num_plugs].impl = impl;
|
||||
dsp->num_plugs++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
retro_dsp_filter_t *retro_dsp_filter_new(
|
||||
const char *filter_config,
|
||||
void *string_data,
|
||||
float sample_rate)
|
||||
{
|
||||
config_file_t *conf = NULL;
|
||||
struct string_list *plugs = NULL;
|
||||
retro_dsp_filter_t *dsp = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));
|
||||
|
||||
if (!dsp)
|
||||
return NULL;
|
||||
|
||||
if (!(conf = config_file_new_from_path_to_string(filter_config)))
|
||||
goto error;
|
||||
|
||||
dsp->conf = conf;
|
||||
|
||||
if (string_data)
|
||||
plugs = (struct string_list*)string_data;
|
||||
|
||||
#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
|
||||
if (!append_plugs(dsp, plugs))
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
if (plugs)
|
||||
string_list_free(plugs);
|
||||
plugs = NULL;
|
||||
|
||||
if (!create_filter_graph(dsp, sample_rate))
|
||||
goto error;
|
||||
|
||||
return dsp;
|
||||
|
||||
error:
|
||||
if (plugs)
|
||||
string_list_free(plugs);
|
||||
retro_dsp_filter_free(dsp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
|
||||
{
|
||||
unsigned i;
|
||||
if (!dsp)
|
||||
return;
|
||||
|
||||
for (i = 0; i < dsp->num_instances; i++)
|
||||
{
|
||||
if (dsp->instances[i].impl_data && dsp->instances[i].impl)
|
||||
dsp->instances[i].impl->free(dsp->instances[i].impl_data);
|
||||
}
|
||||
free(dsp->instances);
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
for (i = 0; i < dsp->num_plugs; i++)
|
||||
{
|
||||
if (dsp->plugs[i].lib)
|
||||
dylib_close(dsp->plugs[i].lib);
|
||||
}
|
||||
free(dsp->plugs);
|
||||
#endif
|
||||
|
||||
if (dsp->conf)
|
||||
config_file_free(dsp->conf);
|
||||
|
||||
free(dsp);
|
||||
}
|
||||
|
||||
void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
|
||||
struct retro_dsp_data *data)
|
||||
{
|
||||
unsigned i;
|
||||
struct dspfilter_output output = {0};
|
||||
struct dspfilter_input input = {0};
|
||||
|
||||
output.samples = data->input;
|
||||
output.frames = data->input_frames;
|
||||
|
||||
for (i = 0; i < dsp->num_instances; i++)
|
||||
{
|
||||
input.samples = output.samples;
|
||||
input.frames = output.frames;
|
||||
dsp->instances[i].impl->process(
|
||||
dsp->instances[i].impl_data, &output, &input);
|
||||
}
|
||||
|
||||
data->output = output.samples;
|
||||
data->output_frames = output.frames;
|
||||
}
|
||||
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
11
src/minarch/libretro-common/audio/dsp_filters/BassBoost.dsp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
filters = 2
|
||||
filter0 = iir
|
||||
filter1 = panning
|
||||
|
||||
iir_gain = 10.0
|
||||
iir_type = BBOOST
|
||||
iir_frequency = 200.0
|
||||
|
||||
# Avoids clipping.
|
||||
panning_left_mix = "0.3 0.0"
|
||||
panning_right_mix = "0.0 0.3"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
iir_frequency = 8600.0
|
||||
iir_quality = 0.707
|
||||
iir_gain = 6.0
|
||||
iir_type = LPF
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
filters = 4
|
||||
filter0 = eq
|
||||
filter1 = reverb
|
||||
filter2 = iir
|
||||
filter3 = panning
|
||||
|
||||
eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
|
||||
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"
|
||||
|
||||
# Reverb - slight reverb
|
||||
reverb_drytime = 0.5
|
||||
reverb_wettime = 0.15
|
||||
reverb_damping = 0.8
|
||||
reverb_roomwidth = 0.25
|
||||
reverb_roomsize = 0.25
|
||||
|
||||
# IIR - filters out some harsh sounds on the upper end
|
||||
iir_type = RIAA_CD
|
||||
|
||||
# Panning - cut the volume a bit
|
||||
panning_left_mix = "0.75 0.0"
|
||||
panning_right_mix = "0.0 0.75"
|
||||
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
14
src/minarch/libretro-common/audio/dsp_filters/Chorus.dsp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
filters = 1
|
||||
filter0 = chorus
|
||||
|
||||
# Controls the base delay of the chorus (milliseconds).
|
||||
# chorus_delay_ms = 25.0
|
||||
#
|
||||
# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms.
|
||||
# chorus_depth_ms = 1.0
|
||||
#
|
||||
# Frequency of LFO which controls delay.
|
||||
# chorus_lfo_freq = 0.5
|
||||
#
|
||||
# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus.
|
||||
# chorus_drywet = 0.8
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
filters = 1
|
||||
filter0 = crystalizer
|
||||
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
|
||||
crystalizer_intensity = 5.0
|
||||
41
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal file
41
src/minarch/libretro-common/audio/dsp_filters/EQ.dsp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
filters = 1
|
||||
filter0 = eq
|
||||
|
||||
# Defaults
|
||||
|
||||
# Beta factor for Kaiser window.
|
||||
# Lower values will allow better frequency resolution, but more ripple.
|
||||
# eq_window_beta = 4.0
|
||||
|
||||
# The block size on which FFT is done.
|
||||
# Too high value requires more processing as well as longer latency but
|
||||
# allows finer-grained control over the spectrum.
|
||||
# eq_block_size_log2 = 8
|
||||
|
||||
# An array of which frequencies to control.
|
||||
# You can create an arbitrary amount of these sampling points.
|
||||
# The EQ will try to create a frequency response which fits well to these points.
|
||||
# The filter response is linearly interpolated between sampling points here.
|
||||
#
|
||||
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||
#
|
||||
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||
# eq_frequencies = "500 1000 2000"
|
||||
# eq_gains = "0 3 0"
|
||||
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||
|
||||
# By default, this filter has a flat frequency response.
|
||||
|
||||
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||
# with one coefficient per line.
|
||||
# eq_impulse_response_output = "eq_impulse.txt"
|
||||
#
|
||||
# Using GNU Octave or Matlab, you can plot the response with:
|
||||
#
|
||||
# f = fopen('/path/to/eq_impulse.txt');
|
||||
# l = textscan(f, '%f');
|
||||
# res = l{1};
|
||||
# freqz(res, 1, 4096, 48000);
|
||||
#
|
||||
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
||||
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal file
19
src/minarch/libretro-common/audio/dsp_filters/Echo.dsp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
filters = 1
|
||||
filter0 = echo
|
||||
|
||||
# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors.
|
||||
# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired.
|
||||
|
||||
# Defaults, 200 ms delay echo with feedback:
|
||||
# Delay in ms. Takes an array with multiple channels.
|
||||
# echo_delay = "200"
|
||||
# Feedback factor for echo.
|
||||
# echo_feedback = "0.5"
|
||||
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
|
||||
# echo_amp = "0.2"
|
||||
|
||||
# Reverby preset.
|
||||
# echo_delay = " 60 80 120 172 200 320 380"
|
||||
# echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2"
|
||||
|
||||
# echo_amp = "0.12"
|
||||
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/EchoReverb.dsp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
filters = 2
|
||||
filter0 = echo
|
||||
filter1 = reverb
|
||||
|
||||
echo_delay = "200"
|
||||
echo_feedback = "0.6"
|
||||
echo_amp = "0.25"
|
||||
|
||||
reverb_roomwidth = 0.75
|
||||
reverb_roomsize = 0.75
|
||||
reverb_damping = 1.0
|
||||
reverb_wettime = 0.3
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
iir_gain = -12.0
|
||||
iir_type = HSH
|
||||
iir_frequency = 8000.0
|
||||
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/IIR.dsp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
filters = 1
|
||||
filter0 = iir
|
||||
|
||||
# Defaults.
|
||||
#iir_frequency = 1024.0
|
||||
#iir_quality = 0.707
|
||||
#iir_gain = 0.0
|
||||
#iir_type = LPF
|
||||
|
||||
# Filter types:
|
||||
# LPF: Low-pass
|
||||
# HPF: High-pass
|
||||
# BPCSGF: Band-pass #1
|
||||
# BPZPGF: Band-pass #2
|
||||
# APF: Allpass
|
||||
# NOTCH: Notch filter
|
||||
# RIAA_phono: RIAA record/tape deemphasis
|
||||
# PEQ: peaking band EQ
|
||||
# BBOOST: Bassboost
|
||||
# LSH: Low-shelf
|
||||
# HSH: High-shelf
|
||||
# RIAA_CD: CD de-emphasis
|
||||
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
47
src/minarch/libretro-common/audio/dsp_filters/LowPassCPS.dsp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
filters = 1
|
||||
filter0 = eq
|
||||
|
||||
eq_frequencies = "8000 10000 12500 16000 20000"
|
||||
eq_gains = "0 -30 -30 -30 -30"
|
||||
|
||||
# Low pass filter for the QSound chip from CPS-1/2.
|
||||
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz
|
||||
|
||||
# Defaults
|
||||
|
||||
# Beta factor for Kaiser window.
|
||||
# Lower values will allow better frequency resolution, but more ripple.
|
||||
# eq_window_beta = 4.0
|
||||
|
||||
# The block size on which FFT is done.
|
||||
# Too high value requires more processing as well as longer latency but
|
||||
# allows finer-grained control over the spectrum.
|
||||
# eq_block_size_log2 = 8
|
||||
|
||||
# An array of which frequencies to control.
|
||||
# You can create an arbitrary amount of these sampling points.
|
||||
# The EQ will try to create a frequency response which fits well to these points.
|
||||
# The filter response is linearly interpolated between sampling points here.
|
||||
#
|
||||
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
|
||||
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
|
||||
#
|
||||
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
|
||||
# eq_frequencies = "500 1000 2000"
|
||||
# eq_gains = "0 3 0"
|
||||
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
|
||||
|
||||
# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.
|
||||
|
||||
# Dumps the impulse response generated by the EQ as a plain-text file
|
||||
# with one coefficient per line.
|
||||
# eq_impulse_response_output = "eq_impulse.txt"
|
||||
#
|
||||
# Using GNU Octave or Matlab, you can plot the response with:
|
||||
#
|
||||
# f = fopen('/path/to/eq_impulse.txt');
|
||||
# l = textscan(f, '%f');
|
||||
# res = l{1};
|
||||
# freqz(res, 1, 4096, 48000);
|
||||
#
|
||||
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch
|
||||
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal file
135
src/minarch/libretro-common/audio/dsp_filters/Makefile
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
compiler := gcc
|
||||
extra_flags :=
|
||||
use_neon := 0
|
||||
build = release
|
||||
DYLIB := so
|
||||
PREFIX := /usr
|
||||
INSTALLDIR := $(PREFIX)/lib/retroarch/filters/audio
|
||||
|
||||
ifeq ($(platform),)
|
||||
platform = unix
|
||||
ifeq ($(shell uname -s),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(shell uname -s)),)
|
||||
platform = osx
|
||||
arch = intel
|
||||
ifeq ($(shell uname -p),powerpc)
|
||||
arch = ppc
|
||||
endif
|
||||
else ifneq ($(findstring MINGW,$(shell uname -s)),)
|
||||
platform = win
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(platform),gcc)
|
||||
extra_rules_gcc := $(shell $(compiler) -dumpmachine)
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring armv7,$(extra_rules_gcc)))
|
||||
extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon
|
||||
use_neon := 1
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring hardfloat,$(extra_rules_gcc)))
|
||||
extra_flags += -mfloat-abi=hard
|
||||
endif
|
||||
|
||||
ifeq (release,$(build))
|
||||
extra_flags += -O2
|
||||
endif
|
||||
|
||||
ifeq (debug,$(build))
|
||||
extra_flags += -O0 -g
|
||||
endif
|
||||
|
||||
ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T
|
||||
|
||||
ifeq ($(platform), unix)
|
||||
DYLIB = so
|
||||
else ifeq ($(platform), osx)
|
||||
compiler := $(CC)
|
||||
DYLIB = dylib
|
||||
ldflags := -dynamiclib
|
||||
ARCHFLAGS=
|
||||
MINVERFLAGS=
|
||||
ifeq ($(shell uname -p),arm)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
|
||||
else ifeq ($(HAVE_METAL),1)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++ # macOS (Metal, x86 64bit)
|
||||
else ifeq ($(shell uname -p),powerpc)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.5 # macOSX (PowerPC 32-bit)
|
||||
else ifeq ($(shell uname -m),i386)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.6 # macOSX (OpenGL, x86 32bit)
|
||||
else
|
||||
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++ # macOSX (OpenGL, x86 64bit)
|
||||
endif
|
||||
|
||||
# Build for a specific architecture when ARCH is defined as a switch
|
||||
ifeq ($(ARCH),arm64)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS (Metal, ARM 64bit)
|
||||
ARCHFLAGS = -arch arm64
|
||||
else ifeq ($(ARCH),x86_64)
|
||||
ifeq ($(HAVE_METAL),1)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++
|
||||
else
|
||||
MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++
|
||||
endif
|
||||
ARCHFLAGS = -arch x86_64
|
||||
else ifeq ($(ARCH),x86)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.6
|
||||
ARCHFLAGS = -arch x86
|
||||
else ifeq ($(ARCH),ppc)
|
||||
MINVERFLAGS = -mmacosx-version-min=10.5
|
||||
ARCHFLAGS = -arch ppc
|
||||
endif
|
||||
ifeq ($(BUILDBOT),1)
|
||||
ARCHFLAGS = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
|
||||
endif
|
||||
extraflags += $(MINVERFLAGS) $(ARCHFLAGS)
|
||||
ldflags += $(MINVERFLAGS) $(ARCHFLAGS)
|
||||
else
|
||||
extra_flags += -static-libgcc -static-libstdc++
|
||||
DYLIB = dll
|
||||
endif
|
||||
|
||||
CC := $(compiler) -Wall
|
||||
CXX := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall
|
||||
flags := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include
|
||||
asflags := $(ASFLAGS) -fPIC $(extra_flags)
|
||||
objects :=
|
||||
|
||||
ifeq (1,$(use_neon))
|
||||
ASMFLAGS := -INEON/asm
|
||||
asflags += -mfpu=neon
|
||||
endif
|
||||
|
||||
plugs := $(wildcard *.c)
|
||||
objects := $(plugs:.c=.o)
|
||||
targets := $(objects:.o=.$(DYLIB))
|
||||
|
||||
all: build;
|
||||
|
||||
%.o: %.S
|
||||
$(CC) -c -o $@ $(asflags) $(ASMFLAGS) $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $(flags) $<
|
||||
|
||||
%.$(DYLIB): %.o
|
||||
$(CC) -o $@ $(ldflags) $(flags) $^
|
||||
|
||||
build: $(targets)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *.$(DYLIB)
|
||||
|
||||
strip:
|
||||
strip -s *.$(DYLIB)
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(INSTALLDIR)
|
||||
cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp
|
||||
|
||||
test-install:
|
||||
DESTDIR=/tmp/build $(MAKE) install
|
||||
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal file
12
src/minarch/libretro-common/audio/dsp_filters/Mono.dsp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
filters = 1
|
||||
filter0 = panning
|
||||
|
||||
# Gains are linear.
|
||||
|
||||
# Stereo Mono:
|
||||
panning_left_mix = "0.5 0.5"
|
||||
panning_right_mix = "0.5 0.5"
|
||||
|
||||
# Mono on one speaker:
|
||||
# panning_left_mix = "0.5 0.5"
|
||||
# panning_right_mix = "0.0 0.0"
|
||||
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal file
22
src/minarch/libretro-common/audio/dsp_filters/Panning.dsp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
filters = 1
|
||||
filter0 = panning
|
||||
|
||||
# Gains are linear.
|
||||
|
||||
# The default. Left and right channels map to each other.
|
||||
panning_left_mix = "1.0 0.0"
|
||||
panning_right_mix = "0.0 1.0"
|
||||
|
||||
# Some examples:
|
||||
#
|
||||
# Mono:
|
||||
# panning_left_mix = "0.5 0.5"
|
||||
# panning_right_mix = "0.5 0.5"
|
||||
|
||||
# Swap left and right channels:
|
||||
# panning_left_mix = "0.0 1.0"
|
||||
# panning_right_mix = "1.0 0.0"
|
||||
#
|
||||
# Mono on one speaker:
|
||||
# panning_left_mix = "0.5 0.5"
|
||||
# panning_right_mix = "0.0 0.0"
|
||||
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
10
src/minarch/libretro-common/audio/dsp_filters/Phaser.dsp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
filters = 1
|
||||
filter0 = phaser
|
||||
|
||||
# Defaults.
|
||||
# phaser_lfo_freq = 0.4
|
||||
# phaser_lfo_start_phase = 0.0
|
||||
# phaser_feedback = 0.0
|
||||
# phaser_depth = 0.4
|
||||
# phaser_dry_wet = 0.5
|
||||
# phaser_stages = 2
|
||||
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/Reverb.dsp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
filters = 1
|
||||
filter0 = reverb
|
||||
|
||||
# Defaults.
|
||||
# reverb_drytime = 0.43
|
||||
# reverb_wettime = 0.4
|
||||
# reverb_damping = 0.8
|
||||
# reverb_roomwidth = 0.56
|
||||
# reverb_roomsize = 0.56
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = tremolo
|
||||
|
||||
# Defaults.
|
||||
#tremolo_frequency = 4.0
|
||||
#tremolo_depth = 0.9
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
filters = 1
|
||||
filter0 = vibrato
|
||||
|
||||
# Defaults.
|
||||
#vibrato_frequency = 5.0
|
||||
#vibrato_depth = 0.5
|
||||
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
9
src/minarch/libretro-common/audio/dsp_filters/WahWah.dsp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
filters = 1
|
||||
filter0 = wahwah
|
||||
|
||||
# Defaults.
|
||||
# wahwah_lfo_freq = 1.5
|
||||
# wahwah_lfo_start_phase = 0.0
|
||||
# wahwah_freq_offset = 0.3
|
||||
# wahwah_depth = 0.7
|
||||
# wahwah_resonance = 2.5
|
||||
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal file
160
src/minarch/libretro-common/audio/dsp_filters/chorus.c
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (chorus.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
#define CHORUS_MAX_DELAY 4096
|
||||
#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1)
|
||||
|
||||
struct chorus_data
|
||||
{
|
||||
float old[2][CHORUS_MAX_DELAY];
|
||||
unsigned old_ptr;
|
||||
|
||||
float delay;
|
||||
float depth;
|
||||
float input_rate;
|
||||
float mix_dry;
|
||||
float mix_wet;
|
||||
unsigned lfo_ptr;
|
||||
unsigned lfo_period;
|
||||
};
|
||||
|
||||
static void chorus_free(void *data)
|
||||
{
|
||||
if (data)
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void chorus_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
float *out = NULL;
|
||||
struct chorus_data *ch = (struct chorus_data*)data;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
out = output->samples;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
unsigned delay_int;
|
||||
float delay_frac, l_a, l_b, r_a, r_b;
|
||||
float chorus_l, chorus_r;
|
||||
float in[2] = { out[0], out[1] };
|
||||
float delay = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period);
|
||||
|
||||
delay *= ch->input_rate;
|
||||
if (ch->lfo_ptr >= ch->lfo_period)
|
||||
ch->lfo_ptr = 0;
|
||||
|
||||
delay_int = (unsigned)delay;
|
||||
|
||||
if (delay_int >= CHORUS_MAX_DELAY - 1)
|
||||
delay_int = CHORUS_MAX_DELAY - 2;
|
||||
|
||||
delay_frac = delay - delay_int;
|
||||
|
||||
ch->old[0][ch->old_ptr] = in[0];
|
||||
ch->old[1][ch->old_ptr] = in[1];
|
||||
|
||||
l_a = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
|
||||
l_b = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
|
||||
r_a = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
|
||||
r_b = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
|
||||
|
||||
/* Lerp introduces aliasing of the chorus component,
|
||||
* but doing full polyphase here is probably overkill. */
|
||||
chorus_l = l_a * (1.0f - delay_frac) + l_b * delay_frac;
|
||||
chorus_r = r_a * (1.0f - delay_frac) + r_b * delay_frac;
|
||||
|
||||
out[0] = ch->mix_dry * in[0] + ch->mix_wet * chorus_l;
|
||||
out[1] = ch->mix_dry * in[1] + ch->mix_wet * chorus_r;
|
||||
|
||||
ch->old_ptr = (ch->old_ptr + 1) & CHORUS_DELAY_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
static void *chorus_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float delay, depth, lfo_freq, drywet;
|
||||
struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch));
|
||||
if (!ch)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "delay_ms", &delay, 25.0f);
|
||||
config->get_float(userdata, "depth_ms", &depth, 1.0f);
|
||||
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f);
|
||||
config->get_float(userdata, "drywet", &drywet, 0.8f);
|
||||
|
||||
delay /= 1000.0f;
|
||||
depth /= 1000.0f;
|
||||
|
||||
if (depth > delay)
|
||||
depth = delay;
|
||||
|
||||
if (drywet < 0.0f)
|
||||
drywet = 0.0f;
|
||||
else if (drywet > 1.0f)
|
||||
drywet = 1.0f;
|
||||
|
||||
ch->mix_dry = 1.0f - 0.5f * drywet;
|
||||
ch->mix_wet = 0.5f * drywet;
|
||||
|
||||
ch->delay = delay;
|
||||
ch->depth = depth;
|
||||
ch->lfo_period = (1.0f / lfo_freq) * info->input_rate;
|
||||
ch->input_rate = info->input_rate;
|
||||
if (!ch->lfo_period)
|
||||
ch->lfo_period = 1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation chorus_plug = {
|
||||
chorus_init,
|
||||
chorus_process,
|
||||
chorus_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Chorus",
|
||||
"chorus",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation chorus_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *
|
||||
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &chorus_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
3
src/minarch/libretro-common/audio/dsp_filters/configure
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
PACKAGE_NAME=retroarch-filters-audio
|
||||
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal file
90
src/minarch/libretro-common/audio/dsp_filters/crystalizer.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (echo.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
struct delta_data
|
||||
{
|
||||
float intensity;
|
||||
float old[2];
|
||||
};
|
||||
|
||||
static void delta_free(void *data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void delta_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i, c;
|
||||
struct delta_data *d = (struct delta_data*)data;
|
||||
float *out = output->samples;
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
for (i = 0; i < input->frames; i++)
|
||||
{
|
||||
for (c = 0; c < 2; c++)
|
||||
{
|
||||
float current = *out;
|
||||
*out++ = current + (current - d->old[c]) * d->intensity;
|
||||
d->old[c] = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *delta_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d));
|
||||
if (!d)
|
||||
return NULL;
|
||||
config->get_float(userdata, "intensity", &d->intensity, 5.0f);
|
||||
return d;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation delta_plug = {
|
||||
delta_init,
|
||||
delta_process,
|
||||
delta_free,
|
||||
DSPFILTER_API_VERSION,
|
||||
"Delta Sharpening",
|
||||
"crystalizer",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation delta_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &delta_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal file
180
src/minarch/libretro-common/audio/dsp_filters/echo.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (echo.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
struct echo_channel
|
||||
{
|
||||
float *buffer;
|
||||
unsigned ptr;
|
||||
unsigned frames;
|
||||
float feedback;
|
||||
};
|
||||
|
||||
struct echo_data
|
||||
{
|
||||
struct echo_channel *channels;
|
||||
unsigned num_channels;
|
||||
float amp;
|
||||
};
|
||||
|
||||
static void echo_free(void *data)
|
||||
{
|
||||
unsigned i;
|
||||
struct echo_data *echo = (struct echo_data*)data;
|
||||
|
||||
for (i = 0; i < echo->num_channels; i++)
|
||||
free(echo->channels[i].buffer);
|
||||
free(echo->channels);
|
||||
free(echo);
|
||||
}
|
||||
|
||||
static void echo_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i, c;
|
||||
float *out = NULL;
|
||||
struct echo_data *echo = (struct echo_data*)data;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
out = output->samples;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float left, right;
|
||||
float echo_left = 0.0f;
|
||||
float echo_right = 0.0f;
|
||||
|
||||
for (c = 0; c < echo->num_channels; c++)
|
||||
{
|
||||
echo_left += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0];
|
||||
echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1];
|
||||
}
|
||||
|
||||
echo_left *= echo->amp;
|
||||
echo_right *= echo->amp;
|
||||
|
||||
left = out[0] + echo_left;
|
||||
right = out[1] + echo_right;
|
||||
|
||||
for (c = 0; c < echo->num_channels; c++)
|
||||
{
|
||||
float feedback_left = out[0] + echo->channels[c].feedback * echo_left;
|
||||
float feedback_right = out[1] + echo->channels[c].feedback * echo_right;
|
||||
|
||||
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left;
|
||||
echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right;
|
||||
|
||||
echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames;
|
||||
}
|
||||
|
||||
out[0] = left;
|
||||
out[1] = right;
|
||||
}
|
||||
}
|
||||
|
||||
static void *echo_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
unsigned i, channels;
|
||||
struct echo_channel *echo_channels = NULL;
|
||||
float *delay = NULL;
|
||||
float *feedback = NULL;
|
||||
unsigned num_delay = 0;
|
||||
unsigned num_feedback = 0;
|
||||
|
||||
static const float default_delay[] = { 200.0f };
|
||||
static const float default_feedback[] = { 0.5f };
|
||||
struct echo_data *echo = (struct echo_data*)
|
||||
calloc(1, sizeof(*echo));
|
||||
|
||||
if (!echo)
|
||||
return NULL;
|
||||
|
||||
config->get_float_array(userdata, "delay", &delay,
|
||||
&num_delay, default_delay, 1);
|
||||
config->get_float_array(userdata, "feedback", &feedback,
|
||||
&num_feedback, default_feedback, 1);
|
||||
config->get_float(userdata, "amp", &echo->amp, 0.2f);
|
||||
|
||||
channels = num_feedback = num_delay = MIN(num_delay, num_feedback);
|
||||
|
||||
echo_channels = (struct echo_channel*)calloc(channels,
|
||||
sizeof(*echo_channels));
|
||||
|
||||
if (!echo_channels)
|
||||
goto error;
|
||||
|
||||
echo->channels = echo_channels;
|
||||
echo->num_channels = channels;
|
||||
|
||||
for (i = 0; i < channels; i++)
|
||||
{
|
||||
unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f);
|
||||
if (!frames)
|
||||
goto error;
|
||||
|
||||
echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float));
|
||||
if (!echo->channels[i].buffer)
|
||||
goto error;
|
||||
|
||||
echo->channels[i].frames = frames;
|
||||
echo->channels[i].feedback = feedback[i];
|
||||
}
|
||||
|
||||
config->free(delay);
|
||||
config->free(feedback);
|
||||
return echo;
|
||||
|
||||
error:
|
||||
config->free(delay);
|
||||
config->free(feedback);
|
||||
echo_free(echo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation echo_plug = {
|
||||
echo_init,
|
||||
echo_process,
|
||||
echo_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Multi-Echo",
|
||||
"echo",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation echo_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &echo_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
352
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal file
352
src/minarch/libretro-common/audio/dsp_filters/eq.c
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (eq.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <filters.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
#include "fft/fft.c"
|
||||
|
||||
struct eq_data
|
||||
{
|
||||
fft_t *fft;
|
||||
float buffer[8 * 1024];
|
||||
|
||||
float *save;
|
||||
float *block;
|
||||
fft_complex_t *filter;
|
||||
fft_complex_t *fftblock;
|
||||
unsigned block_size;
|
||||
unsigned block_ptr;
|
||||
};
|
||||
|
||||
struct eq_gain
|
||||
{
|
||||
float freq;
|
||||
float gain; /* Linear. */
|
||||
};
|
||||
|
||||
static void eq_free(void *data)
|
||||
{
|
||||
struct eq_data *eq = (struct eq_data*)data;
|
||||
if (!eq)
|
||||
return;
|
||||
|
||||
fft_free(eq->fft);
|
||||
free(eq->save);
|
||||
free(eq->block);
|
||||
free(eq->fftblock);
|
||||
free(eq->filter);
|
||||
free(eq);
|
||||
}
|
||||
|
||||
static void eq_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
float *out;
|
||||
const float *in;
|
||||
unsigned input_frames;
|
||||
struct eq_data *eq = (struct eq_data*)data;
|
||||
|
||||
output->samples = eq->buffer;
|
||||
output->frames = 0;
|
||||
|
||||
out = eq->buffer;
|
||||
in = input->samples;
|
||||
input_frames = input->frames;
|
||||
|
||||
while (input_frames)
|
||||
{
|
||||
unsigned write_avail = eq->block_size - eq->block_ptr;
|
||||
|
||||
if (input_frames < write_avail)
|
||||
write_avail = input_frames;
|
||||
|
||||
memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float));
|
||||
|
||||
in += write_avail * 2;
|
||||
input_frames -= write_avail;
|
||||
eq->block_ptr += write_avail;
|
||||
|
||||
// Convolve a new block.
|
||||
if (eq->block_ptr == eq->block_size)
|
||||
{
|
||||
unsigned i, c;
|
||||
|
||||
for (c = 0; c < 2; c++)
|
||||
{
|
||||
fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2);
|
||||
for (i = 0; i < 2 * eq->block_size; i++)
|
||||
eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]);
|
||||
fft_process_inverse(eq->fft, out + c, eq->fftblock, 2);
|
||||
}
|
||||
|
||||
// Overlap add method, so add in saved block now.
|
||||
for (i = 0; i < 2 * eq->block_size; i++)
|
||||
out[i] += eq->save[i];
|
||||
|
||||
// Save block for later.
|
||||
memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float));
|
||||
|
||||
out += eq->block_size * 2;
|
||||
output->frames += eq->block_size;
|
||||
eq->block_ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gains_cmp(const void *a_, const void *b_)
|
||||
{
|
||||
const struct eq_gain *a = (const struct eq_gain*)a_;
|
||||
const struct eq_gain *b = (const struct eq_gain*)b_;
|
||||
if (a->freq < b->freq)
|
||||
return -1;
|
||||
if (a->freq > b->freq)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generate_response(fft_complex_t *response,
|
||||
const struct eq_gain *gains, unsigned num_gains, unsigned samples)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
float start_freq = 0.0f;
|
||||
float start_gain = 1.0f;
|
||||
|
||||
float end_freq = 1.0f;
|
||||
float end_gain = 1.0f;
|
||||
|
||||
if (num_gains)
|
||||
{
|
||||
end_freq = gains->freq;
|
||||
end_gain = gains->gain;
|
||||
num_gains--;
|
||||
gains++;
|
||||
}
|
||||
|
||||
/* Create a response by linear interpolation between
|
||||
* known frequency sample points. */
|
||||
for (i = 0; i <= samples; i++)
|
||||
{
|
||||
float gain;
|
||||
float lerp = 0.5f;
|
||||
float freq = (float)i / samples;
|
||||
|
||||
while (freq >= end_freq)
|
||||
{
|
||||
if (num_gains)
|
||||
{
|
||||
start_freq = end_freq;
|
||||
start_gain = end_gain;
|
||||
end_freq = gains->freq;
|
||||
end_gain = gains->gain;
|
||||
|
||||
gains++;
|
||||
num_gains--;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_freq = end_freq;
|
||||
start_gain = end_gain;
|
||||
end_freq = 1.0f;
|
||||
end_gain = 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Edge case where i == samples. */
|
||||
if (end_freq > start_freq)
|
||||
lerp = (freq - start_freq) / (end_freq - start_freq);
|
||||
gain = (1.0f - lerp) * start_gain + lerp * end_gain;
|
||||
|
||||
response[i].real = gain;
|
||||
response[i].imag = 0.0f;
|
||||
response[2 * samples - i].real = gain;
|
||||
response[2 * samples - i].imag = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void create_filter(struct eq_data *eq, unsigned size_log2,
|
||||
struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
|
||||
{
|
||||
int i;
|
||||
int half_block_size = eq->block_size >> 1;
|
||||
double window_mod = 1.0 / kaiser_window_function(0.0, beta);
|
||||
|
||||
fft_t *fft = fft_new(size_log2);
|
||||
float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
|
||||
if (!fft || !time_filter)
|
||||
goto end;
|
||||
|
||||
/* Make sure bands are in correct order. */
|
||||
qsort(gains, num_gains, sizeof(*gains), gains_cmp);
|
||||
|
||||
/* Compute desired filter response. */
|
||||
generate_response(eq->filter, gains, num_gains, half_block_size);
|
||||
|
||||
/* Get equivalent time-domain filter. */
|
||||
fft_process_inverse(fft, time_filter, eq->filter, 1);
|
||||
|
||||
/* ifftshift() to create the correct linear phase filter.
|
||||
* The filter response was designed with zero phase, which
|
||||
* won't work unless we compensate
|
||||
* for the repeating property of the FFT here
|
||||
* by flipping left and right blocks. */
|
||||
for (i = 0; i < half_block_size; i++)
|
||||
{
|
||||
float tmp = time_filter[i + half_block_size];
|
||||
time_filter[i + half_block_size] = time_filter[i];
|
||||
time_filter[i] = tmp;
|
||||
}
|
||||
|
||||
/* Apply a window to smooth out the frequency repsonse. */
|
||||
for (i = 0; i < (int)eq->block_size; i++)
|
||||
{
|
||||
/* Kaiser window. */
|
||||
double phase = (double)i / eq->block_size;
|
||||
phase = 2.0 * (phase - 0.5);
|
||||
time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Debugging. */
|
||||
if (filter_path)
|
||||
{
|
||||
FILE *file = fopen(filter_path, "w");
|
||||
if (file)
|
||||
{
|
||||
for (i = 0; i < (int)eq->block_size - 1; i++)
|
||||
fprintf(file, "%.8f\n", time_filter[i + 1]);
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Padded FFT to create our FFT filter.
|
||||
* Make our even-length filter odd by discarding the first coefficient.
|
||||
* For some interesting reason, this allows us to design an odd-length linear phase filter.
|
||||
*/
|
||||
fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);
|
||||
|
||||
end:
|
||||
fft_free(fft);
|
||||
free(time_filter);
|
||||
}
|
||||
|
||||
static void *eq_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float *frequencies, *gain;
|
||||
unsigned num_freq, num_gain, i, size;
|
||||
int size_log2;
|
||||
float beta;
|
||||
struct eq_gain *gains = NULL;
|
||||
char *filter_path = NULL;
|
||||
const float default_freq[] = { 0.0f, info->input_rate };
|
||||
const float default_gain[] = { 0.0f, 0.0f };
|
||||
struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq));
|
||||
if (!eq)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "window_beta", &beta, 4.0f);
|
||||
|
||||
config->get_int(userdata, "block_size_log2", &size_log2, 8);
|
||||
size = 1 << size_log2;
|
||||
|
||||
config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2);
|
||||
config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);
|
||||
|
||||
if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
|
||||
{
|
||||
config->free(filter_path);
|
||||
filter_path = NULL;
|
||||
}
|
||||
|
||||
num_gain = num_freq = MIN(num_gain, num_freq);
|
||||
|
||||
gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains));
|
||||
if (!gains)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < num_gain; i++)
|
||||
{
|
||||
gains[i].freq = frequencies[i] / (0.5f * info->input_rate);
|
||||
gains[i].gain = pow(10.0, gain[i] / 20.0);
|
||||
}
|
||||
config->free(frequencies);
|
||||
config->free(gain);
|
||||
|
||||
eq->block_size = size;
|
||||
|
||||
eq->save = (float*)calloc( size, 2 * sizeof(*eq->save));
|
||||
eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block));
|
||||
eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock));
|
||||
eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter));
|
||||
|
||||
/* Use an FFT which is twice the block size with zero-padding
|
||||
* to make circular convolution => proper convolution.
|
||||
*/
|
||||
eq->fft = fft_new(size_log2 + 1);
|
||||
|
||||
if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)
|
||||
goto error;
|
||||
|
||||
create_filter(eq, size_log2, gains, num_gain, beta, filter_path);
|
||||
config->free(filter_path);
|
||||
filter_path = NULL;
|
||||
|
||||
free(gains);
|
||||
return eq;
|
||||
|
||||
error:
|
||||
free(gains);
|
||||
eq_free(eq);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation eq_plug = {
|
||||
eq_init,
|
||||
eq_process,
|
||||
eq_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Linear-Phase FFT Equalizer",
|
||||
"eq",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation eq_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &eq_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal file
204
src/minarch/libretro-common/audio/dsp_filters/fft/fft.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fft.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fft.h"
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
struct fft
|
||||
{
|
||||
fft_complex_t *interleave_buffer;
|
||||
fft_complex_t *phase_lut;
|
||||
unsigned *bitinverse_buffer;
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
static unsigned bitswap(unsigned x, unsigned size_log2)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned ret = 0;
|
||||
for (i = 0; i < size_log2; i++)
|
||||
ret |= ((x >> i) & 1) << (size_log2 - i - 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void build_bitinverse(unsigned *bitinverse, unsigned size_log2)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned size = 1 << size_log2;
|
||||
for (i = 0; i < size; i++)
|
||||
bitinverse[i] = bitswap(i, size_log2);
|
||||
}
|
||||
|
||||
static fft_complex_t exp_imag(double phase)
|
||||
{
|
||||
fft_complex_t out = { cos(phase), sin(phase) };
|
||||
return out;
|
||||
}
|
||||
|
||||
static void build_phase_lut(fft_complex_t *out, int size)
|
||||
{
|
||||
int i;
|
||||
out += size;
|
||||
for (i = -size; i <= size; i++)
|
||||
out[i] = exp_imag((M_PI * i) / size);
|
||||
}
|
||||
|
||||
static void interleave_complex(const unsigned *bitinverse,
|
||||
fft_complex_t *out, const fft_complex_t *in,
|
||||
unsigned samples, unsigned step)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < samples; i++, in += step)
|
||||
out[bitinverse[i]] = *in;
|
||||
}
|
||||
|
||||
static void interleave_float(const unsigned *bitinverse,
|
||||
fft_complex_t *out, const float *in,
|
||||
unsigned samples, unsigned step)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < samples; i++, in += step)
|
||||
{
|
||||
unsigned inv_i = bitinverse[i];
|
||||
out[inv_i].real = *in;
|
||||
out[inv_i].imag = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
|
||||
float gain, unsigned step)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < samples; i++, in++, out += step)
|
||||
*out = gain * in->real;
|
||||
}
|
||||
|
||||
fft_t *fft_new(unsigned block_size_log2)
|
||||
{
|
||||
unsigned size;
|
||||
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
|
||||
if (!fft)
|
||||
return NULL;
|
||||
|
||||
size = 1 << block_size_log2;
|
||||
fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer));
|
||||
fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer));
|
||||
fft->phase_lut = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut));
|
||||
|
||||
if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut)
|
||||
goto error;
|
||||
|
||||
fft->size = size;
|
||||
|
||||
build_bitinverse(fft->bitinverse_buffer, block_size_log2);
|
||||
build_phase_lut(fft->phase_lut, size);
|
||||
return fft;
|
||||
|
||||
error:
|
||||
fft_free(fft);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fft_free(fft_t *fft)
|
||||
{
|
||||
if (!fft)
|
||||
return;
|
||||
|
||||
free(fft->interleave_buffer);
|
||||
free(fft->bitinverse_buffer);
|
||||
free(fft->phase_lut);
|
||||
free(fft);
|
||||
}
|
||||
|
||||
static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
|
||||
{
|
||||
mod = fft_complex_mul(mod, *b);
|
||||
*b = fft_complex_sub(*a, mod);
|
||||
*a = fft_complex_add(*a, mod);
|
||||
}
|
||||
|
||||
static void butterflies(fft_complex_t *butterfly_buf,
|
||||
const fft_complex_t *phase_lut,
|
||||
int phase_dir, unsigned step_size, unsigned samples)
|
||||
{
|
||||
unsigned i, j;
|
||||
for (i = 0; i < samples; i += step_size << 1)
|
||||
{
|
||||
int phase_step = (int)samples * phase_dir / (int)step_size;
|
||||
for (j = i; j < i + step_size; j++)
|
||||
butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size],
|
||||
phase_lut[phase_step * (int)(j - i)]);
|
||||
}
|
||||
}
|
||||
|
||||
void fft_process_forward_complex(fft_t *fft,
|
||||
fft_complex_t *out, const fft_complex_t *in, unsigned step)
|
||||
{
|
||||
unsigned step_size;
|
||||
unsigned samples = fft->size;
|
||||
interleave_complex(fft->bitinverse_buffer, out, in, samples, step);
|
||||
|
||||
for (step_size = 1; step_size < samples; step_size <<= 1)
|
||||
{
|
||||
butterflies(out,
|
||||
fft->phase_lut + samples,
|
||||
-1, step_size, samples);
|
||||
}
|
||||
}
|
||||
|
||||
void fft_process_forward(fft_t *fft,
|
||||
fft_complex_t *out, const float *in, unsigned step)
|
||||
{
|
||||
unsigned step_size;
|
||||
unsigned samples = fft->size;
|
||||
interleave_float(fft->bitinverse_buffer, out, in, samples, step);
|
||||
|
||||
for (step_size = 1; step_size < fft->size; step_size <<= 1)
|
||||
{
|
||||
butterflies(out,
|
||||
fft->phase_lut + samples,
|
||||
-1, step_size, samples);
|
||||
}
|
||||
}
|
||||
|
||||
void fft_process_inverse(fft_t *fft,
|
||||
float *out, const fft_complex_t *in, unsigned step)
|
||||
{
|
||||
unsigned step_size;
|
||||
unsigned samples = fft->size;
|
||||
|
||||
interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
|
||||
in, samples, 1);
|
||||
|
||||
for (step_size = 1; step_size < samples; step_size <<= 1)
|
||||
{
|
||||
butterflies(fft->interleave_buffer,
|
||||
fft->phase_lut + samples,
|
||||
1, step_size, samples);
|
||||
}
|
||||
|
||||
resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step);
|
||||
}
|
||||
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal file
44
src/minarch/libretro-common/audio/dsp_filters/fft/fft.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (fft.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef RARCH_FFT_H__
|
||||
#define RARCH_FFT_H__
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <math/complex.h>
|
||||
|
||||
typedef struct fft fft_t;
|
||||
|
||||
fft_t *fft_new(unsigned block_size_log2);
|
||||
|
||||
void fft_free(fft_t *fft);
|
||||
|
||||
void fft_process_forward_complex(fft_t *fft,
|
||||
fft_complex_t *out, const fft_complex_t *in, unsigned step);
|
||||
|
||||
void fft_process_forward(fft_t *fft,
|
||||
fft_complex_t *out, const float *in, unsigned step);
|
||||
|
||||
void fft_process_inverse(fft_t *fft,
|
||||
float *out, const fft_complex_t *in, unsigned step);
|
||||
|
||||
#endif
|
||||
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal file
371
src/minarch/libretro-common/audio/dsp_filters/iir.c
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (iir.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#define sqr(a) ((a) * (a))
|
||||
|
||||
/* filter types */
|
||||
enum IIRFilter
|
||||
{
|
||||
LPF, /* low pass filter */
|
||||
HPF, /* High pass filter */
|
||||
BPCSGF, /* band pass filter 1 */
|
||||
BPZPGF, /* band pass filter 2 */
|
||||
APF, /* Allpass filter*/
|
||||
NOTCH, /* Notch Filter */
|
||||
RIAA_phono, /* RIAA record/tape deemphasis */
|
||||
PEQ, /* Peaking band EQ filter */
|
||||
BBOOST, /* Bassboost filter */
|
||||
LSH, /* Low shelf filter */
|
||||
HSH, /* High shelf filter */
|
||||
RIAA_CD /* CD de-emphasis */
|
||||
};
|
||||
|
||||
struct iir_data
|
||||
{
|
||||
float b0, b1, b2;
|
||||
float a0, a1, a2;
|
||||
|
||||
struct
|
||||
{
|
||||
float xn1, xn2;
|
||||
float yn1, yn2;
|
||||
} l, r;
|
||||
};
|
||||
|
||||
static void iir_free(void *data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void iir_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
struct iir_data *iir = (struct iir_data*)data;
|
||||
float *out = output->samples;
|
||||
|
||||
float b0 = iir->b0;
|
||||
float b1 = iir->b1;
|
||||
float b2 = iir->b2;
|
||||
float a0 = iir->a0;
|
||||
float a1 = iir->a1;
|
||||
float a2 = iir->a2;
|
||||
|
||||
float xn1_l = iir->l.xn1;
|
||||
float xn2_l = iir->l.xn2;
|
||||
float yn1_l = iir->l.yn1;
|
||||
float yn2_l = iir->l.yn2;
|
||||
|
||||
float xn1_r = iir->r.xn1;
|
||||
float xn2_r = iir->r.xn2;
|
||||
float yn1_r = iir->r.yn1;
|
||||
float yn2_r = iir->r.yn2;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float in_l = out[0];
|
||||
float in_r = out[1];
|
||||
|
||||
float l = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0;
|
||||
float r = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0;
|
||||
|
||||
xn2_l = xn1_l;
|
||||
xn1_l = in_l;
|
||||
yn2_l = yn1_l;
|
||||
yn1_l = l;
|
||||
|
||||
xn2_r = xn1_r;
|
||||
xn1_r = in_r;
|
||||
yn2_r = yn1_r;
|
||||
yn1_r = r;
|
||||
|
||||
out[0] = l;
|
||||
out[1] = r;
|
||||
}
|
||||
|
||||
iir->l.xn1 = xn1_l;
|
||||
iir->l.xn2 = xn2_l;
|
||||
iir->l.yn1 = yn1_l;
|
||||
iir->l.yn2 = yn2_l;
|
||||
|
||||
iir->r.xn1 = xn1_r;
|
||||
iir->r.xn2 = xn2_r;
|
||||
iir->r.yn1 = yn1_r;
|
||||
iir->r.yn2 = yn2_r;
|
||||
}
|
||||
|
||||
#define CHECK(x) if (string_is_equal(str, #x)) return x
|
||||
static enum IIRFilter str_to_type(const char *str)
|
||||
{
|
||||
CHECK(LPF);
|
||||
CHECK(HPF);
|
||||
CHECK(BPCSGF);
|
||||
CHECK(BPZPGF);
|
||||
CHECK(APF);
|
||||
CHECK(NOTCH);
|
||||
CHECK(RIAA_phono);
|
||||
CHECK(PEQ);
|
||||
CHECK(BBOOST);
|
||||
CHECK(LSH);
|
||||
CHECK(HSH);
|
||||
CHECK(RIAA_CD);
|
||||
|
||||
return LPF; /* Fallback. */
|
||||
}
|
||||
|
||||
static void make_poly_from_roots(
|
||||
const double *roots, unsigned num_roots, float *poly)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
poly[0] = 1;
|
||||
poly[1] = -roots[0];
|
||||
memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly));
|
||||
|
||||
for (i = 1; i < num_roots; i++)
|
||||
for (j = num_roots; j > 0; j--)
|
||||
poly[j] -= poly[j - 1] * roots[i];
|
||||
}
|
||||
|
||||
static void iir_filter_init(struct iir_data *iir,
|
||||
float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type)
|
||||
{
|
||||
double omega = 2.0 * M_PI * freq / sample_rate;
|
||||
double cs = cos(omega);
|
||||
double sn = sin(omega);
|
||||
double a1pha = sn / (2.0 * qual);
|
||||
double A = exp(log(10.0) * gain / 40.0);
|
||||
double beta = sqrt(A + A);
|
||||
|
||||
float b0 = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0;
|
||||
|
||||
/* Set up filter coefficients according to type */
|
||||
switch (filter_type)
|
||||
{
|
||||
case LPF:
|
||||
b0 = (1.0 - cs) / 2.0;
|
||||
b1 = 1.0 - cs ;
|
||||
b2 = (1.0 - cs) / 2.0;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case HPF:
|
||||
b0 = (1.0 + cs) / 2.0;
|
||||
b1 = -(1.0 + cs);
|
||||
b2 = (1.0 + cs) / 2.0;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case APF:
|
||||
b0 = 1.0 - a1pha;
|
||||
b1 = -2.0 * cs;
|
||||
b2 = 1.0 + a1pha;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case BPZPGF:
|
||||
b0 = a1pha;
|
||||
b1 = 0.0;
|
||||
b2 = -a1pha;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case BPCSGF:
|
||||
b0 = sn / 2.0;
|
||||
b1 = 0.0;
|
||||
b2 = -sn / 2.0;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case NOTCH:
|
||||
b0 = 1.0;
|
||||
b1 = -2.0 * cs;
|
||||
b2 = 1.0;
|
||||
a0 = 1.0 + a1pha;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha;
|
||||
break;
|
||||
case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */
|
||||
{
|
||||
double y, b_re, a_re, b_im, a_im, g;
|
||||
float b[3] = {0.0f};
|
||||
float a[3] = {0.0f};
|
||||
|
||||
if ((int)sample_rate == 44100)
|
||||
{
|
||||
static const double zeros[] = {-0.2014898, 0.9233820};
|
||||
static const double poles[] = {0.7083149, 0.9924091};
|
||||
make_poly_from_roots(zeros, 2, b);
|
||||
make_poly_from_roots(poles, 2, a);
|
||||
}
|
||||
else if ((int)sample_rate == 48000)
|
||||
{
|
||||
static const double zeros[] = {-0.1766069, 0.9321590};
|
||||
static const double poles[] = {0.7396325, 0.9931330};
|
||||
make_poly_from_roots(zeros, 2, b);
|
||||
make_poly_from_roots(poles, 2, a);
|
||||
}
|
||||
else if ((int)sample_rate == 88200)
|
||||
{
|
||||
static const double zeros[] = {-0.1168735, 0.9648312};
|
||||
static const double poles[] = {0.8590646, 0.9964002};
|
||||
make_poly_from_roots(zeros, 2, b);
|
||||
make_poly_from_roots(poles, 2, a);
|
||||
}
|
||||
else if ((int)sample_rate == 96000)
|
||||
{
|
||||
static const double zeros[] = {-0.1141486, 0.9676817};
|
||||
static const double poles[] = {0.8699137, 0.9966946};
|
||||
make_poly_from_roots(zeros, 2, b);
|
||||
make_poly_from_roots(poles, 2, a);
|
||||
}
|
||||
|
||||
b0 = b[0];
|
||||
b1 = b[1];
|
||||
b2 = b[2];
|
||||
a0 = a[0];
|
||||
a1 = a[1];
|
||||
a2 = a[2];
|
||||
|
||||
/* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
|
||||
y = 2.0 * M_PI * 1000.0 / sample_rate;
|
||||
b_re = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y);
|
||||
a_re = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y);
|
||||
b_im = b1 * sin(-y) + b2 * sin(-2.0 * y);
|
||||
a_im = a1 * sin(-y) + a2 * sin(-2.0 * y);
|
||||
g = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im)));
|
||||
b0 *= g; b1 *= g; b2 *= g;
|
||||
break;
|
||||
}
|
||||
case PEQ:
|
||||
b0 = 1.0 + a1pha * A;
|
||||
b1 = -2.0 * cs;
|
||||
b2 = 1.0 - a1pha * A;
|
||||
a0 = 1.0 + a1pha / A;
|
||||
a1 = -2.0 * cs;
|
||||
a2 = 1.0 - a1pha / A;
|
||||
break;
|
||||
case BBOOST:
|
||||
beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2)));
|
||||
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
|
||||
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
|
||||
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
|
||||
a0 = ((A + 1) + (A - 1) * cs + beta * sn);
|
||||
a1 = -2 * ((A - 1) + (A + 1) * cs);
|
||||
a2 = (A + 1) + (A - 1) * cs - beta * sn;
|
||||
break;
|
||||
case LSH:
|
||||
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
|
||||
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
|
||||
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
|
||||
a0 = (A + 1) + (A - 1) * cs + beta * sn;
|
||||
a1 = -2 * ((A - 1) + (A + 1) * cs);
|
||||
a2 = (A + 1) + (A - 1) * cs - beta * sn;
|
||||
break;
|
||||
case RIAA_CD:
|
||||
omega = 2.0 * M_PI * 5283.0 / sample_rate;
|
||||
cs = cos(omega);
|
||||
sn = sin(omega);
|
||||
a1pha = sn / (2.0 * 0.4845);
|
||||
A = exp(log(10.0) * -9.477 / 40.0);
|
||||
beta = sqrt(A + A);
|
||||
(void)a1pha;
|
||||
case HSH:
|
||||
b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn);
|
||||
b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs);
|
||||
b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn);
|
||||
a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn;
|
||||
a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs);
|
||||
a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
iir->b0 = b0;
|
||||
iir->b1 = b1;
|
||||
iir->b2 = b2;
|
||||
iir->a0 = a0;
|
||||
iir->a1 = a1;
|
||||
iir->a2 = a2;
|
||||
}
|
||||
|
||||
static void *iir_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float freq, qual, gain;
|
||||
enum IIRFilter filter = LPF;
|
||||
char *type = NULL;
|
||||
struct iir_data *iir = (struct iir_data*)calloc(1, sizeof(*iir));
|
||||
if (!iir)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "frequency", &freq, 1024.0f);
|
||||
config->get_float(userdata, "quality", &qual, 0.707f);
|
||||
config->get_float(userdata, "gain", &gain, 0.0f);
|
||||
|
||||
config->get_string(userdata, "type", &type, "LPF");
|
||||
|
||||
filter = str_to_type(type);
|
||||
config->free(type);
|
||||
|
||||
iir_filter_init(iir, info->input_rate, freq, qual, gain, filter);
|
||||
return iir;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation iir_plug = {
|
||||
iir_init,
|
||||
iir_process,
|
||||
iir_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"IIR",
|
||||
"iir",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation iir_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &iir_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
4
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
4
src/minarch/libretro-common/audio/dsp_filters/link.T
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
global: dspfilter_get_implementation;
|
||||
local: *;
|
||||
};
|
||||
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal file
111
src/minarch/libretro-common/audio/dsp_filters/panning.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (panning.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
struct panning_data
|
||||
{
|
||||
float left[2];
|
||||
float right[2];
|
||||
};
|
||||
|
||||
static void panning_free(void *data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void panning_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
struct panning_data *pan = (struct panning_data*)data;
|
||||
float *out = output->samples;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float left = out[0];
|
||||
float right = out[1];
|
||||
out[0] = left * pan->left[0] + right * pan->left[1];
|
||||
out[1] = left * pan->right[0] + right * pan->right[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void *panning_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
static const float default_left[] = { 1.0f, 0.0f };
|
||||
static const float default_right[] = { 0.0f, 1.0f };
|
||||
float *left = NULL;
|
||||
float *right = NULL;
|
||||
unsigned num_left = 0;
|
||||
unsigned num_right = 0;
|
||||
struct panning_data *pan = (struct panning_data*)
|
||||
calloc(1, sizeof(*pan));
|
||||
|
||||
if (!pan)
|
||||
return NULL;
|
||||
|
||||
config->get_float_array(userdata, "left_mix",
|
||||
&left, &num_left, default_left, 2);
|
||||
config->get_float_array(userdata, "right_mix",
|
||||
&right, &num_right, default_right, 2);
|
||||
|
||||
memcpy(pan->left, (num_left == 2) ?
|
||||
left : default_left, sizeof(pan->left));
|
||||
memcpy(pan->right, (num_right == 2) ?
|
||||
right : default_right, sizeof(pan->right));
|
||||
|
||||
config->free(left);
|
||||
config->free(right);
|
||||
|
||||
return pan;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation panning = {
|
||||
panning_init,
|
||||
panning_process,
|
||||
panning_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Panning",
|
||||
"panning",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation panning_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *
|
||||
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &panning;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal file
145
src/minarch/libretro-common/audio/dsp_filters/phaser.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (phaser.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
#define phaserlfoshape 4.0
|
||||
#define phaserlfoskipsamples 20
|
||||
|
||||
struct phaser_data
|
||||
{
|
||||
float freq;
|
||||
float startphase;
|
||||
float fb;
|
||||
float depth;
|
||||
float drywet;
|
||||
float old[2][24];
|
||||
float gain;
|
||||
float fbout[2];
|
||||
float lfoskip;
|
||||
float phase;
|
||||
|
||||
int stages;
|
||||
unsigned long skipcount;
|
||||
};
|
||||
|
||||
static void phaser_free(void *data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void phaser_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i, c;
|
||||
int s;
|
||||
float m[2], tmp[2];
|
||||
struct phaser_data *ph = (struct phaser_data*)data;
|
||||
float *out = output->samples;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float in[2] = { out[0], out[1] };
|
||||
|
||||
for (c = 0; c < 2; c++)
|
||||
m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f;
|
||||
|
||||
if ((ph->skipcount++ % phaserlfoskipsamples) == 0)
|
||||
{
|
||||
ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase));
|
||||
ph->gain = (exp(ph->gain * phaserlfoshape) - 1.0) / (exp(phaserlfoshape) - 1);
|
||||
ph->gain = 1.0 - ph->gain * ph->depth;
|
||||
}
|
||||
|
||||
for (s = 0; s < ph->stages; s++)
|
||||
{
|
||||
for (c = 0; c < 2; c++)
|
||||
{
|
||||
tmp[c] = ph->old[c][s];
|
||||
ph->old[c][s] = ph->gain * tmp[c] + m[c];
|
||||
m[c] = tmp[c] - ph->gain * ph->old[c][s];
|
||||
}
|
||||
}
|
||||
|
||||
for (c = 0; c < 2; c++)
|
||||
{
|
||||
ph->fbout[c] = m[c];
|
||||
out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *phaser_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float lfo_freq, lfo_start_phase;
|
||||
struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
|
||||
if (!ph)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f);
|
||||
config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f);
|
||||
config->get_float(userdata, "feedback", &ph->fb, 0.0f);
|
||||
config->get_float(userdata, "depth", &ph->depth, 0.4f);
|
||||
config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f);
|
||||
config->get_int(userdata, "stages", &ph->stages, 2);
|
||||
|
||||
if (ph->stages < 1)
|
||||
ph->stages = 1;
|
||||
else if (ph->stages > 24)
|
||||
ph->stages = 24;
|
||||
|
||||
ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate;
|
||||
ph->phase = lfo_start_phase * M_PI / 180.0;
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation phaser_plug = {
|
||||
phaser_init,
|
||||
phaser_process,
|
||||
phaser_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Phaser",
|
||||
"phaser",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation phaser_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &phaser_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal file
318
src/minarch/libretro-common/audio/dsp_filters/reverb.c
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (reverb.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
struct comb
|
||||
{
|
||||
float *buffer;
|
||||
unsigned bufsize;
|
||||
unsigned bufidx;
|
||||
|
||||
float feedback;
|
||||
float filterstore;
|
||||
float damp1, damp2;
|
||||
};
|
||||
|
||||
struct allpass
|
||||
{
|
||||
float *buffer;
|
||||
float feedback;
|
||||
unsigned bufsize;
|
||||
unsigned bufidx;
|
||||
};
|
||||
|
||||
static INLINE float comb_process(struct comb *c, float input)
|
||||
{
|
||||
float output = c->buffer[c->bufidx];
|
||||
c->filterstore = (output * c->damp2) + (c->filterstore * c->damp1);
|
||||
|
||||
c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);
|
||||
|
||||
c->bufidx++;
|
||||
if (c->bufidx >= c->bufsize)
|
||||
c->bufidx = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static INLINE float allpass_process(struct allpass *a, float input)
|
||||
{
|
||||
float bufout = a->buffer[a->bufidx];
|
||||
float output = -input + bufout;
|
||||
a->buffer[a->bufidx] = input + bufout * a->feedback;
|
||||
|
||||
a->bufidx++;
|
||||
if (a->bufidx >= a->bufsize)
|
||||
a->bufidx = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#define numcombs 8
|
||||
#define numallpasses 4
|
||||
static const float muted = 0;
|
||||
static const float fixedgain = 0.015f;
|
||||
static const float scalewet = 3;
|
||||
static const float scaledry = 2;
|
||||
static const float scaledamp = 0.4f;
|
||||
static const float scaleroom = 0.28f;
|
||||
static const float offsetroom = 0.7f;
|
||||
static const float initialroom = 0.5f;
|
||||
static const float initialdamp = 0.5f;
|
||||
static const float initialwet = 1.0f / 3.0f;
|
||||
static const float initialdry = 0;
|
||||
static const float initialwidth = 1;
|
||||
static const float initialmode = 0;
|
||||
static const float freezemode = 0.5f;
|
||||
|
||||
struct revmodel
|
||||
{
|
||||
struct comb combL[numcombs];
|
||||
struct allpass allpassL[numallpasses];
|
||||
|
||||
float *bufcomb[numcombs];
|
||||
float *bufallpass[numallpasses];
|
||||
|
||||
float gain;
|
||||
float roomsize, roomsize1;
|
||||
float damp, damp1;
|
||||
float wet, wet1, wet2;
|
||||
float dry;
|
||||
float width;
|
||||
float mode;
|
||||
};
|
||||
|
||||
static float revmodel_process(struct revmodel *rev, float in)
|
||||
{
|
||||
int i;
|
||||
float mono_out = 0.0f;
|
||||
float mono_in = in;
|
||||
float input = mono_in * rev->gain;
|
||||
|
||||
for (i = 0; i < numcombs; i++)
|
||||
mono_out += comb_process(&rev->combL[i], input);
|
||||
|
||||
for (i = 0; i < numallpasses; i++)
|
||||
mono_out = allpass_process(&rev->allpassL[i], mono_out);
|
||||
|
||||
return mono_in * rev->dry + mono_out * rev->wet1;
|
||||
}
|
||||
|
||||
static void revmodel_update(struct revmodel *rev)
|
||||
{
|
||||
int i;
|
||||
rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
|
||||
|
||||
if (rev->mode >= freezemode)
|
||||
{
|
||||
rev->roomsize1 = 1.0f;
|
||||
rev->damp1 = 0.0f;
|
||||
rev->gain = muted;
|
||||
}
|
||||
else
|
||||
{
|
||||
rev->roomsize1 = rev->roomsize;
|
||||
rev->damp1 = rev->damp;
|
||||
rev->gain = fixedgain;
|
||||
}
|
||||
|
||||
for (i = 0; i < numcombs; i++)
|
||||
{
|
||||
rev->combL[i].feedback = rev->roomsize1;
|
||||
rev->combL[i].damp1 = rev->damp1;
|
||||
rev->combL[i].damp2 = 1.0f - rev->damp1;
|
||||
}
|
||||
}
|
||||
|
||||
static void revmodel_setroomsize(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->roomsize = value * scaleroom + offsetroom;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_setdamp(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->damp = value * scaledamp;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_setwet(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->wet = value * scalewet;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_setdry(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->dry = value * scaledry;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_setwidth(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->width = value;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_setmode(struct revmodel *rev, float value)
|
||||
{
|
||||
rev->mode = value;
|
||||
revmodel_update(rev);
|
||||
}
|
||||
|
||||
static void revmodel_init(struct revmodel *rev,int srate,bool right)
|
||||
{
|
||||
|
||||
static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 };
|
||||
static const int allpass_lengths[4] = { 225,341,441,556 };
|
||||
double r = srate * (1 / 44100.0);
|
||||
int stereosep = right ? 23 : 0;
|
||||
unsigned c;
|
||||
|
||||
|
||||
for (c = 0; c < numcombs; ++c)
|
||||
{
|
||||
rev->bufcomb[c] = malloc(r*comb_lengths[c]+stereosep*sizeof(float));
|
||||
rev->combL[c].buffer = rev->bufcomb[c];
|
||||
memset(rev->combL[c].buffer,0,r*comb_lengths[c]+stereosep*sizeof(float));
|
||||
rev->combL[c].bufsize=r*comb_lengths[c]+stereosep;
|
||||
}
|
||||
|
||||
for (c = 0; c < numallpasses; ++c)
|
||||
{
|
||||
rev->bufallpass[c] = malloc(r*allpass_lengths[c]+stereosep*sizeof(float));
|
||||
rev->allpassL[c].buffer = rev->bufallpass[c];
|
||||
memset(rev->allpassL[c].buffer,0,r*allpass_lengths[c]+stereosep*sizeof(float));
|
||||
rev->allpassL[c].bufsize=r*allpass_lengths[c]+stereosep;
|
||||
rev->allpassL[c].feedback = 0.5f;
|
||||
}
|
||||
|
||||
revmodel_setwet(rev, initialwet);
|
||||
revmodel_setroomsize(rev, initialroom);
|
||||
revmodel_setdry(rev, initialdry);
|
||||
revmodel_setdamp(rev, initialdamp);
|
||||
revmodel_setwidth(rev, initialwidth);
|
||||
revmodel_setmode(rev, initialmode);
|
||||
}
|
||||
|
||||
struct reverb_data
|
||||
{
|
||||
struct revmodel left, right;
|
||||
};
|
||||
|
||||
static void reverb_free(void *data)
|
||||
{
|
||||
struct reverb_data *rev = (struct reverb_data*)data;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < numcombs; i++) {
|
||||
free(rev->left.bufcomb[i]);
|
||||
free(rev->right.bufcomb[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < numallpasses; i++) {
|
||||
free(rev->left.bufallpass[i]);
|
||||
free(rev->right.bufallpass[i]);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void reverb_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
float *out;
|
||||
struct reverb_data *rev = (struct reverb_data*)data;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
out = output->samples;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float in[2] = { out[0], out[1] };
|
||||
|
||||
out[0] = revmodel_process(&rev->left, in[0]);
|
||||
out[1] = revmodel_process(&rev->right, in[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void *reverb_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float drytime, wettime, damping, roomwidth, roomsize;
|
||||
struct reverb_data *rev = (struct reverb_data*)
|
||||
calloc(1, sizeof(*rev));
|
||||
if (!rev)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "drytime", &drytime, 0.43f);
|
||||
config->get_float(userdata, "wettime", &wettime, 0.4f);
|
||||
config->get_float(userdata, "damping", &damping, 0.8f);
|
||||
config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
|
||||
config->get_float(userdata, "roomsize", &roomsize, 0.56f);
|
||||
|
||||
revmodel_init(&rev->left,info->input_rate,false);
|
||||
revmodel_init(&rev->right,info->input_rate,true);
|
||||
|
||||
revmodel_setdamp(&rev->left, damping);
|
||||
revmodel_setdry(&rev->left, drytime);
|
||||
revmodel_setwet(&rev->left, wettime);
|
||||
revmodel_setwidth(&rev->left, roomwidth);
|
||||
revmodel_setroomsize(&rev->left, roomsize);
|
||||
|
||||
revmodel_setdamp(&rev->right, damping);
|
||||
revmodel_setdry(&rev->right, drytime);
|
||||
revmodel_setwet(&rev->right, wettime);
|
||||
revmodel_setwidth(&rev->right, roomwidth);
|
||||
revmodel_setroomsize(&rev->right, roomsize);
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation reverb_plug = {
|
||||
reverb_init,
|
||||
reverb_process,
|
||||
reverb_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Reverb",
|
||||
"reverb",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation reverb_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &reverb_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal file
132
src/minarch/libretro-common/audio/dsp_filters/tremolo.c
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (tremolo.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#define sqr(a) ((a) * (a))
|
||||
|
||||
struct tremolo_core
|
||||
{
|
||||
float freq;
|
||||
float depth;
|
||||
float* wavetable;
|
||||
int index;
|
||||
int maxindex;
|
||||
};
|
||||
|
||||
struct tremolo
|
||||
{
|
||||
struct tremolo_core left, right;
|
||||
};
|
||||
|
||||
static void tremolo_free(void *data)
|
||||
{
|
||||
struct tremolo *tre = (struct tremolo*)data;
|
||||
free(tre->left.wavetable);
|
||||
free(tre->right.wavetable);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void tremolocore_init(struct tremolo_core *core,float depth,int samplerate,float freq)
|
||||
{
|
||||
const double offset = 1. - depth / 2.;
|
||||
unsigned i;
|
||||
double env;
|
||||
core->index = 0;
|
||||
core->maxindex = samplerate/freq;
|
||||
core->wavetable = malloc(core->maxindex*sizeof(float));
|
||||
memset(core->wavetable, 0, core->maxindex * sizeof(float));
|
||||
for (i = 0; i < core->maxindex; i++) {
|
||||
env = freq * i / samplerate;
|
||||
env = sin((M_PI*2) * fmod(env + 0.25, 1.0));
|
||||
core->wavetable[i] = env * (1 - fabs(offset)) + offset;
|
||||
}
|
||||
}
|
||||
|
||||
float tremolocore_core(struct tremolo_core *core,float in)
|
||||
{
|
||||
core->index = core->index % core->maxindex;
|
||||
return in * core->wavetable[core->index++];
|
||||
}
|
||||
|
||||
static void tremolo_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
float *out;
|
||||
struct tremolo *tre = (struct tremolo*)data;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
out = output->samples;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float in[2] = { out[0], out[1] };
|
||||
|
||||
out[0] = tremolocore_core(&tre->left, in[0]);
|
||||
out[1] = tremolocore_core(&tre->right, in[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void *tremolo_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float freq, depth;
|
||||
struct tremolo *tre = (struct tremolo*)calloc(1, sizeof(*tre));
|
||||
if (!tre)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "freq", &freq,4.0f);
|
||||
config->get_float(userdata, "depth", &depth, 0.9f);
|
||||
tremolocore_init(&tre->left,depth,info->input_rate,freq);
|
||||
tremolocore_init(&tre->right,depth,info->input_rate,freq);
|
||||
return tre;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation tremolo_plug = {
|
||||
tremolo_init,
|
||||
tremolo_process,
|
||||
tremolo_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Tremolo",
|
||||
"tremolo",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation tremolo_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &tremolo_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal file
167
src/minarch/libretro-common/audio/dsp_filters/vibrato.c
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (vibrato.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#define sqr(a) ((a) * (a))
|
||||
|
||||
const float BASE_DELAY_SEC = 0.002; // 2 ms
|
||||
const float VIBRATO_FREQUENCY_DEFAULT_HZ = 2;
|
||||
const float VIBRATO_FREQUENCY_MAX_HZ = 14;
|
||||
const float VIBRATO_DEPTH_DEFAULT_PERCENT = 50;
|
||||
const int add_delay = 3;
|
||||
|
||||
float hermite_interp(float x, float *y)
|
||||
{
|
||||
float c0, c1, c2, c3;
|
||||
c0 = y[1];
|
||||
c1 = (1.0 / 2.0)*(y[2] - y[0]);
|
||||
c2 = (y[0] - (5.0 / 2.0)*y[1]) + (2.0*y[2] - (1.0 / 2.0)*y[3]);
|
||||
c3 = (1.0 / 2.0)*(y[3] - y[0]) + (3.0 / 2.0)*(y[1] - y[2]);
|
||||
return ((c3*x + c2)*x + c1)*x + c0;
|
||||
}
|
||||
|
||||
struct vibrato_core
|
||||
{
|
||||
float freq;
|
||||
float samplerate;
|
||||
int phase;
|
||||
float depth;
|
||||
float* buffer;
|
||||
int writeindex;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct vibrato
|
||||
{
|
||||
struct vibrato_core left, right;
|
||||
};
|
||||
|
||||
static void vibrato_free(void *data)
|
||||
{
|
||||
struct vibrato *vib = (struct vibrato*)data;
|
||||
free(vib->left.buffer);
|
||||
free(vib->right.buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void vibratocore_init(struct vibrato_core *core,float depth,int samplerate,float freq)
|
||||
{
|
||||
core->size = BASE_DELAY_SEC * samplerate * 2;
|
||||
core->buffer = malloc((core->size + add_delay)*sizeof(float));
|
||||
memset(core->buffer, 0, (core->size + add_delay) * sizeof(float));
|
||||
core->samplerate = samplerate;
|
||||
core->freq = freq;
|
||||
core->depth = depth;
|
||||
core->phase = 0;
|
||||
core->writeindex = 0;
|
||||
}
|
||||
|
||||
float vibratocore_core(struct vibrato_core *core,float in)
|
||||
{
|
||||
float M = core->freq / core->samplerate;
|
||||
int maxphase = core->samplerate / core->freq;
|
||||
float lfo = sin(M * 2. * M_PI * core->phase++);
|
||||
core->phase = core->phase % maxphase;
|
||||
lfo = (lfo + 1) * 1.; // transform from [-1; 1] to [0; 1]
|
||||
int maxdelay = BASE_DELAY_SEC * core->samplerate;
|
||||
float delay = lfo * core->depth * maxdelay;
|
||||
delay += add_delay;
|
||||
float readindex = core->writeindex - 1 - delay;
|
||||
while (readindex < 0)readindex += core->size;
|
||||
while (readindex >= core->size)readindex -= core->size;
|
||||
int ipart = (int)readindex; // integer part of the delay
|
||||
float fpart = readindex - ipart; // fractional part of the delay
|
||||
float value = hermite_interp(fpart, &(core->buffer[ipart]));
|
||||
core->buffer[core->writeindex] = in;
|
||||
if (core->writeindex < add_delay){
|
||||
core->buffer[core->size + core->writeindex] = in;
|
||||
}
|
||||
core->writeindex++;
|
||||
if (core->writeindex == core->size) {
|
||||
core->writeindex = 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void vibrato_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
float *out;
|
||||
struct vibrato *vib = (struct vibrato*)data;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
out = output->samples;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float in[2] = { out[0], out[1] };
|
||||
|
||||
out[0] = vibratocore_core(&vib->left, in[0]);
|
||||
out[1] = vibratocore_core(&vib->right, in[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void *vibrato_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
float freq, depth;
|
||||
struct vibrato *vib = (struct vibrato*)calloc(1, sizeof(*vib));
|
||||
if (!vib)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "freq", &freq,5.0f);
|
||||
config->get_float(userdata, "depth", &depth, 0.5f);
|
||||
vibratocore_init(&vib->left,depth,info->input_rate,freq);
|
||||
vibratocore_init(&vib->right,depth,info->input_rate,freq);
|
||||
return vib;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation vibrato_plug = {
|
||||
vibrato_init,
|
||||
vibrato_process,
|
||||
vibrato_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Vibrato",
|
||||
"vibrato",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation vibrato_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &vibrato_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal file
147
src/minarch/libretro-common/audio/dsp_filters/wahwah.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (wahwah.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <libretro_dspfilter.h>
|
||||
|
||||
#define WAHWAH_LFO_SKIP_SAMPLES 30
|
||||
|
||||
struct wahwah_data
|
||||
{
|
||||
float phase;
|
||||
float lfoskip;
|
||||
float b0, b1, b2, a0, a1, a2;
|
||||
float freq, startphase;
|
||||
float depth, freqofs, res;
|
||||
unsigned long skipcount;
|
||||
|
||||
struct
|
||||
{
|
||||
float xn1, xn2, yn1, yn2;
|
||||
} l, r;
|
||||
};
|
||||
|
||||
static void wahwah_free(void *data)
|
||||
{
|
||||
if (data)
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void wahwah_process(void *data, struct dspfilter_output *output,
|
||||
const struct dspfilter_input *input)
|
||||
{
|
||||
unsigned i;
|
||||
struct wahwah_data *wah = (struct wahwah_data*)data;
|
||||
float *out = output->samples;
|
||||
|
||||
output->samples = input->samples;
|
||||
output->frames = input->frames;
|
||||
|
||||
for (i = 0; i < input->frames; i++, out += 2)
|
||||
{
|
||||
float out_l, out_r;
|
||||
float in[2] = { out[0], out[1] };
|
||||
|
||||
if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0)
|
||||
{
|
||||
float omega, sn, cs, alpha;
|
||||
float frequency = (1.0 + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0;
|
||||
|
||||
frequency = frequency * wah->depth * (1.0 - wah->freqofs) + wah->freqofs;
|
||||
frequency = exp((frequency - 1.0) * 6.0);
|
||||
|
||||
omega = M_PI * frequency;
|
||||
sn = sin(omega);
|
||||
cs = cos(omega);
|
||||
alpha = sn / (2.0 * wah->res);
|
||||
|
||||
wah->b0 = (1.0 - cs) / 2.0;
|
||||
wah->b1 = 1.0 - cs;
|
||||
wah->b2 = (1.0 - cs) / 2.0;
|
||||
wah->a0 = 1.0 + alpha;
|
||||
wah->a1 = -2.0 * cs;
|
||||
wah->a2 = 1.0 - alpha;
|
||||
}
|
||||
|
||||
out_l = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0;
|
||||
out_r = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0;
|
||||
|
||||
wah->l.xn2 = wah->l.xn1;
|
||||
wah->l.xn1 = in[0];
|
||||
wah->l.yn2 = wah->l.yn1;
|
||||
wah->l.yn1 = out_l;
|
||||
|
||||
wah->r.xn2 = wah->r.xn1;
|
||||
wah->r.xn1 = in[1];
|
||||
wah->r.yn2 = wah->r.yn1;
|
||||
wah->r.yn1 = out_r;
|
||||
|
||||
out[0] = out_l;
|
||||
out[1] = out_r;
|
||||
}
|
||||
}
|
||||
|
||||
static void *wahwah_init(const struct dspfilter_info *info,
|
||||
const struct dspfilter_config *config, void *userdata)
|
||||
{
|
||||
struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah));
|
||||
if (!wah)
|
||||
return NULL;
|
||||
|
||||
config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f);
|
||||
config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f);
|
||||
config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f);
|
||||
config->get_float(userdata, "depth", &wah->depth, 0.7f);
|
||||
config->get_float(userdata, "resonance", &wah->res, 2.5f);
|
||||
|
||||
wah->lfoskip = wah->freq * 2.0 * M_PI / info->input_rate;
|
||||
wah->phase = wah->startphase * M_PI / 180.0;
|
||||
|
||||
return wah;
|
||||
}
|
||||
|
||||
static const struct dspfilter_implementation wahwah_plug = {
|
||||
wahwah_init,
|
||||
wahwah_process,
|
||||
wahwah_free,
|
||||
|
||||
DSPFILTER_API_VERSION,
|
||||
"Wah-Wah",
|
||||
"wahwah",
|
||||
};
|
||||
|
||||
#ifdef HAVE_FILTERS_BUILTIN
|
||||
#define dspfilter_get_implementation wahwah_dspfilter_get_implementation
|
||||
#endif
|
||||
|
||||
const struct dspfilter_implementation *
|
||||
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
|
||||
{
|
||||
(void)mask;
|
||||
return &wahwah_plug;
|
||||
}
|
||||
|
||||
#undef dspfilter_get_implementation
|
||||
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal file
189
src/minarch/libretro-common/audio/resampler/audio_resampler.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (audio_resampler.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <file/config_file_userdata.h>
|
||||
|
||||
#include <audio/audio_resampler.h>
|
||||
|
||||
static void resampler_null_process(void *a, struct resampler_data *b) { }
|
||||
static void resampler_null_free(void *a) { }
|
||||
static void *resampler_null_init(const struct resampler_config *a, double b,
|
||||
enum resampler_quality c, resampler_simd_mask_t d) { return (void*)0; }
|
||||
|
||||
retro_resampler_t null_resampler = {
|
||||
resampler_null_init,
|
||||
resampler_null_process,
|
||||
resampler_null_free,
|
||||
RESAMPLER_API_VERSION,
|
||||
"null",
|
||||
"null"
|
||||
};
|
||||
|
||||
static const retro_resampler_t *resampler_drivers[] = {
|
||||
&sinc_resampler,
|
||||
#ifdef HAVE_CC_RESAMPLER
|
||||
&CC_resampler,
|
||||
#endif
|
||||
#ifdef HAVE_NEAREST_RESAMPLER
|
||||
&nearest_resampler,
|
||||
#endif
|
||||
&null_resampler,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct resampler_config resampler_config = {
|
||||
config_userdata_get_float,
|
||||
config_userdata_get_int,
|
||||
config_userdata_get_float_array,
|
||||
config_userdata_get_int_array,
|
||||
config_userdata_get_string,
|
||||
config_userdata_free,
|
||||
};
|
||||
|
||||
/**
|
||||
* find_resampler_driver_index:
|
||||
* @ident : Identifier of resampler driver to find.
|
||||
*
|
||||
* Finds resampler driver index by @ident name.
|
||||
*
|
||||
* Returns: resampler driver index if resampler driver was found, otherwise
|
||||
* -1.
|
||||
**/
|
||||
static int find_resampler_driver_index(const char *ident)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; resampler_drivers[i]; i++)
|
||||
if (string_is_equal_noncase(ident, resampler_drivers[i]->ident))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_resampler_driver:
|
||||
* @ident : Identifier of resampler driver to find.
|
||||
*
|
||||
* Finds resampler by @ident name.
|
||||
*
|
||||
* Returns: resampler driver if resampler driver was found, otherwise
|
||||
* NULL.
|
||||
**/
|
||||
static const retro_resampler_t *find_resampler_driver(const char *ident)
|
||||
{
|
||||
int i = find_resampler_driver_index(ident);
|
||||
|
||||
if (i >= 0)
|
||||
return resampler_drivers[i];
|
||||
|
||||
return resampler_drivers[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* resampler_append_plugs:
|
||||
* @re : Resampler handle
|
||||
* @backend : Resampler backend that is about to be set.
|
||||
* @bw_ratio : Bandwidth ratio.
|
||||
*
|
||||
* Initializes resampler driver based on queried CPU features.
|
||||
*
|
||||
* Returns: true (1) if successfully initialized, otherwise false (0).
|
||||
**/
|
||||
static bool resampler_append_plugs(void **re,
|
||||
const retro_resampler_t **backend,
|
||||
enum resampler_quality quality,
|
||||
double bw_ratio)
|
||||
{
|
||||
resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();
|
||||
|
||||
if (*backend)
|
||||
*re = (*backend)->init(&resampler_config, bw_ratio, quality, mask);
|
||||
|
||||
if (!*re)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_handle:
|
||||
* @idx : index of driver to get handle to.
|
||||
*
|
||||
* Returns: handle to audio resampler driver at index. Can be NULL
|
||||
* if nothing found.
|
||||
**/
|
||||
const void *audio_resampler_driver_find_handle(int idx)
|
||||
{
|
||||
const void *drv = resampler_drivers[idx];
|
||||
if (!drv)
|
||||
return NULL;
|
||||
return drv;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_resampler_driver_find_ident:
|
||||
* @idx : index of driver to get handle to.
|
||||
*
|
||||
* Returns: Human-readable identifier of audio resampler driver at index.
|
||||
* Can be NULL if nothing found.
|
||||
**/
|
||||
const char *audio_resampler_driver_find_ident(int idx)
|
||||
{
|
||||
const retro_resampler_t *drv = resampler_drivers[idx];
|
||||
if (!drv)
|
||||
return NULL;
|
||||
return drv->ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* retro_resampler_realloc:
|
||||
* @re : Resampler handle
|
||||
* @backend : Resampler backend that is about to be set.
|
||||
* @ident : Identifier name for resampler we want.
|
||||
* @bw_ratio : Bandwidth ratio.
|
||||
*
|
||||
* Reallocates resampler. Will free previous handle before
|
||||
* allocating a new one. If ident is NULL, first resampler will be used.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
|
||||
const char *ident, enum resampler_quality quality, double bw_ratio)
|
||||
{
|
||||
if (*re && *backend)
|
||||
(*backend)->free(*re);
|
||||
|
||||
*re = NULL;
|
||||
*backend = find_resampler_driver(ident);
|
||||
|
||||
if (!resampler_append_plugs(re, backend, quality, bw_ratio))
|
||||
{
|
||||
if (!*re)
|
||||
*backend = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (nearest_resampler.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <audio/audio_resampler.h>
|
||||
|
||||
typedef struct rarch_nearest_resampler
|
||||
{
|
||||
float fraction;
|
||||
} rarch_nearest_resampler_t;
|
||||
|
||||
static void resampler_nearest_process(
|
||||
void *re_, struct resampler_data *data)
|
||||
{
|
||||
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
|
||||
audio_frame_float_t *inp = (audio_frame_float_t*)data->data_in;
|
||||
audio_frame_float_t *inp_max = (audio_frame_float_t*)inp + data->input_frames;
|
||||
audio_frame_float_t *outp = (audio_frame_float_t*)data->data_out;
|
||||
float ratio = 1.0 / data->ratio;
|
||||
|
||||
while (inp != inp_max)
|
||||
{
|
||||
while (re->fraction > 1)
|
||||
{
|
||||
*outp++ = *inp;
|
||||
re->fraction -= ratio;
|
||||
}
|
||||
re->fraction++;
|
||||
inp++;
|
||||
}
|
||||
|
||||
data->output_frames = (outp - (audio_frame_float_t*)data->data_out);
|
||||
}
|
||||
|
||||
static void resampler_nearest_free(void *re_)
|
||||
{
|
||||
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
|
||||
if (re)
|
||||
free(re);
|
||||
}
|
||||
|
||||
static void *resampler_nearest_init(const struct resampler_config *config,
|
||||
double bandwidth_mod,
|
||||
enum resampler_quality quality,
|
||||
resampler_simd_mask_t mask)
|
||||
{
|
||||
rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)
|
||||
calloc(1, sizeof(rarch_nearest_resampler_t));
|
||||
if (!re)
|
||||
return NULL;
|
||||
re->fraction = 0;
|
||||
return re;
|
||||
}
|
||||
|
||||
retro_resampler_t nearest_resampler = {
|
||||
resampler_nearest_init,
|
||||
resampler_nearest_process,
|
||||
resampler_nearest_free,
|
||||
RESAMPLER_API_VERSION,
|
||||
"nearest",
|
||||
"nearest"
|
||||
};
|
||||
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
1025
src/minarch/libretro-common/audio/resampler/drivers/sinc_resampler.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,74 @@
|
|||
/* Copyright (C) 2010-2020 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (sinc_resampler_neon.S).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)
|
||||
|
||||
#ifndef __MACH__
|
||||
.arm
|
||||
#endif
|
||||
.align 4
|
||||
.globl process_sinc_neon_asm
|
||||
#ifndef __MACH__
|
||||
.type process_sinc_neon_asm, %function
|
||||
#endif
|
||||
.globl _process_sinc_neon_asm
|
||||
#ifndef __MACH__
|
||||
.type _process_sinc_neon_asm, %function
|
||||
#endif
|
||||
# void process_sinc_neon(float *out, const float *left, const float *right, const float *coeff, unsigned taps)
|
||||
# Assumes taps is >= 8, and a multiple of 8.
|
||||
process_sinc_neon_asm:
|
||||
_process_sinc_neon_asm:
|
||||
|
||||
push {r4, lr}
|
||||
vmov.f32 q0, #0.0
|
||||
vmov.f32 q8, #0.0
|
||||
|
||||
# Taps argument (r4) goes on stack in armeabi.
|
||||
ldr r4, [sp, #8]
|
||||
|
||||
1:
|
||||
# Left
|
||||
vld1.f32 {q2-q3}, [r1]!
|
||||
# Right
|
||||
vld1.f32 {q10-q11}, [r2]!
|
||||
# Coeff
|
||||
vld1.f32 {q12-q13}, [r3, :128]!
|
||||
|
||||
# Left / Right
|
||||
vmla.f32 q0, q2, q12
|
||||
vmla.f32 q8, q10, q12
|
||||
vmla.f32 q0, q3, q13
|
||||
vmla.f32 q8, q11, q13
|
||||
|
||||
subs r4, r4, #8
|
||||
bne 1b
|
||||
|
||||
# Add everything together
|
||||
vadd.f32 d0, d0, d1
|
||||
vadd.f32 d16, d16, d17
|
||||
vpadd.f32 d0, d0, d16
|
||||
vst1.f32 d0, [r0]
|
||||
|
||||
pop {r4, pc}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue