@ -13,26 +13,40 @@ class Tts:
self . install ( config [ " service_directory " ] )
def tts ( self , text ) :
with tempfile . NamedTemporaryFile ( suffix = " .wav " , delete = False ) as temp_file :
output_file = temp_file . name
piper_dir = self . piper_directory
subprocess . run ( [
os . path . join ( piper_dir , ' piper ' ) ,
' --model ' , os . path . join ( piper_dir , os . getenv ( ' PIPER_VOICE_NAME ' , ' en_US-lessac-medium.onnx ' ) ) ,
' --output_file ' , output_file
] , input = text , stdout = subprocess . PIPE , stderr = subprocess . PIPE , text = True )
subprocess . run (
[
os . path . join ( piper_dir , " piper " ) ,
" --model " ,
os . path . join (
piper_dir ,
os . getenv ( " PIPER_VOICE_NAME " , " en_US-lessac-medium.onnx " ) ,
) ,
" --output_file " ,
output_file ,
] ,
input = text ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
text = True ,
)
# TODO: hack to format audio correctly for device
outfile = tempfile . gettempdir ( ) + " / " + " raw.dat "
ffmpeg . input ( temp_file . name ) . output ( outfile , f = " s16le " , ar = " 16000 " , ac = " 1 " , loglevel = ' panic ' ) . run ( )
ffmpeg . input ( temp_file . name ) . output (
outfile , f = " s16le " , ar = " 16000 " , ac = " 1 " , loglevel = " panic "
) . run ( )
return outfile
def install ( self , service_directory ) :
PIPER_FOLDER_PATH = service_directory
self . piper_directory = os . path . join ( PIPER_FOLDER_PATH , ' piper ' )
if not os . path . isdir ( self . piper_directory ) : # Check if the Piper directory exists
self . piper_directory = os . path . join ( PIPER_FOLDER_PATH , " piper " )
if not os . path . isdir (
self . piper_directory
) : # Check if the Piper directory exists
os . makedirs ( PIPER_FOLDER_PATH , exist_ok = True )
# Determine OS and architecture
@ -60,52 +74,92 @@ class Tts:
asset_url = f " { PIPER_URL } { PIPER_ASSETNAME } "
if OS == " windows " :
asset_url = asset_url . replace ( " .tar.gz " , " .zip " )
# Download and extract Piper
urllib . request . urlretrieve ( asset_url , os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME ) )
urllib . request . urlretrieve (
asset_url , os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME )
)
# Extract the downloaded file
if OS == " windows " :
import zipfile
with zipfile . ZipFile ( os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME ) , ' r ' ) as zip_ref :
with zipfile . ZipFile (
os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME ) , " r "
) as zip_ref :
zip_ref . extractall ( path = PIPER_FOLDER_PATH )
else :
with tarfile . open ( os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME ) , ' r:gz ' ) as tar :
with tarfile . open (
os . path . join ( PIPER_FOLDER_PATH , PIPER_ASSETNAME ) , " r:gz "
) as tar :
tar . extractall ( path = PIPER_FOLDER_PATH )
PIPER_VOICE_URL = os . getenv ( ' PIPER_VOICE_URL ' ,
' https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/ ' )
PIPER_VOICE_NAME = os . getenv ( ' PIPER_VOICE_NAME ' , ' en_US-lessac-medium.onnx ' )
PIPER_VOICE_URL = os . getenv (
" PIPER_VOICE_URL " ,
" https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/ " ,
)
PIPER_VOICE_NAME = os . getenv ( " PIPER_VOICE_NAME " , " en_US-lessac-medium.onnx " )
# Download voice model and its json file
urllib . request . urlretrieve ( f " { PIPER_VOICE_URL } { PIPER_VOICE_NAME } " ,
os . path . join ( self . piper_directory , PIPER_VOICE_NAME ) )
urllib . request . urlretrieve ( f " { PIPER_VOICE_URL } { PIPER_VOICE_NAME } .json " ,
os . path . join ( self . piper_directory , f " { PIPER_VOICE_NAME } .json " ) )
urllib . request . urlretrieve (
f " { PIPER_VOICE_URL } { PIPER_VOICE_NAME } " ,
os . path . join ( self . piper_directory , PIPER_VOICE_NAME ) ,
)
urllib . request . urlretrieve (
f " { PIPER_VOICE_URL } { PIPER_VOICE_NAME } .json " ,
os . path . join ( self . piper_directory , f " { PIPER_VOICE_NAME } .json " ) ,
)
# Additional setup for macOS
if OS == " macos " :
if ARCH == " x64 " :
subprocess . run ( [ ' softwareupdate ' , ' --install-rosetta ' , ' --agree-to-license ' ] )
subprocess . run (
[ " softwareupdate " , " --install-rosetta " , " --agree-to-license " ]
)
PIPER_PHONEMIZE_ASSETNAME = f " piper-phonemize_ { OS } _ { ARCH } .tar.gz "
PIPER_PHONEMIZE_URL = " https://github.com/rhasspy/piper-phonemize/releases/latest/download/ "
urllib . request . urlretrieve ( f " { PIPER_PHONEMIZE_URL } { PIPER_PHONEMIZE_ASSETNAME } " ,
os . path . join ( self . piper_directory , PIPER_PHONEMIZE_ASSETNAME ) )
with tarfile . open ( os . path . join ( self . piper_directory , PIPER_PHONEMIZE_ASSETNAME ) , ' r:gz ' ) as tar :
urllib . request . urlretrieve (
f " { PIPER_PHONEMIZE_URL } { PIPER_PHONEMIZE_ASSETNAME } " ,
os . path . join ( self . piper_directory , PIPER_PHONEMIZE_ASSETNAME ) ,
)
with tarfile . open (
os . path . join ( self . piper_directory , PIPER_PHONEMIZE_ASSETNAME ) ,
" r:gz " ,
) as tar :
tar . extractall ( path = self . piper_directory )
PIPER_DIR = self . piper_directory
subprocess . run ( [ ' install_name_tool ' , ' -change ' , ' @rpath/libespeak-ng.1.dylib ' ,
f " { PIPER_DIR } /piper-phonemize/lib/libespeak-ng.1.dylib " , f " { PIPER_DIR } /piper " ] )
subprocess . run ( [ ' install_name_tool ' , ' -change ' , ' @rpath/libonnxruntime.1.14.1.dylib ' ,
f " { PIPER_DIR } /piper-phonemize/lib/libonnxruntime.1.14.1.dylib " , f " { PIPER_DIR } /piper " ] )
subprocess . run ( [ ' install_name_tool ' , ' -change ' , ' @rpath/libpiper_phonemize.1.dylib ' ,
f " { PIPER_DIR } /piper-phonemize/lib/libpiper_phonemize.1.dylib " , f " { PIPER_DIR } /piper " ] )
subprocess . run (
[
" install_name_tool " ,
" -change " ,
" @rpath/libespeak-ng.1.dylib " ,
f " { PIPER_DIR } /piper-phonemize/lib/libespeak-ng.1.dylib " ,
f " { PIPER_DIR } /piper " ,
]
)
subprocess . run (
[
" install_name_tool " ,
" -change " ,
" @rpath/libonnxruntime.1.14.1.dylib " ,
f " { PIPER_DIR } /piper-phonemize/lib/libonnxruntime.1.14.1.dylib " ,
f " { PIPER_DIR } /piper " ,
]
)
subprocess . run (
[
" install_name_tool " ,
" -change " ,
" @rpath/libpiper_phonemize.1.dylib " ,
f " { PIPER_DIR } /piper-phonemize/lib/libpiper_phonemize.1.dylib " ,
f " { PIPER_DIR } /piper " ,
]
)
print ( " Piper setup completed. " )
else :
print ( " Piper already set up. Skipping download. " )
print ( " Piper already set up. Skipping download. " )