javascript-为网络音频创建音量控制
作者:互联网
因此,我正在通过网络音频制作钢琴,但无法实现音量控制.每当单击一个键时,音量控件都应指示以什么音量播放.我使用了html5rocks的代码,并将其修改为自己的用途.基本上,不是将VolumeSample数组,而是将我的所有声音片段加载到BUFFERS数组中.每当我尝试操纵滑块并更改剪辑的增益时,都会得到“无法读取”属性“增益”为null的信息.我正在通过调试器对其进行测试,一切正常,直到this.gainNode.gain.value = fraction * fraction;我的代码的一部分.只需看一下我的代码,希望您能看到我所缺少的内容.我想提醒大家注意playSounds(buffer)方法,它是创建和连接增益节点的地方,而方法changeVolume则位于底部,实际上是增益节点发生变化的地方:
var context;
var bufferLoader;
var BUFFERS = {};
var VolumeMain = {};
var LowPFilter = {FREQ_MUL: 7000,
QUAL_MUL: 30};
var BUFFERS_TO_LOAD = {
Down1: 'mp3/0C.mp3',
Down2: 'mp3/0CS.mp3',
Down3: 'mp3/0D.mp3',
Down4: 'mp3/0DS.mp3',
Down5: 'mp3/0E.mp3',
Down6: 'mp3/0F.mp3',
Down7: 'mp3/0FS.mp3',
Down8: 'mp3/0G.mp3',
Down9: 'mp3/0GS.mp3',
Down10: 'mp3/0A.mp3',
Down11: 'mp3/0AS.mp3',
Down12: 'mp3/0B.mp3',
Up13: 'mp3/1C.mp3',
Up14: 'mp3/1CS.mp3',
Up15: 'mp3/1D.mp3',
Up16: 'mp3/1DS.mp3',
Up17: 'mp3/1E.mp3',
Up18: 'mp3/1F.mp3',
Up19: 'mp3/1FS.mp3',
Up20: 'mp3/1G.mp3',
Up21: 'mp3/1GS.mp3',
Up22: 'mp3/1A.mp3',
Up23: 'mp3/1AS.mp3',
Up24: 'mp3/1B.mp3',
Beat1: 'mp3/beat1.mp3',
Beat2: 'mp3/beat2.mp3'
};
function loadBuffers() {
var names = [];
var paths = [];
for (var name in BUFFERS_TO_LOAD) {
var path = BUFFERS_TO_LOAD[name];
names.push(name);
paths.push(path);
}
bufferLoader = new BufferLoader(context, paths, function(bufferList) {
for (var i = 0; i < bufferList.length; i++) {
var buffer = bufferList[i];
var name = names[i];
BUFFERS[name] = buffer;
}
});
bufferLoader.load();
}
document.addEventListener('DOMContentLoaded', function() {
try {
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
}
catch(e) {
alert("Web Audio API is not supported in this browser");
}
loadBuffers();
});
function playSound(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
var filter1 = context.createBiquadFilter();
filter1.type = 0;
filter1.frequency.value = 5000;
var gainNode = context.createGain();
source.connect(gainNode);
source.connect(filter1);
gainNode.connect(context.destination);
filter1.connect(context.destination);
source.start(0);
}
//volume control
VolumeMain.gainNode = null;
VolumeMain.changeVolume = function(element) {
var volume = element.value;
var fraction = parseInt(element.value) / parseInt(element.max);
this.gainNode.gain.value = fraction * fraction; //error occurs here
};
// Start off by initializing a new context.
context = new (window.AudioContext || window.webkitAudioContext)();
if (!context.createGain)
context.createGain = context.createGainNode;
if (!context.createDelay)
context.createDelay = context.createDelayNode;
if (!context.createScriptProcessor)
context.createScriptProcessor = context.createJavaScriptNode;
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
};
BufferLoader.prototype.load = function() {
for (var i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
}
LowPFilter.changeFrequency = function(element) {
// Clamp the frequency between the minimum value (40 Hz) and half of the
// sampling rate.
var minValue = 40;
var maxValue = context.sampleRate / 2;
// Logarithm (base 2) to compute how many octaves fall in the range.
var numberOfOctaves = Math.log(maxValue / minValue) / Math.LN2;
// Compute a multiplier from 0 to 1 based on an exponential scale.
var multiplier = Math.pow(2, numberOfOctaves * (element.value - 1.0));
// Get back to the frequency value between min and max.
this.filter1.frequency.value = maxValue * multiplier;
};
LowPFilter.changeQuality = function(element) {
this.filter1.Q.value = element.value * this.QUAL_MUL;
};
LowPFilter.toggleFilter = function(element) {
this.source.disconnect(0);
this.filter1.disconnect(0);
// Check if we want to enable the filter.
if (element.checked) {
// Connect through the filter.
this.source.connect(this.filter1);
this.filter1.connect(context.destination);
} else {
// Otherwise, connect directly.
this.source.connect(context.destination);
}
};
function Beat1() {
this.isPlaying = false;
};
Beat1.prototype.play = function() {
this.gainNode = context.createGain();
this.source = context.createBufferSource();
this.source.buffer = BUFFERS.Beat1;
// Connect source to a gain node
this.source.connect(this.gainNode);
// Connect gain node to destination
this.gainNode.connect(context.destination);
// Start playback in a loop
this.source.loop = true;
this.source[this.source.start ? 'start' : 'noteOn'](0);
};
Beat1.prototype.changeVolume = function(element) {
var volume = element.value;
var fraction = parseInt(element.value) / parseInt(element.max);
// Let's use an x*x curve (x-squared) since simple linear (x) does not
// sound as good.
this.gainNode.gain.value = fraction * fraction;
};
Beat1.prototype.stop = function() {
this.source[this.source.stop ? 'stop' : 'noteOff'](0);
};
Beat1.prototype.toggle = function() {
this.isPlaying ? this.stop() : this.play();
this.isPlaying = !this.isPlaying;
};
function Beat2() {
this.isPlaying = false;
};
Beat2.prototype.play = function() {
this.gainNode = context.createGain();
this.source = context.createBufferSource();
this.source.buffer = BUFFERS.Beat2;
// Connect source to a gain node
this.source.connect(this.gainNode);
// Connect gain node to destination
this.gainNode.connect(context.destination);
// Start playback in a loop
this.source.loop = true;
this.source[this.source.start ? 'start' : 'noteOn'](0);
};
Beat2.prototype.changeVolume = function(element) {
var volume = element.value;
var fraction = parseInt(element.value) / parseInt(element.max);
// Let's use an x*x curve (x-squared) since simple linear (x) does not
// sound as good.
this.gainNode.gain.value = fraction * fraction;
};
Beat2.prototype.stop = function() {
this.source[this.source.stop ? 'stop' : 'noteOff'](0);
};
Beat2.prototype.toggle = function() {
this.isPlaying ? this.stop() : this.play();
this.isPlaying = !this.isPlaying;
};
这是我制造钢琴并检查单击哪个键并播放适当声音(单独的JS文件)的地方:
// keyboard creation function
window.onload = function () {
// Keyboard Height
var keyboard_height = 120;
// Keyboard Width
var keyboard_width = 980;
// White Key Color
var white_color = 'white';
// Black Key Color
var black_color = 'black';
// Number of octaves
var octaves = 2;
// ID of containing Div
var div_id = 'keyboard';
//------------------------------------------------------------
var paper = Raphael(div_id, keyboard_width, keyboard_height);
// Define white key specs
var white_width = keyboard_width / 14;
// Define black key specs
var black_width = white_width/2;
var black_height = keyboard_height/1.6;
var repeat = 0;
var keyboard_keys = [];
//define white and black key names
var wkn = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
var bkn = ['Csharp', 'Dsharp', 'Fsharp', 'Gsharp', 'Asharp'];
//create octave groups
for (i=0;i<octaves;i++) {
//create white keys first
for (var w=0; w <= 6 ; w++) {
keyboard_keys[wkn[w]+i] = paper.rect(white_width*(repeat + w), 0, white_width, keyboard_height).attr("fill", white_color);
};
//set multiplier for black key placement
var bw_multiplier = 1.5;
//then black keys on top
for (var b=0; b <= 4 ; b++) {
keyboard_keys[bkn[b]+i] = paper.rect((white_width*repeat) + (black_width*bw_multiplier), 0, black_width, black_height).attr("fill", black_color);
bw_multiplier = (b == 1) ? bw_multiplier + 4 : bw_multiplier + 2;
};
repeat = repeat + 7;
}
for (var i in keyboard_keys) {
(function (st) {
st.node.onclick = function(event) {
var newColor = '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
st.animate({fill:newColor}, 100);
var testKey = st.paper.getElementByPoint(event.pageX, event.pageY);
var indexOfKey = testKey.id;
if (indexOfKey == 0)
{
playSound(BUFFERS.Down1);
}
else if (indexOfKey == 1)
{
playSound(BUFFERS.Down3);
}
else if (indexOfKey == 2)
{
playSound(BUFFERS.Down5);
}
else if (indexOfKey == 3)
{
playSound(BUFFERS.Down6);
}
else if (indexOfKey == 4)
{
playSound(BUFFERS.Down8);
}
else if (indexOfKey == 5)
{
playSound(BUFFERS.Down10);
}
else if (indexOfKey == 6)
{
playSound(BUFFERS.Down12);
}
else if (indexOfKey == 7)
{
playSound(BUFFERS.Down2);
}
else if (indexOfKey == 8)
{
playSound(BUFFERS.Down4);
}
else if (indexOfKey == 9)
{
playSound(BUFFERS.Down7);
}
else if (indexOfKey == 10)
{
playSound(BUFFERS.Down9);
}
else if (indexOfKey == 11)
{
playSound(BUFFERS.Down11);
}
else if (indexOfKey == 12)
{
playSound(BUFFERS.Up13);
}
else if (indexOfKey == 13)
{
playSound(BUFFERS.Up15);
}
else if (indexOfKey == 14)
{
playSound(BUFFERS.Up17);
}
else if (indexOfKey == 15)
{
playSound(BUFFERS.Up18);
}
else if (indexOfKey == 16)
{
playSound(BUFFERS.Up20);
}
else if (indexOfKey == 17)
{
playSound(BUFFERS.Up22);
}
else if (indexOfKey == 18)
{
playSound(BUFFERS.Up24);
}
else if (indexOfKey == 19)
{
playSound(BUFFERS.Up14);
}
else if (indexOfKey == 20)
{
playSound(BUFFERS.Up16)
}
else if (indexOfKey == 21)
{
playSound(BUFFERS.Up19);
}
else if (indexOfKey == 22)
{
playSound(BUFFERS.Up21);
}
else
{
playSound(BUFFERS.Up23);
}
};
})(keyboard_keys[i]);
}
};
在这里,我在HTML中定义了音量控件的范围滑块(不用担心它在我的代码中格式正确):
<div id="keyboard">
<script>
loadBuffers();
var beat1 = new Beat1();
var beat2 = new Beat2();
</script>
</div>
<div>Volume: <input type="range" min="0" max="100" value="100" oninput="VolumeMain.changeVolume(this);" /></div>
<div>Low Pass Filter on: <input type="checkbox" checked="false" oninput="LowPFilter.toggleFilter(this);" />
Frequency: <input type="range" min="0" max="1" step="0.01" value="1" oninput="LowPFilter.changeFrequency(this);" />
Quality: <input type="range" min="0" max="1" step="0.01" value="0" oninput="LowPFilter.changeQuality(this);" /></div>
<div>Beat 1: <input type="button" onclick="beat1.toggle();" value="Play/Pause"/>
Volume: <input type="range" min="0" max="100" value="100" onchange="beat1.changeVolume(this);"></div>
<div>Beat 2: <input type="button" onclick="beat2.toggle();" value="Play/Pause"/>
Volume: <input type="range" min="0" max="100" value="100" onchange="beat2.changeVolume(this);"></div>
</div>
这个问题似乎是因为键盘本身使用的音量控件无法检测到要使用和修改的声音缓冲区.当您确切知道要为哪个源调整音量时,如在Beat1和Beat 2的情况下(这些音量控件都可以正常工作),提供的代码非常有用.我需要代码能够修改缓冲区数组中任何源的音量.我正在使用Raphael包来创建键盘,如果有帮助的话(可能没有用).我会引起人们对playSound(buffer)方法和VolumeMain.changeVolume函数的注意. LowPFilter方法都不起作用,但是一旦我们弄清楚如何为任何给定源调整音量,该方法的问题也将得到解决.
解决方法:
编辑(更新).这样可以消除错误,并允许您访问gainNode值
var gainNode = context.createGain();
function playSound(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
var filter1 = context.createBiquadFilter();
filter1.type = 0;
filter1.frequency.value = 5000;
source.connect(gainNode);
source.connect(filter1);
gainNode.connect(context.destination);
filter1.connect(context.destination);
source.start(audioContext.currentTime);
}
//volume control
VolumeMain.changeVolume = function(element) {
var volume = element.value;
var fraction = parseInt(element.value) / parseInt(element.max);
gainNode.gain.value = fraction * fraction;
console.log(gainNode.gain.value); // Console log of gain value when slider is moved
};
上一个回复
我不是很了解这个问题,但是如果您只想用一段代码作为使用HTML范围滑块设置增益节点的示例,那么这是一个带有振荡器的示例.您可能需要进行一些尖峰测试,并使用振荡器检查类似的代码是否在您的代码中起作用,然后尝试将其应用于音频缓冲区代码.
<input id="gainSlider" type="range" min="0" max="1" step="0.05" value="0.5"/>
var audioContext = new webkitAudioContext();
var osc = audioContext.createOscillator();
osc.start(audioContext.cueentTime);
var gainChan1 = audioContext.createGain();
osc.connect(gainChan1);
gainChan1.connect(audioContext.destination);
var gainSlider = document.getElementById("gainSlider");
gainSlider.addEventListener('change', function() {
gainChan1.gain.value = this.value;
});
标签:html5,web-audio,audio,javascript 来源: https://codeday.me/bug/20191121/2049532.html