Recording Sound in JavaScript with MediaRecorder

The JavaScript program below, when run, presents an interface that allows the user to record an audio clip and play it back. To see it in action, copy it into an .html file and open that file in a web browser that runs JavaScript.

UPDATE 2016/05/31 – I have updated the code to add a button that allows the recording to be saved to a file. As saved by Google Chrome, though, it appears to save the audio in WebM format, which only seems to be understood by browsers, and not by any of my other audio programs. I would much prefer a simple WAV, but unless and until I can figure out how to do that, I suppose this is better than nothing.

SoundRecorder


<html>
<body>

<div id="divMain"></div>

<script type="text/javascript"> 

// classes

function FileHelper()
{
	// static class
}
{
	FileHelper.destroyClickedElement = function(event)
	{
		document.body.removeChild(event.target);
	}
	
	FileHelper.saveBlobAsFile = function(blobToSave, fileNameToSaveAs)
	{
		var downloadLink = document.createElement("a");
		downloadLink.download = fileNameToSaveAs;
		downloadLink.innerHTML = "Download File";
		downloadLink.href = window.URL.createObjectURL(blobToSave);
		downloadLink.onclick = FileHelper.destroyClickedElement;
		downloadLink.style.display = "none";
		document.body.appendChild(downloadLink);	
		downloadLink.click();
	}
}

function Globals()
{
	// do nothing
}
{
	Globals.Instance = new Globals();
	
	Globals.prototype.initialize = function()
	{
		this.soundRecorder = new SoundRecorder();
		
		var divMain = document.getElementById("divMain");
		divMain.appendChild(this.soundRecorder.toDomElement());
		
		this.soundRecorder.initialize();
	}
}

function SoundRecorder()
{
	// do nothing
}
{
	SoundRecorder.prototype.initialize = function()
	{	
		navigator.getUserMedia = 
		( 
			navigator.getUserMedia ||
			navigator.webkitGetUserMedia ||
			navigator.mozGetUserMedia ||
			navigator.msGetUserMedia
		);
	
		navigator.getUserMedia
		(
			{ audio: true },
			
			this.mediaRecorder_GetUserMedia_Successful.bind(this),

			// error callback
			function(error)
			{
				alert("ERROR: " + error);
			}
		);
	}
	
	SoundRecorder.prototype.play = function()
	{
		document.getElementById("inputInfo").value = "Playing...";

		if (this.audioDataRecorded != null)		
		{			
			var audioURLRecorded = window.URL.createObjectURL(this.audioDataRecorded);
			var domElementForAudio = document.createElement("audio");
			domElementForAudio.src = audioURLRecorded;
			domElementForAudio.onended = this.stop.bind(this);
			domElementForAudio.play();
		}
	}

	SoundRecorder.prototype.record = function()
	{
		document.getElementById("inputInfo").value = "Recording...";
		this.mediaRecorder.start
		(
			// hack - Not sure why I have to do this.
			// May limit the recording to 60 seconds?
			60000 // timeslice 
		);
	}
	
	SoundRecorder.prototype.save = function()
	{		
		// The audio appears to be in some sort of "WebM" format.
		// Browsers appear to understand it,
		// but I would much prefer a simple WAV.
	
		FileHelper.saveBlobAsFile
		(
			this.audioDataRecorded,
			"Recording.webm" 
		);
	}
	
	SoundRecorder.prototype.stop = function()
	{
		document.getElementById("inputInfo").value = "Stopped.";
		if (this.mediaRecorder.state == "recording")
		{
			this.mediaRecorder.stop();
		}
	}
	
	// MediaRecorder event handlers
	
	SoundRecorder.prototype.mediaRecorder_DataAvailable = function(event) 
	{
		if (this.mediaRecorder.state != "recording")
		{
			this.audioDataRecorded = event.data;
			document.getElementById("inputInfo").value = "Sound recorded.";
		}
	}

	SoundRecorder.prototype.mediaRecorder_GetUserMedia_Successful = function(stream)
	{	
		this.mediaRecorder = new MediaRecorder(stream);
		this.mediaRecorder.ondataavailable = this.mediaRecorder_DataAvailable.bind(this);
	}	
	
	// dom
	
	SoundRecorder.prototype.toDomElement = function()
	{
		var returnValue = document.createElement("div");

		var buttonRecord = document.createElement("button");
		buttonRecord.innerHTML = "Record";
		buttonRecord.onclick = this.record.bind(this);
		returnValue.appendChild(buttonRecord);		
	
		var buttonStop = document.createElement("button");
		buttonStop.innerHTML = "Stop";
		buttonStop.onclick = this.stop.bind(this);
		returnValue.appendChild(buttonStop);
		
		var labelInfo = document.createElement("label");	
		labelInfo.innerHTML = "Info:";
		returnValue.appendChild(labelInfo);
		
		var inputInfo = document.createElement("input");
		inputInfo.id = "inputInfo";
		inputInfo.readonly = true;
		inputInfo.value = "Stopped.";
		returnValue.appendChild(inputInfo);
		
		var buttonPlay = document.createElement("button");
		buttonPlay.innerHTML = "Play";
		buttonPlay.onclick = this.play.bind(this);
		returnValue.appendChild(buttonPlay);
		
		var buttonSave = document.createElement("button");
		buttonSave.innerHTML = "Save";
		buttonSave.onclick = this.save.bind(this);
		returnValue.appendChild(buttonSave);
	
		this.domElement = returnValue;
	
		return returnValue;
	}
}

// run

Globals.Instance.initialize();

</script>
</body>
</html>

Advertisements
This entry was posted in Uncategorized and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s