wyoming-piper 1.5.3__py3-none-any.whl → 1.6.3__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.
wyoming_piper/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Wyoming server for piper."""
2
+
2
3
  from importlib.metadata import version
3
4
 
4
5
  __version__ = version("wyoming_piper")
wyoming_piper/__main__.py CHANGED
@@ -60,6 +60,11 @@ async def main() -> None:
60
60
  default=1,
61
61
  help="Maximum number of piper process to run simultaneously (default: 1)",
62
62
  )
63
+ parser.add_argument(
64
+ "--streaming",
65
+ action="store_true",
66
+ help="Enable audio streaming on sentence boundaries",
67
+ )
63
68
  #
64
69
  parser.add_argument(
65
70
  "--update-voices",
@@ -113,12 +118,14 @@ async def main() -> None:
113
118
  voice_info.get("espeak", {}).get("voice", voice_name.split("_")[0]),
114
119
  )
115
120
  ],
116
- speakers=[
117
- TtsVoiceSpeaker(name=speaker_name)
118
- for speaker_name in voice_info["speaker_id_map"]
119
- ]
120
- if voice_info.get("speaker_id_map")
121
- else None,
121
+ speakers=(
122
+ [
123
+ TtsVoiceSpeaker(name=speaker_name)
124
+ for speaker_name in voice_info["speaker_id_map"]
125
+ ]
126
+ if voice_info.get("speaker_id_map")
127
+ else None
128
+ ),
122
129
  )
123
130
  for voice_name, voice_info in voices_info.items()
124
131
  if not voice_info.get("_is_alias", False)
@@ -180,6 +187,7 @@ async def main() -> None:
180
187
  installed=True,
181
188
  voices=sorted(voices, key=lambda v: v.name),
182
189
  version=__version__,
190
+ supports_synthesize_streaming=args.streaming,
183
191
  )
184
192
  ],
185
193
  )
wyoming_piper/download.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Utility for downloading Piper voices."""
2
+
2
3
  import json
3
4
  import logging
4
5
  import shutil
@@ -10,7 +11,7 @@ from urllib.request import urlopen
10
11
 
11
12
  from .file_hash import get_file_hash
12
13
 
13
- URL_FORMAT = "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/{file}"
14
+ URL_FORMAT = "https://huggingface.co/rhasspy/piper-voices/resolve/main/{file}"
14
15
 
15
16
  _DIR = Path(__file__).parent
16
17
  _LOGGER = logging.getLogger(__name__)
@@ -47,20 +48,21 @@ def get_voices(
47
48
  except Exception:
48
49
  _LOGGER.exception("Failed to update voices list")
49
50
 
51
+ voices_embedded = _DIR / "voices.json"
52
+ _LOGGER.debug("Loading %s", voices_embedded)
53
+ with open(voices_embedded, "r", encoding="utf-8") as voices_file:
54
+ voices = json.load(voices_file)
55
+
50
56
  # Prefer downloaded file to embedded
51
57
  if voices_download.exists():
52
58
  try:
53
59
  _LOGGER.debug("Loading %s", voices_download)
54
60
  with open(voices_download, "r", encoding="utf-8") as voices_file:
55
- return json.load(voices_file)
61
+ voices.update(json.load(voices_file))
56
62
  except Exception:
57
63
  _LOGGER.exception("Failed to load %s", voices_download)
58
64
 
59
- # Fall back to embedded
60
- voices_embedded = _DIR / "voices.json"
61
- _LOGGER.debug("Loading %s", voices_embedded)
62
- with open(voices_embedded, "r", encoding="utf-8") as voices_file:
63
- return json.load(voices_file)
65
+ return voices
64
66
 
65
67
 
66
68
  def ensure_voice_exists(
wyoming_piper/handler.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Event handler for clients of the server."""
2
+
2
3
  import argparse
3
4
  import json
4
5
  import logging
@@ -12,9 +13,16 @@ from wyoming.error import Error
12
13
  from wyoming.event import Event
13
14
  from wyoming.info import Describe, Info
14
15
  from wyoming.server import AsyncEventHandler
15
- from wyoming.tts import Synthesize
16
+ from wyoming.tts import (
17
+ Synthesize,
18
+ SynthesizeChunk,
19
+ SynthesizeStart,
20
+ SynthesizeStop,
21
+ SynthesizeStopped,
22
+ )
16
23
 
17
24
  from .process import PiperProcessManager
25
+ from .sentence_boundary import SentenceBoundaryDetector, remove_asterisks
18
26
 
19
27
  _LOGGER = logging.getLogger(__name__)
20
28
 
@@ -33,6 +41,9 @@ class PiperEventHandler(AsyncEventHandler):
33
41
  self.cli_args = cli_args
34
42
  self.wyoming_info_event = wyoming_info.event()
35
43
  self.process_manager = process_manager
44
+ self.sbd = SentenceBoundaryDetector()
45
+ self.is_streaming: Optional[bool] = None
46
+ self._synthesize: Optional[Synthesize] = None
36
47
 
37
48
  async def handle_event(self, event: Event) -> bool:
38
49
  if Describe.is_type(event.type):
@@ -40,20 +51,67 @@ class PiperEventHandler(AsyncEventHandler):
40
51
  _LOGGER.debug("Sent info")
41
52
  return True
42
53
 
43
- if not Synthesize.is_type(event.type):
44
- _LOGGER.warning("Unexpected event: %s", event)
45
- return True
46
-
47
54
  try:
48
- return await self._handle_event(event)
55
+ if Synthesize.is_type(event.type):
56
+ if self.is_streaming:
57
+ # Ignore since this is only sent for compatibility reasons.
58
+ # For streaming, we expect:
59
+ # [synthesize-start] -> [synthesize-chunk]+ -> [synthesize]? -> [synthesize-stop]
60
+ return True
61
+
62
+ # Sent outside a stream, so we must process it
63
+ synthesize = Synthesize.from_event(event)
64
+ synthesize.text = remove_asterisks(synthesize.text)
65
+ return await self._handle_synthesize(synthesize)
66
+
67
+ if not self.cli_args.streaming:
68
+ # Streaming is not enabled
69
+ return True
70
+
71
+ if SynthesizeStart.is_type(event.type):
72
+ # Start of a stream
73
+ stream_start = SynthesizeStart.from_event(event)
74
+ self.is_streaming = True
75
+ self.sbd = SentenceBoundaryDetector()
76
+ self._synthesize = Synthesize(text="", voice=stream_start.voice)
77
+ _LOGGER.debug("Text stream started: voice=%s", stream_start.voice)
78
+ return True
79
+
80
+ if SynthesizeChunk.is_type(event.type):
81
+ assert self._synthesize is not None
82
+ stream_chunk = SynthesizeChunk.from_event(event)
83
+ for sentence in self.sbd.add_chunk(stream_chunk.text):
84
+ _LOGGER.debug("Synthesizing stream sentence: %s", sentence)
85
+ self._synthesize.text = sentence
86
+ await self._handle_synthesize(self._synthesize)
87
+
88
+ return True
89
+
90
+ if SynthesizeStop.is_type(event.type):
91
+ assert self._synthesize is not None
92
+ self._synthesize.text = self.sbd.finish()
93
+ if self._synthesize.text:
94
+ # Final audio chunk(s)
95
+ await self._handle_synthesize(self._synthesize)
96
+
97
+ # End of audio
98
+ await self.write_event(SynthesizeStopped().event())
99
+
100
+ _LOGGER.debug("Text stream stopped")
101
+ return True
102
+
103
+ if not Synthesize.is_type(event.type):
104
+ return True
105
+
106
+ synthesize = Synthesize.from_event(event)
107
+ return await self._handle_synthesize(synthesize)
49
108
  except Exception as err:
50
109
  await self.write_event(
51
110
  Error(text=str(err), code=err.__class__.__name__).event()
52
111
  )
53
112
  raise err
54
113
 
55
- async def _handle_event(self, event: Event) -> bool:
56
- synthesize = Synthesize.from_event(event)
114
+ async def _handle_synthesize(self, synthesize: Synthesize) -> bool:
57
115
  _LOGGER.debug(synthesize)
58
116
 
59
117
  raw_text = synthesize.text
@@ -0,0 +1,58 @@
1
+ """Guess the sentence boundaries in text."""
2
+
3
+ from collections.abc import Iterable
4
+
5
+ import regex as re
6
+
7
+ SENTENCE_END = r"[.!?…]|[。!?]|[؟]|[।॥]"
8
+ ABBREVIATION_RE = re.compile(r"\b\p{L}{1,3}\.$", re.UNICODE)
9
+
10
+ SENTENCE_BOUNDARY_RE = re.compile(
11
+ rf"(.*?(?:{SENTENCE_END}+))(?=\s+[\p{{Lu}}\p{{Lt}}\p{{Lo}}]|(?:\s+\d+\.\s+))",
12
+ re.DOTALL,
13
+ )
14
+ WORD_ASTERISKS = re.compile(r"\*+([^\*]+)\*+")
15
+ LINE_ASTERICKS = re.compile(r"(?<=^|\n)\s*\*+")
16
+
17
+
18
+ class SentenceBoundaryDetector:
19
+ def __init__(self) -> None:
20
+ self.remaining_text = ""
21
+ self.current_sentence = ""
22
+
23
+ def add_chunk(self, chunk: str) -> Iterable[str]:
24
+ self.remaining_text += chunk
25
+ while self.remaining_text:
26
+ match = SENTENCE_BOUNDARY_RE.search(self.remaining_text)
27
+ if not match:
28
+ break
29
+
30
+ match_text = match.group(0)
31
+
32
+ if not self.current_sentence:
33
+ self.current_sentence = match_text
34
+ elif ABBREVIATION_RE.search(self.current_sentence[-5:]):
35
+ self.current_sentence += match_text
36
+ else:
37
+ yield remove_asterisks(self.current_sentence.strip())
38
+ self.current_sentence = match_text
39
+
40
+ if not ABBREVIATION_RE.search(self.current_sentence[-5:]):
41
+ yield remove_asterisks(self.current_sentence.strip())
42
+ self.current_sentence = ""
43
+
44
+ self.remaining_text = self.remaining_text[match.end() :]
45
+
46
+ def finish(self) -> str:
47
+ text = (self.current_sentence + self.remaining_text).strip()
48
+ self.remaining_text = ""
49
+ self.current_sentence = ""
50
+
51
+ return remove_asterisks(text)
52
+
53
+
54
+ def remove_asterisks(text: str) -> str:
55
+ """Remove *asterisks* surrounding **words**"""
56
+ text = WORD_ASTERISKS.sub(r"\1", text)
57
+ text = LINE_ASTERICKS.sub("", text)
58
+ return text
wyoming_piper/voices.json CHANGED
@@ -4013,6 +4013,36 @@
4013
4013
  },
4014
4014
  "aliases": []
4015
4015
  },
4016
+ "es_AR-daniela-high": {
4017
+ "key": "es_AR-daniela-high",
4018
+ "name": "daniela",
4019
+ "language": {
4020
+ "code": "es_AR",
4021
+ "family": "es",
4022
+ "region": "AR",
4023
+ "name_native": "Español",
4024
+ "name_english": "Spanish",
4025
+ "country_english": "Argentina"
4026
+ },
4027
+ "quality": "high",
4028
+ "num_speakers": 1,
4029
+ "speaker_id_map": {},
4030
+ "files": {
4031
+ "es/es_AR/daniela/high/es_AR-daniela-high.onnx": {
4032
+ "size_bytes": 114199011,
4033
+ "md5_digest": "e373fb657c93877dbc438badeadff4cb"
4034
+ },
4035
+ "es/es_AR/daniela/high/es_AR-daniela-high.onnx.json": {
4036
+ "size_bytes": 7248,
4037
+ "md5_digest": "b68d699a1779d98ee94c0cd9a08ba95b"
4038
+ },
4039
+ "es/es_AR/daniela/high/MODEL_CARD": {
4040
+ "size_bytes": 353,
4041
+ "md5_digest": "d940f7c6667a11ea165ff83ef7a86669"
4042
+ }
4043
+ },
4044
+ "aliases": []
4045
+ },
4016
4046
  "es_ES-carlfm-x_low": {
4017
4047
  "key": "es_ES-carlfm-x_low",
4018
4048
  "name": "carlfm",
@@ -4791,6 +4821,66 @@
4791
4821
  },
4792
4822
  "aliases": []
4793
4823
  },
4824
+ "hi_IN-pratham-medium": {
4825
+ "key": "hi_IN-pratham-medium",
4826
+ "name": "pratham",
4827
+ "language": {
4828
+ "code": "hi_IN",
4829
+ "family": "hi",
4830
+ "region": "IN",
4831
+ "name_native": "हिन्दी",
4832
+ "name_english": "Hindi",
4833
+ "country_english": "India"
4834
+ },
4835
+ "quality": "medium",
4836
+ "num_speakers": 1,
4837
+ "speaker_id_map": {},
4838
+ "files": {
4839
+ "hi/hi_IN/pratham/medium/hi_IN-pratham-medium.onnx": {
4840
+ "size_bytes": 63516050,
4841
+ "md5_digest": "f1e5a629a9e533a7155910530109eb86"
4842
+ },
4843
+ "hi/hi_IN/pratham/medium/hi_IN-pratham-medium.onnx.json": {
4844
+ "size_bytes": 4970,
4845
+ "md5_digest": "ab655cdad90f1939f00d1b3aaf440e99"
4846
+ },
4847
+ "hi/hi_IN/pratham/medium/MODEL_CARD": {
4848
+ "size_bytes": 304,
4849
+ "md5_digest": "eccc77c3d857a883e85c52cb17653c1f"
4850
+ }
4851
+ },
4852
+ "aliases": []
4853
+ },
4854
+ "hi_IN-priyamvada-medium": {
4855
+ "key": "hi_IN-priyamvada-medium",
4856
+ "name": "priyamvada",
4857
+ "language": {
4858
+ "code": "hi_IN",
4859
+ "family": "hi",
4860
+ "region": "IN",
4861
+ "name_native": "हिन्दी",
4862
+ "name_english": "Hindi",
4863
+ "country_english": "India"
4864
+ },
4865
+ "quality": "medium",
4866
+ "num_speakers": 1,
4867
+ "speaker_id_map": {},
4868
+ "files": {
4869
+ "hi/hi_IN/priyamvada/medium/hi_IN-priyamvada-medium.onnx": {
4870
+ "size_bytes": 63516050,
4871
+ "md5_digest": "7d5e20c2d1e72de8ed772f222e679626"
4872
+ },
4873
+ "hi/hi_IN/priyamvada/medium/hi_IN-priyamvada-medium.onnx.json": {
4874
+ "size_bytes": 4973,
4875
+ "md5_digest": "599ca4dc5d421a9c66692433f618e080"
4876
+ },
4877
+ "hi/hi_IN/priyamvada/medium/MODEL_CARD": {
4878
+ "size_bytes": 307,
4879
+ "md5_digest": "e2b745cf97087be0a97c7f10215bfa70"
4880
+ }
4881
+ },
4882
+ "aliases": []
4883
+ },
4794
4884
  "hu_HU-anna-medium": {
4795
4885
  "key": "hu_HU-anna-medium",
4796
4886
  "name": "anna",
@@ -5264,6 +5354,96 @@
5264
5354
  },
5265
5355
  "aliases": []
5266
5356
  },
5357
+ "ml_IN-arjun-medium": {
5358
+ "key": "ml_IN-arjun-medium",
5359
+ "name": "arjun",
5360
+ "language": {
5361
+ "code": "ml_IN",
5362
+ "family": "ml",
5363
+ "region": "IN",
5364
+ "name_native": "മലയാളം",
5365
+ "name_english": "Malayalam",
5366
+ "country_english": "India"
5367
+ },
5368
+ "quality": "medium",
5369
+ "num_speakers": 1,
5370
+ "speaker_id_map": {},
5371
+ "files": {
5372
+ "ml/ml_IN/arjun/medium/ml_IN-arjun-medium.onnx": {
5373
+ "size_bytes": 62950044,
5374
+ "md5_digest": "4f20109c108aa80f46df85ab9cda5daa"
5375
+ },
5376
+ "ml/ml_IN/arjun/medium/ml_IN-arjun-medium.onnx.json": {
5377
+ "size_bytes": 5044,
5378
+ "md5_digest": "18454efcad5bb889ee4cb1cb035993f5"
5379
+ },
5380
+ "ml/ml_IN/arjun/medium/MODEL_CARD": {
5381
+ "size_bytes": 308,
5382
+ "md5_digest": "eb0037396b93f38ffbc093dba42cf8be"
5383
+ }
5384
+ },
5385
+ "aliases": []
5386
+ },
5387
+ "ml_IN-meera-medium": {
5388
+ "key": "ml_IN-meera-medium",
5389
+ "name": "meera",
5390
+ "language": {
5391
+ "code": "ml_IN",
5392
+ "family": "ml",
5393
+ "region": "IN",
5394
+ "name_native": "മലയാളം",
5395
+ "name_english": "Malayalam",
5396
+ "country_english": "India"
5397
+ },
5398
+ "quality": "medium",
5399
+ "num_speakers": 1,
5400
+ "speaker_id_map": {},
5401
+ "files": {
5402
+ "ml/ml_IN/meera/medium/ml_IN-meera-medium.onnx": {
5403
+ "size_bytes": 62950044,
5404
+ "md5_digest": "3eb7b05d25c1551f7a7cec1e1c153b1f"
5405
+ },
5406
+ "ml/ml_IN/meera/medium/ml_IN-meera-medium.onnx.json": {
5407
+ "size_bytes": 5045,
5408
+ "md5_digest": "64c2eee28c62e3a072ccc2ea3d812748"
5409
+ },
5410
+ "ml/ml_IN/meera/medium/MODEL_CARD": {
5411
+ "size_bytes": 308,
5412
+ "md5_digest": "626a0c1d744d0ce6ffca95665c526939"
5413
+ }
5414
+ },
5415
+ "aliases": []
5416
+ },
5417
+ "ne_NP-chitwan-medium": {
5418
+ "key": "ne_NP-chitwan-medium",
5419
+ "name": "chitwan",
5420
+ "language": {
5421
+ "code": "ne_NP",
5422
+ "family": "ne",
5423
+ "region": "NP",
5424
+ "name_native": "नेपाली",
5425
+ "name_english": "Nepali",
5426
+ "country_english": "Nepal"
5427
+ },
5428
+ "quality": "medium",
5429
+ "num_speakers": 1,
5430
+ "speaker_id_map": {},
5431
+ "files": {
5432
+ "ne/ne_NP/chitwan/medium/ne_NP-chitwan-medium.onnx": {
5433
+ "size_bytes": 62950044,
5434
+ "md5_digest": "74cdb5b32816c366af74b55ed7494e25"
5435
+ },
5436
+ "ne/ne_NP/chitwan/medium/ne_NP-chitwan-medium.onnx.json": {
5437
+ "size_bytes": 5043,
5438
+ "md5_digest": "7ddc520e24a862016f6d22cde4b70ac1"
5439
+ },
5440
+ "ne/ne_NP/chitwan/medium/MODEL_CARD": {
5441
+ "size_bytes": 275,
5442
+ "md5_digest": "dcd35df097f84ace498d7afeb5b1a29c"
5443
+ }
5444
+ },
5445
+ "aliases": []
5446
+ },
5267
5447
  "ne_NP-google-medium": {
5268
5448
  "key": "ne_NP-google-medium",
5269
5449
  "name": "google",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wyoming-piper
3
- Version: 1.5.3
3
+ Version: 1.6.3
4
4
  Summary: Wyoming Server for Piper
5
5
  Author-email: Michael Hansen <mike@rhasspy.org>
6
6
  License: MIT
@@ -14,10 +14,12 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
- Requires-Python: <3.13,>=3.8.1
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.8
18
19
  Description-Content-Type: text/markdown
19
20
  License-File: LICENSE.md
20
- Requires-Dist: wyoming>=1.5.3
21
+ Requires-Dist: wyoming<1.8,>=1.7.2
22
+ Requires-Dist: regex==2024.11.6
21
23
  Provides-Extra: dev
22
24
  Requires-Dist: black==22.12.0; extra == "dev"
23
25
  Requires-Dist: flake8==6.0.0; extra == "dev"
@@ -0,0 +1,15 @@
1
+ wyoming_piper/__init__.py,sha256=z1dsCtGazHHufHQpoVgNtMObt25qYBSOM85o7xgbIJA,139
2
+ wyoming_piper/__main__.py,sha256=SuJ6XY6zy68N8L-N_n_EIWK0vpZwbYQXW5vlSC8BpW8,7445
3
+ wyoming_piper/const.py,sha256=04sCdtJ2QGuF1BQGkOuQW10og61PgH3fCnPhaYu-YoU,1015
4
+ wyoming_piper/download.py,sha256=UpczxHWqLkcOblHmrwgBHSR6wG1LR-hZ4V6QSsrghns,6185
5
+ wyoming_piper/file_hash.py,sha256=HMuwrgEIg-bCOXHG0wE3vtjrqGD7QaA_UNfvBMXeUcY,1107
6
+ wyoming_piper/handler.py,sha256=WVpmnRVYmsd3DrLcMfBOsuo_J1HJ0h00-HwQ1iZEToo,7360
7
+ wyoming_piper/process.py,sha256=L_qqxQcQawrC940fwlv4u6KM9KjCq6N6ym-OADSZcrM,5794
8
+ wyoming_piper/sentence_boundary.py,sha256=pHVo92_weusnVLRVicnS0-Tst_eR-pMrnRrGL96HxC8,1875
9
+ wyoming_piper/voices.json,sha256=elUT3cM0Wlgo8N8E5nhMbMSCPB8zU4SY2XGKwe-T2ys,209108
10
+ wyoming_piper-1.6.3.dist-info/licenses/LICENSE.md,sha256=E3RtUJ105V6iJl--8gS7fNv4SoMVsCB-mIMmy1Q4cCg,1071
11
+ wyoming_piper-1.6.3.dist-info/METADATA,sha256=sNP4bue0pO2mFBb3xUnXfgibofOvaCFuJqN7Hik3fmQ,2543
12
+ wyoming_piper-1.6.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ wyoming_piper-1.6.3.dist-info/entry_points.txt,sha256=n2UgsOCQitQ5Itr20aITTWZLL2dAtaVKn5pdecXdDHE,61
14
+ wyoming_piper-1.6.3.dist-info/top_level.txt,sha256=t7U7-u1sK_4xy_qbTJhxQRbxle3cLQfPq2oVLezHVNU,14
15
+ wyoming_piper-1.6.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,14 +0,0 @@
1
- wyoming_piper/__init__.py,sha256=jq4RWRoEFQNm73oe9FNeVxdHTbu-MdElAnW1fbodESo,138
2
- wyoming_piper/__main__.py,sha256=knqXEoX_oxJr69oKitAILZKioKsn14HSrTpa3lCT-nA,7186
3
- wyoming_piper/const.py,sha256=04sCdtJ2QGuF1BQGkOuQW10og61PgH3fCnPhaYu-YoU,1015
4
- wyoming_piper/download.py,sha256=n3oKXN0fLk7Dn7x4wnR17b3QzXilFgF-QSxtN0QE10s,6185
5
- wyoming_piper/file_hash.py,sha256=HMuwrgEIg-bCOXHG0wE3vtjrqGD7QaA_UNfvBMXeUcY,1107
6
- wyoming_piper/handler.py,sha256=0bcllkoR6iNsseWQ1Y-Uj4sMV_az6mn4ivS8AAo4RHw,4936
7
- wyoming_piper/process.py,sha256=L_qqxQcQawrC940fwlv4u6KM9KjCq6N6ym-OADSZcrM,5794
8
- wyoming_piper/voices.json,sha256=3uEoyiBYbQlJnFYUEKnFHYK0Wq40sbaHiD9AsgRWKzc,202976
9
- wyoming_piper-1.5.3.dist-info/licenses/LICENSE.md,sha256=E3RtUJ105V6iJl--8gS7fNv4SoMVsCB-mIMmy1Q4cCg,1071
10
- wyoming_piper-1.5.3.dist-info/METADATA,sha256=9fYLRK0GOJCnaf9FBwlV1zoYUoDtyb0h66tOilKf2YU,2463
11
- wyoming_piper-1.5.3.dist-info/WHEEL,sha256=A8Eltl-h0W-qZDVezsLjjslosEH_pdYC2lQ0JcbgCzs,91
12
- wyoming_piper-1.5.3.dist-info/entry_points.txt,sha256=n2UgsOCQitQ5Itr20aITTWZLL2dAtaVKn5pdecXdDHE,61
13
- wyoming_piper-1.5.3.dist-info/top_level.txt,sha256=t7U7-u1sK_4xy_qbTJhxQRbxle3cLQfPq2oVLezHVNU,14
14
- wyoming_piper-1.5.3.dist-info/RECORD,,