Loading Symbol

How to Process an Uploaded File with the Web Audio API

Adio MIDI Controller
The Web Audio API was created to give developers more control over the audio that plays in browsers. This tutorial will show you how to use the Web Audio API to process audio files uploaded by users in their browser. We will be using a Compressor Node in this tutorial, but you can use one or more of the other nodes by swapping them out with the Compressor. The basic flow is like this:

Input -> Audio Nodes -> Destination

Loading GIF

Step 1: Create HTML page

Let’s get started with a blank HTML page that has bootstrap 4, jQuery, and a file input element. It will also contain a “Compress” button for the user to click when they’re ready to process their file.
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

  <!-- Bootstrap 4 -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
</head>

<body>
    <div class="container">
      <div class="row">
       <div class="col-12">
        <div style="margin: 200px auto 0; width: 300px;">

          <h5>Upload Audio File</h5>
          <fieldset>
            <input type="file" id="audio-file" accept="audio/mpeg, audio/ogg, audio/*" />
            <button type="button" id="compress_btn" class="btn btn-outline-primary" style="margin-top: 20px;">Compress</button>
          </fieldset>

          </div>
        <div>
      </div>
    </div>
  </div>
</body>
</html>

Step 2: Define our audio processing in compressor.js

Next, we’ll create a JavaScript file called compressor.js and add it add the bottom of the page just above the body tag. Don’t forget to change the path.
<script type="text/javascript" src="/wp-content/themes/your-theme/js/compressor.js"></script>
Now we can start adding the Audio Processing functionality to compressor.js. First, to get the file that the user uploaded, we select it.
var fileInput = document.getElementById('audio-file');
Next, we create an AudioContext.
var audioCtx = new (AudioContext || webkitAudioContext)();
Once the user uploads a file, they’ll need to click the button to kick off the processing. We’ll add an event listener to that button, and check for the file inside the event listener callback.
var compress_btn = document.getElementById('compress_btn');

//Load audio file listener
compress_btn.addEventListener("click", function() {

  // check for file
  if(fileInput.files[0] == undefined) {

    // Stop the process and tell the user they need to upload a file.
    return false;
  }

  // Reader will go here

}, false);
Now, that we’ve checked for the input file, we can create a FileReader and start reading the file to an ArrayBuffer. Once it’s finished, we will call decodeAudioData(ArrayBuffer) on the AudioContext we created, passing in the ArrayBuffer of audio data. We will use the Promise version of decodeAudioData as opposed to the event based version. After decodeAudioData is through, we’ll pass the resulting AudioBuffer to the then() function of the Promise. Inside the decodeAudioData() function, we call createBufferSource() on the AudioContext to create a BufferSourceNode. Then we set it’s buffer to the buffer passed in from the Promise. The BufferSourceNode is the first node the audio will travel through. After this node, we can send it to any node(s) we want.
// Reader will go here
var reader1 = new FileReader();
reader1.onload = function(ev) {
    
    // Decode audio
    audioCtx.decodeAudioData(ev.target.result).then(function(buffer) {

    var soundSource = audioCtx.createBufferSource();
    soundSource.buffer = buffer;

    // Create Compressor Node

    });
  };
  reader1.readAsArrayBuffer(fileInput.files[0]);

Process the AudioBuffer

Finally, we’re ready to create the Compressor Node. We use createDynamicsCompressor() and then put in our compressor settings. You can put whatever you want here, but these settings tell our compressor how to work.
// Create Compressor Node
var compressor = audioCtx.createDynamicsCompressor();

compressor.threshold.setValueAtTime(-20, audioCtx.currentTime);
compressor.knee.setValueAtTime(-30, audioCtx.currentTime);
compressor.ratio.setValueAtTime(5, audioCtx.currentTime);
compressor.attack.setValueAtTime(.05, audioCtx.currentTime);
compressor.release.setValueAtTime(.25, audioCtx.currentTime);

Loading GIF

Now we use the connect() method to create a chain of nodes. The parameter passed into the connect() function is the next node in the chain. Finally, we connect the last node (the compressor in this case) to the destination. We start with the source node that we created with soundSource = audioCtx.createBufferSource();
soundSource.buffer = buffer;
soundSource.buffer = buffer; 

 soundSource.connect(compressor);
Finally, connect the last node to the AudioContext destination and play the audio.
compressor.connect(audioCtx.destination);
soundSource.start(0);
You’re compressor.js should like something like this:
var fileInput = document.getElementById('audio-file');
var audioCtx = new (AudioContext || webkitAudioContext)();

var compress_btn = document.getElementById('compress_btn');

//Load audio file listener
compress_btn.addEventListener("click", function() {

  // check for file
  if(fileInput.files[0] == undefined) {

    // Stop the process and tell user they need to upload a file.
    return false;
  }

  var reader1 = new FileReader();
	reader1.onload = function(ev) {
	    
	    // Decode audio
	    audioCtx.decodeAudioData(ev.target.result).then(function(buffer) {

	    var soundSource = audioCtx.createBufferSource();

		soundSource.buffer = buffer;
		
	    // Create Compressor Node
		compressor = audioCtx.createDynamicsCompressor();

		compressor.threshold.setValueAtTime(-20, audioCtx.currentTime);
		compressor.knee.setValueAtTime(-30, audioCtx.currentTime);
		compressor.ratio.setValueAtTime(5, audioCtx.currentTime);
		compressor.attack.setValueAtTime(.05, audioCtx.currentTime);
		compressor.release.setValueAtTime(.25, audioCtx.currentTime);

		soundSource.connect(compressor);

		compressor.connect(audioCtx.destination);
		soundSource.start(0);

	    });
	  };
	  reader1.readAsArrayBuffer(fileInput.files[0]);

}, false);
Now you should be able to upload a file, click the Compress button, and hear the processed audio played back.

Conclusion

Now you can manipulate audio from the audio file in the client’s browser. If you want to learn how to write the processed audio data to a new file, check out my tutorial on how to write an ArrayBuffer to an audio file.

Loading Symbol


3 responses to “How to Process an Uploaded File with the Web Audio API

  1. yes it works great but you omitted the
    Var

    Var compressor = audioCtx.createDynamicsCompressor();
    Great stuff!!;
    Thanks..

Leave a Reply

Your email address will not be published. Required fields are marked *