// Oscillator.ts import { SAMPLE_RATE } from "./globals"; export enum OscillatorMode { OSCILLATOR_MODE_SINE, OSCILLATOR_MODE_SAW, OSCILLATOR_MODE_SQUARE, OSCILLATOR_MODE_TRIANGLE, } export type FrequencyFunction = (sample_i: number) => number; export type OscillatorArgs = { oscillator_mode?: OscillatorMode; frequency?: number; phase?: number; frequency_function?: FrequencyFunction; sample_rate?: number; }; export class Oscillator { oscillator_mode: OscillatorMode; frequency: number; phase: number; sample_rate: number; frequency_function: FrequencyFunction; phase_increment: number; constructor({ oscillator_mode = OscillatorMode.OSCILLATOR_MODE_SINE, frequency = 440, phase = 0, frequency_function = () => frequency, sample_rate = SAMPLE_RATE, }: OscillatorArgs) { this.oscillator_mode = oscillator_mode; this.frequency = frequency; this.phase = phase; this.sample_rate = sample_rate; this.frequency_function = frequency_function; this.phase_increment = (this.frequency * 2 * Math.PI) / this.sample_rate; } set_mode(mode: OscillatorMode) { this.oscillator_mode = mode; } set_frequency(frequency: number) { this.frequency = frequency; this.update_increment(); } set_sample_rate(sample_rate: number) { this.sample_rate = sample_rate; this.update_increment(); } next_sample() { const value = this.naive_wave_form(this.oscillator_mode); this.phase += this.phase_increment; while (this.phase >= 2 * Math.PI) { this.phase -= 2 * Math.PI; } // console.log(value); return value; } generate(buffer: AudioBuffer) { for (let channel = 0; channel < buffer.numberOfChannels; channel++) { // This gives us the actual array that contains the data const now_buffering = buffer.getChannelData(channel); for (let sample_i = 0; sample_i < buffer.length; sample_i++) { const next_frequency = this.frequency_function(sample_i); if (this.frequency !== next_frequency) { this.set_frequency(next_frequency); } now_buffering[sample_i] = this.next_sample(); } } } naive_wave_form(oscillator_mode: OscillatorMode) { let buffer_value = 0; switch (oscillator_mode) { case OscillatorMode.OSCILLATOR_MODE_SINE: buffer_value = Math.sin(this.phase); break; case OscillatorMode.OSCILLATOR_MODE_SAW: buffer_value = (2 * this.phase) / (2 * Math.PI) - 1; break; case OscillatorMode.OSCILLATOR_MODE_SQUARE: buffer_value = this.phase <= Math.PI ? 1 : -1; break; case OscillatorMode.OSCILLATOR_MODE_TRIANGLE: const value = -1 + (2 * this.phase) / (2 * Math.PI); buffer_value = 2 * (Math.abs(value) - 0.5); break; default: break; } return buffer_value; } update_increment() { this.phase_increment = (this.frequency * 2 * Math.PI) / this.sample_rate; } }