Fewer clicky noises
This commit is contained in:
parent
eea0e3f879
commit
953f33f023
132
index.js
132
index.js
@ -9,7 +9,8 @@ const audioCtx = new AudioContext();
|
|||||||
const frequencies = [];
|
const frequencies = [];
|
||||||
const holds = [];
|
const holds = [];
|
||||||
|
|
||||||
let globalVolume = 1.0;
|
let globalVolume = new GainNode(audioCtx, {gain: 1.0});
|
||||||
|
globalVolume.connect(audioCtx.destination);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
@ -19,48 +20,90 @@ let globalVolume = 1.0;
|
|||||||
* - keyboard playing (qwerty)
|
* - keyboard playing (qwerty)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function playNote(frequency, duration=86400) {
|
class Envelope {
|
||||||
let currentGain = globalVolume;
|
constructor() {
|
||||||
|
this.envelope = new GainNode(audioCtx);
|
||||||
const bassBoost = f => {
|
|
||||||
const t = Math.pow(f / 20000, 1/10);
|
|
||||||
const a = 1.0;
|
|
||||||
const b = 0.05;
|
|
||||||
|
|
||||||
return b * t + a * (1 - t);
|
|
||||||
};
|
|
||||||
|
|
||||||
const oscillators = [];
|
|
||||||
|
|
||||||
while (frequency < 20000) {
|
|
||||||
const oscillator = new OscillatorNode(audioCtx, {frequency});
|
|
||||||
const gain = new GainNode(audioCtx);
|
|
||||||
|
|
||||||
const g = bassBoost(frequency) * currentGain;
|
|
||||||
gain.gain.setValueAtTime(0, audioCtx.currentTime);
|
|
||||||
gain.gain.linearRampToValueAtTime(g, audioCtx.currentTime + 0.010);
|
|
||||||
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + duration);
|
|
||||||
|
|
||||||
oscillator.connect(gain).connect(audioCtx.destination);
|
|
||||||
oscillator.start();
|
|
||||||
|
|
||||||
oscillators.push(oscillator);
|
|
||||||
|
|
||||||
// break; // only one frequency for now
|
|
||||||
|
|
||||||
frequency *= 2;
|
|
||||||
currentGain *= 0.3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stop = () => {
|
hold(attack=0.040, decay=0.110, sustain=0.8) {
|
||||||
for (let oscillator of oscillators) {
|
this.envelope.gain.setValueAtTime(0, audioCtx.currentTime);
|
||||||
oscillator.stop();
|
this.envelope.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + attack);
|
||||||
|
this.envelope.gain.linearRampToValueAtTime(sustain, audioCtx.currentTime + decay);
|
||||||
|
}
|
||||||
|
|
||||||
|
release(release=0.200) {
|
||||||
|
this.envelope.gain.setTargetAtTime(0.0, audioCtx.currentTime + release, release/3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bassBoost(f) {
|
||||||
|
const t = Math.pow(f / 20000, 1/10);
|
||||||
|
const a = 1.0;
|
||||||
|
const b = 0.05;
|
||||||
|
|
||||||
|
return b * t + a * (1 - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Voice {
|
||||||
|
constructor(baseFrequency) {
|
||||||
|
this.baseFrequency = baseFrequency;
|
||||||
|
this.oscillators = [];
|
||||||
|
this.envelope = new Envelope();
|
||||||
|
|
||||||
|
let frequency = baseFrequency;
|
||||||
|
let gain = 1.0;
|
||||||
|
|
||||||
|
while (frequency < 20000) {
|
||||||
|
const oscillator = new OscillatorNode(audioCtx, {frequency});
|
||||||
|
const volume = new GainNode(audioCtx, {gain: bassBoost(frequency) * gain});
|
||||||
|
oscillator
|
||||||
|
.connect(volume)
|
||||||
|
.connect(this.envelope.envelope)
|
||||||
|
.connect(globalVolume);
|
||||||
|
|
||||||
|
this.oscillators.push(oscillator);
|
||||||
|
|
||||||
|
frequency *= 2;
|
||||||
|
gain *= 0.3;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
setTimeout(stop, duration*1000);
|
play(duration=86400) {
|
||||||
|
this.start();
|
||||||
|
|
||||||
return stop;
|
setTimeout(() => {
|
||||||
|
this.stop();
|
||||||
|
}, duration * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.envelope.hold();
|
||||||
|
for (let oscillator of this.oscillators) {
|
||||||
|
oscillator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.envelope.release();
|
||||||
|
for (let oscillator of this.oscillators) {
|
||||||
|
//oscillator.stop(0.200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tune(newBaseFrequency) {
|
||||||
|
const adjust = newBaseFrequency / this.baseFrequency;
|
||||||
|
this.baseFrequency = newBaseFrequency;
|
||||||
|
|
||||||
|
for (let oscillator of this.oscillators) {
|
||||||
|
oscillator.frequency.value *= adjust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playNote(frequency, duration=86400) {
|
||||||
|
const voice = new Voice(frequency);
|
||||||
|
voice.play(duration);
|
||||||
|
return voice;
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyIndex(key) {
|
function keyIndex(key) {
|
||||||
@ -104,7 +147,9 @@ function updateOptions(key) {
|
|||||||
function onKeyClick(event) {
|
function onKeyClick(event) {
|
||||||
const keySpan = event.srcElement;
|
const keySpan = event.srcElement;
|
||||||
const key = keySpan.id;
|
const key = keySpan.id;
|
||||||
playNote(noteFrequency(key), 0.5);
|
if (holds[keyIndex(key)] === undefined) {
|
||||||
|
playNote(noteFrequency(key), 0.5);
|
||||||
|
}
|
||||||
updateOptions(key);
|
updateOptions(key);
|
||||||
|
|
||||||
keySpan.classList.add('playing');
|
keySpan.classList.add('playing');
|
||||||
@ -157,7 +202,7 @@ function addHold(key) {
|
|||||||
function removeHold(key) {
|
function removeHold(key) {
|
||||||
const index = keyIndex(key);
|
const index = keyIndex(key);
|
||||||
if (holds[index] !== undefined) {
|
if (holds[index] !== undefined) {
|
||||||
holds[index]();
|
holds[index].stop();
|
||||||
}
|
}
|
||||||
delete holds[index];
|
delete holds[index];
|
||||||
document.getElementById(key).classList.remove('held');
|
document.getElementById(key).classList.remove('held');
|
||||||
@ -166,8 +211,7 @@ function removeHold(key) {
|
|||||||
function refreshHolds() {
|
function refreshHolds() {
|
||||||
for (let i = 0; i < holds.length; i++) {
|
for (let i = 0; i < holds.length; i++) {
|
||||||
if (holds[i] !== undefined) {
|
if (holds[i] !== undefined) {
|
||||||
holds[i]();
|
holds[i].tune(frequencies[i]);
|
||||||
holds[i] = playNote(frequencies[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,9 +235,9 @@ function setupListeners() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const range = document.getElementById("range");
|
const range = document.getElementById("range");
|
||||||
range.onchange = e => {
|
range.oninput = e => {
|
||||||
const val = parseFloat(range.value);
|
const val = parseFloat(range.value);
|
||||||
globalVolume = val;
|
globalVolume.gain.value = val;
|
||||||
refreshHolds();
|
refreshHolds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user