Skip to content

Commit 9b7a9dd

Browse files
fourjrnateshmbhat
authored andcommitted
Implement Linux Support in save_to_file (#76)
* idk why but this is not working * FIXED * make an indicator that it is modified * fix * Implement a working save_to_file * Cleanup code * Typo * Fix bug: runandwait runs indefinitely after say * remove unnecessary * Linux support for save_to_file * Fix * Fix issue where ALSA logs were printed * clean up requirements
1 parent 021a97b commit 9b7a9dd

File tree

6 files changed

+46
-9
lines changed

6 files changed

+46
-9
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,6 @@ ENV/
101101
.mypy_cache/
102102
docs/make.bat
103103
docs/make.bat
104+
105+
# vscode
106+
.vscode/

docs/install.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,5 @@ code-block:: bash
8989

9090
.. _espeak: http://espeak.sourceforge.net/
9191
.. _virtualenv: https://pypi.python.org/pypi/virtualenv/1.10.1
92-
.. _pip: https://pypi.python.org/pypi/pip
92+
.. _pip: https://pypi.python.org/pypi/pip
93+
.. _ffmpeg: https://www.ffmpeg.org/

pyttsx3/drivers/_espeak.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ def SetUriCallback(cb):
145145
POS_WORD = 2
146146
POS_SENTENCE = 3
147147

148-
def Synth(text, position=0, position_type=POS_CHARACTER, end_position=0, flags=0):
149-
return cSynth(text, len(text)*10, position, position_type, end_position, flags, None, None)
148+
def Synth(text, position=0, position_type=POS_CHARACTER, end_position=0, flags=0, user_data=None):
149+
return cSynth(text, len(text)*10, position, position_type, end_position, flags, None, user_data)
150150

151151
cSynth = cfunc('espeak_Synth', dll, c_int,
152152
('text', c_char_p, 1),

pyttsx3/drivers/espeak.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11

22
import time
33
import ctypes
4+
import io
5+
import wave
6+
import os
7+
from tempfile import NamedTemporaryFile
48
from ..voice import Voice
59
from . import _espeak, toUtf8, fromUtf8
610

@@ -18,7 +22,7 @@ def __init__(self, proxy):
1822
# espeak cannot initialize more than once per process and has
1923
# issues when terminating from python (assert error on close)
2024
# so just keep it alive and init once
21-
rate = _espeak.Initialize(_espeak.AUDIO_OUTPUT_PLAYBACK, 1000)
25+
rate = _espeak.Initialize(_espeak.AUDIO_OUTPUT_RETRIEVAL, 1000)
2226
if rate == -1:
2327
raise RuntimeError('could not initialize espeak')
2428
EspeakDriver._defaultVoice = 'default'
@@ -31,6 +35,15 @@ def __init__(self, proxy):
3135
self._proxy = proxy
3236
self._looping = True
3337
self._stopping = False
38+
self._data_buffer = b''
39+
self._numerise_buffer = []
40+
41+
def numerise(self, data):
42+
self._numerise_buffer.append(data)
43+
return ctypes.c_void_p(len(self._numerise_buffer))
44+
45+
def decode_numeric(self, data):
46+
return self._numerise_buffer[int(data) - 1]
3447

3548
def destroy(self):
3649
_espeak.SetSynthCallback(None)
@@ -111,7 +124,9 @@ def startLoop(self):
111124
time.sleep(0.01)
112125

113126
def save_to_file(self, text, filename):
114-
raise NotImplementedError
127+
code = self.numerise(filename)
128+
_espeak.Synth(toUtf8(text), flags=_espeak.ENDPAUSE |
129+
_espeak.CHARS_UTF8, user_data=code)
115130

116131
def endLoop(self):
117132
self._looping = False
@@ -139,7 +154,25 @@ def _onSynth(self, wav, numsamples, events):
139154
location=event.text_position - 1,
140155
length=event.length)
141156
elif event.type == _espeak.EVENT_MSG_TERMINATED:
157+
stream = NamedTemporaryFile()
158+
159+
with wave.open(stream, 'wb') as f:
160+
f.setnchannels(1)
161+
f.setsampwidth(2)
162+
f.setframerate(22050.0)
163+
f.writeframes(self._data_buffer)
164+
165+
if event.user_data:
166+
os.system('ffmpeg -y -i {} {} -loglevel quiet'.format(stream.name, self.decode_numeric(event.user_data)))
167+
else:
168+
os.system('aplay {} -q'.format(stream.name)) # -q for quiet
169+
170+
self._data_buffer = b''
142171
self._proxy.notify('finished-utterance', completed=True)
143172
self._proxy.setBusy(False)
144173
i += 1
174+
175+
if numsamples > 0:
176+
self._data_buffer += ctypes.string_at(wav, numsamples *
177+
ctypes.sizeof(ctypes.c_short))
145178
return 0

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# pyttsx3 only requires `espeak` driver/library which is system-dependent
33

44
### Ubuntu
5-
#$ sudo apt install espeak
5+
#$ sudo apt install espeak ffmpeg
66

77
### Mac OS X (os.platform == 'Darwin')
88
# pyobjc>=2.4

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
],
99
':"win32" in sys_platform': [
1010
'comtypes'
11-
],
11+
]
1212
}
1313

14-
# Ubuntu: sudo apt install espeak
14+
# Ubuntu: sudo apt install espeak ffmpeg
1515
install_requires = []
1616
if platform.system() == 'Windows':
1717
install_requires += [
18-
'pypiwin32'
18+
'comtypes'
1919
]
2020
elif platform.system() == 'Darwin':
2121
install_requires += [

0 commit comments

Comments
 (0)