feat: Add emotion control feature with presets and intensity adjustment in CustomVoiceForm

This commit is contained in:
2026-03-12 14:10:17 +08:00
parent c79ffac6d9
commit 649a52c07b
2 changed files with 59 additions and 22 deletions

View File

@@ -63,8 +63,21 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
const [isLoading, setIsLoading] = useState(false)
const [advancedOpen, setAdvancedOpen] = useState(false)
const [indexTTS2Open, setIndexTTS2Open] = useState(false)
const [selectedEmotion, setSelectedEmotion] = useState('none')
const [emoText, setEmoText] = useState('')
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.6 },
{ value: 'fear', label: '恐惧', emo_text: '恐惧', emo_alpha: 0.6 },
{ 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 [tempAdvancedParams, setTempAdvancedParams] = useState({
max_new_tokens: 2048,
@@ -555,26 +568,46 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
<DialogTrigger asChild>
<Button type="button" variant="outline" className="w-full">
<Zap className="mr-2 h-4 w-4" />
IndexTTS2
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[480px]">
<DialogHeader>
<DialogTitle>IndexTTS2</DialogTitle>
<DialogDescription>使 IndexTTS2 </DialogDescription>
<DialogTitle></DialogTitle>
<DialogDescription>使 IndexTTS2 </DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>emo_text</Label>
<Textarea
placeholder="输入情感参考句,如:你吓死我了!(留空则不使用情感控制)"
value={emoText}
onChange={e => setEmoText(e.target.value)}
className="min-h-[60px]"
/>
<Label></Label>
<Select
value={selectedEmotion}
onValueChange={(value) => {
const preset = EMOTION_PRESETS.find(p => p.value === value)
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>
{selectedEmotion !== 'none' && (
<div className="space-y-2">
<Label>emo_alpha{emoAlpha.toFixed(2)}</Label>
<Label>{emoAlpha.toFixed(2)}</Label>
<Input
type="range"
min={0}
@@ -583,7 +616,11 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
value={emoAlpha}
onChange={e => setEmoAlpha(parseFloat(e.target.value))}
/>
{(selectedEmotion === 'angry') && (
<p className="text-xs text-muted-foreground"> 0.2</p>
)}
</div>
)}
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setIndexTTS2Open(false)}></Button>

View File

@@ -73,7 +73,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
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" &&
"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