ai-chatbot/lib/video-canvas/audio-tools.ts

62 lines
2.2 KiB
TypeScript

export const MIN_AUDIO_PLAYBACK_RATE = 0.1;
export const MAX_AUDIO_PLAYBACK_RATE = 4;
export const AUDIO_PLAYBACK_RATE_STEP = 0.1;
export type AudioToolRequest =
| { kind: "trim"; startSeconds: number; endSeconds: number }
| { kind: "speed"; playbackRate: number };
export function normalizeAudioPlaybackRate(value: unknown, fallback = 1) {
const parsed = Number.parseFloat(String(value ?? "").replace(/x$/i, ""));
const safe = Number.isFinite(parsed) ? parsed : fallback;
return Math.round(
Math.min(MAX_AUDIO_PLAYBACK_RATE, Math.max(MIN_AUDIO_PLAYBACK_RATE, safe)) * 100,
) / 100;
}
export function stepAudioPlaybackRate(value: unknown, direction: 1 | -1) {
return normalizeAudioPlaybackRate(
normalizeAudioPlaybackRate(value) + direction * AUDIO_PLAYBACK_RATE_STEP,
);
}
export function formatAudioPlaybackRate(value: unknown) {
return `${normalizeAudioPlaybackRate(value).toFixed(2)}x`;
}
export function formatAudioPlaybackRateSuffix(value: unknown) {
const rate = normalizeAudioPlaybackRate(value);
return Number.isInteger(rate) ? `${rate}x` : `${rate.toFixed(2).replace(/0+$/, "")}x`;
}
export function getDefaultAudioTrimRange(durationSeconds: unknown) {
const duration = Math.max(0.1, Number(durationSeconds) || 0.1);
return {
startSeconds: 0,
endSeconds: Math.min(duration, Math.max(0.1, duration / 3)),
};
}
export function normalizeAudioTrimRange(
durationSeconds: unknown,
startSeconds: unknown,
endSeconds: unknown,
) {
const duration = Math.max(0.1, Number(durationSeconds) || 0.1);
const start = Math.min(duration - 0.05, Math.max(0, Number(startSeconds) || 0));
const end = Math.min(duration, Math.max(start + 0.05, Number(endSeconds) || duration));
return { startSeconds: start, endSeconds: end };
}
export function getProcessedAudioTitle(sourceTitle: string, request: AudioToolRequest) {
const base = String(sourceTitle || "音频").trim() || "音频";
return request.kind === "trim"
? `${base}_截取`
: `${base}_${formatAudioPlaybackRateSuffix(request.playbackRate)}`;
}
export function getAudioDownloadExtension(source: unknown) {
const pathname = String(source || "").split(/[?#]/, 1)[0];
return pathname.match(/\.(wav|mp3|m4a|aac|ogg|webm)$/i)?.[1]?.toLowerCase() || "mp3";
}