| @@ -26,6 +26,7 @@ | |||
| /******************************************************************/ | |||
| // A u d i o E f f e c t F l a n g e | |||
| // Written by Pete (El Supremo) Jan 2014 | |||
| // 140529 - change to handle mono stream and change modify() to voices() | |||
| // 140207 - fix calculation of delay_rate_incr which is expressed as | |||
| // a fraction of 2*PI | |||
| // 140207 - cosmetic fix to begin() | |||
| @@ -71,14 +72,11 @@ if(0) { | |||
| } | |||
| 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 = delay_rate/44100.*2147483648.; | |||
| //Serial.println(delay_rate_incr,HEX); | |||
| @@ -96,7 +94,7 @@ if(0) { | |||
| } | |||
| boolean AudioEffectFlange::modify(int delay_offset,int d_depth,float delay_rate) | |||
| boolean AudioEffectFlange::voices(int delay_offset,int d_depth,float delay_rate) | |||
| { | |||
| boolean all_ok = true; | |||
| @@ -115,9 +113,7 @@ boolean AudioEffectFlange::modify(int delay_offset,int d_depth,float delay_rate) | |||
| all_ok = false; | |||
| } | |||
| l_delay_rate_index = 0; | |||
| r_delay_rate_index = 0; | |||
| l_circ_idx = 0; | |||
| r_circ_idx = 0; | |||
| return(all_ok); | |||
| } | |||
| @@ -130,7 +126,6 @@ void AudioEffectFlange::update(void) | |||
| int idx1; | |||
| if(l_delayline == NULL)return; | |||
| if(r_delayline == NULL)return; | |||
| // do passthru | |||
| if(delay_offset_idx == FLANGE_DELAY_PASSTHRU) { | |||
| @@ -138,6 +133,7 @@ void AudioEffectFlange::update(void) | |||
| block = receiveWritable(0); | |||
| if(block) { | |||
| bp = block->data; | |||
| // fill the delay line | |||
| for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | |||
| l_circ_idx++; | |||
| if(l_circ_idx >= delay_length) { | |||
| @@ -145,22 +141,10 @@ void AudioEffectFlange::update(void) | |||
| } | |||
| l_delayline[l_circ_idx] = *bp++; | |||
| } | |||
| // transmit the unmodified block | |||
| 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; | |||
| } | |||
| @@ -218,12 +202,7 @@ void AudioEffectFlange::update(void) | |||
| // Do the interpolation | |||
| 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; | |||
| *bp++ = (l_delayline[l_circ_idx]+ l_delayline[idx] + frac)/2; | |||
| l_delay_rate_index += delay_rate_incr; | |||
| if(l_delay_rate_index & 0x80000000) { | |||
| @@ -234,57 +213,6 @@ void AudioEffectFlange::update(void) | |||
| 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; | |||
| frac = arm_sin_q15( (q15_t)((r_delay_rate_index >> 16)&0x7fff)); | |||
| idx = (frac * 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); | |||
| } | |||
| } | |||
| @@ -28,6 +28,7 @@ | |||
| /******************************************************************/ | |||
| // A u d i o E f f e c t F l a n g e | |||
| // Written by Pete (El Supremo) Jan 2014 | |||
| // 140529 - change to handle mono stream and change modify() to voices() | |||
| #define FLANGE_DELAY_PASSTHRU 0 | |||
| @@ -36,26 +37,23 @@ public AudioStream | |||
| { | |||
| public: | |||
| AudioEffectFlange(void): | |||
| AudioStream(2,inputQueueArray) { | |||
| AudioStream(1,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); | |||
| boolean voices(int delay_offset,int d_depth,float delay_rate); | |||
| virtual void update(void); | |||
| void stop(void); | |||
| private: | |||
| audio_block_t *inputQueueArray[2]; | |||
| audio_block_t *inputQueueArray[1]; | |||
| short *l_delayline; | |||
| short *r_delayline; | |||
| int delay_length; | |||
| short l_circ_idx; | |||
| short r_circ_idx; | |||
| int delay_depth; | |||
| int delay_offset_idx; | |||
| int delay_rate_incr; | |||
| unsigned int l_delay_rate_index; | |||
| unsigned int r_delay_rate_index; | |||
| }; | |||
| #endif | |||
| @@ -1,7 +1,21 @@ | |||
| /* | |||
| Change the chorus code to produce a flange effect | |||
| 140219 | |||
| e | |||
| VERSION 2 - use modified library which has been changed to handle | |||
| one channel instead of two | |||
| Proc = 21 (22), Mem = 4 (6) | |||
| 140529 | |||
| 2a | |||
| - default at startup is to have passthru ON and the button | |||
| switches the flange effect in. | |||
| 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. | |||
| @@ -39,18 +53,14 @@ many blocks you provided with AudioMemory(). | |||
| #include <SPI.h> | |||
| #include <Bounce.h> | |||
| // Number of samples in ONE channel | |||
| // Number of samples in each delay line | |||
| #define FLANGE_DELAY_LENGTH (6*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 effect is turned off, | |||
| // which makes it just pass through the audio | |||
| // Allocate the delay lines for left and right channels | |||
| short l_delayline[FLANGE_DELAY_LENGTH]; | |||
| short r_delayline[FLANGE_DELAY_LENGTH]; | |||
| // Default is to just pass the audio through. Grounding this pin | |||
| // applies the flange effect | |||
| // Don't use any of the pins listed above | |||
| #define PASSTHRU_PIN 1 | |||
| @@ -60,82 +70,21 @@ Bounce b_passthru = Bounce(PASSTHRU_PIN,15); | |||
| const int myInput = AUDIO_INPUT_LINEIN; | |||
| AudioInputI2S audioInput; // audio shield: mic or line-in | |||
| AudioEffectFlange myEffect; | |||
| AudioEffectFlange l_myEffect; | |||
| AudioEffectFlange r_myEffect; | |||
| AudioOutputI2S audioOutput; // audio shield: headphones & line-out | |||
| // Create Audio connections between the components | |||
| // Both channels of the audio input go to the flange effect | |||
| AudioConnection c1(audioInput, 0, myEffect, 0); | |||
| AudioConnection c2(audioInput, 1, myEffect, 1); | |||
| AudioConnection c1(audioInput, 0, l_myEffect, 0); | |||
| AudioConnection c2(audioInput, 1, r_myEffect, 0); | |||
| // both channels from the flange effect go to the audio output | |||
| AudioConnection c3(myEffect, 0, audioOutput, 0); | |||
| AudioConnection c4(myEffect, 1, audioOutput, 1); | |||
| AudioConnection c3(l_myEffect, 0, audioOutput, 0); | |||
| AudioConnection c4(r_myEffect, 0, 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; | |||
| */ | |||
| /* | |||
| // Real flange effect! delay line is 2* | |||
| int s_idx = 2*FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/4; | |||
| double s_freq = 2; | |||
| */ | |||
| /* 2 - | |||
| int s_idx = 2*FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/8; | |||
| double s_freq = 4; | |||
| */ | |||
| /* | |||
| // 4 | |||
| int s_idx = FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/4; | |||
| double s_freq = .25; | |||
| */ | |||
| // 4 | |||
| int s_idx = FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/4; | |||
| double s_freq = .5; | |||
| @@ -164,20 +113,25 @@ void setup() { | |||
| 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); | |||
| // Set up the flange effect: | |||
| // address of delayline | |||
| // total number of samples 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 | |||
| l_myEffect.begin(l_delayline,FLANGE_DELAY_LENGTH,s_idx,s_depth,s_freq); | |||
| r_myEffect.begin(r_delayline,FLANGE_DELAY_LENGTH,s_idx,s_depth,s_freq); | |||
| // Initially the effect is off. It is switched on when the | |||
| // PASSTHRU button is pushed. | |||
| l_myEffect.voices(FLANGE_DELAY_PASSTHRU,0,0); | |||
| r_myEffect.voices(FLANGE_DELAY_PASSTHRU,0,0); | |||
| // I want output on the line out too | |||
| audioShield.unmuteLineout(); | |||
| Serial.println("setup done"); | |||
| AudioProcessorUsageMaxReset(); | |||
| AudioMemoryUsageMaxReset(); | |||
| AudioProcessorUsageMaxReset(); | |||
| AudioMemoryUsageMaxReset(); | |||
| } | |||
| @@ -210,15 +164,18 @@ if(0) { | |||
| // update the button | |||
| b_passthru.update(); | |||
| // If the passthru button is pushed, save the current | |||
| // If the passthru button is pushed | |||
| // turn the flange effect on | |||
| // filter index and then switch the effect to passthru | |||
| if(b_passthru.fallingEdge()) { | |||
| myEffect.modify(DELAY_PASSTHRU,0,0); | |||
| l_myEffect.voices(s_idx,s_depth,s_freq); | |||
| r_myEffect.voices(s_idx,s_depth,s_freq); | |||
| } | |||
| // If passthru button is released, restore the effect | |||
| // If passthru button is released restore passthru | |||
| if(b_passthru.risingEdge()) { | |||
| myEffect.modify(s_idx,s_depth,s_freq); | |||
| l_myEffect.voices(FLANGE_DELAY_PASSTHRU,0,0); | |||
| r_myEffect.voices(FLANGE_DELAY_PASSTHRU,0,0); | |||
| } | |||
| } | |||
| @@ -17,11 +17,10 @@ CHORUS 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) | |||
| result = (sample(0) + sample(dt))/2 | |||
| 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. | |||
| @@ -32,13 +31,24 @@ FLANGE EFFECT | |||
| -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! | |||
| Try these settings: | |||
| #define FLANGE_DELAY_LENGTH (2*AUDIO_BLOCK_SAMPLES) | |||
| and | |||
| int s_idx = 2*FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/4; | |||
| double s_freq = 3; | |||
| The flange effect can also produce a chorus effect if a longer | |||
| delay line is used with a slower rate, for example try: | |||
| #define FLANGE_DELAY_LENGTH (12*AUDIO_BLOCK_SAMPLES) | |||
| and | |||
| int s_idx = 3*FLANGE_DELAY_LENGTH/4; | |||
| int s_depth = FLANGE_DELAY_LENGTH/8; | |||
| double s_freq = .0625; | |||
| 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 | |||
| "in front" of the accompaniment. Tracks which already contain | |||
| flange or chorus effects don't work well. | |||
| */ | |||