I am trying to send an Int16Array
from a node-red dashboard template. In the template I have:
var i16Buff = new Int16Array(i16BuffSize);
//... fill with data
scope.send({payload: i16Buff});
The buffer comes through msg.payload
and I can see the data in console.log
as a JSON array. How do I send it from node-red dashboard template so it remains an Int16Array
.
Because this was so painful to figure out, I thought I'd just put my entire code up as an example for others:
<!DOCTYPE html>
<video style="height: 0px; width: 0px;"></video>
<md-button ng-click="startStopRec()">{{ label }}</md-button>
<script>
(function(scope) {
// Setup cross browser compatibility
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
var video = document.querySelector('video');
var startedRecording = false;
var endRecording = false;
var scriptNode;
var audioCtx;
// Check if supported by browser
if (navigator.getUserMedia) {
console.log('getUserMedia supported.');
navigator.getUserMedia (
{
audio: true,
video: false
},
// Success callback
function(stream) {
var buffSize = 2048;
var buff = [];
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
video.onloadedmetadata = function(e) {
video.muted = 'true';
};
audioCtx = new AudioContext();
var source = audioCtx.createMediaStreamSource(stream);
scriptNode = audioCtx.createScriptProcessor(buffSize, 1, 1);
scriptNode.onaudioprocess = function(APE) {
if(endRecording === true) {
// There is probably a better way to do this but it worked for me
scriptNode.disconnect();
startedRecording = false;
endRecording = false;
// This was key for creating appropriate buffer for msg.payload.
var rawBuffer = new ArrayBuffer(buff.length * buffSize * 2);
var rawView = new DataView(rawBuffer);
var index = 0;
for (var i = 0; i < buff.length; i++) {
// Convert multi array audio buffer to flat Int16
for (var j = 0; j < (buffSize); j++){
rawView.setInt16(index, buff[i][j] * 0x7FFF, true);
index += 2;
}
}
// Send msg
scope.send({payload: rawBuffer, sampleRate: audioCtx.sampleRate});
// Clear buffer for next time
buff = [];
} else {
// Collect audio buffers into array
console.log('Getting data');
buff.push(new Float32Array(APE.inputBuffer.getChannelData(0)));
}
}
source.connect(scriptNode);
},
// Error callback
function(err) {
console.log('The following gUM error occured: ' + err);
}
);
} else {
console.log('getUserMedia not supported on your browser!');
}
function writeUTFBytes(view, offset, string){
var lng = string.length;
for (var i = 0; i < lng; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
if(scope.label === undefined) {
scope.label = 'Record';
}
scope.startStopRec = function() {
if(scope.label === 'Record') {
scope.label = 'Send';
scriptNode.connect(audioCtx.destination);
video.play();
startedRecording = true;
} else {
scope.label = 'Record';
if(startedRecording === true) {
endRecording = true;
video.pause();
}
}
}
})(scope);
</script>
This template returns (msg.payload) a buffer of raw mono audio data from the microphone and (msg.sampleRate) the sample rate through msg to the next node in the line.
NOTE: You must use HTTPS for it to work (from what I've read/experienced).