Skip to content

Commit 96f3d79

Browse files
Add README
1 parent 45c379d commit 96f3d79

File tree

2 files changed

+121
-6
lines changed

2 files changed

+121
-6
lines changed

.github/workflows/publish.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ on:
33
push:
44
branches:
55
- main
6+
paths-ignore:
7+
- '*.md'
8+
- 'LICENSE'
9+
- '.gitignore'
610
permissions:
711
contents: read
812
pages: write

README.md

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,120 @@
1-
# 香港圍頭話及客家話文字轉語音
1+
<h1>
2+
<a href="https://hkilang.github.io/TTS/"><img src="./public/assets/favicon-256x256.png" width="84" align="left" /></a>
3+
<div lang="zh-HK">香港圍頭話及客家話文字轉語音</div>
4+
<div><sub>Hong Kong Waitau & Hakka Text-to-Speech</sub></div>
5+
</h1>
26

3-
## Data Preprocessing
7+
> <p>
8+
> <div lang="zh-HK">輸入文字,聆聽圍頭話、客家話發音,傳承本土語言。</div>
9+
> <div>Hear Waitau and Hakka from your words. Keep the languages alive.</div>
10+
> </p>
411
5-
**Inputs:** `dictionary.csv`, `public.csv`, `HakkaWords.csv`, `WaitauWords.csv`
6-
**Process:** `compile.py`
7-
**Outputs:** `chars.csv`, `hakka_words.csv`, `waitau_words.csv`
12+
<p>
13+
<div lang="zh-HK">本儲存庫包含<a href="https://hkilang.github.io/TTS/"><strong>香港圍頭話及客家話文字轉語音</strong></a>朗讀器前端部分之原始碼。</div>
14+
<div>This repository contains the source code of the front-end part of the <a href="https://hkilang.github.io/TTS/"><strong>Hong Kong Waitau & Hakka Text-to-Speech</strong></a> reader.</div>
15+
</p>
816

9-
In addition to words from `HakkaWords.csv` and `WaitauWords.csv`, extra words are automatically generated from collocations from the note column of `dictionary.csv` and entries with frequencies ≥ 10 from `public.csv`. Only entries which include at least one polyphone in the target language are included.
17+
<p>
18+
<a href="https://hkilang.org"><img src="./public/assets/hkilang-logo.svg" width="64" align="left" /></a>
19+
<div lang="zh-HK">本程式由<a href="https://hkilang.org">香港本土語言保育協會</a>開發及提供。</div>
20+
<div>This application is developed and made available by the <a href="https://hkilang.org">Association for Conservation of Hong Kong Indigenous Languages</a> (HKILANG).</div>
21+
</p>
22+
23+
## 簡介 Introduction
24+
25+
<p>
26+
<div lang="zh-HK"><strong>圍頭話</strong>及<strong>客家話</strong>皆是香港的非物質文化遺產,然而這些本土語言傳承因城市化出現了斷層。圍村新一代接觸圍頭話、客家話的機會甚少,或「曉聽唔曉講」。</div>
27+
<div><strong>Waitau</strong> and <strong>Hakka</strong> are both recognised as intangible cultural heritage in Hong Kong. However, urbanisation has been disrupting the transmission of these indigenous languages. Younger generations in walled villages are rarely exposed to Waitau and Hakka, and many are what the elders call “<span lang="zh-HK">曉聽唔曉講</span>” — able to understand, but unable to speak.</div>
28+
</p>
29+
30+
<p>
31+
<div lang="zh-HK">使用本文字轉語音朗讀器,可以作為學習圍頭話、客家話的資源,亦可以成為與圍村長輩溝通的工具,延續家庭和社區的語言傳承。</div>
32+
<div>This text-to-speech reader serves not only as a resource for learning Waitau and Hakka, but also as a communication tool for engaging with the elderly in walled villages, helping to preserve the linguistic heritage within families and communities.</div>
33+
</p>
34+
35+
## Development
36+
37+
This app is a static single-paged application (SPA) built with [TypeScript](https://www.typescriptlang.org), [React](https://reactjs.org), [Tailwind CSS](https://tailwindcss.com) and [daisyUI](https://daisyui.com).
38+
39+
To convert it to a native Android and iOS application, it is made a progressive web application (PWA) powered by the [Vite PWA plugin](https://vite-pwa-org.netlify.app), then transformed with [PWABuilder](https://www.pwabuilder.com). For iOS, the output from PWABuilder is further compiled with Xcode.
40+
41+
### Files Overview
42+
43+
* `public/`
44+
* `assets/`: contains pre-generated icons and screenshots of different sizes for use as PWA
45+
* `site.webmanifest`: web application manifest for use as PWA
46+
* `src/`
47+
* `db/`: contains database initialisation & manipulation logic that downloads and saves model & audio data into an Indexed DB for offline usage.
48+
* `res/`: contains both raw and processed Waitau & Hakka pronunciation data of Chinese characters and words, as well as the compilation script. See the _Data Preprocessing_ section below.
49+
* `inference/`: contains the code of a Web Worker for offline model inference as well as the API of the worker. A brief description of `infer.ts` is given in the _Models & Inference_ section below.
50+
* `index.tsx` is the entry point of the app.
51+
* `index.css` is a Tailwind CSS stylesheet containing repeatedly used styles that are not suitable to be inlined.
52+
* `App.tsx` contains the outermost React component.
53+
* The remaining files contain the definitions of other components, hooks, types and utility functions.
54+
55+
### Technical Overview of How the App Works
56+
57+
The app first segments the input into characters and non-characters (punctuation and symbols) in `src/parse.ts`. Then, characters are converted into pronunciation using pronunciation data loaded into a Trie data structure in `src/Resource.ts`. The input and the conversion result is then displayed as a `SentenceCard` component (`src/SentenceCard.ts`). The user can choose the desired pronunciation inside the card and the audio is generated by feeding the pronunciation as the input to the text-to-speech model.
58+
59+
### Pronunciation Data
60+
61+
Developers can safely ignore the actual content in the `src/res/` folder and only need to keep in mind of the following:
62+
63+
* The app only loads and should only load the processed outputs, `chars.csv`, `waitau_words.csv` and `hakka_words.csv`. Among them:
64+
* `chars.csv` contains four columns:
65+
* `char`: the Chinese character consisting a single Unicode codepoint. This column is **not unique** and may repeat if the character is a polyphone (has multiple pronunciation).
66+
* `waitau`, `hakka`: the Waitau and Hakka pronunciation of the character in [HKILANG](https://hkilang.org)'s own romanisation scheme, if any. You can refer to the website for the details of the romanisation scheme, but do not make further assumption of the format beyond `/^[a-zäöüæ]+[1-6]$/` for Waitau and `/^[a-z]+[1-6]$/` for Hakka in the code.
67+
* `notes`: further explanation/clarification/disambiguation displayed underneath the character, if any
68+
* `waitau_words.csv` and `hakka_words.csv` each contains two columns:
69+
* `char`: the Chinese characters consisting two or more Unicode codepoints. Again, this column is **not unique** and may repeat if the word can pronounce in multiple variations.
70+
* `pron`: the Waitau or Hakka pronunciation of the character with the **same number of syllables** as the number of characters in the `char` column. Each pair of syllables is separated by an ASCII (ordinary) whitespace.
71+
* The raw data, `dictionary.csv`, `WaitauWords.csv`, `HakkaWords.csv` and `public.csv`, as well as `compile.py`, should **never** be referenced in the code.
72+
73+
The outputs are precompiled and managed as part of the Git repository, so you need not generate them manually unless you modified the inputs or the complication script described below.
74+
75+
#### Data Preprocessing
76+
77+
> [!NOTE]
78+
> <p>
79+
> <div>This section is intended for dictionary maintainers. Developers of the body of the app need not read it.</div>
80+
> <div>Read the above section for the description of the compilation outputs.</div>
81+
> </p>
82+
83+
`src/res/compile.py` gathers pronunciation data from the following sources as the inputs:
84+
85+
* `dictionary.csv`, `WaitauWords.csv`, `HakkaWords.csv`: Pronunciation data from the [HKILANG](https://hkilang.org)'s dictionary, surveyed and collected from villages in Hong Kong in an earlier project. These are the core sources.
86+
* `public.csv`: Lexicon table from the [TypeDuck](https://typeduck.hk) Cantonese keyboard, for further supplement of relatively uncommon words in order to facilitate automatic choice of pronunciation for polyphones (characters with multiple pronunciation). This is done inside the `generate` function in `compile.py` by looking up the Waitau/Hakka equivalent of the Cantonese pronunciation in the `dictionary.csv` table after Jyutping is converted into HKILANG's romanisation scheme by the `rom_map` function. Only entries with frequencies ≥ 10 which include at least one polyphone in the target language are included.
87+
88+
In addition to words from `WaitauWords.csv`, `HakkaWords.csv` and `public.csv`, words are also extracted from collocations from the note column of `dictionary.csv`.
89+
90+
The compilation script cleanses and normalises the inputs, computes extra words and outputs the result into the three files described in the above section. All monosyllabic results, whether linguistically a word or not, are included in the `chars.csv` file, and the polysyllabic results are written to `waitau_words.csv` and `hakka_words.csv`.
91+
92+
### Audio Generation
93+
94+
The app provides 3 different modes for generating audio from the input:
95+
96+
1. **Online inference**: The app requests (`fetch`es) audio from the backend from the following URL:
97+
98+
> `https://Chaak2.pythonanywhere.com/TTS/${language}/${text}?voice=${voice}&speed=${speed}`
99+
100+
where the parameters are:
101+
102+
* `${language}`, which must be one of `waitau` or `hakka`;
103+
* `${text}`, which is the **romanised** text input, separated by spaces (`%20`) or `+`. There are 7 available punctuation marks: `.`, `,`, `!`, `?`, ``, `'` and `-`. Separators are required both before and after punctuation marks. Percent-encoding is not mandatory except for the punctuation `?` (`%3F`).
104+
Currently, only transliterations in HKILANG's own romanisation system is accepted. **Chinese characters are not yet supported** in the API, so you will need to first convert Chinese text into pronunciation using this app's interface.
105+
* `${voice}`, which may be one of `male` and `female` (optional, defaults to `male`); and
106+
* `${speed}`, which may be any number between 0.5 and 2 (optional, defaults to 1).
107+
108+
`${` and `}` indicate a parameter and should not be included as part of the URL.
109+
110+
The backend is deployed as a [PythonAnywhere](https://www.pythonanywhere.com) instance and the code is open sourced in [github.com/hkilang/TTS-API](../../../TTS-API), which is a dead code eliminated reduction of [Bert-VITS2](../../../../fishaudio/Bert-VITS2). The pre-trained [PyTorch](https://pytorch.org) machine learning models used for inference are published on [the release page](../../../TTS-API/releases).
111+
112+
2. **Offline inference**: The app performs inference within itself in the Web Worker, `src/inference/worker.ts`, using the same machine learning models used for online inference but exported as [ONNX](https://onnx.ai) format, available in the [github.com/hkilang/TTS-models](../../../TTS-models) repo. Each model consists of several components, some of which are split into smaller chunks due to size limitations. In the app, each model is downloaded and stored into an IndexedDB per user request. The user must download the desired model manually before audio generation.
113+
114+
The `infer` method in `src/inference/infer.ts` resembles the `SynthesizerTrn.infer()` method in [`models.py`](../../../TTS-API/blob/main/models.py) in the TTS-API repo. In the method, each model component is loaded from the IndexedDB and the weights are released immediately after use to avoid out-of-memory errors in low-end devices with limited memory. A custom class, `NDArray`, is written for performing mathematical computation on the immediate results inferred by the model components.
115+
116+
3. **Lightweight mode**: The app **concatenates the pre-generated audio files** available in the [github.com/hkilang/TTS-audios](../../../TTS-audios) repo. These files are created as follows: for each character in the dictionary, an audio file is generated using the same model as offline inference. The generated files are then concatenated into a single file, `chars.bin`, and the corresponding pronunciation and the start offset for each audio file is saved into an offset table, `chars.csv`. The same is performed for each word in the dictionary, producing the files `words.bin` (split into smaller chunks due to size limitations) and `words.csv`. These 4 files form an audio pack.
117+
118+
In the app, each audio pack is downloaded and stored into an IndexedDB per user request. The user must download the desired audio pack manually before audio generation. During audio generation, the app loads the audio components from IndexedDB, uses the offset tables to locate the byte ranges for each phrase, slices those segments from the audio components and decodes them. Then, all the decoded audio segments are concatenated in order into a single audio buffer for playback.
119+
120+
Although the generation process is fast thanks to its computational simplicity, **the use of this mode is discouraged** due to the poor quality of the results produced and is intended only as a last resort in extremely low-end devices without an Internet connection when even offline inference fails.

0 commit comments

Comments
 (0)