2014-10-18 2 views
3

Итак, я создаю пианино через веб-аудио, и у меня проблемы с реализацией регулятора громкости. Всякий раз, когда нажимается клавиша, регулятор громкости должен определять, на каком громкости он воспроизводится. Я использовал код от html5rocks и модифицировал его в своих целях. В основном вместо массива VolumeSample у меня есть все мои звуковые клики, загруженные в массив BUFFERS. Всякий раз, когда я пытаюсь манипулировать слайдером и изменять коэффициент усиления клипа, я получаю «не могу прочитать свойство« получить »значение null. Я тестирую его через отладчик, и все работает отлично до тех пор, пока эта фракция this.gainNode.gain.value = fraction *; часть моего кода. Просто взгляните на мой код и, надеюсь, вы увидите, что мне не хватает. Я хотел бы обратить внимание на метод playSounds (буфер), который где создать и подключить узел усиления, и changeVolume метода в нижней части, что где actualy изменение узла усиления происходит:создать регулятор громкости для веб-аудио

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 (буфер) и функции VolumeMain.changeVolume. Ни один из методов LowPFilter не работает, но как только мы выясним, как настроить громкость для любого источника, проблема этого метода также будет исправлена.

ответ

3

Редактировать (обновить). Это устраняет ошибки и позволяет получить доступ к значению 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 - вот пример с генератором. Возможно, вы захотите сделать небольшой тест на шип и посмотреть, работает ли что-то подобное в коде с помощью генератора, а затем попробуйте применить его к вашему звуковому буферному коду.

http://jsfiddle.net/vqb9dmrL/

<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; 
}); 

+0

, кажется, не работает, так как я получаю неперехваченного TypeError: Не удается прочитать свойство 'addEventListener' от нулевой ошибки. Спасибо за попытку, хотя – TeamRival

+0

Не могли бы вы отредактировать свой пост и опубликовать больше своего кода, чтобы показать, как вы интегрируете все. Небольшой код, который я написал, работает автономно, и он модифицирует узел усиления, поэтому он должен работать с любым звуком, направляемым в затронутый узел усиления независимо от того, является ли он осциллятором или звуковым буфером. – William

+0

сделано, я также попытался объяснить проблему лучше внизу.Предоставленный код должен содержать все необходимое – TeamRival

Смежные вопросы