使用 javascript/html5 动态生成声音
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6343450/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generating sound on the fly with javascript/html5
提问by Johannes Hoff
Is it possible to generate a constant sound stream with javascript/html5? For example, to generate a perpetual sine wave, I would have a callback function, that would be called whenever the output buffer is about to become empty:
是否可以使用 javascript/html5 生成恒定的声音流?例如,要生成永久正弦波,我将有一个回调函数,每当输出缓冲区即将变空时都会调用该函数:
function getSampleAt(timestep)
{
return Math.sin(timestep);
}
(The idea is to use this to make an interactive synth. I don't know in advance how long a key will be pressed, so I can't use a fixed length buffer)
(想法是用这个来做一个交互式合成器。我事先不知道一个键会被按下多长时间,所以我不能使用固定长度的缓冲区)
采纳答案by Nick
Using the HTML5 audio element
使用 HTML5 音频元素
Cross-browser generative sustained audio using JavaScript and the audio
element isn't currently possible, as Steven Wittens notes in a blog post on creating a JavaScript synth:
audio
正如 Steven Wittens在一篇关于创建 JavaScript 合成器的博客文章中所指出的那样,使用 JavaScript 和元素的跨浏览器生成持续音频目前是不可能的:
"...there is no way to queue up chunks of synthesized audio for seamless playback".
“......没有办法将合成音频块排队以进行无缝播放”。
Using the Web Audio API
使用网络音频 API
The Web Audio APIwas designed to facilitate JavaScript audio synthesis. The Mozilla Developer Network has a Web Based Tone Generatorthat works in Firefox 4+ [demo 1]. Add these two lines to that code and you have a working synth with generative sustained audio upon keypress [demo 2- works in Firefox 4 only, click the 'Results' area first, then press any key]:
该网络音频API的设计,方便的JavaScript音频合成。Mozilla 开发人员网络有一个基于 Web 的音调生成器,可在 Firefox 4+ [演示 1] 中运行。将这两行添加到该代码中,您就有了一个在按键时具有生成持续音频的工作合成器 [演示 2- 仅适用于 Firefox 4,首先单击“结果”区域,然后按任意键]:
window.onkeydown = start;
window.onkeyup = stop;
The BBC's page on the Web Audio APIis worth reviewing too. Unfortunately, support for the Web Audio API doesn't extend to other browsers yet.
BBC在 Web Audio API 上的页面也值得一看。不幸的是,对 Web Audio API 的支持还没有扩展到其他浏览器。
Possible workarounds
可能的解决方法
To create a cross-browser synth at present, you'll likely have to fall back on prerecorded audio by:
目前要创建跨浏览器合成器,您可能不得不通过以下方式使用预先录制的音频:
- Using long prerecorded ogg/mp3 sample tones, embedding them in separate
audio
elements and starting and stopping them upon keypress. - Embedding an swf file containing the audio elements and controlling playback via JavaScript. (This appears to be the method that the Google Les Paul Doodleemploys.)
- 使用长时间预先录制的 ogg/mp3 样本音调,将它们嵌入到单独的
audio
元素中,并在按键时启动和停止它们。 - 嵌入包含音频元素的 swf 文件并通过 JavaScript 控制播放。(这似乎是Google Les Paul Doodle采用的方法。)
回答by snapfractalpop
You can use the Web Audio APIin most browsers now (excepting IE and Opera Mini).
您现在可以在大多数浏览器中使用Web Audio API(IE 和 Opera Mini 除外)。
Try out this code:
试试这个代码:
// one context per document
var context = new (window.AudioContext || window.webkitAudioContext)();
var osc = context.createOscillator(); // instantiate an oscillator
osc.type = 'sine'; // this is the default - also square, sawtooth, triangle
osc.frequency.value = 440; // Hz
osc.connect(context.destination); // connect it to the destination
osc.start(); // start the oscillator
osc.stop(context.currentTime + 2); // stop 2 seconds after the current time
If you want the volume lower, you can do something like this:
如果您希望音量降低,您可以执行以下操作:
var context = new webkitAudioContext();
var osc = context.createOscillator();
var vol = context.createGain();
vol.gain.value = 0.1; // from 0 to 1, 1 full volume, 0 is muted
osc.connect(vol); // connect osc to vol
vol.connect(context.destination); // connect vol to context destination
osc.start(context.currentTime + 3); // start it three seconds from now
I got most of this from experimenting in chromium while reading the Web Audio API Working Draft, which I found from @brainjam 's link.
我在阅读Web Audio API Working Draft时通过在 Chrome 中进行实验获得了大部分内容,该草案是我从 @brainjam 的链接中找到的。
I hope that helps. Lastly, it is very helpful to inspect the various objects in the chrome inspector (ctrl-shift-i).
我希望这有帮助。最后,在 chrome 检查器 (ctrl-shift-i) 中检查各种对象非常有帮助。
回答by brainjam
Web Audio API is coming to Chrome. See http://googlechrome.github.io/web-audio-samples/samples/audio/index.html
网络音频 API 即将加入 Chrome。请参阅http://googlechrome.github.io/web-audio-samples/samples/audio/index.html
Follow the directions in "Getting Started" there, and then check out the very impressive demos.
按照那里的“入门”中的说明进行操作,然后查看令人印象深刻的演示。
Update(2017):by now this is a much more mature interface. The API is documented at https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
更新(2017):现在这是一个更加成熟的界面。API 记录在https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
回答by CaptureWiz
Sure! You could use the tone synthesizer in this demo:
当然!您可以在此演示中使用音调合成器:
audioCtx = new(window.AudioContext || window.webkitAudioContext)();
show();
function show() {
frequency = document.getElementById("fIn").value;
document.getElementById("fOut").innerHTML = frequency + ' Hz';
switch (document.getElementById("tIn").value * 1) {
case 0: type = 'sine'; break;
case 1: type = 'square'; break;
case 2: type = 'sawtooth'; break;
case 3: type = 'triangle'; break;
}
document.getElementById("tOut").innerHTML = type;
volume = document.getElementById("vIn").value / 100;
document.getElementById("vOut").innerHTML = volume;
duration = document.getElementById("dIn").value;
document.getElementById("dOut").innerHTML = duration + ' ms';
}
function beep() {
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.value = volume;
oscillator.frequency.value = frequency;
oscillator.type = type;
oscillator.start();
setTimeout(
function() {
oscillator.stop();
},
duration
);
};
frequency
<input type="range" id="fIn" min="40" max="6000" oninput="show()" />
<span id="fOut"></span><br>
type
<input type="range" id="tIn" min="0" max="3" oninput="show()" />
<span id="tOut"></span><br>
volume
<input type="range" id="vIn" min="0" max="100" oninput="show()" />
<span id="vOut"></span><br>
duration
<input type="range" id="dIn" min="1" max="5000" oninput="show()" />
<span id="dOut"></span>
<br>
<button onclick='beep();'>Play</button>
Have fun!
玩得开心!
I got the solution from Houshalter here: How do I make Javascript beep?
我从 Houshalter 那里得到了解决方案: How do I make Javascript beep?
You can clone and tweak the code here: Tone synthesizer demo on JS Bin
您可以在此处克隆和调整代码: JS Bin 上的音调合成器演示
Compatible browsers:
兼容浏览器:
- Chrome mobile & desktop
- Firefox mobile & desktop Opera mobile, mini & desktop
- Android browser
- Microsoft Edge browser
- Safari on iPhone or iPad
- Chrome 移动版和桌面版
- Firefox 移动版和桌面版 Opera 移动版、迷你版和桌面版
- 安卓浏览器
- 微软边缘浏览器
- iPhone 或 iPad 上的 Safari
Not Compatible
不兼容
- Internet Explorer version 11 (but does work on the Edge browser)
- Internet Explorer 版本 11(但适用于 Edge 浏览器)
回答by Kamil Kie?czewski
You can generate wav-e file in the fly and play it (src)
您可以即时生成 wav-e 文件并播放它(src)
// Legend
// DUR - duration in seconds SPS - sample per second (default 44100)
// NCH - number of channels BPS - bytes per sample
// t - is number from range [0, DUR), return number in range [0, 1]
function getSampleAt(t,DUR,SPS)
{
return Math.sin(6000*t);
}
function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
let size = DUR*NCH*SPS*BPS;
let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
let p = (...a) => a.map( b=> put(...[b].flat()) ).join('');
let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
for (let i = 0; i < DUR*SPS; i++) {
let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
}
return "data:Audio/WAV;base64," + btoa(data);
}
var WAV = new Audio( genWAVUrl(getSampleAt,5) ); // 5s
WAV.setAttribute("controls", "controls");
document.body.appendChild(WAV);
//WAV.play()
Here is visualistation
这里是可视化
function getSampleAt(t,DUR,SPS)
{
return 0.5+Math.sin(15*t)/(1+t*t);
}
// ----------------------------------------------
function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
let size = DUR*NCH*SPS*BPS;
let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
let p = (...a) => a.map( b=> put(...[b].flat()) ).join('');
let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
for (let i = 0; i < DUR*SPS; i++) {
let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
}
return "data:Audio/WAV;base64," + btoa(data);
}
function draw(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
time.innerHTML=DUR+'s';
time.setAttribute('x',DUR-0.3);
svgCh.setAttribute('viewBox',`0 0 ${DUR} 1`);
let p='', n=100; // n how many points to ommit
for (let i = 0; i < DUR*SPS/n; i++) p+= ` ${DUR*(n*i/SPS)/DUR}, ${1-fun(n*i/SPS, DUR,SPS)}`;
chart.setAttribute('points', p);
}
function frame() {
let t=WAV.currentTime;
point.setAttribute('cx',t)
point.setAttribute('cy',1-getSampleAt(t))
window.requestAnimationFrame(frame);
}
function changeStart(e) {
var r = e.target.getBoundingClientRect();
var x = e.clientX - r.left;
WAV.currentTime = dur*x/r.width;
WAV.play()
}
var dur=5; // seconds
var WAV = new Audio(genWAVUrl(getSampleAt,dur));
draw(getSampleAt,dur);
frame();
.chart { border: 1px dashed #ccc; }
.axis { font-size: 0.2px}
audio { outline: none; }
Click at blue line (make volume to max):
<svg class="chart" id="svgCh" onclick="changeStart(event)">
<circle cx="0" cy="-1" r="0.05" style="fill: rgba(255,0,0,1)" id="point"></circle>
<polyline id="chart" fill="none" stroke="#0074d9" stroke-width="0.01" points=""/>
<text x="0.03" y="0.9" class="axis">0</text>
<text x="0.03" y="0.2" class="axis">1</text>
<text x="4.8" y="0.9" class="axis" id="time"></text>
</svg><br>
回答by Xenon
This is what I have looked for like forever and in the end I managed to do it myself like I wanted. Maybe you will like it too. Simple slider with frequency and push on/off:
这就是我一直在寻找的东西,最后我按照自己的意愿设法做到了。也许你也会喜欢它。带有频率和推开/关闭的简单滑块:
buttonClickResult = function () {
var button = document.getElementById('btn1');
button.onclick = function buttonClicked() {
if(button.className=="off") {
button.className="on";
oscOn ();
}
else if(button.className=="on") {
button.className="off";
oscillator.disconnect();
}
}
};
buttonClickResult();
var oscOn = function(){
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
var gainNode = context.createGain ? context.createGain() : context.createGainNode();
//context = new window.AudioContext();
oscillator = context.createOscillator(),
oscillator.type ='sine';
oscillator.frequency.value = document.getElementById("fIn").value;
//gainNode = createGainNode();
oscillator.connect(gainNode);
gainNode.connect(context.destination);
gainNode.gain.value = 1;
oscillator.start(0);
};
<p class="texts">Frekvence [Hz]</p>
<input type="range" id="fIn" min="20" max="20000" step="100" value="1234" oninput="show()" />
<span id="fOut"></span><br>
<input class="off" type="button" id="btn1" value="Start / Stop" />
回答by Mara Maya
This is not real answer on your question because you have asked for a JavaScript solution, but you can use ActionScript. It should run on all major browsers.
这不是您问题的真正答案,因为您要求提供 JavaScript 解决方案,但您可以使用 ActionScript。它应该在所有主要浏览器上运行。
You can call ActionScript functions from within JavaScript.
您可以从 JavaScript 中调用 ActionScript 函数。
In that way you can wrap the ActionScript sound generation functions and make a JavaScript implementation of them. Just use Adobe Flex to build a tiny swf and then use that as backend for your JavaScript code.
通过这种方式,您可以包装 ActionScript 声音生成函数并对其进行 JavaScript 实现。只需使用 Adobe Flex 构建一个小型 swf,然后将其用作 JavaScript 代码的后端。