Skip to content

Commit

Permalink
Merge branch 'main' into sounds-of-american-english-2
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaolai authored Mar 28, 2024
2 parents d039a51 + 211e1f5 commit 5922b6f
Show file tree
Hide file tree
Showing 29 changed files with 278 additions and 62 deletions.
32 changes: 27 additions & 5 deletions 1000-hours/enjoy-app/audios.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,26 @@ Enjoy 支持添加本地的音频资源,和在线资源。在音频页面,
如果第二步语音转文本失败,请检查是否在正在使用本地 whisper 组件进行语音转文本,在某些电脑上可能因为兼容性和未知问题导致无法使用。如果出现这种情况,请在 [语音转文本服务设置](./settings#语音转文本服务) 中改用其他语音转文本的云服务。
:::

## 播放

点击播放键(或者快捷键 <kbd>Space</kbd>)即可播放或者暂停音频。

Enjoy 会将音频按照句子切分,默认播放模式为“播放单句”,以便逐句反复听练。

其他可选的播放模式有:

- 单句循环
- 播放所有

## 智能断句

Enjoy 按照原音的停顿和标点符号,将当前句子分成几个断句,以便逐一反复练习。

你也可以通过点击当前句子的任意词语(或同时按下 <kbd>Shift</kbd> 进行多选)进行选中单词或者短语,进行听练。

## 录音

Enjoy 会将音频按照句子切分,用户以句子为单位进行跟读练习。在激活的音频句子下,点击下方的录音按钮,即可开始录音,用户可以模仿音频朗读当前句子作为练习。
Enjoy 会将音频按照句子切分,用户以句子为单位进行跟读练习。在激活的音频句子下,点击红色的录音按钮(或者快捷键 <kbd>r</kbd>),即可开始录音,用户可以模仿音频朗读当前句子作为练习。

![音频播放页面](/images/enjoy/audio-page.png)
_\* 音频播放页面_
Expand All @@ -36,9 +53,16 @@ _\* 音频播放页面_
在 Mac 电脑上,首次使用录音功能时,会弹窗请求麦克风的使用权限,请务必点允许,否则无法使用录音功能。
:::

## 录音对比

将录音的 Pitch contour 与原音对比,以便自纠发音。对比状态下,按下播放键,会同时播放录音和原音。

![录音对比](/images/enjoy/recording-comparing.png)
_\* 录音于原音对比_

## 发音评估

Enjoy 集成了微软 Azure 的发音评估功能,作为自我的发音检查参考。点击录音右下方的仪表盘图标,即可打开发音评估弹窗。
Enjoy 集成了微软 Azure 的发音评估功能,作为自我的发音检查参考。

该功能会以**录音时的句子文本作为参考**,评估录音的发音情况,各指标的详细说明可以参考微软的 [官方文档](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/how-to-pronunciation-assessment?pivots=programming-language-javascript#scripted-assessment-results)

Expand All @@ -48,9 +72,7 @@ _\* 发音评估示例_
::: warning 发音评估的使用建议
该功能是收费功能,每次使用均会在 Enjoy 账户的余额扣费,如果余额不足则需要 [充值](./settings#充值) 后才可继续使用。

根据实测,该功能对提高学习者的发音作用水平有限,其评估结果仅做参考,不应该过度依赖。

另外,我们正在开发一套更有效的发音评估体系,敬请期待。
值得注意的是,发音评估更侧重于单词的发音是否正确,无法评估音调变化是否正确。
:::

## 修改音频信息
Expand Down
23 changes: 23 additions & 0 deletions 1000-hours/enjoy-app/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 常见问题

## 为什么本地语音转文本服务无法使用?

Enjoy 集成了 [whipser.cpp](https://github.com/ggerganov/whisper.cpp) 作为本地的语音转文本(STT)服务,但是由于兼容性的问题,某些配置较低或者操作系统版本较低的电脑无法使用。

如果您遇到这种情况,Enjoy 提供了其他 STT 的云服务,可以前往 [软件设置](./settings#语音转文本服务) 进行配置。推荐优先使用 Azure AI。

## 403 Insufficient balance

遇到这个报错,说明您正在使用 Enjoy 的付费功能,但是账户余额不足了。

Enjoy 内有很多功能都由 AI 驱动,比如[智能助手](./ai-assistant)、智能翻译、句子分析等。如果您在 [软件设置](./settings#默认-ai-引擎) 中的配置了 `OpenAI` 作为默认 AI 引擎,在使用这些功能时候,会使用您配置的 OpenAI 信息进行实现,不会涉及 Enjoy 的扣费。

(需要注意的是,[智能助手](./ai-assistant) 的对话一旦创建,AI 引擎无法修改。如果需要切换,比如由 Enjoy AI 换成 Open AI,则需要新建一个对话)

另外,[发音评估](./audios#发音评估) 是收费功能,并非 OpenAI 提供,所以无论 [默认 AI 引擎](./settings#默认-ai-引擎) 选了什么,使用发音评估时,总是会在 Enjoy 账户中扣费。

如果需要充值,请参考[充值](./settings#充值)

## 如何下载音频、录音

Enjoy 提供了音频、视频、录音的下载功能,以便可以在其他设备使用。
12 changes: 6 additions & 6 deletions 1000-hours/enjoy-app/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ Enjoy App 是一个跨平台的桌面应用,可以在 Windows、Mac 和 Linux

请根据电脑设备的操作系统,下载相应的版本安装使用。

当前最新版本:[v0.1.0-alpha.13](https://github.com/xiaolai/everyone-can-use-english/releases/latest)
当前最新版本:[v0.2.0](https://github.com/xiaolai/everyone-can-use-english/releases/latest)

## Windows

支持 Windows 10 以上版本。

[点击下载](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-Setup.exe)
[点击下载](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0.Setup.exe)

下载后,双击即可安装。

## Mac

根据使用 Mac 电脑的芯片不同,需要下载不同的版本。

- [Silicon 芯片版本(arm64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-arm64.dmg)
- [Intel 芯片版本(x64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-x64.dmg)
- [Silicon 芯片版本(arm64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0-arm64.dmg)
- [Intel 芯片版本(x64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0-x64.dmg)

::: info 如何查看本机配置
M1 以后的 Mac 电脑型号(M1、M2、M3),均为 Silicon 芯片。
Expand All @@ -37,8 +37,8 @@ M1 以后的 Mac 电脑型号(M1、M2、M3),均为 Silicon 芯片。

请根据不同发行版本选用安装文件。

- [下载 deb 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/enjoy_0.1.0-alpha.13_amd64.deb)
- [下载 rpm 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/enjoy-0.1.0.alpha.13-1.x86_64.rpm)
- [下载 deb 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/enjoy_0.2.0_amd64.deb)
- [下载 rpm 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-linux-x64-0.2.0.zip)

## 历史版本

Expand Down
2 changes: 1 addition & 1 deletion 1000-hours/enjoy-app/use-case-generate-audio-resources.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 利用 AI 生成训练材料
# 使用案例:利用 AI 生成训练材料

用外语说我们自己想说的话是学习外语的其中一个重要目的。市面上的口语书实际上并不实用,因为那些话大都不是我们想要说的,我们需要创建专属自己的口语书。使用 Enjoy 可以很容易做到这一点。

Expand Down
4 changes: 0 additions & 4 deletions 1000-hours/enjoy-app/videos.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
![视频资源播放页面](/images/enjoy/video-page.png)
_\* 视频播放页面_

## 录音

在视频播放页面,需要录音时,可以点播放控制栏上的录音图标,视频将会被缩小至小窗,主界面上会显示录音按钮。

## 视频大小的限制

过大的视频文件会导致加载卡死而无法使用,目前 Enjoy 将添加视频的大小限制在 100 Mb,超过则会提示添加失败。
Expand Down
Binary file modified 1000-hours/public/images/enjoy/audio-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 1000-hours/public/images/enjoy/select-ai-role.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 1000-hours/public/images/enjoy/video-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions 1000-hours/training-tasks/procedures.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 2. 训练方法

请后继反复认真阅读[语音塑造](/sounds-of-english/01-basics)》中的每一个章节。
请后续反复认真阅读[语音塑造](/sounds-of-english/01-basics)》中的每一个章节。

## 2.1 搞清每个单词的读音

Expand Down Expand Up @@ -61,7 +61,7 @@

自然语音中有**可换气停顿**的地方,就相当于是乐谱里的小节分界线。分段练习,实在难的小节,还可以继续拆分……

在跟读的时候,有必要**夸张**一点。声音也要正常地**** —— 确实不用喊,嗓子的确需要保护。实际上,我们主要需要练的是嘴唇、舌头、气流震动的配合,主要练的还真不是声带。
在跟读的时候,有必要**夸张**一点。声音也要正常地**** —— 确实不用喊,嗓子的确需要保护。实际上,我们主要需要练的是嘴唇、舌头、气流振动的配合,主要练的还真不是声带。

跟读的时候,可以戴着耳机。一个比较好的方法是戴单只耳机 —— 这样,一方面录音听得更清楚,另外一方面也不妨碍听清自己的声音,还可以换着耳朵戴。

Expand Down Expand Up @@ -119,4 +119,4 @@ TED 上有一个讲座,Benjamin Zander 讲 [The transformative power of classi

一天至少三个小时,每周最多可以中止一天。

每天结束之前,要花 5~10 分钟做一下**复盘**,回忆并记录一下自己今天遇到的困难、已经克服的困难、尚未克服的困难、以及面对那些困难时所采用的方法…… 想一想还有什么方法可以试试?这些都要写下来。相信我,记录这个东西 ,总是会以想象不到的方式起想象不到的作用。
每天结束之前,要花 5~10 分钟做一下**复盘**,回忆并记录一下自己今天遇到的困难、已经克服的困难、尚未克服的困难、以及面对那些困难时所采用的方法…… 想一想还有什么方法可以试试?这些都要写下来。相信我,记录这个东西 ,总是会以想象不到的方式起想象不到的作用。
2 changes: 1 addition & 1 deletion book/chapter2.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* [GRE的Issue题目](files/GRE-Analytical-Writing-Argument-Task-Topics.md)
* [GRE的Argument题目](files/GRE-Analytical-Writing-Issue-Task-Topics)

在相当长一段时间里,我以为自己 “发现” 了一条真正的捷径,后来才发现 “我并不孤独”。在国内,卖得最好的口语教材,实际上是北京外国语大学的专业教材,作者是北外教授吴桢福老师:一共三本,《英语初级口语》、《英语中级口语》、《英语高级口语》,多年来多次再版,印数均超过 100 万本。如果读者有机会,不妨去书店翻翻这套教材──你会发现其中的大多数课文话题,实际上也许都是从 TOEFL 作文题脱胎而来的。
在相当长一段时间里,我以为自己 “发现” 了一条真正的捷径,后来才发现 “我并不孤独”。在国内,卖得最好的口语教材,实际上是北京外国语大学的专业教材,作者是北外教授吴祯福老师:一共三本,《英语初级口语》、《英语中级口语》、《英语高级口语》,多年来多次再版,印数均超过 100 万本。如果读者有机会,不妨去书店翻翻这套教材──你会发现其中的大多数课文话题,实际上也许都是从 TOEFL 作文题脱胎而来的。

请允许我重复一遍:**你的问题也许不在于你不会说,而在于你没什么话可说。**

Expand Down
5 changes: 4 additions & 1 deletion enjoy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"name": "enjoy",
"productName": "Enjoy",
"version": "0.2.0-preview",
"version": "0.2.0",
"description": "Enjoy desktop app",
"main": ".vite/build/main.js",
"types": "./src/types.d.ts",
Expand Down Expand Up @@ -110,8 +110,11 @@
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@sentry/electron": "^4.21.0",
"@types/ahoy.js": "^0.4.2",
"@uidotdev/usehooks": "^2.4.1",
"@vidstack/react": "^1.10.9",
"ahoy.js": "^0.4.3",
"autosize": "^6.0.1",
"axios": "^1.6.8",
"camelcase": "^8.0.0",
Expand Down
26 changes: 10 additions & 16 deletions enjoy/src/commands/analyze.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,26 @@ export const analyzeCommand = async (
},
cache: false,
verbose: true,
maxRetries: 2,
});

const prompt = ChatPromptTemplate.fromMessages([
["system", SYSTEM_PROMPT],
["human", TRANSLATION_PROMPT],
["human", text],
]);

const response = await prompt.pipe(chatModel).invoke({
native_language: "Chinese",
text,
});
const response = await prompt.pipe(chatModel).invoke({});

return response.text;
};

const SYSTEM_PROMPT = `You are a language coach of English, and you are helping a student to learn {native_language}.`;
const TRANSLATION_PROMPT = `
{text}
Please analyze the text above, including sentence structure, grammar, and vocabulary/phrases, and provide a detailed explanation of the text. Please reply in {native_language} and return the result only in the following format:
const SYSTEM_PROMPT = `你是我的英语教练,我将提供英语文本,你将帮助我分析文本的句子结构、语法和词汇/短语,并对文本进行详细解释。请用中文回答,并按以下格式返回结果:
### Sentence structure
(explain every element of the sentence)
### 句子结构
(解释句子的每个元素)
### Grammar
(explain the grammar of the sentence)
### 语法
(解释句子的语法)
### Vocabulary/phrases
(explain the key vocabulary and phrases used)`;
### 词汇/短语
(解释使用的关键词汇和短语)`;
1 change: 1 addition & 0 deletions enjoy/src/commands/extract-story.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const extractStoryCommand = async (
},
cache: true,
verbose: true,
maxRetries: 2,
}).bind({
tools: [
{
Expand Down
1 change: 1 addition & 0 deletions enjoy/src/commands/ipa.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const ipaCommand = async (
},
cache: true,
verbose: true,
maxRetries: 2,
});

const prompt = ChatPromptTemplate.fromMessages([
Expand Down
1 change: 1 addition & 0 deletions enjoy/src/commands/lookup.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const lookupCommand = async (
},
cache: true,
verbose: true,
maxRetries: 2,
});

const prompt = ChatPromptTemplate.fromMessages([
Expand Down
1 change: 1 addition & 0 deletions enjoy/src/commands/translate.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const translateCommand = async (
},
cache: false,
verbose: true,
maxRetries: 2,
});

const prompt = ChatPromptTemplate.fromMessages([
Expand Down
2 changes: 2 additions & 0 deletions enjoy/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const WEB_API_URL = "https://enjoy-web.fly.dev";

export const REPO_URL = "https://github.com/xiaolai/everyone-can-use-english";

export const SENTRY_DSN = "https://d51056d7af7d14eae446c0c15b4f3d31@o1168905.ingest.us.sentry.io/4506969353289728"

export const MAGIC_TOKEN_REGEX =
/\b(Mrs|Ms|Mr|Dr|Prof|St|[a-zA-Z]{1,2}|\d{1,2})\.\b/g;
export const END_OF_SENTENCE_REGEX = /[^\.!,\?][\.!\?]/g;
Expand Down
6 changes: 6 additions & 0 deletions enjoy/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import mainWindow from "@main/window";
import ElectronSquirrelStartup from "electron-squirrel-startup";
import contextMenu from "electron-context-menu";
import { t } from "i18next";
import * as Sentry from "@sentry/electron";
import { SENTRY_DSN } from "@/constants";

Sentry.init({
dsn: SENTRY_DSN,
});

app.commandLine.appendSwitch("enable-features", "SharedArrayBuffer");

Expand Down
6 changes: 6 additions & 0 deletions enjoy/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@

import "./index.css";
import "./renderer/index";
import * as Sentry from "@sentry/electron";
import { SENTRY_DSN } from "@/constants";

Sentry.init({
dsn: SENTRY_DSN,
});

declare global {
interface Window {
Expand Down
4 changes: 2 additions & 2 deletions enjoy/src/renderer/components/medias/media-caption-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ const TranslationTabContent = (props: { text: string }) => {
setTranslation(result);
}
})
.catch((err) => t("translationFailed", { error: err.message }))
.catch((err) => toast.error(err.message))
.finally(() => {
setTranslating(false);
});
Expand Down Expand Up @@ -422,7 +422,7 @@ const AnalysisTabContent = (props: { text: string }) => {
setAnalysisResult(result);
}
})
.catch((err) => t("analysisFailed", { error: err.message }))
.catch((err) => toast.error(err.message))
.finally(() => {
setAnalyzing(false);
});
Expand Down
21 changes: 13 additions & 8 deletions enjoy/src/renderer/components/medias/media-current-recording.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => {

const calContainerWidth = () => {
const w = document
.querySelector(".media-recording-container")
.querySelector(".media-recording-wrapper")
?.getBoundingClientRect()?.width;
if (!w) return;

Expand Down Expand Up @@ -371,10 +371,15 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
]);

useEffect(() => {
if (!ref?.current) return;
if (!width) return;

ref.current.style.width = `${width}px`;
}, [width]);
const container: HTMLDivElement = document.querySelector(
".media-recording-container"
);
if (!container) return;

container.style.width = `${width}px`;
}, [width, currentRecording, isRecording]);

useEffect(() => {
calContainerWidth();
Expand All @@ -387,7 +392,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
calContainerWidth();
});
};
}, []);
}, [currentRecording, isRecording]);

useHotkeys(
["Ctrl+R", "Meta+R"],
Expand Down Expand Up @@ -428,9 +433,9 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
);

return (
<div className="flex space-x-4 media-recording-container">
<div className="border rounded-xl shadow-lg flex-1 relative">
<div ref={ref}></div>
<div className="flex space-x-4 media-recording-wrapper">
<div className="border rounded-xl shadow-lg flex-1 relative media-recording-container">
<div className="w-full" ref={ref}></div>

<div className="absolute right-2 top-1">
<span className="text-sm">{formatDuration(currentTime || 0)}</span>
Expand Down
Loading

0 comments on commit 5922b6f

Please sign in to comment.