ピッチに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); // 位相をラジアンで } }