126 lines
3.3 KiB
Python
126 lines
3.3 KiB
Python
import wave
|
|
from piper import PiperVoice
|
|
import pygame
|
|
import time
|
|
import os
|
|
import sys
|
|
import pyaudio
|
|
import requests
|
|
from pathlib import Path
|
|
from enum import Enum
|
|
|
|
class Language(Enum):
|
|
en_US = "en_US"
|
|
de_DE = "de_DE"
|
|
en_GB = "en_GB"
|
|
|
|
|
|
class Quality(Enum):
|
|
high = 3
|
|
medium = 2
|
|
low = 1
|
|
x_low = 0
|
|
|
|
class LLM:
|
|
def __init__(self, model: str, url: str):
|
|
self.url = url
|
|
self.model = model
|
|
|
|
|
|
class Narrator:
|
|
def __init__(self, language: Language, name: str, repo: Path, llm: LLM):
|
|
|
|
for quality in Quality:
|
|
filename = f"{language.name}-{name}-{quality.name}.onnx"
|
|
self.path = repo / language.name / name / quality.name / filename
|
|
|
|
print("Teste:", self.path)
|
|
|
|
if self.path.exists():
|
|
print("Gefunden:", self.path)
|
|
break
|
|
else:
|
|
raise FileNotFoundError("Keine passende Voice-Datei gefunden")
|
|
|
|
print("load voice")
|
|
|
|
self.voice = PiperVoice.load(self.path)
|
|
|
|
# for narrator texts -> connect to LLM
|
|
print(" create llm")
|
|
self.llm = llm
|
|
|
|
|
|
# Stream audio chunks and play them immediately
|
|
def read(self, text: str):
|
|
p = pyaudio.PyAudio()
|
|
|
|
stream = None
|
|
|
|
for audio_chunk in self.voice.synthesize(text):
|
|
|
|
# Stream erst öffnen, wenn erster Chunk da ist
|
|
if stream is None:
|
|
stream = p.open(
|
|
format=p.get_format_from_width(audio_chunk.sample_width),
|
|
channels=audio_chunk.sample_channels,
|
|
rate=audio_chunk.sample_rate,
|
|
output=True
|
|
)
|
|
|
|
# Audio sofort abspielen
|
|
stream.write(audio_chunk.audio_int16_bytes)
|
|
|
|
if stream:
|
|
stream.stop_stream()
|
|
stream.close()
|
|
|
|
p.terminate()
|
|
|
|
|
|
# play selected audiofile and delete it after playing
|
|
def read_audio(self, number: int):
|
|
#setup
|
|
filename = f"output{number}.wav"
|
|
pygame.mixer.init()
|
|
|
|
#play next audio
|
|
sound = pygame.mixer.Sound(filename)
|
|
channel = sound.play()
|
|
|
|
# wait until the sound has finished playing
|
|
while channel.get_busy():
|
|
time.sleep(0.1)
|
|
|
|
# delete now obsolete file
|
|
os.remove(filename)
|
|
|
|
pygame.mixer.quit()
|
|
|
|
|
|
# create wavefile from text
|
|
def create_audio(self, texts: list[str]):
|
|
|
|
for i, text in enumerate(texts):
|
|
filename = f"output{i}.wav"
|
|
|
|
with wave.open(f"output{i}.wav", "wb") as wav_file:
|
|
self.voice.synthesize_wav(text, wav_file)
|
|
|
|
# create a story from a base text
|
|
def write_story(self, text, count):
|
|
|
|
session = requests.Session()
|
|
session.trust_env = False
|
|
|
|
payload = {
|
|
"model" : self.llm.model,
|
|
"input": "Formuliere:" + text +" zu einem erzählerischen Sprachtext um. Nicht mehr als " + count + " Sätze. Handlungsaufforderungen sollen klar heraushörbar sein."
|
|
}
|
|
|
|
response = requests.post(self.llm.url, json=payload, timeout=30)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
story = data["output"][0]["content"]
|
|
|
|
return story |