blob: 4a0903dd6f1fa672b3db0bbbea38d032c5caf28b [file] [log] [blame]
/*
* Bandpass filter sweep effect
* Copyright (c) Maarten de Boer <mdeboer@iua.upf.es>
*
* 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 <math.h>
#include <alsa/asoundlib.h>
struct effect_private {
/* filter the sweep variables */
float lfo,dlfo,fs,fc,BW,C,D,a0,a1,a2,b1,b2,*x[3],*y[3];
float lfo_depth, lfo_center;
unsigned int channels;
};
static int effect_init(struct lookback *loopback,
void *private_data,
snd_pcm_access_t access,
unsigned int channels,
unsigned int rate,
snd_pcm_format_t format)
{
struct effect_private *priv = private_data;
int i;
#if __BYTE_ORDER == __LITTLE_ENDIAN
if (format != SND_PCM_FORMAT_S16_LE)
return -EIO;
#elif __BYTE_ORDER == __BIG_ENDIAN
if (format != SND_PCM_FORMAT_S16_BE)
return -EIO;
#else
return -EIO;
#endif
priv->fs = (float) rate;
priv->channels = channels;
for (i = 0; i < 3; i++) {
priv->x[i] = calloc(channels * sizeof(float));
priv->y[i] = calloc(channels * sizeof(float));
}
return 0;
}
static int effect_done(struct loopback *loopback,
void *private_data)
{
struct effect_private *priv = private_data;
int i;
for (i = 0; i < 3; i++) {
free(priv->x[i]);
free(priv->y[i]);
}
return 0;
}
static int effect_apply(struct loopback *loopback,
void *private_data,
const snd_pcm_channel_area_t *areas,
snd_uframes_t offset,
snd_uframes_t frames)
{
struct effect_private *priv = private_data;
short *samples = (short*)areas[0].addr + offset*priv->channels;
snd_uframes_t i;
for (i=0; i < frames; i++) {
int chn;
fc = sin(priv->lfo)*priv->lfo_depth+priv->lfo_center;
priv->lfo += priv->dlfo;
if (priv->lfo>2.*M_PI) priv->lfo -= 2.*M_PI;
priv->C = 1./tan(M_PI*priv->BW/priv->fs);
priv->D = 2.*cos(2*M_PI*fc/fs);
priv->a0 = 1./(1.+priv->C);
priv->a1 = 0;
priv->a2 = -priv->a0;
priv->b1 = -priv->C*priv->D*a0;
priv->b2 = (priv->C-1)*priv->a0;
for (chn=0; chn < priv->channels; chn++)
{
priv->x[chn][2] = priv->x[chn][1];
priv->x[chn][1] = priv->x[chn][0];
priv->y[chn][2] = priv->y[chn][1];
priv->y[chn][1] = priv->y[chn][0];
priv->x[chn][0] = samples[i*channels+chn];
priv->y[chn][0] = priv->a0*priv->x[0][chn]
+ priv->a1*priv->x[1][chn] + priv->a2*x[2][chn]
- priv->b1*priv->y[1][chn] - priv->b2*y[2][chn];
samples[i*channels+chn] = priv->y[chn][0];
}
}
return 0;
}
void effect_init_sweep(void)
{
struct effect_private *priv;
priv = register_effect(effect_init,
effect_apply,
effect_done,
sizeof(struct effectprivate));
if (priv) {
priv->lfo_center = 2000.;
priv->lfo_depth = 1800.;
priv->lfo_freq = 0.2;
priv->BW = 50;
}
}