This GEMINI.md file provides essential context for working with the piper1-gpl project, especially within a Termux (Android) environment.
Current Task:
The project is currently in a maintenance status. Our primary focus is to address any issues that may arise and ensure the continued stability and functionality of the piper1-gpl project. New features or major refactoring will only be undertaken if specifically requested or deemed critical for the project's health.
This section clarifies the relationships between the various Git repositories encountered during this session to prevent future confusion.
1. Current Working Directory Repository (/data/data/com.termux/files/home/downloads/GitHub/piper1-gpl)
* **Local Path:** `/data/data/com.termux/files/home/downloads/GitHub/piper1-gpl`
- Your Fork (
originremote):https://github.com/Manamama/piper1-gpl- This is the user's personal fork of the
piper1-gplproject. Local changes are pushed to this remote.
- This is the user's personal fork of the
- Original/Upstream (
upstreamremote):https://github.com/OHF-Voice/piper1-gpl- This is the repository from which the user's
Manamama/piper1-gplfork was created. Updates are pulled from here to synchronize the user's fork with the original project.
- This is the repository from which the user's
* **Local Path:** `/data/data/com.termux/files/home/downloads/GitHub/piper1-gpl/piper-tts-for-termux`
- Its Origin:
https://github.com/gyroing/piper-tts-for-termux- This is a distinct Git repository. Its primary online location is
gyroing/piper-tts-for-termux.
- This is a distinct Git repository. Its primary online location is
- Its Fork Remote: This repository also contains a remote named
forkpointing tohttps://github.com/Manamama/piper1-gpl.git. This suggests an interaction or consumption of the user'spiper1-gplfork by thispiper-tts-for-termuxproject.
This section summarizes the current state of building Piper TTS on Termux, highlighting the successful compilation and the automated improvements implemented.
The project now compiles successfully on Termux. The following key improvements have been made to streamline the build process:
- Hybrid
espeak-ngStrategy: The build system employs a two-stage strategy for maximum reliability. It first ensures the system'sespeak-ngpackage is installed viapkgto satisfy any underlying dependencies. It then proceeds to clone and compile a fresh version ofespeak-ngfrom source. This guarantees that the project links against a known, consistent version of the library, eliminating potential ABI conflicts and ensuring a self-contained, robust build. - Automated ONNX Runtime Handling: The
CMakeLists.txtnow automatically detects and links against the system'slibonnxruntime.soprovided by thepython-onnxruntimeTermux package. This eliminates the need for manual downloading, extraction, or linking against pre-compiled.aarfiles, ensuring better ABI compatibility and a more streamlined build process. - ABI Compatibility Resolved: By ensuring all native C++ components (like
espeakbridge.soandpiper_phonemize_cpp) are built and linked against the system'slibc++and other core libraries, the notorious ABI compatibility issues (such as thenlohmann::jsonparsing errors) are inherently addressed. - Simplified Installation: The overall goal is to transform the installation into a "go for coffee" experience. After installing the initial
pkgprerequisites, a simplepip install piper-ttswill manage the compilation and linking of all native components, allowing the user to focus on using Piper rather than troubleshooting build errors. - Reduced Manual Intervention: The need for manual extraction of
libonnxruntime.sofrom.aarfiles or usingpatchelffor library path adjustments is significantly reduced or provided as clear fallback steps.
Before executing any file modification or complex command, I must pause and perform a "Strategic Sanity Check." This involves asking:
- What is the overall strategic goal? (e.g., "To create a reliable build," "To fix a specific bug," "To refactor for clarity.")
- Does my planned action directly and logically serve this goal?
- Does this action conflict with any "Lessons Learned" or historical failures documented in my memory files?
If my planned action fails this check—if it is a tactical solution that undermines the strategic goal or repeats a past mistake—I must stop, report the conflict to the user, and propose a better course of action. I will not be a "bulldog" focused only on the immediate task if it compromises the larger objective.
Termux Environment and Shebangs: Its crucial to understand that Termux operates on top of the Android OS. When a scripts shebang (e.g., #!/bin/sh) is invoked, it typically resolves to the native Android system shell (/bin/sh), not the Termux shell, unless specific Termux virtualization commands are employed. This native shell is often minimal and may not be suitable for complex build scripts. To ensure scripts are run by the full-featured Termux shell, explicitly invoke them with /data/data/com.termux/files/usr/bin/bash -c "..." or ensure the PATH is correctly set for the sub-process. If unsure about the nature of a binary or script, use file <path> or ldd <path> to inspect it.
When testing the ready piper package do use the audio models which are located in ~/.cache/piper. For the API synthesis test to pass, the en_US-lessac-medium voice model must be downloaded to ~/.cache/piper/ using python3 -m piper.download_voices en_US-lessac-medium. Do use piper's --help to learn how to
UPDATE: The root cause of recent build failures (related to both compile-time and runtime linking errors) has been identified. The full analysis and remediation plan can be found in BUILD_FAILURE_ANALYSIS.md.
When attempting to run the piper command, an ImportError occurs, indicating that the espeakbridge module cannot be imported:
ImportError: cannot import name 'espeakbridge' from 'piper' (/data/data/com.termux/files/usr/lib/python3.12/site-packages/piper/__init__.py)
This suggests that the native espeakbridge component, crucial for phonemization, is is not being correctly built or installed during the pip install . process. This is the primary blocker for the piper package's functionality.
The persistent ImportError: cannot import name 'espeakbridge' has been addressed. The root cause was the espeakbridge.c C extension not being properly compiled and integrated into the Python package.
Solution:
setup.py was modified to explicitly define espeakbridge as a setuptools.Extension. This involved:
- Adding
import sysandfrom setuptools import Extension. - Defining an
espeakbridge_extensionobject, specifyingsrc/piper/espeakbridge.cas its source. - Using
sys.prefixto dynamically determine theespeak-nginclude and library paths for portability (e.g.,Path(sys.prefix) / "include" / "espeak-ng"). - Adding
espeakbridge_extensionto theext_moduleslist in thesetup()call.
Verification (Manual Build):
Running python3 setup.py build_ext --inplace successfully compiled espeakbridge.c into espeakbridge.cpython-312.so and placed it in the src/piper directory, resolving the compilation aspect of the ImportError.
Next Steps:
While the C extension now compiles, the piper package itself is not yet fully discoverable by the Python interpreter in a standard way (e.g., python3 -m piper still fails). This indicates that a proper installation (e.g., via pip install . or pip install -e .) is still required to make the package and its entry points accessible in the Python environment.
The persistent ImportError: cannot import name 'espeakbridge' has been fully addressed, and the piper package now installs and functions correctly with pip install . (including build isolation).
Root Cause:
The primary issue was the espeakbridge.c C extension not being properly compiled and integrated into the Python package, specifically due to setuptools misinterpreting relative paths as absolute within pip's isolated build environment.
Solution:
setup.py was modified to explicitly define espeakbridge as a setuptools.Extension with a robust relative path construction. This involved:
- Adding
import osand ensuringimport sysandfrom setuptools import Extensionare present. - Defining an
espeakbridge_extensionobject. - Crucially, setting the
sourcesargument forespeakbridge_extensionto useos.path.relpath(os.path.join(os.path.dirname(__file__), "src", "piper", "espeakbridge.c")). This ensures the path is always correctly interpreted as relative tosetup.py, even in isolated build environments. - Using
sys.prefixto dynamically determine theespeak-nginclude and library paths for portability (e.g.,str(Path(sys.prefix) / "include" / "espeak-ng")). - Adding
espeakbridge_extensionto theext_moduleslist in thesetup()call.
Verification:
Running pip install . -v now successfully builds and installs the piper package, including the espeakbridge C extension. The piper command is accessible in the PATH, and speech generation functions as expected.
The README.md file has been significantly updated and simplified to reflect the successful build process and provide a clearer user experience. Key changes include:
- Introduction of a "Quick Start (Recommended Method)" section: This new section highlights the streamlined
pip install .process, making it the primary and easiest way for users to get started. - Removal of obsolete manual build instructions: All detailed, step-by-step guides for manual compilation, ONNX Runtime handling, and
patchelfusage have been removed as they are no longer necessary with the automated build. - Consolidation and update of usage examples: The various usage examples have been combined into a single, comprehensive "Usage" section. All examples now correctly demonstrate how to use the installed Python package via
python3 -m piper. - Refined "Environment Variables" section: This section has been updated to reflect the relevant environment variables for the Python API and CLI, removing outdated information.
- Removal of the Debian package section: The section detailing the
piper-tts-clideb package has been removed as it is no longer the primary or recommended installation method. - Retention of "Building from Source & Development": This section remains and now explicitly points to
docs/BUILDING.mdfor advanced users who need detailed build information or wish to contribute to development.
The project's CMakeLists.txt has been refactored to merge platform-specific build logic into a single, unified file. This improves maintainability and simplifies the build process across different operating systems. Key changes include:
- Consolidated Build Logic: The separate build configurations for Windows, Android (Termux), and generic Unix (Linux/macOS) have been merged into one
CMakeLists.txtusing conditionalif(WIN32),elseif(ANDROID), andelseif(UNIX)blocks. - Platform-Specific External Project Handling: The
espeak-ngexternal project is now built with parameters tailored to each platform (e.g., static libraries for Windows/Unix, shared libraries for Android). - Unified
espeakbridgeDefinition: TheespeakbridgePython C extension is defined once, with its linking and properties adjusted based on the target platform. - Automated Dependency Management for Android: The Android-specific section retains the
pkgcalls for automatic installation of Termux prerequisites and the discovery of the system'sonnxruntimelibrary. - Renamed Original CMakeLists: The original
CMakeLists.txt(version 1.3.0) has been renamed toCMakeLists.txt.bakfor historical reference.
This section summarizes key insights and practical takeaways from the recent development session focused on improving the piper1-gpl build process and documentation.
replacetool limitations: Thereplacetool proved challenging for complex, multi-line text manipulations due to its strict exact-match requirement (including whitespace and indentation). This led to repeated failures and inefficient iterations.- Over-aggressive content removal: Initial attempts to simplify
README.mdresulted in the accidental removal of valuable end-user information, highlighting the need for a more nuanced approach to documentation updates. - Complexity of
CMakeLists.txtmerge: While conceptually straightforward, the practical implementation of merging platform-specific CMake logic required meticulous attention to detail regarding variable scoping, external project configurations, and dependency management across Windows, Android, and generic Unix.
- Effective problem understanding: The existing
GEMINI.mdand clear error messages from theespeakbridgeImportErrorfacilitated a quick grasp of the core build challenges. - Conceptual clarity of CMake merge: The strategy of using conditional
if(PLATFORM)blocks for unifying the build system was a clear and effective path forward.
run_shell_commandwithsed: This proved to be an invaluable tool for robust text manipulation, especially for multi-line replacements and deletions where thereplacetool struggled. Its flexibility and power were critical in overcoming previous roadblocks.GEMINI.mdas a knowledge base: The detailed historical context and ongoing documentation withinGEMINI.mdprovided a strong foundation for understanding project specifics and past solutions.
Current Status: The en_US-lessac-medium voice model (and its corresponding JSON configuration file) is confirmed to be present and ready in the ~/.cache/piper/ directory. This was verified by running ls -F ~/.cache/piper/
The runtime error related to the .onnx.onnx file path has been resolved. The issue stemmed from a semantic mismatch between src/piper/__main__.py and src/piper/voice.py regarding how voice model paths were handled. __main__.py was passing a full file path to voice.py's load_by_name method, which expected a simple voice name and then re-appended the .onnx extension, leading to the duplication.
Resolution: The bug was resolved by reverting both src/piper/__main__.py and src/piper/voice.py to their respective upstream/main versions. This removed the problematic load_by_name method from voice.py and restored __main__.py's original logic for handling model paths directly.
Lessons Learned: This particular bug proved challenging and time-consuming to diagnose and fix due to several factors, including initial misinterpretations of the problem's scope and the impact of local development environment nuances (e.g., pip install -e). A more strategic approach, focusing on understanding the intended API contracts and the propagation of changes, would have led to a quicker resolution. Further lessons learned regarding debugging strategies and AI-human collaboration will be documented soon.
This section outlines the detailed steps for preparing the feat/termux-build-pr branch for the upstream Pull Request, ensuring only relevant changes are included.
- Switch to
feat/termux-build-pr: Ensure we are on the correct branch.git checkout feat/termux-build-pr - Reset to
upstream/main: Ensure a clean slate, discarding any previous partial changes on this branch.git reset --hard upstream/main - Copy Relevant Files from
termux-build-improvements: For each file that is modified or added intermux-build-improvementsand should be included in the upstream PR, copy its content to the current branch.- Added Files (from
termux-build-improvements):Architecture_materials/Mermaid_02.mdArchitecture_materials/Mermaid_Piper_02.pnglibpiper/clean_text.cpplibpiper/piper.cpplibpiper/say.cpppytest.inisrc/piper/include/piper.htests/test_installation_api.pytests/test_installation_cli.py
- Modified Files (from
termux-build-improvements):.gitignoreCHANGELOG.mdCMakeLists.txtREADME.mddocs/BUILDING.mdlibpiper/CMakeLists.txtpyproject.tomlsetup.pysrc/piper/phonemize_espeak.py
- Added Files (from
- Handle Deleted Files: Remove
build_monotonic_align.shas it was deleted intermux-build-improvementsand should be deleted in the upstream PR.rm build_monotonic_align.sh - Stage All Changes: Stage all the copied and deleted files.
git add . - Commit Changes: Create a single, comprehensive commit with the agreed-upon message. `git commit -m "feat: Termux build improvements and unified CMakeLists.txt
..."7. **Verify Diff:** Confirm that the staged changes accurately reflect only the intended PR content.git diff upstream/main --name-status8. **Run Tests:** Install the package and run the full test suite to ensure everything works as expected on this PR branch.pip install . pytest9. **Push to Fork:** Push thefeat/termux-build-prbranch to the user's fork.git push origin feat/termux-build-pr10. **Create Pull Request:** Usegh pr createto open the PR toOHF-Voice/piper1-gpl:main`.
Recent audio playback issues have highlighted a critical principle for development in Termux: direct linking against native system libraries is superior to using complex, ported abstractions.
- The Problem: Solutions relying on ported Linux sound servers like PulseAudio or toolkits like SoX have proven unreliable and difficult to debug on Android. They introduce unnecessary layers that conflict with the native Android audio stack.
- The Solution: The most successful and stable approach is to use tools that link directly against native Android system libraries. The
play-audioutility is the prime example. As confirmed byldd, it links directly tolibOpenSLES.so, the native Android audio library. This direct path is efficient, low-latency, and reliable. - Our Guiding Principle: For audio output and other low-level hardware interactions, we will prioritize solutions that leverage direct linking to the underlying system's
.solibraries. Thelddcommand will be a key diagnostic tool to verify an executable's dependencies and ensure it follows this principle. This strategy is critical for avoiding and fixing bugs related to hardware integration on Android.
Status: Under Investigation (Testing based on user reports)
Based on real-world reports, a critical runtime linking failure can occur on Android after a successful installation. The application may fail to start or crash with an "undefined symbol" error.
The issue stems from the hybrid build strategy for espeak-ng on Android:
- Private Shared Library: The build process compiles a private, shared
libespeak-ng.sowhich is used to link the finalespeakbridge.somodule. RPATHIssue: The installedespeakbridge.somodule may lack a correctRPATH(runtime search path) pointing to the location of this privatelibespeak-ng.so.- Incorrect Linker Resolution: At runtime, the Android dynamic linker fails to find the private library. It falls back to searching system paths and incorrectly loads the generic
libespeak-ng.sothat was installed viapkg. - ABI Mismatch: This causes a fatal ABI (Application Binary Interface) mismatch, as
espeakbridge.sowas compiled to work with the private library, not the system one, leading to a crash.
We are currently testing solutions to ensure the RPATH of the espeakbridge.so module is correctly configured to point to its private libespeak-ng.so, which should resolve this failure.
A key lesson from the recent build failures is the importance of understanding and controlling the exact role of our dependencies. A library like espeak-ng can have multiple functions, and failing to disable the ones we don't need can introduce critical build and runtime errors.
-
Phonemization Engine (What Piper NEEDS): This is
espeak-ng's core linguistic capability. It takes input text ("Hello World") and converts it into a stream of phonemes (həˈloʊ wɜːld). This is the only part ofespeak-ngthatpiperuses. It acts as the "front-end" that feeds thepiperneural network. -
Audio Player (What Piper DOES NOT NEED):
espeak-ngcan also function as a complete, standalone text-to-speech system. It has its own simple synthesizer and "speech backends" to send the audio it generates to your speakers using libraries like PulseAudio, ALSA, etc.
Piper does not use espeak-ng's built-in audio player. Instead, piper takes the phonemes from espeak-ng and processes them through its own high-quality neural network models via onnxruntime to generate the final audio.
The problem is that when we build espeak-ng from source, its configure script automatically detects and enables its own audio player backends (like the PulseAudio one). This causes our private libespeak-ng.so to be linked against libpulse.so, introducing the unwanted dependency and causing the runtime linking failures.
This principle of disabling unused features in dependencies is key to creating a stable and minimal build.