JSFiddle - React, Tailwind, and code Playground
by booktu8
HTML
<input type="text" size="4" id="freq" value="440"><label for="hz">Hz</label>
<button onclick="start()">play</button>
<button onclick="stop()">stop</button>
JavaScript
function AudioDataDestination(sampleRate, readFn) {
// 音頻輸出的初使化
var audio = new Audio();
audio.mozSetup(1, sampleRate);
var currentWritePosition = 0;
var prebufferSize = sampleRate / 2; // 緩衝 500ms
var tail = null;
// 這個函數會被定期呼叫以填充音頻輸出緩衝
setInterval(function() {
var written;
// 看看之前也沒有留下未被寫入的資料
if(tail) {
written = audio.mozWriteAudio(tail);
currentWritePosition += written;
if(written < tail.length) {
// 並非所有資料都被寫入了,把剩下的存下來...
tail = tail.slice(written);
return; // ... 並結束這個函式
}
tail = null;
}
// 看看是不是有需要再加新資料至音頻輸出
var currentPosition = audio.mozCurrentSampleOffset();
var available = currentPosition + prebufferSize - currentWritePosition;
if(available > 0) {
// 從回撥函式(callback)要一些音效資料
var soundData = new Float32Array(parseFloat(available));
readFn(soundData);
// 寫這些資料
written = audio.mozWriteAudio(soundData);
if(written < soundData.length) {
// 不是所有資料都被寫入了,存下剩下的部份。
tail = soundData.slice(written);
}
currentWritePosition += written;
}
}, 100);
}
// 控制並製造聲音
var frequency = 0, currentSoundSample;
var sampleRate = 44100;
function requestSoundData(soundData) {
if (!frequency) {
return; // 已被靜音
}
var k = 2* Math.PI * frequency / sampleRate;
for (var i=0, size=soundData.length; i<size; i++) {
soundData[i] = Math.sin(k * currentSoundSample++);
}
}
var audioDestination = new AudioDataDestination(sampleRate, requestSoundData);
function start() {
currentSoundSample = 0;
frequency = parseFloat(document.getElementById("freq").value);
}
...