I'm trying to record audio from a website user and save the audio to my server. Many of the posts I have studied so far have referenced Matt Diamond's recorderjs. I attempted to recreate the demo at .html by opening the source code through my browser. I copied the html, "audiodisplay.js", "recorder.js", and "main.js" and put them on my server. I also added the "recorderWorker.js" file from his GitHub site. In the recorder.js file, I changed var WORKER_PATH = 'js/recorderjs/recorderWorker.js' to var WORKER_PATH = 'recorderWorker.js';
When I run the demo I set up, I'm getting the "would you like to share your microphone.." warning and I can start the recording by pressing the mic icon on the right side. However, when I stop recording, the audio waveform doesn't show up below like in Matt's demo and the save icon doesn't become activated.
If I can get the demo up and running, the next problem I have is saving the wav file to the server instead of locally like in the demo. I've found several posts saying to use XMLHttpRequest(), however I can't really figure out how to connect those examples to recorderjs. Saving WAV File Recorded in Chrome to Server HTML5 & getUserMedia - Record Audio & Save to Web Server after Certain Time RecorderJS uploading recorded blob via AJAX
I'm trying to record audio from a website user and save the audio to my server. Many of the posts I have studied so far have referenced Matt Diamond's recorderjs. I attempted to recreate the demo at http://webaudiodemos.appspot.com/AudioRecorder/index.html by opening the source code through my browser. I copied the html, "audiodisplay.js", "recorder.js", and "main.js" and put them on my server. I also added the "recorderWorker.js" file from his GitHub site. In the recorder.js file, I changed var WORKER_PATH = 'js/recorderjs/recorderWorker.js' to var WORKER_PATH = 'recorderWorker.js';
When I run the demo I set up, I'm getting the "would you like to share your microphone.." warning and I can start the recording by pressing the mic icon on the right side. However, when I stop recording, the audio waveform doesn't show up below like in Matt's demo and the save icon doesn't become activated.
If I can get the demo up and running, the next problem I have is saving the wav file to the server instead of locally like in the demo. I've found several posts saying to use XMLHttpRequest(), however I can't really figure out how to connect those examples to recorderjs. Saving WAV File Recorded in Chrome to Server HTML5 & getUserMedia - Record Audio & Save to Web Server after Certain Time RecorderJS uploading recorded blob via AJAX
Share Improve this question edited May 23, 2017 at 10:29 CommunityBot 11 silver badge asked Dec 1, 2014 at 2:31 user3080392user3080392 1,2245 gold badges19 silver badges37 bronze badges3 Answers
Reset to default 11Using XMLHttpRequest
to post wav or mp3 blobs to server is simple.
Just run this code wherever you have access to the blob
element:
var xhr=new XMLHttpRequest();
xhr.onload=function(e) {
if(this.readyState === 4) {
console.log("Server returned: ",e.target.responseText);
}
};
var fd=new FormData();
fd.append("audio_data",blob, "filename");
xhr.open("POST","upload.php",true);
xhr.send(fd);
I prefer XMLHttpRequest
to $.ajax()
because it does not require jQuery.
Server-side, upload.php
is as simple as:
$input = $_FILES['audio_data']['tmp_name']; //temporary name that PHP gave to the uploaded file
$output = $_FILES['audio_data']['name'].".wav"; //letting the client control the filename is a rather bad idea
//move the file from temp name to local folder using $output name
move_uploaded_file($input, $output)
Source: https://blog.addpipe.com/using-recorder-js-to-capture-wav-audio-in-your-html5-web-site/ Live demo: https://addpipe.com/simple-recorderjs-demo/
I figured out one solution, but would still welcome others related to recorderjs. I used MP3RecorderJS at https://github.com/icatcher-at/MP3RecorderJS. The demo html works if you change the top of the html from src="js/jquery.min.js" and src="js/mp3recorder.js" to wherever they're located in your server. For me, it is src="jquery.min.js" and src="mp3recorder.js" I also had to do the same thing to the "mp3recorder.js" file: var RECORDER_WORKER_PATH = 'js/recorderWorker.js'; var ENCODER_WORKER_PATH = 'js/mp3Worker.js'; changed to var RECORDER_WORKER_PATH = 'recorderWorker.js'; var ENCODER_WORKER_PATH = 'mp3Worker.js';
The program is set up to record both mp3 and wav. I wanted wav, so I made a few more adjustments to the html file. At line 55 you'll find:
recorderObject.exportMP3(function(base64_mp3_data) {
var url = 'data:audio/mp3;base64,' + base64_mp3_data;
var au = document.createElement('audio');
I changed that to:
recorderObject.exportWAV(function(base64_wav_data) {
var url = 'data:audio/wav;base64,' + base64_wav_data;
var au = document.createElement('audio');
The demo appends a new player each time you record. To prevent this, I deleted (commented out) the $recorder.append(au); part, made a new div to store the audio player, and then I clear that div each time, before the audio player is created. To upload to my server, I used a technique I learned from uploading images to a server save canvas image to server Basically, the "url" variable in line 56 was what I needed, but couldn't figure out how to put it in a universal variable to use by another function. So, I made a hidden div and made the contents of it equal to "url". I then referenced that div in a new function called "upload". I then used a php file called "uploadWav.php". I still have to figure out a way to activate and deactivate the upload button to prevent the user from uploading a blank file before recording, but that's another issue. Here's the final html and php that worked for me:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>MP3 Recorder test</title>
</head>
<body id="index" onload="">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="mp3recorder.js"></script>
<script type="text/javascript">
var audio_context;
function __log(e, data) {
log.innerHTML += "\n" + e + " " + (data || '');
}
$(function() {
try {
// webkit shim
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
window.URL = window.URL || window.webkitURL;
var audio_context = new AudioContext;
__log('Audio context set up.');
__log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
} catch (e) {
alert('No web audio support in this browser!');
}
$('.recorder .start').on('click', function() {
$this = $(this);
$recorder = $this.parent();
navigator.getUserMedia({audio: true}, function(stream) {
var recorderObject = new MP3Recorder(audio_context, stream, { statusContainer: $recorder.find('.status'), statusMethod: 'replace' });
$recorder.data('recorderObject', recorderObject);
recorderObject.start();
}, function(e) { });
});
$('.recorder .stop').on('click', function() {
$this = $(this);
$recorder = $this.parent();
recorderObject = $recorder.data('recorderObject');
recorderObject.stop();
recorderObject.exportWAV(function(base64_wav_data) {
var url = 'data:audio/wav;base64,' + base64_wav_data;
var au = document.createElement('audio');
document.getElementById("playerContainer").innerHTML = "";
//console.log(url)
var duc = document.getElementById("dataUrlcontainer");
duc.innerHTML = url;
au.controls = true;
au.src = url;
//$recorder.append(au);
$('#playerContainer').append(au);
recorderObject.logStatus('');
});
});
});
</script>
<script>
function upload(){
var dataURL = document.getElementById("dataUrlcontainer").innerHTML;
$.ajax({
type: "POST",
url: "uploadWav.php",
data: {
wavBase64: dataURL
}
}).done(function(o) {
console.log('saved');
});
}
</script>
<div class="recorder">
Recorder 1
<input type="button" class="start" value="Record" />
<input type="button" class="stop" value="Stop" />
<pre class="status"></pre>
</div>
<div><button onclick="upload()">Upload</button></div>
<div id="playerContainer"></div>
<div id="dataUrlcontainer" hidden></div>
<pre id="log"></pre>
</body>
</html>
and the "uploadWav.php" file:
<?php
// requires php5
define('UPLOAD_DIR', 'uploads/');
$img = $_POST['wavBase64'];
$img = str_replace('data:audio/wav;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
$file = UPLOAD_DIR . uniqid() . '.wav';
$success = file_put_contents($file, $data);
print $success ? $file : 'Unable to save the file.';
?>
//**Server Side Code**
package myPack;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
@WebServlet("/MyServlet")
@MultipartConfig
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public MyServlet() {
super();
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
try {
String name = request.getParameter("fname");
String url = request.getParameter("myUrl");
url = url.replace("data:audio/wav;base64,", "");
url = url.replace(" ", "+");
byte[] bytes = url.getBytes();
byte[] valueDecoded = Base64.decodeBase64(bytes);
FileOutputStream os = new FileOutputStream(new File("D://" + name
+ ".wav"));
os.write(valueDecoded);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
**Client Side Code**
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>MP3 Recorder test</title>
</head>
<body id="index" onload="">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/recorder.js"></script>
<script type="text/javascript">
var audio_context;
function __log(e, data) {
log.innerHTML += "\n" + e + " " + (data || '');
}
$(function() {
try {
// webkit shim
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
window.URL = window.URL || window.webkitURL;
var audio_context = new AudioContext;
__log('Audio context set up.');
__log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
} catch (e) {
alert('No web audio support in this browser!');
}
$('.recorder .start').on('click', function() {
$this = $(this);
$recorder = $this.parent();
navigator.getUserMedia({audio: true}, function(stream) {
var recorderObject = new MP3Recorder(audio_context, stream, { statusContainer: $recorder.find('.status'), statusMethod: 'replace' });
$recorder.data('recorderObject', recorderObject);
recorderObject.start();
}, function(e) { });
});
$('.recorder .stop').on('click', function() {
$this = $(this);
$recorder = $this.parent();
recorderObject = $recorder.data('recorderObject');
recorderObject.stop();
recorderObject.exportWAV(function(base64_wav_data) {
var url = 'data:audio/wav;base64,' + base64_wav_data;
var au = document.createElement('audio');
document.getElementById("playerContainer").innerHTML = "";
//console.log(url)
var duc = document.getElementById("dataUrlcontainer");
duc.innerHTML = url;
au.controls = true;
au.src = url;
//$recorder.append(au);
$('#playerContainer').append(au);
var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('myUrl', duc.innerHTML);
$.ajax({
type: "POST",
url: "/audioPart2/MyServlet",
data: fd,
processData: false,
contentType: false
});
recorderObject.logStatus('');
});
});
});
</script>
<div class="recorder">
Recorder 1 <input type="button" class="start" value="Record" /> <input
type="button" class="stop" value="Stop" />
<div id="playerContainer"></div>
<div id="dataUrlcontainer" hidden></div>
<pre class="status"></pre>
</div>
<!-- <div class="recorder"> -->
<!-- Recorder 2 <input type="button" class="start" value="Record" /> <input -->
<!-- type="button" class="stop" value="Stop" /> -->
<!-- <pre class="status"></pre> -->
<!-- </div> -->
<pre id="log"></pre>
</body>
</html>
**// Required JS
1)jquery.min.js
2) recorder.js**
**recorder.js is below**
(function(window){
var RECORDER_WORKER_PATH = 'js/recorderWorker.js';
var ENCODER_WORKER_PATH = 'js/mp3Worker.js';
var MP3Recorder = function(context, stream, cfg) {
var config = cfg || { statusContainer: null, statusMethod: 'append' }
var bufferLen = 4096;
var recording = false;
this.source = context.createMediaStreamSource(stream);
this.node = (context.createScriptProcessor || context.createJavaScriptNode).call(context, bufferLen, 1, 1);
var recorderWorker = new Worker(RECORDER_WORKER_PATH);
var encoderWorker = new Worker(ENCODER_WORKER_PATH);
var exportCallback;
// initialize the Recorder Worker
recorderWorker.postMessage({ cmd: 'init', sampleRate: context.sampleRate });
// the recording loop
this.node.onaudioprocess = function(e) {
if(!recording) return;
recorderWorker.postMessage({ cmd: 'record', buffer: e.inputBuffer.getChannelData(0) });
}
this.start = function() {
recording = true;
this.logStatus('recording...');
}
this.stop = function() {
recording = false;
this.logStatus('stopping...');
}
this.destroy = function() { recorderWorker.postMessage({ cmd: 'destroy' }); }
this.logStatus = function(status) {
if(config.statusContainer) {
if(config.statusMethod == 'append') {
config.statusContainer.text(config.statusContainer.text + "\n" + status);
} else {
config.statusContainer.text(status);
}
}
}
this.exportBlob = function(cb) {
exportCallback = cb;
if (!exportCallback) throw new Error('Callback not set');
recorderWorker.postMessage({ cmd: 'exportBlob' });
}
this.exportWAV = function(cb) {
// export the blob from the worker
this.exportBlob(function(blob) {
var fileReader = new FileReader();
// read the blob as array buffer and convert it
// to a base64 encoded WAV buffer
fileReader.addEventListener("loadend", function() {
var resultBuffer = new Uint8Array(this.result);
cb(encode64(resultBuffer));
});
fileReader.readAsArrayBuffer(blob);
});
}
this.exportMP3 = function(cb) {
this.logStatus('converting...');
// export the blob from the worker
this.exportBlob(function(blob) {
var fileReader = new FileReader();
fileReader.addEventListener("loadend", function() {
var wavBuffer = new Uint8Array(this.result);
var wavData = parseWav(wavBuffer);
encoderWorker.addEventListener('message', function(e) {
if (e.data.cmd == 'data') {
cb(encode64(e.data.buffer));
}
});
encoderWorker.postMessage({ cmd: 'init', config: { mode: 3, channels: 1, samplerate: wavData.sampleRate, bitrate: wavData.bitsPerSample } });
encoderWorker.postMessage({ cmd: 'encode', buf: Uint8ArrayToFloat32Array(wavData.samples) });
encoderWorker.postMessage({ cmd: 'finish' });
});
fileReader.readAsArrayBuffer(blob);
});
}
// event listener for return values of the recorderWorker
recorderWorker.addEventListener('message', function(e) {
switch(e.data.from) {
case 'exportBlob':
exportCallback(e.data.blob);
break;
};
});
// HELPER FUNCTIONS
function encode64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for(var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
function parseWav(wav) {
function readInt(i, bytes) {
var ret = 0, shft = 0;
while(bytes) {
ret += wav[i] << shft; shft += 8;
i++; bytes--;
}
return ret;
}
if(readInt(20, 2) != 1) throw 'Invalid compression code, not PCM';
if(readInt(22, 2) != 1) throw 'Invalid number of channels, not 1';
return { sampleRate: readInt(24, 4), bitsPerSample: readInt(34, 2), samples: wav.subarray(44) };
}
function Uint8ArrayToFloat32Array(u8a){
var f32Buffer = new Float32Array(u8a.length);
for (var i = 0; i < u8a.length; i++) {
var value = u8a[i<<1] + (u8a[(i<<1)+1]<<8);
if (value >= 0x8000) value |= ~0x7FFF;
f32Buffer[i] = value / 0x8000;
}
return f32Buffer;
}
this.source.connect(this.node);
this.node.connect(context.destination); // this should not be necessary
}
window.MP3Recorder = MP3Recorder;
})(window);