From afabe616a2659c1a023a683ec840c321c6153ae2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 12 Aug 2025 10:14:43 -0500 Subject: [PATCH 1/2] adding Pi KittenTTS demo code --- Raspberry_Pi_KittenTTS/code.py | 126 +++++++++++++++++++ Raspberry_Pi_KittenTTS/generate_forecast.py | 54 ++++++++ Raspberry_Pi_KittenTTS/weather_narrator.toml | 16 +++ 3 files changed, 196 insertions(+) create mode 100644 Raspberry_Pi_KittenTTS/code.py create mode 100644 Raspberry_Pi_KittenTTS/generate_forecast.py create mode 100644 Raspberry_Pi_KittenTTS/weather_narrator.toml diff --git a/Raspberry_Pi_KittenTTS/code.py b/Raspberry_Pi_KittenTTS/code.py new file mode 100644 index 000000000..ba7a7b147 --- /dev/null +++ b/Raspberry_Pi_KittenTTS/code.py @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import json +import time +import glob +import os +import tomllib +from datetime import datetime +import board +from digitalio import DigitalInOut, Direction, Pull +from adafruit_debouncer import Debouncer + + +from kittentts import KittenTTS +import soundfile as sf + + +print("initializing...") + +with open("weather_narrator.toml", "rb") as f: + config = tomllib.load(f) +voice = config.get("voice", None) +sound_device = config.get("sound_device", None) + +day_of_month_words = [ + "1st", + "2nd", + "3rd", + "4th", + "5th", + "6th", + "7th", + "8th", + "9th", + "10th", + "11th", + "12th", + "13th", + "14th", + "15th", + "16th", + "17th", + "18th", + "19th", + "20th", + "21st", + "22nd", + "23rd", + "24th", + "25th", + "26th", + "27th", + "28th", + "29th", + "30th", + "31st", +] + +button = DigitalInOut(board.D17) +button.direction = Direction.INPUT +button.pull = Pull.UP + +debounced_btn = Debouncer(button) + +with open("forecast.json", "r") as f: + forecast = json.load(f) + +m = KittenTTS("KittenML/kitten-tts-nano-0.1") + + +def generate_date_time_audio(date_obj): + replacements = { + "00": "oh clock", + "01": "oh 1", + "02": "oh 2", + "03": "oh 3", + "04": "oh 4", + "05": "oh 5", + "06": "oh 6", + "07": "oh 7", + "08": "oh 8", + "09": "oh 9", + } + + now_date_obj = datetime.now() + try: + os.remove("date.wav") + except FileNotFoundError: + pass + month = date_obj.strftime("%B") + day_word = day_of_month_words[date_obj.day - 1] + date_script = f"{month} {day_word}, {date_obj.year}." + + time_script = now_date_obj.strftime("%-I %M %p") + for key, val in replacements.items(): + time_script = time_script.replace(key, val) + + date_script += f" The time is: {time_script}." + audio = m.generate(date_script, voice=voice) + sf.write("date.wav", audio, 24000) + + +print("Press button to hear time and weather...") +while True: + debounced_btn.update() + if debounced_btn.fell: + print("just pressed") + + dt_format = "%Y-%m-%dT%H:%M:%S%z" + forecast_date_obj = datetime.strptime( + forecast["properties"]["periods"][0]["startTime"], dt_format + ) + + generate_date_time_audio(forecast_date_obj) + + files_to_read = glob.glob("sound_files/*.wav") + sorted_files_asc = sorted(files_to_read, key=os.path.getmtime) + sorted_files_asc.insert(0, "date.wav") + for file in sorted_files_asc: + if sound_device is None: + os.system(f"aplay {file}") + else: + os.system(f"aplay -D {sound_device} {file}") + + time.sleep(0.01) diff --git a/Raspberry_Pi_KittenTTS/generate_forecast.py b/Raspberry_Pi_KittenTTS/generate_forecast.py new file mode 100644 index 000000000..380580bca --- /dev/null +++ b/Raspberry_Pi_KittenTTS/generate_forecast.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import json +import os +import tomllib +import shutil +import requests +from kittentts import KittenTTS +import soundfile as sf + + +with open("weather_narrator.toml", "rb") as f: + config = tomllib.load(f) + +m = KittenTTS("KittenML/kitten-tts-nano-0.1") + +replacements = {"mph": "miles per hour"} + +# latlng_lookup_url = "https://api.weather.gov/points/{lat},{lon}" + +voice = config.get("voice", None) +location_points = config.get("location_points", "36,33") + +weather_data = requests.get( + f"https://api.weather.gov/gridpoints/TOP/{location_points}/forecast", timeout=20 +).json() +print("Got weather. Building script...") + +with open("forecast.json", "w") as f: + json.dump(weather_data, f) + +forecast_length = config.get("forecast_length", None) + +shutil.rmtree("sound_files", ignore_errors=True) +os.mkdir("sound_files") + +for i, period in enumerate(weather_data["properties"]["periods"]): + if forecast_length is None or i < forecast_length: + filename = period["name"].replace(" ", "_") + outstr = "" + if i == 0: + outstr += f'Current Temperature is {period["temperature"]} degrees. ' + outstr += f'{period["name"]} {period["detailedForecast"]}' + + for key, replacement in replacements.items(): + outstr = outstr.replace(key, replacement) + print(f"script: {outstr}") + print("Generating audio...") + audio = m.generate(outstr, voice=voice) + output_file = f"sound_files/{filename}.wav" + print(f"Writing {output_file}") + sf.write(output_file, audio, 24000) +print("Audio generation complete") diff --git a/Raspberry_Pi_KittenTTS/weather_narrator.toml b/Raspberry_Pi_KittenTTS/weather_narrator.toml new file mode 100644 index 000000000..8f16f0672 --- /dev/null +++ b/Raspberry_Pi_KittenTTS/weather_narrator.toml @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +voice = "expr-voice-3-f" +location_points = "36,33" +forecast_length = 3 +#sound_device = "plughw:3,0" +available_voices = [ + 'expr-voice-2-m', + 'expr-voice-2-f', + 'expr-voice-3-m', + 'expr-voice-3-f', + 'expr-voice-4-m', + 'expr-voice-4-f', + 'expr-voice-5-m', + 'expr-voice-5-f' ] \ No newline at end of file From 2a399b4f0fb9a31a5e9b4df170df8aa9577de21b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 12 Aug 2025 10:16:22 -0500 Subject: [PATCH 2/2] eof newline --- Raspberry_Pi_KittenTTS/weather_narrator.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Raspberry_Pi_KittenTTS/weather_narrator.toml b/Raspberry_Pi_KittenTTS/weather_narrator.toml index 8f16f0672..15acb2987 100644 --- a/Raspberry_Pi_KittenTTS/weather_narrator.toml +++ b/Raspberry_Pi_KittenTTS/weather_narrator.toml @@ -13,4 +13,4 @@ available_voices = [ 'expr-voice-4-m', 'expr-voice-4-f', 'expr-voice-5-m', - 'expr-voice-5-f' ] \ No newline at end of file + 'expr-voice-5-f' ]