使用audio標籤播放pcm音訊流
阿新 • • 發佈:2022-12-02
1 首先我們要獲取到pcm流 如果已經獲取了 就可以直接對其進行操作 我們這邊是沒有獲取的 使用fetch方法遠端請求資源,程式碼如下
fetch(url, { responseType: 'arrayBuffer', headers: {} }).then((res) => { return res.arrayBuffer(); }).then((arrayBuffer) => { console.log(arrayBuffer, '加密') })
這邊url 就是遠端請求的地址 注意返回型別是arrayBuffer,一種原始的二進位制資料流
2 由於我們的arrayBuffer 是加密的 所以需要先對其解密再進行操作 加密庫我們使用的是CryptoJS,解密程式碼如下
decryptFile(wordArray, secret) { const key = CryptoJS.enc.Utf8.parse(CryptoJS.MD5(secret)); // secret就是金鑰 const iv = CryptoJS.enc.Utf8.parse(""); // iv裡面的值需要和後端確認 wordArray = CryptoJS.enc.Base64.stringify(wordArray) const decrypt = CryptoJS.AES.decrypt(wordArray, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 //偏移量 }) return this.wordArrayToArrayBuffer(decrypt).buffer // 注意我們要拿到arrayBuffer 進行操作 },
注意這裡的第一個引數需要我們構建一下,程式碼如下:
const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer)
這裡的arrayBuffer 就是我們請求的原始資料,同時附上 上面wordArrayToArrayBuffer函式
wordArrayToArrayBuffer(wordArray) { const { words } = wordArray const { sigBytes } = wordArray const u8 = new Uint8Array(sigBytes) for (let i = 0; i < sigBytes; i += 1) { u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff } return u8 },
這裡我們已經完成了解密 然後將這個流轉為audio標籤可以播放的格式
addWavHeader(samples, sampleRateTmp, sampleBits, channelCount) { const dataLength = samples.byteLength; /* 新的buffer類,預留44bytes的heaer空間 */ const buffer = new ArrayBuffer(44 + dataLength); /* 轉為 Dataview, 利用API來填充位元組 */ const view = new DataView(buffer); let offset = 0; /* ChunkID, 4 bytes, 資源交換檔案識別符號 */ this.writeString(view, offset, 'RIFF'); offset += 4; /* ChunkSize, 4 bytes, 下個地址開始到檔案尾總位元組數,即檔案大小-8 */ view.setUint32(offset, /* 32 */ 36 + dataLength, true); offset += 4; /* Format, 4 bytes, WAV檔案標誌 */ this.writeString(view, offset, 'WAVE'); offset += 4; /* Subchunk1 ID, 4 bytes, 波形格式標誌 */ this.writeString(view, offset, 'fmt '); offset += 4; /* Subchunk1 Size, 4 bytes, 過濾位元組,一般為 0x10 = 16 */ view.setUint32(offset, 16, true); offset += 4; /* Audio Format, 2 bytes, 格式類別 (PCM形式取樣資料) */ view.setUint16(offset, 1, true); offset += 2; /* Num Channels, 2 bytes, 通道數 */ view.setUint16(offset, channelCount, true); offset += 2; /* SampleRate, 4 bytes, 取樣率,每秒樣本數,表示每個通道的播放速度 */ view.setUint32(offset, sampleRateTmp, true); offset += 4; /* ByteRate, 4 bytes, 波形資料傳輸率 (每秒平均位元組數) 通道數×每秒資料位數×每樣本資料位/8 */ view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset += 4; /* BlockAlign, 2 bytes, 快資料調整數 取樣一次佔用位元組數 通道數×每樣本的資料位數/8 */ view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2; /* BitsPerSample, 2 bytes, 每樣本資料位數 */ view.setUint16(offset, sampleBits, true); offset += 2; /* Subchunk2 ID, 4 bytes, 資料識別符號 */ this.writeString(view, offset, 'data'); offset += 4; /* Subchunk2 Size, 4 bytes, 取樣資料總數,即資料總大小-44 */ view.setUint32(offset, dataLength, true); offset += 4; if (sampleBits === 16) { this.floatTo16BitPCM(view, samples); } else if (sampleBits === 8) { this.floatTo8BitPCM(view, samples); } else { this.floatTo32BitPCM(view, samples); } // return view.buffer return new Blob([view], { type: 'audio/wav' }); }, writeString(view, offset, string) { for (let i = 0; i < string.length; i += 1) { view.setUint8(offset + i, string.charCodeAt(i)); } }, floatTo32BitPCM(output, input) { const oinput = new Int32Array(input); let newoffset = 44; for (let i = 0; i < oinput.length; i += 1, newoffset += 4) { output.setInt32(newoffset, oinput[i], true); } }, floatTo16BitPCM(output, input) { const oinput = new Int16Array(input); let newoffset = 44; for (let i = 0; i < oinput.length; i += 1, newoffset += 2) { output.setInt16(newoffset, oinput[i], true); } }, floatTo8BitPCM(output, input) { const oinput = new Int8Array(input); let newoffset = 44; for (let i = 0; i < oinput.length; i += 1, newoffset += 1) { output.setInt8(newoffset, oinput[i], true); } },
總體來說就是給fem格式的加一個頭
最後我們將這個流轉為blob型別 這樣就可以直接播放了
readBlobFile(Blobdata, audioRef) { const reader = new FileReader(); reader.readAsArrayBuffer(Blobdata); reader.onload = (e) => { const bufer = e.target.result; const blob = this.addWavHeader(bufer, 16000, 16, 1); console.log(blob, "blob") this.srcUrl = window.URL.createObjectURL(blob); this.$nextTick(() => { this.$refs[audioRef].src = this.srcUrl this.$refs[audioRef].load() }) console.log(this.srcUrl, 'readBlobFile') }; },