网络音频API故障/失真问题

问题描述:

我是新来的网络音频API,并做了一个简单的合成器来了解情况。麻烦是我的音频在输入很重的声音后会变形很多。所以如果我通过它推动一大堆频率会扭曲。任何熟悉API的人都可以快速浏览一下我的代码,看看是否有任何重大错误/遗漏突出显示?可以在Safari,Chrome和Firefox中重新创建问题。演示版本是HERE感谢您的任何帮助!网络音频API故障/失真问题

//start new audio session. 
var context = new (window.webkitAudioContext || window.AudioContext || window.mozAudioContext) 



function playSound(note) { 
    oscillator = context.createOscillator(); 

//create volume controller 
var gainNode = context.createGain(); 

//connect signal to audio output(speakers by default) 
oscillator.connect(gainNode); 
gainNode.connect(context.destination); 

//adjusts frequency played by 50%, 100% or 200% 
var octave = document.getElementById('octave').value; 

//sets oscillator frequency 
oscillator.frequency.value = frequencies[note] * octave; 

//oscillator wave type 
oscillator.type = document.getElementById('waveSelect').value; 


//initialize gain at 0 and ramp up to full volume very quikcly (prevents audible 'pop') 
gainNode.gain.value = 0 
var quickFadeIn = gainNode.gain.setTargetAtTime(1, context.currentTime, 0.1); 

//starts oscillator. Delayed start can be achieved by adding time(in secs) after currentTime 
oscillator.start(context.currentTime + .05); 

/** 
* AUDIO EFFECTS 
*/ 

function delayNode() { 
    //create delay 
    var delay = context.createDelay(); 
    delay.delayTime.value = .5; 

    //create gain 
    gainNode; 
    //gainNode.gain.value = 0.8; 
    quickFadeIn; 

    //create feedback loop 
    oscillator.connect(gainNode); 
    gainNode.connect(delay); 
    delay.connect(gainNode); 
    delay.connect(context.destination); 

    //decrease gain 
    quickFadeOut; 
} 

function distortionNode() { 
    var distortion = context.createWaveShaper(); 

//distortion curve taken from MDN which they in turn took from Stack Overflow 
    function makeDistortionCurve(amount) { 
     var k = typeof amount === 'number' ? amount : 50, 
     n_samples = 44100, 
     curve = new Float32Array(n_samples), 
     deg = Math.PI/90, 
     i = 0, 
     x; 
     for (; i < n_samples; ++i) { 
     x = i * 2/n_samples - 1; 
     curve[i] = (3 + k) * x * 20 * deg/(Math.PI + k * Math.abs(x)); 
     } 
     return curve; 
    }; 

    distortion.curve = makeDistortionCurve(500); 
    distortion.oversample = '4x'; 

    gainNode; 

    quickFadeIn; 

    oscillator.connect(gainNode); 
    gainNode.connect(distortion); 
    distortion.connect(context.destination); 

    //decrease gain 
    quickFadeOut; 
} 

if (document.getElementById('toggleDelay').value == 'true'){delayNode();} 
if (document.getElementById('toggleDistortion').value == 'true'){distortionNode();} 

//determines note duration 
var sustain = parseFloat(document.getElementById('sustain').value); 

//stops oscillator by exponentially ramping down sound over .015 seconds to avoid audible click 
var quickFadeOut = gainNode.gain.setTargetAtTime(0, context.currentTime + sustain, 0.0015); 

//change key color on keypress 

    //append the word "note" to the object.name note to identify the correct key div 
    var divId = "note" + String(note); 
    var element = document.getElementById(divId); 

    //change background color for durarion of note length 
    var currentColor = element.style.backgroundColor; 
    element.style.backgroundColor = '#3cf7ac'; 
    setTimeout(function() { 
    if (currentColor != 'rgb(60, 247, 172)') { 
     element.style.backgroundColor = currentColor 
    } 
}, 1000 * sustain); 



//for testing 
console.log('playSound Hz:' + frequencies[note] * octave + ' octave:' + octave + ' wave:' + oscillator.type + ' duration: ' + sustain + ' time:' + context.currentTime.toFixed(2)); 
} 

//controls 2nd keyboard. Same logic as playSound() 
function playSoundb(note) { 
oscillator = context.createOscillator(); 
var gainNode = context.createGain(); 
oscillator.connect(gainNode); 
gainNode.connect(context.destination); 



var octaveb = document.getElementById('octaveb').value; 
    oscillator.frequency.value = frequencies[note] * octaveb; 


oscillator.type = document.getElementById('waveSelectb').value; 

gainNode.gain.value = 0 
var quickFadeIn = gainNode.gain.setTargetAtTime(.75, context.currentTime, .1); 
oscillator.start(context.currentTime + .05); 

/** 
* AUDIO EFFECTS 
*/ 

function delayNode() { 
    var delay = context.createDelay(); 
    delay.delayTime.value = .5; 

    gainNode; 
    quickFadeIn; 

    //create feedback loop 
    oscillator.connect(gainNode); 
    gainNode.connect(delay); 
    delay.connect(gainNode); 
    delay.connect(context.destination); 

    //decrease gain 
    quickFadeOut; 
} 

function distortionNode() { 
    var distortion = context.createWaveShaper(); 

    function makeDistortionCurve(amount) { 
     var k = typeof amount === 'number' ? amount : 50, 
     n_samples = 44100, 
     curve = new Float32Array(n_samples), 
     deg = Math.PI/90, 
     i = 0, 
     x; 
     for (; i < n_samples; ++i) { 
     x = i * 2/n_samples - 1; 
     curve[i] = (3 + k) * x * 20 * deg/(Math.PI + k * Math.abs(x)); 
     } 
     return curve; 
    }; 

    distortion.curve = makeDistortionCurve(900); 
    distortion.oversample = '4x'; 

    gainNode; 
    quickFadeIn; 

    oscillator.connect(gainNode); 
    gainNode.connect(distortion); 
    distortion.connect(context.destination); 

    quickFadeOut; 
} 

if (document.getElementById('toggleDelayb').value == 'true'){delayNode();} 
if (document.getElementById('toggleDistortionb').value == 'true'){distortionNode();}   

var sustainb = parseFloat(document.getElementById('sustainb').value); 

var quickFadeOut = gainNode.gain.setTargetAtTime(0, context.currentTime + sustainb, 0.0015); 

//change key color on keypress 
var divId = "note" + String(note) + "b"; 
var element = document.getElementById(divId); 
var currentColor = element.style.backgroundColor; 
element.style.backgroundColor = '#3ce4f7'; 
setTimeout(function() { 
    if (currentColor != 'rgb(60, 228, 247)') { 
     element.style.backgroundColor = currentColor 
    } 
}, 1000 * sustainb); 

//for testing 
console.log('playSound*B* Hz:' + frequencies[note] * octave + ' octave:' + octave + ' wave:' + oscillator.type + ' duration: ' + sustain + ' time:' + context.currentTime); 

}

//reveals 2nd keyboard 
function displayKeyboard2(lowersynth, uppersynth) { 
    var bottom = document.getElementById(lowersynth); 
    var top = document.getElementById(uppersynth); 

if (bottom.style.display == 'block') { 
    bottom.style.display = 'none'; 
    top.style.marginTop = '150px'; 
} 

else { 
    bottom.style.display = 'block'; 
    top.style.marginTop = '0'; 
} 

}

//Frequencies in Hz of notes to be played. 
var frequencies = { 
    'C_1': 130.81, 
    'C#1': 139.00, 
    'D_1': 146.83, 
    'D#1': 156.00, 
    'E_1': 164.81, 
    'F_1': 174.61, 
    'F#1': 185.00, 
    'G_1': 196.00, 
    'G#1': 208.00, 
    'A_1': 220.00, 
    'A#1': 233.00, 
    'B_1': 246.94, 
    'C_2': 261.63, 
    'C#2': 277.00, 
    'D_2': 293.66, 
    'D#2': 311.00, 
    'E_2': 329.63, 
    'F_2': 349.23, 
    'F#2': 370.00, 
    'G_2': 392.00, 
    'G#2': 415.00, 
    'A_2': 440.00, 
    'A#2': 466.00, 
    'B_2': 493.88, 
    'C_3': 523.25, 
}; 

//triggers playSound() to create note 
document.getElementById('noteC_1').addEventListener(('click' || 'touchstart'),function() { playSound('C_1');}); 
document.getElementById('noteC#1').addEventListener(('click' || 'touchstart'),function() { playSound('C#1');}); 
document.getElementById('noteD_1').addEventListener(('click' || 'touchstart'),function() { playSound('D_1');}); 
document.getElementById('noteD#1').addEventListener(('click' || 'touchstart'),function() { playSound('D#1');}); 
document.getElementById('noteE_1').addEventListener(('click' || 'touchstart'),function() { playSound('E_1');}); 
document.getElementById('noteF_1').addEventListener(('click' || 'touchstart'),function() { playSound('F_1');}); 
document.getElementById('noteF#1').addEventListener(('click' || 'touchstart'),function() { playSound('F#1');}); 
document.getElementById('noteG_1').addEventListener(('click' || 'touchstart'),function() { playSound('G_1');}); 
document.getElementById('noteG#1').addEventListener(('click' || 'touchstart'),function() { playSound('G#1');}); 
document.getElementById('noteA_1').addEventListener(('click' || 'touchstart'),function() { playSound('A_1');}); 
document.getElementById('noteA#1').addEventListener(('click' || 'touchstart'),function() { playSound('A#1');}); 
document.getElementById('noteB_1').addEventListener(('click' || 'touchstart'),function() { playSound('B_1');}); 
document.getElementById('noteC_2').addEventListener(('click' || 'touchstart'),function() { playSound('C_2');}); 
document.getElementById('noteC#2').addEventListener(('click' || 'touchstart'),function() { playSound('C#2');}); 
document.getElementById('noteD_2').addEventListener(('click' || 'touchstart'),function() { playSound('D_2');}); 
document.getElementById('noteD#2').addEventListener(('click' || 'touchstart'),function() { playSound('D#2');}); 
document.getElementById('noteE_2').addEventListener(('click' || 'touchstart'),function() { playSound('E_2');}); 
document.getElementById('noteF_2').addEventListener(('click' || 'touchstart'),function() { playSound('F_2');}); 
document.getElementById('noteF#2').addEventListener(('click' || 'touchstart'),function() { playSound('F#2');}); 
document.getElementById('noteG_2').addEventListener(('click' || 'touchstart'),function() { playSound('G_2');}); 
document.getElementById('noteG#2').addEventListener(('click' || 'touchstart'),function() { playSound('G#2');}); 
document.getElementById('noteA_2').addEventListener(('click' || 'touchstart'),function() { playSound('A_2');}); 
document.getElementById('noteA#2').addEventListener(('click' || 'touchstart'),function() { playSound('A#2');}); 
document.getElementById('noteB_2').addEventListener(('click' || 'touchstart'),function() { playSound('B_2');}); 
document.getElementById('noteC_3').addEventListener(('click' || 'touchstart'),function() { playSound('C_3');}); 


document.getElementById('noteC_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('C_1');}); 
document.getElementById('noteC#1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('C#1');}); 
document.getElementById('noteD_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('D_1');}); 
document.getElementById('noteD#1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('D#1');}); 
document.getElementById('noteE_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('E_1');}); 
document.getElementById('noteF_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('F_1');}); 
document.getElementById('noteF#1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('F#1');}); 
document.getElementById('noteG_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('G_1');}); 
document.getElementById('noteG#1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('G#1');}); 
document.getElementById('noteA_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('A_1');}); 
document.getElementById('noteA#1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('A#1');}); 
document.getElementById('noteB_1b').addEventListener(('click' || 'touchstart'),function() { playSoundb('B_1');}); 
document.getElementById('noteC_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('C_2');}); 
document.getElementById('noteC#2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('C#2');}); 
document.getElementById('noteD_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('D_2');}); 
document.getElementById('noteD#2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('D#2');}); 
document.getElementById('noteE_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('E_2');}); 
document.getElementById('noteF_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('F_2');}); 
document.getElementById('noteF#2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('F#2');}); 
document.getElementById('noteG_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('G_2');}); 
document.getElementById('noteG#2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('G#2');}); 
document.getElementById('noteA_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('A_2');}); 
document.getElementById('noteA#2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('A#2');}); 
document.getElementById('noteB_2b').addEventListener(('click' || 'touchstart'),function() { playSoundb('B_2');}); 
document.getElementById('noteC_3b').addEventListener(('click' || 'touchstart'),function() { playSoundb('C_3');}); 

无重大遗漏的 - 这是在数字音频刚刚发生的事情,当你重载输出(即音频的瞬时值< -1或> +1) - 你会得到裁剪,这通常听起来很讨厌。可能最好的做法(除了保持增益值低于1)是将DynamicsCompressor放在输出上(即通过context.createDynamicsCompressor()创建一个DynamicsCompressorNode,将它连接到context.destination,然后将注释连接到压缩器比context.destination)。在这种情况下,默认值是合理的(压缩器设置是一个音乐决定,但这至少有助于剪辑)。

+0

谢谢Chris!我会玩弄增益和压缩机的建议。很高兴知道我没有犯任何重大错误。我真的很感谢你看看我的代码! – Fedreg