如何从节点红色仪表板模板发送 msg.payload Int16Array

How to send msg.payload Int16Array from node-red dashboard template

我正在尝试从节点红色仪表板模板发送 Int16Array。在我的模板中:

var i16Buff = new Int16Array(i16BuffSize);

//... fill with data

scope.send({payload: i16Buff});

缓冲区来自 msg.payload,我可以看到 console.log 中的数据是一个 JSON 数组。我如何从节点红色仪表板模板发送它,使其保持 Int16Array.

因为弄清楚这一点太痛苦了,我想我只是把我的整个代码作为示例放在一起供其他人使用:

<!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>

此模板 returns (msg.payload) 来自麦克风的原始单声道音频数据缓冲区和 (msg.sampleRate) 通过 msg 到行中下一个节点的采样率。

注意:您必须使用 HTTPS 才能正常工作(根据我的 read/experienced)。