jQuery(($) => {
"use strict"
class MySoundBase {
constructor() {
const audioContext =
window.AudioContext || window.webkitAudioContext;
this.ctx = new audioContext();
this.init();
}
init() {
this.type = 'sine';
this.gain = 0.25;
}
now() {
return this.ctx.currentTime;
}
setType(value) {
this.type = value;
}
setGain(value) {
this.gain = value;
}
sound(f = 440, duration = 1, start = null) {
if (start === null) start = this.ctx.currentTime;
const gainNode = this.ctx.createGain();
gainNode.connect(this.ctx.destination);
this.setGainParams(gainNode, duration, start);
const osc = this.ctx.createOscillator();
osc.onended = this.onEnded.bind(this);
osc.connect(gainNode);
osc.type = this.type;
osc.frequency.value = f;
osc.start(start);
osc.stop(start + duration);
}
setGainParams(gainNode, duration, start) {
gainNode.gain.linearRampToValueAtTime(this.gain, start);
gainNode.gain.linearRampToValueAtTime(0, start + 0.5);
}
// note=48 -> Oct3: C..B
// note=60 -> Oct4: C..B
// note=72 -> Oct5: C..B
note(note, duration, start = null) {
this.sound(this.noteToF(note), duration, start);
}
noteToF(note) {
return 440 * Math.pow(2, (note - 69) / 12);
}
onEnded(ev) {
}
}
main();
function main() {
const typeNames = ['sine', 'square', 'sawtooth', 'triangle'];
let type = 0, typeName;
let A, B;
$('.js-play1').click((ev) => {
typeName = typeNames[type++ % 4];
if (!A) A = new MySoundBase();
A.init();
A.setType(typeName);
A.sound(440);
});
$('.js-play2').click((ev) => {
const t = 0.25;
let pos, notes;
typeName = typeNames[type++ % 4];
// 第一声
if (!A) A = new MySoundBase();
A.init();
A.setType(typeName);
pos = A.now();
notes = [
60, 62, 64, 65, 64, 62, 60, 0,
64, 65, 67, 69, 67, 65, 64, 0,
];
for (const note of notes) {
if (note != 0) A.note(note, t, pos);
pos += t;
}
// 第二声
if (!B) B = new MySoundBase();
B.init();
B.setType(typeName);
pos = B.now();
notes = [
0, 0, 0, 0, 0, 0, 0, 0,
60, 62, 64, 65, 64, 62, 60, 0,
];
for (const note of notes) {
if (note != 0) B.note(note, t, pos);
pos += t;
}
});
}
});