This commit is contained in:
zhihao li
2023-04-10 22:11:49 -07:00
parent 0f739f442e
commit 8934e6dc93
11 changed files with 257 additions and 12 deletions

27
app/api/tts.ts Normal file
View File

@@ -0,0 +1,27 @@
import {
AudioConfig,
SpeechConfig,
SpeechSynthesizer,
SpeakerAudioDestination,
SpeechSynthesisResult,
} from "microsoft-cognitiveservices-speech-sdk";
import { getLang } from "../locales";
const SUB_KEY = process.env.SPEECH_SUB_KEY ?? "";
const REGION = process.env.SPEECH_REGION ?? "";
export async function speak(
text: string,
callback: ((e: SpeechSynthesisResult) => void) | undefined,
) {
if (!text) return;
const player = new SpeakerAudioDestination();
const audioConfig = AudioConfig.fromSpeakerOutput(player);
const speechConfig = SpeechConfig.fromSubscription(SUB_KEY, REGION);
speechConfig.speechSynthesisVoiceName =
getLang() === "cn" ? "zh-CN-XiaoxiaoNeural" : "en-US-AriaNeural";
const synthesizer = new SpeechSynthesizer(speechConfig, audioConfig);
synthesizer.speakTextAsync(text, callback);
player.resume();
}

View File

@@ -44,6 +44,7 @@ import styles from "./home.module.scss";
import chatStyle from "./chat.module.scss";
import { Input, Modal, showModal } from "./ui-lib";
import { speak } from "../api/tts";
const Markdown = dynamic(
async () => memo((await import("./markdown")).Markdown),
@@ -490,6 +491,13 @@ export function Chat(props: {
const accessStore = useAccessStore();
const callback = (result: any) => {
const audio = new Audio();
audio.src = URL.createObjectURL(result.audioData);
audio.volume = 0.5;
audio.play();
};
if (
context.length === 0 &&
session.messages.at(0)?.content !== BOT_HELLO.content
@@ -670,6 +678,17 @@ export function Chat(props: {
>
{Locale.Chat.Actions.Copy}
</div>
<div
className={styles["chat-message-top-action"]}
onClick={() =>
accessStore.isAuthorized()
? speak(message.content, callback)
: () => {}
}
>
{Locale.Chat.Actions.Speak}
</div>
</div>
)}
{(message.preview || message.content.length === 0) &&

View File

@@ -7,6 +7,8 @@ declare global {
CODE?: string;
PROXY_URL?: string;
VERCEL?: string;
SPEECH_SUB_KEY?: string;
SPEECH_REGION?: string;
}
}
}

View File

@@ -17,6 +17,7 @@ const cn = {
Copy: "复制",
Stop: "停止",
Retry: "重试",
Speak: "朗读",
},
Rename: "重命名对话",
Typing: "正在输入…",

View File

@@ -19,6 +19,7 @@ const en: LocaleType = {
Copy: "Copy",
Stop: "Stop",
Retry: "Retry",
Speak: "Speak",
},
Rename: "Rename Chat",
Typing: "Typing…",

View File

@@ -19,6 +19,7 @@ const es: LocaleType = {
Copy: "Copiar",
Stop: "Detener",
Retry: "Reintentar",
Speak: "Leer",
},
Rename: "Renombrar chat",
Typing: "Escribiendo...",

View File

@@ -19,6 +19,7 @@ const it: LocaleType = {
Copy: "Copia",
Stop: "Stop",
Retry: "Riprova",
Speak: "Leggi",
},
Rename: "Rinomina Chat",
Typing: "Typing…",

View File

@@ -19,6 +19,7 @@ const tr: LocaleType = {
Copy: "Kopyala",
Stop: "Durdur",
Retry: "Tekrar Dene",
Speak: "Oku",
},
Rename: "Sohbeti Yeniden Adlandır",
Typing: "Yazıyor…",
@@ -136,11 +137,13 @@ const tr: LocaleType = {
Model: "Model",
Temperature: {
Title: "Gerçeklik",
SubTitle: "Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir",
SubTitle:
"Daha büyük bir değer girildiğinde gerçeklik oranı düşer ve daha rastgele çıktılar üretir",
},
MaxTokens: {
Title: "Maksimum Belirteç",
SubTitle: "Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu",
SubTitle:
"Girdi belirteçlerinin ve oluşturulan belirteçlerin maksimum uzunluğu",
},
PresencePenlty: {
Title: "Varlık Cezası",
@@ -161,7 +164,8 @@ const tr: LocaleType = {
Summarize:
"Gelecekteki bağlam için bir bilgi istemi olarak kullanmak üzere tartışmamızı en fazla 200 kelimeyle özetleyin.",
},
ConfirmClearAll: "Tüm sohbet ve ayar verilerini temizlemeyi onaylıyor musunuz?",
ConfirmClearAll:
"Tüm sohbet ve ayar verilerini temizlemeyi onaylıyor musunuz?",
},
Copy: {
Success: "Panoya kopyalandı",

View File

@@ -18,6 +18,7 @@ const tw: LocaleType = {
Copy: "複製",
Stop: "停止",
Retry: "重試",
Speak: "朗讀",
},
Rename: "重命名對話",
Typing: "正在輸入…",