Search code examples
html5-audioweb-audio-apiaudiocontext

Why the web audio output from oscillator is not working as expected?


Here is the code:

I want to create an audio program that can play audio from very low frequency to high frequency.

However, this code results in different output (even with the same device):

  1. The sound comes out suddenly - the expected result is it comes out gradually. I am sure my hearing is okay because I've asked my friends to hear;
  2. The audio sounds different on the same frequency.

WARNING: Please adjust your volume to very low in case of any hurting before running this script.

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

// create Oscillator node
var oscillator = audioCtx.createOscillator();

var osc_arr = [];

function purgeSound(){
  osc_arr.forEach(function(v){
    try {
      v.stop();
      v.disconnect(audioCtx.destination);
    } catch (e) {}
  })
}

function playSoundAtFreq(fq){
  purgeSound();
  var osc = audioCtx.createOscillator();
  osc_arr.push(osc);
  osc.type = 'square';
  osc.frequency.setValueAtTime(fq, audioCtx.currentTime); // value in hertz
  $('#fff').val(fq);
  osc.connect(audioCtx.destination);
  osc.start();
}

$('#stop').click(function(){
  purgeSound();
  _break = true;
})

var _break = false;
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
var pointer = 0;
var go = appendAttemptAsync(10000);
async function appendAttemptAsync(range) {
  if(_break) return;
  var target = pointer+range;
  for (pointer; pointer<range; pointer++) {
    playSoundAtFreq(pointer);
    console.log(pointer)
    //if(pointer % 1 == 0) {
      await sleep(100)
    //}
  }
  return 5221;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id='stop'>stop</button>
<input id="fff" type="text" />

WARNING: Please adjust your volume to very low in case of any hurting before running this script.

Thanks for any kind of suggestions to improve my code.


Solution

  • If you want an Oscillator to sweep like in the YouTube video that you mentioned, you can do something like:

    let osc = new OscillatorNode(audioCtx);
    osc.connect(audioCtx.destination);
    osc.frequency.setValueAtTime(20, audioCtx.currentTime);
    osc.frequency.linearRampToValueAtTime(audioCtx.sampleRate / 2, audioCtx.currentTime + 300);
    osc.start();
    

    Change the 300 to some appropriate time over which the tone sweeps. I arbitrarily chose 5 minutes.

    I do not know why your example doesn't work, but this snippet is the typical way to sweep a tone using WebAudio.