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.
ReplyDeletePython training develops professional coding skills.It emphasizes practical assignments.This Python training supports career advancement.It is reliable.
ReplyDeleteMaster UI/UX design skills to create intuitive, visually appealing interfaces with real-world project experience.learn ui ux
ReplyDeleteiOS developer training focuses on professional application development skills for Apple platforms. It explains real-world implementation methods. This ios developer training strengthens programming expertise. Learners develop scalable applications. App architecture is included. Testing procedures are covered. Hands-on practice is provided. It prepares job-ready developers.
ReplyDeleteSalesforce admin course online delivers flexible learning on platform setup and reporting tools. It explains dashboard creation and customization clearly. This salesforce admin course online strengthens practical CRM knowledge. Learners work on implementation projects. Case studies are included. Expert mentorship is provided. It prepares career-focused administrators.
ReplyDeleteNice write-up! ServiceNow online training is a convenient way to gain practical platform skills, learn ITSM best practices, and prepare for real-world ServiceNow administration roles.servicenow online training
ReplyDeleteGreat post! Our devops full course
ReplyDeletebuilds real-world skills in CI/CD, automation, and cloud deployment.
Great article! I was searching for the best Mulesoft developer course to understand API development and integration concepts. This information is very helpful for beginners who want to build strong MuleSoft development skills.mulesoft developer course
ReplyDeleteGreat article and very informative. Many professionals are enrolling in a Salesforce CPQ course to learn advanced sales automation and quoting processes. A well-structured Salesforce CPQ course provides hands-on experience with configuration, pricing rules, and quote generation in Salesforce.salesforce cpq course
ReplyDeleteGreat article! OnlineITGuru’s servicenow training classes are very helpful for beginners and professionals who want to improve their ServiceNow expertise.servicenow training classes
ReplyDeleteIf you want hands-on experience in API development and management, the Mulesoft API Training by OnlineITGuru provides comprehensive modules covering RAML, API design, and deployment. It’s ideal for professionals seeking practical skills in creating scalable and secure APIs for modern businessesmulesoft api training
ReplyDeleteFor professionals in India looking to boost their integration skills, MuleSoft Training India from OnlineITGuru offers expert-led sessions, practical projects, and certification guidance. It’s a great way to enhance your career in cloud integration and enterprise application developmentMuleSoft Training India
ReplyDeleteSalesforce CPQ is one of the most valuable skills in today’s CRM job market. Organizations worldwide are searching for skilled professionals who can design and implement efficient sales automation systemslearn salesforce cpq
ReplyDeleteThanks for sharing such useful information about enterprise platforms. Many IT professionals are now upgrading their skills with Servicenow Training to understand workflow automation, platform configuration, and IT service management processes. OnlineITGuru offers structured learning with practical examples.servicenow training
ReplyDeleteVery informative article! Many IT professionals are now exploring the Servicenow Administrator Course to improve their skills in ITSM and automation. Training programs offered by OnlineITGuru include practical labs and real-world scenarios that make it easier to understand ServiceNow administration concepts.servicenow administrator course
ReplyDeleteThanks for sharing this useful information about sales automation platforms. A Salesforce CPQ Course helps professionals understand product configuration, pricing strategies, and quote generation within Salesforce. OnlineITGuru offers structured training programs that include practical exercises and real-world use cases.salesforce cpq course
ReplyDeleteIntegration skills are becoming very important in modern IT environments. I recently came across Mulesoft Training programs from OnlineITGuru that focus on API-led connectivity, real-time projects, and practical learning. It’s a good option for developers who want to work with enterprise integration platforms.mulesoft training
ReplyDeleteOur MuleSoft Developer Training focuses on building REST and SOAP APIs, data transformation with DataWeave, and designing scalable integration solutions. Practical exercises ensure hands-on experience.mulesoft developer training
ReplyDelete"This article on ITSM concepts was very insightful! The Servicenow Administrator Course offered by OnlineITGuru covers platform configuration, workflow automation, and ITSM modules, perfect for career advancement.servicenow administrator course
ReplyDeleteNice blog post. Mulesoft certification training helps developers prepare for certification exams while gaining practical API development skills.mulesoft certification training
ReplyDeleteExcellent information shared here. ServiceNow Admin Training is essential for IT professionals who want to build strong skills in ServiceNow platform administration.servicenow admin training
ReplyDelete