p5 Audio

Resources

p5 - Sound Library Reference

Shiffman - 17.1: Loading and Playing - p5.js Sound Tutorial
Shiffman - 17.2: Play and Pause Button - p5.js Sound Tutorial
Shiffman - 17.4: Amplitude Analysis - p5.js Sound Tutorial
Shiffman - 17.6: Sound Synthesis - p5.js Sound Tutorial
Shiffman - 17.8: Microphone Input - p5.js Sound Tutorial
Shiffman - 17.11: Frequency Analysis with FFT - p5.js Sound Tutorial

Using the Audio Library

The examples on this page will show you how to record, play and analyze audio. In order to follow along with these examples, you'll need to run a local server. You'll also need to include the p5 Sound library. Here's an index.html file that you can use for these examples (assuming you have a sketch.js in the same folder):

<!DOCTYPE html>
<html>
<head>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.dom.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.sound.min.js"></script>
 <script src="sketch.js"></script>
</head>
<body>
 
</body>
</html> 

Play a Sound

We'll also need to download an .mp3 file (or .wav) to play. Feel free to use any audio sample you have available to follow along, or download this short sample (the Amen break by the Winstons) and long sample (Simplicty by Macroform). We can also load remote resources like https://ia800608.us.archive.org/22/items/jamendo-067400/05.mp3.

We can load the sound file using loadSound() in the preload() function, as we've done before with loadImage() or loadJSON(). We then invoke the .play() method, which plays the sound one time. Some other useful methods to call on sound files include .loop(), .pan(), .setVolume(). A full list is here.


let song;

function preload(){
  song = loadSound("assets/simplicity.mp3");
}

function setup() {
  song.play();
}

Alternatively, if we want to load new sounds during run-time, we can use a callback function:


let song, button;

function setup() {
  button = createButton("Load and Play Song", 10, 10);
  button.mousePressed(loadSong);
}

function loadSong(){
  song = loadSound("assets/simplicity.mp3", songLoaded);
}

function songLoaded() {
  song.play();
}

Next, let's go back to loading the file in preload() and playing automatically, and add a slider to control volume using .setVolume()


let song, slider;

function preload(){
  song = loadSound("assets/simplicity.mp3");
}

function setup() {
  song.play();
  slider = createSlider(0, 1, 0.5, 0.01);
}

function draw(){
  song.setVolume(slider.value());
}

Try to add two more sliders, using the methods song.pan() and song.rate()

Or, we can control the sound with mouse position and visualize it with graphical elements.


let song;

function preload() {
  song = loadSound('assets/simplicity.mp3');
}

function setup() {
  createCanvas(710, 400);
  song.loop();
}

function draw() {
  background(200);

  // Set the volume to a range between 0 and 1.0
  let volume = map(mouseX, 0, width, 0, 1);
  volume = constrain(volume, 0, 1);
  song.amp(volume);

  // Set the rate to a range between 0.1 and 4
  // Changing the rate alters the pitch
  let speed = map(mouseY, 0.1, height, 0, 2);
  speed = constrain(speed, 0.01, 4);
  song.rate(speed);

  // Draw some circles to show what is going on
  stroke(0);
  fill(51, 100);
  ellipse(mouseX, 100, 48, 48);
  stroke(0);
  fill(51, 100);
  ellipse(100, mouseY, 48, 48);
}

Generating Sound

We can also use the many oscillator methods in p5 to synthesize audio.


let osc;
let playing = false;

function setup() {
  noCanvas();
  button = createButton("Play", 10, 10);
  button.mousePressed(togglePlay);

  osc = new p5.Oscillator();
  osc.setType('sine');
  osc.freq(240);
  osc.amp(0);
  osc.start();
}

function togglePlay() {
  if (!playing) {
    // ramp amplitude to 0.5 over 0.05 seconds
    osc.amp(0.5, 0.05);
    playing = true;
    button.html('Pause');
  } else {
    // ramp amplitude to 0 over 0.5 seconds
    osc.amp(0, 0.5);
    playing = false;
    button.html('Play');
  }
}

Otherwise, we can generate compositions using a library of samples. In this sketch, 5 different samples are loaded and sometimes played back. The sequencer triggers an event every 200-1000 mSecs randomly. Each time a sound is played a colored rect with a random color is displayed. You can download to your /assets folder sample0, sample1, sample2, sample3, and sample4.


let sounds = [];
let numSounds = 5;

// The playSound array is defining how many samples will be played at each trigger event
let playSound = [1,1,1,1,1];

// The trigger is an integer number in milliseconds so we can schedule new events in the draw loop
let trigger;

function preload(){
  // Load 5 soundfiles from a folder in a for loop. By naming the files 1., 2., 3., n.aif it is easy to iterate
  // through the folder and load all files in one line of code.
  for (let i = 0; i < numSounds; i++){
    sounds[i] = loadSound('assets/' + i + '.wav');
  }
}

function setup(){
  createCanvas(640, 360);
  // Create a trigger which will be the basis for our random sequencer. 
  trigger = millis(); 
  noStroke();
}

function draw(){
  // If the determined trigger moment in time matches up with the computer clock events get triggered.
  if (millis() > trigger){
    background(255);
    
    for (let i = 0; i < numSounds; i++){      
      // Check which indexes are 1 and 0. Only play if playSound[i] == 1.
      if (playSound[i] == 1){ 
        let rate;
        fill(random(255),random(255),random(255));
        rect((width/numSounds) * i, 0, (width/numSounds), height);
        sounds[i].play();
      }
      
      // Renew the indexes of playSound so that at the next event the order is different and randomized.
      playSound[i] = round(random(0,1));
    }
    
    // Create a new triggertime in the future, with a random offset between 200 and 1000 milliseconds
    trigger = millis() + round(random(1,4))*200;
  }
}

Mic Input

We can use the p5.AudioIn() method to grab the audio from the user's microphone. The .getLevel() method returns the current volume, which we can use for simple visualizations.


let mic;

function setup(){
  mic = new p5.AudioIn();
  mic.start();
}

function draw(){
  background(0);
  micLevel = mic.getLevel();
  ellipse(width/2, constrain(height-micLevel*height*5, 0, height), 10, 10);
}

FFT

More sophisticated music visualizations usually involve FFT. FFT (Fast Fourier Transform) is an analysis algorithm that isolates individual audio frequencies within a waveform. Once instantiated, a p5.FFT object can return an array based on two types of analyses: • FFT.waveform() computes amplitude values along the time domain. The array indices correspond to samples across a brief moment in time. Each value represents amplitude of the waveform at that sample of time. • FFT.analyze() computes amplitude values along the frequency domain. The array indices correspond to frequencies (i.e. pitches), from the lowest to the highest that humans can hear. Each value represents amplitude at that slice of the frequency spectrum. Use with getEnergy() to measure amplitude at specific frequencies, or within a range of frequencies.

let song, fft;

function preload() {
  song = loadSound('assets/amen.wav');
}

function setup() {
  createCanvas(710,400);
  noFill();
  song.loop();
  fft = new p5.FFT();
}

function draw() {
   background(200);
   let spectrum = fft.analyze();

   beginShape();
   for (let i = 0; i < spectrum.length; i++) {
    vertex(i, map(spectrum[i], 0, 255, height, 0) );
   }
   endShape();
}