batchalign 0.8.2.post6__tar.gz → 0.8.2.post8__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 (158) hide show
  1. {batchalign-0.8.2.post6/batchalign.egg-info → batchalign-0.8.2.post8}/PKG-INFO +2 -1
  2. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/cli/cli.py +26 -0
  3. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/cli/dispatch.py +8 -0
  4. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/document.py +11 -8
  5. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/analysis/compare.py +44 -16
  6. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/dispatch.py +6 -0
  7. batchalign-0.8.2.post8/batchalign/pipelines/segmentation/__init__.py +15 -0
  8. batchalign-0.8.2.post8/batchalign/pipelines/segmentation/cantonese_seg.py +109 -0
  9. batchalign-0.8.2.post8/batchalign/version +3 -0
  10. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8/batchalign.egg-info}/PKG-INFO +2 -1
  11. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign.egg-info/SOURCES.txt +2 -0
  12. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign.egg-info/requires.txt +1 -0
  13. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/setup.py +1 -0
  14. batchalign-0.8.2.post6/batchalign/version +0 -3
  15. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/LICENSE +0 -0
  16. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/MANIFEST.in +0 -0
  17. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/README.md +0 -0
  18. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/__init__.py +0 -0
  19. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/__main__.py +0 -0
  20. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/cli/__init__.py +0 -0
  21. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/cli/bench.py +0 -0
  22. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/cli/cache.py +0 -0
  23. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/constants.py +0 -0
  24. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/errors.py +0 -0
  25. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/__init__.py +0 -0
  26. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/base.py +0 -0
  27. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/__init__.py +0 -0
  28. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/file.py +0 -0
  29. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/generator.py +0 -0
  30. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/lexer.py +0 -0
  31. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/parser.py +0 -0
  32. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/chat/utils.py +0 -0
  33. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/textgrid/__init__.py +0 -0
  34. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/textgrid/file.py +0 -0
  35. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/textgrid/generator.py +0 -0
  36. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/formats/textgrid/parser.py +0 -0
  37. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/__init__.py +0 -0
  38. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/audio_io.py +0 -0
  39. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/resolve.py +0 -0
  40. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/speaker/__init__.py +0 -0
  41. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/speaker/config.yaml +0 -0
  42. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/speaker/infer.py +0 -0
  43. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/speaker/utils.py +0 -0
  44. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/training/__init__.py +0 -0
  45. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/training/run.py +0 -0
  46. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/training/utils.py +0 -0
  47. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utils.py +0 -0
  48. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/__init__.py +0 -0
  49. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/cantonese_infer.py +0 -0
  50. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/dataset.py +0 -0
  51. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/execute.py +0 -0
  52. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/infer.py +0 -0
  53. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/prep.py +0 -0
  54. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/utterance/train.py +0 -0
  55. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/wave2vec/__init__.py +0 -0
  56. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/wave2vec/infer_fa.py +0 -0
  57. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/whisper/__init__.py +0 -0
  58. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/whisper/infer_asr.py +0 -0
  59. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/models/whisper/infer_fa.py +0 -0
  60. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/__init__.py +0 -0
  61. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/analysis/__init__.py +0 -0
  62. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/analysis/eval.py +0 -0
  63. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/__init__.py +0 -0
  64. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2chinese.py +0 -0
  65. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/__init__.py +0 -0
  66. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/deu.py +0 -0
  67. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/ell.py +0 -0
  68. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/eng.py +0 -0
  69. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/eus.py +0 -0
  70. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/fra.py +0 -0
  71. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/hrv.py +0 -0
  72. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/ind.py +0 -0
  73. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/jpn.py +0 -0
  74. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/nld.py +0 -0
  75. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/por.py +0 -0
  76. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/spa.py +0 -0
  77. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/num2lang/tha.py +0 -0
  78. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/oai_whisper.py +0 -0
  79. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/rev.py +0 -0
  80. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/utils.py +0 -0
  81. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/whisper.py +0 -0
  82. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/asr/whisperx.py +0 -0
  83. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/avqi/__init__.py +0 -0
  84. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/avqi/engine.py +0 -0
  85. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/base.py +0 -0
  86. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cache.py +0 -0
  87. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/__init__.py +0 -0
  88. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/cleanup.py +0 -0
  89. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/disfluencies.py +0 -0
  90. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/parse_support.py +0 -0
  91. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/retrace.py +0 -0
  92. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/support/filled_pauses.eng +0 -0
  93. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/support/replacements.eng +0 -0
  94. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/cleanup/support/test.test +0 -0
  95. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/diarization/__init__.py +0 -0
  96. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/diarization/pyannote.py +0 -0
  97. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/fa/__init__.py +0 -0
  98. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/fa/wave2vec_fa.py +0 -0
  99. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/fa/whisper_fa.py +0 -0
  100. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/__init__.py +0 -0
  101. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/coref.py +0 -0
  102. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/en/irr.py +0 -0
  103. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/fr/apm.py +0 -0
  104. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/fr/apmn.py +0 -0
  105. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/fr/case.py +0 -0
  106. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/ja/verbforms.py +0 -0
  107. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/morphosyntax/ud.py +0 -0
  108. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/opensmile/__init__.py +0 -0
  109. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/opensmile/engine.py +0 -0
  110. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/pipeline.py +0 -0
  111. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/speaker/__init__.py +0 -0
  112. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/speaker/nemo_speaker.py +0 -0
  113. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/translate/__init__.py +0 -0
  114. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/translate/gtrans.py +0 -0
  115. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/translate/seamless.py +0 -0
  116. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/translate/utils.py +0 -0
  117. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utr/__init__.py +0 -0
  118. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utr/rev_utr.py +0 -0
  119. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utr/utils.py +0 -0
  120. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utr/whisper_utr.py +0 -0
  121. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utterance/__init__.py +0 -0
  122. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/pipelines/utterance/ud_utterance.py +0 -0
  123. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/__init__.py +0 -0
  124. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/cli/test_dispatch_memory.py +0 -0
  125. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/conftest.py +0 -0
  126. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/chat/test_chat_file.py +0 -0
  127. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/chat/test_chat_generator.py +0 -0
  128. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/chat/test_chat_lexer.py +0 -0
  129. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/chat/test_chat_parser.py +0 -0
  130. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/chat/test_chat_utils.py +0 -0
  131. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/formats/textgrid/test_textgrid.py +0 -0
  132. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/models/test_audio_io.py +0 -0
  133. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/models/test_audio_lazy.py +0 -0
  134. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/analysis/test_eval.py +0 -0
  135. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/asr/test_asr_pipeline.py +0 -0
  136. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/asr/test_asr_utils.py +0 -0
  137. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/cache/__init__.py +0 -0
  138. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/cache/test_cache.py +0 -0
  139. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/cleanup/test_disfluency.py +0 -0
  140. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/cleanup/test_parse_support.py +0 -0
  141. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/fa/test_fa_pipeline.py +0 -0
  142. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/fa/test_fa_short_segments.py +0 -0
  143. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/fixures.py +0 -0
  144. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/test_pipeline.py +0 -0
  145. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/pipelines/test_pipeline_models.py +0 -0
  146. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/tests/test_document.py +0 -0
  147. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/__init__.py +0 -0
  148. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/abbrev.py +0 -0
  149. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/compounds.py +0 -0
  150. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/config.py +0 -0
  151. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/device.py +0 -0
  152. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/dp.py +0 -0
  153. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/names.py +0 -0
  154. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign/utils/utils.py +0 -0
  155. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign.egg-info/dependency_links.txt +0 -0
  156. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign.egg-info/entry_points.txt +0 -0
  157. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/batchalign.egg-info/top_level.txt +0 -0
  158. {batchalign-0.8.2.post6 → batchalign-0.8.2.post8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: batchalign
3
- Version: 0.8.2.post6
3
+ Version: 0.8.2.post8
4
4
  Summary: Python Speech Language Sample Analysis
5
5
  Author: Brian MacWhinney, Houjun Liu
6
6
  Author-email: macw@cmu.edu, houjun@cmu.edu
@@ -42,6 +42,7 @@ Requires-Dist: rich-click>=1.7.0
42
42
  Requires-Dist: typing-extensions
43
43
  Requires-Dist: num2words
44
44
  Requires-Dist: tiktoken
45
+ Requires-Dist: pycantonese
45
46
  Requires-Dist: blobfile
46
47
  Requires-Dist: sentencepiece
47
48
  Requires-Dist: googletrans
@@ -348,6 +348,32 @@ def utseg(ctx, in_dir, out_dir, lang, num_speakers, **kwargs):
348
348
  in_dir, out_dir,
349
349
  loader, writer, C)
350
350
 
351
+ #################### SEGMENT ################################
352
+
353
+ @batchalign.command()
354
+ @common_options
355
+ @click.option("--lang",
356
+ help="sample language in three-letter ISO 3166-1 alpha-3 code",
357
+ show_default=True,
358
+ default="yue",
359
+ type=str)
360
+ @click.option("--merge-abbrev/--no-merge-abbrev",
361
+ default=False, help="Merge abbreviations in output. Default: no.")
362
+ @click.pass_context
363
+ def segment(ctx, in_dir, out_dir, lang, **kwargs):
364
+ """Perform word segmentation on transcripts (Cantonese)."""
365
+ from batchalign.formats.chat import CHATFile
366
+
367
+ def loader(file):
368
+ return CHATFile(path=os.path.abspath(file)).doc
369
+
370
+ def writer(doc, output):
371
+ CHATFile(doc=doc).write(output, merge_abbrev=kwargs.get("merge_abbrev", False))
372
+
373
+ _dispatch("segment", lang, 1, ["cha"], ctx,
374
+ in_dir, out_dir,
375
+ loader, writer, C)
376
+
351
377
  #################### BENCHMARK ################################
352
378
 
353
379
  @batchalign.command()
@@ -70,6 +70,7 @@ POOL_SAFE_ENGINES = {
70
70
  "replacement",
71
71
  "ngram",
72
72
  "compare_analysis_engine",
73
+ "cantonese_seg",
73
74
  }
74
75
 
75
76
  warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')
@@ -234,6 +235,11 @@ def _run_pipeline_for_file(command, pipeline, file, output, loader_info, writer_
234
235
  with open(json_path, 'w') as f:
235
236
  _json.dump(metrics, f)
236
237
 
238
+ elif command == "segment":
239
+ doc = CHATFile(path=os.path.abspath(file)).doc
240
+ doc = pipeline(doc, callback=progress_callback)
241
+ CHATFile(doc=doc).write(output)
242
+
237
243
  elif command == "opensmile":
238
244
  from batchalign.document import Document
239
245
  doc = Document.new(media_path=file, lang=local_kwargs.get("lang", kwargs.get("lang", "eng")))
@@ -386,6 +392,7 @@ Cmd2Task = {
386
392
  "translate": "translate",
387
393
  "opensmile": "opensmile",
388
394
  "compare": "compare,compare_analysis",
395
+ "segment": "segment",
389
396
  }
390
397
 
391
398
  # this is the main runner used by all functions
@@ -406,6 +413,7 @@ def _dispatch(command, lang, num_speakers,
406
413
  "benchmark",
407
414
  "opensmile",
408
415
  "compare",
416
+ "segment",
409
417
  }
410
418
  if command in worker_handled:
411
419
  # Avoid pickling CLI-local loader/writer functions when the worker
@@ -27,14 +27,15 @@ class Task(IntEnum):
27
27
  DISFLUENCY_ANALYSIS = 6
28
28
  RETRACE_ANALYSIS = 7
29
29
  UTTERANCE_TIMING_RECOVERY = 8 # "bulletize"
30
- FORCED_ALIGNMENT = 9
31
- FEATURE_EXTRACT = 10
32
- MORPHOSYNTAX = 11
33
- COREF = 12
34
- WER = 13
35
- TRANSLATE = 14
36
- COMPARE = 15
37
- COMPARE_ANALYSIS = 16
30
+ WORD_SEGMENTATION = 9
31
+ FORCED_ALIGNMENT = 10
32
+ FEATURE_EXTRACT = 11
33
+ MORPHOSYNTAX = 12
34
+ COREF = 13
35
+ WER = 14
36
+ TRANSLATE = 15
37
+ COMPARE = 16
38
+ COMPARE_ANALYSIS = 17
38
39
 
39
40
 
40
41
  DEBUG__G = 0
@@ -51,6 +52,7 @@ TypeMap = {
51
52
  Task.SPEAKER_RECOGNITION: TaskType.PROCESSING,
52
53
  Task.UTTERANCE_SEGMENTATION: TaskType.PROCESSING,
53
54
  Task.UTTERANCE_TIMING_RECOVERY: TaskType.PROCESSING,
55
+ Task.WORD_SEGMENTATION: TaskType.PROCESSING,
54
56
  Task.FORCED_ALIGNMENT: TaskType.PROCESSING,
55
57
  Task.MORPHOSYNTAX: TaskType.PROCESSING,
56
58
  Task.FEATURE_EXTRACT: TaskType.ANALYSIS,
@@ -73,6 +75,7 @@ TaskFriendlyName = {
73
75
  Task.SPEAKER_RECOGNITION: "Speaker Recognition",
74
76
  Task.UTTERANCE_SEGMENTATION: "Utterance Segmentation",
75
77
  Task.UTTERANCE_TIMING_RECOVERY: "Utterance Timing Recovery",
78
+ Task.WORD_SEGMENTATION: "Word Segmentation",
76
79
  Task.FORCED_ALIGNMENT: "Forced Alignment",
77
80
  Task.MORPHOSYNTAX: "Morpho-Syntax",
78
81
  Task.FEATURE_EXTRACT: "Feature Extraction",
@@ -329,6 +329,10 @@ class CompareEngine(BatchalignEngine):
329
329
  # window in the remaining main tokens, then run Levenshtein inside
330
330
  # that window to produce the annotations.
331
331
  utt_positioned = {i: [] for i in range(len(gold_utterances))}
332
+ # Track which main (child) forms land in each gold utterance's window
333
+ utt_main_forms = {i: [] for i in range(len(gold_utterances))}
334
+ # Track which main utterance(s) contribute to each gold utterance
335
+ utt_main_speakers = {i: [] for i in range(len(gold_utterances))}
332
336
  search_start = 0
333
337
 
334
338
  for utt_idx in range(len(gold_utterances)):
@@ -345,6 +349,17 @@ class CompareEngine(BatchalignEngine):
345
349
  abs_start = search_start + win_start
346
350
  abs_end = search_start + win_end
347
351
 
352
+ # Collect unique main forms in the window (deduplicate
353
+ # across conformed expansions that map to the same form)
354
+ seen_main = set()
355
+ for j in range(abs_start, abs_end):
356
+ orig_idx = main_map[j]
357
+ if orig_idx not in seen_main:
358
+ seen_main.add(orig_idx)
359
+ m_utt_idx, m_form_idx, m_form = main_info[orig_idx]
360
+ utt_main_forms[utt_idx].append(m_form)
361
+ utt_main_speakers[utt_idx].append(m_utt_idx)
362
+
348
363
  # Align the chosen window against this gold utterance
349
364
  window_main = conformed_main[abs_start:abs_end]
350
365
  utt_alignment = align(window_main, g_tokens, False, match_fn)
@@ -363,11 +378,6 @@ class CompareEngine(BatchalignEngine):
363
378
  gold_form = gold_info[orig_gold_idx][2]
364
379
  last_gold_form_idx = gold_form_idx
365
380
 
366
- # Copy timing from main; keep gold's own morphology
367
- if main_form.time is not None:
368
- gold_form.time = main_form.time
369
- gold_form.dependency = None
370
-
371
381
  utt_positioned[utt_idx].append((gold_form_idx, CompareToken(
372
382
  text=item.key,
373
383
  pos=_get_pos(gold_form),
@@ -383,8 +393,6 @@ class CompareEngine(BatchalignEngine):
383
393
  gold_form = gold_info[orig_gold_idx][2]
384
394
  last_gold_form_idx = gold_form_idx
385
395
 
386
- gold_form.dependency = None
387
-
388
396
  utt_positioned[utt_idx].append((gold_form_idx, CompareToken(
389
397
  text=item.key,
390
398
  pos=_get_pos(gold_form),
@@ -415,21 +423,41 @@ class CompareEngine(BatchalignEngine):
415
423
  pos="PUNCT",
416
424
  status="match"
417
425
  )))
418
- # Ensure punctuation forms have morphology so %mor includes
419
- # the final delimiter (matches generator's PUNCT|<char> logic)
420
- if form.morphology is None:
421
- form.morphology = [Morphology(
422
- pos="PUNCT", lemma=form.text.strip(), feats=""
423
- )]
424
- form.dependency = None
425
426
  utt_positioned[utt_idx].sort(key=lambda x: x[0])
426
427
 
427
- # --- 7. Set comparison on each gold utterance ---
428
+ # --- 7. Replace each gold utterance's main line with the child's
429
+ # (main/proposal) forms and set the child's speaker. ---
428
430
  for utt_idx, utt in enumerate(gold_utterances):
429
431
  tokens = [tok for _, tok in utt_positioned[utt_idx]]
430
432
  utt.comparison = tokens if tokens else None
431
433
 
432
- timed_forms = [form for form in utt.content if form.time is not None]
434
+ # Replace content with the child/main forms from the window
435
+ child_forms = utt_main_forms[utt_idx]
436
+ if child_forms:
437
+ # Grab the ending punctuation from gold so the utterance
438
+ # delimiter is preserved
439
+ gold_ending = [
440
+ f for f in utt.content
441
+ if f.text.strip() in ENDING_PUNCT
442
+ ]
443
+ # Strip dependency from child forms (morphosyntax was run
444
+ # on the main doc separately; gold structure differs)
445
+ for f in child_forms:
446
+ f.dependency = None
447
+ utt.content = child_forms + gold_ending
448
+
449
+ # Set speaker from the child utterance that contributed
450
+ # the most forms to this window
451
+ speaker_indices = utt_main_speakers[utt_idx]
452
+ if speaker_indices:
453
+ # Pick the most common main utterance index
454
+ majority_utt_idx = max(
455
+ set(speaker_indices), key=speaker_indices.count
456
+ )
457
+ utt.tier = main_utterances[majority_utt_idx].tier
458
+
459
+ # Derive utterance timing from the (now-child) forms
460
+ timed_forms = [f for f in utt.content if f.time is not None]
433
461
  if timed_forms:
434
462
  utt.time = (timed_forms[0].time[0], timed_forms[-1].time[1])
435
463
  utt.text = None
@@ -16,6 +16,7 @@ DEFAULT_PACKAGES = {
16
16
  "asr": "whisper_oai",
17
17
  "utr": "whisper_utr",
18
18
  "fa": "whisper_fa",
19
+ "segment": "cantonese_seg",
19
20
  "speaker": "pyannote",
20
21
  "morphosyntax": "stanza",
21
22
  "disfluency": "replacement",
@@ -56,6 +57,8 @@ def resolve_engine_specs(pkg_str, lang, num_speakers=None, **arg_overrides):
56
57
  if "fa" in packages:
57
58
  if "utr" not in packages:
58
59
  packages.append("utr")
60
+ if "segment" not in packages and lang == "yue":
61
+ packages.append("segment")
59
62
 
60
63
  overrides = LANGUAGE_OVERRIDE_PACKAGES.get(lang, {})
61
64
  specs = []
@@ -123,6 +126,9 @@ def dispatch_pipeline(pkg_str, lang, num_speakers=None, **arg_overrides):
123
126
  elif engine == "ngram":
124
127
  from batchalign.pipelines.cleanup import NgramRetraceEngine
125
128
  engines.append(NgramRetraceEngine())
129
+ elif engine == "cantonese_seg":
130
+ from batchalign.pipelines.segmentation import CantoneseSegmentationEngine
131
+ engines.append(CantoneseSegmentationEngine())
126
132
  elif engine == "whisper_fa":
127
133
  from batchalign.pipelines.fa import WhisperFAEngine
128
134
  engines.append(WhisperFAEngine())
@@ -0,0 +1,15 @@
1
+ """
2
+ Segmentation Pipeline Module
3
+
4
+ Provides engines for word segmentation of unsegmented text.
5
+ Uses lazy loading to avoid importing heavy dependencies until needed.
6
+ """
7
+
8
+ def __getattr__(name):
9
+ """Lazy loading for segmentation engines."""
10
+ if name == 'CantoneseSegmentationEngine':
11
+ from .cantonese_seg import CantoneseSegmentationEngine
12
+ return CantoneseSegmentationEngine
13
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
14
+
15
+ __all__ = ['CantoneseSegmentationEngine']
@@ -0,0 +1,109 @@
1
+ from batchalign.document import *
2
+ from batchalign.pipelines.base import *
3
+ from batchalign.utils.dp import *
4
+ from batchalign.constants import *
5
+
6
+ import logging
7
+ L = logging.getLogger("batchalign")
8
+
9
+
10
+ class CantoneseSegmentationEngine(BatchalignEngine):
11
+ tasks = [Task.WORD_SEGMENTATION]
12
+
13
+ def __init__(self):
14
+ import pycantonese
15
+ self.__segment = pycantonese.segment
16
+
17
+ def process(self, doc: Document, **kwargs):
18
+ # no-op for non-Cantonese
19
+ if "yue" not in doc.langs:
20
+ L.debug("CantoneseSegmentation: skipping non-Cantonese document.")
21
+ return doc
22
+
23
+ for ut in doc.content:
24
+ if not isinstance(ut, Utterance):
25
+ continue
26
+
27
+ # separate regular words from punctuation/features at the end
28
+ regular_forms = []
29
+ trailing = []
30
+ for f in ut.content:
31
+ if f.type in (TokenType.PUNCT, TokenType.FEAT, TokenType.ANNOT):
32
+ trailing.append(f)
33
+ else:
34
+ # if we already started collecting trailing punct,
35
+ # but hit another regular token, flush trailing back
36
+ if trailing:
37
+ regular_forms.extend(trailing)
38
+ trailing = []
39
+ regular_forms.append(f)
40
+
41
+ if not regular_forms:
42
+ continue
43
+
44
+ # combine all regular token text (no spaces for CJK)
45
+ original_text = "".join(f.text for f in regular_forms)
46
+ if not original_text.strip():
47
+ continue
48
+
49
+ # run pycantonese segmentation
50
+ segmented_words = self.__segment(original_text)
51
+
52
+ # use character-level DP alignment to map segmented words
53
+ # back onto original tokens
54
+ #
55
+ # reference: chars from original tokens, payload = index into regular_forms
56
+ ref_targets = []
57
+ for i, f in enumerate(regular_forms):
58
+ for ch in f.text:
59
+ ref_targets.append(ReferenceTarget(ch, payload=i))
60
+
61
+ # payload: chars from segmented words, payload = index into segmented_words
62
+ pay_targets = []
63
+ for i, word in enumerate(segmented_words):
64
+ for ch in word:
65
+ pay_targets.append(PayloadTarget(ch, payload=i))
66
+
67
+ alignments = align(pay_targets, ref_targets, tqdm=False)
68
+
69
+ # build a mapping: segmented word index -> list of original token indices
70
+ seg_to_orig: dict[int, list[int]] = {}
71
+ for elem in alignments:
72
+ if isinstance(elem, Match):
73
+ seg_to_orig.setdefault(elem.payload, []).append(elem.reference_payload)
74
+
75
+ # construct new Form objects from segmented words
76
+ new_forms = []
77
+ for seg_idx, word in enumerate(segmented_words):
78
+ orig_indices = seg_to_orig.get(seg_idx, [])
79
+
80
+ # inherit timing: start from first orig, end from last orig
81
+ time = None
82
+ if orig_indices:
83
+ first_time = regular_forms[orig_indices[0]].time
84
+ last_time = regular_forms[orig_indices[-1]].time
85
+ if first_time is not None and last_time is not None:
86
+ time = (first_time[0], last_time[1])
87
+ elif first_time is not None:
88
+ time = first_time
89
+ elif last_time is not None:
90
+ time = last_time
91
+
92
+ # determine token type from the first matched original
93
+ token_type = TokenType.REGULAR
94
+ if orig_indices:
95
+ token_type = regular_forms[orig_indices[0]].type
96
+
97
+ new_forms.append(Form(
98
+ text=word,
99
+ time=time,
100
+ type=token_type,
101
+ ))
102
+
103
+ # re-append trailing punctuation/features
104
+ new_forms.extend(trailing)
105
+ ut.content = new_forms
106
+ # clear cached text so _detokenize() regenerates from new Forms
107
+ ut.text = None
108
+
109
+ return doc
@@ -0,0 +1,3 @@
1
+ 0.8.2-post.8
2
+ April 02 2026
3
+ segment
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: batchalign
3
- Version: 0.8.2.post6
3
+ Version: 0.8.2.post8
4
4
  Summary: Python Speech Language Sample Analysis
5
5
  Author: Brian MacWhinney, Houjun Liu
6
6
  Author-email: macw@cmu.edu, houjun@cmu.edu
@@ -42,6 +42,7 @@ Requires-Dist: rich-click>=1.7.0
42
42
  Requires-Dist: typing-extensions
43
43
  Requires-Dist: num2words
44
44
  Requires-Dist: tiktoken
45
+ Requires-Dist: pycantonese
45
46
  Requires-Dist: blobfile
46
47
  Requires-Dist: sentencepiece
47
48
  Requires-Dist: googletrans
@@ -107,6 +107,8 @@ batchalign/pipelines/morphosyntax/fr/case.py
107
107
  batchalign/pipelines/morphosyntax/ja/verbforms.py
108
108
  batchalign/pipelines/opensmile/__init__.py
109
109
  batchalign/pipelines/opensmile/engine.py
110
+ batchalign/pipelines/segmentation/__init__.py
111
+ batchalign/pipelines/segmentation/cantonese_seg.py
110
112
  batchalign/pipelines/speaker/__init__.py
111
113
  batchalign/pipelines/speaker/nemo_speaker.py
112
114
  batchalign/pipelines/translate/__init__.py
@@ -26,6 +26,7 @@ rich-click>=1.7.0
26
26
  typing-extensions
27
27
  num2words
28
28
  tiktoken
29
+ pycantonese
29
30
  blobfile
30
31
  sentencepiece
31
32
  googletrans
@@ -58,6 +58,7 @@ setup(
58
58
  "typing-extensions",
59
59
  "num2words",
60
60
  "tiktoken",
61
+ "pycantonese",
61
62
  "blobfile",
62
63
  "sentencepiece",
63
64
  "googletrans",
@@ -1,3 +0,0 @@
1
- 0.8.2-post.6
2
- March 28 2026
3
- fix bugs