type EncodeWavOptions = { numChannels?: number; sampleRate?: number; byteRate?: number; }; /** * 编码pcm文件为wav文件 * @param rawPCM * @param options * @returns */ export function encodeWav(rawPCM: Buffer | string, options?: EncodeWavOptions) { if (typeof rawPCM === 'string') { rawPCM = Buffer.from(rawPCM, 'binary'); } if (!Buffer.isBuffer(rawPCM)) { throw new TypeError('pcm data must be Buffer or string'); } const opt = options || {}; const sampleRate = opt.sampleRate || 16000; const numChannels = opt.numChannels || 1; const byteRate = opt.byteRate || 16; const buf = rawPCM; const header = Buffer.alloc(44); header.write('RIFF', 0); header.writeUInt32LE(buf.length, 4); header.write('WAVE', 8); header.write('fmt ', 12); header.writeUInt8(16, 16); header.writeUInt8(1, 20); header.writeUInt8(numChannels, 22); header.writeUInt32LE(sampleRate, 24); header.writeUInt32LE(byteRate, 28); header.writeUInt8(4, 32); header.writeUInt8(16, 34); header.write('data', 36); header.writeUInt32LE(buf.length + 44 - 8, 40); return Buffer.concat([header, buf]); } /** * 解码wav文件 * @param rawWav * @returns */ export function decodeWav(rawWav: Buffer | string) { if (typeof rawWav === 'string') { rawWav = Buffer.from(rawWav, 'binary'); } if (!Buffer.isBuffer(rawWav)) { throw new TypeError('pcm data must be Buffer or string'); } // remove the header of pcm format rawWav = rawWav.subarray(44); return rawWav; } export const converter = { encodeWav, decodeWav, }; /** * 生成无声音频 * @param duration 音频时长(秒) * @param sampleRate 采样率 * @param channels 声道数 * @returns 无声音频 */ export const generateSilent = (duration: number = 2, sampleRate: number = 16000, channels: number = 1) => { const bufferSize = Math.floor(duration * sampleRate * channels * 2); // 2 bytes per sample const silent = Buffer.alloc(bufferSize); return silent; };