Skip to content

Commit

Permalink
✨ feat: 优化语音设置样式
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmclin2 committed May 19, 2024
1 parent 6ee5043 commit 000183c
Show file tree
Hide file tree
Showing 11 changed files with 375 additions and 241 deletions.
27 changes: 27 additions & 0 deletions src/constants/tts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,30 @@ export const DEFAULT_TTS_CONFIG: TTS = {
speed: 1,
voice: 'zh-CN-XiaoxiaoNeural',
};
export const supportedLocales = [
{
label: '中文(普通话)',
sample: '正在为你准备我的整个世界',
value: 'zh-CN',
},
{
label: '日语(日本)',
sample: 'あなたのために私の全世界を準備しています',
value: 'ja-JP',
},
{
label: '英语(美国)',
sample: "I'm preparing my whole world for you.",
value: 'en-US',
},
{
label: '韩语(韩国)',
sample: '당신을 위해 내 전 세계를 준비하고 있습니다.',
value: 'ko-KR',
},
{
label: '中文(粤语)',
sample: '正在为您准备我的整个世界',
value: 'zh-HK',
},
];
35 changes: 35 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSEngine/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Select } from 'antd';
import React, { CSSProperties, memo } from 'react';

import { agentSelectors, useAgentStore } from '@/store/agent';
import { TTS_ENGINE } from '@/types/tts';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const [tts, updateAgentTTS] = useAgentStore((s) => [
agentSelectors.currentAgentTTS(s),
s.updateAgentTTS,
]);

return (
<Select
className={className}
style={style}
value={tts?.engine}
options={[
{
label: 'Edge',
value: 'edge',
},
]}
onChange={(value) => {
updateAgentTTS({ engine: value as TTS_ENGINE });
}}
/>
);
});
30 changes: 30 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSLocale/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Select } from 'antd';
import React, { CSSProperties, memo } from 'react';

import { supportedLocales } from '@/constants/tts';
import { agentSelectors, useAgentStore } from '@/store/agent';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const [tts, updateAgentTTS] = useAgentStore((s) => [
agentSelectors.currentAgentTTS(s),
s.updateAgentTTS,
]);

return (
<Select
className={className}
style={style}
value={tts?.locale}
options={supportedLocales}
onChange={(value) => {
updateAgentTTS({ locale: value });
}}
/>
);
});
31 changes: 31 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSPitch/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Slider } from 'antd';
import React, { CSSProperties, memo } from 'react';

import { agentSelectors, useAgentStore } from '@/store/agent';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const [tts, updateAgentTTS] = useAgentStore((s) => [
agentSelectors.currentAgentTTS(s),
s.updateAgentTTS,
]);

return (
<Slider
className={className}
style={style}
value={tts?.pitch}
max={2}
min={0}
step={0.01}
onChange={(value) => {
updateAgentTTS({ pitch: value });
}}
/>
);
});
70 changes: 70 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSPlay/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { PlayCircleOutlined } from '@ant-design/icons';
import { useRequest } from 'ahooks';
import { Button, message } from 'antd';
import React, { CSSProperties, memo, useRef } from 'react';

import { supportedLocales } from '@/constants/tts';
import { speechApi } from '@/services/tts';
import { agentSelectors, useAgentStore } from '@/store/agent';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const ref = useRef<HTMLAudioElement>(null);

const tts = useAgentStore((s) => agentSelectors.currentAgentTTS(s));
const sample = supportedLocales.find((item) => item.value === tts?.locale)?.sample;

const { loading, run: speek } = useRequest(speechApi, {
manual: true,
onError: (err) => {
message.error(err.message);
if (ref.current) {
ref.current.pause();
ref.current.currentTime = 0;
ref.current.src = '';
}
},
onSuccess: (res) => {
message.success('转换成功');
const adUrl = URL.createObjectURL(new Blob([res]));
if (ref.current) {
ref.current.src = adUrl;
ref.current.play();
}
},
});

return (
<>
<Button
htmlType="button"
type={'primary'}
style={style}
className={className}
icon={<PlayCircleOutlined />}
loading={loading}
onClick={() => {
if (!tts?.locale) {
message.error('请先选择语言');
return;
}
if (!tts?.voice) {
message.error('请先选择语音');
return;
}
if (sample) {
speek({ ...tts, message: sample });
}
}}
>
试听
</Button>
<audio ref={ref} />
</>
);
});
31 changes: 31 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSSpeed/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Slider } from 'antd';
import React, { CSSProperties, memo } from 'react';

import { agentSelectors, useAgentStore } from '@/store/agent';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const [tts, updateAgentTTS] = useAgentStore((s) => [
agentSelectors.currentAgentTTS(s),
s.updateAgentTTS,
]);

return (
<Slider
className={className}
style={style}
value={tts?.speed}
max={3}
min={0}
step={0.01}
onChange={(value) => {
updateAgentTTS({ speed: value });
}}
/>
);
});
67 changes: 67 additions & 0 deletions src/panels/RolePanel/RoleEdit/Voice/TTSVoice/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useRequest } from 'ahooks';
import { Select } from 'antd';
import { isEqual } from 'lodash-es';
import React, { CSSProperties, memo, useEffect, useState } from 'react';

import { voiceListApi } from '@/services/tts';
import { agentSelectors, useAgentStore } from '@/store/agent';
import { Voice } from '@/types/tts';

interface Props {
className?: string;
style?: CSSProperties;
}

export default memo<Props>((props) => {
const { style, className } = props;
const [voices, setVoices] = useState<Voice[]>([]);

const [tts, updateAgentTTS] = useAgentStore(
(s) => [agentSelectors.currentAgentTTS(s), s.updateAgentTTS],
isEqual,
);

const { loading: voiceLoading } = useRequest(
() => {
if (!tts?.engine) {
return Promise.resolve({ data: [] });
}
return voiceListApi(tts?.engine);
},
{
onSuccess: (res) => {
setVoices(res.data);
},
refreshDeps: [tts?.engine],
},
);

useEffect(() => {
if (!tts?.locale) {
return;
}
const voice = voices.find((voice) => voice.locale === tts.locale);
if (voice) {
updateAgentTTS({ voice: voice.ShortName });
}
}, [tts?.locale, tts?.engine]);

return (
<Select
className={className}
style={style}
value={tts?.voice}
disabled={voiceLoading}
loading={voiceLoading}
options={voices
.filter((voice) => voice.locale === tts?.locale)
.map((item) => ({
label: `${item.DisplayName}-${item.LocalName}`,
value: item.ShortName,
}))}
onChange={(value) => {
updateAgentTTS({ voice: value });
}}
/>
);
});
Loading

0 comments on commit 000183c

Please sign in to comment.