LFO

ピッチにLFOをかけたサンプル。マウスの横軸でLFOの周波数を、縦軸で振幅を操作できます。
http://toshiyakobayashi.googlepages.com/LFOExample.html
ご覧の通りブチブチ鳴りますね。バッファの切れ目がどうなっているか分かるように、audioStreamWriteが呼ばれたときのバッファ処理の前後で波形を描画しています。このブチブチ(Glitch Noise)を解消する汎用的なクラスを・・・次回アップします。

import krister.Ess.*;

float SAMPLE_RATE = 44100;
long currentFrame = 0;

AudioStream myStream; // Audio stream to write into
LFO lfo;
int offset = 0;
int BUFFSIZE = 10000;

void setup() {
  size(640, 480);
  Ess.start(this); // Start Ess
  myStream = new AudioStream(BUFFSIZE); // Create a new AudioStream
  myStream.smoothPan = true;
  myStream.pan = -1.0f;
  lfo = new LFO(1f, 200);
  myStream.start(); // Start audio
  noLoop();
}

void draw() {
}

void mouseMoved() {  
}

void audioStreamWrite(AudioStream s) {
  background(255);

  // バッファの切れ目の波形を描画
  int lx = 0, ly = height / 2;
  for (int i = 0; i < width / 2; i++) {
    int x, y;
    x = i;
    y = (int)((s.buffer[s.buffer.length - (width / 2) + i] * -1 + 1.0f) / 2 * height);
    line(lx, ly, x, y);
    lx = x;
    ly = y;
  }

  float last2 = s.buffer[s.buffer.length-2];
  float last1 = s.buffer[s.buffer.length-1];

  // process !!
  process(s);

  for (int i = 0; i < (width / 2); i++) {
    int x, y;
    x = i + (width / 2);
    y = (int)((s.buffer[i] * -1 + 1.0f) / 2 * height);
      line(lx, ly, x, y);
      lx = x;
      ly = y;
  }

  redraw();
}

void process(AudioStream s) {
  
  // mouseXで周波数、mouseYで振幅を操作する
  lfo.fq = (float)mouseX / width * 10;
  lfo.amp = (float)(height - mouseY) / height * 200;
  
  // 1波形ずつ生成
  for (int offset = 0; offset < s.buffer.length; ) {
    float fq = lfo.getValue(currentFrame + offset) + 440;
    int genFrames = (int)((1 / fq) * SAMPLE_RATE);
    SineWave wave = new SineWave(fq, 1f);
    wave.generate(s, offset, genFrames);
    offset += genFrames;
  }
  
  currentFrame += s.buffer.length;
}

class LFO {
  float fq;
  float amp;
  
  LFO(float fq, float amp) {
    this.fq = fq;
    this.amp = amp;
  }

  float getValue(long frame) {
    float phaseRad = getRadianByFrame(frame, fq);
    return (float)Math.sin(phaseRad) * amp;    
  }
  
  float getRadianByFrame(long frame, float fq) {
    int priodFrames = (int)((1F / fq) * SAMPLE_RATE); // 1周期のframe数
    int phaseFrame = (int)(frame % priodFrames); // 位相をframe数で
    return (float)((phaseFrame / (float)priodFrames) * 2 * Math.PI); // 位相をラジアンで
  }
}