build: Merge submodules into repo

This commit is contained in:
robshape 2023-04-15 10:36:01 +02:00
parent 540a30f719
commit 4c64279f90
422 changed files with 106715 additions and 8 deletions

View 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;
}

File diff suppressed because it is too large Load diff

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View 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;
}

View 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"

View file

@ -0,0 +1,7 @@
filters = 1
filter0 = iir
iir_frequency = 8600.0
iir_quality = 0.707
iir_gain = 6.0
iir_type = LPF

View file

@ -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"

View 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

View file

@ -0,0 +1,4 @@
filters = 1
filter0 = crystalizer
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
crystalizer_intensity = 5.0

View 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

View 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"

View 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

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = iir
iir_gain = -12.0
iir_type = HSH
iir_frequency = 8000.0

View 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

View 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

View 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

View 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"

View 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"

View 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

View 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

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = tremolo
# Defaults.
#tremolo_frequency = 4.0
#tremolo_depth = 0.9

View file

@ -0,0 +1,6 @@
filters = 1
filter0 = vibrato
# Defaults.
#vibrato_frequency = 5.0
#vibrato_depth = 0.5

View 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

View 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

View file

@ -0,0 +1,3 @@
#!/bin/sh
PACKAGE_NAME=retroarch-filters-audio

View 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

View 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

View 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

View 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);
}

View 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

View 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

View file

@ -0,0 +1,4 @@
{
global: dspfilter_get_implementation;
local: *;
};

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View file

@ -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"
};

File diff suppressed because it is too large Load diff

View file

@ -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