Wednesday, January 26, 2022

Using the Web Audio API in 2022

draft
  
The good news is that there is a pretty interesting Javascript interface to audio inside HTML 5 that is supported by most browsers.  The bad news is that the documentation is not perfect, it rarely is.  Here is a list of some of the things I learned implementing a web browser based "typewriter"-like interface.
 
1. The abbreviation "mdn" stands for Mozilla Developer Network and is the best place that I have found for documentation on HTML and the Javascript API. So by prefacing any search for information with the letters "mdn" I eliminate most of the extraneous trash.
 
2. Most browsers seem to support the Web Audio API just fine, but you will discover that the browser web page will start muted, with audio disabled. Therefore, you need something like a button the user has to hit to enable audio and you need to check the state of your audio context and possibly "resume" it.
 
3. Not all nodes support start() and stop().

4. Nodes that do support start() and stop() can only be used once.  Therefore you will need to recreate any node for each time you plan to start/stop it.

5. The good news is that this node recreation can be fast, particularly if you are just reusing an audio buffer having previously created it.

6. To use sampled audio (e.g. not an oscillator or a constant), you will need to do the following or equivalent:


a. Defeat CORS
 /usr/bin/chromium-browser --disable-web-security

b.  Move the samples into /var/www/html and run Apache on the local machine.

c. Get the IP number or host name of the machine running Apache

d. Create an audio context

           var audioContext = new AudioContext();
 
e. Create a helper function to start the data transfer if you are going to do this many times.
 
       function loadAudioFile(url, handler) {

           var request = new XMLHttpRequest();

          request.open('GET', url, true);
          request.responseType = 'arraybuffer';
          request.onload = handler;
          request.send();
          console.log("requesting file " + url);
          return request;
      }
 
f. Then, for each audio sample you want to use, keep some variables to track the state of things and do bookkeeping and then start the transfer
 
          var dataLoaded1 = false;
          var url1 = "http://192.168.0.6/many.mp3";
          var request1 = loadAudioFile(url1, audio.onLoad1);     
 
 
g. Possibly create a gain node.

          var gain1 = audioContext.createGain();
          gain1.gain.value = 1.0;

h. Create somewhere some code to receive information about the data transfer, which in my program is part of a closure named "audio".  I have one of these for each different sample.  It saves the returned data, decodes the data and puts it somewhere it can be found later, and sets a variable to indicate that the data has been loaded and decoded.

      onLoad1: function() {

          var audioData = request1.response;
          audioContext.decodeAudioData(audioData, function(buffer) {
          dataLoaded1 = true;
          buf1 = buffer;
          });
      }

i. Then each time I want to use the data, e.g. play the specific sample we loaded, we do the following things: check that audio is running, check that the data has been loaded, create a one-use-only buffer source, attach the buffer, create the part of the audio graph that goes through the gain knob on its way to the output (destination).  In this case, we want to play the sample for "sample_stop" seconds.

      carriageReturnLen: function(len_string) {

          if (audioEnabled === true && audioPause === false) {

          var sample_stop = Math.max(0.5, (len_string / 30.0) * 3.0); 
         
          if (dataLoaded1 === true) {
              src1 = audioContext.createBufferSource();
              src1.buffer = buf1;
              src1.connect(gain1).connect(audioContext.destination);
              src1.start(0);
              src1.stop(audioContext.currentTime + sample_stop);
          }
          console.log("carriage return " + sample_stop);
          }
      },

And that is all there is to it!

7. Be aware that the documentation points to examples that may not be working, although they probably worked at one point.  

8. There will be latency.

9. I never got the <audio> tag to work.

10. At some point, there will be a link here to a working example.

 




No comments:

Post a Comment