シンプルなのに出音がすごいシンセサイザ | ノイズのなぐさめ

ノイズのなぐさめ

歪んだ美意識。
ちっぽけ 些々たる しがない
たわい無い 些末 末梢的
スマート、スィート、デンジャラス
大切なのは長く働くこと。

// Auduino, the Lo-Fi granular synthesiser

http://pc.watch.impress.co.jp/docs/column/musashino_proto/20090716_302590.html
// by Peter Knight, Tinker.it http://tinker.it
// http://code.google.com/p/tinkerit/
// Code license: GNU LGPL
// Content license: CC3.0 BY-SA
// 日本語コメント: 武蔵野電波

#include <avr/io.h>
#include <avr/interrupt.h>

// アナログ入力ピン
#define SYNC_CONTROL (4) // ピン4: グレイン繰り返し周波数
#define GRAIN_FREQ_CONTROL (0) // ピン0: グレイン1 ピッチ
#define GRAIN_DECAY_CONTROL (2) // ピン2: グレイン1 ディケイ
#define GRAIN2_FREQ_CONTROL (3) // ピン3: グレイン2 ピッチ
#define GRAIN2_DECAY_CONTROL (1) // ピン1: グレイン2 ディケイ

// オーディオ出力=デジタルピン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

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;

// マッピング(ペンタトニック)
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]);
}

// 対数マッピング
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);
}


void audioOn() {
// PWMを31.25kHzに設定(正確な位相)
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);
}

void setup() {
pinMode(PWM_PIN,OUTPUT);
audioOn();
pinMode(LED_PIN,OUTPUT);
}

void loop() {
// loopはとてもシンプルです - オシレータの変数を更新しているだけです

// ペンタトニック・マッピングを使用: D, E, G, A, B
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) {
// 次のグレインがスタートする時間
grainPhaseAcc = 0;
grainAmp = 0x7fff;
grain2PhaseAcc = 0;
grain2Amp = 0x7fff;
LED_PORT ^= 1 << LED_BIT; // digitalWriteより高速
}

// グレインオシレータの位相を進める
grainPhaseAcc += grainPhaseInc;
grain2PhaseAcc += grain2PhaseInc;

// 位相を三角波に変換
value = (grainPhaseAcc >> 7) & 0xff;
if (grainPhaseAcc & 0x8000) value = ~value;
// 現在のグレイン振幅を掛けてサンプルを取得
output = value * (grainAmp >> 8);

// 2つ目のグレイン
value = (grain2PhaseAcc >> 7) & 0xff;
if (grain2PhaseAcc & 0x8000) value = ~value;
output += value * (grain2Amp >> 8);

// グレイン振幅にディケイを反映させる(指数関数的ディケイ)
grainAmp -= (grainAmp >> 8) * grainDecay;
grain2Amp -= (grain2Amp >> 8) * grain2Decay;

// 出力をスケーリング(必要ならクリッピング)
output >>= 9;
if (output > 255) output = 255;

// PWMに出力 (analogWriteよりも高速)
PWM_VALUE = output;
}