本文采用单声道 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());
}
}
}