js获取麦克风数据通过websocket发送到后台

实现效果:PC端通过麦克风录音,通过 WebSocket实时传递到后台

原始每包数据过于大,后台不能接收,需要分包处理,每包最大1024
原始采样率为48000;通过合并压缩为自己所需采样率,demo中最终采样率为16000

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta name="apple-mobile-web-capable" content="yes">
        <title>录音并传递给后台</title>
    </head>
    <body>
        <button id="intercomBegin">开始对讲</button>
        <button id="intercomEnd">关闭对讲</button>
    </body>
    <script type="text/javascript">
	
        var begin = document.getElementById('intercomBegin');
        var end = document.getElementById('intercomEnd');
		
        var ws = null; //实现WebSocket 
        var record = null; //多媒体对象,用来处理音频
 
        function init(rec) {
            record = rec;
        }
		
        //录音对象
        var Recorder = function(stream) {
            var sampleBits = 16; //输出采样数位 8, 16
            var sampleRate = 16000; //输出采样率
            var context = new AudioContext();
            var audioInput = context.createMediaStreamSource(stream);
            var recorder = context.createScriptProcessor(4096, 1, 1);
            var audioData = {
                size: 0, //录音文件长度
                buffer: [], //录音缓存
                inputSampleRate: 48000, //输入采样率,网页默认的采样率即为48000
                inputSampleBits: 16, //输入采样数位 8, 16
                outputSampleRate: sampleRate, //输出采样数位
                oututSampleBits: sampleBits, //输出采样率
                clear: function() {
                    this.buffer = [];
                    this.size = 0;
                },
                input: function(data) {
                    this.buffer.push(new Float32Array(data));
                    this.size += data.length;		
                },
                compress: function() { //合并压缩
                    //合并
                    var data = new Float32Array(this.size);
                    var offset = 0;
                    for (var i = 0; i < this.buffer.length; i++) {
                        data.set(this.buffer[i], offset);
                        offset += this.buffer[i].length;
                    }
                    //压缩
                    var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
                    var length = data.length / compression;
                    var result = new Float32Array(length);
                    var index = 0,
                    j = 0;
                    while (index < length) {
                        result[index] = data[j];
                        j += compression;
                        index++;
                    }
                    return result;
                },
                encodePCM: function() { //这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。
                    var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
                    var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
                    var bytes = this.compress();
                    var dataLength = bytes.length * (sampleBits / 8);	
                    var buffer = new ArrayBuffer(dataLength);
                    var data = new DataView(buffer);
                    var offset = 0;
                    for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                        data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                    }
					
                    return new Blob([data]);
                }
				
            };
 
            var sendData = function() { //对以获取的数据进行处理(分包)
                var reader = new FileReader();
                reader.onload = e => {
                    var outbuffer = e.target.result;
					//ws.send(outbuffer);
					//console.log(outbuffer);
                    var arr = new Int8Array(outbuffer);
                    if (arr.length > 0) {
						//var tmparr = new Int8Array(arr.length);
						//ws.send(tmparr);
                        var tmparr = new Int8Array(1024);
						console.log(tmparr);
                        var j = 0;
                        for (var i = 0; i < arr.byteLength; i++) {
                            tmparr[j++] = arr[i];
                            if (((i + 1) % 1024) == 0) {
                                ws.send(tmparr);
                                if (arr.byteLength - i - 1 >= 1024) {
                                    tmparr = new Int8Array(1024);
                                } else {
                                    tmparr = new Int8Array(arr.byteLength - i - 1);
                                }
                                j = 0;
                           }
                           if ((i + 1 == arr.byteLength) && ((i + 1) % 1024) != 0) {
                                ws.send(tmparr);
                            }
                        }
                    }
                };
                reader.readAsArrayBuffer(audioData.encodePCM());
                audioData.clear();//每次发送完成则清理掉旧数据
            };
			
            this.start = function() {
                audioInput.connect(recorder);
                recorder.connect(context.destination);
            }
 
            this.stop = function() {
                recorder.disconnect();
            }
 
            this.getBlob = function() {
                return audioData.encodePCM();
            }
 
            this.clear = function() {
                audioData.clear();
            }
			
            recorder.onaudioprocess = function(e) {
                var inputBuffer = e.inputBuffer.getChannelData(0);
                audioData.input(inputBuffer);
                sendData();
            }
        }
        
		
        /*
        * WebSocket
        */
        function useWebSocket() {
            ws = new WebSocket("ws://192.168.16.98:80/ws/asset");
            ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
            ws.onopen = function(event) {
                console.log('握手成功');
				timeInte=setInterval(function(){
					//if (ws.readyState == 1) { //ws进入连接状态,则每隔180毫秒发送一包数据
                    record.start();
					
					//}
				},180);  
				
				//clearInterval(timeInte);
            };
			
            ws.onmessage = function(msg) {
                console.info(msg)
            }
			
            ws.onerror = function(err) {
                console.info(err)
            }
        }
		
        /*
        * 开始对讲
        */
        begin.onclick = function() {
			navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;  
  
            if (!navigator.getUserMedia) {
                alert('浏览器不支持音频输入');
            } else {
                navigator.getUserMedia({
                audio: true
            },
            function(mediaStream) {
                init(new Recorder(mediaStream));
                console.log('开始对讲');
                useWebSocket();
            },
            function(error) {
                console.log(error);
                switch (error.message || error.name) {
                    case 'PERMISSION_DENIED':  
                    case 'PermissionDeniedError':  
                        console.info('用户拒绝提供信息。');  
                        break;  
                    case 'NOT_SUPPORTED_ERROR':  
                    case 'NotSupportedError':  
                        console.info('浏览器不支持硬件设备。');  
                        break;  
                    case 'MANDATORY_UNSATISFIED_ERROR':  
                    case 'MandatoryUnsatisfiedError':  
                        console.info('无法发现指定的硬件设备。');  
                        break;  
                        default:  
                        console.info('无法打开麦克风。异常信息:' + (error.code || error.name));  
                        break;  
                        }  
                    }
                )
            }
        }
 
        /*
        * 关闭对讲
        */
        end.onclick = function() {
            if (ws) {
                ws.close();
                record.stop();
                console.log('关闭对讲以及WebSocket');
            }
        }
    </script>
</html>

后台使用的是java,把音频流存储到pcm文件,在websocket关闭时候,将pcm转换成wav,可以播放。
websocket类只贴部分方法

FileOutputStream fos = null;
@OnOpen
	public void onOpen(Session session) {
	fos =  new FileOutputStream("D://2020717.pcm");
}

@OnMessage
	public void onMessage(byte[] messages, Session session) {		
			fos.write(messages);
	}
	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose(Session session) {
		ShellUtils.tranPcmToWavFile(new File("D://2020717.pcm"));
		}

ShellUtils.java

package com.emeet.emwohospeech.utils;
import java.io.*;

public class ShellUtils {
	public static File tranPcmToWavFile(File pcmFile) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(pcmFile);
			String wavfilepath = "D://2020717.wav";


			File file = new File(wavfilepath);
			if (!file.exists()) {
				file.createNewFile();
			}
			fos = new FileOutputStream(wavfilepath);


			int PCMSize = 0;
			byte[] buf = new byte[1024 * 4];
			int size = fis.read(buf);

			while (size != -1) {
				PCMSize += size;
				size = fis.read(buf);
			}
			fis.close();

			//填入参数,比特率等等。这里用的是16位单声道 8000 hz

			WaveHeader header = new WaveHeader();
			//长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
			header.fileLength = PCMSize + (44 - 8);
			header.FmtHdrLeth = 16;
			header.BitsPerSample = 16;
			header.Channels = 1;
			header.FormatTag = 0x0001;
			header.SamplesPerSec = 16000;//采样率和js里转换后的必须一致
			header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
			header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
			header.DataHdrLeth = PCMSize;

			byte[] h = header.getHeader();

			assert h.length == 44; //WAV标准,头部应该是44字节
			//write header
			fos.write(h, 0, h.length);
			//write data stream
			fis = new FileInputStream(pcmFile);
			size = fis.read(buf);
			while (size != -1) {
				fos.write(buf, 0, size);
				size = fis.read(buf);
			}
			fis.close();
			fos.close();
			return new File(wavfilepath);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
}

WaveHeader.java

package com.emeet.emwohospeech.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class WaveHeader {

    public final char fileID[] = {'R', 'I', 'F', 'F'};
    public int fileLength;
    public char wavTag[] = {'W', 'A', 'V', 'E'};;
    public char FmtHdrID[] = {'f', 'm', 't', ' '};
    public int FmtHdrLeth;
    public short FormatTag;
    public short Channels;
    public int SamplesPerSec;
    public int AvgBytesPerSec;
    public short BlockAlign;
    public short BitsPerSample;
    public char DataHdrID[] = {'d','a','t','a'};
    public int DataHdrLeth;

    public byte[] getHeader() throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        WriteChar(bos, fileID);
        WriteInt(bos, fileLength);
        WriteChar(bos, wavTag);
        WriteChar(bos, FmtHdrID);
        WriteInt(bos,FmtHdrLeth);
        WriteShort(bos,FormatTag);
        WriteShort(bos,Channels);
        WriteInt(bos,SamplesPerSec);
        WriteInt(bos,AvgBytesPerSec);
        WriteShort(bos,BlockAlign);
        WriteShort(bos,BitsPerSample);
        WriteChar(bos,DataHdrID);
        WriteInt(bos,DataHdrLeth);
        bos.flush();
        byte[] r = bos.toByteArray();
        bos.close();
        return r;
    }

    private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {
        byte[] mybyte = new byte[2];
        mybyte[1] =(byte)( (s << 16) >> 24 );
        mybyte[0] =(byte)( (s << 24) >> 24 );
        bos.write(mybyte);
    }


    private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {
        byte[] buf = new byte[4];
        buf[3] =(byte)( n >> 24 );
        buf[2] =(byte)( (n << 8) >> 24 );
        buf[1] =(byte)( (n << 16) >> 24 );
        buf[0] =(byte)( (n << 24) >> 24 );
        bos.write(buf);
    }

    private void WriteChar(ByteArrayOutputStream bos, char[] id) {
        for (int i=0; i<id.length; i++) {
            char c = id[i];
            bos.write(c);
        }
    }
}


参考:https://blog.csdn.net/qiao_1017/article/details/102609243

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值