summaryrefslogtreecommitdiff
path: root/src/player/equalizer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/player/equalizer.c')
-rw-r--r--src/player/equalizer.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/player/equalizer.c b/src/player/equalizer.c
new file mode 100644
index 0000000..feecef4
--- /dev/null
+++ b/src/player/equalizer.c
@@ -0,0 +1,255 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * This program 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 Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sndfile.h"
+#include "cmixer.h"
+#include <math.h>
+
+
+#define EQ_BANDWIDTH 2.0
+#define EQ_ZERO 0.000001
+
+
+
+typedef struct {
+ float a0, a1, a2, b1, b2;
+ float x1, x2, y1, y2;
+ float gain, center_frequency;
+ int enabled;
+} eq_band;
+
+
+
+//static REAL f2ic = (REAL)(1 << 28);
+//static REAL i2fc = (REAL)(1.0 / (1 << 28));
+
+static eq_band eq[MAX_EQ_BANDS * 2] =
+{
+ // Default: Flat EQ
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 120, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 600, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1200, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 120, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 600, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1200, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6000, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10000, 0},
+};
+
+
+static void eq_filter(eq_band *pbs, float *pbuffer, unsigned int count)
+{
+ for (unsigned int i = 0; i < count; i++) {
+ float x = pbuffer[i];
+ float y = pbs->a1 * pbs->x1 +
+ pbs->a2 * pbs->x2 +
+ pbs->a0 * x +
+ pbs->b1 * pbs->y1 +
+ pbs->b2 * pbs->y2;
+
+ pbs->x2 = pbs->x1;
+ pbs->y2 = pbs->y1;
+ pbs->x1 = x;
+ pbuffer[i] = y;
+ pbs->y1 = y;
+ }
+}
+
+
+void eq_mono(song_t *csf, int *buffer, unsigned int count)
+{
+ mono_mix_to_float(buffer, csf->mix_buffer_float, count);
+
+ for (unsigned int b = 0; b < MAX_EQ_BANDS; b++)
+ {
+ if (eq[b].enabled && eq[b].gain != 1.0f)
+ eq_filter(&eq[b], csf->mix_buffer_float, count);
+ }
+
+ float_to_mono_mix(csf->mix_buffer_float, buffer, count);
+}
+
+
+// XXX: I rolled the two loops into one. Make sure this works.
+void eq_stereo(song_t *csf, int *buffer, unsigned int count)
+{
+ stereo_mix_to_float(buffer, csf->mix_buffer_float, csf->mix_buffer_float + MIXBUFFERSIZE, count);
+
+ for (unsigned int b = 0; b < MAX_EQ_BANDS; b++) {
+ int br = b + MAX_EQ_BANDS;
+
+ // Left band
+ if (eq[b].enabled && eq[b].gain != 1.0f)
+ eq_filter(&eq[b], csf->mix_buffer_float, count);
+
+ // Right band
+ if (eq[br].enabled && eq[br].gain != 1.0f)
+ eq_filter(&eq[br], csf->mix_buffer_float + MIXBUFFERSIZE, count);
+ }
+
+ float_to_stereo_mix(csf->mix_buffer_float, csf->mix_buffer_float + MIXBUFFERSIZE, buffer, count);
+}
+
+
+void initialize_eq(int reset, float freq)
+{
+ //float fMixingFreq = (REAL)mix_frequency;
+
+ // Gain = 0.5 (-6dB) .. 2 (+6dB)
+ for (unsigned int band = 0; band < MAX_EQ_BANDS * 2; band++) {
+ float k, k2, r, f;
+ float v0, v1;
+ int b = reset;
+
+ if (!eq[band].enabled) {
+ eq[band].a0 = 0;
+ eq[band].a1 = 0;
+ eq[band].a2 = 0;
+ eq[band].b1 = 0;
+ eq[band].b2 = 0;
+ eq[band].x1 = 0;
+ eq[band].x2 = 0;
+ eq[band].y1 = 0;
+ eq[band].y2 = 0;
+ continue;
+ }
+
+ f = eq[band].center_frequency / freq;
+
+ if (f > 0.45f)
+ eq[band].gain = 1;
+
+ //if (f > 0.25)
+ // f = 0.25;
+
+ //k = tan(PI * f);
+
+ k = f * 3.141592654f;
+ k = k + k * f;
+
+ //if (k > (float) 0.707)
+ // k = (float) 0.707;
+
+ k2 = k*k;
+ v0 = eq[band].gain;
+ v1 = 1;
+
+ if (eq[band].gain < 1.0) {
+ v0 *= 0.5f / EQ_BANDWIDTH;
+ v1 *= 0.5f / EQ_BANDWIDTH;
+ }
+ else {
+ v0 *= 1.0f / EQ_BANDWIDTH;
+ v1 *= 1.0f / EQ_BANDWIDTH;
+ }
+
+ r = (1 + v0 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a0) {
+ eq[band].a0 = r;
+ b = 1;
+ }
+
+ r = 2 * (k2 - 1) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a1) {
+ eq[band].a1 = r;
+ b = 1;
+ }
+
+ r = (1 - v0 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].a2) {
+ eq[band].a2 = r;
+ b = 1;
+ }
+
+ r = -2 * (k2 - 1) / (1 + v1 * k + k2);
+
+ if (r != eq[band].b1) {
+ eq[band].b1 = r;
+ b = 1;
+ }
+
+ r = -(1 - v1 * k + k2) / (1 + v1 * k + k2);
+
+ if (r != eq[band].b2) {
+ eq[band].b2 = r;
+ b = 1;
+ }
+
+ if (b) {
+ eq[band].x1 = 0;
+ eq[band].x2 = 0;
+ eq[band].y1 = 0;
+ eq[band].y2 = 0;
+ }
+ }
+}
+
+
+void set_eq_gains(const unsigned int *gainbuff, unsigned int gains, const unsigned int *freqs,
+ int reset, int mix_freq)
+{
+ for (unsigned int i = 0; i < MAX_EQ_BANDS; i++) {
+ float g, f = 0;
+
+ if (i < gains) {
+ unsigned int n = gainbuff[i];
+
+ //if (n > 32)
+ // n = 32;
+
+ g = 1.0 + (((double) n) / 64.0);
+
+ if (freqs)
+ f = (float)(int) freqs[i];
+ }
+ else {
+ g = 1;
+ }
+
+ eq[i].gain =
+ eq[i + MAX_EQ_BANDS].gain = g;
+ eq[i].center_frequency =
+ eq[i + MAX_EQ_BANDS].center_frequency = f;
+
+ /* don't enable bands outside... */
+ if (f > 20.0f &&
+ i < gains) {
+ eq[i].enabled =
+ eq[i + MAX_EQ_BANDS].enabled = 1;
+ }
+ else {
+ eq[i].enabled =
+ eq[i + MAX_EQ_BANDS].enabled = 0;
+ }
+ }
+
+ initialize_eq(reset, mix_freq);
+}
+
© All Rights Reserved