Created
July 7, 2020 03:29
-
-
Save huanggm/bfc8d0c84afb5aaa3c28f5efcaeb4d62 to your computer and use it in GitHub Desktop.
audio scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 录音数据是共享内存的,所以在一个回调事件内是有效的 | |
// 下一个回调事件内则会把上一份数据覆盖 | |
const f32 = event.inputBuffer.getChannelData(0); | |
// 复制一份内存数据 | |
const f32Ret = new Float32Array(f32); | |
// 把一个音频文件切片,分次上传 | |
this.http.get('http://local.yuanfudao.ws:3000/assets/rec-001.wav', { | |
responseType: 'blob' | |
}).pipe( | |
switchMap(blob => { | |
const fileSize = blob.size; | |
console.log('fileSize: ', fileSize); | |
let start = 44; | |
const blobList = []; | |
while(start < fileSize) { | |
const end = start + 5000 > fileSize ? fileSize : start + 5000; | |
blobList.push(blob.slice(start, end)); | |
start = end; | |
} | |
return zip(...blobList.map(item => new Observable(observer => { | |
const reader = new FileReader(); | |
reader.onload = (e: any) => { | |
const base64=(/.+;\s*base64\s*,\s*(.+)$/i.exec(e.target.result)||[])[1]; | |
observer.next(base64); | |
} | |
reader.readAsDataURL(item); | |
}) as Observable<string> | |
)) | |
}), | |
tap((base64Arr: string[]) => { | |
console.log('tap tap', base64Arr); | |
this.wsSubject.next({type: 1, text: 'xxx', params: {audioType: 'pcm'}}); | |
let index = 0; | |
const length = base64Arr.length; | |
const timer = setInterval(() => { | |
console.log('setInterval', index); | |
if (index < length) { | |
this.wsSubject.next({type: 2, packet: base64Arr[index], packetIndex: index}); | |
index ++; | |
} else { | |
clearInterval(timer); | |
this.wsSubject.next({type: 3}); | |
} | |
}, 500); | |
}), | |
catchError(e => { | |
console.log('eeee', e); | |
return of(null); | |
}) | |
).subscribe(); | |
// 合并多批次的数据为一个Float32Array | |
// 并把Float32Array转成Int16Array | |
private transArr32to16WithStep(bufferArr: Float32Array[][]) { | |
const f32ArrArr = bufferArr.map(b => b[0]); // 取单声道的数据 | |
const totalLength = f32ArrArr.reduce((len, f32) => len + f32.length, 0); | |
const f32Arr = new Float32Array(totalLength); | |
let offset = 0; | |
for (let i = 0; i < f32ArrArr.length; i++) { | |
f32Arr.set(f32ArrArr[i], offset); | |
offset += f32ArrArr[i].length; | |
} | |
const int16Arr = new Int16Array(totalLength); | |
for (let i = 0; i < totalLength; i++) { | |
let s = Math.max(-1, Math.min(1, f32Arr[i])); | |
s = s < 0 ? s * 0x8000 : s * 0x7FFF; | |
int16Arr[i] = s; | |
} | |
return int16Arr; | |
} | |
// 把Int16Array转成wav文件 | |
// 其实就是加上了wav文件头 | |
private getWavFileFromBuffer(buffer16: Int16Array) { | |
console.log('buffer16 length', buffer16.length); | |
const size = buffer16.length; | |
const sampleRate = 16000; | |
const bitRate = 16; | |
// 编码数据 https://github.com/mattdiamond/Recorderjs https://www.cnblogs.com/blqw/p/3782420.html https://www.cnblogs.com/xiaoqi/p/6993912.html | |
const dataLength = size * ( bitRate / 8 ); | |
const buffer = new ArrayBuffer(44 + dataLength); | |
const data = new DataView(buffer); | |
let offset = 0; | |
const writeString = function(str) { | |
for (let i = 0; i < str.length; i++, offset++) { | |
data.setUint8(offset, str.charCodeAt(i)); | |
}; | |
}; | |
const write16 = function(v) { | |
data.setUint16(offset, v, true); | |
offset += 2; | |
}; | |
const write32 = function(v) { | |
data.setUint32(offset, v, true); | |
offset += 4; | |
}; | |
/* RIFF identifier */ | |
writeString('RIFF'); | |
/* RIFF chunk length */ | |
write32(36 + dataLength); | |
/* RIFF type */ | |
writeString('WAVE'); | |
/* format chunk identifier */ | |
writeString('fmt '); | |
/* format chunk length */ | |
write32(16); | |
/* sample format (raw) */ | |
write16(1); | |
/* channel count */ | |
write16(1); | |
/* sample rate */ | |
write32(sampleRate); | |
/* byte rate (sample rate * block align) */ | |
write32(sampleRate * (bitRate / 8)); | |
/* block align (channel count * bytes per sample) */ | |
write16(bitRate / 8); | |
/* bits per sample */ | |
write16(bitRate); | |
/* data chunk identifier */ | |
writeString('data'); | |
/* data chunk length */ | |
write32(dataLength); | |
// 写入采样数据 | |
for (let i = 0; i < size; i++, offset += 2) { | |
data.setInt16(offset, buffer16[i], true); | |
}; | |
console.log(data.buffer); | |
const blob = new Blob([data.buffer], { type: 'audio/wav' }) | |
const blobUrl = URL.createObjectURL(blob); | |
// @ts-ignore | |
document.querySelector('#audio-node-hgm').src = blobUrl; | |
const link = document.createElement('a'); | |
link.href = blobUrl; | |
link.download = 'hello1.wav'; | |
document.body.appendChild(link); | |
link.dispatchEvent(new MouseEvent('click', { | |
bubbles: true, | |
cancelable: true, | |
view: window | |
})) | |
} | |
// 使用btoa编码buffer到base64 | |
const buffer16 = new ArrayBuffer(buffer32.length * 2); | |
const base64String = btoa(String.fromCharCode(...new Uint8Array(buffer16.buffer))); | |
// 使用第三方库编码 | |
import {encode} from 'base64-arraybuffer' | |
const base64String = encode(buffer16); | |
// 使用FileReader Api读取buffer | |
const blob = new Blob([buffer]); | |
const reader = new FileReader(); | |
reader.onloadend = (e: any) => { | |
// @ts-ignore | |
const base64 = (/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1]; | |
} | |
reader.readAsDataURL(blob); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment