petersalomonsen / javascriptmusic

A.K.A. WebAssembly Music. Live coding music and synthesis in Javascript / AssemblyScript (WebAssembly)
https://petersalomonsen.com
GNU General Public License v3.0
382 stars 32 forks source link

Inverse FFT synthesis #27

Closed petersalomonsen closed 4 years ago

petersalomonsen commented 4 years ago

Additive synthesis using inverse FFT.

Includes an FFT implementation for Assemblyscript: https://github.com/petersalomonsen/javascriptmusic/blob/d56b6a7e4cdc65115334d30f383fa075e2bce716/wasmaudioworklet/synth1/assembly/math/fft.ts

// create oscillator of `pow(2,8)` frames (256) 
const osc: IFFTOscillator = new IFFTOscillator(8);
// specify real and imaginary levels
osc.createWave(
          [0,0,0,0],
          [1, 1/2, 1/4, 1/8]);
osc.fft.calculateInverse();
osc.frequency = 440; // set frequency of tone to be played
// get sample for next frame
osc.next();

Example MIDI instrument with LFO oscillators altering the levels of different frequencies:

export class FFTSynthTest extends MidiVoice {
    readonly envelope: Envelope = new Envelope(0.01, 0.5, 0.1, 0.5);
    readonly lfo: SineOscillator = new SineOscillator();
    readonly lfo2: SineOscillator = new SineOscillator();
    readonly lfo3: SineOscillator = new SineOscillator();
    readonly osc: IFFTOscillator = new IFFTOscillator(8);
    pan: Pan = new Pan();

    framecount: i32 = 0;

    noteon(note: u8, velocity: u8): void {
      super.noteon(note, velocity);

      this.osc.frequency = notefreq(note);
      this.lfo.frequency = 256;
      this.lfo2.frequency = 512;
      this.lfo3.frequency = 1024;
      this.pan.setPan(note as f32/ 127.0);
      this.envelope.attack();   
    }

    noteoff(): void {
      this.envelope.release();

    }

    nextframe(): void {   
      const env: f32 = this.envelope.next();
      if((this.framecount & 0xff) === 0) {
        this.osc.createWave(
          [0.0,env,0,0,0,0,0.1,env,0.2,0,0,env,0,0],
          [1 * env,this.lfo.next()* env,this.lfo2.next() * env,0.5 * env,this.lfo3.next() * env,env,env,,,env,env]);
        this.osc.fft.calculateInverse();
      }
      this.framecount ++;

        const signal = this.osc.next() * 0.2;

        this.channel.signal.add(signal * this.pan.leftLevel,
                         signal * this.pan.rightLevel);

    } 
}