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