Browse Source

Add chorus and flange effects and an example sketch for each

dds
Pete (El Supremo) 10 years ago
parent
commit
0b64ca0e24
6 changed files with 1140 additions and 0 deletions
  1. +412
    -0
      Audio.cpp
  2. +63
    -0
      Audio.h
  3. +264
    -0
      examples/effect_chorus/effect_chorus.ino
  4. +43
    -0
      examples/effect_chorus/effects_info.h
  5. +314
    -0
      examples/effect_flange/effect_flange.ino
  6. +44
    -0
      examples/effect_flange/effects_info.h

+ 412
- 0
Audio.cpp View File

@@ -3148,3 +3148,415 @@ void AudioFilterFIR::update(void)
if(b_new)release(b_new);
}


/******************************************************************/
// A u d i o E f f e c t F l a n g e
// Written by Pete (El Supremo) Jan 2014

// circular addressing indices for left and right channels
short AudioEffectFlange::l_circ_idx;
short AudioEffectFlange::r_circ_idx;

short * AudioEffectFlange::l_delayline = NULL;
short * AudioEffectFlange::r_delayline = NULL;

// User-supplied offset for the delayed sample
// but start with passthru
int AudioEffectFlange::delay_offset_idx = DELAY_PASSTHRU;
int AudioEffectFlange::delay_length;

int AudioEffectFlange::delay_depth;
int AudioEffectFlange::delay_rate_incr;
unsigned int AudioEffectFlange::l_delay_rate_index;
unsigned int AudioEffectFlange::r_delay_rate_index;
// fails if the user provides unreasonable values but will
// coerce them and go ahead anyway. e.g. if the delay offset
// is >= CHORUS_DELAY_LENGTH, the code will force it to
// CHORUS_DELAY_LENGTH-1 and return false.
// delay_rate is the rate (in Hz) of the sine wave modulation
// delay_depth is the maximum variation around delay_offset
// i.e. the total offset is delay_offset + delay_depth * sin(delay_rate)
boolean AudioEffectFlange::begin(short *delayline,int d_length,int delay_offset,int d_depth,float delay_rate)
{
boolean all_ok = true;

Serial.print("AudioEffectFlange.begin(ofsset = ");
Serial.print(delay_offset);
Serial.print(", depth = ");
Serial.print(d_depth);
Serial.print(", rate = ");
Serial.print(delay_rate,3);
Serial.println(")");
Serial.print(" CHORUS_DELAY_LENGTH = ");
Serial.println(d_length);

delay_length = d_length/2;
l_delayline = delayline;
r_delayline = delayline + delay_length;
delay_depth = d_depth;
// initial index
l_delay_rate_index = 0;
r_delay_rate_index = 0;
l_circ_idx = 0;
r_circ_idx = 0;
delay_rate_incr = 2*PI*delay_rate/44100.*2147483648.;
//Serial.println(delay_rate_incr,HEX);

delay_offset_idx = delay_offset;
// Allow the passthru code to go through
if(delay_offset_idx < -1) {
delay_offset_idx = 0;
all_ok = false;
}
if(delay_offset_idx >= delay_length) {
delay_offset_idx = delay_length - 1;
all_ok = false;
}
return(all_ok);
}


boolean AudioEffectFlange::modify(int delay_offset,int d_depth,float delay_rate)
{
boolean all_ok = true;
delay_depth = d_depth;

delay_rate_incr = 2*PI*delay_rate/44100.*2147483648.;
delay_offset_idx = delay_offset;
// Allow the passthru code to go through
if(delay_offset_idx < -1) {
delay_offset_idx = 0;
all_ok = false;
}
if(delay_offset_idx >= delay_length) {
delay_offset_idx = delay_length - 1;
all_ok = false;
}
l_delay_rate_index = 0;
r_delay_rate_index = 0;
l_circ_idx = 0;
r_circ_idx = 0;
return(all_ok);
}

void AudioEffectFlange::update(void)
{
audio_block_t *block;
int idx;
short *bp;
short frac;
int idx1;

if(l_delayline == NULL)return;
if(r_delayline == NULL)return;

// do passthru
if(delay_offset_idx == DELAY_PASSTHRU) {
// Just passthrough
block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp++;
}
transmit(block,0);
release(block);
}
block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp++;
}
transmit(block,1);
release(block);
}
return;
}

// L E F T C H A N N E L

block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp;
idx = arm_sin_q15( (q15_t)((l_delay_rate_index >> 16) & 0x7fff));
idx = (idx * delay_depth) >> 15;
//Serial.println(idx);
idx = l_circ_idx - (delay_offset_idx + idx);
if(idx < 0) {
idx += delay_length;
}
if(idx >= delay_length) {
idx -= delay_length;
}

if(frac < 0)
idx1 = idx - 1;
else
idx1 = idx + 1;
if(idx1 < 0) {
idx1 += delay_length;
}
if(idx1 >= delay_length) {
idx1 -= delay_length;
}
frac = (l_delay_rate_index >> 1) &0x7fff;
frac = (( (int)(l_delayline[idx1] - l_delayline[idx])*frac) >> 15);

//frac = 0;
*bp++ = (l_delayline[l_circ_idx]
+ l_delayline[idx] + frac
// + l_delayline[(l_circ_idx + delay_length/2) % delay_length]
)/2;

l_delay_rate_index += delay_rate_incr;
if(l_delay_rate_index & 0x80000000) {
l_delay_rate_index &= 0x7fffffff;
}
}
// send the effect output to the left channel
transmit(block,0);
release(block);
}

// R I G H T C H A N N E L

block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp;
idx = arm_sin_q15( (q15_t)((r_delay_rate_index >> 16)&0x7fff));
idx = (idx * delay_depth) >> 15;

idx = r_circ_idx - (delay_offset_idx + idx);
if(idx < 0) {
idx += delay_length;
}
if(idx >= delay_length) {
idx -= delay_length;
}

if(frac < 0)
idx1 = idx - 1;
else
idx1 = idx + 1;
if(idx1 < 0) {
idx1 += delay_length;
}
if(idx1 >= delay_length) {
idx1 -= delay_length;
}
frac = (r_delay_rate_index >> 1) &0x7fff;
frac = (( (int)(r_delayline[idx1] - r_delayline[idx])*frac) >> 15);

//frac = 0;

*bp++ = (r_delayline[r_circ_idx]
+ r_delayline[idx] + frac
)/2;

r_delay_rate_index += delay_rate_incr;
if(r_delay_rate_index & 0x80000000) {
r_delay_rate_index &= 0x7fffffff;
}

}
// send the effect output to the right channel
transmit(block,1);
release(block);
}
}



/******************************************************************/

// A u d i o E f f e c t C h o r u s
// Written by Pete (El Supremo) Jan 2014

// circular addressing indices for left and right channels
short AudioEffectChorus::l_circ_idx;
short AudioEffectChorus::r_circ_idx;

short * AudioEffectChorus::l_delayline = NULL;
short * AudioEffectChorus::r_delayline = NULL;
int AudioEffectChorus::delay_length;
// An initial value of zero indicates passthru
int AudioEffectChorus::num_chorus = 0;


// All three must be valid.
boolean AudioEffectChorus::begin(short *delayline,int d_length,int n_chorus)
{
Serial.print("AudioEffectChorus.begin(Chorus delay line length = ");
Serial.print(d_length);
Serial.print(", n_chorus = ");
Serial.print(n_chorus);
Serial.println(")");

l_delayline = NULL;
r_delayline = NULL;
delay_length = 0;
l_circ_idx = 0;
r_circ_idx = 0;

if(delayline == NULL) {
return(false);
}
if(d_length < 10) {
return(false);
}
if(n_chorus < 1) {
return(false);
}
l_delayline = delayline;
r_delayline = delayline + d_length/2;
delay_length = d_length/2;
num_chorus = n_chorus;
return(true);
}

// This has the same effect as begin(NULL,0);
void AudioEffectChorus::stop(void)
{

}

void AudioEffectChorus::modify(int n_chorus)
{
num_chorus = n_chorus;
}

int iabs(int x)
{
if(x < 0)return(-x);
return(x);
}
//static int d_count = 0;

int last_idx = 0;
void AudioEffectChorus::update(void)
{
audio_block_t *block;
short *bp;
int sum;
int c_idx;

if(l_delayline == NULL)return;
if(r_delayline == NULL)return;
// do passthru
// It stores the unmodified data in the delay line so that
// it isn't as likely to click
if(num_chorus < 1) {
// Just passthrough
block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp++;
}
transmit(block,0);
release(block);
}
block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp++;
}
transmit(block,1);
release(block);
}
return;
}

// L E F T C H A N N E L

block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp;
sum = 0;
c_idx = l_circ_idx;
for(int k = 0; k < num_chorus; k++) {
sum += l_delayline[c_idx];
if(num_chorus > 1)c_idx -= delay_length/(num_chorus - 1) - 1;
if(c_idx < 0) {
c_idx += delay_length;
}
}
*bp++ = sum/num_chorus;
}

// send the effect output to the left channel
transmit(block,0);
release(block);
}

// R I G H T C H A N N E L

block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp;
sum = 0;
c_idx = r_circ_idx;
for(int k = 0; k < num_chorus; k++) {
sum += r_delayline[c_idx];
if(num_chorus > 1)c_idx -= delay_length/(num_chorus - 1) - 1;
if(c_idx < 0) {
c_idx += delay_length;
}
}
*bp++ = sum/num_chorus;
}

// send the effect output to the left channel
transmit(block,1);
release(block);
}
}


+ 63
- 0
Audio.h View File

@@ -576,6 +576,69 @@ private:



/******************************************************************/
// A u d i o E f f e c t F l a n g e
// Written by Pete (El Supremo) Jan 2014

#define DELAY_PASSTHRU 0

class AudioEffectFlange :
public AudioStream
{
public:
AudioEffectFlange(void):
AudioStream(2,inputQueueArray) {
}

boolean begin(short *delayline,int d_length,int delay_offset,int d_depth,float delay_rate);
boolean modify(int delay_offset,int d_depth,float delay_rate);
virtual void update(void);
void stop(void);
private:
audio_block_t *inputQueueArray[2];
static short *l_delayline;
static short *r_delayline;
static int delay_length;
static short l_circ_idx;
static short r_circ_idx;
static int delay_depth;
static int delay_offset_idx;
static int delay_rate_incr;
static unsigned int l_delay_rate_index;
static unsigned int r_delay_rate_index;
};


/******************************************************************/

// A u d i o E f f e c t C h o r u s
// Written by Pete (El Supremo) Jan 2014

#define DELAY_PASSTHRU -1

class AudioEffectChorus :
public AudioStream
{
public:
AudioEffectChorus(void):
AudioStream(2,inputQueueArray) {
}

boolean begin(short *delayline,int delay_length,int n_chorus);
virtual void update(void);
void stop(void);
void modify(int n_chorus);
private:
audio_block_t *inputQueueArray[2];
static short *l_delayline;
static short *r_delayline;
static short l_circ_idx;
static short r_circ_idx;
static int num_chorus;
static int delay_length;
};




+ 264
- 0
examples/effect_chorus/effect_chorus.ino View File

@@ -0,0 +1,264 @@
/*
PROC/MEM 9/4
Modify filter_test_f to try out a chorus effect.
TODO:


140203
o
I have mixed up the names "chorus" and flange". The sketches named
chorus have, up to version 'm', actually implemented a flanger.
From version 'o' onwards the sketches named chorus will implement
a real chorus effect and flange will do a flanging effect.

n only changed the effect parameters
m

140201
l - found the problem at last using my_flange_cd_usd_c
YES! Way back when I found I hadn't been using d_depth. Now I
have just discovered that I haven't been using delay_offset!!!!!
140201
k
interpolation doesn't remove the ticking
140131
j
>>> The lower the frequency, the less ticking.
- try interpolation
140201
- got this restored to the way it was last night
and then reinstated the changes. I had a couple of
changes to the right channel that were incorrect or
weren't carried over from the left channel changes
i
- don't know why but this version seems to have more "presence"
than previous versions.
The presence occurred when "sign = 1" was put in front of the left.
It essentially makes it a passthrough.
If both have "sign=1" then it is identical to passthrough
Ticking is still not fixed
h
- add sign reversal. It seems to make audio much clearer
BUT it hasn't got rid of the ticking noise
g
- I wasn't even using delay_depth!!!!
140131
f
- added code to print the processor and memory usage every 5 seconds
NOW the problem is to try to remove the ticking

e
FOUND the problem with the right channel. It was also in the left channel
but the placement of the delay line arrays made it more noticeable in the
right channel. I was not calculating idx properly. In particular, the
resuling index could be negative.

I have shortened the delay line to only 2*AUDIO_BLOCK_SAMPLES

- removed redundancies in the update code. rewrite the block
instead of getting a new one
Haven't solved the noise in the right channel yet.
Tried duplicating right channel code to left but noise stays on the right

140130
d
The noise stays in the right channel even if it is calculated first
Switching the L/R inputs doesn't switch the noise to the left channel
c
>> Now add a sinusoidal modulation to the offset
There's an awful noise in both channels but it is much louder in
the right channel. NOPE - it is ONLY in the right channel
but the audio does sound like it is working (sort of) except that it
is rather tinny. Maybe it needs to have the interpolation added.

b
- this works with clip16_6s.wav.
The original of this audio file was from http://www.donreiman.com/Chorus/Chorus.htm
I reworked it with Goldwave to make it a stereo WAV file
But with Rick Wakewan's Jane Seymour it seems to act more like
a high-pass filter.
a
- removed FIR stuff and changed the name to AudioEffectChorus
it's basically a blank template and compiles.

From: http://www.cs.cf.ac.uk/Dave/CM0268/PDF/10_CM0268_Audio_FX.pdf
about Comb filter effects
Effect Delay range (ms) Modulation
Resonator 0 - 20 None
Flanger 0 - 15 Sinusoidal (approx 1Hz)
Chorus 25 - 50 None
Echo >50 None

FMI:
The audio board uses the following pins.
6 - MEMCS
7 - MOSI
9 - BCLK
10 - SDCS
11 - MCLK
12 - MISO
13 - RX
14 - SCLK
15 - VOL
18 - SDA
19 - SCL
22 - TX
23 - LRCLK


AudioProcessorUsage()
AudioProcessorUsageMax()
AudioProcessorUsageMaxReset()
AudioMemoryUsage()
AudioMemoryUsageMax()
AudioMemoryUsageMaxReset()

The CPU usage is an integer from 0 to 100, and the memory is from 0 to however
many blocks you provided with AudioMemory().

*/

#include <arm_math.h>
#include <Audio.h>
#include <Wire.h>
//#include <WM8731.h>
#include <SD.h>
#include <SPI.h>
#include <Bounce.h>



// Number of samples in ONE channel
#define CHORUS_DELAY_LENGTH (16*AUDIO_BLOCK_SAMPLES)
// Allocate the delay line for left and right channels
// The delayline will hold left and right samples so it
// should be declared to be twice as long as the desired
// number of samples in one channel
#define CHORUS_DELAYLINE (CHORUS_DELAY_LENGTH*2)
// The delay line for left and right channels
short delayline[CHORUS_DELAYLINE];

// If this pin is grounded the FIR filter is turned which
// makes just pass through the audio
// Don't use any of the pins listed above
#define PASSTHRU_PIN 1

Bounce b_passthru = Bounce(PASSTHRU_PIN,15);

//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;

AudioInputI2S audioInput; // audio shield: mic or line-in
AudioEffectChorus myEffect;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
// Both channels of the audio input go to the FIR filter
AudioConnection c1(audioInput, 0, myEffect, 0);
AudioConnection c2(audioInput, 1, myEffect, 1);
// both channels from the FIR filter go to the audio output
AudioConnection c3(myEffect, 0, audioOutput, 0);
AudioConnection c4(myEffect, 1, audioOutput, 1);

AudioControlSGTL5000 audioShield;


// number of "voices" in the chorus which INCLUDES the original voice
int n_chorus = 3;

// <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>
void setup() {
Serial.begin(9600);
while (!Serial) ;
delay(3000);

pinMode(PASSTHRU_PIN,INPUT_PULLUP);

// Maximum memory usage was reported as 4
// Proc = 9 (9), Mem = 4 (4)
AudioMemory(4);

audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.volume(50);
// Warn that the passthru pin is grounded
if(!digitalRead(PASSTHRU_PIN)) {
Serial.print("PASSTHRU_PIN (");
Serial.print(PASSTHRU_PIN);
Serial.println(") is grounded");
}

// Initialize the effect
// address of delayline
// total number of samples (left AND right) in the delay line
// number of voices in the chorus INCLUDING the original voice
if(!myEffect.begin(delayline,CHORUS_DELAYLINE,n_chorus)) {
Serial.println("AudioEffectChorus - begin failed");
while(1);
}
// I want output on the line out too
audioShield.unmuteLineout();
// audioShield.muteHeadphone();
Serial.println("setup done");
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();
}


// audio volume
int volume = 0;

unsigned long last_time = millis();
void loop()
{
// Volume control
int n = analogRead(15);
if (n != volume) {
volume = n;
audioShield.volume((float)n / 10.23);
}
if(1) {
if(millis() - last_time >= 5000) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
Serial.print(AudioProcessorUsageMax());
Serial.print("), Mem = ");
Serial.print(AudioMemoryUsage());
Serial.print(" (");
Serial.print(AudioMemoryUsageMax());
Serial.println(")");
last_time = millis();
}
}
// update the button
b_passthru.update();
// If the passthru button is pushed, switch the effect to passthru
if(b_passthru.fallingEdge()) {
myEffect.modify(0);
}
// If passthru button is released, restore the previous chorus
if(b_passthru.risingEdge()) {
myEffect.modify(n_chorus);
}

}




+ 43
- 0
examples/effect_chorus/effects_info.h View File

@@ -0,0 +1,43 @@
/*
CHORUS and FLANGE effects
Both effects use a delay line to hold previous samples. This allows
the current sample to be combined in some way with a sample that
occurred in the past. An obvious effect this would allow would be
an echo where the current sample is combined with a sample from,
say, 250 milliseconds ago. The chorus and flange effects do this
as well but they combine samples from only about 50ms or less ago.
CHORUS EFFECT
This combines one or more samples up to about 50ms ago. In this
library, the additional samples are evenly spread through the
supplied delay line.
E.G. If the number of voices is specified as 2 then the effect
combines the current sample and the oldest sample (the last one in
the delay line). If the number of voices is 3 then the effect
combines the most recent sample, the oldest sample and the sample
in the middle of the delay line.
For two voices the effect can be represented as:
result = sample(0) + sample(dt)
where sample(0) represents the current sample and sample(dt) is
the sample in the delay line from dt milliseconds ago.

FLANGE EFFECT
This combines only one sample from the delay line but the position
of that sample varies sinusoidally.
In this case the effect can be represented as:
result = sample(0) + sample(dt + depth*sin(2*PI*Fe))
The value of the sine function is always a number from -1 to +1
and so the result of depth*(sinFe) is always a number from
-depth to +depth. Thus, the delayed sample will be selected from
the range (dt-depth) to (dt+depth). This selection will vary
at whatever rate is specified as the frequency of the effect Fe.
I have found that rates of .25 seconds or less are best, otherwise
the effect is very "watery" and in extreme cases the sound is
even off-key!

When trying out these effects with recorded music as input, it is
best to use those where there is a solo voice which is clearly
"in front" of the accompaninemnt. Tracks which already contain
flange or chorus effects don't work well.
*/

+ 314
- 0
examples/effect_flange/effect_flange.ino View File

@@ -0,0 +1,314 @@
/*
Change the chorus code to produce a flange effect

PROC/MEM 25/4
140204
d
- fixed the problem with user-supplied delay line

140203
c
140203
b
- when switching to/from passthru, keep the delay line filled
BUT to be effective must also fix up begin and add another
function to allow changing to/from passthru without
reinitialing everything.
I have mixed up the names "chorus" and flange". The sketches named
chorus have, up to version 'm', actually implemented a flanger.
From version 'o' onwards the sketches named chorus will implement
a real chorus effect and flange will do a flanging effect.

140202
a
Modify filter_test_f to try out a chorus effect

m + n only changed the effect parameters
-
140201
l - found the problem at last using my_flange_cd_usd_c
YES! Way back when I found I hadn't been using d_depth. Now I
have just discovered that I haven't been using delay_offset!!!!!
140201
k
interpolation doesn't remove the ticking
140131
j
>>> The lower the frequency, the less ticking.
- try interpolation
140201
- got this restored to the way it was last night
and then reinstated the changes. I had a couple of
changes to the right channel that were incorrect or
weren't carried over from the left channel changes
i
- don't know why but this version seems to have more "presence"
than previous versions.
The presence occurred when "sign = 1" was put in front of the left.
It essentially makes it a passthrough.
If both have "sign=1" then it is identical to passthrough
Ticking is still not fixed
h
- add sign reversal. It seems to make audio much clearer
BUT it hasn't got rid of the ticking noise
g
- I wasn't even using delay_depth!!!!
140131
f
- added code to print the processor and memory usage every 5 seconds
NOW the problem is to try to remove the ticking

e
FOUND the problem with the right channel. It was also in the left channel
but the placement of the delay line arrays made it more noticeable in the
right channel. I was not calculating idx properly. In particular, the
resuling index could be negative.

I have shortened the delay line to only 2*AUDIO_BLOCK_SAMPLES

- removed redundancies in the update code. rewrite the block
instead of getting a new one
Haven't solved the noise in the right channel yet.
Tried duplicating right channel code to left but noise stays on the right

140130
d
The noise stays in the right channel even if it is calculated first
Switching the L/R inputs doesn't switch the noise to the left channel
c
>> Now add a sinusoidal modulation to the offset
There's an awful noise in both channels but it is much louder in
the right channel. NOPE - it is ONLY in the right channel
but the audio does sound like it is working (sort of) except that it
is rather tinny. Maybe it needs to have the interpolation added.

b
- this works with clip16_6s.wav.
The original of this audio file was from http://www.donreiman.com/Chorus/Chorus.htm
I reworked it with Goldwave to make it a stereo WAV file
But with Rick Wakewan's Jane Seymour it seems to act more like
a high-pass filter.
a
- removed FIR stuff and changed the name to AudioEffectChorus
it's basically a blank template and compiles.

From: http://www.cs.cf.ac.uk/Dave/CM0268/PDF/10_CM0268_Audio_FX.pdf
about Comb filter effects
Effect Delay range (ms) Modulation
Resonator 0 - 20 None
Flanger 0 - 15 Sinusoidal (approx 1Hz)
Chorus 25 - 50 None
Echo >50 None

FMI:
The audio board uses the following pins.
6 - MEMCS
7 - MOSI
9 - BCLK
10 - SDCS
11 - MCLK
12 - MISO
13 - RX
14 - SCLK
15 - VOL
18 - SDA
19 - SCL
22 - TX
23 - LRCLK


AudioProcessorUsage()
AudioProcessorUsageMax()
AudioProcessorUsageMaxReset()
AudioMemoryUsage()
AudioMemoryUsageMax()
AudioMemoryUsageMaxReset()

The CPU usage is an integer from 0 to 100, and the memory is from 0 to however
many blocks you provided with AudioMemory().


*/

#include <arm_math.h>
#include <Audio.h>
#include <Wire.h>
//#include <WM8731.h>
#include <SD.h>
#include <SPI.h>
#include <Bounce.h>

// Number of samples in ONE channel
#define FLANGE_DELAY_LENGTH (16*AUDIO_BLOCK_SAMPLES)
// Allocate the delay line for left and right channels
// The delayline will hold left and right samples so it
// should be declared to be twice as long as the desired
// number of samples in one channel
#define FLANGE_DELAYLINE (FLANGE_DELAY_LENGTH*2)
// The delay line for left and right channels
short delayline[FLANGE_DELAYLINE];

// If this pin is grounded the FIR filter is turned which
// makes just pass through the audio
// Don't use any of the pins listed above
#define PASSTHRU_PIN 1

Bounce b_passthru = Bounce(PASSTHRU_PIN,15);

//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;

AudioInputI2S audioInput; // audio shield: mic or line-in
AudioEffectFlange myEffect;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
// Both channels of the audio input go to the FIR filter
AudioConnection c1(audioInput, 0, myEffect, 0);
AudioConnection c2(audioInput, 1, myEffect, 1);
// both channels from the FIR filter go to the audio output
AudioConnection c3(myEffect, 0, audioOutput, 0);
AudioConnection c4(myEffect, 1, audioOutput, 1);

AudioControlSGTL5000 audioShield;

/*
int s_idx = FLANGE_DELAY_LENGTH/2;
int s_depth = FLANGE_DELAY_LENGTH/16;
double s_freq = 1;

// <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>
// 12
int s_idx = FLANGE_DELAY_LENGTH/2;
int s_depth = FLANGE_DELAY_LENGTH/8;
double s_freq = .125;
// with .125 the ticking is about 1Hz with music
// but with the noise sample it is a bit slower than that
// <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>
*/
/*
// <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>
// 12
int s_idx = FLANGE_DELAY_LENGTH/2;
int s_depth = FLANGE_DELAY_LENGTH/12;
double s_freq = .125;
// with .125 the ticking is about 1Hz with music
// but with the noise sample it is a bit slower than that
// <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>
*/
/*
//12
int s_idx = 15*FLANGE_DELAY_LENGTH/16;
int s_depth = 15*FLANGE_DELAY_LENGTH/16;
double s_freq = 0;
*/
/*
//12
int s_idx = 2*FLANGE_DELAY_LENGTH/4;
int s_depth = FLANGE_DELAY_LENGTH/8;
double s_freq = .0625;
*/

//12 - good with Eric Clapton Unplugged
int s_idx = 3*FLANGE_DELAY_LENGTH/4;
int s_depth = FLANGE_DELAY_LENGTH/8;
double s_freq = .0625;

void setup() {
Serial.begin(9600);
while (!Serial) ;
delay(3000);

pinMode(PASSTHRU_PIN,INPUT_PULLUP);

// It doesn't work properly with any less than 8
// but that was an earlier version. Processor and
// memory usage are now (ver j)
// Proc = 24 (24), Mem = 4 (4)
AudioMemory(8);

audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.volume(50);
// Warn that the passthru pin is grounded
if(!digitalRead(PASSTHRU_PIN)) {
Serial.print("PASSTHRU_PIN (");
Serial.print(PASSTHRU_PIN);
Serial.println(") is grounded");
}

// Set up the flange effect
// address of delayline
// total number of samples (left AND right) in the delay line
// Index (in samples) into the delay line for the added voice
// Depth of the flange effect
// frequency of the flange effect
myEffect.begin(delayline,FLANGE_DELAYLINE,s_idx,s_depth,s_freq);

// I want output on the line out too
audioShield.unmuteLineout();
Serial.println("setup done");
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();
}


// audio volume
int volume = 0;

unsigned long last_time = millis();
void loop()
{
// Volume control
int n = analogRead(15);
if (n != volume) {
volume = n;
audioShield.volume((float)n / 10.23);
}
if(1) {
if(millis() - last_time >= 5000) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
Serial.print(AudioProcessorUsageMax());
Serial.print("), Mem = ");
Serial.print(AudioMemoryUsage());
Serial.print(" (");
Serial.print(AudioMemoryUsageMax());
Serial.println(")");
last_time = millis();
}
}
// update the button
b_passthru.update();
// If the passthru button is pushed, save the current
// filter index and then switch the filter to passthru
if(b_passthru.fallingEdge()) {
myEffect.modify(DELAY_PASSTHRU,0,0);
}
// If passthru button is released, restore the effect
if(b_passthru.risingEdge()) {
myEffect.modify(s_idx,s_depth,s_freq);
}

}





+ 44
- 0
examples/effect_flange/effects_info.h View File

@@ -0,0 +1,44 @@
/*
CHORUS and FLANGE effects
Both effects use a delay line to hold previous samples. This allows
the current sample to be combined in some way with a sample that
occurred in the past. An obvious effect this would allow would be
an echo where the current sample is combined with a sample from,
say, 250 milliseconds ago. The chorus and flange effects do this
as well but they combine samples from only about 50ms (or less) ago.
CHORUS EFFECT
This combines one or more samples up to about 50ms ago. In this
library, the additional samples are evenly spread through the
supplied delay line.
E.G. If the number of voices is specified as 2 then the effect
combines the current sample and the oldest sample (the last one in
the delay line). If the number of voices is 3 then the effect
combines the most recent sample, the oldest sample and the sample
in the middle of the delay line.
For two voices the effect can be represented as:
result = sample(0) + sample(dt)
where sample(0) represents the current sample and sample(dt) is
the sample in the delay line from dt milliseconds ago.


FLANGE EFFECT
This combines only one sample from the delay line but the position
of that sample varies sinusoidally.
In this case the effect can be represented as:
result = sample(0) + sample(dt + depth*sin(2*PI*Fe))
The value of the sine function is always a number from -1 to +1
and so the result of depth*(sinFe) is always a number from
-depth to +depth. Thus, the delayed sample will be selected from
the range (dt-depth) to (dt+depth). This selection will vary
at whatever rate is specified as the frequency of the effect Fe.
I have found that rates of .25 seconds or less are best, otherwise
the effect is very "watery" and in extreme cases the sound is
even off-key!

When trying out these effects with recorded music as input, it is
best to use those where there is a solo voice which is clearly
"in front" of the accompaninemnt. Tracks which already contain
flange or chorus effects don't work well.
*/

Loading…
Cancel
Save