Get 16 bit output audio using AudioContext - javascript

requirement :
Container WAV
Encoding PCM
Rate 16K
Sample Format 16 bit
Channels Mono
My output :
Container WAV
Encoding PCM
Rate 16K
Sample Format 32 bit float
Channels Mono
I need to get an audio output with a sample format of 16-bit PCM, currently the only output i get is 32-bit FLOAT
My code :
URL = window.URL || window.webkitURL;
var gumStream;
//stream from getUserMedia()
var rec;
//Recorder.js object
var input;
//MediaStreamAudioSourceNode we'll be recording
// shim for AudioContext when it's not avb.
//new audio context to help us record
var recordButton = document.getElementById("recordButton");
var stopButton = document.getElementById("stopButton");
var pauseButton = document.getElementById("pauseButton");
var recordButton_ = document.getElementById("recordButton_");
var stopButton_ = document.getElementById("stopButton_");
var pauseButton_ = document.getElementById("pauseButton_");
//add events to those 3 buttons
recordButton.addEventListener("click", startRecording);
stopButton.addEventListener("click", stopRecording);
pauseButton.addEventListener("click", pauseRecording);
function startRecording() {
var AudioContext = (window.AudioContext) || (window.webkitAudioContext)
var audioContext = new AudioContext({
sampleRate: 16000,
});
console.log("recordButton clicked");
/* Simple constraints object, for more advanced audio features see
https://addpipe.com/blog/audio-constraints-getusermedia/ */
var constraints = {
audio: true,
video: false
}
/* Disable the record button until we get a success or fail from getUserMedia() */
recordButton.disabled = true;
stopButton.disabled = false;
pauseButton.disabled = false
/* We're using the standard promise based getUserMedia()
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia */
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
/* assign to gumStream for later use */
gumStream = stream;
/* use the stream */
input = audioContext.createMediaStreamSource(stream);
/* Create the Recorder object and configure to record mono sound (1 channel) Recording 2 channels will double the file size */
rec = new Recorder(input, {
numChannels: 1
})
//start the recording process
rec.record()
console.log("Recording started");
}).catch(function(err) {
//enable the record button if getUserMedia() fails
recordButton.disabled = false;
stopButton.disabled = true;
pauseButton.disabled = true
});
}
function pauseRecording() {
console.log("pauseButton clicked rec.recording=", rec.recording);
if (rec.recording) {
//pause
rec.stop();
pauseButton.innerHTML = "Resume";
} else {
//resume
rec.record()
pauseButton.innerHTML = "Pause";
}
}
function stopRecording() {
console.log("stopButton clicked");
//disable the stop button, enable the record too allow for new recordings
stopButton.disabled = true;
recordButton.disabled = false;
pauseButton.disabled = true;
//reset button just in case the recording is stopped while paused
pauseButton.innerHTML = "Pause";
//tell the recorder to stop the recording
rec.stop(); //stop microphone access
gumStream.getAudioTracks()[0].stop();
//create the wav blob and pass it on to createDownloadLink
rec.exportWAV(createDownloadLink);
}
function createDownloadLink(blob) {
var url = URL.createObjectURL(blob);
var au = document.createElement('audio');
var li = document.createElement('li');
var link = document.createElement('a');
//add controls to the <audio> element
au.controls = true;
au.src = url;
au.sampleRate = 16000
//link the a element to the blob
link.href = url;
// link.download = new Date().toISOString() + '.wav';
link.innerHTML = link.download;
//add the new audio and a elements to the li element
li.appendChild(au);
li.appendChild(link);
//add the li element to the ordered list
recordingsList.appendChild(li);
var p = document.createElement("br");
recordingsList.appendChild(p);
}

There's nothing wrong on your code, by default the output provided by the Web Audio API is 32 bit, you will need to process it by using the BitCrusher node as described in the documentation:
https://webaudio.github.io/web-audio-api/#the-bitcrusher-node
Hope this helps.

Related

Recording Audio from Google Colab using javascript

The following function records audio from collab perfectly, the issue is that it only stops and saves the recording when manually selecting the stop recording button. I want the recording to end at a set time (example= 5 seconds), without me clicking anything after running the cell.
def get_audio2():
from IPython.display import HTML, Audio
from google.colab.output import eval_js
from base64 import b64decode
import numpy as np
from scipy.io.wavfile import read as wav_read
import io
import ffmpeg
# Modulo para grabar desde collab
AUDIO_HTML = """
<script>
var my_div = document.createElement("DIV");
var my_p = document.createElement("P");
var my_btn = document.createElement("BUTTON");
var t = document.createTextNode("Press to start recording");
my_btn.appendChild(t);
//my_p.appendChild(my_btn);
my_div.appendChild(my_btn);
document.body.appendChild(my_div);
var base64data = 0;
var reader;
var recorder, gumStream;
var recordButton = my_btn;
var handleSuccess = function(stream) {
gumStream = stream;
var options = {
//bitsPerSecond: 8000, //chrome seems to ignore, always 48k
mimeType : 'audio/webm;codecs=opus'
//mimeType : 'audio/webm;codecs=pcm'
};
//recorder = new MediaRecorder(stream, options);
recorder = new MediaRecorder(stream);
recorder.ondataavailable = function(e) {
var url = URL.createObjectURL(e.data);
var preview = document.createElement('audio');
preview.controls = true;
preview.src = url;
document.body.appendChild(preview);
reader = new FileReader();
reader.readAsDataURL(e.data);
reader.onloadend = function() {
base64data = reader.result;
//console.log("Inside FileReader:" + base64data);
}
};
recorder.start();
};
recordButton.innerText = "Recording... press to stop";
navigator.mediaDevices.getUserMedia({audio: true}).then(handleSuccess);
function toggleRecording() {
if (recorder && recorder.state == "recording") {
recorder.stop();
gumStream.getAudioTracks()[0].stop();
recordButton.innerText = "Saving the recording... pls wait!"
}
}
// https://stackoverflow.com/a/951057
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
var data = new Promise(resolve=>{
//recordButton.addEventListener("click", toggleRecording);
recordButton.onclick = ()=>{
toggleRecording()
sleep(2000).then(() => {
// wait 2000ms for the data to be available...
// ideally this should use something like await...
//console.log("Inside data:" + base64data)
resolve(base64data.toString())
});
}
});
</script>
"""
display(HTML(AUDIO_HTML))
data = eval_js("data")
binary = b64decode(data.split(',')[1])
process = (ffmpeg
.input('pipe:0')
.output('pipe:1', format='wav')
.run_async(pipe_stdin=True, pipe_stdout=True, pipe_stderr=True, quiet=True, overwrite_output=True)
)
output, err = process.communicate(input=binary)
riff_chunk_size = len(output) - 8
# Break up the chunk size into four bytes, held in b.
q = riff_chunk_size
b = []
for i in range(4):
q, r = divmod(q, 256)
b.append(r)
# Replace bytes 4:8 in proc.stdout with the actual size of the RIFF chunk.
riff = output[:4] + bytes(b) + output[8:]
sr, audio = wav_read(io.BytesIO(riff))
return audio, sr
I tried changing the recordButton.onclick to variations of sleep thinking it would run after a set time, but either it never stopped recording even if I clicked the stop recording button and the cell never ended or I kept getting this error:
MessageError: ReferenceError: data is not defined

JS base64 encode wav binary file

I need to encode to base64 a wav binary file that I get with the famous
https://github.com/mattdiamond/Recorderjs project:
function ($scope) {
var ctrl = this;
var gumStream;
//stream from getUserMedia()
var rec;
//Recorder.js object
var input;
var audioContext = new AudioContext();
//new audio context to help us record
var recordButton = document.getElementById("recordButton");
var stopButton = document.getElementById("stopButton");
$scope.properties.value = undefined;
var srConfig = {"config":
{
"sampleRateHertz":16000,
"languageCode": $scope.properties.languageCode
},
"audio":
{"content":""}
};
/* Disable the record button until we get a success or fail from getUserMedia() */
this.startRecording = function(){
console.log("recordButton clicked");
var constraints = { audio: true, video:false };
recordButton.disabled = true;
stopButton.disabled = false;
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
document.getElementById("formats").innerHTML="Format: 1 channel pcm # "+audioContext.sampleRate/1000+"kHz";
/* assign to gumStream for later use */
srConfig.config.sampleRateHertz = audioContext.sampleRate;
gumStream = stream;
/* use the stream */
input = audioContext.createMediaStreamSource(stream);
rec = new Recorder(input,{numChannels:1});
//start the recording process
rec.record();
console.log("Recording started");
setTimeout(function(){
ctrl.stopRecording();
}, parseInt($scope.properties.secondsToRegister)*1000);
}).catch(function(err) {
//enable the record button if getUserMedia() fails
console.log("Error in getUserMedia");
console.log(err);
recordButton.disabled = false;
stopButton.disabled = true;
});
};
this.stopRecording =function(){
console.log("stopButton clicked");
//disable the stop button, enable the record too allow for new recordings
stopButton.disabled = true;
recordButton.disabled = false;
//tell the recorder to stop the recording
rec.stop(); //stop microphone access
gumStream.getAudioTracks()[0].stop();
//create the wav blob and pass it on to createDownloadLink
var response = rec.exportWAV(this.callDetectorService);
};
this.callDetectorService = function(blob){
var xhr=new XMLHttpRequest();
if ("withCredentials" in xhr){
console.log("withCredentials supported");
}
else{
console.log("withCredentials NOT supported");
}
var response;
xhr.onload=function(e) {
if(this.readyState === 4) {
console.log("Server returned: ", e.target.responseText);
$scope.$apply(function () {
$scope.properties.value = JSON.parse(e.target.responseText);
});
response = e.target.responseText;
}
};
srConfig.audio.content=btoa(unescape(encodeURIComponent(blob)));
console.log(srConfig);
xhr.open("POST",$scope.properties.serviceUrl+"?key="+$scope.properties.secretkey,true);
var data = JSON.stringify(srConfig);
xhr.send(data);
};
}
When the request is submitted (line xhr.send(data)) I get then the following error:
Server returned: {
"error": {
"code": 400,
"message": "Invalid recognition 'config': bad encoding..",
"status": "INVALID_ARGUMENT"
}
}
This let me think that the mistake is in the line that makes the encoding:
srConfig.audio.content=btoa(unescape(encodeURIComponent(blob)));
Thank you

Make WAV file from raw data

I use this examples for capture data from device microphone, but I can't figure how to convert it to WAV file for send to my server.
<script>
var handleSuccess = function(stream) {
var context = new AudioContext();
var source = context.createMediaStreamSource(stream);
var processor = context.createScriptProcessor(1024, 1, 1);
source.connect(processor);
processor.connect(context.destination);
processor.onaudioprocess = function(e) {
// Convert this to WAV and send to server
console.log(e.inputBuffer);
};
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
</script>
Disclosure synth-js is written by me.
The following script will create a valid WAV file as a Blob, containing the first 5 seconds of audio:
<script src="https://unpkg.com/synth-js/dst/synth.min.js"></script>
<script>
var handleSuccess = function(stream) {
var context = new AudioContext();
var source = context.createMediaStreamSource(stream);
var processor = context.createScriptProcessor(1024, 1, 1);
var data = [];
source.connect(processor);
processor.connect(context.destination);
processor.onaudioprocess = function(e) {
data.push.apply(data, e.inputBuffer.getChannelData(0));
// cut off after 5 seconds
if (data.length >= context.sampleRate * 5) {
context.close();
var track = stream.getAudioTracks()[0];
track.stop();
// Convert this to WAV
var wav = new synth.WAV(1, context.sampleRate, 16, true, data);
var blob = wav.toBlob();
// do something with blob
var src = URL.createObjectURL(blob);
var audio = new Audio();
audio.controls = true;
document.body.appendChild(audio);
// play back audio
audio.addEventListener('canplaythrough', function() { audio.play(); });
audio.src = src;
}
};
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(handleSuccess);
</script>
You can try this on JSFiddle since Stack Snippets do not allow access to the microphone.
The line var wav = new synth.WAV(1, context.sampleRate, 16, true, data); creates a new WAV object with 1 channel, a sample rate that matches the input, 16 bits per sample in the WAV binary, in little endian format (required), with the PCM data collected by the onaudioprocess events.

how to detect headphone and cam in recordrtc

How to detect headphone and cam when calling start recording function in recordRTC liabrary.
btnStartRecording.onclick = function() {
btnStartRecording.disabled = true;
captureUserMedia(function(stream) {
mediaStream = stream;
videoElement.src = window.URL.createObjectURL(stream);
videoElement.play();
videoElement.muted = true;
videoElement.controls = false;
// it is second parameter of the RecordRTC
var audioConfig = {};
if (!isRecordOnlyAudio) {
audioConfig.onAudioProcessStarted = function() {
// invoke video recorder in this callback
// to get maximum sync
videoRecorder.startRecording();
};
}
audioRecorder = RecordRTC(stream, audioConfig);
if (!isRecordOnlyAudio) {
// it is second parameter of the RecordRTC
var videoConfig = {type: 'video'};
videoRecorder = RecordRTC(stream, videoConfig);
}
audioRecorder.startRecording();
// enable stop-recording button
btnStopRecording.disabled = false;
});
};
If any method in captureUserMedia that detects devices and return error message.

HTML5 record audio to file

What I ultimately want to do is record from the user's microphone, and upload the file to the server when they're done. So far, I've managed to make a stream to an element with the following code:
var audio = document.getElementById("audio_preview");
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getUserMedia({video: false, audio: true}, function(stream) {
audio.src = window.URL.createObjectURL(stream);
}, onRecordFail);
var onRecordFail = function (e) {
console.log(e);
}
How do I go from that, to recording to a file?
There is a fairly complete recording demo available at: http://webaudiodemos.appspot.com/AudioRecorder/index.html
It allows you to record audio in the browser, then gives you the option to export and download what you've recorded.
You can view the source of that page to find links to the javascript, but to summarize, there's a Recorder object that contains an exportWAV method, and a forceDownload method.
The code shown below is copyrighted to Matt Diamond and available for use under MIT license. The original files are here:
http://webaudiodemos.appspot.com/AudioRecorder/index.html
http://webaudiodemos.appspot.com/AudioRecorder/js/recorderjs/recorderWorker.js
Save this files and use
(function(window){
var WORKER_PATH = 'recorderWorker.js';
var Recorder = function(source, cfg){
var config = cfg || {};
var bufferLen = config.bufferLen || 4096;
this.context = source.context;
this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
var worker = new Worker(config.workerPath || WORKER_PATH);
worker.postMessage({
command: 'init',
config: {
sampleRate: this.context.sampleRate
}
});
var recording = false,
currCallback;
this.node.onaudioprocess = function(e){
if (!recording) return;
worker.postMessage({
command: 'record',
buffer: [
e.inputBuffer.getChannelData(0),
e.inputBuffer.getChannelData(1)
]
});
}
this.configure = function(cfg){
for (var prop in cfg){
if (cfg.hasOwnProperty(prop)){
config[prop] = cfg[prop];
}
}
}
this.record = function(){
recording = true;
}
this.stop = function(){
recording = false;
}
this.clear = function(){
worker.postMessage({ command: 'clear' });
}
this.getBuffer = function(cb) {
currCallback = cb || config.callback;
worker.postMessage({ command: 'getBuffer' })
}
this.exportWAV = function(cb, type){
currCallback = cb || config.callback;
type = type || config.type || 'audio/wav';
if (!currCallback) throw new Error('Callback not set');
worker.postMessage({
command: 'exportWAV',
type: type
});
}
worker.onmessage = function(e){
var blob = e.data;
currCallback(blob);
}
source.connect(this.node);
this.node.connect(this.context.destination); //this should not be necessary
};
Recorder.forceDownload = function(blob, filename){
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var link = window.document.createElement('a');
link.href = url;
link.download = filename || 'output.wav';
var click = document.createEvent("Event");
click.initEvent("click", true, true);
link.dispatchEvent(click);
}
window.Recorder = Recorder;
})(window);
//ADDITIONAL JS recorderWorker.js
var recLength = 0,
recBuffersL = [],
recBuffersR = [],
sampleRate;
this.onmessage = function(e){
switch(e.data.command){
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportWAV':
exportWAV(e.data.type);
break;
case 'getBuffer':
getBuffer();
break;
case 'clear':
clear();
break;
}
};
function init(config){
sampleRate = config.sampleRate;
}
function record(inputBuffer){
recBuffersL.push(inputBuffer[0]);
recBuffersR.push(inputBuffer[1]);
recLength += inputBuffer[0].length;
}
function exportWAV(type){
var bufferL = mergeBuffers(recBuffersL, recLength);
var bufferR = mergeBuffers(recBuffersR, recLength);
var interleaved = interleave(bufferL, bufferR);
var dataview = encodeWAV(interleaved);
var audioBlob = new Blob([dataview], { type: type });
this.postMessage(audioBlob);
}
function getBuffer() {
var buffers = [];
buffers.push( mergeBuffers(recBuffersL, recLength) );
buffers.push( mergeBuffers(recBuffersR, recLength) );
this.postMessage(buffers);
}
function clear(){
recLength = 0;
recBuffersL = [];
recBuffersR = [];
}
function mergeBuffers(recBuffers, recLength){
var result = new Float32Array(recLength);
var offset = 0;
for (var i = 0; i < recBuffers.length; i++){
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function interleave(inputL, inputR){
var length = inputL.length + inputR.length;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length){
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function floatTo16BitPCM(output, offset, input){
for (var i = 0; i < input.length; i++, offset+=2){
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function writeString(view, offset, string){
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function encodeWAV(samples){
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* file length */
view.setUint32(4, 32 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, 2, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 4, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, 4, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * 2, true);
floatTo16BitPCM(view, 44, samples);
return view;
}
<html>
<body>
<audio controls autoplay></audio>
<script type="text/javascript" src="recorder.js"> </script>
<fieldset><legend>RECORD AUDIO</legend>
<input onclick="startRecording()" type="button" value="start recording" />
<input onclick="stopRecording()" type="button" value="stop recording and play" />
</fieldset>
<script>
var onFail = function(e) {
console.log('Rejected!', e);
};
var onSuccess = function(s) {
var context = new webkitAudioContext();
var mediaStreamSource = context.createMediaStreamSource(s);
recorder = new Recorder(mediaStreamSource);
recorder.record();
// audio loopback
// mediaStreamSource.connect(context.destination);
}
window.URL = window.URL || window.webkitURL;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
var recorder;
var audio = document.querySelector('audio');
function startRecording() {
if (navigator.getUserMedia) {
navigator.getUserMedia({audio: true}, onSuccess, onFail);
} else {
console.log('navigator.getUserMedia not present');
}
}
function stopRecording() {
recorder.stop();
recorder.exportWAV(function(s) {
audio.src = window.URL.createObjectURL(s);
});
}
</script>
</body>
</html>
Update now Chrome also supports MediaRecorder API from v47. The same thing to do would be to use it( guessing native recording method is bound to be faster than work arounds), the API is really easy to use, and you would find tons of answers as to how to upload a blob for the server.
Demo - would work in Chrome and Firefox, intentionally left out pushing blob to server...
Code Source
Currently, there are three ways to do it:
as wav[ all code client-side, uncompressed recording], you can check out --> Recorderjs. Problem: file size is quite big, more upload bandwidth required.
as mp3[ all code client-side, compressed recording], you can check out --> mp3Recorder. Problem: personally, I find the quality bad, also there is this licensing issue.
as ogg [ client+ server(node.js) code, compressed recording, infinite hours of recording without browser crash ], you can check out --> recordOpus, either only client-side recording, or client-server bundling, the choice is yours.
ogg recording example( only firefox):
var mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start(); // to start recording.
...
mediaRecorder.stop(); // to stop recording.
mediaRecorder.ondataavailable = function(e) {
// do something with the data.
}
Fiddle Demo for ogg recording.
This is a simple JavaScript sound recorder and editor. You can try it.
https://www.danieldemmel.me/JSSoundRecorder/
Can download from here
https://github.com/daaain/JSSoundRecorder
The question is very old and many of the answers are not supported in the current version of the browser. I was trying to create the audio recorder using simple html, css, and js. I further went on to use the same code in electron to make a cross platform application.
<html>
<head>
<title>Recorder App</title>
</head>
<h2>Recorder App</h2>
<p>
<button type="button" id="record">Record</button>
<button type="button" id="stopRecord" disabled>Stop</button>
</p>
<p>
<audio id="recordedAudio"></audio>
</p>
<script>
navigator.mediaDevices.getUserMedia({audio:true})
.then(stream => {handlerFunction(stream)})
function handlerFunction(stream) {
rec = new MediaRecorder(stream);
rec.ondataavailable = e => {
audioChunks.push(e.data);
if (rec.state == "inactive"){
let blob = new Blob(audioChunks,{type:'audio/mp3'});
recordedAudio.src = URL.createObjectURL(blob);
recordedAudio.controls=true;
recordedAudio.autoplay=true;
sendData(blob)
}
}
}
function sendData(data) {}
record.onclick = e => {
record.disabled = true;
record.style.backgroundColor = "blue"
stopRecord.disabled=false;
audioChunks = [];
rec.start();
}
stopRecord.onclick = e => {
record.disabled = false;
stop.disabled=true;
record.style.backgroundColor = "red"
rec.stop();
}
</script>
</html>
The above code works in Windows 10, Mac, Linux, and obviously, on both google chrome and firefox.
You can use Recordmp3js from GitHub to achieve your requirements. You can record from user's microphone and then get the file as an mp3. Finally upload it to your server.
I used this in my demo. There is a already a sample available with the source code by the author in this location :
https://github.com/Audior/Recordmp3js
The demo is here:
http://audior.ec/recordmp3js/
But currently works only on Chrome and Firefox.
Seems to work fine and pretty simple. Hope this helps.
Here's a gitHub project that does just that.
It records audio from the browser in mp3 format, and it automatically saves it to the webserver.
https://github.com/Audior/Recordmp3js
You can also view a detailed explanation of the implementation:
http://audior.ec/blog/recording-mp3-using-only-html5-and-javascript-recordmp3-js/
Stream audio in realtime without waiting for recording to end:
https://github.com/noamtcohen/AudioStreamer
This streams PCM data but you could modify the code to stream mp3 or Speex

Categories

Resources