video-tools/src/utils/convert.ts
2025-04-20 01:32:28 +08:00

83 lines
2.0 KiB
TypeScript

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;
};