diff --git a/1000-hours/.vitepress/config.mts b/1000-hours/.vitepress/config.mts
index 8455ae49..4a8155c9 100644
--- a/1000-hours/.vitepress/config.mts
+++ b/1000-hours/.vitepress/config.mts
@@ -226,8 +226,12 @@ export default withMermaid(
link: "/sounds-of-american-english/4.3-group",
},
{
- text: "4.4. 句子",
- link: "/sounds-of-american-english/4.4-sentences",
+ text: "4.4. 连接",
+ link: "/sounds-of-american-english/4.4-linking",
+ },
+ {
+ text: "4.5. 句子",
+ link: "/sounds-of-american-english/4.5-sentences",
},
],
},
diff --git a/1000-hours/public/audios/A-woman,-without-her,-man-is-nothing.-nova.mp3 b/1000-hours/public/audios/A-woman,-without-her,-man-is-nothing.-nova.mp3
new file mode 100644
index 00000000..ebb23cc1
Binary files /dev/null and b/1000-hours/public/audios/A-woman,-without-her,-man-is-nothing.-nova.mp3 differ
diff --git a/1000-hours/public/audios/A-woman-without-her-man-is-nothing.-alloy.mp3 b/1000-hours/public/audios/A-woman-without-her-man-is-nothing.-alloy.mp3
new file mode 100644
index 00000000..817f21a6
Binary files /dev/null and b/1000-hours/public/audios/A-woman-without-her-man-is-nothing.-alloy.mp3 differ
diff --git a/1000-hours/public/audios/Let's-eat-kids.-alloy.mp3 b/1000-hours/public/audios/Let's-eat-kids.-alloy.mp3
new file mode 100644
index 00000000..13a8af0e
Binary files /dev/null and b/1000-hours/public/audios/Let's-eat-kids.-alloy.mp3 differ
diff --git a/1000-hours/public/audios/Let's-eat...-kids.-alloy.mp3 b/1000-hours/public/audios/Let's-eat...-kids.-alloy.mp3
new file mode 100644
index 00000000..1756e829
Binary files /dev/null and b/1000-hours/public/audios/Let's-eat...-kids.-alloy.mp3 differ
diff --git a/1000-hours/public/audios/hotdog-us-female.mp3 b/1000-hours/public/audios/hotdog-us-female.mp3
new file mode 100644
index 00000000..cd86c34f
Binary files /dev/null and b/1000-hours/public/audios/hotdog-us-female.mp3 differ
diff --git a/1000-hours/public/audios/hotdog-us-male.mp3 b/1000-hours/public/audios/hotdog-us-male.mp3
new file mode 100644
index 00000000..4080f0d9
Binary files /dev/null and b/1000-hours/public/audios/hotdog-us-male.mp3 differ
diff --git a/1000-hours/public/audios/network-us-female.mp3 b/1000-hours/public/audios/network-us-female.mp3
new file mode 100644
index 00000000..a55c3a78
Binary files /dev/null and b/1000-hours/public/audios/network-us-female.mp3 differ
diff --git a/1000-hours/public/audios/network-us-male.mp3 b/1000-hours/public/audios/network-us-male.mp3
new file mode 100644
index 00000000..0cc67617
Binary files /dev/null and b/1000-hours/public/audios/network-us-male.mp3 differ
diff --git a/1000-hours/public/jupyter-notebooks/edge-tts-valcab-pronounciation.ipynb b/1000-hours/public/jupyter-notebooks/edge-tts-valcab-pronounciation.ipynb
index 5ca36b08..3d5e8c7e 100644
--- a/1000-hours/public/jupyter-notebooks/edge-tts-valcab-pronounciation.ipynb
+++ b/1000-hours/public/jupyter-notebooks/edge-tts-valcab-pronounciation.ipynb
@@ -29,7 +29,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": 2,
"id": "71d35cd9",
"metadata": {},
"outputs": [
@@ -37,19 +37,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "['en-US-GuyNeural', 'en-US-AriaNeural']\n",
- "hello\n",
- "../audios/hello-us-male.mp3 created\n",
- "../audios/hello-us-female.mp3 created\n",
- "\n",
- "heat\n",
- "../audios/heat-us-male.mp3 created\n",
- "../audios/heat-us-female.mp3 created\n",
- "\n",
- "high\n",
- "../audios/high-us-male.mp3 created\n",
- "../audios/high-us-female.mp3 created\n",
- "\n"
+ "pygame 2.6.0 (SDL 2.28.4, Python 3.12.2)\n",
+ "Hello from the pygame community. https://www.pygame.org/contribute.html\n"
]
}
],
@@ -84,10 +73,34 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"id": "4146f92e",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "['en-US-GuyNeural', 'en-US-AriaNeural']\n",
+ "apple\n",
+ "../audios/apple-us-male.mp3 exists, skipping...\n",
+ "../audios/apple-us-female.mp3 exists, skipping...\n",
+ "\n",
+ "impossible\n",
+ "../audios/impossible-us-male.mp3 exists, skipping...\n",
+ "../audios/impossible-us-female.mp3 exists, skipping...\n",
+ "\n",
+ "possible\n",
+ "../audios/possible-us-male.mp3 created\n",
+ "../audios/possible-us-female.mp3 created\n",
+ "\n",
+ "ichthyosaur\n",
+ "../audios/ichthyosaur-us-male.mp3 created\n",
+ "../audios/ichthyosaur-us-female.mp3 created\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"\n",
"voices = [\"en-US-GuyNeural\", \"en-US-AriaNeural\", \"en-GB-RyanNeural\", \"en-GB-LibbyNeural\"]\n",
@@ -101,9 +114,11 @@
" print(voices)\n",
"\n",
"words = \"\"\"\n",
- "hello,\n",
- "heat,\n",
- "high,\n",
+ "apple,\n",
+ "impossible,\n",
+ "possible,\n",
+ "ichthyosaur,\n",
+ "\n",
"\"\"\"\n",
"\n",
"for word in words.strip().split(','):\n",
@@ -117,10 +132,18 @@
},
{
"cell_type": "code",
- "execution_count": 63,
+ "execution_count": 15,
"id": "2d46cde4",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "a-woman--without-her--man-is-nothing_openai.mp3\n"
+ ]
+ }
+ ],
"source": [
"def get_openai_tts_audio(text, path, performer='alloy'):\n",
" \n",
@@ -137,12 +160,14 @@
" ) as response:\n",
" response.stream_to_file(path)\n",
" \n",
- "sentence = \"The explanation you gave was clear but I need a more detailed explanation.\"\n",
+ "sentence = \"A woman..., without her..., man is nothing.\"\n",
"\n",
"# remove all punctuation at the end of sentence,\n",
"# replace all spaces and punctuations in the sentence with dash\n",
- "audio_filename_openai = sentence.translate(str.maketrans(' ,.?!', '-----')).strip().replace(\"--\", \"-\") + '_openai.mp3'\n",
- "audio_filename_msedge = sentence.translate(str.maketrans(' ,.?!', '-----')).strip().replace(\"--\", \"-\") + '_msedge.mp3'\n",
+ "audio_filename_openai = sentence.strip().translate(str.maketrans(',.?! ', '-----')).replace(\"--\", \"-\").lower().rstrip('-') + '_openai.mp3'\n",
+ "audio_filename_msedge = sentence.strip().translate(str.maketrans(',.?! ', '-----')).replace(\"--\", \"-\").lower().rstrip('-') + '_msedge.mp3'\n",
+ "\n",
+ "print(audio_filename_openai)\n",
"# get_openai_tts_audio(sentence, audio_filename_openai, performer='alloy')\n",
"# await generate_edge_tts_audio(sentence, audio_filename_msedge, voice=\"en-US-GuyNeural\", verbose=True, overwrite=True, play=True)\n",
"\n",
diff --git a/1000-hours/public/videos/yet-it-is-a-fact-of-life.mp4 b/1000-hours/public/videos/yet-it-is-a-fact-of-life.mp4
index 9c6cde72..3c630e24 100644
Binary files a/1000-hours/public/videos/yet-it-is-a-fact-of-life.mp4 and b/1000-hours/public/videos/yet-it-is-a-fact-of-life.mp4 differ
diff --git a/1000-hours/sounds-of-american-english/4.3-group.md b/1000-hours/sounds-of-american-english/4.3-group.md
index dfea2575..97bb9a61 100644
--- a/1000-hours/sounds-of-american-english/4.3-group.md
+++ b/1000-hours/sounds-of-american-english/4.3-group.md
@@ -1,4 +1,71 @@
# 4.2. 意群
-在自然语流中,很多句子并不是 “一口气” 读完的 —— 句子常常会被分割为若干个**意群**。
+在自然语流中,很多句子并不是 “一口气” 读完的……
+## 4.2.1. 意群
+
+**句子**,只要略微长一点,就可能会被分割为若干个**意群**。
+
+先听听以下视频里的这段话,同时观察一下语音的 “波谱”(*waveform*)及其 “音高变化”(*pitch contour*):
+
+> Yet, it is a fact of life that an unlettered peasant is considered ignorant.[^1]
+>
+
+从波谱可以清晰地看到,这一整句话,显然被分成了 3 个**意群**:
+
+> * Yet,
+> * it is a fact of life
+> * that an unlettered peasant is considered ignorant.
+
+> [!Note]
+> 这个视频截取自 [Enjoy App](https://1000h.org/enjoy-app/)。Enjoy 是一个英语学习工具(及其社区)。我们可以用它分析任何语音文件,清楚地看到 “停顿” 或者 “声调” 之类过去我们仅用听力难以完整分别的细节……
+
+事实上,如果你反复多听几遍,你会发现,*peasant* 这个词,ˈpez.ənt 是这样读出来的:ˈpez.ən 之后有个很细微的停顿(stop),而后 t 与后面的 ɪz 连了起来…… 本质上,这是因为这个 t 的舌尖动作完整,然后有个停顿,而后接着说出了后面的 ɪz,所以它们才听起来被连起来了。
+
+无论是**暂停**(pause)还是**停顿**(stop),虽然它们没有声音,却都在自然语流中都起着重要的作用。
+
+为了将来的说明清楚与方便,我们会将意群之间的语流空隙称为 “**停**”(或者 “**暂停**”,即,*pause*),而把词汇内音节之间的语流空隙称为 “**顿**”(或者 “**停顿**”,即,*stop*)。
+
+> * **停**(**暂停**/*pause*):发生在意群之间,语流空隙时段较长,可以用来换气,不会产生 “连读” 现象
+> * **顿**(**停顿**/*stop*):发生在音节之间,语流空隙时段很短(但的确存在),不会用来换气,前后可能产生 “连读现象
+
+## 4.2.2. 停/暂停(pause)
+
+暂停的作用就是 “断句” —— 英文和中文一样的,其实任何语言也都是如此,不同的断句,有可能造成意义或者逻辑的变化。
+
+> * Let's eat, kids.
+> * Let's eat kids.
+
+或者
+
+> * A woman without her man is nothing.
+> * A woman: without her, man is nothing.
+
+而同一意群中细微的**停顿**(*stop*)影响自然语流的节奏,不符合惯常节奏的声音,总是需要 “多一层的处理” 才能够理解。
+
+## 4.2.3. 顿/停顿(stop)
+
+**暂停/停**(*pause*)往往很容易分辨,但,**停顿**(*stop*)就是几乎被所有初学者完全忽视的重要细节。
+
+一个常见的停顿来自于**重读音节**。当我们想要强调某个音节的时候,为了能把它读得相对较重,在此之前往往需要**停顿**一下 —— 做个准备。
+
+比如,你可以试着读一下 *absolutely* ˌæbsəˈluːtli…… 为了把 luː 这个音节读成**重音**(顺带说,这是个长元音),你可能就会不由自主地在发出 sə 之后略微**停顿**一下…… 拿之前的例子 *individual* ˌɪndəˈvɪdʒuəl 再试一下,若是在 vɪ 之前略微**停顿**一下,就能相对更为轻松地将它读成**重音**。
+
+另外一个更为常见的**停顿**相对比较微妙,它就在那里,可不仅听不到,就算说话的人就在眼前,也看不到他们停顿时的发声器官的具体动作。
+
+比如,在 *hotdog* ˈhɑːt.dɑːɡ这个词里,t 的声音是听不到的,但,它的舌尖动作却是**完整**的,所以,能够听到的不是 ˈhɑtˌdɔg,也不是 ˈhɑˌdɔg,而是 ˈhɑt̚ ˌdɔg。你可以再试试 *network* ˈnetˌwərk 这个词 —— 这里面的 t 也是同样的机制。(注意,也有人用喉塞音 ʔ 读这这两个词中的 t。)
+
+这类**停顿**(*stop*),也叫**塞音**,最常见的有以下几种:
+
+> - 唇塞音(*Bilabial stop*):p、b、m、f、v —— *I just cannot hel**p** myself*. 第四个单词 help 末尾的 p 就是一个唇塞音。
+> - 齿塞音(*Dental stop*):θ、ð —— *I wish the pa**th** to success was smoother.* 第四个单词 pæθ 末尾的 θ 就是一个齿塞音。
+> - 龈塞音(*Alveolar stop*): t、d —— *He trie**d** to discuss his idea with his boss.* 第二个单词 traɪd 末尾的 d 就是一个龈塞音。
+> - 喉塞音(*Glottal stop*):k, g, t —— IPA 里用 ʔ标注。 *I don't thin**k** that is true.* 第三个单词 *think* 末尾的 k 就是一个喉塞音,θɪŋʔ ðæt。 *uh-oh* ɑʔoʊ 是另外一个例子…… t 也有可能用这个喉塞音,比如,*utmost* ˈʌʔˌmoʊst,或者 ˈneʔwɝːk。
+
+不管是哪一种,都是因为那个辅音**虽然并未发出声音,但整个发声器官却动作完整**而形成的。
+
+这是一个非常重要的建议:
+
+> 在研究 “**连读**” 之前,可能更应该先仔细研究 “**停**与**顿**”。
+
+[^1]: 这段语音截取自 [Thomas Sowell](https://en.wikipedia.org/wiki/Thomas_Sowell) 的 [Knowledge and Decisions](https://www.amazon.com/Knowledge-Decisions-Thomas-Sowell-ebook/dp/B09NKRSC3W/) 有声版(Audible)
diff --git a/1000-hours/sounds-of-american-english/4.4-sentences.md b/1000-hours/sounds-of-american-english/4.4-linking.md
similarity index 100%
rename from 1000-hours/sounds-of-american-english/4.4-sentences.md
rename to 1000-hours/sounds-of-american-english/4.4-linking.md
diff --git a/1000-hours/sounds-of-american-english/4.5-sentences.md b/1000-hours/sounds-of-american-english/4.5-sentences.md
new file mode 100644
index 00000000..e69de29b