Content posted here with the permission of the author, who is currently employed at Josh Software. Original post available here.
In this article, Let’s see how audio recording is possible without any shim or plugin, but only by using the Recorder.js library in the browser and how to upload recorded file on s3 (Or any other cloud storage). Before going to do audio recording first we will see which technology we need and why.
There are resources available for handing these individually but not together. I used references from below links. It is advisable to read below links before proceeding.
Technology Used:
How does the recording process works?
To record audio in the browser, you will need obviously access to the microphone with the getUserMedia API and the AudioContext API, so be sure that your browser supports these APIs
- Web Audio API
The Web Audio API is a JavaScript API for processing and synthesizing audio in web applications.The API supports loading audio file data in multiple formats, such as WAV, MP3, AAC, OGG and others. Browser’s support for different audio format varies(Check Here).
- AudioContext API
The AudioContext is used for creating, processing and decoding audio. You need to create an instance of AudioContext class before you can do anything because all are processed / decoded, controlled for audio generation by this API.
How AudioContext declare?:
window.AudioContext = window.AudioContext or window.webkitAudioContext
- getUserMedia API
Before going to do recording we should know whether our web application can access user’s camera or microphone. So getUserMedia is an API that gives a web page access to a user’s camera and microphone via JavaScript. getUserMedia() has been supported since Chrome 21, Opera 18, and Firefox 17.
Make sure your application is secured with HTTPS protocol to use “getUserMedia API”. It does not work on insecure origins. Starting with Chrome 47, getUserMedia() requests are only allowed from secure origins: HTTPS or localhost.
How to check getUserMedia
if navigator.getUserMedia # do something cool else # fallback code
- Recorderjs
Recorderjs is a JavaScript library that records audio input (Web Audio API AudioNode object) and encodes to audio file image (Blob object). It supports three encoding formats. By using Recorder.js, the recording process is very simple. All that you need to do is to request the microphone in browser, once the user allows the access to the microphone, a stream object is received in the success callback of the initialization.This stream needs to be handled with the createMediaStreamSource method from an instance of the AudioContext API.All the variables should be stored globally or at least accessible in the scope for the rest of functions.
To understand above explanation, added code here:
startUserMedia = (stream) -> input = window.audio_context.createMediaStreamSource(stream) console.log 'Media stream created.' window.recorder = new Recorder(input, 'audio/mp3') window.recorder.record() console.log 'Recorder initialised.' return
The Recorder.js class expects the handled stream as first argument namely the input. With this instance of Recorder.js you can trigger the record method that starts to recording the received Audio and it will run indefinitely until the stop method from the same instance of recorder is triggered.
Export Recorded Audio
Export the Audio Blob from the recorder audio using the exportWAV method of the recorder instance. The exportWAV method can export the audio in wav and mp3 format when the second parameter is specified with the correct mimetype (‘audio/wav’, ‘audio/mp3’). Don’t forget to use the clear method of the recorder to start with a new one later. With the generated blob you will be able to upload it as an audio file to the server, in the same browser or just to play it in the same document.
Added Code here:
createDownloadLink = -> recorder and recorder.exportWAV((blob) -> url = URL.createObjectURL(blob) li = document.createElement('li') au = document.createElement('audio') hf = document.createElement('a') au.controls = true au.src = url hf.href = url hf.download = 'audio.mp3' hf.innerHTML = hf.download li.appendChild au li.appendChild hf playRecord.appendChild li
How to record Audio with Recorder.js
To make the explanation as simple as possible, we’ll just write a haml document that implements all the previously described process. It consists of 2 buttons namely Start and Play that triggers the recording process with Recorder.js. When the user stops the recording, it will append an Audio Track to an UL list with a downloadable audio file with like “audio.mp3”:
HAML code:
Create 2 buttons as “Start Recording” and “Stop Recording”
!!! %html %head %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %title Live input record and playback :css ul { list-style: none; } #playRecord audio { display: block; margin-bottom: 10px; } %body %h1 Recorder.js export example %button#start-btn Start recording %button#stop-btn{:disabled => "disabled"} Stop recording %h2 Stored Recordings %ul#playRecord
Click on Start button will start recording and clicking on Stop button will stop recording and export it.
$('#start-btn').on 'click', -> if confirm "Once you start recording, you can't pause it." initAudioRecording() $('#stop-btn').on 'click', -> stopRecording(this) stopRecording = (button) -> if window.recorder != undefined window.recorder window.recorder.stop() createRecording() return createRecording = -> recorder and recorder.exportWAV((blob) data = new FormData(); data.append("audio", blob); console.log(data) $.ajax url: 'Your Controller path' type: 'POST' data: data contentType: false processData: false success: -> console.log "Successfully uploaded recording." ) return startUserMedia = (stream) -> input = window.audio_context.createMediaStreamSource(stream) # console.log 'Media stream created.' window.recorder = new Recorder(input, 'audio/mp3') window.recorder.record() # console.log 'Recorder initialised.' return initAudioRecording = -> try window.AudioContext = window.AudioContext or window.webkitAudioContext navigator.getUserMedia = navigator.getUserMedia or navigator.webkitGetUserMedia or navigator.mozGetUserMedia window.URL = window.URL or window.webkitURL window.audio_context = new AudioContext console.log 'navigator.getUserMedia ' + (if navigator.getUserMedia then 'available.' else 'not present!') catch e alert 'No web audio support in this browser!' navigator.getUserMedia { audio: true }, startUserMedia, (e) -> console.log 'No live audio input: ' + e return return
Browser Support
- Make sure you are using a recent version of browser.
- Make sure your application is secured with HTTPS protocol to use “Web Audio API”. It does not work on insecure origins. As of Chrome 47, the getUserMedia API cannot be called from insecure origins.
- There is no issue while implementing Web Audio API in development environment i.e. http://localhost:3000
- You can test your browser compatibility test.Here
How to upload recorded file on s3
Till now we have seen how to get audio blob object, but the major challenge is how to upload this audio blob on s3(or on cloud).
See below coffee script code:
createRecording = -> recorder and recorder.exportWAV((blob) -> data = new FormData(); data.append("audio", blob); $.ajax url: 'Your controller path to upload recording' type: 'POST' data: data contentType: false processData: false success: -> if confirm "Successfully uploaded recording." window.location.href = 'Redirect path after successful ajax call.'
To make it very simple to understand I will explain you above code:
I have created a method as “createRecording” which calls exportWAV() method from Recorder.js, so we get a blob object.
Our aim is to upload this blob object on s3, for that I have created FormData() object and append audio blob to FormData() object.
Send this FormData() object to server by ajax call with “POST” method.
NOTE:
As we may be sending large amount of data to server (in terms of MB), we should have proper nginx entry for client_max_body_size.We should change nginx configuration in file /etc/nginx/nginx.conf, to expected max size of our recorded file.
client_max_body_size 50M;
Let’s see server side implementation:
def your_controller_method @model_object.audio = params[:audio] @model_object.save render nothing: true end
Once file object reaches to your server upload same to S3 (or any other cloud storage) using regular file upload method.
For uploading file we have used carrierwave gem(click here)