adding Pi KittenTTS demo code
This commit is contained in:
parent
e804e240a6
commit
afabe616a2
3 changed files with 196 additions and 0 deletions
126
Raspberry_Pi_KittenTTS/code.py
Normal file
126
Raspberry_Pi_KittenTTS/code.py
Normal file
|
|
@ -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)
|
||||||
54
Raspberry_Pi_KittenTTS/generate_forecast.py
Normal file
54
Raspberry_Pi_KittenTTS/generate_forecast.py
Normal file
|
|
@ -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")
|
||||||
16
Raspberry_Pi_KittenTTS/weather_narrator.toml
Normal file
16
Raspberry_Pi_KittenTTS/weather_narrator.toml
Normal file
|
|
@ -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' ]
|
||||||
Loading…
Reference in a new issue