πŸ‘¨β€πŸ’» dev shondo

development

fukurou

the supreme coder
ADMIN
Python:
import os
import pygame
import threading

from fishaudio import FishAudio
from fishaudio.utils import save

from DLC.skills_monitor import DiInstaller
from LivinGrimoirePacket.AXPython import DrawRnd
from LivinGrimoirePacket.LivinGrimoire import Skill, Brain
from LivinGrimoirePacket.UniqueSkills import DiCMD

os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "1"

# ╔════════════════════════════════════════════════════════════╗
# β•‘  πŸ“ Add a 'fish_audio' directory at the same level as main.pyβ•‘
# β•‘  🎢 MP3 files will be created inside it automatically.       β•‘
# β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•


class ShorniTTS:
    def __init__(self, api_key: str, reference_id: str):
        pygame.init()
        pygame.mixer.init()
        self.client = FishAudio(api_key=api_key)
        self.reference_id = reference_id
        self.voice = "default"  # label used for filenames

    @staticmethod
    def __clean_filename(txt: str) -> str:
        invalid_chars = ['?', ':', ',', "'", '\n', '"', '!', '.', ';']
        for char in invalid_chars:
            txt = txt.replace(char, "")
        return txt.replace(" ", "_")

    @staticmethod
    def __play_file(filepath: str):
        try:
            sound = pygame.mixer.Sound(filepath)
            sound.play()
            while pygame.mixer.get_busy():
                pygame.time.delay(100)
        except pygame.error:
            if os.path.exists(filepath):
                os.remove(filepath)

    def speak(self, txt: str):
        file_name = self.__clean_filename(txt)
        filepath = f'fish_audio/{self.voice}_{file_name}.mp3'

        if len(txt) > 242:
            my_thread = threading.Thread(target=self.__create_and_play_temp, args=(txt,))
        elif os.path.isfile(filepath):
            my_thread = threading.Thread(target=self.__play_file, args=(filepath,))
        else:
            my_thread = threading.Thread(target=self.__create_and_save_and_play, args=(txt,))
        my_thread.daemon = True
        my_thread.start()

    def __create_and_play_temp(self, txt: str):
        try:
            audio = self.client.tts.convert(text=txt, reference_id=self.reference_id)
            temp_file = f'fish_audio/temp_{hash(txt)}.mp3'
            save(audio, temp_file)
            if os.path.getsize(temp_file) > 0:
                self.__play_file(temp_file)
            os.remove(temp_file)
        except Exception as e:
            print(f"Error playing TTS: {e}")

    def __create_and_save_and_play(self, txt: str):
        try:
            audio = self.client.tts.convert(text=txt, reference_id=self.reference_id)
            file_name = self.__clean_filename(txt)
            filepath = f'fish_audio/{self.voice}_{file_name}.mp3'
            save(audio, filepath)
            if os.path.getsize(filepath) > 0:
                self.__play_file(filepath)
            else:
                os.remove(filepath)
        except Exception as e:
            print(f"Error creating TTS file: {e}")

    def setVoice(self, newVoice: str, newReferenceId: str = None):
        self.voice = newVoice
        if newReferenceId:
            self.reference_id = newReferenceId


class DiTTS_fish(Skill):
    def __init__(self, api_key: str, reference_ids: list[str]):
        super().__init__()
        self.set_skill_type(3)  # continuous skill
        self.set_skill_lobe(2)  # output(hardware) skill
        self.voices: DrawRnd = DrawRnd(*reference_ids)
        # pick one reference_id at random
        ref_id = self.voices.renewableDraw()
        self.speech: ShorniTTS = ShorniTTS(api_key=api_key, reference_id=ref_id)
        self.speech.setVoice("voice")

    def input(self, ear: str, skin: str, eye: str):
        if self._kokoro.toHeart["cmd"] == "change voice":
            new_ref = self.voices.renewableDraw()
            self.speech.setVoice("voice", new_ref)
            self.speech.speak("my voice has been changed")
            return
        if len(ear) == 0:
            return
        self.speech.speak(ear)


class DiTTSInstaller(DiInstaller):
    def __init__(self, brain: Brain, api_key: str, reference_ids: list[str]):
        super().__init__(brain)
        self.skills.append(DiCMD().addModes("change voice"))
        self.skills.append(DiTTS_fish(api_key, reference_ids))

    def input(self, ear: str, skin: str, eye: str):
        self.setSimpleAlg("Installer removed; input is unreachable.")

##########
Replace "your_api_key_here" with your Fish Audio API key.

Provide a list of reference_ids (voice IDs from Fish Audio) when instantiating DiTTSInstaller.

MP3s will now be saved in fish_audio/ instead of sounds/.
 
Top