我正在制作一个程序,将文本转换为莫尔斯电码音频。
说我输入sos。我的程序将把它变成数组[1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1]。哪里s = dot dot dot(或1,1,1),和o = dash dash dash(或2,2,2)。这部分很容易。
接下来,我有两个声音文件:
var dot = new Audio('dot.mp3');
var dash = new Audio('dash.mp3');
我的目标是有一个功能,dot.mp3当它看到a时1,dash.mp3当它看到a时会播放,并在看到a 2时暂停0。
以下类型/有时/有时可行,但我认为它存在根本缺陷,我不知道如何修复它。
function playMorseArr(morseArr) {
for (let i = 0; i < morseArr.length; i++) {
setTimeout(function() {
if (morseArr[i] === 1) {
dot.play();
}
if (morseArr[i] === 2) {
dash.play();
}
}, 250*i);
}
}
问题:
我可以遍历数组,播放声音文件,但时机是一个挑战。如果我没有setTimeout()恰当地设置间隔,如果最后一个音频文件未完成播放且250ms已经过,则将跳过数组中的下一个元素。所以dash.mp3比...更长dot.mp3。如果我的时间太短,我可能会听到[dot dot dot pause dash dash pause dot dot dot],或者说那种效果。
我想要的效果
我希望程序像这样(在伪代码中):
- 看一下ith数组元素
- if 1or 2,开始播放声音文件或者创建暂停
- 等待声音文件或暂停完成
- 增加i并返回步骤1
我想到了什么,但不知道如何实施
因此,我希望循环同步进行。我已经在我有几个函数的情况下使用了promises,我希望按特定顺序执行,但是如何链接未知数量的函数?
我也考虑过使用自定义事件,但我遇到了同样的问题。
不要将HTMLAudioElement用于那种应用程序。
HTMLMediaElements本质上是异步的,从play()方法到pause()一个并且通过明显的资源获取和不太明显的currentTime设置的所有内容都是异步的。
这意味着对于需要完美计时的应用程序(如摩尔斯电码阅读器),这些元素纯粹是不可靠的。
而是使用Web Audio API及其AudioBufferSourceNode对象,您可以使用μs精度控制它们。
首先将所有资源作为ArrayBuffers获取,然后在需要时从这些ArrayBuffers生成并播放AudioBufferSourceNodes。
您将能够同步开始播放这些内容,或以比setTimeout为您提供的更高精度安排它们(AudioContext使用自己的时钟)。
担心让几个AudioBufferSourceNodes播放你的样本会对内存造成影响吗?不要。数据仅存储在内存中,一次存储在AudioBuffer中。AudioBufferSourceNodes只是对这些数据的观看,并没有任何地方。
// I use a lib for Morse encoding, didn't tested it too much though
// https://github.com/Syncthetic/MorseCode/
const morse = Object.create(MorseCode);
const ctx = new (window.AudioContext || window.webkitAudioContext)();
(async function initMorseData() {
// our AudioBuffers objects
const [short, long] = await fetchBuffers();
btn.onclick = e => {
let time = 0; // a simple time counter
const sequence = morse.encode(inp.value);
console.log(sequence); // dots and dashes
sequence.split('').forEach(type => {
if(type === ' ') { // space => 0.5s of silence
time += 0.5;
return;
}
// create an AudioBufferSourceNode
let source = ctx.createBufferSource();
// assign the correct AudioBuffer to it
source.buffer = type === '-' ? long : short;
// connect to our output audio
source.connect(ctx.destination);
// schedule it to start at the end of previous one
source.start(ctx.currentTime + time);
// increment our timer with our sample's duration
time += source.buffer.duration;
});
};
// ready to go
btn.disabled = false
})()
.catch(console.error);
function fetchBuffers() {
return Promise.all(
[
'https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3',
'https://dl.dropboxusercontent.com/s/h2j6vm17r07jf03/snare.mp3'
].map(url => fetch(url)
.then(r => r.arrayBuffer())
.then(buf => ctx.decodeAudioData(buf))
)
);
}
<button>play/<button>
閱讀更多 國外遊戲視頻分享 的文章