phonebox 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. phonebox-0.1.0/LICENSE +21 -0
  2. phonebox-0.1.0/MANIFEST.in +19 -0
  3. phonebox-0.1.0/PKG-INFO +249 -0
  4. phonebox-0.1.0/README.md +206 -0
  5. phonebox-0.1.0/examples/library_usage.py +133 -0
  6. phonebox-0.1.0/examples/nbest_example.py +286 -0
  7. phonebox-0.1.0/examples/score_dictionary.py +342 -0
  8. phonebox-0.1.0/examples/score_pronunciation.py +82 -0
  9. phonebox-0.1.0/examples/simple_api.py +19 -0
  10. phonebox-0.1.0/examples/training_example.py +81 -0
  11. phonebox-0.1.0/phonebox/__init__.py +41 -0
  12. phonebox-0.1.0/phonebox/bundler.py +77 -0
  13. phonebox-0.1.0/phonebox/cart/__init__.py +11 -0
  14. phonebox-0.1.0/phonebox/cart/g2p_predict.py +315 -0
  15. phonebox-0.1.0/phonebox/cli/__init__.py +8 -0
  16. phonebox-0.1.0/phonebox/cli/commands/__init__.py +3 -0
  17. phonebox-0.1.0/phonebox/cli/commands/_common.py +61 -0
  18. phonebox-0.1.0/phonebox/cli/commands/align.py +73 -0
  19. phonebox-0.1.0/phonebox/cli/commands/bundle.py +71 -0
  20. phonebox-0.1.0/phonebox/cli/commands/check.py +218 -0
  21. phonebox-0.1.0/phonebox/cli/commands/compare.py +157 -0
  22. phonebox-0.1.0/phonebox/cli/commands/dict.py +120 -0
  23. phonebox-0.1.0/phonebox/cli/commands/model.py +353 -0
  24. phonebox-0.1.0/phonebox/cli/commands/normalize.py +79 -0
  25. phonebox-0.1.0/phonebox/cli/commands/pronounce.py +190 -0
  26. phonebox-0.1.0/phonebox/cli/commands/recipe.py +302 -0
  27. phonebox-0.1.0/phonebox/cli/commands/suggest_joins.py +291 -0
  28. phonebox-0.1.0/phonebox/cli/commands/train.py +182 -0
  29. phonebox-0.1.0/phonebox/cli/commands/train_multigram.py +124 -0
  30. phonebox-0.1.0/phonebox/cli/commands/vectorize.py +61 -0
  31. phonebox-0.1.0/phonebox/cli/main.py +159 -0
  32. phonebox-0.1.0/phonebox/config/__init__.py +1 -0
  33. phonebox-0.1.0/phonebox/config/locales/de_DE/config.json +25 -0
  34. phonebox-0.1.0/phonebox/config/locales/de_DE/defaults.json +13 -0
  35. phonebox-0.1.0/phonebox/config/locales/de_DE/g2p.xlit +13 -0
  36. phonebox-0.1.0/phonebox/config/locales/default/config.json +8 -0
  37. phonebox-0.1.0/phonebox/config/locales/default/defaults.json +8 -0
  38. phonebox-0.1.0/phonebox/config/locales/default/g2p.xlit +6 -0
  39. phonebox-0.1.0/phonebox/config/locales/en_IN/config.json +32 -0
  40. phonebox-0.1.0/phonebox/config/locales/en_IN/defaults.json +12 -0
  41. phonebox-0.1.0/phonebox/config/locales/en_US/config.json +64 -0
  42. phonebox-0.1.0/phonebox/config/locales/en_US/defaults.json +12 -0
  43. phonebox-0.1.0/phonebox/config/locales/en_US/g2p.xlit +6 -0
  44. phonebox-0.1.0/phonebox/config/locales/es_MX/config.json +12 -0
  45. phonebox-0.1.0/phonebox/config/locales/es_MX/defaults.json +10 -0
  46. phonebox-0.1.0/phonebox/config/locales/es_MX/g2p.xlit +30 -0
  47. phonebox-0.1.0/phonebox/config/locales/fr_FR/config.json +22 -0
  48. phonebox-0.1.0/phonebox/config/locales/fr_FR/defaults.json +12 -0
  49. phonebox-0.1.0/phonebox/config/locales/fr_FR/g2p.xlit +14 -0
  50. phonebox-0.1.0/phonebox/config/locales/it_IT/config.json +22 -0
  51. phonebox-0.1.0/phonebox/config/locales/it_IT/defaults.json +8 -0
  52. phonebox-0.1.0/phonebox/config/locales/it_IT/g2p.xlit +23 -0
  53. phonebox-0.1.0/phonebox/config/locales/norm.xlit +36 -0
  54. phonebox-0.1.0/phonebox/config/locales/pt_BR/config.json +14 -0
  55. phonebox-0.1.0/phonebox/config/locales/pt_BR/defaults.json +12 -0
  56. phonebox-0.1.0/phonebox/config/locales/pt_BR/g2p.xlit +12 -0
  57. phonebox-0.1.0/phonebox/config_builder.py +63 -0
  58. phonebox-0.1.0/phonebox/config_loader.py +206 -0
  59. phonebox-0.1.0/phonebox/configs/__init__.py +54 -0
  60. phonebox-0.1.0/phonebox/configs/accurate.yaml +25 -0
  61. phonebox-0.1.0/phonebox/configs/defaults.yaml +82 -0
  62. phonebox-0.1.0/phonebox/configs/fast.yaml +25 -0
  63. phonebox-0.1.0/phonebox/configs/pocketsphinx.yaml +28 -0
  64. phonebox-0.1.0/phonebox/configs/xsampa.yaml +27 -0
  65. phonebox-0.1.0/phonebox/constants.py +230 -0
  66. phonebox-0.1.0/phonebox/converter.py +366 -0
  67. phonebox-0.1.0/phonebox/core/__init__.py +11 -0
  68. phonebox-0.1.0/phonebox/core/decision_tree.py +8 -0
  69. phonebox-0.1.0/phonebox/core/em_align.py +514 -0
  70. phonebox-0.1.0/phonebox/core/g2p_model.py +960 -0
  71. phonebox-0.1.0/phonebox/core/joint_decode.py +111 -0
  72. phonebox-0.1.0/phonebox/core/multigram_align.py +703 -0
  73. phonebox-0.1.0/phonebox/core/multigram_g2p.py +286 -0
  74. phonebox-0.1.0/phonebox/core/multigram_lm.py +187 -0
  75. phonebox-0.1.0/phonebox/core/nbest.py +69 -0
  76. phonebox-0.1.0/phonebox/core/vectorizer.py +585 -0
  77. phonebox-0.1.0/phonebox/dictionary.py +428 -0
  78. phonebox-0.1.0/phonebox/eval/__init__.py +29 -0
  79. phonebox-0.1.0/phonebox/eval/g2p_compare.py +672 -0
  80. phonebox-0.1.0/phonebox/eval/g2p_compare_all.py +342 -0
  81. phonebox-0.1.0/phonebox/experiments/__init__.py +11 -0
  82. phonebox-0.1.0/phonebox/experiments/analysis.py +82 -0
  83. phonebox-0.1.0/phonebox/experiments/equiv.py +37 -0
  84. phonebox-0.1.0/phonebox/experiments/metrics.py +14 -0
  85. phonebox-0.1.0/phonebox/experiments/normalize.py +122 -0
  86. phonebox-0.1.0/phonebox/experiments/split.py +26 -0
  87. phonebox-0.1.0/phonebox/locales.py +84 -0
  88. phonebox-0.1.0/phonebox/normalize.py +62 -0
  89. phonebox-0.1.0/phonebox/py.typed +0 -0
  90. phonebox-0.1.0/phonebox/runner.py +245 -0
  91. phonebox-0.1.0/phonebox/tools/__init__.py +5 -0
  92. phonebox-0.1.0/phonebox/tools/count_diphones.py +53 -0
  93. phonebox-0.1.0/phonebox/utils/__init__.py +9 -0
  94. phonebox-0.1.0/phonebox/utils/icu_utils.py +60 -0
  95. phonebox-0.1.0/phonebox/utils/io.py +24 -0
  96. phonebox-0.1.0/phonebox/utils/logging_config.py +21 -0
  97. phonebox-0.1.0/phonebox.egg-info/PKG-INFO +249 -0
  98. phonebox-0.1.0/phonebox.egg-info/SOURCES.txt +123 -0
  99. phonebox-0.1.0/phonebox.egg-info/dependency_links.txt +1 -0
  100. phonebox-0.1.0/phonebox.egg-info/entry_points.txt +2 -0
  101. phonebox-0.1.0/phonebox.egg-info/requires.txt +21 -0
  102. phonebox-0.1.0/phonebox.egg-info/top_level.txt +1 -0
  103. phonebox-0.1.0/pyproject.toml +144 -0
  104. phonebox-0.1.0/requirements-dev.txt +30 -0
  105. phonebox-0.1.0/requirements.txt +5 -0
  106. phonebox-0.1.0/setup.cfg +4 -0
  107. phonebox-0.1.0/tests/__init__.py +1 -0
  108. phonebox-0.1.0/tests/test_cli.py +506 -0
  109. phonebox-0.1.0/tests/test_decision_tree.py +124 -0
  110. phonebox-0.1.0/tests/test_dict_processing.py +137 -0
  111. phonebox-0.1.0/tests/test_dictionary.py +121 -0
  112. phonebox-0.1.0/tests/test_edge_cases.py +591 -0
  113. phonebox-0.1.0/tests/test_experiment_normalize.py +69 -0
  114. phonebox-0.1.0/tests/test_g2p_api.py +204 -0
  115. phonebox-0.1.0/tests/test_integration.py +99 -0
  116. phonebox-0.1.0/tests/test_it_IT_locale.py +45 -0
  117. phonebox-0.1.0/tests/test_joint_decode.py +34 -0
  118. phonebox-0.1.0/tests/test_multigram_align.py +217 -0
  119. phonebox-0.1.0/tests/test_multigram_g2p.py +144 -0
  120. phonebox-0.1.0/tests/test_multigram_lm.py +17 -0
  121. phonebox-0.1.0/tests/test_nbest.py +446 -0
  122. phonebox-0.1.0/tests/test_no_config_joins.py +21 -0
  123. phonebox-0.1.0/tests/test_pocketsphinx.py +154 -0
  124. phonebox-0.1.0/tests/test_runner.py +165 -0
  125. phonebox-0.1.0/tests/test_vectorizer.py +101 -0
phonebox-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-2024 Kevin Lenzo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ # Include package data
2
+ include README.md
3
+ include LICENSE
4
+ include requirements.txt
5
+ include requirements-dev.txt
6
+
7
+ # Include all configuration files
8
+ recursive-include g2p/config *.json *.xlit
9
+
10
+ # Include examples
11
+ recursive-include examples *.py
12
+
13
+ # Include tests
14
+ recursive-include tests *.py
15
+
16
+ # Exclude build artifacts
17
+ global-exclude __pycache__
18
+ global-exclude *.py[co]
19
+ global-exclude .DS_Store
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: phonebox
3
+ Version: 0.1.0
4
+ Summary: Fast, lightweight grapheme-to-phoneme conversion using decision trees
5
+ Author: Kevin Lenzo (lenzo-ka)
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/lenzo-ka/phonebox
8
+ Project-URL: Bug Reports, https://github.com/lenzo-ka/phonebox/issues
9
+ Project-URL: Source, https://github.com/lenzo-ka/phonebox
10
+ Keywords: g2p,grapheme-to-phoneme,pronunciation,phonetics,speech,nlp,phonebox
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Text Processing :: Linguistic
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: cartlet>=0.5.0
26
+ Requires-Dist: icukit>=0.1.2
27
+ Provides-Extra: icu
28
+ Requires-Dist: PyICU>=2.11; extra == "icu"
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.0; extra == "dev"
34
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
35
+ Provides-Extra: docs
36
+ Requires-Dist: sphinx>=5.0; extra == "docs"
37
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
38
+ Requires-Dist: myst-parser>=0.18; extra == "docs"
39
+ Provides-Extra: viz
40
+ Requires-Dist: graphviz>=0.20; extra == "viz"
41
+ Requires-Dist: matplotlib>=3.5.0; extra == "viz"
42
+ Dynamic: license-file
43
+
44
+ # Phonebox: Grapheme-to-Phoneme Conversion
45
+
46
+ Fast, lightweight grapheme-to-phoneme (G2P) conversion using decision trees and
47
+ EM alignment. The package also includes **MultigramG2P** (n:m joint Viterbi) in
48
+ the library; 1:1 CLI commands use the decision tree path.
49
+
50
+ Decision-tree G2P is fast, compact, and interpretable; it trades some accuracy
51
+ for those properties, and neural G2P methods will generally score better.
52
+ CMUdict / PocketSphinx workflows are the main English use case; measured phone
53
+ error rates and IPA locale benchmarks are in
54
+ [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
55
+
56
+ ## Features
57
+
58
+ - **Measured accuracy**: reported phone error rates on CMUdict (see docs/BENCHMARKS.md); neural G2P methods can be more accurate
59
+ - **Compact Models**: < 1MB model size for typical 1:1 trees
60
+ - **MultigramG2P**: n:m joint EM + Viterbi decode (`MultigramG2P` in Python API)
61
+ - **Zero Dependencies**: Bundled Python executable works standalone
62
+ - **CMUdict Support**: English pronunciation with PocketSphinx compatibility
63
+
64
+ ## Installation
65
+
66
+ ```bash
67
+ pip install phonebox
68
+ ```
69
+
70
+ Or from source:
71
+
72
+ ```bash
73
+ git clone https://github.com/lenzo-ka/phonebox.git
74
+ cd phonebox
75
+ pip install -e .
76
+ ```
77
+
78
+ ## Quick Start
79
+
80
+ ### One Command
81
+
82
+ ```bash
83
+ # Build G2P from CMUdict, bundle as Python executable
84
+ phonebox recipe cmudict pocketsphinx -o g2p.py
85
+
86
+ # Use it
87
+ python g2p.py "Hello, world!"
88
+ ```
89
+
90
+ ### TTS Preset (keeps stress)
91
+
92
+ ```bash
93
+ phonebox recipe cmudict tts -o g2p.py
94
+ ```
95
+
96
+ ## Using Bundled G2P
97
+
98
+ ### Command Line
99
+
100
+ ```bash
101
+ python g2p.py "Hello, world!"
102
+ # hello HH AH L OW
103
+ # world W ER L D
104
+
105
+ # Raw mode (no text normalization)
106
+ python g2p.py -r "Hello,"
107
+ ```
108
+
109
+ ### Python Library
110
+
111
+ ```python
112
+ from g2p import G2PPredictor
113
+
114
+ g2p = G2PPredictor.from_embedded()
115
+ phones = g2p.pronounce("hello") # ['HH', 'AH', 'L', 'OW']
116
+
117
+ # Process text (tokenizes automatically)
118
+ for word, phones in g2p.pronounce_text("Hello, world!"):
119
+ print(f"{word}: {' '.join(phones)}")
120
+ ```
121
+
122
+ ## CLI Commands
123
+
124
+ ```
125
+ Quick Start:
126
+ recipe Build complete G2P from dictionary (one command)
127
+
128
+ Using Models:
129
+ pronounce Get pronunciations for words
130
+ normalize Preview text normalization/tokenization
131
+ bundle Create standalone executable with embedded model
132
+
133
+ Building Models:
134
+ model Model operations (build, train, convert, benchmark)
135
+ dict Dictionary operations (fetch, export-vectors)
136
+
137
+ Low-Level:
138
+ align Align letters to phonemes (EM algorithm)
139
+ vectorize Convert alignments to feature vectors
140
+
141
+ Quality:
142
+ check Validate lexicon against phoneset
143
+ suggest-joins Discover join candidates (multigram EM)
144
+ compare 1:1 vs n:m eval (locale or all IPA locales)
145
+ train-multigram Train/export MultigramG2P
146
+ ```
147
+
148
+ ### G2P evaluation (IPA locales)
149
+
150
+ ```bash
151
+ phonebox compare all # docs/G2P_COMPARE.md
152
+ phonebox compare locale --lexicon … --locale it_IT
153
+ phonebox train-multigram --locale it_IT --lexicon it_ipa.tsv -o model.g2p.gz
154
+ ```
155
+
156
+ Repo wrappers: `compare_g2p_all.py`, `compare_g2p_sweep.py`, `dump_units.py`.
157
+ See [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
158
+
159
+ ### Examples
160
+
161
+ ```bash
162
+ # Preview text normalization
163
+ phonebox normalize "Hello, world!"
164
+
165
+ # Pronounce with existing model
166
+ phonebox pronounce hello world -m model.g2p.gz
167
+
168
+ # Benchmark model
169
+ phonebox model benchmark model.g2p.gz
170
+
171
+ # Fetch dictionary manually
172
+ phonebox dict fetch cmudict
173
+ ```
174
+
175
+ ## Python API
176
+
177
+ ```python
178
+ from phonebox import G2P, MultigramG2P
179
+
180
+ # 1:1 decision tree (CLI: phonebox pronounce)
181
+ g2p = G2P(model="model.g2p.gz")
182
+ phones = g2p.pronounce("hello")
183
+ print(phones) # ['HH', 'AH', 'L', 'OW']
184
+
185
+ # n:m multigram (train via CLI or library)
186
+ mg = MultigramG2P(max_letter_span=2, max_phone_span=2)
187
+ mg.train_from_dict("lexicon.tsv")
188
+ # phonebox train-multigram … ; phonebox pronounce -m model.g2p.gz (sidecar auto-detect)
189
+
190
+ # N-best alternatives
191
+ for pron, score in g2p.pronounce_nbest("read", n=3):
192
+ print(f"{pron} ({score:.3f})")
193
+ ```
194
+
195
+ ## Step-by-Step Training
196
+
197
+ For debugging or custom workflows:
198
+
199
+ ```bash
200
+ # 1. Fetch dictionary
201
+ phonebox dict fetch cmudict
202
+
203
+ # 2. Align letters to phonemes
204
+ phonebox align data/cmudict/cmudict.dict -o alignments.txt --remove-stress
205
+
206
+ # 3. Vectorize alignments
207
+ phonebox vectorize alignments.txt -o vectors.txt
208
+
209
+ # 4. Train from vectors
210
+ phonebox model train en_US --vectors vectors.txt -o model.g2p.gz
211
+
212
+ # 5. Bundle
213
+ phonebox bundle model.g2p.gz -o g2p.py
214
+ ```
215
+
216
+ ## Standalone Deployment
217
+
218
+ Bundled files have zero dependencies beyond the Python standard library:
219
+
220
+ ```bash
221
+ phonebox bundle model.g2p.gz -o g2p.py
222
+ python g2p.py "test"
223
+ ```
224
+
225
+ ## Algorithm
226
+
227
+ 1. **EM Alignment**: Expectation-Maximization aligns letters to phonemes
228
+ 2. **Feature Extraction**: 7-gram letter windows create feature vectors
229
+ 3. **Decision Tree**: ID3-style tree trained on aligned data
230
+ 4. **Prediction**: Tree traversal based on letter context
231
+
232
+ Based on research from CMU:
233
+ - [CMU G2P Research](http://www.cs.cmu.edu/afs/cs.cmu.edu/user/lenzo/html/areas/t2p/)
234
+ - [ICSLP 1998 Paper](https://www.isca-speech.org/archive/icslp_1998/i98_0561.html)
235
+
236
+ ## License
237
+
238
+ MIT License - see LICENSE file.
239
+
240
+ ## Author
241
+
242
+ Kevin Lenzo ([@lenzo-ka](https://github.com/lenzo-ka))
243
+
244
+ ## Contributing
245
+
246
+ 1. Fork the repository
247
+ 2. Create a feature branch
248
+ 3. Add tests for new functionality
249
+ 4. Submit a pull request
@@ -0,0 +1,206 @@
1
+ # Phonebox: Grapheme-to-Phoneme Conversion
2
+
3
+ Fast, lightweight grapheme-to-phoneme (G2P) conversion using decision trees and
4
+ EM alignment. The package also includes **MultigramG2P** (n:m joint Viterbi) in
5
+ the library; 1:1 CLI commands use the decision tree path.
6
+
7
+ Decision-tree G2P is fast, compact, and interpretable; it trades some accuracy
8
+ for those properties, and neural G2P methods will generally score better.
9
+ CMUdict / PocketSphinx workflows are the main English use case; measured phone
10
+ error rates and IPA locale benchmarks are in
11
+ [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
12
+
13
+ ## Features
14
+
15
+ - **Measured accuracy**: reported phone error rates on CMUdict (see docs/BENCHMARKS.md); neural G2P methods can be more accurate
16
+ - **Compact Models**: < 1MB model size for typical 1:1 trees
17
+ - **MultigramG2P**: n:m joint EM + Viterbi decode (`MultigramG2P` in Python API)
18
+ - **Zero Dependencies**: Bundled Python executable works standalone
19
+ - **CMUdict Support**: English pronunciation with PocketSphinx compatibility
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install phonebox
25
+ ```
26
+
27
+ Or from source:
28
+
29
+ ```bash
30
+ git clone https://github.com/lenzo-ka/phonebox.git
31
+ cd phonebox
32
+ pip install -e .
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### One Command
38
+
39
+ ```bash
40
+ # Build G2P from CMUdict, bundle as Python executable
41
+ phonebox recipe cmudict pocketsphinx -o g2p.py
42
+
43
+ # Use it
44
+ python g2p.py "Hello, world!"
45
+ ```
46
+
47
+ ### TTS Preset (keeps stress)
48
+
49
+ ```bash
50
+ phonebox recipe cmudict tts -o g2p.py
51
+ ```
52
+
53
+ ## Using Bundled G2P
54
+
55
+ ### Command Line
56
+
57
+ ```bash
58
+ python g2p.py "Hello, world!"
59
+ # hello HH AH L OW
60
+ # world W ER L D
61
+
62
+ # Raw mode (no text normalization)
63
+ python g2p.py -r "Hello,"
64
+ ```
65
+
66
+ ### Python Library
67
+
68
+ ```python
69
+ from g2p import G2PPredictor
70
+
71
+ g2p = G2PPredictor.from_embedded()
72
+ phones = g2p.pronounce("hello") # ['HH', 'AH', 'L', 'OW']
73
+
74
+ # Process text (tokenizes automatically)
75
+ for word, phones in g2p.pronounce_text("Hello, world!"):
76
+ print(f"{word}: {' '.join(phones)}")
77
+ ```
78
+
79
+ ## CLI Commands
80
+
81
+ ```
82
+ Quick Start:
83
+ recipe Build complete G2P from dictionary (one command)
84
+
85
+ Using Models:
86
+ pronounce Get pronunciations for words
87
+ normalize Preview text normalization/tokenization
88
+ bundle Create standalone executable with embedded model
89
+
90
+ Building Models:
91
+ model Model operations (build, train, convert, benchmark)
92
+ dict Dictionary operations (fetch, export-vectors)
93
+
94
+ Low-Level:
95
+ align Align letters to phonemes (EM algorithm)
96
+ vectorize Convert alignments to feature vectors
97
+
98
+ Quality:
99
+ check Validate lexicon against phoneset
100
+ suggest-joins Discover join candidates (multigram EM)
101
+ compare 1:1 vs n:m eval (locale or all IPA locales)
102
+ train-multigram Train/export MultigramG2P
103
+ ```
104
+
105
+ ### G2P evaluation (IPA locales)
106
+
107
+ ```bash
108
+ phonebox compare all # docs/G2P_COMPARE.md
109
+ phonebox compare locale --lexicon … --locale it_IT
110
+ phonebox train-multigram --locale it_IT --lexicon it_ipa.tsv -o model.g2p.gz
111
+ ```
112
+
113
+ Repo wrappers: `compare_g2p_all.py`, `compare_g2p_sweep.py`, `dump_units.py`.
114
+ See [`docs/G2P_EVAL.md`](docs/G2P_EVAL.md).
115
+
116
+ ### Examples
117
+
118
+ ```bash
119
+ # Preview text normalization
120
+ phonebox normalize "Hello, world!"
121
+
122
+ # Pronounce with existing model
123
+ phonebox pronounce hello world -m model.g2p.gz
124
+
125
+ # Benchmark model
126
+ phonebox model benchmark model.g2p.gz
127
+
128
+ # Fetch dictionary manually
129
+ phonebox dict fetch cmudict
130
+ ```
131
+
132
+ ## Python API
133
+
134
+ ```python
135
+ from phonebox import G2P, MultigramG2P
136
+
137
+ # 1:1 decision tree (CLI: phonebox pronounce)
138
+ g2p = G2P(model="model.g2p.gz")
139
+ phones = g2p.pronounce("hello")
140
+ print(phones) # ['HH', 'AH', 'L', 'OW']
141
+
142
+ # n:m multigram (train via CLI or library)
143
+ mg = MultigramG2P(max_letter_span=2, max_phone_span=2)
144
+ mg.train_from_dict("lexicon.tsv")
145
+ # phonebox train-multigram … ; phonebox pronounce -m model.g2p.gz (sidecar auto-detect)
146
+
147
+ # N-best alternatives
148
+ for pron, score in g2p.pronounce_nbest("read", n=3):
149
+ print(f"{pron} ({score:.3f})")
150
+ ```
151
+
152
+ ## Step-by-Step Training
153
+
154
+ For debugging or custom workflows:
155
+
156
+ ```bash
157
+ # 1. Fetch dictionary
158
+ phonebox dict fetch cmudict
159
+
160
+ # 2. Align letters to phonemes
161
+ phonebox align data/cmudict/cmudict.dict -o alignments.txt --remove-stress
162
+
163
+ # 3. Vectorize alignments
164
+ phonebox vectorize alignments.txt -o vectors.txt
165
+
166
+ # 4. Train from vectors
167
+ phonebox model train en_US --vectors vectors.txt -o model.g2p.gz
168
+
169
+ # 5. Bundle
170
+ phonebox bundle model.g2p.gz -o g2p.py
171
+ ```
172
+
173
+ ## Standalone Deployment
174
+
175
+ Bundled files have zero dependencies beyond the Python standard library:
176
+
177
+ ```bash
178
+ phonebox bundle model.g2p.gz -o g2p.py
179
+ python g2p.py "test"
180
+ ```
181
+
182
+ ## Algorithm
183
+
184
+ 1. **EM Alignment**: Expectation-Maximization aligns letters to phonemes
185
+ 2. **Feature Extraction**: 7-gram letter windows create feature vectors
186
+ 3. **Decision Tree**: ID3-style tree trained on aligned data
187
+ 4. **Prediction**: Tree traversal based on letter context
188
+
189
+ Based on research from CMU:
190
+ - [CMU G2P Research](http://www.cs.cmu.edu/afs/cs.cmu.edu/user/lenzo/html/areas/t2p/)
191
+ - [ICSLP 1998 Paper](https://www.isca-speech.org/archive/icslp_1998/i98_0561.html)
192
+
193
+ ## License
194
+
195
+ MIT License - see LICENSE file.
196
+
197
+ ## Author
198
+
199
+ Kevin Lenzo ([@lenzo-ka](https://github.com/lenzo-ka))
200
+
201
+ ## Contributing
202
+
203
+ 1. Fork the repository
204
+ 2. Create a feature branch
205
+ 3. Add tests for new functionality
206
+ 4. Submit a pull request
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Examples of using phonebox as a Python library.
4
+ """
5
+
6
+ from phonebox import G2P, Dictionary
7
+
8
+
9
+ def example_simple_usage():
10
+ """Simplest way to use phonebox."""
11
+ print("=== Simple Usage ===")
12
+
13
+ # Load pre-trained model
14
+ g2p = G2P(model="models/en_US_nostress.g2p.gz")
15
+
16
+ # Pronounce a word
17
+ pronunciation = g2p.pronounce("hello")
18
+ print(f"hello: {pronunciation}")
19
+
20
+ # Or use as callable
21
+ print(f"world: {g2p('world')}")
22
+
23
+ # Batch pronunciation
24
+ words = ["python", "grapheme", "phoneme"]
25
+ for word, phones in g2p.pronounce_batch(words):
26
+ print(f"{word}: {' '.join(phones)}")
27
+
28
+
29
+ def example_pocketsphinx_setup():
30
+ """One-command PocketSphinx setup."""
31
+ print("\n=== PocketSphinx Setup ===")
32
+
33
+ # This fetches CMUdict, processes it, and trains a model
34
+ g2p = G2P.from_pocketsphinx()
35
+
36
+ # Use it
37
+ print(f"test: {g2p('test')}")
38
+
39
+
40
+ def example_dictionary_class():
41
+ """Using the Dictionary class."""
42
+ print("\n=== Dictionary Class ===")
43
+
44
+ # Fetch a dictionary
45
+ dict = Dictionary.fetch("cmudict")
46
+ print(f"Dictionary: {dict}")
47
+ print(f"Size: {len(dict)} entries")
48
+
49
+ # Process it
50
+ processed = dict.process(remove_stress=True, output="processed.dict")
51
+ print(f"Processed: {processed}")
52
+
53
+ # Train a model
54
+ _ = processed.train_g2p_model(locale="en_US", output="my_model.g2p.gz")
55
+ print("Model trained")
56
+
57
+
58
+ def example_train_from_custom_dict():
59
+ """Train from your own dictionary."""
60
+ print("\n=== Train from Custom Dictionary ===")
61
+
62
+ # Create a small test dictionary
63
+ test_dict = """
64
+ hello HH AH L OW
65
+ world W ER L D
66
+ python P AY TH AA N
67
+ """.strip()
68
+
69
+ with open("test_dict.txt", "w") as f:
70
+ f.write(test_dict)
71
+
72
+ # Train directly
73
+ g2p = G2P.train(dictionary="test_dict.txt", locale="en_US", remove_stress=False)
74
+
75
+ # Use it
76
+ print(f"hello: {g2p('hello')}")
77
+
78
+ # Clean up
79
+ import os
80
+
81
+ os.remove("test_dict.txt")
82
+
83
+
84
+ def example_method_chaining():
85
+ """Fluent API with method chaining."""
86
+ print("\n=== Method Chaining ===")
87
+
88
+ # Fetch, process, and train in one line
89
+ _ = (
90
+ Dictionary.fetch("cmudict")
91
+ .process(remove_stress=True)
92
+ .train_g2p_model("en_US", output="model.g2p.gz")
93
+ )
94
+
95
+ print("Model trained via chaining")
96
+
97
+
98
+ def example_advanced_usage():
99
+ """Advanced features."""
100
+ print("\n=== Advanced Usage ===")
101
+
102
+ # Direct DecisionTree access for full control
103
+ from phonebox import DecisionTree
104
+
105
+ dt = DecisionTree(
106
+ locale="en_US",
107
+ phoneset_name="cmu",
108
+ remove_stress=True,
109
+ max_iterations=50,
110
+ min_samples_split=5,
111
+ )
112
+
113
+ # Load and train
114
+ with open("data/cmudict/cmudict_nostress.dict") as f:
115
+ dt.load_prondict(f)
116
+ dt.align()
117
+ dt.train()
118
+
119
+ # Use it
120
+ print(f"pronunciation: {dt.pronounce('pronunciation')}")
121
+
122
+
123
+ if __name__ == "__main__":
124
+ # Run only the simple example by default
125
+ # Uncomment others as needed
126
+
127
+ example_simple_usage()
128
+
129
+ # example_pocketsphinx_setup() # Requires internet
130
+ # example_dictionary_class() # Requires internet
131
+ # example_train_from_custom_dict()
132
+ # example_method_chaining() # Requires internet
133
+ # example_advanced_usage() # Requires data/