From 1c8ab50f0a793c2497f4f4eb3c34e65777a69b9b Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Sun, 29 Jan 2023 16:00:59 -0700 Subject: [PATCH] Noise generator, for #54 --- static/index.html | 23 +++++++ static/scripts/noise.mjs | 132 ++++++++++++++++++++++++++++++++------- static/scripts/vail.mjs | 10 +++ 3 files changed, 143 insertions(+), 22 deletions(-) diff --git a/static/index.html b/static/index.html index 276aa63..946514a 100644 --- a/static/index.html +++ b/static/index.html @@ -268,6 +268,29 @@ + +
+
+ +
+
+
+
+ +
+
+
+
diff --git a/static/scripts/noise.mjs b/static/scripts/noise.mjs index 5b60fb1..2f72cd7 100644 --- a/static/scripts/noise.mjs +++ b/static/scripts/noise.mjs @@ -1,35 +1,123 @@ +import {AudioSource, AudioContextTime} from "./audio.mjs" /** - * Create a noise generator with a low pass filter + * Create a white noise generator with a biquad filter * * @param {AudioContext} context Audio context - * @param {Number} lowpassFreq Low-pass filter frequency (Hz) - * @returns {GainNode} Gain object for noise + * @returns {BiquadFilterNode} Noise filter */ - function Noise(context, lowpassFreq = 100) { + function WhiteNoise(context) { let bufferSize = 17 * context.sampleRate - let noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate) + let noiseBuffer = new AudioBuffer({ + sampleRate: context.sampleRate, + length: bufferSize, + }) let output = noiseBuffer.getChannelData(0) for (let i = 0; i < bufferSize; i++) { - output[i] = Math.random() * 2 - 1; + output[i] = Math.random() * 2 - 1 } - let whiteNoise = context.createBufferSource(); - whiteNoise.buffer = noiseBuffer; - whiteNoise.loop = true; - whiteNoise.start(0); + let whiteNoise = context.createBufferSource() + whiteNoise.buffer = noiseBuffer + whiteNoise.loop = true + whiteNoise.start(0) - let filter = context.createBiquadFilter() - filter.type = "lowpass" - filter.frequency.value = lowpassFreq - - let gain = context.createGain() - gain.gain.value = 0.1 - - whiteNoise.connect(filter) - filter.connect(gain) - - return gain -} + let noiseFilter = new BiquadFilterNode(context, {type: "bandpass"}) + whiteNoise.connect(noiseFilter) + + return noiseFilter } +class Noise extends AudioSource { + /** + * + * @param {AudioContext} context + */ + constructor(context, noises=2) { + super(context) + + this.whiteNoise = [] + + for (let i = 0; i < noises; i++) { + let wn = { + modulator: new OscillatorNode(context), + modulatorGain: new GainNode(context), + filter: WhiteNoise(context), + filterGain: new GainNode(context), + } + + wn.modulator.frequency.value = 0 + wn.modulatorGain.gain.value = 0 + wn.filter.frequency.value = 800 + wn.filterGain.gain.value = 0.8 / noises + + wn.modulator.connect(wn.modulatorGain) + wn.modulatorGain.connect(wn.filter.frequency) + wn.filter.connect(wn.filterGain) + wn.filterGain.connect(this.masterGain) + + wn.modulator.start() + this.whiteNoise.push(wn) + } + + this.SetNoiseParams(0, 0.07, 70, 400, 0.4) + this.SetNoiseParams(1, 0.03, 200, 1600, 0.4) + this.masterGain.gain.value = 0.5 + } + + /** + * Set modulator frequency + * + * You probably want this to be under 1Hz, for a subtle sweeping effect + * + * @param {Number} n Which noise generator + * @param {Number} frequency Frequency (Hz) + */ + SetNoiseModulator(n, frequency) { + this.whiteNoise[n].modulator.frequency.value = frequency + } + + /** + * Set modulator depth + * + * The output of the modulator [-1,1] is multiplied this and added to the + * base frequency of the filter. + * + * @param {Number} n Which noise generator + * @param {Number} depth Depth of modulation + */ + SetNoiseDepth(n, depth) { + this.whiteNoise[n].modulatorGain.gain.value = depth + } + + /** + * Set noise filter base frequency + * + * @param {Number} n Which noise generator + * @param {Number} frequency Frequency (Hz) + */ + SetNoiseFrequency(n, frequency) { + this.whiteNoise[n].filter.frequency.value = frequency + } + + /** + * Set gain of a noise generator + * + * @param {Number} n Which noise generator + * @param {Number} gain Gain level (typically [0-1]) + */ + SetNoiseGain(n, gain) { + this.whiteNoise[n].filterGain.gain.value = gain + } + + SetNoiseParams(n, modulatorFrequency, depth, baseFrequency, gain) { + this.SetNoiseModulator(n, modulatorFrequency) + this.SetNoiseDepth(n, depth) + this.SetNoiseFrequency(n, baseFrequency) + this.SetNoiseGain(n, gain) + } +} + +export { + Noise, +} diff --git a/static/scripts/vail.mjs b/static/scripts/vail.mjs index b2c1bf9..38d3923 100644 --- a/static/scripts/vail.mjs +++ b/static/scripts/vail.mjs @@ -7,6 +7,7 @@ import * as I18n from "./i18n.mjs" import * as time from "./time.mjs" import * as Music from "./music.mjs" import * as Icon from "./icon.mjs" +import * as Noise from "./noise.mjs" const DefaultRepeater = "General" @@ -46,6 +47,12 @@ class VailClient { // Outputs this.outputs = new Outputs.Collection(globalAudioContext) this.outputs.connect(globalAudioContext.destination) + + // Noise + this.noise = new Noise.Noise(globalAudioContext) + this.noise.connect(globalAudioContext.destination) + + // App icon this.icon = new Icon.Icon() // Keyers @@ -90,6 +97,9 @@ class VailClient { this.inputInit("#masterGain", e => { this.outputs.SetGain(e.target.value / 100) }) + this.inputInit("#noiseGain", e => { + this.noise.SetGain(e.target.value / 100) + }) let toneTransform = { note: Music.MIDINoteName, freq: Music.MIDINoteFrequency,