Files

133 lines
5.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ════════════════════════════════════════════════════════════════════════════
// 语音模块 - TTS 合成服务
// ════════════════════════════════════════════════════════════════════════════
export const TTS_WORKER_URL = 'https://hstts.velure.codes';
export const DEFAULT_VOICE = 'female_1';
export const DEFAULT_SPEED = 1.0;
export const VALID_EMOTIONS = ['happy', 'sad', 'angry', 'surprise', 'scare', 'hate'];
export const EMOTION_ICONS = {
happy: '😄', sad: '😢', angry: '😠', surprise: '😮', scare: '😨', hate: '🤢'
};
let voiceListCache = null;
let defaultVoiceKey = DEFAULT_VOICE;
// ════════════════════════════════════════════════════════════════════════════
// 声音列表管理
// ════════════════════════════════════════════════════════════════════════════
/**
* 加载可用声音列表
*/
export async function loadVoices() {
if (voiceListCache) return { voices: voiceListCache, defaultVoice: defaultVoiceKey };
try {
const res = await fetch(`${TTS_WORKER_URL}/voices`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
voiceListCache = data.voices || [];
defaultVoiceKey = data.defaultVoice || DEFAULT_VOICE;
return { voices: voiceListCache, defaultVoice: defaultVoiceKey };
} catch (err) {
console.error('[FW Voice] 加载声音列表失败:', err);
return { voices: [], defaultVoice: DEFAULT_VOICE };
}
}
/**
* 获取已缓存的声音列表
*/
export function getVoiceList() {
return voiceListCache || [];
}
/**
* 获取默认声音
*/
export function getDefaultVoice() {
return defaultVoiceKey;
}
// ════════════════════════════════════════════════════════════════════════════
// TTS 合成
// ════════════════════════════════════════════════════════════════════════════
/**
* 合成语音
* @param {string} text - 要合成的文本
* @param {Object} options - 选项
* @param {string} [options.voiceKey] - 声音标识
* @param {number} [options.speed] - 语速 0.5-2.0
* @param {string} [options.emotion] - 情绪
* @returns {Promise<string>} base64 编码的音频数据
*/
export async function synthesizeSpeech(text, options = {}) {
const {
voiceKey = defaultVoiceKey,
speed = DEFAULT_SPEED,
emotion = null
} = options;
const requestBody = {
voiceKey,
text: String(text || ''),
speed: Number(speed) || DEFAULT_SPEED,
uid: 'xb_' + Date.now(),
reqid: crypto.randomUUID?.() || `${Date.now()}_${Math.random().toString(36).slice(2)}`
};
if (emotion && VALID_EMOTIONS.includes(emotion)) {
requestBody.emotion = emotion;
requestBody.emotionScale = 5;
}
const res = await fetch(TTS_WORKER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
});
if (!res.ok) throw new Error(`TTS HTTP ${res.status}`);
const data = await res.json();
if (data.code !== 3000) throw new Error(data.message || 'TTS 合成失败');
return data.data; // base64 音频
}
// ════════════════════════════════════════════════════════════════════════════
// 提示词指南
// ════════════════════════════════════════════════════════════════════════════
export const VOICE_GUIDELINE = `## 模拟语音
如需发送语音消息,使用以下格式:
[voice:情绪:语音内容]
### 情绪参数7选1
- 空 = 平静/默认(例:[voice::今天天气不错]
- happy = 开心/兴奋
- sad = 悲伤/低落
- angry = 生气/愤怒
- surprise = 惊讶/震惊
- scare = 恐惧/害怕
- hate = 厌恶/反感
### 标点辅助控制语气:
- …… 拖长、犹豫、伤感
- !有力、激动
- 更激动
- 疑问、上扬
- ?!惊讶质问
- 撒娇、轻快
- —— 拉长、戏剧化
- ——! 惊叫、强烈
- ,。 正常停顿
### 示例:
[voice:happy:太好了!终于见到你了~]
[voice::——啊!——不要!]
注意voice部分需要在<msg>内`;