{"id":317,"date":"2024-03-30T14:06:19","date_gmt":"2024-03-30T14:06:19","guid":{"rendered":"https:\/\/noerguerra.com\/?p=317"},"modified":"2024-10-26T06:05:55","modified_gmt":"2024-10-26T06:05:55","slug":"how-to-read-text-aloud-with-piper-and-python","status":"publish","type":"post","link":"https:\/\/noerguerra.com\/blog\/how-to-read-text-aloud-with-piper-and-python\/","title":{"rendered":"How to read text aloud with Piper and Python"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Piper<\/strong> is a neural text-to-speech system that can run locally and deliver great sounding audio clips even on underpowered computers. Piper is optimized to run on the Raspberry Pi 4, and you can easily import it to your application as a library.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I stumbled upon Piper TTS after looking for a simple text-to-speech application where I could input text and read it out loud on Linux Minut. Coming from using Microsoft online voices on Microsoft Edge, I was looking for a more natural sound than the robotic voices from programs like Festival or eSpeak, and I was impressed by the natural sound of Piper and its capabilities to run smoothly on almost any kind of modern computer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can listen to <a href=\"https:\/\/rhasspy.github.io\/piper-samples\/\">samples generated using Piper here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing Piper on Linux<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can install Piper through pip with this command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install piper-tts<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Afterwards, you should be able to import <code>piper<\/code> into your program with <code>import piper<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can quickly test piper from the terminal by piping the output of a program for it to read, like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"Hello world! This is text to speech\" | piper \\\n--model en_US-lessac-medium \\\n--output_file audio.wav<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The resulting audio will be saved in <code>audio.wav<\/code> and can be played with any media player.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding models to Piper<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Piper repository includes a variety of <a href=\"https:\/\/huggingface.co\/rhasspy\/piper-voices\/tree\/main\">pre-trained voice models<\/a> sorted by language that you can use in your projects. These models determine how the synthesized speech will sound, in other words, each model is a different &#8220;voice&#8221; you can use with Piper (Although occasionally a model will contain multiple voices). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can also create your own voice model using the Piper Recording Studio, a web application that you can run locally to generate a Piper dataset by recording clips with your voice. However, make sure to have a decent graphics card on your device, or training your model could be a very slow task. For more information on the process of creating a model for Piper, look at <a href=\"https:\/\/ssamjh.nz\/create-custom-piper-tts-voice\/\">this article from Sam Howell<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In order to add models to Piper, you need to obtain the <code>.onnx<\/code> format and the <code>.onnx.json<\/code> file. These JSON files contain important metadata about the models, such as their sample rate and phoneme set, and must always have the same name as the <code>.onnx<\/code> file, and be located within the same directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>directory\/\n|-- ...\n|-- es_MX-claude-high.onnx\n|-- es_MX-claude-high.onnx.json\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Generating audio files from text<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To generate audio files programatically using Piper, you&#8217;ll need to import the <code>PiperVoice<\/code> class and use the appropiate methods, like this (based on <a href=\"https:\/\/github.com\/rhasspy\/piper\/discussions\/326#discussioncomment-7935208\">this answer<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import wave\nfrom piper.voice import PiperVoice\n\nmodel = \"\/path\/to\/model.onnx\"\nvoice = PiperVoice.load(model)\ntext = \"This is an example of text to speech\"\nwav_file = wave.open(\"output.wav\", \"w\")\naudio = voice.synthesize(text, wav_file)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this code, we:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Import the wave module to create WAV files, and PiperVoice to generate the audio from our text.<\/li>\n\n\n\n<li>Specify the <code>model<\/code> file we want to use and load it into Piper.<\/li>\n\n\n\n<li>Create a <code>wav_file<\/code> object where the program will write the synthesized audio data.<\/li>\n\n\n\n<li>Define the <code>text<\/code> we want to convert to speech.<\/li>\n\n\n\n<li>We call the synthesize method of PiperVoice to generate <code>audio<\/code> from the text and save it to the WAV file.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Streaming text to speech with Piper<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It is possible to stream the audio directly to an audio device without having to save it to a file first, as seen in <a href=\"https:\/\/github.com\/rhasspy\/piper\/discussions\/326#discussioncomment-8855827\">this answer<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import numpy as np\nimport sounddevice as sd\nfrom piper.voice import PiperVoice\n\nmodel = \"\/path\/to\/model.onnx\"\nvoice = PiperVoice.load(model)\ntext = \"This is an example of text to speech\"\n\n# Setup a sounddevice OutputStream with appropriate parameters\n# The sample rate and channels should match the properties of the PCM data\nstream = sd.OutputStream(samplerate=voice.config.sample_rate, channels=1, dtype='int16')\nstream.start()\n\nfor audio_bytes in voice.synthesize_stream_raw(text):\n    int_data = np.frombuffer(audio_bytes, dtype=np.int16)\n    stream.write(int_data)\n\nstream.stop()\nstream.close()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you get an error <code>OSError: PortAudio library not found<\/code>, you can fix it by installing the portaudio library. You can do this in Ubuntu and Debian-based distributions with this command:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>sudo apt-get install libportaudio2<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The previous code is similar to the one we used to create WAV files from text. This time, we:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Import <code>sounddevice<\/code> for audio streaming, <code>PiperVoice<\/code> generate the audio from our text, and <code>numpy<\/code> to interpret the data as an array.<\/li>\n\n\n\n<li>Define the <code>model<\/code> we want to use and load it into Piper.<\/li>\n\n\n\n<li>Provide the <code>text<\/code> we want to convert to speech.<\/li>\n\n\n\n<li>Set up a <code>sounddevice OutputStream<\/code> with parameters matching the properties of the PCM (Pulse Code Modulation) data produced by Piper. This <code>stream<\/code> will be used to play the audio generated by Piper.<\/li>\n\n\n\n<li>Iterate over the raw audio data generated by <code>voice.synthesize_stream_raw<\/code>, convert it to an array of integers, and write it to the <code>stream<\/code> for real-time playback.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In summary, Piper offers a powerful solution for local text-to-speech synthesis. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Although the speech quality is not as high as tools like <a href=\"https:\/\/coqui.ai\/\">Coqui<\/a>, the fact that Piper can generate audio quickly in devices with limited resources make it, in my opinion, the best local text to speech tool currently.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By importing Piper as a library with Python, you can easily integrate it into your programs, and deliver natural sounding voices while barely affecting performance. If you want to see an example, take a look at this simple <a href=\"https:\/\/github.com\/NoeRGuerra\/PiperReadAloudGUI\/\">read aloud program<\/a> that I wrote with Python and Tkinter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Update:<\/strong> The original code mistakenly called <code>PiperVoice(model)<\/code>, which was incorrect and didn\u2019t work as intended because the <code>.load()<\/code> method was missing. The corrected code now calls <code>PiperVoice.load(model)<\/code>. Thanks to everyone who pointed this out! \ud83d\ude42\ufe0f<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Piper is a neural text-to-speech system that can run locally and deliver great sounding audio clips even on underpowered computers. Piper is optimized to run on the Raspberry Pi 4, and you can easily import it to your application as a library. I stumbled upon Piper TTS after looking for a simple text-to-speech application where&hellip; <a class=\"more-link\" href=\"https:\/\/noerguerra.com\/blog\/how-to-read-text-aloud-with-piper-and-python\/\">Continue reading <span class=\"screen-reader-text\">How to read text aloud with Piper and Python<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":326,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4],"tags":[20],"class_list":["post-317","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-text-to-speech","entry"],"_links":{"self":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/317","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/comments?post=317"}],"version-history":[{"count":11,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/317\/revisions"}],"predecessor-version":[{"id":336,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/posts\/317\/revisions\/336"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/media\/326"}],"wp:attachment":[{"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/media?parent=317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/categories?post=317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/noerguerra.com\/blog\/wp-json\/wp\/v2\/tags?post=317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}