Auduino Sequencer

860 views
Skip to first unread message

Goatboy

unread,
Jun 9, 2010, 1:04:54 PM6/9/10
to Auduino
Hello all, I'm trying to take the Auduino and create a step sequencer
with it. At this stage I just want to get the Auduino to play
continually through a looped sequence of notes from an array similar
to some of the Arduino examples that are available. I've borrowed a
piece of code from the "Arduino Punk Console".......................

void loop() {
// Main sequence loop
for (int i=0; i<4; i++)
{
audioOn(); (steps[i], duration);
}
delay (tempo);

I've replaced "freqout" from the Punk Console with "audioOn" to see if
it might work in the same way but to no avail. Also the array I'm
using is.......int steps[] = {77,82,86,92}; .......But I imagine this
is also wrong and I should be accessing the pentatonicTable in some
way. Is it possible because of the way the code works for the Auduino,
that I can't create a stepped sequence?
Below is the code that I'm working with. I've whittled it back
from the original Auduino code for simplicity's sake. Hope someone can
help, Thanks.........


// 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;
uint16_t syncPhaseInc;
uint16_t grainPhaseAcc;
uint16_t grainPhaseInc;
uint16_t grainAmp;
uint8_t grainDecay;
uint16_t grain2PhaseAcc;
uint16_t grain2PhaseInc;
uint16_t grain2Amp;
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)
#define AnalogInTempo (5)
#define AnalogInDuration (6)

#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


// 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 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() {

// Set up PWM to 31.25kHz, phase accurate
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);

}
// Set up the array for each step
int steps[] = {77,82,86,92};

int duration = 50;
int tempo = 100;


void setup() {
//audioOn();
pinMode(PWM_PIN,OUTPUT);
pinMode(LED_PIN,OUTPUT);
int steps[] = {77,82,86,92};
int duration = 50;
int tempo = 100;

}

void loop() {
// Main sequence loop
for (int i=0; i<4; i++)
{
audioOn(); (steps[i], duration);
}
delay (tempo);



syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));

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;

// Output to PWM (this is faster than using analogWrite)
PWM_VALUE = output;


}

Mathlar

unread,
Jul 20, 2010, 6:37:47 AM7/20/10
to Auduino
I've just finished building a prototype that merges the two projects
myself. I've taken the basic ideas from Beavis Audio's Punk console -
so that there are 8 step slots which can each have a separate grain
repetition rate. The grain1/grain2 frequencies and decays are
global, affecting all 8 slots, and I've added another analogue input
that sets a note duration, so that the auduino steps through one note
after another (setting the relevant grain repetition rate
accordingly).

It's actually pretty easy but you shouldn't change the structure of
the code quite so radically or you'll affect the interrupt handling.
First up, leave the audioOn() call in setup() - this configures PWM on
pin 3, and this needs to be done once and once only to setup the
hardware. Next, loop() should just be used to read the pots and input
buttons - all sound generation should still be done in the interrupt
handling code. Finally, you've declared your new variables inside
setup() itself - this makes them local to that function and they don't
exist outside of that scope.

Here's a summary of the changes I made, which should get you started.
I won't post the full code yet, as I should really provide the
schematic also and I've not finished working on it yet. I also need
to add credits to Beavis Audio Research before I post it.

- I added some new variables for the note timing and replaced the
individual syncPhaseInc variable with an array:

// Current playing note and current note accumulator
uint8_t note;
uint16_t noteDurationInc;
uint16_t noteDurationAcc;

// Note grain repetition frequencies and accumulator variable
uint16_t syncPhaseIncs[] = { 0, 0, 0, 0, 0, 0, 0, 0 };

- Then I added a new analogue input for the note duration:

// Map Analogue channels
#define NOTEDURATION (5)

- I added variables for the digital inputs (working around pins 3 and
11 - I might extend the code to use pin 11 with the same interrupt
timing, to either add stereo reverb/effects, or to add a monitor so
that you can hear the synth in a pair of headphones but output the
sequence to an amp on pin 3)

// Map digital channels
#define DIGITALINSWITCH0 (2)
#define DIGITALINSWITCH1 (4)
#define DIGITALINSWITCH2 (5)
#define DIGITALINSWITCH3 (6)
#define DIGITALINSWITCH4 (7)
#define DIGITALINSWITCH5 (8)
#define DIGITALINSWITCH6 (9)
#define DIGITALINSWITCH7 (10)

- Then at the end of setup(), I initialise the digital inputs:

// Set input channels
pinMode(DIGITALINSWITCH0, INPUT);
pinMode(DIGITALINSWITCH1, INPUT);
pinMode(DIGITALINSWITCH2, INPUT);
pinMode(DIGITALINSWITCH3, INPUT);
pinMode(DIGITALINSWITCH4, INPUT);
pinMode(DIGITALINSWITCH5, INPUT);
pinMode(DIGITALINSWITCH6, INPUT);
pinMode(DIGITALINSWITCH7, INPUT);

- Now in loop(), I remove the call to set syncPhaseInc, and instead
call a subroutine to read the buttons like in the Punk console, and to
also read the note duration:

void loop()
{

readSwitches();

noteDurationInc = mapPhaseInc(analogRead(NOTEDURATION));

... code continues as before to set grain variables
}

- Here is a code snippet for readSwitches() - should look pretty
familiar at this stage:

void readSwitches()
{
// check switch 0, if pressed, get the current freq into step 0,
etc. etc.
if (digitalRead (DIGITALINSWITCH0) == HIGH)
{
syncPhaseIncs[0] = mapPentatonic(analogRead(SYNC_CONTROL));
}

else if (digitalRead (DIGITALINSWITCH1) == HIGH)
{
syncPhaseIncs[1] = mapPentatonic(analogRead(SYNC_CONTROL));
}

else if (....)
}

- And finally here are the changes at the top of my SIGNAL interrupt
call. I increment the note duration until there's an overflow and
then we move onto the next note. I also added a bit of a cludge to
allow me to set the repetition rate for a note to 0 and turn it off -
note that this *only* works with the pentatonic mapping for now, as
that's the only mapping capable of returning 0. I need to think of
another way to handle that...

SIGNAL(PWM_INTERRUPT)
{
uint8_t value;
uint16_t output;

noteDurationAcc += noteDurationInc;

if (noteDurationAcc < noteDurationInc)
{
note++;
syncPhaseAcc = 0;
if (note == 8) note = 0;
}

if (syncPhaseIncs[note] == 0)
{
PWM_VALUE = 0;
LED_PORT = 0;
return;
}

syncPhaseAcc += syncPhaseIncs[note];
if (syncPhaseAcc < syncPhaseIncs[note]) {

... code continues as before
Reply all
Reply to author
Forward
0 new messages