import { Router } from "express"; import multer from "multer"; import { assertBoard } from "./authz.js"; import { voicePipelineService } from "../services/voice-pipeline.js"; import { MAX_ATTACHMENT_BYTES } from "../attachment-types.js"; export function voiceRoutes(): Router { const router = Router(); const svc = voicePipelineService(); const audioUpload = multer({ storage: multer.memoryStorage(), limits: { fileSize: MAX_ATTACHMENT_BYTES, files: 1 }, }); // POST /api/transcribe — transcribe uploaded audio via VoicePipelineService router.post("/transcribe", async (req, res) => { assertBoard(req); await new Promise((resolve, reject) => audioUpload.single("audio")(req, res, (err) => (err ? reject(err) : resolve())) ); const file = (req as any).file as { buffer: Buffer; mimetype: string } | undefined; if (!file) { res.status(400).json({ error: "Missing audio field" }); return; } const fmt = file.mimetype.includes("ogg") ? "ogg" : file.mimetype.includes("wav") ? "wav" : "webm"; const result = await svc.transcribe(file.buffer, fmt); res.json(result); }); // POST /api/synthesize — synthesize text to speech via VoicePipelineService router.post("/synthesize", async (req, res) => { assertBoard(req); const { text, voiceId } = req.body as { text?: string; voiceId?: string }; if (!text || typeof text !== "string") { res.status(400).json({ error: "text is required" }); return; } const audioBuffer = await svc.synthesize(text, voiceId); res.setHeader("Content-Type", "audio/wav"); res.send(audioBuffer); }); return router; }