wyoming-piper 1.6.3__py3-none-any.whl → 2.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wyoming-piper
3
- Version: 1.6.3
3
+ Version: 2.2.0
4
4
  Summary: Wyoming Server for Piper
5
5
  Author-email: Michael Hansen <mike@rhasspy.org>
6
6
  License: MIT
@@ -8,30 +8,34 @@ Project-URL: Homepage, http://github.com/rhasspy/wyoming-piper
8
8
  Keywords: rhasspy,wyoming,piper,tts
9
9
  Classifier: Development Status :: 3 - Alpha
10
10
  Classifier: Intended Audience :: Developers
11
- Classifier: Topic :: Text Processing :: Linguistic
12
- Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
13
12
  Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
17
16
  Classifier: Programming Language :: Python :: 3.13
18
- Requires-Python: >=3.8
17
+ Requires-Python: >=3.9
19
18
  Description-Content-Type: text/markdown
20
19
  License-File: LICENSE.md
21
- Requires-Dist: wyoming<1.8,>=1.7.2
22
- Requires-Dist: regex==2024.11.6
20
+ Requires-Dist: wyoming<2,>=1.8
21
+ Requires-Dist: regex>=2024.11.6
22
+ Requires-Dist: piper-tts<2,>=1.4.0
23
+ Requires-Dist: sentence-stream<2,>=1.2.0
23
24
  Provides-Extra: dev
24
- Requires-Dist: black==22.12.0; extra == "dev"
25
- Requires-Dist: flake8==6.0.0; extra == "dev"
26
- Requires-Dist: isort==5.11.3; extra == "dev"
27
- Requires-Dist: mypy==0.991; extra == "dev"
28
- Requires-Dist: pylint==2.15.9; extra == "dev"
29
- Requires-Dist: pytest==7.4.4; extra == "dev"
30
- Requires-Dist: pytest-asyncio==0.23.3; extra == "dev"
31
- Requires-Dist: build==1.2.2.post1; extra == "dev"
25
+ Requires-Dist: black; extra == "dev"
26
+ Requires-Dist: flake8; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+ Requires-Dist: pylint; extra == "dev"
29
+ Requires-Dist: pytest; extra == "dev"
30
+ Requires-Dist: pytest-asyncio; extra == "dev"
31
+ Requires-Dist: build; extra == "dev"
32
32
  Requires-Dist: scipy<2,>=1.10; extra == "dev"
33
33
  Requires-Dist: numpy<2,>=1.20; extra == "dev"
34
- Requires-Dist: python-speech-features==0.6; extra == "dev"
34
+ Requires-Dist: python-speech-features<1,>=0.6; extra == "dev"
35
+ Provides-Extra: zeroconf
36
+ Requires-Dist: piper-tts[zeroconf]; extra == "zeroconf"
37
+ Provides-Extra: zh
38
+ Requires-Dist: piper-tts[zh]; extra == "zh"
35
39
  Dynamic: license-file
36
40
 
37
41
  # Wyoming Piper
@@ -0,0 +1,13 @@
1
+ wyoming_piper/__init__.py,sha256=z1dsCtGazHHufHQpoVgNtMObt25qYBSOM85o7xgbIJA,139
2
+ wyoming_piper/__main__.py,sha256=I_i_zcO7Z7_mSPTJVe207jnMGKymeNlDlNph4DC_Py8,8104
3
+ wyoming_piper/const.py,sha256=04sCdtJ2QGuF1BQGkOuQW10og61PgH3fCnPhaYu-YoU,1015
4
+ wyoming_piper/download.py,sha256=At1RBaVKsTTAO71LAJn8bDeJnvJXBS0vA6iCIo6BqZs,6349
5
+ wyoming_piper/file_hash.py,sha256=HMuwrgEIg-bCOXHG0wE3vtjrqGD7QaA_UNfvBMXeUcY,1107
6
+ wyoming_piper/handler.py,sha256=LHbJLmhq9j4rZE7fBrDJhPGm1nFIegNXfRlsjFOGhno,10195
7
+ wyoming_piper/voices.json,sha256=Fkqd82jiQGWAZrD_q6_5adVRMaPEAWh1ehezFeGkeFw,217559
8
+ wyoming_piper-2.2.0.dist-info/licenses/LICENSE.md,sha256=E3RtUJ105V6iJl--8gS7fNv4SoMVsCB-mIMmy1Q4cCg,1071
9
+ wyoming_piper-2.2.0.dist-info/METADATA,sha256=IQlhbgewWw46T0Ot8ACoil5kZLylNU4mTLI6bkV8WsE,2614
10
+ wyoming_piper-2.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
11
+ wyoming_piper-2.2.0.dist-info/entry_points.txt,sha256=n2UgsOCQitQ5Itr20aITTWZLL2dAtaVKn5pdecXdDHE,61
12
+ wyoming_piper-2.2.0.dist-info/top_level.txt,sha256=t7U7-u1sK_4xy_qbTJhxQRbxle3cLQfPq2oVLezHVNU,14
13
+ wyoming_piper-2.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
wyoming_piper/process.py DELETED
@@ -1,171 +0,0 @@
1
- #!/usr/bin/env python3
2
- import argparse
3
- import asyncio
4
- import json
5
- import logging
6
- import tempfile
7
- import time
8
- from dataclasses import dataclass
9
- from typing import Any, Dict, Optional
10
-
11
- from .download import ensure_voice_exists, find_voice
12
-
13
- _LOGGER = logging.getLogger(__name__)
14
-
15
-
16
- @dataclass
17
- class PiperProcess:
18
- """Info for a running Piper process (one voice)."""
19
-
20
- name: str
21
- proc: "asyncio.subprocess.Process"
22
- config: Dict[str, Any]
23
- wav_dir: tempfile.TemporaryDirectory
24
- last_used: int = 0
25
-
26
- def get_speaker_id(self, speaker: str) -> Optional[int]:
27
- """Get speaker by name or id."""
28
- return _get_speaker_id(self.config, speaker)
29
-
30
- @property
31
- def is_multispeaker(self) -> bool:
32
- """True if model has more than one speaker."""
33
- return _is_multispeaker(self.config)
34
-
35
-
36
- def _get_speaker_id(config: Dict[str, Any], speaker: str) -> Optional[int]:
37
- """Get speaker by name or id."""
38
- speaker_id_map = config.get("speaker_id_map", {})
39
- speaker_id = speaker_id_map.get(speaker)
40
- if speaker_id is None:
41
- try:
42
- # Try to interpret as an id
43
- speaker_id = int(speaker)
44
- except ValueError:
45
- pass
46
-
47
- return speaker_id
48
-
49
-
50
- def _is_multispeaker(config: Dict[str, Any]) -> bool:
51
- """True if model has more than one speaker."""
52
- return config.get("num_speakers", 1) > 1
53
-
54
-
55
- # -----------------------------------------------------------------------------
56
-
57
-
58
- class PiperProcessManager:
59
- """Manager of running Piper processes."""
60
-
61
- def __init__(self, args: argparse.Namespace, voices_info: Dict[str, Any]):
62
- self.voices_info = voices_info
63
- self.args = args
64
- self.processes: Dict[str, PiperProcess] = {}
65
- self.processes_lock = asyncio.Lock()
66
-
67
- async def get_process(self, voice_name: Optional[str] = None) -> PiperProcess:
68
- """Get a running Piper process or start a new one if necessary."""
69
- voice_speaker: Optional[str] = None
70
- if voice_name is None:
71
- # Default voice
72
- voice_name = self.args.voice
73
-
74
- if voice_name == self.args.voice:
75
- # Default speaker
76
- voice_speaker = self.args.speaker
77
-
78
- assert voice_name is not None
79
-
80
- # Resolve alias
81
- voice_info = self.voices_info.get(voice_name, {})
82
- voice_name = voice_info.get("key", voice_name)
83
- assert voice_name is not None
84
-
85
- piper_proc = self.processes.get(voice_name)
86
- if (piper_proc is None) or (piper_proc.proc.returncode is not None):
87
- # Remove if stopped
88
- self.processes.pop(voice_name, None)
89
-
90
- # Start new Piper process
91
- if self.args.max_piper_procs > 0:
92
- # Restrict number of running processes
93
- while len(self.processes) >= self.args.max_piper_procs:
94
- # Stop least recently used process
95
- lru_proc_name, lru_proc = sorted(
96
- self.processes.items(), key=lambda kv: kv[1].last_used
97
- )[0]
98
- _LOGGER.debug("Stopping process for: %s", lru_proc_name)
99
- self.processes.pop(lru_proc_name, None)
100
- if lru_proc.proc.returncode is None:
101
- try:
102
- lru_proc.proc.terminate()
103
- await lru_proc.proc.wait()
104
- except Exception:
105
- _LOGGER.exception("Unexpected error stopping piper process")
106
-
107
- _LOGGER.debug(
108
- "Starting process for: %s (%s/%s)",
109
- voice_name,
110
- len(self.processes) + 1,
111
- self.args.max_piper_procs,
112
- )
113
-
114
- ensure_voice_exists(
115
- voice_name,
116
- self.args.data_dir,
117
- self.args.download_dir,
118
- self.voices_info,
119
- )
120
-
121
- onnx_path, config_path = find_voice(voice_name, self.args.data_dir)
122
- with open(config_path, "r", encoding="utf-8") as config_file:
123
- config = json.load(config_file)
124
-
125
- wav_dir = tempfile.TemporaryDirectory()
126
- piper_args = [
127
- "--model",
128
- str(onnx_path),
129
- "--config",
130
- str(config_path),
131
- "--output_dir",
132
- str(wav_dir.name),
133
- "--json-input", # piper 1.1+
134
- ]
135
-
136
- if voice_speaker is not None:
137
- if _is_multispeaker(config):
138
- speaker_id = _get_speaker_id(config, voice_speaker)
139
- if speaker_id is not None:
140
- piper_args.extend(["--speaker", str(speaker_id)])
141
-
142
- if self.args.noise_scale:
143
- piper_args.extend(["--noise-scale", str(self.args.noise_scale)])
144
-
145
- if self.args.length_scale:
146
- piper_args.extend(["--length-scale", str(self.args.length_scale)])
147
-
148
- if self.args.noise_w:
149
- piper_args.extend(["--noise-w", str(self.args.noise_w)])
150
-
151
- _LOGGER.debug(
152
- "Starting piper process: %s args=%s", self.args.piper, piper_args
153
- )
154
- piper_proc = PiperProcess(
155
- name=voice_name,
156
- proc=await asyncio.create_subprocess_exec(
157
- self.args.piper,
158
- *piper_args,
159
- stdin=asyncio.subprocess.PIPE,
160
- stdout=asyncio.subprocess.PIPE,
161
- stderr=asyncio.subprocess.DEVNULL,
162
- ),
163
- config=config,
164
- wav_dir=wav_dir,
165
- )
166
- self.processes[voice_name] = piper_proc
167
-
168
- # Update used
169
- piper_proc.last_used = time.monotonic_ns()
170
-
171
- return piper_proc
@@ -1,58 +0,0 @@
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
@@ -1,15 +0,0 @@
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,,