Auduino port

Dependencies:   mbed

auduino.cpp

Committer:
kshoji
Date:
2010-09-15
Revision:
1:a553c5d93bb8
Parent:
0:831ef5c249ef

File content as of revision 1:a553c5d93bb8:

#include <math.h>
#include "mbed.h"

#define FEATURE_ORIGINAL_AUDUINO

// Auduino for mbed
// Ported and arranged by Kaoru Shoji

// Original source code:
// 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
//
// 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)

// Analog in p16: Grain 1 pitch
// Analog in p17: Grain 1 decay
// Analog in p19: Grain 2 pitch
// Analog in p20: Grain 2 decay
// Analog in p15: Grain repetition frequency
// Analog out p18: Audio out

Ticker interrupt;
AnalogIn SYNC_CONTROL(p15);
AnalogIn GRAIN_FREQ_CONTROL(p16);
AnalogIn GRAIN_DECAY_CONTROL(p17);
AnalogIn GRAIN2_FREQ_CONTROL(p19);
AnalogIn GRAIN2_DECAY_CONTROL(p20);
AnalogOut OUTPUT(p18);
DigitalOut LED_PIN(LED1);

float syncPhaseAcc;
float syncPhaseInc;

float grainPhaseAcc;
float grainPhaseInc;
float grainAmp;
float grainDecay;

float grain2PhaseAcc;
float grain2PhaseInc;
float grain2Amp;
float grain2Decay;

// Smooth logarithmic mapping
//
float mapPhaseInc(double input) {
  return pow(65536.0, 1.0 - input);
}

// 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(float input) {
  return midiTable[(int)(127.9 - input * 127.9)];
}

// 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(float input) {
  uint8_t value = ((int)(1023.9 - input * 1023.9)) / (1024/53);
  return pentatonicTable[value];
}

void updateLatestInputValue() {
  // 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.
  
#ifndef FEATURE_ORIGINAL_AUDUINO
  // Smooth frequency mapping
  syncPhaseInc = mapPhaseInc(SYNC_CONTROL) * 4.0;
#endif
  
  // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
  //syncPhaseInc = mapMidi(SYNC_CONTROL);
  
#ifdef FEATURE_ORIGINAL_AUDUINO
  // Stepped pentatonic mapping: D, E, G, A, B
  syncPhaseInc = mapPentatonic(SYNC_CONTROL);

  grainPhaseInc  = mapPhaseInc(GRAIN_FREQ_CONTROL) * 2.0;
  grainDecay     = GRAIN_DECAY_CONTROL * 128.0;
  grain2PhaseInc = mapPhaseInc(GRAIN2_FREQ_CONTROL) * 2.0;
  grain2Decay    = GRAIN2_DECAY_CONTROL * 256.0;
#else
  grainPhaseInc  = mapPhaseInc(GRAIN_FREQ_CONTROL) * 4.0;
  grainDecay     = GRAIN_DECAY_CONTROL * 16.0;
  grain2PhaseInc = mapPhaseInc(GRAIN2_FREQ_CONTROL) * 4.0;
  grain2Decay    = GRAIN2_DECAY_CONTROL * 32.0;
#endif
}

float adjustPhaseValue(float phaseAcc) {
  float value = phaseAcc / 128.0;
  value = ((value / 256.0) - (int)(value / 256.0)) * 256.0;
  if (value > 256) value = 256;
  if (phaseAcc > 32768) value = 256 - value;
  return value;
}

void interruptHandler() {
  float value;
  float output;
  
  updateLatestInputValue();

  syncPhaseAcc += syncPhaseInc;
  if (syncPhaseAcc > 65536.0) {
    syncPhaseAcc -= 65536.0;
  }
  if (syncPhaseAcc < syncPhaseInc) {
    // Time to start the next grain
    grainPhaseAcc = 0;
    grainAmp = 32768.0;
    grain2PhaseAcc = 0;
    grain2Amp = 32768.0;
    LED_PIN = !LED_PIN;
  }
  
  // Increment the phase of the grain oscillators
  grainPhaseAcc += grainPhaseInc;
  grain2PhaseAcc += grain2PhaseInc;
  if (grainPhaseAcc > 65536) grainPhaseAcc -= 65536;
  if (grain2PhaseAcc > 65536) grain2PhaseAcc -= 65536;

  // Convert phase into a triangle wave
  value = adjustPhaseValue(grainPhaseAcc);
  // Multiply by current grain amplitude to get sample
  output = value * (grainAmp / 512.0);

  // Repeat for second grain
  value = adjustPhaseValue(grain2PhaseAcc);
  output += value * (grain2Amp / 512.0);

  // Make the grain amplitudes decay by a factor every sample (exponential decay)
  grainAmp -= (grainAmp / 256.0) * grainDecay;
  grain2Amp -= (grain2Amp / 256.0) * grain2Decay;

  OUTPUT.write_u16(output);
}

int main() {
  interrupt.attach_us(&interruptHandler, 32);
}