Generate editable drawing of audio waveform from audio file in p5js?

Can anyone help me? Im trying to draw a audio waveform of an mp3 file in p5js. Essentially like what you would see on the track in a DAW like LogicPro/Audacity/Adobe Audition. I want the full track to be generated as a drawing, but all at once instead of in realtime while the audio is playing. Is this possible?

Any tips would be appreciated!

Thanks
Gersh

1 Like

There is a popular library for dealing with sound, audio inputs, and sound files in p5.js: p5.sound. Unfortunately it doesn’t have great support for analyzing audio buffers that aren’t actually being played (as far as I can tell). However, the p5.SoundFile class has an undocumented property buffer which is an AudioBuffer, and that class (which is a standard class provided by the browser) allows you to get the raw amplitude data for each channel in an audio buffer. This can then be analyzed to render amplitude or frequency information. Here’s a simple example:

function audioViz(p) {
	let audioFileSelector;
	let audioFile;
	let leftChannelData;
	let rightChannelData;

	p.setup = function() {
		p.createCanvas(400, 400);
		audioFileSelector = p.createFileInput(loadaudioFile);
		audioFileSelector.position(0, 0);
		p.textAlign(p.CENTER, p.CENTER);
		p.noLoop();
	}

	p.draw = function() {
		p.background('lightgray');
		p.noFill();
		p.stroke('red');
		p.strokeWeight(1);
		let loading = true;
		if (leftChannelData) {
			renderChannel(leftChannelData, 0);
			loading = false;
		}
		if (rightChannelData) {
			renderChannel(rightChannelData, p.height);
			loading = false;
		}
		if (loading) {
			p.noStroke();
			p.fill('red');
			p.text('Waiting/Loading..', p.width / 2, p.height / 2);
		}
	}
	
	function renderChannel(data, peakHeight) {
		// values per pixel
		let resolution = p.int(p.max(1, p.round(data.length / p.width)));
		
		// Normalize our graph based on the peak amplitude of this channel
		let maxAmp;
		for (let i = 0; i < data.length; i++) {
			if (maxAmp === undefined || p.abs(data[i]) > maxAmp) {
				maxAmp = data[i];
			}
		}
		
		let x = 0;
		let sum = 0;
		p.beginShape();
		for (let i = 0; i < data.length; i++) {
			// Compute an average for each set of values
			sum += p.abs(data[i]);
			
			if (i % resolution == 0) {
				p.vertex(
					x++,
					// map the average amplitude to range from the center of the canvas to
					// either the top or bottom depending on the channel
					p.map(sum / resolution, 0, maxAmp, p.height / 2, peakHeight));
				sum = 0;
			}
		}
		p.endShape();
	}

	function loadaudioFile(file) {
		audioFile =
			p.loadSound(
				file.data,
				() => {
					// This gets the raw amplitude data for the channel, this is an array
					// of values between -1 and 1
					leftChannelData = audioFile.buffer.getChannelData(0);
					rightChannelData =
						audioFile.buffer.numberOfChannels > 1 ?
						  audioFile.buffer.getChannelData(1) :
							undefined;
					p.redraw();
				},
				e => console.error(e)
			);
	}
}

new p5(audioViz);

Here’s the runnable sketch.

Would it be possible to tweak this to change the way it looks on user input? Or is it only drawn once? For instance a slider that changes the amplitude of the waveform, or change the color in realtime etc.
Also is it possible to make the visual output more pleasing? Ive seen some people make a waveform from rectangle bars and i think that looks better than the vertex lines? Would it be possible to do that with this code?

Of course it is possible. This example is just meant to demonstrate the use of loadSound/buffer/getChannelData to get audio file data. I chose to use a beginShape/vertex/endShape to render the data because it was simple. You can use whatever approach you want.

I suggest you read this example carefully and read the documentation for any methods you aren’t familiar with. If there are specific things you still don’t understand, feel free to ask.

However if you’re asking me to make a bunch of tweaks to this example to make it look precisely how you want… that’s gonna be a no :fishing_pole_and_fish:

1 Like

just to add - programming takes time. Fortunately or unfortunately we are used to commercial software with polished user interfaces but that took years and numerous programmers to do it. If you start coding something like that by yourself, be patient and embrace small steps. You may not make Ableton 10 in a week but at first they started with a very small team
https://roberthenke.com/technology/ableton_live.html

2 Likes

Hi yes thanks, thats what i wanted to know, im still relatively new to p5js so was wondering if it could be done, wasnt asking for you to do it for me…

I do have one more question tho, although it is unrelated to this topic, would best practice be asking here or asking in a new post?

Great! If it’s unrelated you should definitely make a new post, you’ll get better visibility that way.

Thanks, ill do that.