Compare commits
3 Commits
8aec4f6f44
...
1757f39322
| Author | SHA1 | Date | |
|---|---|---|---|
| 1757f39322 | |||
| 649a52c07b | |||
| c79ffac6d9 |
@@ -475,8 +475,10 @@ class IndexTTS2Backend:
|
|||||||
|
|
||||||
emo_vector = None
|
emo_vector = None
|
||||||
if emo_text and len(emo_text.strip()) > 0:
|
if emo_text and len(emo_text.strip()) > 0:
|
||||||
emo_vector = self._emo_text_to_vector(emo_text)
|
raw_vector = self._emo_text_to_vector(emo_text)
|
||||||
logger.info(f"IndexTTS2 emo_text={repr(emo_text)} → emo_vector={emo_vector}")
|
if raw_vector is not None:
|
||||||
|
emo_vector = [v * emo_alpha for v in raw_vector]
|
||||||
|
logger.info(f"IndexTTS2 emo_text={repr(emo_text)} emo_alpha={emo_alpha} → emo_vector={emo_vector}")
|
||||||
|
|
||||||
async with IndexTTS2Backend._gpu_lock:
|
async with IndexTTS2Backend._gpu_lock:
|
||||||
await loop.run_in_executor(
|
await loop.run_in_executor(
|
||||||
|
|||||||
@@ -63,8 +63,21 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [advancedOpen, setAdvancedOpen] = useState(false)
|
const [advancedOpen, setAdvancedOpen] = useState(false)
|
||||||
const [indexTTS2Open, setIndexTTS2Open] = useState(false)
|
const [indexTTS2Open, setIndexTTS2Open] = useState(false)
|
||||||
|
const [selectedEmotion, setSelectedEmotion] = useState('none')
|
||||||
const [emoText, setEmoText] = useState('')
|
const [emoText, setEmoText] = useState('')
|
||||||
const [emoAlpha, setEmoAlpha] = useState(0.6)
|
const [emoAlpha, setEmoAlpha] = useState(0.6)
|
||||||
|
|
||||||
|
const EMOTION_PRESETS = [
|
||||||
|
{ value: 'none', label: '不使用情感控制', emo_text: '', emo_alpha: 0.5 },
|
||||||
|
{ value: 'happy', label: '开心', emo_text: '开心', emo_alpha: 0.6 },
|
||||||
|
{ value: 'angry', label: '愤怒', emo_text: '愤怒', emo_alpha: 0.15 },
|
||||||
|
{ value: 'sad', label: '悲伤', emo_text: '悲伤', emo_alpha: 0.4 },
|
||||||
|
{ value: 'fear', label: '恐惧', emo_text: '恐惧', emo_alpha: 0.4 },
|
||||||
|
{ value: 'hate', label: '厌恶', emo_text: '厌恶', emo_alpha: 0.6 },
|
||||||
|
{ value: 'low', label: '低沉', emo_text: '低沉', emo_alpha: 0.6 },
|
||||||
|
{ value: 'surprise', label: '惊讶', emo_text: '惊讶', emo_alpha: 0.3 },
|
||||||
|
{ value: 'neutral', label: '中性', emo_text: '中性', emo_alpha: 0.5 },
|
||||||
|
]
|
||||||
const [isIndexTTS2Loading, setIsIndexTTS2Loading] = useState(false)
|
const [isIndexTTS2Loading, setIsIndexTTS2Loading] = useState(false)
|
||||||
const [tempAdvancedParams, setTempAdvancedParams] = useState({
|
const [tempAdvancedParams, setTempAdvancedParams] = useState({
|
||||||
max_new_tokens: 2048,
|
max_new_tokens: 2048,
|
||||||
@@ -555,26 +568,46 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button type="button" variant="outline" className="w-full">
|
<Button type="button" variant="outline" className="w-full">
|
||||||
<Zap className="mr-2 h-4 w-4" />
|
<Zap className="mr-2 h-4 w-4" />
|
||||||
IndexTTS2
|
情感控制
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[480px]">
|
<DialogContent className="sm:max-w-[480px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>IndexTTS2</DialogTitle>
|
<DialogTitle>情感控制</DialogTitle>
|
||||||
<DialogDescription>使用 IndexTTS2 合成,支持情感控制</DialogDescription>
|
<DialogDescription>使用 IndexTTS2 合成,选择情感预设</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 py-4">
|
<div className="space-y-4 py-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>情感参考文本(emo_text)</Label>
|
<Label>情感</Label>
|
||||||
<Textarea
|
<Select
|
||||||
placeholder="输入情感参考句,如:你吓死我了!(留空则不使用情感控制)"
|
value={selectedEmotion}
|
||||||
value={emoText}
|
onValueChange={(value) => {
|
||||||
onChange={e => setEmoText(e.target.value)}
|
const preset = EMOTION_PRESETS.find(p => p.value === value)
|
||||||
className="min-h-[60px]"
|
if (preset) {
|
||||||
/>
|
setSelectedEmotion(value)
|
||||||
|
setEmoText(preset.emo_text)
|
||||||
|
setEmoAlpha(preset.emo_alpha)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{EMOTION_PRESETS.map(p => (
|
||||||
|
<SelectItem key={p.value} value={p.value}>
|
||||||
|
{p.label}
|
||||||
|
{p.value !== 'none' && (
|
||||||
|
<span className="ml-2 text-xs text-muted-foreground">强度 {p.emo_alpha}</span>
|
||||||
|
)}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{selectedEmotion !== 'none' && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>情感强度(emo_alpha):{emoAlpha.toFixed(2)}</Label>
|
<Label>情感强度:{emoAlpha.toFixed(2)}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="range"
|
type="range"
|
||||||
min={0}
|
min={0}
|
||||||
@@ -583,7 +616,11 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
value={emoAlpha}
|
value={emoAlpha}
|
||||||
onChange={e => setEmoAlpha(parseFloat(e.target.value))}
|
onChange={e => setEmoAlpha(parseFloat(e.target.value))}
|
||||||
/>
|
/>
|
||||||
|
{(selectedEmotion === 'angry') && (
|
||||||
|
<p className="text-xs text-muted-foreground">愤怒情感建议强度低于 0.2,过高会失真</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="button" variant="outline" onClick={() => setIndexTTS2Open(false)}>取消</Button>
|
<Button type="button" variant="outline" onClick={() => setIndexTTS2Open(false)}>取消</Button>
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ const SelectContent = React.forwardRef<
|
|||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
"relative z-[200] max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
className
|
className
|
||||||
|
|||||||
Reference in New Issue
Block a user