diff --git a/audio/filters/Makefile b/audio/filters/Makefile index 5d2eddf639..2495bb2b13 100644 --- a/audio/filters/Makefile +++ b/audio/filters/Makefile @@ -68,7 +68,7 @@ ASMFLAGS := -INEON/asm asflags += -mfpu=neon endif -objects += eq.$(DYLIB) iir.$(DYLIB) phaser.$(DYLIB) reverb.$(DYLIB) volume.$(DYLIB) wah.$(DYLIB) +objects += echo.$(DYLIB) eq.$(DYLIB) iir.$(DYLIB) phaser.$(DYLIB) reverb.$(DYLIB) volume.$(DYLIB) wah.$(DYLIB) all: build; diff --git a/audio/filters/echo.c b/audio/filters/echo.c new file mode 100644 index 0000000000..87c4138c4f --- /dev/null +++ b/audio/filters/echo.c @@ -0,0 +1,190 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2014 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include "rarch_dsp.h" + +// 4 source echo. + +#ifdef __GNUC__ +#define ALIGNED __attribute__((aligned(16))) +#else +#define ALIGNED +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +struct echo_filter +{ + float *history; // history buffer + int pos; // current position in history buffer + int amp; // amplification of echoes (0-256) + int delay; // delay in number of samples + int ms; // delay in miliseconds + int rate; // sample rate + + float f_amp; // amplification (0-1) + float input_rate; +}; + +struct echo_filter_data +{ + struct echo_filter echo_l; + struct echo_filter echo_r; + float buf[4096]; +}; + +#ifdef RARCH_INTERNAL +#define rarch_dsp_plugin_init echo_dsp_plugin_init +#endif + +static float echo_process(void *data, float in) +{ + struct echo_filter *echo = (struct echo_filter*)data; + + float smp = echo->history[echo->pos]; + smp *= echo->f_amp; + smp += in; + echo->history[echo->pos] = smp; + echo->pos = (echo->pos + 1) % echo->delay; + return smp; +} + +static void echo_dsp_process(void *data, rarch_dsp_output_t *output, + const rarch_dsp_input_t *input) +{ + int num_samples, i; + struct echo_filter_data *echo = (struct echo_filter_data*)data; + output->samples = echo->buf; + num_samples = input->frames * 2; + + for (i = 0; i < num_samples;) + { + echo->buf[i] = echo_process(&echo->echo_l, input->samples[i]); + i++; + echo->buf[i] = echo_process(&echo->echo_r, input->samples[i]); + i++; + } + output->frames = input->frames; +} + +static void echo_dsp_free(void *data) +{ + struct echo_filter_data *echo = (struct echo_filter_data*)data; + + if (echo->echo_l.history) + free(echo->echo_l.history); + if (echo->echo_r.history) + free(echo->echo_r.history); + + if (echo) + free(echo); +} + +static void echo_set_delay(void *data, int ms) +{ + int new_delay, how_much, i; + float *new_history; + struct echo_filter *echo = (struct echo_filter*)data; + + new_delay = ms * echo->input_rate / 1000; + if (new_delay == 0) + new_delay = 1; + + new_history = (float*)malloc(new_delay * sizeof(float)); + memset(new_history, 0, new_delay * sizeof(float)); + + if (echo->history) + { + how_much = echo->delay - echo->pos; + how_much = min(how_much, new_delay); + memcpy(new_history, echo->history + echo->pos, how_much * sizeof(float)); + + if (how_much < new_delay) + { + i = how_much; + how_much = new_delay - how_much; + how_much = min(how_much, echo->delay); + how_much = min(how_much, echo->pos); + memcpy(new_history + i, echo->history, how_much * sizeof(float)); + } + + if (echo->history) + free(echo->history); + } + echo->history = new_history; + echo->pos = 0; + echo->delay = new_delay; + echo->ms = ms; +} + +static void *echo_dsp_init(const rarch_dsp_info_t *info) +{ + struct echo_filter_data *echo = (struct echo_filter_data*)calloc(1, sizeof(*echo));; + + if (!echo) + return NULL; + + echo->echo_l.history = NULL; + echo->echo_l.input_rate = info->input_rate; + echo_set_delay(&echo->echo_l, 200); + echo->echo_l.amp = 128; + echo->echo_l.f_amp = (float)echo->echo_l.amp / 256.0f; + echo->echo_l.pos = 0; + + echo->echo_r.history = NULL; + echo->echo_r.input_rate = info->input_rate; + echo_set_delay(&echo->echo_r, 200); + echo->echo_r.amp = 128; + echo->echo_r.f_amp = (float)echo->echo_r.amp / 256.0f; + echo->echo_r.pos = 0; + + fprintf(stderr, "[Echo] loaded!\n"); + + return echo; +} + +static void echo_dsp_config(void *data) +{ + (void)data; +} + +static const rarch_dsp_plugin_t dsp_plug = { + echo_dsp_init, + echo_dsp_process, + echo_dsp_free, + RARCH_DSP_API_VERSION, + echo_dsp_config, + "Echo", + NULL +}; + +const rarch_dsp_plugin_t *rarch_dsp_plugin_init(void) +{ + return &dsp_plug; +} + +#ifdef RARCH_INTERNAL +#undef rarch_dsp_plugin_init +#endif diff --git a/audio/filters/echo_sse.c b/audio/filters/echo_sse.c deleted file mode 100644 index 35c3beeb0d..0000000000 --- a/audio/filters/echo_sse.c +++ /dev/null @@ -1,213 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2014 - Daniel De Matteis - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include "rarch_dsp.h" - -#include - -// 4 source echo. - -#ifdef __GNUC__ -#define ALIGNED __attribute__((aligned(16))) -#else -#define ALIGNED -#endif - -#define ECHO_MS 150 -#define AMP 0.0 - -struct echo_filter_data -{ - float echo_buffer[4][0x10000] ALIGNED; - float buffer[4096] ALIGNED; - float scratch_buf[4] ALIGNED; - - unsigned buf_size[4]; - unsigned ptr[4]; - - unsigned scratch_ptr; - __m128 amp[4] ALIGNED; - __m128 feedback ALIGNED; - float input_rate; -}; - -#ifdef RARCH_INTERNAL -#define rarch_dsp_plugin_init echo_dsp_plugin_init -#endif - -static void echo_init(void *data) -{ - unsigned i, j; - struct echo_filter_data *echo = (struct echo_filter_data*)data; - - for (i = 0; i < 4; i++) - { - echo->ptr[i] = 0.0f; - echo->amp[i] = _mm_set1_ps(AMP); - } - - echo->scratch_ptr = 0; - echo->feedback = _mm_set1_ps(0.0f); - echo->input_rate = 32000.0; - - for (i = 0; i < 4; i++) - { - echo->scratch_buf[i] = 0.0f; - - for (j = 0; j < 0x10000; j++) - echo->echo_buffer[i][j] = 0.0f; - } - - for (i = 0; i < 4096; i++) - echo->buffer[i] = 0.0f; -} - -static unsigned echo_sse2_process(void *data, const float *input, unsigned frames) -{ - unsigned frames_out, i; - float *buffer_out; - struct echo_filter_data *echo = (struct echo_filter_data*)data; - - frames_out = 0; - buffer_out = echo->buffer; - - __m128 amp[4] = { - echo->amp[0], - echo->amp[1], - echo->amp[2], - echo->amp[3], - }; - - __m128 feedback = echo->feedback; - -#define DO_FILTER() \ - __m128 result[4]; \ - __m128 echo_[4]; \ - for (i = 0; i < 4; i++) \ - { \ - echo_[i] = _mm_load_ps(echo->echo_buffer[i] + echo->ptr[i]); \ - result[i] = _mm_mul_ps(amp[i], echo_[i]); \ - } \ - __m128 final_result = _mm_add_ps(_mm_add_ps(result[0], result[1]), _mm_add_ps(result[2], result[3])); \ - __m128 feedback_result = _mm_mul_ps(feedback, final_result); \ - final_result = _mm_add_ps(reg, final_result); \ - feedback_result = _mm_add_ps(reg, feedback_result); \ - for (i = 0; i < 4; i++) \ - _mm_store_ps(echo->echo_buffer[i] + echo->ptr[i], feedback_result); \ - _mm_store_ps(buffer_out, final_result); \ - for (i = 0; i < 4; i++) \ - echo->ptr[i] = (echo->ptr[i] + 4) % echo->buf_size[i] - - - // Fill up scratch buffer and flush. - if (echo->scratch_ptr) - { - for (i = echo->scratch_ptr; i < 4; i += 2) - { - echo->scratch_buf[i] = *input++; - echo->scratch_buf[i + 1] = *input++; - frames--; - } - - echo->scratch_ptr = 0; - - __m128 reg = _mm_load_ps(echo->scratch_buf); - - DO_FILTER(); - - frames_out += 2; - buffer_out += 4; - } - - // Main processing. - for (i = 0; (i + 4) <= (frames * 2); i += 4, input += 4, buffer_out += 4, frames_out += 2) - { - __m128 reg = _mm_loadu_ps(input); // Might not be aligned. - DO_FILTER(); - } - - // Flush rest to scratch buffer. - for (; i < (frames * 2); i++) - echo->scratch_buf[echo->scratch_ptr++] = *input++; - - return frames_out; -} - -static void echo_sse_dsp_process(void *data, rarch_dsp_output_t *output, - const rarch_dsp_input_t *input) -{ - struct echo_filter_data *echo = (struct echo_filter_data*)data; - output->samples = echo->buffer; - output->frames = echo_sse2_process(echo, input->samples, input->frames); -} - -static void echo_sse_dsp_free(void *data) -{ - struct echo_filter_data *echo = (struct echo_filter_data*)data; - - if (echo) - free(echo); -} - -static void *echo_sse_dsp_init(const rarch_dsp_info_t *info) -{ - struct echo_filter_data *echo = (struct echo_filter_data*)calloc(1, sizeof(*echo));; - - if (!echo) - return NULL; - - for (unsigned i = 0; i < 4; i++) - echo->buf_size[i] = ECHO_MS * (info->input_rate * 2) / 1000; - - echo_init(echo); - - echo->input_rate = info->input_rate; - - fprintf(stderr, "[Echo] loaded!\n"); - - return echo; -} - -static void echo_sse_dsp_config(void *data) -{ - (void)data; -} - -static const rarch_dsp_plugin_t dsp_plug = { - echo_sse_dsp_init, - echo_sse_dsp_process, - echo_sse_dsp_free, - RARCH_DSP_API_VERSION, - echo_sse_dsp_config, - "Echo (SSE2)", - NULL -}; - -const rarch_dsp_plugin_t *rarch_dsp_plugin_init(void) -{ - return &dsp_plug; -} - -#ifdef RARCH_INTERNAL -#undef rarch_dsp_plugin_init -#endif