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>