import ffmpeg from "fluent-ffmpeg";
import { Readable } from "node:stream";
import { logger } from "../../logger";
export class FFMpeg {
static async init(): Promise<FFMpeg> {
return import("@ffmpeg-installer/ffmpeg").then((ffmpegInstaller) => {
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
logger.info("FFmpeg path set to:", ffmpegInstaller.path);
return new FFMpeg();
});
}
async saveNormalizedAudio(
audio: ArrayBuffer,
outputPath: string,
): Promise<string> {
logger.debug("Normalizing audio for Whisper");
const inputStream = new Readable();
inputStream.push(Buffer.from(audio));
inputStream.push(null);
return new Promise((resolve, reject) => {
ffmpeg()
.input(inputStream)
.audioCodec("pcm_s16le")
.audioChannels(1)
.audioFrequency(16000)
.toFormat("wav")
.on("end", () => {
logger.debug("Audio normalization complete");
resolve(outputPath);
})
.on("error", (error: unknown) => {
logger.error(error, "Error normalizing audio:");
reject(error);
})
.save(outputPath);
});
}
async createMp3DataUri(audio: ArrayBuffer): Promise<string> {
const inputStream = new Readable();
inputStream.push(Buffer.from(audio));
inputStream.push(null);
return new Promise((resolve, reject) => {
const chunk: Buffer[] = [];
ffmpeg()
.input(inputStream)
.audioCodec("libmp3lame")
.audioBitrate(128)
.audioChannels(2)
.toFormat("mp3")
.on("error", (err) => {
reject(err);
})
.pipe()
.on("data", (data: Buffer) => {
chunk.push(data);
})
.on("end", () => {
const buffer = Buffer.concat(chunk);
resolve(`data:audio/mp3;base64,${buffer.toString("base64")}`);
})
.on("error", (err) => {
reject(err);
});
});
}
async saveToMp3(audio: ArrayBuffer, filePath: string): Promise<string> {
const inputStream = new Readable();
inputStream.push(Buffer.from(audio));
inputStream.push(null);
return new Promise((resolve, reject) => {
ffmpeg()
.input(inputStream)
.audioCodec("libmp3lame")
.audioBitrate(128)
.audioChannels(2)
.toFormat("mp3")
.save(filePath)
.on("end", () => {
logger.debug("Audio conversion complete");
resolve(filePath);
})
.on("error", (err) => {
reject(err);
});
});
}
}