LoginSignup
3
2

More than 3 years have passed since last update.

マイク音声を拾ってWebSocketで送信するだけ

Last updated at Posted at 2020-06-19

ResamplerはOfflineAudioContextを使いました。
WebSocket側は16000Hz/16Bitで受ける事を想定しています。

<!DOCTYPE html>
<html lang='ja'>

<head>
    <title>Recording to WebSocket</title>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
</head>

<body>
    <button class='start'>start</button>
    <button class='stop' disabled>stop</button>
    <div id='response'></div>

    <script>

        const WEBSOCKET_ENDPOINT = 'wss://echo.websocket.org'
        const WEBSOCKET_SAMPLERATE = 16000

        const bufferSize = 0 //256, 512, 1024, 2048, 4096, 8192, 16384
        const numberOfInputChannels = 1
        const numberOfOutputChannels = 1

        const startButton = document.querySelector(".start")
        const stopButton = document.querySelector(".stop")

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

        let _WebSocket
        var _MediaStream
        var _ScriptProcessorNode

        // ---------------------------------------------
        // Start Button
        // ---------------------------------------------
        startButton.onclick = () => {
            startButton.disabled = true
            stopButton.disabled = !startButton.disabled

            const _AudioContext = new (window.AudioContext || window.webkitAudioContext)()

            // ##############################################
            // #   WebSocket
            // ##############################################
            _WebSocket = new WebSocket(WEBSOCKET_ENDPOINT)
            _WebSocket.binaryType = 'arraybuffer'
            _WebSocket.onopen = (e) => {
                console.log('WebSocket connect')
                _WebSocket.onmessage = (e) => {
                    // var receive = JSON.parse(e.data)
                    // console.log(receive)
                    console.log(e.data)
                }
                _WebSocket.onclose = (e) => {
                    console.log('WebSocket disconnect')
                    stopButton.onclick()
                }
                _WebSocket.onerror = (e) => {
                    console.error("WebSocket error observed:", e)
                    stopButton.onclick()
                }
            }

            // ##############################################
            // #   Recording
            // ##############################################
            navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((constraints) => {
                console.log('start recording')

                _MediaStream = constraints

                let _MediaStreamAudioSourceNode = _AudioContext.createMediaStreamSource(_MediaStream)
                _ScriptProcessorNode = _AudioContext.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels)
                _ScriptProcessorNode.onaudioprocess = (_AudioProcessingEvent) => {
                    var RecordingData = _AudioProcessingEvent.inputBuffer// 44100-48000Hz/32Bit
                    resample(RecordingData, (resampled) => {// to 16000Hz
                        var resampledData = resampled.renderedBuffer.getChannelData(0)// 16000Hz/32Bit
                        floatTo16Bit(resampledData, (data) => {// to 16Bit
                            if (_WebSocket.readyState == WebSocket.OPEN)
                                _WebSocket.send(data)// send WebSocket 16000Hz/16Bit
                        })
                    })
                }
                _MediaStreamAudioSourceNode.connect(_ScriptProcessorNode)
                _ScriptProcessorNode.connect(_AudioContext.destination)

            }).catch((err) => {
                console.log(err);
            })

        }

        // ---------------------------------------------
        // Resampler(44100/48000Hz? -> 16000Hz)
        // ---------------------------------------------
        function resample(inputBuffer, callback) {
            var _OfflineAudioContext = new OfflineAudioContext(inputBuffer.numberOfChannels, inputBuffer.duration * inputBuffer.numberOfChannels * WEBSOCKET_SAMPLERATE, WEBSOCKET_SAMPLERATE);
            var _AudioBuffer = _OfflineAudioContext.createBuffer(inputBuffer.numberOfChannels, inputBuffer.length, inputBuffer.sampleRate);
            for (var channel = 0; channel < inputBuffer.numberOfChannels; channel++) {
                _AudioBuffer.copyToChannel(inputBuffer.getChannelData(channel), channel);
            }
            var _AudioBufferSourceNode = _OfflineAudioContext.createBufferSource();
            _AudioBufferSourceNode.buffer = _AudioBuffer;
            _AudioBufferSourceNode.connect(_OfflineAudioContext.destination);
            _AudioBufferSourceNode.start(0);
            _OfflineAudioContext.oncomplete = (_OfflineAudioCompletionEvent) => {
                callback(_OfflineAudioCompletionEvent)
            }
            _OfflineAudioContext.startRendering();
        }

        // ---------------------------------------------
        // Resampler(32bit -> 16bit)
        // ---------------------------------------------
        function floatTo16Bit(buffer, callback) {
            var _Float32Array = new Float32Array(buffer)
            var _Int16Array = new Int16Array(_Float32Array.length);
            for (var i = 0; i < _Float32Array.length; i++) {
                var s = Math.max(-1, Math.min(1, _Float32Array[i]));
                _Int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
            }
            callback(_Int16Array);
        }

        // ---------------------------------------------
        // Stop Button
        // ---------------------------------------------
        stopButton.onclick = () => {
            if (!startButton.disabled)
                return
            startButton.disabled = false
            stopButton.disabled = !startButton.disabled
            if (_ScriptProcessorNode != null) {
                _ScriptProcessorNode.disconnect()
                _ScriptProcessorNode.onaudioprocess = null
                _ScriptProcessorNode = null
            }
            _MediaStream.getTracks().forEach((track) => {
                track.stop()
                console.log('stop recording')
            })
            if (_WebSocket.readyState == WebSocket.OPEN)
                _WebSocket.close()
        }

    </script>
</body>

</html>
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2