yime 0.1.0__py3-none-any.whl

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 (364) hide show
  1. pianyin/__init__.py +4 -0
  2. pianyin/calculate_pentatonic_value.py +82 -0
  3. pianyin/calculate_speech_pentatonic_value.py +64 -0
  4. pianyin/calculate_speech_pentatonic_value_1.py +94 -0
  5. pianyin/calculate_tone_median.py +100 -0
  6. pianyin/indeterminate_pitch_pianyin.py +64 -0
  7. pianyin/initial.py +112 -0
  8. pianyin/pianyin.py +66 -0
  9. pianyin/tone_statistics_analyzer.py +190 -0
  10. pinyin/__init__.py +9 -0
  11. pinyin/compare_pinyin_lists.py +39 -0
  12. pinyin/constants.py +123 -0
  13. pinyin/generate_yinjie.py +72 -0
  14. pinyin/hanzi_pinyin/compare_files.py +102 -0
  15. pinyin/hanzi_pinyin/extract_pinyin_1.py +72 -0
  16. pinyin/hanzi_pinyin/extract_pinyin_2.py +62 -0
  17. pinyin/hanzi_pinyin/format_yaml_file.py +141 -0
  18. pinyin/hanzi_pinyin/merge_json.py +152 -0
  19. pinyin/hanzi_pinyin/normalize_pinyin_file.py +13 -0
  20. pinyin/hanzi_pinyin/pinyin.py +189 -0
  21. pinyin/hanzi_pinyin/pinyin_classifier.py +132 -0
  22. pinyin/hanzi_pinyin/pinyin_danzi.py +54 -0
  23. pinyin/hanzi_pinyin/pinyin_duozi.py +45 -0
  24. pinyin/hanzi_pinyin/pinyin_hanzi.py +78 -0
  25. pinyin/hanzi_pinyin/pinyin_validator.py +148 -0
  26. pinyin/hanzi_pinyin/remove_percent.py +43 -0
  27. pinyin/hanzi_pinyin/reverse_key_value_pairs.py +76 -0
  28. pinyin/hanzi_pinyin/split_yaml_file.py +46 -0
  29. pinyin/hanzi_pinyin/standard_pinyin.py +106 -0
  30. pinyin/hanzi_pinyin/unicode_hanzi_pinyin.py +90 -0
  31. pinyin/hanzi_pinyin/yaml_to_json.py +206 -0
  32. pinyin/hanzi_pinyin/yaml_to_json_danzi_converter.py +210 -0
  33. pinyin/hanzi_pinyin/yaml_to_json_duozi_converter.py +184 -0
  34. pinyin/keys_to_yunmu.py +68 -0
  35. pinyin/plugins/__init__.py +5 -0
  36. pinyin/plugins/default_rules.py +14 -0
  37. pinyin/plugins/example_plugin.py +27 -0
  38. pinyin/rule_plugin.py +24 -0
  39. pinyin/test/test_yunmu_converter.py +65 -0
  40. pinyin/test_yunmu_to_keys.py +55 -0
  41. pinyin/test_yunmu_to_keys_comprehensive.py +823 -0
  42. pinyin/yunmu_to_keys copy 2.py +140 -0
  43. pinyin/yunmu_to_keys copy.py +115 -0
  44. pinyin/yunmu_to_keys.py +255 -0
  45. syllable/__init__.py +9 -0
  46. syllable/analysis/__init__.py +2 -0
  47. syllable/analysis/initial_final_with_tone/analysis_executor.py +142 -0
  48. syllable/analysis/initial_final_with_tone/initial_final.py +99 -0
  49. syllable/analysis/initial_final_with_tone/initial_final_with_tone.py +46 -0
  50. syllable/analysis/initial_final_with_tone/initial_final_with_tone_analyzer.py +14 -0
  51. syllable/analysis/initial_final_with_tone/potential_syllable.py +75 -0
  52. syllable/analysis/initial_final_with_tone/temp.py +12 -0
  53. syllable/analysis/slice/Syllable.py +69 -0
  54. syllable/analysis/slice/__init__.py +5 -0
  55. syllable/analysis/slice/analyze_classification.py +43 -0
  56. syllable/analysis/slice/comprehensive_verification.py +276 -0
  57. syllable/analysis/slice/convert_pitch_style.py +92 -0
  58. syllable/analysis/slice/extract_musical_element.py +60 -0
  59. syllable/analysis/slice/extract_yueyin_pianyin.py +92 -0
  60. syllable/analysis/slice/extract_yueyin_yinyuan.py +60 -0
  61. syllable/analysis/slice/final_categorizer.py +507 -0
  62. syllable/analysis/slice/ganyin.py +54 -0
  63. syllable/analysis/slice/ganyin_analyzer.py +175 -0
  64. syllable/analysis/slice/ganyin_categorizer.py +29 -0
  65. syllable/analysis/slice/ganyin_encoder.py +301 -0
  66. syllable/analysis/slice/ganyin_enhanced.py +98 -0
  67. syllable/analysis/slice/ganyin_slicer.py +279 -0
  68. syllable/analysis/slice/ganyin_theoretical_generator.py +120 -0
  69. syllable/analysis/slice/ganyin_to_pianyin_sequence.py +283 -0
  70. syllable/analysis/slice/ganyin_to_yinyuan_sequence.py +83 -0
  71. syllable/analysis/slice/generate_zaoyin_yinyuan.py +198 -0
  72. syllable/analysis/slice/interactive_yinjie_session.py +43 -0
  73. syllable/analysis/slice/pianyin.py +66 -0
  74. syllable/analysis/slice/pitched_pianyin.py +128 -0
  75. syllable/analysis/slice/pitched_yinyuan.py +57 -0
  76. syllable/analysis/slice/reverse_key_value_pairs.py +71 -0
  77. syllable/analysis/slice/run_analyzer.py +59 -0
  78. syllable/analysis/slice/run_syllable_analyzer.py +34 -0
  79. syllable/analysis/slice/shouyin.py +19 -0
  80. syllable/analysis/slice/shouyin_analyzer.py +147 -0
  81. syllable/analysis/slice/shouyin_encoder.py +168 -0
  82. syllable/analysis/slice/slice_analyzer.py +10 -0
  83. syllable/analysis/slice/slicer.py +15 -0
  84. syllable/analysis/slice/syllable_analyzer.py +175 -0
  85. syllable/analysis/slice/syllable_categorizer.py +36 -0
  86. syllable/analysis/slice/syllable_encoding_pipeline.py +98 -0
  87. syllable/analysis/slice/syllable_segmenter.py +74 -0
  88. syllable/analysis/slice/syllable_splitter.py +81 -0
  89. syllable/analysis/slice/temp.py +146 -0
  90. syllable/analysis/slice/test_comprehensive_verification.py +51 -0
  91. syllable/analysis/slice/test_verify_classification.py +47 -0
  92. syllable/analysis/slice/unpitched_pianyin.py +66 -0
  93. syllable/analysis/slice/verify_classification.py +93 -0
  94. syllable/analysis/slice/verify_encode_ganyin.py +80 -0
  95. syllable/analysis/slice/verify_encode_shouyin.py +56 -0
  96. syllable/analysis/slice/yinjie_api_manifest.py +57 -0
  97. syllable/analysis/slice/yinjie_composition.py +65 -0
  98. syllable/analysis/slice/yinjie_encoder.py +588 -0
  99. syllable/analysis/slice/yinyuan/final_styles.json +237 -0
  100. syllable/analysis/slice/yinyuan/ganyin.json +232 -0
  101. syllable/analysis/slice/yinyuan/ganyin_encoding.json +222 -0
  102. syllable/analysis/slice/yinyuan/ganyin_enhanced.json +1110 -0
  103. syllable/analysis/slice/yinyuan/ganyin_theoretical.json +262 -0
  104. syllable/analysis/slice/yinyuan/ganyin_to_fixed_length_yinyuan_sequence.json +222 -0
  105. syllable/analysis/slice/yinyuan/ganyin_to_pianyin_sequence.json +1110 -0
  106. syllable/analysis/slice/yinyuan/ganyin_to_variable_length_yinyuan_sequence.json +882 -0
  107. syllable/analysis/slice/yinyuan/ganyin_to_yinyuan_seq_marks.json +1110 -0
  108. syllable/analysis/slice/yinyuan/ganyin_to_yinyuan_seq_notes.json +1110 -0
  109. syllable/analysis/slice/yinyuan/ganyin_to_yinyuan_sequence.json +1110 -0
  110. syllable/analysis/slice/yinyuan/initial_ipa.json +32 -0
  111. syllable/analysis/slice/yinyuan/initial_pianyin.json +31 -0
  112. syllable/analysis/slice/yinyuan/merged_musical_yinyuan.json +71 -0
  113. syllable/analysis/slice/yinyuan/merged_yueyin_by_tone.json +71 -0
  114. syllable/analysis/slice/yinyuan/musical_pianyin_attributes.json +117 -0
  115. syllable/analysis/slice/yinyuan/noise_yinyuan.json +64 -0
  116. syllable/analysis/slice/yinyuan/noise_yinyuan_encoding.json +29 -0
  117. syllable/analysis/slice/yinyuan/noise_yinyuan_simplified.json +29 -0
  118. syllable/analysis/slice/yinyuan/pianyin_initial.json +36 -0
  119. syllable/analysis/slice/yinyuan/pitch_quality_synchronous_yinyuan.json +61 -0
  120. syllable/analysis/slice/yinyuan/pitched_pianyin.json +109 -0
  121. syllable/analysis/slice/yinyuan/pitched_yinyuan_of_mid_high_median_model.json +35 -0
  122. syllable/analysis/slice/yinyuan/pitched_yinyuan_of_mid_level_median_model.json +35 -0
  123. syllable/analysis/slice/yinyuan/shouyin.json +28 -0
  124. syllable/analysis/slice/yinyuan/shouyin_codepoint.json +28 -0
  125. syllable/analysis/slice/yinyuan/shouyin_yinyuan.json +29 -0
  126. syllable/analysis/slice/yinyuan/standard_pinyin.json +1785 -0
  127. syllable/analysis/slice/yinyuan/variables_of_attributes.json +39 -0
  128. syllable/analysis/slice/yinyuan/variables_of_pitch_and_quality.json +35 -0
  129. syllable/analysis/slice/yinyuan/yinjie_code.json +1838 -0
  130. syllable/analysis/slice/yinyuan/yinyuan_codepoint.json +63 -0
  131. syllable/analysis/slice/yinyuan/yueyin_yinyuan.json +175 -0
  132. syllable/analysis/slice/yinyuan/yueyin_yinyuan_enhanced.json +41 -0
  133. syllable/analysis/slice/yinyuan/zaoyin_yinyuan.json +91 -0
  134. syllable/analysis/slice/yinyuan/zaoyin_yinyuan_enhanced.json +239 -0
  135. syllable/analysis/slice/yinyuan.py +134 -0
  136. syllable/analysis/slice/yueyin_yinyuan.py +259 -0
  137. syllable/analysis/slice/zaoyin_yinyuan.py +90 -0
  138. syllable/syllable_analyzer_strategy.py +34 -0
  139. syllable/syllable_factory.py +17 -0
  140. syllable/syllable_mapper.py +28 -0
  141. syllable/three_models.py +49 -0
  142. syllable_codec/__init__.py +14 -0
  143. syllable_codec/interactive_yinjie.py +16 -0
  144. syllable_codec/key_to_code.json +59 -0
  145. syllable_codec/paths.py +15 -0
  146. syllable_codec/yinjie.py +122 -0
  147. syllable_codec/yinjie_code.json +1551 -0
  148. syllable_codec/yinjie_decoder.py +246 -0
  149. syllable_codec/yinjie_encoder.py +11 -0
  150. utils/__init__.py +0 -0
  151. utils/code_bintree.py +185 -0
  152. utils/example.py +188 -0
  153. utils/ooder_set.py +101 -0
  154. utils/pinyin_normalizer.py +231 -0
  155. utils/pinyin_zhuyin.py +95 -0
  156. utils/position.py +151 -0
  157. utils/test_pinyin_normalizer.py +82 -0
  158. utils/trie_tree.py +170 -0
  159. yime/Import_yinyuan_pinyin.py +352 -0
  160. yime/Initialize_hanzi_pinyin.py +93 -0
  161. yime/Initialize_pinyin_mapping.py +303 -0
  162. yime/__init__.py +2 -0
  163. yime/backup_database.py +61 -0
  164. yime/borrow_wanxiang_frequency.py +428 -0
  165. yime/build_minimal_lexicon_db.py +130 -0
  166. yime/canonical_yime_mapping.py +181 -0
  167. yime/code_pinyin.json +5334 -0
  168. yime/codes_dict.json +63 -0
  169. yime/consolidate_mappings.py +124 -0
  170. yime/convert_pinyin_to_hanzi.py +218 -0
  171. yime/create_formal_key_plan.sql +122 -0
  172. yime/create_minimal_lexicon_schema.sql +69 -0
  173. yime/create_prototype_schema_additions.sql +360 -0
  174. yime/create_symbol_key_simulation.sql +137 -0
  175. yime/create_table.py +104 -0
  176. yime/create_universal_mapping.py +37 -0
  177. yime/create_yime_db_schema.sql +520 -0
  178. yime/database.py +133 -0
  179. yime/db_manager.py +292 -0
  180. yime/dictionary.txt +5 -0
  181. yime/encoding_scheme.json +43 -0
  182. yime/enhanced_yinjie_mapping.json +33052 -0
  183. yime/export_phrase_code_backfill.py +66 -0
  184. yime/export_runtime_candidates_json.py +171 -0
  185. yime/gui_input.py +361 -0
  186. yime/hanzi_db_manager.py +142 -0
  187. yime/import_8105_char_frequency.py +117 -0
  188. yime/import_danzi_into_prototype_tables.py +323 -0
  189. yime/import_duozi_into_prototype_tables.py +277 -0
  190. yime/import_initial.py +88 -0
  191. yime/import_layout_sources.py +370 -0
  192. yime/import_numeric_pinyin.py +192 -0
  193. yime/import_xiandaihaiyu_phrase_frequency.py +78 -0
  194. yime/initial_ipa.json +26 -0
  195. yime/input_code.json +63 -0
  196. yime/input_method/__init__.py +18 -0
  197. yime/input_method/app.py +1093 -0
  198. yime/input_method/app_base.py +1917 -0
  199. yime/input_method/app_global.py +186 -0
  200. yime/input_method/core/__init__.py +19 -0
  201. yime/input_method/core/char_code_index.py +97 -0
  202. yime/input_method/core/decoders.py +546 -0
  203. yime/input_method/core/input_manager.py +318 -0
  204. yime/input_method/core/input_visualization.py +226 -0
  205. yime/input_method/core/keyboard_listener.py +695 -0
  206. yime/input_method/core/prefix_tree.py +96 -0
  207. yime/input_method/core/runtime_decoder_base.py +263 -0
  208. yime/input_method/core/runtime_json_store.py +115 -0
  209. yime/input_method/core/runtime_lookup.py +174 -0
  210. yime/input_method/core/runtime_ranking.py +474 -0
  211. yime/input_method/core/sqlite_char_store.py +165 -0
  212. yime/input_method/core/sqlite_phrase_store.py +156 -0
  213. yime/input_method/core/sqlite_runtime_source.py +42 -0
  214. yime/input_method/test_altgr_modifiers.py +13 -0
  215. yime/input_method/test_app_base_reverse_lookup.py +149 -0
  216. yime/input_method/test_app_base_user_lexicon.py +602 -0
  217. yime/input_method/test_app_candidate_box_factory.py +90 -0
  218. yime/input_method/test_app_cross_window_regression.py +259 -0
  219. yime/input_method/test_app_mode_feedback.py +220 -0
  220. yime/input_method/test_app_runtime_paths.py +46 -0
  221. yime/input_method/test_app_selection_feedback.py +44 -0
  222. yime/input_method/test_app_ui_settings.py +307 -0
  223. yime/input_method/test_candidate_box_bindings.py +1259 -0
  224. yime/input_method/test_candidate_box_layout_init.py +165 -0
  225. yime/input_method/test_candidate_box_manual_input.py +103 -0
  226. yime/input_method/test_candidate_window_system.py +39 -0
  227. yime/input_method/test_decoding_scenarios.py +170 -0
  228. yime/input_method/test_input_method.py +3400 -0
  229. yime/input_method/test_local_phrase_priority_baseline.py +88 -0
  230. yime/input_method/test_manual_input_resolver.py +21 -0
  231. yime/input_method/test_runtime_reverse_lookup.py +46 -0
  232. yime/input_method/test_user_lexicon.py +277 -0
  233. yime/input_method/test_user_lexicon_repair.py +75 -0
  234. yime/input_method/ui/__init__.py +5 -0
  235. yime/input_method/ui/candidate_box.py +1498 -0
  236. yime/input_method/ui/candidate_box_actions.py +1265 -0
  237. yime/input_method/ui/candidate_geometry.py +201 -0
  238. yime/input_method/ui/candidate_layout.py +280 -0
  239. yime/input_method/ui/candidate_renderer.py +362 -0
  240. yime/input_method/ui/candidate_system.py +203 -0
  241. yime/input_method/ui/manual_input_resolver.py +164 -0
  242. yime/input_method/ui/prefix_hint_panel.py +31 -0
  243. yime/input_method/utils/__init__.py +21 -0
  244. yime/input_method/utils/clipboard.py +44 -0
  245. yime/input_method/utils/keyboard_simulator.py +149 -0
  246. yime/input_method/utils/modifier_state.py +9 -0
  247. yime/input_method/utils/runtime_reverse_lookup.py +116 -0
  248. yime/input_method/utils/user_lexicon.py +1148 -0
  249. yime/input_method/utils/window_manager.py +318 -0
  250. yime/jsonpath_example.py +140 -0
  251. yime/key_to_code.json +59 -0
  252. yime/legacy/cleanup_test_rows.py +21 -0
  253. yime/legacy/db_checks.py +35 -0
  254. yime/legacy/db_inspect.py +25 -0
  255. yime/legacy/db_inspect_verbose.py +41 -0
  256. yime/legacy/db_table_list.py +45 -0
  257. yime/legacy/export_mappings_csv.py +47 -0
  258. yime/legacy/migrate_pinyin_table.py +50 -0
  259. yime/map_code_to_hanzi.py +243 -0
  260. yime/map_key_to_code.py +47 -0
  261. yime/map_pinyin_to_hanzi.py +118 -0
  262. yime/map_shouyin_to_code.py +47 -0
  263. yime/mappings_export.csv +1749 -0
  264. yime/migrations/apply_create_hanzi_mapping.py +24 -0
  265. yime/migrations/apply_create_mapping_queue.py +25 -0
  266. yime/migrations/assign_mapping_if_safe_999005.py +23 -0
  267. yime/migrations/backfill_mappings.py +130 -0
  268. yime/migrations/check_imported_samples.py +9 -0
  269. yime/migrations/check_mysql_conn.py +42 -0
  270. yime/migrations/check_mysql_conn_hosts.py +9 -0
  271. yime/migrations/check_recent_inserts.py +31 -0
  272. yime/migrations/choose_fix_999005_999006.py +132 -0
  273. yime/migrations/choose_fix_999005_interactive.py +103 -0
  274. yime/migrations/compose_audio_to_standard.py +59 -0
  275. yime/migrations/connect.py +63 -0
  276. yime/migrations/connect_to_mysql.py +53 -0
  277. yime/migrations/create_db_and_user.py +66 -0
  278. yime/migrations/create_mapping_queue.py +25 -0
  279. yime/migrations/create_placeholder_mappings.py +47 -0
  280. yime/migrations/create_user_noninteractive.py +37 -0
  281. yime/migrations/diagnose_fk.py +19 -0
  282. yime/migrations/enqueue_all_force.py +35 -0
  283. yime/migrations/enqueue_missing.py +18 -0
  284. yime/migrations/ensure_placeholders_for_fk.py +66 -0
  285. yime/migrations/ensure_placeholders_in_pinyin_mapping.py +70 -0
  286. yime/migrations/find_char_filters.py +10 -0
  287. yime/migrations/find_conflict_for_999005.py +29 -0
  288. yime/migrations/fix_missing_999005.py +54 -0
  289. yime/migrations/fix_missing_999005_final.py +68 -0
  290. yime/migrations/identify_import_issues.py +41 -0
  291. yime/migrations/import_from_json.py +27 -0
  292. yime/migrations/import_hanzi_phoneme_mapping.py +84 -0
  293. yime/migrations/import_missing_samples.py +11 -0
  294. yime/migrations/import_normalized_json.py +138 -0
  295. yime/migrations/insert_missing_999006.py +48 -0
  296. yime/migrations/insert_or_update_missing_samples.py +62 -0
  297. yime/migrations/insert_unique_abbc.py +44 -0
  298. yime/migrations/insert_unique_abbc_final.py +81 -0
  299. yime/migrations/inspect_queue.py +11 -0
  300. yime/migrations/mapping_queue.py +17 -0
  301. yime/migrations/remove_unused_placeholders.py +37 -0
  302. yime/migrations/resolve_duplicate_mappings.py +47 -0
  303. yime/migrations/run_backfill_queue.py +18 -0
  304. yime/migrations/run_import_with_logging.py +72 -0
  305. yime/migrations/set_mapping_ids_from_sample.py +32 -0
  306. yime/migrations/show_audio_fk_exact.py +17 -0
  307. yime/migrations/show_conflicts_and_missing.py +29 -0
  308. yime/migrations/show_fk_details.py +21 -0
  309. yime/migrations/sqlite_to_mysql.py +173 -0
  310. yime/migrations/temp.py +41 -0
  311. yime/migrations/update_mapping_999005.py +16 -0
  312. yime/migrations/verify_migration.py +93 -0
  313. yime/output_hanzi.py +45 -0
  314. yime/phoneme_dict.json +63 -0
  315. yime/pinyin_converter.py +80 -0
  316. yime/pinyin_db_manager.py +183 -0
  317. yime/pinyin_hanzi.json +56557 -0
  318. yime/pinyin_importer.py +434 -0
  319. yime/pinyin_mapping.py +114 -0
  320. yime/pinyin_normalized.json +1838 -0
  321. yime/pinyin_yinyuan.json +9 -0
  322. yime/prototype_queries.sql +56 -0
  323. yime/refresh_runtime_yime_codes.py +1933 -0
  324. yime/reverse_key_value_pairs.py +62 -0
  325. yime/run_db_setup.py +67 -0
  326. yime/run_full_import.py +167 -0
  327. yime/run_sample_import.py +62 -0
  328. yime/safe_test_unique.py +37 -0
  329. yime/safe_test_unique_ignore.py +31 -0
  330. yime/shengmu.csv +28 -0
  331. yime/syllable_code.json +1838 -0
  332. yime/syllable_decoder.py +143 -0
  333. yime/syllable_mapping.py +16 -0
  334. yime/syllable_structure.py +279 -0
  335. yime/test_db_manager.py +264 -0
  336. yime/test_db_manager_final.py +161 -0
  337. yime/test_db_manager_final_v2.py +204 -0
  338. yime/test_db_manager_real.py +159 -0
  339. yime/test_db_manager_refactored.py +312 -0
  340. yime/test_db_manager_simple.py +160 -0
  341. yime/test_db_manager_working.py +161 -0
  342. yime/test_duplicate_groups.py +28 -0
  343. yime/test_hanzi_db_manager.py +294 -0
  344. yime/test_hanzi_pinyin_data.py +168 -0
  345. yime/test_index_constraint.py +29 -0
  346. yime/test_pinyin_converter.py +291 -0
  347. yime/test_pinyin_db_manager.py +56 -0
  348. yime/test_pinyin_mapping.py +234 -0
  349. yime/test_syllable_decoder.py +177 -0
  350. yime/test_syllable_structure.py +191 -0
  351. yime/transform_dict_structure.py +67 -0
  352. yime/universal_mapping.json +1402 -0
  353. yime/update_table.py +78 -0
  354. yime/user_lexicon_seed.json +26 -0
  355. yime/utils_charfilter.py +34 -0
  356. yime/validate_json.py +63 -0
  357. yime/windows_candidate_box.py +467 -0
  358. yime/yinjie_mapping.json +33216 -0
  359. yime-0.1.0.dist-info/METADATA +98 -0
  360. yime-0.1.0.dist-info/RECORD +364 -0
  361. yime-0.1.0.dist-info/WHEEL +5 -0
  362. yime-0.1.0.dist-info/licenses/LICENSE +21 -0
  363. yime-0.1.0.dist-info/licenses/NOTICE.md +6 -0
  364. yime-0.1.0.dist-info/top_level.txt +6 -0
pianyin/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ """pianyin 包初始化文件"""
2
+ from .pianyin import Pianyin, PitchedPianyin, UnpitchedPianyin
3
+
4
+ __all__ = ['Pianyin', 'PitchedPianyin', 'UnpitchedPianyin']
@@ -0,0 +1,82 @@
1
+ import math
2
+
3
+
4
+ def calculate_pentatonic_value(f, f_min, f_max):
5
+ """
6
+ 计算调域五度值
7
+ :param f: 当前频率值(Hz)
8
+ :param f_min: 频率最小值(Hz)
9
+ :param f_max: 频率最大值(Hz)
10
+ :return: 五度值(1-5)
11
+ """
12
+ if f_min <= 0 or f_max <= 0 or f <= 0:
13
+ raise ValueError("频率值必须大于0")
14
+ if f < f_min or f > f_max:
15
+ raise ValueError("当前频率必须在最小和最大频率范围内")
16
+
17
+ numerator = math.log(f) - math.log(f_min)
18
+ denominator = math.log(f_max) - math.log(f_min)
19
+ pentatonic_value = 5 * (numerator / denominator)
20
+
21
+ return pentatonic_value
22
+
23
+
24
+ # 示例参数
25
+ f_max = 202 # 最大频率
26
+ f_min = 101 # 最小频率
27
+ f_current = 202 # 当前频率
28
+
29
+ # 计算五度值
30
+ try:
31
+ result = calculate_pentatonic_value(f_current, f_min, f_max)
32
+ print(f"五度值为: {result:.2f}")
33
+ except ValueError as e:
34
+ print(f"计算错误: {e}")
35
+
36
+ # 示例参数
37
+ f_max = 202 # 最大频率
38
+ f_min = 101 # 最小频率
39
+ f_current = 175.8 # 当前频率
40
+
41
+ # 计算五度值
42
+ try:
43
+ result = calculate_pentatonic_value(f_current, f_min, f_max)
44
+ print(f"五度值为: {result:.2f}")
45
+ except ValueError as e:
46
+ print(f"计算错误: {e}")
47
+
48
+ # 示例参数
49
+ f_max = 202 # 最大频率
50
+ f_min = 101 # 最小频率
51
+ f_current = 153 # 当前频率
52
+
53
+ # 计算五度值
54
+ try:
55
+ result = calculate_pentatonic_value(f_current, f_min, f_max)
56
+ print(f"五度值为: {result:.2f}")
57
+ except ValueError as e:
58
+ print(f"计算错误: {e}")
59
+
60
+ # 示例参数
61
+ f_max = 202 # 最大频率
62
+ f_min = 101 # 最小频率
63
+ f_current = 133.3 # 当前频率
64
+
65
+ # 计算五度值
66
+ try:
67
+ result = calculate_pentatonic_value(f_current, f_min, f_max)
68
+ print(f"五度值为: {result:.2f}")
69
+ except ValueError as e:
70
+ print(f"计算错误: {e}")
71
+
72
+ # 示例参数
73
+ f_max = 202 # 最大频率
74
+ f_min = 101 # 最小频率
75
+ f_current = 116 # 当前频率
76
+
77
+ # 计算五度值
78
+ try:
79
+ result = calculate_pentatonic_value(f_current, f_min, f_max)
80
+ print(f"五度值为: {result:.2f}")
81
+ except ValueError as e:
82
+ print(f"计算错误: {e}")
@@ -0,0 +1,64 @@
1
+ import math
2
+
3
+
4
+ def calculate_speech_pentatonic_value(f_current, f_min, f_max):
5
+ """
6
+ 计算话语调域五度值
7
+ :param f_current: 当前语音频率值(Hz)
8
+ :param f_min: 话语调域最低频率值(Hz)
9
+ :param f_max: 话语调域最高频率值(Hz)
10
+ :return: 五度值(1-5)
11
+ :raises ValueError: 如果输入参数无效
12
+ """
13
+ # 验证输入参数
14
+ if f_min <= 0 or f_max <= 0 or f_current <= 0:
15
+ raise ValueError("频率值必须大于0")
16
+ if f_min >= f_max:
17
+ raise ValueError("最低频率必须小于最高频率")
18
+ if f_current < f_min or f_current > f_max:
19
+ raise ValueError("当前频率必须在调域范围内")
20
+
21
+ # 计算五度值
22
+ log_range = math.log(f_max) - math.log(f_min)
23
+ position = (math.log(f_current) - math.log(f_min)) / log_range
24
+ pentatonic_value = 1 + 4 * position # 映射到1-5范围
25
+
26
+ return pentatonic_value
27
+
28
+
29
+ def analyze_speech_range(speech_samples):
30
+ """
31
+ 分析语音样本,获取调域范围
32
+ :param speech_samples: 语音频率样本列表(Hz)
33
+ :return: (f_min, f_max) 调域范围
34
+ :raises ValueError: 如果样本数据无效
35
+ """
36
+ if not speech_samples:
37
+ raise ValueError("语音样本不能为空")
38
+
39
+ return min(speech_samples), max(speech_samples)
40
+
41
+
42
+ # 示例使用
43
+ if __name__ == "__main__":
44
+ # 示例1: 已知调域范围
45
+ try:
46
+ f_min = 85 # 正常话语调域最低值示例(Hz)
47
+ f_max = 255 # 正常话语调域最高值示例(Hz)
48
+ current_pitch = 194 # 当前语音频率
49
+
50
+ result = calculate_speech_pentatonic_value(current_pitch, f_min, f_max)
51
+ print(f"五度值: {result:.2f}")
52
+ except ValueError as e:
53
+ print(f"计算错误: {e}")
54
+
55
+ # 示例2: 从语音样本分析
56
+ try:
57
+ speech_samples = [85, 112, 148, 194, 255] # 实际语音频率样本
58
+ f_min, f_max = analyze_speech_range(speech_samples)
59
+
60
+ for sample in speech_samples:
61
+ result = calculate_speech_pentatonic_value(sample, f_min, f_max)
62
+ print(f"频率 {sample}Hz 的五度值: {result:.2f}")
63
+ except ValueError as e:
64
+ print(f"分析错误: {e}")
@@ -0,0 +1,94 @@
1
+ import math
2
+
3
+
4
+ def sigmoid(x, center=0.5, steepness=10):
5
+ """Sigmoid函数实现平滑过渡"""
6
+ return 1 / (1 + math.exp(-steepness * (x - center)))
7
+
8
+
9
+ def calculate_speech_pentatonic_value(f_current, f_min, f_max, gender=None):
10
+ """
11
+ 计算符合五声调式五度制映射的话语调域五度值(改进版)
12
+ 改进点:
13
+ - 使用Sigmoid函数实现平滑过渡
14
+ - 增加性别参数适应不同基频范围
15
+
16
+ 规则:
17
+ - 使用对数划分,更符合五声调式的频率感知特性
18
+ - 通过Sigmoid函数实现五度值之间的平滑过渡
19
+ - 性别参数可调整划分边界(默认无差别)
20
+
21
+ :param f_current: 当前语音频率值(Hz)
22
+ :param f_min: 话语调域最低频率值(Hz)
23
+ :param f_max: 话语调域最高频率值(Hz)
24
+ :param gender: 性别参数('male'/'female'/None),用于调整划分边界
25
+ :return: 五度值(1-5)
26
+ :raises ValueError: 如果输入参数无效
27
+ """
28
+ # 验证输入参数
29
+ if f_min <= 0 or f_max <= 0 or f_current <= 0:
30
+ raise ValueError("频率值必须大于0")
31
+ if f_min >= f_max:
32
+ raise ValueError("最低频率必须小于最高频率")
33
+ if f_current < f_min or f_current > f_max:
34
+ raise ValueError("当前频率必须在调域范围内")
35
+
36
+ # 计算对数位置(更符合人耳对音高的感知)
37
+ log_min = math.log(f_min)
38
+ log_max = math.log(f_max)
39
+ log_current = math.log(f_current)
40
+ position = (log_current - log_min) / (log_max - log_min)
41
+
42
+ # 根据性别调整划分边界(女声整体偏高,男声整体偏低)
43
+ if gender == 'female':
44
+ boundaries = [0.15, 0.35, 0.55, 0.75] # 女声边界调整
45
+ elif gender == 'male':
46
+ boundaries = [0.25, 0.45, 0.65, 0.85] # 男声边界调整
47
+ else:
48
+ boundaries = [0.20, 0.40, 0.60, 0.80] # 默认边界
49
+
50
+ # 使用Sigmoid函数计算各度数的权重
51
+ w5 = sigmoid(position, boundaries[3]) # 5度权重
52
+ w4 = sigmoid(position, boundaries[2]) - w5 # 4度权重
53
+ w3 = sigmoid(position, boundaries[1]) - (w5 + w4) # 3度权重
54
+ w2 = sigmoid(position, boundaries[0]) - (w5 + w4 + w3) # 2度权重
55
+ w1 = 1 - (w5 + w4 + w3 + w2) # 1度权重
56
+
57
+ # 加权计算最终五度值
58
+ return 1 * w1 + 2 * w2 + 3 * w3 + 4 * w4 + 5 * w5
59
+
60
+
61
+ # 示例使用
62
+ if __name__ == "__main__":
63
+ # 示例参数
64
+ f_min = 100 # 最低频率(Hz)
65
+ f_max = 400 # 最高频率(Hz)
66
+
67
+ # 测试不同频率点
68
+ test_frequencies = [100, 120, 150, 200, 250, 300, 350, 400]
69
+
70
+ print("默认(无性别参数):")
71
+ for freq in test_frequencies:
72
+ try:
73
+ result = calculate_speech_pentatonic_value(freq, f_min, f_max)
74
+ print(f"频率 {freq}Hz 的五度值: {result:.2f}")
75
+ except ValueError as e:
76
+ print(f"频率 {freq}Hz 计算错误: {e}")
77
+
78
+ print("\n女声:")
79
+ for freq in test_frequencies:
80
+ try:
81
+ result = calculate_speech_pentatonic_value(
82
+ freq, f_min, f_max, 'female')
83
+ print(f"频率 {freq}Hz 的五度值: {result:.2f}")
84
+ except ValueError as e:
85
+ print(f"频率 {freq}Hz 计算错误: {e}")
86
+
87
+ print("\n男声:")
88
+ for freq in test_frequencies:
89
+ try:
90
+ result = calculate_speech_pentatonic_value(
91
+ freq, f_min, f_max, 'male')
92
+ print(f"频率 {freq}Hz 的五度值: {result:.2f}")
93
+ except ValueError as e:
94
+ print(f"频率 {freq}Hz 计算错误: {e}")
@@ -0,0 +1,100 @@
1
+ class ToneMedianCalculator:
2
+ """
3
+ 五度标调法调域中值计算器
4
+ 1. 计调单个调值中值:
5
+ 2. 计算单个调类中值:
6
+ 3. 计算整个调系中值:
7
+ """
8
+
9
+ @staticmethod
10
+ def calculate_tone_value_median(pitch_levels):
11
+ """
12
+ 计算单个调值中值
13
+ :param pitch_levels: 调值列表,如[5,5,5]代表阴平
14
+ :return: 调值中值
15
+ """
16
+ if not pitch_levels:
17
+ raise ValueError("调值列表不能为空")
18
+ return sum(pitch_levels) / len(pitch_levels)
19
+
20
+ @staticmethod
21
+ def calculate_tone_category_median(tone_values_medians):
22
+ """
23
+ 计算单个调类中值
24
+ :param tone_values_medians: 该调类的多个调值中值列表
25
+ :return: 调类中值
26
+ """
27
+ if not tone_values_medians:
28
+ raise ValueError("调值中值列表不能为空")
29
+ return sum(tone_values_medians) / len(tone_values_medians)
30
+
31
+ @staticmethod
32
+ def calculate_tone_system_median(category_medians):
33
+ """
34
+ 计算整个调系中值
35
+ :param category_medians: 各调类中值列表
36
+ :return: 调系中值
37
+ """
38
+ if not category_medians:
39
+ raise ValueError("调类中值列表不能为空")
40
+ return sum(category_medians) / len(category_medians)
41
+
42
+ @staticmethod
43
+ def calculate_variation_coefficient(values):
44
+ """计算变异系数(标准差/均值)评估离散程度"""
45
+ mean = sum(values) / len(values)
46
+ std_dev = (sum((x - mean)**2 for x in values) / len(values))**0.5
47
+ return std_dev / mean
48
+
49
+ # 应用示例
50
+ low_tone_vc = calculate_variation_coefficient(
51
+ [1.33, 4.00, 2.33]) # 上声变异系数
52
+ falling_tone_vc = calculate_variation_coefficient([3.33, 4.00]) # 去声变异系数
53
+
54
+
55
+ # 示例使用
56
+ if __name__ == "__main__":
57
+ # 1. 计算各调值中值
58
+ high_tone = ToneMedianCalculator.calculate_tone_value_median([5, 5, 5]) # 阴平
59
+ rising_tone = ToneMedianCalculator.calculate_tone_value_median([
60
+ 3, 4, 5]) # 阳平
61
+ low_tone1 = ToneMedianCalculator.calculate_tone_value_median([
62
+ 2, 1, 1]) # 上声1
63
+ low_tone2 = ToneMedianCalculator.calculate_tone_value_median([
64
+ 3, 4, 5]) # 上声2
65
+ low_tone3 = ToneMedianCalculator.calculate_tone_value_median([
66
+ 2, 1, 4]) # 上声3
67
+ falling_tone1 = ToneMedianCalculator.calculate_tone_value_median([
68
+ 5, 4, 1]) # 去声1
69
+ falling_tone2 = ToneMedianCalculator.calculate_tone_value_median([
70
+ 5, 4, 3]) # 去声2
71
+
72
+ print(f"阴平调值中值: {high_tone:.2f}")
73
+ print(f"阳平调值中值: {rising_tone:.2f}")
74
+ print(f"上声1调值中值: {low_tone1:.2f}")
75
+ print(f"上声2调值中值: {low_tone2:.2f}")
76
+ print(f"上声3调值中值: {low_tone3:.2f}")
77
+ print(f"去声1调值中值: {falling_tone1:.2f}")
78
+ print(f"去声2调值中值: {falling_tone2:.2f}")
79
+
80
+ # 2. 计算各调类中值
81
+ high_tone_median = ToneMedianCalculator.calculate_tone_category_median([
82
+ high_tone])
83
+ rising_tone_median = ToneMedianCalculator.calculate_tone_category_median([
84
+ rising_tone])
85
+ low_tone_median = ToneMedianCalculator.calculate_tone_category_median(
86
+ [low_tone1, low_tone2, low_tone3])
87
+ falling_tone_median = ToneMedianCalculator.calculate_tone_category_median([
88
+ falling_tone1, falling_tone2])
89
+
90
+ print(f"\n阴平调类中值: {high_tone_median:.2f}")
91
+ print(f"阳平调类中值: {rising_tone_median:.2f}")
92
+ print(f"上声调类中值: {low_tone_median:.2f}")
93
+ print(f"去声调类中值: {falling_tone_median:.2f}")
94
+
95
+ # 3. 计算调系中值
96
+ system_median = ToneMedianCalculator.calculate_tone_system_median([
97
+ high_tone_median, rising_tone_median, low_tone_median, falling_tone_median
98
+ ])
99
+
100
+ print(f"\n调系调域中值: {system_median:.2f}")
@@ -0,0 +1,64 @@
1
+ # 噪音类片音表示法
2
+ # 噪音类片音分为两类:清辅音和浊辅音。
3
+ # 清音没有音调,浊辅音类噪音有非规律性的音高特征。
4
+ # 浊辅音类噪音的音调不具有辨义作用,通常不标调。
5
+ # 在通用现代汉语中,噪音类片音就是声母的音值(分布在音节中的声母的实际发音)。
6
+ # 噪音类表示汉语音节的噪音
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Optional
10
+
11
+
12
+ class UnpitchedPianyin(ABC):
13
+ """噪音类片音,清音无音调,浊辅音可能有音调"""
14
+
15
+ def __init__(self, quality: str, duration: str = '', loudness: str = '', pitch: Optional[str] = None):
16
+ """
17
+ 初始化噪音对象
18
+ :param quality: 音质(必选)
19
+ :param duration: 音长(默认)
20
+ :param loudness: 音强(默认)
21
+ :param pitch: 音调(可选),None表示清音无音调,不传或特定值表示浊辅音可能有音调
22
+ """
23
+ self.quality = quality
24
+ self.duration = duration
25
+ self.loudness = loudness
26
+ self.pitch = pitch
27
+
28
+ def clear_pitch(self) -> None:
29
+ """明确定义为清音(无音调)"""
30
+ self.pitch = None
31
+
32
+ @abstractmethod
33
+ def is_valid(self) -> bool:
34
+ """检查噪音对象是否有效,子类必须实现"""
35
+ pass
36
+
37
+ def __str__(self) -> str:
38
+ """返回噪音的通用字符串表示"""
39
+ return (f"{self.__class__.__name__}(quality='{self.quality}'"
40
+ f"{f', pitch={repr(self.pitch)}' if self.pitch is not None else ''}"
41
+ f"{f', duration={repr(self.duration)}' if self.duration else ''}"
42
+ f"{f', loudness={repr(self.loudness)}' if self.loudness else ''})")
43
+
44
+
45
+ class ClearPianyin(UnpitchedPianyin):
46
+ """清音类噪音,无音调"""
47
+
48
+ def __init__(self, quality: str, duration: str = '', loudness: str = ''):
49
+ super().__init__(quality=quality, duration=duration, loudness=loudness, pitch=None)
50
+
51
+ def is_valid(self) -> bool:
52
+ """清音只需要有音质即可有效"""
53
+ return bool(self.quality)
54
+
55
+
56
+ class VoicedUnpitchedPianyin(UnpitchedPianyin):
57
+ """浊辅音类噪音,可能有音调"""
58
+
59
+ def __init__(self, quality: str, pitch: Optional[str] = None, duration: str = '', loudness: str = ''):
60
+ super().__init__(quality=quality, pitch=pitch, duration=duration, loudness=loudness)
61
+
62
+ def is_valid(self) -> bool:
63
+ """浊辅音需要有音质,音调可选"""
64
+ return bool(self.quality)
pianyin/initial.py ADDED
@@ -0,0 +1,112 @@
1
+ # Initial Sound Representation Module
2
+ # 首音表示模块
3
+ #
4
+ # This module provides a class to represent the initial sound in Chinese phonetics,
5
+ # supporting multiple representation methods including pinyin, phonetic, pianyin and yinyuan.
6
+ #
7
+ # 首音: 由声母和与声母联结的调段构成的音段
8
+ # 声母 (initial): 必选属性
9
+ # 首调: 与声母联结的调段, 可选属性, 默认值 None
10
+ #
11
+ # The initial sound is a syllable segment composed of the initial consonant and the tone segment connected to the initial consonant
12
+ # The initial consonant is a required attribute
13
+ # The tone of the initial sound (the tone segment connected to the initial consonant), named initial tone, is an optional attribute
14
+
15
+ from pianyin.pianyin import UnpitchedPianyin # Ensure pianyin/pianyin.py exists and contains UnpitchedPianyin
16
+
17
+
18
+ class Initial(UnpitchedPianyin):
19
+ """
20
+ Represents the initial sound of a Chinese syllable.
21
+ 表示汉语音节的首音
22
+
23
+ Attributes:
24
+ quality (str): The quality of the initial consonant (声母/initial)
25
+ tone_segment (str|None): The tone segment using 5-level notation (0-5)
26
+ representation (str): Representation method (pinyin/phonetic/pianyin/yinyuan)
27
+ pitch_style (str): Tone display style ('number' or 'mark')
28
+ """
29
+ # 以下内容保持不变...
30
+ TONE_SEGMENT_MARKS = {
31
+ "5": "˥", # 高平
32
+ "4": "˦", # 次高平调
33
+ "3": "˧", # 中平调
34
+ "2": "˨", # 次低平调
35
+ "1": "˩", # 低调
36
+ "0": "꜌" # 与清声母联结的调段
37
+ }
38
+
39
+ def __init__(self, consonant, tone_segment=None, representation='pinyin', pitch_style='number'):
40
+ """
41
+ Initialize an Initial instance.
42
+ 初始化首音实例
43
+
44
+ Args:
45
+ consonant (str): The initial consonant (声母/initial)
46
+ tone_segment (str|None): The tone segment (0-5) or None for no tone segment (与清声母联结的调段)
47
+ representation (str): Representation method (default 'pinyin')
48
+ pitch_style (str): Tone display style ('number' or 'mark', default 'number')
49
+ """
50
+ super().__init__(quality=consonant)
51
+ self.tone_segment = str(
52
+ tone_segment) if tone_segment is not None else None
53
+ self.representation = representation.lower()
54
+ self.pitch_style = pitch_style.lower()
55
+
56
+ self._validate_input()
57
+
58
+ def _validate_input(self):
59
+ """Validate the input parameters. 验证输入参数"""
60
+ if not isinstance(self.quality, str) or not self.quality:
61
+ raise ValueError("Consonant must be a non-empty string")
62
+
63
+ if self.tone_segment is not None and self.tone_segment not in ["0", "1", "2", "3", "4", "5"]:
64
+ raise ValueError("Tone segment must be between 0-5 or None")
65
+
66
+ if self.representation not in ['pinyin', 'phonetic', 'pianyin', 'yinyuan']:
67
+ raise ValueError("Invalid representation method")
68
+
69
+ if self.pitch_style not in ['number', 'mark']:
70
+ raise ValueError("Tone style must be 'number' or 'mark'")
71
+
72
+ def _get_tone_mark(self):
73
+ """Get tone mark for current tone segment. 获取当前调段的符号标调"""
74
+ if self.tone_segment is None:
75
+ return ""
76
+ return self.TONE_SEGMENT_MARKS.get(self.tone_segment, "")
77
+
78
+ def __repr__(self):
79
+ """General string representation. 一般字符串表示"""
80
+ return f"Initial(quality={self.quality!r}, tone_segment={self.tone_segment!r}, representation={self.representation!r}, pitch_style={self.pitch_style!r})"
81
+
82
+ def __str__(self):
83
+ """简式字符串表示. 只包含必选项 consonant"""
84
+ return f"{self.quality}"
85
+
86
+ @classmethod
87
+ def generate_from_consonant_table(cls, consonant_table, representation='pinyin', pitch_style='number'):
88
+ """
89
+ 从声母表生成首音对象映射
90
+ Args:
91
+ consonant_table (list/dict): 声母列表或字典
92
+ representation (str): 表示方法
93
+ pitch_style (str): pitch_style
94
+ Returns:
95
+ dict: 声母到首音对象的映射
96
+ """
97
+ if isinstance(consonant_table, list):
98
+ return {consonant: cls(consonant, representation=representation, pitch_style=pitch_style)
99
+ for consonant in consonant_table}
100
+ elif isinstance(consonant_table, dict):
101
+ # 处理嵌套字典结构,只使用键(拼音字母)作为声母
102
+ return {k: cls(k, representation=representation, pitch_style=pitch_style)
103
+ for k in consonant_table.keys()}
104
+ else:
105
+ raise ValueError("consonant_table 必须是列表或字典")
106
+
107
+ def is_valid(self) -> bool:
108
+ """检查首音对象是否有效"""
109
+ return bool(self.quality) and (
110
+ self.tone_segment is None or
111
+ self.tone_segment in ["0", "1", "2", "3", "4", "5"]
112
+ )
pianyin/pianyin.py ADDED
@@ -0,0 +1,66 @@
1
+ # 片音(Pianyin)表示法
2
+ # 片音具有音质、音调、音长和音强四个属性
3
+ # 在通用现代汉语中,片音的音质和音调是必选的,音长和音强是默认的
4
+ # 片音类表示汉语音节的片音
5
+ # Pianyin notation
6
+ # A Pianyin has four attributes: quality, pitch, duration, and loudness
7
+ # In Standard Modern Chinese, quality and pitch are required, while duration and loudness are default
8
+ # The Pianyin class represents the phonetic components of a Chinese syllable
9
+ # 片音(Pianyin)分成噪音和乐音两类
10
+ # 噪音的音调是空,音质就是噪音
11
+ # 乐音的音调非空,音质和音调构成乐音
12
+
13
+ from abc import ABC, abstractmethod
14
+ from typing import Optional
15
+
16
+
17
+ class Pianyin(ABC):
18
+ """片音(Pianyin)抽象基类,表示汉语音节的片音"""
19
+
20
+ def __init__(self, quality: str, pitch: Optional[str] = None,
21
+ duration: str = 'neutral', loudness: str = 'neutral'):
22
+ """
23
+ 初始化片音对象
24
+ :param quality: 音质(必选)
25
+ :param pitch: 音调(乐音必选,噪音可选)
26
+ :param duration: 音长(默认)
27
+ :param loudness: 音强(默认)
28
+ """
29
+ self.quality = quality
30
+ self.pitch = pitch
31
+ self.duration = duration
32
+ self.loudness = loudness
33
+
34
+ @abstractmethod
35
+ def is_valid(self) -> bool:
36
+ """检查片音对象是否有效,子类必须实现"""
37
+ pass
38
+
39
+ def __str__(self) -> str:
40
+ """返回片音的通用字符串表示"""
41
+ return (f"{self.__class__.__name__}(quality='{self.quality}'"
42
+ f"{f', pitch={repr(self.pitch)}' if self.pitch is not None else ''}"
43
+ f"{f', duration={repr(self.duration)}' if self.duration else ''}"
44
+ f"{f', loudness={repr(self.loudness)}' if self.loudness else ''})")
45
+
46
+
47
+ class UnpitchedPianyin(Pianyin):
48
+ """噪音类片音,音调为空,音质为噪音"""
49
+
50
+ def __init__(self, quality: str, duration: str = 'neutral', loudness: str = 'neutral'):
51
+ super().__init__(quality=quality, pitch=None, duration=duration, loudness=loudness)
52
+
53
+ def is_valid(self) -> bool:
54
+ """噪音只需要有音质即可有效"""
55
+ return bool(self.quality)
56
+
57
+
58
+ class PitchedPianyin(Pianyin):
59
+ """乐音类片音,音调非空,音质和音调构成乐音"""
60
+
61
+ def __init__(self, quality: str, pitch: str, duration: str = 'neutral', loudness: str = 'neutral'):
62
+ super().__init__(quality=quality, pitch=pitch, duration=duration, loudness=loudness)
63
+
64
+ def is_valid(self) -> bool:
65
+ """乐音需要有音质和音调才能有效"""
66
+ return bool(self.quality and self.pitch)