每 5 秒重复一次 audioContext 振荡器
Repeat an audioContext oscillator every 5 seconds
我正在尝试编写一个莫尔斯电码训练器,它每 5 秒生成一个随机的两个字母模式,并且每个循环都重新创建 audiocontext,但我不知道如何添加将调用重复循环的代码。我试过 setTimeout()
setInterval()
,但它们都消除了音频。
此外,在以下代码中按五次按钮后。
我收到错误
" TypeError: null is not an object (evaluating 'ctx.currentTime')"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick = "startIt()">Play</button>
<button onclick = "stopIt()">Stop</button>
<h2>Morse Code</h2>
<h1 id="demo"></h1>
<h1 id="demo2"></h1>
<script>
var codeStream = '';
var dot = 1.2 / 15;
var text = "";
var display = "";
var k = 0;
var alphabet = [["A",".-"],["B","-..."],["C","-.-."],["D","-.."],["E","."],["F","..-."],["G","--."],["H","...."],["I",".."],["J",".---"],
["K","-.-"],["L",".-.."],["M","--"],["N","-."],["O","---"],["P",".--."],["Q","--.-"],["R",".-."],["S","..."],["T","-"],["U","..-"],
["V","...-"],["W",".--"],["X","-..-"],["Y","-.--"],["Z","--.."],["1",".----"],["2","..---"],["3","...--"],["4","....-"],["5","....."],
["6","-...."],["7","--..."],["8","---.."],["9","----."],["0","-----"],[".",".-.-.-"],[",","--..--"],["?","..--.."],["'",".----."],["!","-.-.--"],
["/","-..-."],[":","---..."],[";","-.-.-."],["=","-...-"],["-","-....-"],["_","..--.-"],["\"",".-..-."],["@",".--.-."],["(","-.--.-"],[" ",""]];
stopIt = function(){
ctx.close();
location.reload();
}
function nextGroup() {
for (i = 0; i < 2; i++){
var randomLetter = Math.floor(Math.random() * 26);
var code = alphabet[randomLetter][1] + " ";
var character = alphabet[randomLetter][0];
display += code;
text += character;
}
codeStream = display;
}
function startIt(){
var AudioContext = window.AudioContext || window.webkitAudioContext;
var ctx = new AudioContext();
var t = ctx.currentTime;
var oscillator = ctx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.value = 600;
oscillator.start();
var gainNode = ctx.createGain();
nextGroup();
console.log(codeStream);
document.getElementById("demo").innerHTML = text;
document.getElementById("demo2").innerHTML = codeStream;
display = "";
text = "";
gainNode.gain.setValueAtTime(0, t);
for (var i = 0; i < codeStream.length; i++) {
switch(codeStream.charAt(i)) {
case ".":
gainNode.gain.setValueAtTime(1, t);
t += dot;
gainNode.gain.setValueAtTime(0, t);
t += dot;
break;
case "-":
gainNode.gain.setValueAtTime(1, t);
t += 3 * dot;
gainNode.gain.setValueAtTime(0, t);
t += dot;
break;
case " ":
t += 7 * dot;
break;
}
}
gainNode.gain.setValueAtTime(0, t);
t += 50 * dot;
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
codeStream = '';
oscillator.stop(t);
}
</script>
</body>
</html>
看起来有些问题与振荡器的范围界定和状态管理有关。我无法重现您看到的错误,但 stopIt
函数肯定无法访问在 startIt
.
中创建的 ctx
替代方案可能是,与其在每个 运行 上重新创建上下文、振荡器和增益节点,不如创建它们一次并重新使用它们。此处演示:http://jsfiddle.net/kts74g0x/
代码:
const ALPHABET = [
["A", ".-"],
...
[" ",""]
];
const DOT = 1;
const DASH = 3;
const NEXT = DOT;
const SPACE = 7;
const SPEED = 1.2 / 15;
const AudioContext = window.AudioContext || window.webkitAudioContext;
/**
* Create a single audio context, oscillator and gain node and repeatedly
* use them instead of creating a new one each time. The gain is just
* silent most of the time.
*/
const ctx = new AudioContext();
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.type = "sine";
oscillator.frequency.value = 600;
oscillator.connect(gainNode);
oscillator.start();
gainNode.connect(ctx.destination);
gainNode.gain.value = 0;
function playCodeStream(stream) {
let t = ctx.currentTime;
gainNode.gain.setValueAtTime(0, t);
for (var i = 0; i < stream.length; i++) {
switch(stream.charAt(i)) {
case ".":
gainNode.gain.setValueAtTime(1, t);
t += DOT * SPEED;
gainNode.gain.setValueAtTime(0, t);
t += NEXT * SPEED;
break;
case "-":
gainNode.gain.setValueAtTime(1, t);
t += DASH * SPEED;
gainNode.gain.setValueAtTime(0, t);
t += NEXT * SPEED;
break;
case " ":
t += SPACE * SPEED;
break;
}
}
}
/**
* Set interval will wait initially for the period of
* time before first triggering the function.
*/
setInterval(() => { playCodeStream([
ALPHABET.filter(v => v[0] === "H"),
ALPHABET.filter(v => v[0] === "E"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "O")
].join(" ")); }, 10000);
设置间隔 returns 可以传递给 clearInterval
的 ID 以防止将来 运行s,播放按钮可能会启动间隔,停止按钮可以清除它,因为例如。
对于 iOS 存在一些限制,因此 AudioContext 无法播放声音,除非它响应用户交互 (https://hackernoon.com/unlocking-web-audio-the-smarter-way-8858218c0e09)。我们可以通过添加一个按钮来解决这个问题。
<button id="go">Go</button>
并检查音频上下文的状态/启动间隔以响应单击此按钮(演示:http://jsfiddle.net/7gfnrubc/)。更新后的代码:
function next() {
playCodeStream([
ALPHABET.filter(v => v[0] === "H"),
ALPHABET.filter(v => v[0] === "E"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "O")
].join(" "));
}
function go() {
if (ctx.state === 'suspended') {
ctx.resume();
}
/**
* Set interval will wait initially for the period of
* time before first triggering the function. Can call
* the function initially to start off.
*/
next();
setInterval(next, 10000);
}
const button = document.getElementById("go");
button.addEventListener("click", go);
我正在尝试编写一个莫尔斯电码训练器,它每 5 秒生成一个随机的两个字母模式,并且每个循环都重新创建 audiocontext,但我不知道如何添加将调用重复循环的代码。我试过 setTimeout()
setInterval()
,但它们都消除了音频。
此外,在以下代码中按五次按钮后。 我收到错误
" TypeError: null is not an object (evaluating 'ctx.currentTime')"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button onclick = "startIt()">Play</button>
<button onclick = "stopIt()">Stop</button>
<h2>Morse Code</h2>
<h1 id="demo"></h1>
<h1 id="demo2"></h1>
<script>
var codeStream = '';
var dot = 1.2 / 15;
var text = "";
var display = "";
var k = 0;
var alphabet = [["A",".-"],["B","-..."],["C","-.-."],["D","-.."],["E","."],["F","..-."],["G","--."],["H","...."],["I",".."],["J",".---"],
["K","-.-"],["L",".-.."],["M","--"],["N","-."],["O","---"],["P",".--."],["Q","--.-"],["R",".-."],["S","..."],["T","-"],["U","..-"],
["V","...-"],["W",".--"],["X","-..-"],["Y","-.--"],["Z","--.."],["1",".----"],["2","..---"],["3","...--"],["4","....-"],["5","....."],
["6","-...."],["7","--..."],["8","---.."],["9","----."],["0","-----"],[".",".-.-.-"],[",","--..--"],["?","..--.."],["'",".----."],["!","-.-.--"],
["/","-..-."],[":","---..."],[";","-.-.-."],["=","-...-"],["-","-....-"],["_","..--.-"],["\"",".-..-."],["@",".--.-."],["(","-.--.-"],[" ",""]];
stopIt = function(){
ctx.close();
location.reload();
}
function nextGroup() {
for (i = 0; i < 2; i++){
var randomLetter = Math.floor(Math.random() * 26);
var code = alphabet[randomLetter][1] + " ";
var character = alphabet[randomLetter][0];
display += code;
text += character;
}
codeStream = display;
}
function startIt(){
var AudioContext = window.AudioContext || window.webkitAudioContext;
var ctx = new AudioContext();
var t = ctx.currentTime;
var oscillator = ctx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.value = 600;
oscillator.start();
var gainNode = ctx.createGain();
nextGroup();
console.log(codeStream);
document.getElementById("demo").innerHTML = text;
document.getElementById("demo2").innerHTML = codeStream;
display = "";
text = "";
gainNode.gain.setValueAtTime(0, t);
for (var i = 0; i < codeStream.length; i++) {
switch(codeStream.charAt(i)) {
case ".":
gainNode.gain.setValueAtTime(1, t);
t += dot;
gainNode.gain.setValueAtTime(0, t);
t += dot;
break;
case "-":
gainNode.gain.setValueAtTime(1, t);
t += 3 * dot;
gainNode.gain.setValueAtTime(0, t);
t += dot;
break;
case " ":
t += 7 * dot;
break;
}
}
gainNode.gain.setValueAtTime(0, t);
t += 50 * dot;
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
codeStream = '';
oscillator.stop(t);
}
</script>
</body>
</html>
看起来有些问题与振荡器的范围界定和状态管理有关。我无法重现您看到的错误,但 stopIt
函数肯定无法访问在 startIt
.
ctx
替代方案可能是,与其在每个 运行 上重新创建上下文、振荡器和增益节点,不如创建它们一次并重新使用它们。此处演示:http://jsfiddle.net/kts74g0x/
代码:
const ALPHABET = [
["A", ".-"],
...
[" ",""]
];
const DOT = 1;
const DASH = 3;
const NEXT = DOT;
const SPACE = 7;
const SPEED = 1.2 / 15;
const AudioContext = window.AudioContext || window.webkitAudioContext;
/**
* Create a single audio context, oscillator and gain node and repeatedly
* use them instead of creating a new one each time. The gain is just
* silent most of the time.
*/
const ctx = new AudioContext();
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.type = "sine";
oscillator.frequency.value = 600;
oscillator.connect(gainNode);
oscillator.start();
gainNode.connect(ctx.destination);
gainNode.gain.value = 0;
function playCodeStream(stream) {
let t = ctx.currentTime;
gainNode.gain.setValueAtTime(0, t);
for (var i = 0; i < stream.length; i++) {
switch(stream.charAt(i)) {
case ".":
gainNode.gain.setValueAtTime(1, t);
t += DOT * SPEED;
gainNode.gain.setValueAtTime(0, t);
t += NEXT * SPEED;
break;
case "-":
gainNode.gain.setValueAtTime(1, t);
t += DASH * SPEED;
gainNode.gain.setValueAtTime(0, t);
t += NEXT * SPEED;
break;
case " ":
t += SPACE * SPEED;
break;
}
}
}
/**
* Set interval will wait initially for the period of
* time before first triggering the function.
*/
setInterval(() => { playCodeStream([
ALPHABET.filter(v => v[0] === "H"),
ALPHABET.filter(v => v[0] === "E"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "O")
].join(" ")); }, 10000);
设置间隔 returns 可以传递给 clearInterval
的 ID 以防止将来 运行s,播放按钮可能会启动间隔,停止按钮可以清除它,因为例如。
对于 iOS 存在一些限制,因此 AudioContext 无法播放声音,除非它响应用户交互 (https://hackernoon.com/unlocking-web-audio-the-smarter-way-8858218c0e09)。我们可以通过添加一个按钮来解决这个问题。
<button id="go">Go</button>
并检查音频上下文的状态/启动间隔以响应单击此按钮(演示:http://jsfiddle.net/7gfnrubc/)。更新后的代码:
function next() {
playCodeStream([
ALPHABET.filter(v => v[0] === "H"),
ALPHABET.filter(v => v[0] === "E"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "L"),
ALPHABET.filter(v => v[0] === "O")
].join(" "));
}
function go() {
if (ctx.state === 'suspended') {
ctx.resume();
}
/**
* Set interval will wait initially for the period of
* time before first triggering the function. Can call
* the function initially to start off.
*/
next();
setInterval(next, 10000);
}
const button = document.getElementById("go");
button.addEventListener("click", go);