Web端录音数据回传unity

本文采用单声道 16b  16K

js部分

var WebLibrary =
{
$audioInput: {},
	$recorder: {},
	$chunks: {},
	$audioContext: {},
	Init: function (url) {

		//初始化录音
		navigator.getUserMedia =
			navigator.getUserMedia ||
			navigator.webkitGetUserMedia ||
			navigator.mozGetUserMedia ||
			navigator.msGetUserMedia

		// 创建音频环境
		try {
			audioContext = new (window.AudioContext || window.webkitAudioContext)()
			audioContext.suspend()
			if (!audioContext) {
				alert('浏览器不支持webAudioApi相关接口')
				return
			}
		} catch (e) {
			if (!audioContext) {
				alert('浏览器不支持webAudioApi相关接口')
				return
			}
		}

		// 获取浏览器录音权限
		if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
			navigator.mediaDevices
				.getUserMedia({
					audio: true,
					video: false,
				})
				.then(function (stream) {
					getMediaSuccess(stream);
				})
				.catch(function (e) {
					getMediaFail(e);
				})
		} else if (navigator.getUserMedia) {
			navigator.getUserMedia(
				{
					audio: true,
					video: false,
				},
				function (stream) {
					getMediaSuccess(stream);
				},
				function (e) {
					getMediaFail(e);
				}
			)
		} else {
			if (navigator.userAgent.toLowerCase().match(/chrome/) && location.origin.indexOf('https://') < 0) {
				alert('chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限')
			} else {
				alert('无法获取浏览器录音功能,请升级浏览器或使用chrome')
			}
			audioContext && audioContext.close()
			return
		};

		function getMediaSuccess(stream) {
			console.log('getMediaSuccess')

			chunks = [];
			recorder = audioContext.createScriptProcessor(0, 1, 1)
			recorder.onaudioprocess = function (e) {
				console.log(e.inputBuffer.getChannelData(0));
				
				chunks.push(e.inputBuffer.getChannelData(0))
			}
			// 创建一个新的MediaStreamAudioSourceNode 对象,使来自MediaStream的音频可以被播放和操作
			audioInput = audioContext.createMediaStreamSource(stream)

			audioInput.connect(recorder)
			recorder.connect(audioContext.destination)

		};
		function getMediaFail(e) {
			alert('请求麦克风失败')
			console.log(e)
			audioContext && audioContext.close()
			audioContext = undefined
		};
	},

	StartRecorder: function () {
		//mediaRecorder.start();
		// 连接
		audioContext.resume()

	},
	
	$sendWAVData: function (blob, sign) {
		var reader = new FileReader();
		reader.onload = function (e) {
			var _value = reader.result;
			var _partLength = 8192;
			var _length = parseInt(_value.length / _partLength);
			if (_length * _partLength < _value.length)
				_length += 1;
			var _head = "Head|" + _length.toString() + "|" + _value.length.toString() + "|" + sign;
			SendMessage("WebSocket", "GetAudioData", _head);
			for (var i = 0; i < _length; i++) {
				var _sendValue = "";
				if (i < _length - 1) {
					_sendValue = _value.substr(i * _partLength, _partLength);
				}
				else {
					_sendValue = _value.substr(i * _partLength, _value.length - i * _partLength);
				}
				_sendValue = "Part|" + i.toString() + "|" + _sendValue;
				SendMessage("WebSocket", "GetAudioData", _sendValue);
			}
			if (sign === "end")
				recorderState = "inactive";
			_value = null;
		}
		reader.readAsDataURL(blob);
	},
	StopRecorder: function () {
		//mediaRecorder.stop();

		//停止
		audioContext.suspend()
		console.log('停了!')
		//recorder.disconnect();
		//SendMessage("WebSocket", "OnMessageFloat32ArrayEnd");
		
	
		var _data = getFullWavData();//getPureWavData(0);
		sendWAVData(_data, "part");
		_data = [];
		chunks = [];
		//var _headerData = getWAVHeaderData(recordedResultLength);
		//sendWAVData(_headerData, "end");
		//_headerData = [];

		function getFullWavData() {
			var sampleRate = 16000
			var sampleBits = 16
			var bytes = getRawData();
			var dataLength = bytes.length * (sampleBits / 8);
			var buffer = new ArrayBuffer(44 + dataLength);
			var data = new DataView(buffer);
			var offset = 0;
			var writeString = function (str) {
				for (var i = 0; i < str.length; i++) {
					data.setUint8(offset + i, str.charCodeAt(i));
				}
			};
			// 资源交换文件标识符   
			writeString('RIFF'); offset += 4;
			// 下个地址开始到文件尾总字节数,即文件大小-8   
			data.setUint32(offset, 36 + dataLength, true); offset += 4;
			// WAV文件标志  
			writeString('WAVE'); offset += 4;
			// 波形格式标志   
			writeString('fmt '); offset += 4;
			// 过滤字节,一般为 0x10 = 16   
			data.setUint32(offset, 16, true); offset += 4;
			// 格式类别 (PCM形式采样数据)   
			data.setUint16(offset, 1, true); offset += 2;
			// 通道数   
			data.setUint16(offset, 1, true); offset += 2;
			// 采样率,每秒样本数,表示每个通道的播放速度   
			data.setUint32(offset, sampleRate, true); offset += 4;
			// 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8   
			data.setUint32(offset, 1 * sampleRate * (sampleBits / 8), true); offset += 4;
			// 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8   
			data.setUint16(offset, 1 * (sampleBits / 8), true); offset += 2;
			// 每样本数据位数   
			data.setUint16(offset, sampleBits, true); offset += 2;
			// 数据标识符   
			writeString('data'); offset += 4;
			// 采样数据总数,即数据总大小-44   
			data.setUint32(offset, dataLength, true); offset += 4;
			// 写入采样数据   
			data = reshapeWavData(sampleBits, offset, bytes, data);
			//                var wavd = new Int8Array(data.buffer.byteLength);
			//                var pos = 0;
			//                for (var i = 0; i < data.buffer.byteLength; i++, pos++) {
			//                    wavd[i] = data.getInt8(pos);
			//                }                //                return wavd;

			return new Blob([data], { type: 'audio/wav' });

		};
		function getPureWavData(offset) {
			var sampleBits = 16
			var bytes = getRawData();
			var dataLength = bytes.length * (sampleBits / 8);
			var buffer = new ArrayBuffer(dataLength);
			var data = new DataView(buffer);
			data = reshapeWavData(sampleBits, offset, bytes, data);
			//                var wavd = new Int8Array(data.buffer.byteLength);
			//                var pos = 0;
			//                for (var i = 0; i < data.buffer.byteLength; i++, pos++) {
			//                    wavd[i] = data.getInt8(pos);
			//                }                //                return wavd;
			return new Blob([data], { type: 'audio/wav' });
		};
		function reshapeWavData(sampleBits, offset, iBytes, oData) {
			if (sampleBits === 8) {
				for (var i = 0; i < iBytes.length; i++, offset++) {
					var s = Math.max(-1, Math.min(1, iBytes[i]));
					var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
					val = parseInt(255 / (65535 / (val + 32768)));
					oData.setInt8(offset, val, true);
				}
			} else {
				for (var i = 0; i < iBytes.length; i++, offset += 2) {
					var s = Math.max(-1, Math.min(1, iBytes[i]));
					oData.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
				}
			}
			return oData;
		};
		function getRawData() { //合并压缩  
			//合并  
			var size = 0;
			for (var i = 0; i < chunks.length; i++) {
				size += chunks[i].length;
			}
			var data = new Float32Array(size);
			var offset = 0;
			for (var i = 0; i < chunks.length; i++) {
				data.set(chunks[i], offset);
				offset += chunks[i].length;
			}
			//压缩
			var getRawDataion = parseInt(audioContext.sampleRate / 16000);
			var length = data.length / getRawDataion;
			var result = new Float32Array(length);
			var index = 0, j = 0;
			while (index < length) {
				result[index] = data[j];
				j += getRawDataion;
				index++;
			}
			return result;
		}
	}

};

C#部分

WAV地址https://answers.unity.com/questions/737002/wav-byte-to-audioclip.html

 public void GetAudioData(string _audioDataString)
    {
        if (_audioDataString.Contains("Head"))
        {
            string[] _headValue = _audioDataString.Split('|');
            m_valuePartCount = int.Parse(_headValue[1]);
            m_audioLength = int.Parse(_headValue[2]);
            m_currentRecorderSign = _headValue[3];
            m_audioData = new string[m_valuePartCount];
            m_getDataLength = 0;
            //Debug.Log("接收数据头:" + m_valuePartCount + "   " + m_audioLength);
        }
        else if (_audioDataString.Contains("Part"))
        {
            string[] _headValue = _audioDataString.Split('|');
            int _dataIndex = int.Parse(_headValue[1]);
            m_audioData[_dataIndex] = _headValue[2];
            m_getDataLength++;
            if (m_getDataLength == m_valuePartCount)
            {
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < m_audioData.Length; i++)
                {
                    stringBuilder.Append(m_audioData[i]);
                }
                string _audioDataValue = stringBuilder.ToString();
                //Debug.Log("接收长度:" + _audioDataValue.Length + " 需接收长度:" + m_audioLength);
                int _index = _audioDataValue.LastIndexOf(',');
                string _value = _audioDataValue.Substring(_index + 1, _audioDataValue.Length - _index - 1);
                data = Convert.FromBase64String(_value);
                //Debug.Log("已接收长度 :" + data.Length);
                WWUtils.Audio.WAV wav = new WAV(data);
                audioClip = AudioClip.Create("wavClip", wav.SampleCount, 1, wav.Frequency, false);
                audioClip.SetData(wav.LeftChannel, 0);
              
                //audioClip = AudioClip.Create("RecordClip", micFlostDataList.Count, 1, 16000, false);
                //audioClip.SetData(micFlostDataList.ToArray(), 0);
                StartCoroutine(onOpen());

            }
        }
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值