HTML5 has tons of new stuff that are exciting and very interesting. It arrived with features that would replace the need of Flash in browsers. Since the features that were available in Flash is coming into the native JavaScript, soon we won’t have to update our Flash player plugin and depend on Adobe for new awesome features.
More integration with hardware technologies to the Web is the main important thing. We can now access the audio & video hardware of the user’s system efficiently with less code.
In this tutorial, I will introduce you to a small plugin I created to record, play & download the microphone input voice of the user. This is made possible by the Web Audio API.
Project Abandoned
I’m abandoning Francium Voice due to lack of interest in maintaining it. No more support will be given in comments or email.
As an alternative you may use Yuji Miyane’s WebAudioRecorderJS.
NOTICE
Since Chrome 47, Web Audio API works only on HTTPS sites and localhost.
When Audio API is used for recording sound on non HTTPS sites and non localhost, console would say :
getUserMedia() no longer works on insecure origins. To <span class="keyword">use</span> <span class="keyword">this</span> feature, you should consider switching your application to a secure origin, such <span class="keyword">as</span> HTTPS. See https:<span class="comment">//goo.gl/rStTGz for more details.
</span>
and the Fr.voice would alert the message :
No Live Audio Input
So to use Fr.voice, your site must have an SSL certificate. But it will work in localhost even if it doesn’t have an HTTPS connection.
Features
- Record, Play, Download recorded audio
- Ability to Pause Recording & Resume (Thanks Gaurav)
- Record for a specific time
- Convert WAV audio to MP3 which will help reduce the size of recorded audio
- Upload recorded audio to server as BLOB or base64 etc.
Introduction
We will only use HTML, JS & CSS. There aren’t any server side stuff in this one. We only have one sub folder for the JS files called “cdn”.
As I said before, I have created a jQuery plugin for recording the voice and managing it. It needs RecorderJS for working. You can get it from here. Note that the plugin also needs “recorderWorker.js” which is in the same GitHub repository.
You can get my Voice plugin from here and name it as “jquery.voice.js” in the “cdn” directory.
Main Page
We have only one page to display to the user :
<!DOCTYPE html>
<html>
<head>
<script src="//lab.subinsb.com/projects/jquery/core/jquery-2.1.1.js"></script>
<script src="//lab.subinsb.com/projects/jquery/voice/recorder.js"></script>
<script src="//lab.subinsb.com/projects/jquery/voice/jquery.voice.min.js"></script>
<script src="cdn/record.js"></script>
</head>
<body>
<div id="content">
<h2>Record, Play & Download Microphone Voice</h2>
<audio controls src="" id="audio"></audio>
<div style="margin:10px;">
<a class="button" id="record">Record</a>
<a class="button disabled one" id="stop">Reset</a>
<a class="button disabled one" id="play">Play</a>
<a class="button disabled one" id="download">Download</a>
<a class="button disabled one" id="base64">Base64 URL</a>
<a class="button disabled one" id="mp3">MP3 URL</a>
</div>
<input class="button" type="checkbox" id="live"/>
<label for="live">Live Output</label>
<style>
.button{
display: inline-block;
vertical-align: middle;
margin: 0px 5px;
padding: 5px 12px;
cursor: pointer;
outline: none;
font-size: 13px;
text-decoration: none !important;
text-align: center;
color:#fff;
background-color: #4D90FE;
background-image: linear-gradient(top,#4D90FE, #4787ED);
background-image: -ms-linear-gradient(top,#4D90FE, #4787ED);
background-image: -o-linear-gradient(top,#4D90FE, #4787ED);
background-image: linear-gradient(top,#4D90FE, #4787ED);
border: 1px solid #4787ED;
box-shadow: 0 1px 3px #BFBFBF;
}
a.button{
color: #fff;
}
.button:hover{
box-shadow: inset 0px 1px 1px #8C8C8C;
}
.button.disabled{
box-shadow:none;
opacity:0.7;
}
</style>
</div>
</body>
</html>
As you can see, we only use CSS for styling the button and nothing else. We also don’t include the “recorderWorker.js”, because it’s loaded by the “recorder.js” which we have included in the page.
record.js
This file adds event listeners to the buttons to control recording and initiates the functions :
function restore(){
$("#record, #live").removeClass("disabled");
$(".one").addClass("disabled");
Fr.voice.stop();
}
$(document).ready(function(){
$(document).on("click", "#record:not(.disabled)", function(){
elem = $(this);
Fr.voice.record($("#live").is(":checked"), function(){
elem.addClass("disabled");
$("#live").addClass("disabled");
$(".one").removeClass("disabled");
});
});
$(document).on("click", "#stop:not(.disabled)", function(){
restore();
});
$(document).on("click", "#play:not(.disabled)", function(){
Fr.voice.export(function(url){
$("#audio").attr("src", url);
$("#audio")[0].play();
}, "URL");
restore();
});
$(document).on("click", "#download:not(.disabled)", function(){
Fr.voice.export(function(url){
$("<a href='"+url+"' download='MyRecording.wav'></a>")[0].click();
}, "URL");
restore();
});
$(document).on("click", "#base64:not(.disabled)", function(){
Fr.voice.export(function(url){
console.log("Here is the base64 URL : " + url);
alert("Check the web console for the URL");
$("<a href='"+ url +"' target='_blank'></a>")[0].click();
}, "base64");
restore();
});
$(document).on("click", "#mp3:not(.disabled)", function(){
alert("The conversion to MP3 will take some time (even 10 minutes), so please wait....");
Fr.voice.export(function(url){
console.log("Here is the MP3 URL : " + url);
alert("Check the web console for the URL");
$("<a href='"+ url +"' target='_blank'></a>")[0].click();
}, "mp3");
restore();
});
});
The restore() function reverts the action buttons to it’s original state ( Record – enabled, other buttons – disabled).
Configure
The location of the “recorderWorker.js” should be mentioned in the Fr.workerPath item of “jquery.voice.js” file. This is really necessary for the working. By default, it’s value is :
cdn/recorderWorker.js
It’s a relative path from the file it’s ran. If the file location is http://mySite.com/project/index.html and “recorderWorker.js” is in http://mySite.com/project/cdn/recorderWorker.js , then the value should be the above “cdn/recorderWorker.js”.
From version 0.3 onwards, if you’re going to use MP3 conversion, you have to configure the value of Fr.mp3WorkerPath item. The default value is :
cdn/mp3Worker.js
Also, for MP3 you have to define the path of libmp3lame.min.js in the first line of mp3Worker.js.
Recording
We start recording by calling the function Fr**.voice.record();** and the user will be asked for authorizing the use of microphone. if the user accepts it, recording will start and if user declined, an alert box will be opened.
Fr.voice.record(false, callback());
There is also a feature to live output the recording voice to the speaker. You can do this by making the first parameter of Fr.voice.record() to boolean true.
The recording is stopped using :
Fr.voice.stop();
It doesn’t accept any parameters and the return value will be Fr.voice object itself.
Pausing & Resuming
You can pause the audio recording and resume later with Fr.voice. To pause recording, just do this :
Fr.voice.pause();
and sometime later, you can resume the recording by :
Fr.voice.resume();
This feature was added in version 0.4
Record For A Specific Time
You can record audio for a specific time :
Fr.voice.record(...);
Fr.voice.stopRecordingAfter(5000, function(){
alert("Time limit reached.");
});
Do the recording normally and call Fr.voice.stopRecordingAfter() with the limiting time. The time should be in milliseconds. The above example uses “5000” = 5 seconds.
Export
Fr.voice.export can be used for obtaining the recorded audio URL and the blob data of the recorder audio. Example of obtaining blob data :
Fr.voice.export(function(blob){
console.log(blob); // The blob data
}, "blob");
It is not necessarily required to make the 2nd parameter value to “blob”, because by default it’s set as “blob”.
You can also get the blob URL of the recorded audio :
Fr.voice.export(function(url){
console.log(url); // The blob URL
}, "URL");
An example of making an audio element to play the audio file :
Fr.voice.export(function(url){
$("<audio src='"+ url +"'></audio>").appendTo("body");
$("body audio:last")[0].play();
}, "URL");
Base64
(This feature has been suggested by the user payam in the comments)
You can obtain the base64 data of the audio with $.voice.export and this data can be used to store the audio in the database.
Fr.voice.export(function(base64){
$.post("server.php", {"audio" : base64}, function(){
// Sent To Server
});
}, "base64");
All you got to do in the server is to store the sent data in the database.
Send BLOB Directly
You can also upload the BLOB file using HTML5‘s FormData function. Example :
Fr.voice.export(function(blob){
var data = new FormData();
data.append('file', blob);
$.ajax({
url: "server.php",
type: 'POST',
data: data,
contentType: false,
processData: false,
success: function(data) {
// Sent to Server
}
});
}, "blob");
Export MP3
You can export MP3 (from version 0.3) as blob, base64 or blob URL just like exporting WAV :
Fr.voice.exportMP3(function(blob){
console.log(blob);
});
Fr.voice.exportMP3(function(base64){
console.log(base64);
}, "base64");
Fr.voice.exportMP3(function(url){
console.log(url); // The blob URL
}, "URL");
Server
Save In Database
Here is an example of the server side script using PDO :
<?php
if(isset($_POST['audio'])){
$audio = $_POST['audio'];
$sql = $PDO->prepare("INSERT INTO `myTable` VALUES (?)");
$sql->execute(array($audio));
}
Now, the database table will have a value like this :
data:audio/wav;base64,UklGRiQAAgBXQVZFZm10IBAAAAABAAIARKwAABCxAgAEABAAZGF0YQAAAgAAAAAAAAAAAAAAA.......
This will be a long long value. So, make sure the database column’s type is set to “LONGTEXT” in MySQL.
When you want to play it, make an audio element with `src` attribute with this base64 value obtained from database. Example :
<?php
$sql = $PDO->query("SELECT `audio` FROM `myTable` WHERE `id` = 'whatever'");
$base64 = $sql->fetchColumn();
echo "<audio src='". $base64 ."'></audio>";
Saving as Files
This is the same as the previous one. Send the base64 data to the server and in the server, do as follows :
<?php
if(isset($_POST['audio'])){
$audio = $_POST['audio'];
$audio = str_replace('data:audio/wav;base64,', '', $audio);
$decoded = base64_decode($audio);
$file_location = "./save_folder/recorded_audio.wav";
file_put_contents($file_location, $decoded);
}
And when you need to retrieve audio, just link the src value of the audio object to the file location in server.
Saving As BLOB In Database
Another method to save is storing the original audio data as blob in MySQL database. For this create a uploads table :
CREATE TABLE IF NOT EXISTS `uploads` (
`id` int(11) NOT NULL,
`audio` longblob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Then, get the blob from browser and save it. Example :
<?php
if(isset($_FILES['file'])){
$audio = file_get_contents($_FILES['file']['tmp_name']);
require_once __DIR__ . "/db.php";
$sql = $dbh->prepare("INSERT INTO `uploads` (`audio`) VALUES(?)");
$sql->execute(array($audio));
$sql = $dbh->query("SELECT `id` FROM `uploads` ORDER BY `id` DESC LIMIT 1");
$id = $sql->fetchColumn();
echo "play.php?id=$id";
}
The “db.php” contains variable $PDO that contains the PDO object to access database. Then when you need to get it back, use this as “play.php” file :
<?php
if(isset($_GET['id'])){
require_once __DIR__ . "/db.php";
$sql = $dbh->prepare("SELECT `audio`, LENGTH(`audio`) FROM `uploads` WHERE `id` = ?");
$sql->execute(array($_GET['id']));
$result = $sql->fetch();
$audio = $result[0];
$size = $result[1];
header("Content-Length: $size");
header("Content-Type: audio/wav");
echo $audio;
}
If you used the HTML5‘s FormData() method, then use $_FILES[‘file’] variable to access the file. You can see this example above.
That’s it. The Voice plugin contains only 90 lines of code, because most of the work is done by “recorder.js”. The Voice plugin is just for making the complicated “recorder.js” file to a simple one.