Generate Notes
by soulwire
JavaScript
const SAMPLE_RATE = 44100;
const TIME_INTERVAL = 1;
const A4 = 440;
const notes = [];
for(let i = -4; i < 4; i++){
let a = A4 * Math.pow(2, i);
for (let n = -8; n <= 2; n++) {
const b = a * Math.pow(2, n / 12);
notes.push(b);
}
}
notes.forEach((freq, index) => {
console.log(`${Math.round(index * TIME_INTERVAL)}s => ${Math.round(freq)}`);
});
const duration = notes.length * TIME_INTERVAL;
const context = new OfflineAudioContext(2, duration * SAMPLE_RATE, SAMPLE_RATE);
const oscillator = context.createOscillator();
let time = context.currentTime;
notes.forEach(freq => {
oscillator.frequency.setValueAtTime(freq, time);
time += TIME_INTERVAL
});
oscillator.type = 'sine';
oscillator.connect(context.destination);
oscillator.start();
context.startRendering().then(buffer => {
const el = new Audio();
el.controls = true;
el.src = URL.createObjectURL(bufferToWAV(buffer));
document.body.appendChild(el);
});
function bufferToWAV(buffer, samples) {
const length = buffer.length * buffer.numberOfChannels * 2 + 44;
const output = new ArrayBuffer(length);
const view = new DataView(output);
const channels = [];
let offset = 0;
let pos = 0;
const setUint16 = data => {
view.setUint32(pos, data, true);
pos += 2;
}
const setUint32 = data => {
view.setUint32(pos, data, true);
pos += 4;
}
// "RIFF"
setUint32(0x46464952);
// file length - 8
setUint32(length - 8);
// "WAVE"
setUint32(0x45564157);
// "fmt " chunk
setUint32(0x20746d66);
// length = 16
setUint32(16)
// PCM (uncompressed)
setUint16(1);
setUint16(buffer.numberOfChannels)
setUint32(buffer.sampleRate)
// avg. bytes/sec
setUint32(buffer.sampleRate * 2 * buffer.numberOfChannels);
// block-align
setUint16(buffer.numberOfChannels * 2);
// 16-bit (hardcoded in this demo)
setUint16(16)
// "data" - chunk
setUint32(0x61746164);
// chunk length
setUint32(length - pos - 4);
// write interleaved data
...