| | export function decodeBase64Audio( |
| | base64String: string, |
| | mimeType = 'audio/mpeg', |
| | sampleRate = 44100, |
| | numChannels = 1 |
| | ): string { |
| | |
| | const byteString = atob(base64String) |
| | const byteArray = new Uint8Array(byteString.length) |
| |
|
| | for (let i = 0; i < byteString.length; i += 1) { |
| | byteArray[i] = byteString.charCodeAt(i) |
| | } |
| |
|
| | let blob: Blob |
| |
|
| | if (mimeType === 'audio/pcm16') { |
| | |
| | const wavHeader = createWavHeader(byteArray.length, sampleRate, numChannels) |
| | const wavData = new Uint8Array(wavHeader.length + byteArray.length) |
| | wavData.set(wavHeader, 0) |
| | wavData.set(byteArray, wavHeader.length) |
| |
|
| | blob = new Blob([wavData], { type: 'audio/wav' }) |
| | } else { |
| | blob = new Blob([byteArray], { type: mimeType }) |
| | } |
| |
|
| | return URL.createObjectURL(blob) |
| | } |
| |
|
| | |
| | function createWavHeader( |
| | dataLength: number, |
| | sampleRate: number, |
| | numChannels: number |
| | ): Uint8Array { |
| | const header = new ArrayBuffer(44) |
| | const view = new DataView(header) |
| |
|
| | const blockAlign = numChannels * 2 |
| | const byteRate = sampleRate * blockAlign |
| |
|
| | |
| | view.setUint32(0, 0x52494646, false) |
| | view.setUint32(4, 36 + dataLength, true) |
| | view.setUint32(8, 0x57415645, false) |
| |
|
| | |
| | view.setUint32(12, 0x666d7420, false) |
| | view.setUint32(16, 16, true) |
| | view.setUint16(20, 1, true) |
| | view.setUint16(22, numChannels, true) |
| | view.setUint32(24, sampleRate, true) |
| | view.setUint32(28, byteRate, true) |
| | view.setUint16(32, blockAlign, true) |
| | view.setUint16(34, 16, true) |
| |
|
| | |
| | view.setUint32(36, 0x64617461, false) |
| | view.setUint32(40, dataLength, true) |
| |
|
| | return new Uint8Array(header) |
| | } |
| |
|