From 1a8c49bd29b3a132721086ee88f2253f788594a8 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Thu, 12 Mar 2026 15:16:51 -0500 Subject: [PATCH] Move to package --- CHANGELOG.md | 4 +++ README.md | 10 +++--- piper_sample_generator/__init__.py | 1 + .../__main__.py | 8 +++-- .../augment.py | 10 +++--- .../impulses}/Accoustic2_Impulse.wav | Bin .../impulses}/Blatty Plate.wav | Bin .../impulses}/Concrete Room.wav | Bin .../impulses}/Derlon Sanctuary.wav | Bin .../impulses}/Fat Bass.wav | Bin .../impulses}/Reverse Gate.wav | Bin .../impulses}/Symphonic.wav | Bin .../impulses}/ir_bathroom1.wav | Bin pylintrc | 12 +++---- pyproject.toml | 34 +++++++----------- requirements.txt | 6 ---- requirements_dev.txt | 5 --- script/format | 6 ++-- script/lint | 12 +++---- script/run | 2 +- 20 files changed, 47 insertions(+), 63 deletions(-) create mode 100644 piper_sample_generator/__init__.py rename generate_samples.py => piper_sample_generator/__main__.py (99%) rename augment.py => piper_sample_generator/augment.py (91%) rename {impulses => piper_sample_generator/impulses}/Accoustic2_Impulse.wav (100%) rename {impulses => piper_sample_generator/impulses}/Blatty Plate.wav (100%) rename {impulses => piper_sample_generator/impulses}/Concrete Room.wav (100%) rename {impulses => piper_sample_generator/impulses}/Derlon Sanctuary.wav (100%) rename {impulses => piper_sample_generator/impulses}/Fat Bass.wav (100%) rename {impulses => piper_sample_generator/impulses}/Reverse Gate.wav (100%) rename {impulses => piper_sample_generator/impulses}/Symphonic.wav (100%) rename {impulses => piper_sample_generator/impulses}/ir_bathroom1.wav (100%) delete mode 100644 requirements.txt delete mode 100644 requirements_dev.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d6cbf8..916341d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.2.0 + +- Refactor as `piper_sample_generator` package + ## 3.1.0 - Support MPS acceleration on Apple Silicon diff --git a/README.md b/README.md index fdc4117..4b4fd07 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ wget -O voices/en_US-lessac-medium.onnx.json 'https://huggingface.co/rhasspy/pip Generate a small set of samples with the CLI: ``` sh -python3 generate_samples.py 'okay piper.' --model voices/en_US-lessac-medium.onnx --max-samples 10 --output-dir okay_piper/ +python3 -m piper_sample_generator 'okay piper.' --model voices/en_US-lessac-medium.onnx --max-samples 10 --output-dir okay_piper/ ``` Check the `okay_piper/` directory for 10 WAV files (named `0.wav` to `9.wav`). @@ -53,7 +53,7 @@ wget -O models/en-us-libritts-high.pt 'https://github.com/rhasspy/piper-sample-g Generate a small set of samples with the CLI: ``` sh -python3 generate_samples.py 'okay piper.' --model models/en-us-libritts-high.pt --max-samples 10 --output-dir okay_piper/ +python3 -m piper_sample_generator 'okay piper.' --model models/en-us-libritts-high.pt --max-samples 10 --output-dir okay_piper/ ``` Check the `okay_piper/` directory for 10 WAV files (named `0.wav` to `9.wav`). @@ -61,7 +61,7 @@ Check the `okay_piper/` directory for 10 WAV files (named `0.wav` to `9.wav`). Generation can be much faster and more efficient if you have a GPU available and PyTorch is configured to use it. In this case, increase the batch size: ``` sh -python3 generate_samples.py 'okay piper.' --model models/en-us-libritts-high.pt --max-samples 100 --batch-size 10 --output-dir okay_piper/ +python3 -m piper_sample_generator 'okay piper.' --model models/en-us-libritts-high.pt --max-samples 100 --batch-size 10 --output-dir okay_piper/ ``` On an NVidia 2080 Ti with 11GB, a batch size of 100 was possible (generating approximately 100 samples per second). @@ -75,14 +75,14 @@ See `--help` for more options, including the `--length-scales` (speaking speeds) Once you have samples generated, you can augment them using [audiomentation](https://iver56.github.io/audiomentations/): ``` sh -python3 augment.py --sample-rate 22050 okay_piper/ okay_piper_augmented/ +python3 -m piper_sample_generator.augment --sample-rate 22050 okay_piper/ okay_piper_augmented/ ``` This will do several things to each sample: 1. Randomly decrease the volume * The original samples are normalized, so different volume levels are needed -2. Randomly apply an [impulse response][] using the files in `impulses/` +2. Randomly apply an [impulse response][] using the files in `piper_sample_generator/impulses/` * Change the acoustics of the sample to sound like the speaker was in a room with echo or using a poor quality microphone 3. Resample to 16Khz for training (e.g., [openWakeWord][]) diff --git a/piper_sample_generator/__init__.py b/piper_sample_generator/__init__.py new file mode 100644 index 0000000..0142775 --- /dev/null +++ b/piper_sample_generator/__init__.py @@ -0,0 +1 @@ +"""Piper sample generator.""" diff --git a/generate_samples.py b/piper_sample_generator/__main__.py similarity index 99% rename from generate_samples.py rename to piper_sample_generator/__main__.py index 753e60e..3074fb5 100755 --- a/generate_samples.py +++ b/piper_sample_generator/__main__.py @@ -16,7 +16,10 @@ import torch from piper import PiperVoice, SynthesisConfig from piper.phonemize_espeak import EspeakPhonemizer -from piper_train.vits import commons +try: + from piper_train.vits import commons +except ImportError: + from piper_train.vits import commons _LOGGER = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) @@ -176,7 +179,8 @@ def generate_samples( audio_numpy = audio.cpu().numpy() if torch.backends.mps.is_available(): - # There seems to be a memory leak if we don't empty the cache after each batch with mps + # There seems to be a memory leak if we don't empty the cache + # after each batch with mps torch.mps.empty_cache() gc.collect() diff --git a/augment.py b/piper_sample_generator/augment.py similarity index 91% rename from augment.py rename to piper_sample_generator/augment.py index dcc16cd..47a226d 100644 --- a/augment.py +++ b/piper_sample_generator/augment.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 import argparse import audioop -import sys import wave from pathlib import Path import numpy as np -from audiomentations import Compose, ApplyImpulseResponse, Gain +from audiomentations import ApplyImpulseResponse, Compose, Gain _DIR = Path(__file__).parent @@ -35,9 +34,10 @@ def main() -> None: output_wav = output_dir / (input_wav.relative_to(input_dir)) output_wav.parent.mkdir(parents=True, exist_ok=True) - with wave.open(str(input_wav), "rb") as input_wav_file, wave.open( - str(output_wav), "wb" - ) as output_wav_file: + with ( + wave.open(str(input_wav), "rb") as input_wav_file, + wave.open(str(output_wav), "wb") as output_wav_file, + ): assert input_wav_file.getsampwidth() == 2 assert input_wav_file.getnchannels() == 1 diff --git a/impulses/Accoustic2_Impulse.wav b/piper_sample_generator/impulses/Accoustic2_Impulse.wav similarity index 100% rename from impulses/Accoustic2_Impulse.wav rename to piper_sample_generator/impulses/Accoustic2_Impulse.wav diff --git a/impulses/Blatty Plate.wav b/piper_sample_generator/impulses/Blatty Plate.wav similarity index 100% rename from impulses/Blatty Plate.wav rename to piper_sample_generator/impulses/Blatty Plate.wav diff --git a/impulses/Concrete Room.wav b/piper_sample_generator/impulses/Concrete Room.wav similarity index 100% rename from impulses/Concrete Room.wav rename to piper_sample_generator/impulses/Concrete Room.wav diff --git a/impulses/Derlon Sanctuary.wav b/piper_sample_generator/impulses/Derlon Sanctuary.wav similarity index 100% rename from impulses/Derlon Sanctuary.wav rename to piper_sample_generator/impulses/Derlon Sanctuary.wav diff --git a/impulses/Fat Bass.wav b/piper_sample_generator/impulses/Fat Bass.wav similarity index 100% rename from impulses/Fat Bass.wav rename to piper_sample_generator/impulses/Fat Bass.wav diff --git a/impulses/Reverse Gate.wav b/piper_sample_generator/impulses/Reverse Gate.wav similarity index 100% rename from impulses/Reverse Gate.wav rename to piper_sample_generator/impulses/Reverse Gate.wav diff --git a/impulses/Symphonic.wav b/piper_sample_generator/impulses/Symphonic.wav similarity index 100% rename from impulses/Symphonic.wav rename to piper_sample_generator/impulses/Symphonic.wav diff --git a/impulses/ir_bathroom1.wav b/piper_sample_generator/impulses/ir_bathroom1.wav similarity index 100% rename from impulses/ir_bathroom1.wav rename to piper_sample_generator/impulses/ir_bathroom1.wav diff --git a/pylintrc b/pylintrc index 561b2f1..60fdb1d 100644 --- a/pylintrc +++ b/pylintrc @@ -1,3 +1,6 @@ +[MASTER] +ignored-modules=torch + [MESSAGES CONTROL] disable= format, @@ -31,14 +34,7 @@ disable= missing-class-docstring, missing-function-docstring, import-error, - consider-using-with + relative-beyond-top-level [FORMAT] expected-line-ending-format=LF - -[TYPECHECK] - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members=numpy.*,torch.* diff --git a/pyproject.toml b/pyproject.toml index 36c9711..99a0bd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,41 +4,31 @@ build-backend = "setuptools.build_meta" [project] name = "piper-sample-generator" -version = "3.1.0" -license = {text = "Apache-2.0"} +version = "3.2.0" +license = {text = "MIT"} description = "Generate TTS audio samples for training wake word systems" readme = "README.md" authors = [ {name = "The Home Assistant Authors", email = "hello@home-assistant.io"} ] keywords = ["piper", "sample", "tts", "wakeword"] -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Text Processing :: Linguistic", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", -] requires-python = ">=3.9.0" dependencies = [ - "piper-tts>=1.3.0,<2", + "audiomentations==0.33.0", + "piper-tts==1.3.0", + "numpy>=2,<3", "torch>=2,<3", "torchaudio", - "audiomentations", - "numpy", + "webrtcvad", ] [project.optional-dependencies] dev = [ - "black==24.8.0", - "flake8==7.2.0", - "mypy==1.14.0", - "pylint==3.2.7", - "pytest==8.3.5", + "black==22.12.0", + "flake8==6.0.0", + "isort==5.11.3", + "mypy==0.991", + "pylint==2.15.9", ] [project.urls] @@ -49,4 +39,4 @@ platforms = ["any"] zip-safe = true [tool.setuptools.packages.find] -include = [] +include = ["piper_sample_generator*"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8443d84..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -audiomentations==0.33.0 -piper-phonemize==1.1.0 -numpy<2 -torch<2 -torchaudio -webrtcvad diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 77190e6..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,5 +0,0 @@ -black==22.12.0 -flake8==6.0.0 -isort==5.11.3 -mypy==0.991 -pylint==2.15.9 diff --git a/script/format b/script/format index 7f04417..b8b283b 100755 --- a/script/format +++ b/script/format @@ -6,7 +6,7 @@ from pathlib import Path _DIR = Path(__file__).parent _PROGRAM_DIR = _DIR.parent _VENV_DIR = _PROGRAM_DIR / ".venv" -_SCRIPT = _PROGRAM_DIR / "generate_samples.py" +_MODULE_DIR = _PROGRAM_DIR / "piper_sample_generator" if _VENV_DIR.exists(): context = venv.EnvBuilder().ensure_directories(_VENV_DIR) @@ -14,5 +14,5 @@ if _VENV_DIR.exists(): else: python_exe = "python3" -subprocess.check_call([python_exe, "-m", "black", str(_SCRIPT)]) -subprocess.check_call([python_exe, "-m", "isort", str(_SCRIPT)]) +subprocess.check_call([python_exe, "-m", "black", str(_MODULE_DIR)]) +subprocess.check_call([python_exe, "-m", "isort", str(_MODULE_DIR)]) diff --git a/script/lint b/script/lint index e4231e0..34222f0 100755 --- a/script/lint +++ b/script/lint @@ -6,7 +6,7 @@ from pathlib import Path _DIR = Path(__file__).parent _PROGRAM_DIR = _DIR.parent _VENV_DIR = _PROGRAM_DIR / ".venv" -_SCRIPT = _PROGRAM_DIR / "generate_samples.py" +_MODULE_DIR = _PROGRAM_DIR / "piper_sample_generator" if _VENV_DIR.exists(): context = venv.EnvBuilder().ensure_directories(_VENV_DIR) @@ -14,8 +14,8 @@ if _VENV_DIR.exists(): else: python_exe = "python3" -subprocess.check_call([python_exe, "-m", "black", str(_SCRIPT), "--check"]) -subprocess.check_call([python_exe, "-m", "isort", str(_SCRIPT), "--check"]) -subprocess.check_call([python_exe, "-m", "flake8", str(_SCRIPT)]) -subprocess.check_call([python_exe, "-m", "pylint", str(_SCRIPT)]) -subprocess.check_call([python_exe, "-m", "mypy", str(_SCRIPT)]) +subprocess.check_call([python_exe, "-m", "black", str(_MODULE_DIR), "--check"]) +subprocess.check_call([python_exe, "-m", "isort", str(_MODULE_DIR), "--check"]) +subprocess.check_call([python_exe, "-m", "flake8", str(_MODULE_DIR)]) +subprocess.check_call([python_exe, "-m", "pylint", str(_MODULE_DIR)]) +subprocess.check_call([python_exe, "-m", "mypy", str(_MODULE_DIR)]) diff --git a/script/run b/script/run index f2837d0..ae921c9 100755 --- a/script/run +++ b/script/run @@ -14,4 +14,4 @@ if _VENV_DIR.exists(): else: python_exe = "python3" -subprocess.check_call([python_exe, "generate_samples.py"] + sys.argv[1:]) +subprocess.check_call([python_exe, "-m", "piper_sample_generator"] + sys.argv[1:])