html5使用websocket发送(PCM)音频数据到服务器,再转在wav文件 您所在的位置:网站首页 音频怎么做成文件形式发送 html5使用websocket发送(PCM)音频数据到服务器,再转在wav文件

html5使用websocket发送(PCM)音频数据到服务器,再转在wav文件

2024-03-24 15:38| 来源: 网络整理| 查看: 265

PCM格式

 

 

通过websocket发送音频数据。 

test 开始对讲 关闭对讲 var begin = document.getElementById('intercomBegin'); var end = document.getElementById('intercomEnd'); navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; var ws = null;//实现WebSocket var record=null;//多媒体对象,用来处理音频 var timeInte = null;//定义一个定时器 begin.onclick = function() { console.log('开始对讲') } end.onclick = function() { console.log('关闭对讲') if(ws) { ws.close(); record.stop(); clearInterval(timeInte); } } function init(rec){ record = rec; } if (!navigator.getUserMedia) { alert('浏览器不支持音频输入'); }else{ navigator.getUserMedia( { audio: true }, function (mediaStream) { init(new Recorder(mediaStream)); },function(error){ console.log(error) } ) } //录音对象 var Recorder = function(stream) { var sampleBits = 16;//输出采样数位 8, 16 var sampleRate = 8000;//输出采样率 var context = new AudioContext(); var audioInput = context.createMediaStreamSource(stream); var recorder = context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0 //录音文件长度 , buffer: [] //录音缓存 , inputSampleRate: sampleRate //输入采样率 , 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]); } }; 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) { audioData.input(e.inputBuffer.getChannelData(0)); } }; function receive(data) { if( typeof e == 'string' && JSON.parse(e).message=='OK'){ console.log('OK'); }else{ var buffer = (new Response(data)).arrayBuffer(); buffer.then(function(buf){ console.log("################recv start ####################################"); var audioContext = new ( window.AudioContext || window.webkitAudioContext )(); var fileResult =addWavHeader(buf, '8000', '16', '1');//解析数据转码wav audioContext.decodeAudioData(fileResult, function(buffer) { _visualize(audioContext,buffer);//播放 }); console.log("################recv end ####################################"); }); } } //处理音频流,转码wav var addWavHeader = function(samples,sampleRateTmp,sampleBits,channelCount){ var dataLength = samples.byteLength; var buffer = new ArrayBuffer(44 + dataLength); var view = new DataView(buffer); function writeString(view, offset, string){ for (var i = 0; i < string.length; i++){ view.setUint8(offset + i, string.charCodeAt(i)); } } var offset = 0; /* 资源交换文件标识符 */ writeString(view, offset, 'RIFF'); offset += 4; /* 下个地址开始到文件尾总字节数,即文件大小-8 */ view.setUint32(offset, /*32*/ 36 + dataLength, true); offset += 4; /* WAV文件标志 */ writeString(view, offset, 'WAVE'); offset += 4; /* 波形格式标志 */ writeString(view, offset, 'fmt '); offset += 4; /* 过滤字节,一般为 0x10 = 16 */ view.setUint32(offset, 16, true); offset += 4; /* 格式类别 (PCM形式采样数据) */ view.setUint16(offset, 1, true); offset += 2; /* 通道数 */ view.setUint16(offset, channelCount, true); offset += 2; /* 采样率,每秒样本数,表示每个通道的播放速度 */ view.setUint32(offset, sampleRateTmp, true); offset += 4; /* 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */ view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset +=4; /* 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */ view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2; /* 每样本数据位数 */ view.setUint16(offset, sampleBits, true); offset += 2; /* 数据标识符 */ writeString(view, offset, 'data'); offset += 4; /* 采样数据总数,即数据总大小-44 */ view.setUint32(offset, dataLength, true); offset += 4; function floatTo32BitPCM(output, offset, input){ input = new Int32Array(input); for (var i = 0; i < input.length; i++, offset+=4){ output.setInt32(offset,input[i],true); } } function floatTo16BitPCM(output, offset, input){ input = new Int16Array(input); for (var i = 0; i < input.length; i++, offset+=2){ output.setInt16(offset,input[i],true); } } function floatTo8BitPCM(output, offset, input){ input = new Int8Array(input); for (var i = 0; i < input.length; i++, offset++){ output.setInt8(offset,input[i],true); } } if(sampleBits == 16){ floatTo16BitPCM(view, 44, samples); }else if(sampleBits == 8){ floatTo8BitPCM(view, 44, samples); }else{ floatTo32BitPCM(view, 44, samples); } return view.buffer; } //播放音频 var _visualize = function(audioContext, buffer) { var audioBufferSouceNode = audioContext.createBufferSource(), analyser = audioContext.createAnalyser(), that = this; //将信号源连接到分析仪 audioBufferSouceNode.connect(analyser); //将分析仪连接到目的地(扬声器),否则我们将听不到声音 analyser.connect(audioContext.destination); //然后将缓冲区分配给缓冲区源节点 audioBufferSouceNode.buffer = buffer; //发挥作用 if (!audioBufferSouceNode.start) { audioBufferSouceNode.start = audioBufferSouceNode.noteOn //在旧浏览器中使用noteOn方法 audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //在旧浏览器中使用noteOff方法 }; //如果有的话,停止前一个声音 if (this.animationId !== null) { cancelAnimationFrame(this.animationId); } audioBufferSouceNode.start(0); audo.source = audioBufferSouceNode; audo.audioContext = audioContext; } begin.onclick = function() { var ws = new WebSocket("ws://127.0.0.1:6200"); ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据 ws.onopen = function(event) { console.log('握手成功'); //业务命令构建 var data = { "cmd": "jtv",//发送命令 "id": "018665897939",//发送设备id "type": 1,//对讲类型 "channel": 0//语音通道 } //ws.send(JSON.stringify(data)); //这里是发送消息。不包括音频数据,先注掉 }; timeInte=setInterval(function(){ if(ws.readyState==1){//ws进入连接状态,则每隔500毫秒发送一包数据 record.start(); console.log("#######################send Blob start ##############################"); console.log(record.getBlob()); ws.send(record.getBlob()); //发送音频数据 console.log("#######################send Blob end ##############################"); record.clear(); //每次发送完成则清理掉旧数据 } },500); //每隔500ms发送一次,定时器 /// ws.onmessage = function (evt){ console.log( "Received Message: " + evt.data); receive(evt.data); } /// }

通过服务器来接收音频数据。这个时间可以以文件形式保存下来为.pcm就可以了。这里就使用工具来接收

 

 

最后生成pcm文件

 

 

保存好用.用Audacity工具来播放,看是不是正常的音频文件

 

 

播放正常。证明是个pcm的音频文件。得到了pcm(裸数据),我们可以做一个把pcm转成wav文件

 

#include #include /** * Convert PCM16LE raw data to WAVE format * @param pcmpath Input PCM file. * @param channels Channel number of PCM file. * @param sample_rate Sample rate of PCM file. * @param wavepath Output WAVE file. */ int simplest_pcm16le_to_wave(const char *pcmpath, int channels, int sample_rate, const char *wavepath) { printf("#########################1111#############################\n"); typedef struct WAVE_HEADER{ char fccID[4]; //内容为""RIFF unsigned long dwSize; //最后填写,WAVE格式音频的大小 char fccType[4]; //内容为"WAVE" }WAVE_HEADER; typedef struct WAVE_FMT{ char fccID[4]; //内容为"fmt " unsigned long dwSize; //内容为WAVE_FMT占的字节数,为16 unsigned short wFormatTag; //如果为PCM,改值为 1 unsigned short wChannels; //通道数,单通道=1,双通道=2 unsigned long dwSamplesPerSec;//采用频率 unsigned long dwAvgBytesPerSec;/* ==dwSamplesPerSec*wChannels*uiBitsPerSample/8 */ unsigned short wBlockAlign;//==wChannels*uiBitsPerSample/8 unsigned short uiBitsPerSample;//每个采样点的bit数,8bits=8, 16bits=16 }WAVE_FMT; typedef struct WAVE_DATA{ char fccID[4]; //内容为"data" unsigned long dwSize; //==NumSamples*wChannels*uiBitsPerSample/8 }WAVE_DATA; if(channels==2 || sample_rate==0) { channels = 2; sample_rate = 44100; } int bits = 16; WAVE_HEADER pcmHEADER; WAVE_FMT pcmFMT; WAVE_DATA pcmDATA; unsigned short m_pcmData; FILE *fp, *fpout; printf("#########################2222#############################\n"); fp = fopen(pcmpath, "rb+"); if(fp==NULL) { printf("Open pcm file error.\n"); return -1; } fpout = fopen(wavepath, "wb+"); if(fpout==NULL) { printf("Create wav file error.\n"); return -1; } printf("########################3333##############################\n"); /* WAVE_HEADER */ memcpy(pcmHEADER.fccID, "RIFF", strlen("RIFF")); memcpy(pcmHEADER.fccType, "WAVE", strlen("WAVE")); fseek(fpout, sizeof(WAVE_HEADER), 1); //1=SEEK_CUR /* WAVE_FMT */ memcpy(pcmFMT.fccID, "fmt ", strlen("fmt ")); pcmFMT.dwSize = 16; pcmFMT.wFormatTag = 1; pcmFMT.wChannels = 2; pcmFMT.dwSamplesPerSec = sample_rate; pcmFMT.uiBitsPerSample = bits; /* ==dwSamplesPerSec*wChannels*uiBitsPerSample/8 */ pcmFMT.dwAvgBytesPerSec = pcmFMT.dwSamplesPerSec*pcmFMT.wChannels*pcmFMT.uiBitsPerSample/8; /* ==wChannels*uiBitsPerSample/8 */ pcmFMT.wBlockAlign = pcmFMT.wChannels*pcmFMT.uiBitsPerSample/8; printf("##################4444####################################\n"); fwrite(&pcmFMT, sizeof(WAVE_FMT), 1, fpout); /* WAVE_DATA */ memcpy(pcmDATA.fccID, "data", strlen("data")); pcmDATA.dwSize = 0; fseek(fpout, sizeof(WAVE_DATA), SEEK_CUR); printf("##################5555####################################\n"); fread(&m_pcmData, sizeof(unsigned short), 1, fp); while(!feof(fp)) { pcmDATA.dwSize += 2; fwrite(&m_pcmData, sizeof(unsigned short), 1, fpout); fread(&m_pcmData, sizeof(unsigned short), 1, fp); } printf("##################4444####################################\n"); /*pcmHEADER.dwSize = 44 + pcmDATA.dwSize;*/ //修改时间:2018年1月5日 pcmHEADER.dwSize = 36 + pcmDATA.dwSize; rewind(fpout); fwrite(&pcmHEADER, sizeof(WAVE_HEADER), 1, fpout); fseek(fpout, sizeof(WAVE_FMT), SEEK_CUR); fwrite(&pcmDATA, sizeof(WAVE_DATA), 1, fpout); fclose(fp); fclose(fpout); printf("##################6666####################################\n"); return 0; } int main() { simplest_pcm16le_to_wave("E:\\project\\audio\\in.pcm", 2, 44100, "E:\\project\\audio\\out.wav"); return 0; }

 

再用Audacity播放out.wav文件,可正常播放。

涉及到音频领域。后面文章,再继讲如何把pcm转在g711a或是aac音频。

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有