Files
gpustack-ui/src/utils/pcm-to-wav.ts
T

55 lines
1.8 KiB
TypeScript

function writeString(view: DataView, offset: number, string: string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
/**
* Convert PCM audio data to WAV format
* @param pcmData - Raw PCM audio data as ArrayBuffer
* @param sampleRate - Sample rate (default: 24000 Hz, common for TTS)
* @param numChannels - Number of channels (default: 1 for mono)
* @param bitsPerSample - Bits per sample (default: 16)
* @returns WAV format audio as Blob
*/
export function pcmToWav(
pcmData: ArrayBuffer,
sampleRate: number = 24000,
numChannels: number = 1,
bitsPerSample: number = 16
): Blob {
const pcmBytes = new Uint8Array(pcmData);
const dataLength = pcmBytes.length;
// WAV file header size
const headerSize = 44;
const wavBuffer = new ArrayBuffer(headerSize + dataLength);
const view = new DataView(wavBuffer);
// Write WAV header
// "RIFF" chunk descriptor
writeString(view, 0, 'RIFF');
view.setUint32(4, 36 + dataLength, true); // File size - 8
writeString(view, 8, 'WAVE');
// "fmt " sub-chunk
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)
view.setUint16(20, 1, true); // AudioFormat (1 for PCM)
view.setUint16(22, numChannels, true); // NumChannels
view.setUint32(24, sampleRate, true); // SampleRate
view.setUint32(28, sampleRate * numChannels * (bitsPerSample / 8), true); // ByteRate
view.setUint16(32, numChannels * (bitsPerSample / 8), true); // BlockAlign
view.setUint16(34, bitsPerSample, true); // BitsPerSample
// "data" sub-chunk
writeString(view, 36, 'data');
view.setUint32(40, dataLength, true); // Subchunk2Size
// Write PCM data
const wavBytes = new Uint8Array(wavBuffer);
wavBytes.set(pcmBytes, headerSize);
return new Blob([wavBuffer], { type: 'audio/wav' });
}