This is the first in a series of posts introducing simple audio effects that can be used in micro controller projects.
Next Week - Bit Crushing effects
The Delay effect is one of the simplest and most effective enhancements we can add to our audio projects.
The delay effect works by recording the output as it is being generated and then mixing this sound back in with itself - after a delay. The result should be familiar to anyone who has every played an electric guitar through an amp with reverb.
In the case of the Auduino synthesizer the result is a mild echo effect and slightly smoother, more metallic sound - the effect can be turned on or off through a push button in the code provided below.
How do we create the delay effect
Delay is very simple to add in a microcontroller, all we need is a block of memory to record the output in.
The larger the block of memory, the longer the delay we can record and the deeper the effect.
In this case we are using a 1K block of memory in the array named sDelayBuffer -
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;
The other modification is inside the interrupt service routine which generates the Audiuno output, essentially what we are doing is adding the sound we recorded 1/8th of a second ago on top of the current output value -
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite
PWM_VALUE = output;
}
We test whether delay is enabled, if it is we calculate the output value by adding the initial output to the earlier recorded output from our delay buffer. After outputting this combined value we record it in the delay buffer replacing the value we just used. Over time, the code cycles through the delay buffer over and over again, mixing the current output with a sample from 1/8th of a second back - a bit like playing your instrument in a large hall where the distinct sound is the result of the current sound being constantly mixed with its echo.
Thats all there is to generating delay in a micro controller synth engine - exactly the same code is used to create the delay effect in the RCArduino Five Dollar Synthesizer.
The RCArduino Five Dollar Synthesizer is another audio project enhanced with this delay effect -
http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html
Further Development
The amount of delay we can provide is determined to the memory we use to record the samples. In the Auduino we are using 1K which at an 8K play back rate gives us 125ms of delay. This can be increased by bit crushing the samples - using 4 bits per sample we get 250ms, 2 bits gets us half a second, with 1 bit we can get a whole second. Unfortunately initial experiments suggest that the effect is largely lost when applying these techniques, its a bit like shouting into a cave and getting a different echo back - your ears just don't buy it.
Auduino Accreditation
The Auduino is an original work by Peter Knight, the original project can be found here -
http://code.google.com/p/tinkerit/wiki/Auduino
Auduino with delay
Auduino with delay is a very slight modification by Duane B (rcarduino) to the original work of Peter Knight.
Notes
- This code also include the volatile fix which allows the Auduino to work correctly in Arduino 1.0 and later
- Remember to use a pull up or pull down resistor if you are not using a push button or switch for the delay button or if your more comfortable modifying the code, replace the button code with true or false.
- LED 13, now indicates whether delay is on or off.
Next Week - Bit Crushing effects
The Delay effect is one of the simplest and most effective enhancements we can add to our audio projects.
The delay effect works by recording the output as it is being generated and then mixing this sound back in with itself - after a delay. The result should be familiar to anyone who has every played an electric guitar through an amp with reverb.
In the case of the Auduino synthesizer the result is a mild echo effect and slightly smoother, more metallic sound - the effect can be turned on or off through a push button in the code provided below.
How do we create the delay effect
Delay is very simple to add in a microcontroller, all we need is a block of memory to record the output in.
The larger the block of memory, the longer the delay we can record and the deeper the effect.
In this case we are using a 1K block of memory in the array named sDelayBuffer -
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;
The other modification is inside the interrupt service routine which generates the Audiuno output, essentially what we are doing is adding the sound we recorded 1/8th of a second ago on top of the current output value -
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite
PWM_VALUE = output;
}
We test whether delay is enabled, if it is we calculate the output value by adding the initial output to the earlier recorded output from our delay buffer. After outputting this combined value we record it in the delay buffer replacing the value we just used. Over time, the code cycles through the delay buffer over and over again, mixing the current output with a sample from 1/8th of a second back - a bit like playing your instrument in a large hall where the distinct sound is the result of the current sound being constantly mixed with its echo.
Thats all there is to generating delay in a micro controller synth engine - exactly the same code is used to create the delay effect in the RCArduino Five Dollar Synthesizer.
The RCArduino Five Dollar Synthesizer is another audio project enhanced with this delay effect -
http://rcarduino.blogspot.com/2012/10/five-dollar-synthesiser.html
Further Development
The amount of delay we can provide is determined to the memory we use to record the samples. In the Auduino we are using 1K which at an 8K play back rate gives us 125ms of delay. This can be increased by bit crushing the samples - using 4 bits per sample we get 250ms, 2 bits gets us half a second, with 1 bit we can get a whole second. Unfortunately initial experiments suggest that the effect is largely lost when applying these techniques, its a bit like shouting into a cave and getting a different echo back - your ears just don't buy it.
Auduino Accreditation
The Auduino is an original work by Peter Knight, the original project can be found here -
http://code.google.com/p/tinkerit/wiki/Auduino
Auduino with delay
Auduino with delay is a very slight modification by Duane B (rcarduino) to the original work of Peter Knight.
Notes
- This code also include the volatile fix which allows the Auduino to work correctly in Arduino 1.0 and later
- Remember to use a pull up or pull down resistor if you are not using a push button or switch for the delay button or if your more comfortable modifying the code, replace the button code with true or false.
- LED 13, now indicates whether delay is on or off.
// Auduino, the Lo-Fi granular synthesiser
//
// by Peter Knight, Tinker.it http://tinker.it
//
// Help: http://code.google.com/p/tinkerit/wiki/Auduino
// More help: http://groups.google.com/group/auduino
//
// Analog in 0: Grain 1 pitch
// Analog in 1: Grain 2 decay
// Analog in 2: Grain 1 decay
// Analog in 3: Grain 2 pitch
// Analog in 4: Grain repetition frequency
//
// Digital 3: Audio out (Digital 11 on ATmega8)
//
// Changelog:
// 19 Nov 2008: Added support for ATmega8 boards
// 21 Mar 2009: Added support for ATmega328 boards
// 7 Apr 2009: Fixed interrupt vector for ATmega328 boards
// 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)
#include <avr/io.h>
#include <avr/interrupt.h>
uint16_t syncPhaseAcc;
volatile uint16_t syncPhaseInc;
uint16_t grainPhaseAcc;
volatile uint16_t grainPhaseInc;
uint16_t grainAmp;
volatile uint8_t grainDecay;
uint16_t grain2PhaseAcc;
volatile uint16_t grain2PhaseInc;
uint16_t grain2Amp;
volatile uint8_t grain2Decay;
// Map Analogue channels
#define SYNC_CONTROL (4)
#define GRAIN_FREQ_CONTROL (0)
#define GRAIN_DECAY_CONTROL (2)
#define GRAIN2_FREQ_CONTROL (3)
#define GRAIN2_DECAY_CONTROL (1)
// DB
#define SMOOTH_PIN 8
// Changing these will also requires rewriting audioOn()
#if defined(__AVR_ATmega8__)
//
// On old ATmega8 boards.
// Output is on pin 11
//
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 5
#define PWM_PIN 11
#define PWM_VALUE OCR2
#define PWM_INTERRUPT TIMER2_OVF_vect
#elif defined(__AVR_ATmega1280__)
//
// On the Arduino Mega
// Output is on pin 3
//
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 7
#define PWM_PIN 3
#define PWM_VALUE OCR3C
#define PWM_INTERRUPT TIMER3_OVF_vect
#else
//
// For modern ATmega168 and ATmega328 boards
// Output is on pin 3
//
#define PWM_PIN 3
#define PWM_VALUE OCR2B
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 5
#define PWM_INTERRUPT TIMER2_OVF_vect
#endif
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;
#define DELAY_BUTTON 4
// Smooth logarithmic mapping
//
uint16_t antilogTable[] = {
64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,
54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,
45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,
38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768
};
uint16_t mapPhaseInc(uint16_t input) {
return (antilogTable[input & 0x3f]) >> (input >> 6);
}
// Stepped chromatic mapping
//
uint16_t midiTable[] = {
17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,
77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,
244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,
732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,
1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,
4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,
10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,
22121,23436,24830,26306
};
uint16_t mapMidi(uint16_t input) {
return (midiTable[(1023-input) >> 3]);
}
// Stepped Pentatonic mapping
//
uint16_t pentatonicTable[54] = {
0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,
411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,
3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306
};
uint16_t mapPentatonic(uint16_t input) {
uint8_t value = (1023-input) / (1024/53);
return (pentatonicTable[value]);
}
void audioOn() {
#if defined(__AVR_ATmega8__)
// ATmega8 has different registers
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
TIMSK = _BV(TOIE2);
#elif defined(__AVR_ATmega1280__)
TCCR3A = _BV(COM3C1) | _BV(WGM30);
TCCR3B = _BV(CS30);
TIMSK3 = _BV(TOIE3);
#else
// Set up PWM to 31.25kHz, phase accurate
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);
#endif
}
void setup() {
pinMode(PWM_PIN,OUTPUT);
audioOn();
pinMode(LED_PIN,OUTPUT);
pinMode(DELAY_BUTTON,INPUT);
// set pin mode and turn on pull up so that default mode
// is PENTATONIC, pull the pin low to switch to smooth
pinMode(SMOOTH_PIN,INPUT);
digitalWrite(SMOOTH_PIN,HIGH);
}
void loop() {
// The loop is pretty simple - it just updates the parameters for the oscillators.
//
// Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
// They will cause clicks and poops in the audio.
// defaults to pentatonic stepped tones, pull pin low for smooth frequency without distinct tones
// syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));
// updated 29/01/2013
// pull the DELAY_BUTTON pin high for delay, low for no delay
// use either a pull up/pull down resistor
// or a pull up resistor with a toggle switch between the pin and ground
bDelay = digitalRead(DELAY_BUTTON);
// Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
//syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
// Stepped pentatonic mapping: D, E, G, A, B
grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8;
grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4;
}
SIGNAL(PWM_INTERRUPT)
{
uint8_t value;
uint16_t output;
syncPhaseAcc += syncPhaseInc;
if (syncPhaseAcc < syncPhaseInc) {
// Time to start the next grain
grainPhaseAcc = 0;
grainAmp = 0x7fff;
grain2PhaseAcc = 0;
grain2Amp = 0x7fff;
// LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite
}
// Increment the phase of the grain oscillators
grainPhaseAcc += grainPhaseInc;
grain2PhaseAcc += grain2PhaseInc;
// Convert phase into a triangle wave
value = (grainPhaseAcc >> 7) & 0xff;
if (grainPhaseAcc & 0x8000) value = ~value;
// Multiply by current grain amplitude to get sample
output = value * (grainAmp >> 8);
// Repeat for second grain
value = (grain2PhaseAcc >> 7) & 0xff;
if (grain2PhaseAcc & 0x8000) value = ~value;
output += value * (grain2Amp >> 8);
// Make the grain amplitudes decay by a factor every sample (exponential decay)
grainAmp -= (grainAmp >> 8) * grainDecay;
grain2Amp -= (grain2Amp >> 8) * grain2Decay;
// Scale output to the available range, clipping if necessary
output >>= 9;
if (output > 255) output = 255;
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite
PWM_VALUE = output;
}
}
//
// by Peter Knight, Tinker.it http://tinker.it
//
// Help: http://code.google.com/p/tinkerit/wiki/Auduino
// More help: http://groups.google.com/group/auduino
//
// Analog in 0: Grain 1 pitch
// Analog in 1: Grain 2 decay
// Analog in 2: Grain 1 decay
// Analog in 3: Grain 2 pitch
// Analog in 4: Grain repetition frequency
//
// Digital 3: Audio out (Digital 11 on ATmega8)
//
// Changelog:
// 19 Nov 2008: Added support for ATmega8 boards
// 21 Mar 2009: Added support for ATmega328 boards
// 7 Apr 2009: Fixed interrupt vector for ATmega328 boards
// 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)
#include <avr/io.h>
#include <avr/interrupt.h>
uint16_t syncPhaseAcc;
volatile uint16_t syncPhaseInc;
uint16_t grainPhaseAcc;
volatile uint16_t grainPhaseInc;
uint16_t grainAmp;
volatile uint8_t grainDecay;
uint16_t grain2PhaseAcc;
volatile uint16_t grain2PhaseInc;
uint16_t grain2Amp;
volatile uint8_t grain2Decay;
// Map Analogue channels
#define SYNC_CONTROL (4)
#define GRAIN_FREQ_CONTROL (0)
#define GRAIN_DECAY_CONTROL (2)
#define GRAIN2_FREQ_CONTROL (3)
#define GRAIN2_DECAY_CONTROL (1)
// DB
#define SMOOTH_PIN 8
// Changing these will also requires rewriting audioOn()
#if defined(__AVR_ATmega8__)
//
// On old ATmega8 boards.
// Output is on pin 11
//
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 5
#define PWM_PIN 11
#define PWM_VALUE OCR2
#define PWM_INTERRUPT TIMER2_OVF_vect
#elif defined(__AVR_ATmega1280__)
//
// On the Arduino Mega
// Output is on pin 3
//
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 7
#define PWM_PIN 3
#define PWM_VALUE OCR3C
#define PWM_INTERRUPT TIMER3_OVF_vect
#else
//
// For modern ATmega168 and ATmega328 boards
// Output is on pin 3
//
#define PWM_PIN 3
#define PWM_VALUE OCR2B
#define LED_PIN 13
#define LED_PORT PORTB
#define LED_BIT 5
#define PWM_INTERRUPT TIMER2_OVF_vect
#endif
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// Very simple ring buffer delay
// we record the output in this array
// and then mix it back in with the output as the buffer wraps around
// can be switched on and off by a button on DELAY_BUTTON
#define MAX_DELAY 1024
unsigned char sDelayBuffer[MAX_DELAY];
unsigned int nDelayCounter = 0;
unsigned char bDelay;
#define DELAY_BUTTON 4
// Smooth logarithmic mapping
//
uint16_t antilogTable[] = {
64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,
54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,
45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,
38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768
};
uint16_t mapPhaseInc(uint16_t input) {
return (antilogTable[input & 0x3f]) >> (input >> 6);
}
// Stepped chromatic mapping
//
uint16_t midiTable[] = {
17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,
77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,
244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,
732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,
1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,
4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,
10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,
22121,23436,24830,26306
};
uint16_t mapMidi(uint16_t input) {
return (midiTable[(1023-input) >> 3]);
}
// Stepped Pentatonic mapping
//
uint16_t pentatonicTable[54] = {
0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,
411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,
3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306
};
uint16_t mapPentatonic(uint16_t input) {
uint8_t value = (1023-input) / (1024/53);
return (pentatonicTable[value]);
}
void audioOn() {
#if defined(__AVR_ATmega8__)
// ATmega8 has different registers
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
TIMSK = _BV(TOIE2);
#elif defined(__AVR_ATmega1280__)
TCCR3A = _BV(COM3C1) | _BV(WGM30);
TCCR3B = _BV(CS30);
TIMSK3 = _BV(TOIE3);
#else
// Set up PWM to 31.25kHz, phase accurate
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);
#endif
}
void setup() {
pinMode(PWM_PIN,OUTPUT);
audioOn();
pinMode(LED_PIN,OUTPUT);
pinMode(DELAY_BUTTON,INPUT);
// set pin mode and turn on pull up so that default mode
// is PENTATONIC, pull the pin low to switch to smooth
pinMode(SMOOTH_PIN,INPUT);
digitalWrite(SMOOTH_PIN,HIGH);
}
void loop() {
// The loop is pretty simple - it just updates the parameters for the oscillators.
//
// Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
// They will cause clicks and poops in the audio.
// defaults to pentatonic stepped tones, pull pin low for smooth frequency without distinct tones
// syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));
// updated 29/01/2013
// pull the DELAY_BUTTON pin high for delay, low for no delay
// use either a pull up/pull down resistor
// or a pull up resistor with a toggle switch between the pin and ground
bDelay = digitalRead(DELAY_BUTTON);
// Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
//syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
// Stepped pentatonic mapping: D, E, G, A, B
grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8;
grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4;
}
SIGNAL(PWM_INTERRUPT)
{
uint8_t value;
uint16_t output;
syncPhaseAcc += syncPhaseInc;
if (syncPhaseAcc < syncPhaseInc) {
// Time to start the next grain
grainPhaseAcc = 0;
grainAmp = 0x7fff;
grain2PhaseAcc = 0;
grain2Amp = 0x7fff;
// LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite
}
// Increment the phase of the grain oscillators
grainPhaseAcc += grainPhaseInc;
grain2PhaseAcc += grain2PhaseInc;
// Convert phase into a triangle wave
value = (grainPhaseAcc >> 7) & 0xff;
if (grainPhaseAcc & 0x8000) value = ~value;
// Multiply by current grain amplitude to get sample
output = value * (grainAmp >> 8);
// Repeat for second grain
value = (grain2PhaseAcc >> 7) & 0xff;
if (grain2PhaseAcc & 0x8000) value = ~value;
output += value * (grain2Amp >> 8);
// Make the grain amplitudes decay by a factor every sample (exponential decay)
grainAmp -= (grainAmp >> 8) * grainDecay;
grain2Amp -= (grain2Amp >> 8) * grain2Decay;
// Scale output to the available range, clipping if necessary
output >>= 9;
if (output > 255) output = 255;
// Duane B
// rcarduino.blogspot.com
// 15/11/2012
// add a button to set bDelay true or false to turn delay on and off
if(bDelay)
{
// Output to PWM (this is faster than using analogWrite)
// Here we add the delay buffer to the output value, this produces
// an subtle echo effect, the delay buffer is effectivley replaying the sound from
// 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite
PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around
sDelayBuffer[nDelayCounter] = PWM_VALUE;
nDelayCounter++;
if(nDelayCounter == MAX_DELAY)
{
nDelayCounter = 0;
}
}
else
{
LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite
PWM_VALUE = output;
}
}
This is gonna sound silly, but how should the button for the delay be wired?
ReplyDeleteThank you so much for updating this code!
LEGIT FULLZ & TOOLS STORE
DeleteHello to All !
We are offering all types of tools & Fullz on discounted price.
If you are in search of anything regarding fullz, tools, tutorials, Hack Pack, etc
Feel Free to contact
***CONTACT 24/7***
**Telegram > @leadsupplier
**ICQ > 752822040
**Skype > Peeterhacks
**Wicker me > peeterhacks
"SSN LEADS/FULLZ AVAILABLE"
"TOOLS & TUTORIALS AVAILABLE FOR HACKING, SPAMMING,
CARDING, CASHOUT, CLONING, SCRIPTING ETC"
**************************************
"Fresh Spammed SSN Fullz info included"
>>SSN FULLZ with complete info
>>CC With CVV (vbv & non vbv) Fullz USA
>>FULLZ FOR SBA, PUA & TAX RETURN FILLING
>>USA I.D Photos Front & Back
>>High Credit Score fullz (700+ Scores)
>>DL number, Employee Details, Bank Details Included
>>Complete Premium Info with Relative Info
***************************************
COMPLETE GUIDE FOR TUTORIALS & TOOLS
"SPAMMING" "HACKING" "CARDING" "CASH OUT"
"KALI LINUX" "BLOCKCHAIN BLUE PRINTS" "SCRIPTING"
"FRAUD BIBLE"
"TOOLS & TUTORIALS LIST"
=>Ethical Hacking Ebooks, Tools & Tutorials
=>Bitcoin Hacking
=>Kali Linux
=>Fraud Bible
=>RAT
=>Keylogger & Keystroke Logger
=>Whatsapp Hacking & Hacked Version of Whatsapp
=>Facebook & Google Hacking
=>Bitcoin Flasher
=>SQL Injector
=>Premium Logs (PayPal/Amazon/Coinbase/Netflix/FedEx/Banks)
=>Bitcoin Cracker
=>SMTP Linux Root
=>Shell Scripting
=>DUMPS with pins track 1 and 2 with & without pin
=>SMTP's, Safe Socks, Rdp's brute
=>PHP mailer
=>SMS Sender & Email Blaster
=>Cpanel
=>Server I.P's & Proxies
=>Viruses & VPN's
=>HQ Email Combo (Gmail, Yahoo, Hotmail, MSN, AOL, etc.)
*Serious buyers will always welcome
*Price will be reduce in bulk order
*Discount offers will gives to serious buyers
*Hope we do a great business together
===>Contact 24/7<===
==>Telegram > @leadsupplier
==>ICQ > 752822040
==>Skype > Peeterhacks
==>Wicker me > peeterhacks
Like this - http://arduino.cc/en/Tutorial/Button but connected to digital pin 4
ReplyDeleteDuane B
Hi Duane, I've built my Auduino in a breadboard with a china made Arduino Duemilanove (Not official that is), could that be the reason it doesn't work? Wiring is OK, but only the first potentiometer seems to work. Wiring is not the problem, as if I switch cables on the breadboard strange noises happen :)
ReplyDeleteRegards,
Pancho
Hi,
ReplyDeleteIf only one pot seems to work, this is usually because the 'volatile' fix is not being used in the code.
Are you using the exact code above or the code from the original tinkerkit link, the tinkerkit code has the bug and does not look as if anyone is every going to update it, the code above has the fix.
Duane B
Hi Duane,
ReplyDeleteim just wondering what the smooth pin 8 is for is something supposed to be connected to that pin?
thanks
Hi,
ReplyDeleteAt one point it was possible to choose which scale the Auduino would use by changing this line based on user input - syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL)); however having tried many different scales and smooth transitions without scales - this option gives the best results. You can try adding other scales by adding a new map function, the chromatic scale is still in the code so an easy place to start. Also check the blog in a few days, I have finished a really nice resonant low pass filter for Arduino audio projects that I will write up and post over the next few days. Duane.
Hi Duane
ReplyDeleteI am trying to upload this code to arduino Micro. But there is problem - TCCR2A was not declared as scope - and higlighted part of the code is TCCR2A = _BV(COM2B1) | _BV(WGM20);. Do you have any solutions or maybe code for arduino micro.
pls REPLAY
thanks
Try TCCR2 without the A for the Micro.
DeleteDuane B
Update, try this as well, change the audioOn function from this -
Deletevoid audioOn() {
#if defined(__AVR_ATmega8__)
// ATmega8 has different registers
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
TIMSK = _BV(TOIE2);
#elif defined(__AVR_ATmega1280__)
TCCR3A = _BV(COM3C1) | _BV(WGM30);
TCCR3B = _BV(CS30);
TIMSK3 = _BV(TOIE3);
#else
// Set up PWM to 31.25kHz, phase accurate
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);
#endif
}
to this -
void audioOn() {
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
TIMSK = _BV(TOIE2);
}
Duane B
still same problem...
Deleteerror copy:
auduino_v5.ino: In function 'void audioOn()':
auduino_v5:147: error: 'TCCR2' was not declared in this scope
auduino_v5:147: error: 'WGM20' was not declared in this scope
auduino_v5:147: error: 'COM21' was not declared in this scope
auduino_v5:147: error: 'CS20' was not declared in this scope
auduino_v5:148: error: 'TIMSK' was not declared in this scope
auduino_v5.ino: In function 'void TIMER2_OVF_vect()':
auduino_v5:245: error: 'OCR2B' was not declared in this scope
auduino_v5:259: error: 'OCR2B' was not declared in this scope
The micro uses a different chip, an ATMega32u4, its very similar to the ATMega328 in the Arduino UNO however if your accessing the chip registers directly some of them are named differently. To convert the timer counter registers from ATMega328 to ATMega32u4, refer to page 89 onwards of the datasheet here - http://www.atmel.com/Images/doc7766.pdf
ReplyDeleteDuane B
hi i'm new at this, wich libraries should i download? or they come with the arduino software by default?
ReplyDeleteHi DuaneB I was wondering how is your code licensed to the public, as gnu licensed or what else?
ReplyDelete
ReplyDeleteI found this is an informative blog and also very useful and knowledgeable. I would like to thank you for the efforts you have made in writing this blog
buy modalert online
buy artvigil online
buy waklert online
buy modvigil online
buy vilafinil online
LEGIT FULLZ & TOOLS STORE
ReplyDeleteHello to All !
We are offering all types of tools & Fullz on discounted price.
If you are in search of anything regarding fullz, tools, tutorials, Hack Pack, etc
Feel Free to contact
***CONTACT 24/7***
**Telegram > @leadsupplier
**ICQ > 752822040
**Skype > Peeterhacks
**Wicker me > peeterhacks
"SSN LEADS/FULLZ AVAILABLE"
"TOOLS & TUTORIALS AVAILABLE FOR HACKING, SPAMMING,
CARDING, CASHOUT, CLONING, SCRIPTING ETC"
**************************************
"Fresh Spammed SSN Fullz info included"
>>SSN FULLZ with complete info
>>CC With CVV (vbv & non vbv) Fullz USA
>>FULLZ FOR SBA, PUA & TAX RETURN FILLING
>>USA I.D Photos Front & Back
>>High Credit Score fullz (700+ Scores)
>>DL number, Employee Details, Bank Details Included
>>Complete Premium Info with Relative Info
***************************************
COMPLETE GUIDE FOR TUTORIALS & TOOLS
"SPAMMING" "HACKING" "CARDING" "CASH OUT"
"KALI LINUX" "BLOCKCHAIN BLUE PRINTS" "SCRIPTING"
"FRAUD BIBLE"
"TOOLS & TUTORIALS LIST"
=>Ethical Hacking Ebooks, Tools & Tutorials
=>Bitcoin Hacking
=>Kali Linux
=>Fraud Bible
=>RAT
=>Keylogger & Keystroke Logger
=>Whatsapp Hacking & Hacked Version of Whatsapp
=>Facebook & Google Hacking
=>Bitcoin Flasher
=>SQL Injector
=>Premium Logs (PayPal/Amazon/Coinbase/Netflix/FedEx/Banks)
=>Bitcoin Cracker
=>SMTP Linux Root
=>Shell Scripting
=>DUMPS with pins track 1 and 2 with & without pin
=>SMTP's, Safe Socks, Rdp's brute
=>PHP mailer
=>SMS Sender & Email Blaster
=>Cpanel
=>Server I.P's & Proxies
=>Viruses & VPN's
=>HQ Email Combo (Gmail, Yahoo, Hotmail, MSN, AOL, etc.)
*Serious buyers will always welcome
*Price will be reduce in bulk order
*Discount offers will gives to serious buyers
*Hope we do a great business together
===>Contact 24/7<===
==>Telegram > @leadsupplier
==>ICQ > 752822040
==>Skype > Peeterhacks
==>Wicker me > peeterhacks
The increasing popularity of ready-to-drink products is also increasing the demand for coffee concentrates in the market. The high paced lifestyles of consumers are shifting the choice of people from traditional coffee to the concentrated form. Additionally, the use of instant coffee in homes and offices is likely to create opportunities for the coffee concentrate market.
ReplyDeleteAlso Read: acquired disease testing market, Latin America Generic Injectables Market.
Good day everyone, San Wilson here! If you are wondering how many pages is 1000 words, contact me any time and I will provide you with full guidance.
ReplyDeleteHi Everyone! My name is Michael and I am an experienced author working at an independent tech-support firm
ReplyDeleteWhy Is My Canon Printer Offline
ReplyDeleteallbet casino online gambling service provider that meets international standards and meets all gambler needs.
ReplyDeletenaza8k We are number 1 in total entertainment.
ReplyDeleteOh my goodness, this is so exciting! I absolutely have to investigate further!
ReplyDeleteCMOLDS leads as the premier mobile app development company uae, offering cutting-edge solutions tailored to your business needs. Elevate your digital presence with our expert team crafting innovative mobile experiences.
ReplyDelete