From 9c349009cb02451ef6f1dbd00eebf57f8725df59 Mon Sep 17 00:00:00 2001 From: Aneesh Dogra Date: Wed, 12 Dec 2012 11:58:44 +0000 Subject: Add gtk3 version of Adding_TTS. --- diff --git a/Adding_TTS_gtk3/ReadEtextsTTS.py b/Adding_TTS_gtk3/ReadEtextsTTS.py new file mode 100755 index 0000000..161f8c1 --- /dev/null +++ b/Adding_TTS_gtk3/ReadEtextsTTS.py @@ -0,0 +1,272 @@ +#! /usr/bin/env python +# +# ReadEtextsTTS.py A standalone ebook reader program that +# demonstrates text to speech with word highlighting. +# Copyright (C) 2010 James D. Simmons +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +import sys +import os +import zipfile +from gi.repository import Gtk +from gi.repository import Gdk +import getopt +from gi.repository import Pango +from gi.repository import GObject +import time +import speech + +speech_supported = True + +try: + import gst + gst.element_factory_make('espeak') + print 'speech supported!' +except Exception, e: + speech_supported = False + print 'speech not supported!' + +page=0 +PAGE_SIZE = 45 + +class ReadEtextsActivity(): + def __init__(self): + "The entry point to the Activity" + speech.highlight_cb = self.highlight_next_word + # print speech.voices() + + def highlight_next_word(self, word_count): + if word_count < len(self.word_tuples): + word_tuple = self.word_tuples[word_count] + textbuffer = self.textview.get_buffer() + tag = textbuffer.create_tag() + tag.set_property('weight', Pango.Weight.BOLD) + tag.set_property('foreground', "white") + tag.set_property('background', "black") + iterStart = textbuffer.get_iter_at_offset(word_tuple[0]) + iterEnd = textbuffer.get_iter_at_offset(word_tuple[1]) + bounds = textbuffer.get_bounds() + textbuffer.remove_all_tags(bounds[0], bounds[1]) + textbuffer.apply_tag(tag, iterStart, iterEnd) + v_adjustment = self.scrolled_window.get_vadjustment() + max = v_adjustment.get_upper() - v_adjustment.get_page_size() + max = max * word_count + max = max / len(self.word_tuples) + v_adjustment.set_value(max) + return True + + def keypress_cb(self, widget, event): + "Respond when the user presses one of the arrow keys" + global done + global speech_supported + keyname = Gdk.keyval_name(event.keyval) + if keyname == 'KP_End' and speech_supported: + if speech.is_paused() or speech.is_stopped(): + speech.play(self.words_on_page) + else: + speech.pause() + return True + if keyname == 'plus': + self.font_increase() + return True + if keyname == 'minus': + self.font_decrease() + return True + if speech_supported and speech.is_stopped() == False and speech.is_paused == False: + # If speech is in progress, ignore other keys. + return True + if keyname == '7': + speech.pitch_down() + speech.say('Pitch Adjusted') + return True + if keyname == '8': + speech.pitch_up() + speech.say('Pitch Adjusted') + return True + if keyname == '9': + speech.rate_down() + speech.say('Rate Adjusted') + return True + if keyname == '0': + speech.rate_up() + speech.say('Rate Adjusted') + return True + if keyname == 'KP_Right': + self.page_next() + return True + if keyname == 'Page_Up' or keyname == 'KP_Up': + self.page_previous() + return True + if keyname == 'KP_Left': + self.page_previous() + return True + if keyname == 'Page_Down' or keyname == 'KP_Down': + self.page_next() + return True + if keyname == 'Up': + self.scroll_up() + return True + if keyname == 'Down': + self.scroll_down() + return True + return False + + def page_previous(self): + global page + page=page-1 + if page < 0: page=0 + self.show_page(page) + v_adjustment = self.scrolled_window.get_vadjustment() + v_adjustment.set_value(v_adjustment.get_upper() - \ + v_adjustment.get_page_size()) + + def page_next(self): + global page + page=page+1 + if page >= len(self.page_index): page=0 + self.show_page(page) + v_adjustment = self.scrolled_window.get_vadjustment() + v_adjustment.set_value(v_adjustment.get_lower()) + + def font_decrease(self): + font_size = self.font_desc.get_size() / 1024 + font_size = font_size - 1 + if font_size < 1: + font_size = 1 + self.font_desc.set_size(font_size * 1024) + self.textview.modify_font(self.font_desc) + + def font_increase(self): + font_size = self.font_desc.get_size() / 1024 + font_size = font_size + 1 + self.font_desc.set_size(font_size * 1024) + self.textview.modify_font(self.font_desc) + + def scroll_down(self): + v_adjustment = self.scrolled_window.get_vadjustment() + if v_adjustment.get_value() == v_adjustment.get_upper() - v_adjustment.get_page_size(): + self.page_next() + return + if v_adjustment.get_value() < v_adjustment.get_upper() - v_adjustment.get_page_size(): + new_value = v_adjustment.get_value() + v_adjustment.step_increment + if new_value > v_adjustment.get_upper() - v_adjustment.get_page_size(): + new_value = v_adjustment.get_upper() - v_adjustment.get_page_size() + v_adjustment.set_value(new_value) + + def scroll_up(self): + v_adjustment = self.scrolled_window.get_vadjustment() + if v_adjustment.get_value() == v_adjustment.get_lower(): + self.page_previous() + return + if v_adjustment.get_value() > v_adjustment.get_lower(): + new_value = v_adjustment.get_value() - v_adjustment.step_increment + if new_value < v_adjustment.get_lower(): + new_value = v_adjustment.get_lower() + v_adjustment.set_value(new_value) + + def show_page(self, page_number): + global PAGE_SIZE, current_word + position = self.page_index[page_number] + self.etext_file.seek(position) + linecount = 0 + label_text = '' + textbuffer = self.textview.get_buffer() + while linecount < PAGE_SIZE: + line = self.etext_file.readline() + label_text = label_text + unicode(line, 'iso-8859-1') + linecount = linecount + 1 + textbuffer.set_text(label_text) + self.textview.set_buffer(textbuffer) + self.word_tuples = speech.prepare_highlighting(label_text) + self.words_on_page = speech.add_word_marks(self.word_tuples) + + def save_extracted_file(self, zipfile, filename): + "Extract the file to a temp directory for viewing" + filebytes = zipfile.read(filename) + f = open("/tmp/" + filename, 'w') + try: + f.write(filebytes) + finally: + f.close() + + def read_file(self, filename): + "Read the Etext file" + global PAGE_SIZE + + if zipfile.is_zipfile(filename): + self.zf = zipfile.ZipFile(filename, 'r') + self.book_files = self.zf.namelist() + self.save_extracted_file(self.zf, self.book_files[0]) + currentFileName = "/tmp/" + self.book_files[0] + else: + currentFileName = filename + + self.etext_file = open(currentFileName,"r") + self.page_index = [ 0 ] + linecount = 0 + while self.etext_file: + line = self.etext_file.readline() + if not line: + break + linecount = linecount + 1 + if linecount >= PAGE_SIZE: + position = self.etext_file.tell() + self.page_index.append(position) + linecount = 0 + if filename.endswith(".zip"): + os.remove(currentFileName) + + def delete_cb(self, widget, event, data=None): + speech.stop() + return False + + def destroy_cb(self, widget, data=None): + speech.stop() + Gtk.main_quit() + + def main(self, file_path): + self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL) + self.window.connect("delete_event", self.delete_cb) + self.window.connect("destroy", self.destroy_cb) + self.window.set_title("Read Etexts Activity") + self.window.set_size_request(800, 600) + self.window.set_border_width(0) + self.read_file(file_path) + self.scrolled_window = Gtk.ScrolledWindow(hadjustment=None, vadjustment=None) + self.textview = Gtk.TextView() + self.textview.set_editable(False) + self.textview.set_left_margin(50) + self.textview.set_cursor_visible(False) + self.textview.connect("key_press_event", self.keypress_cb) + self.font_desc = Pango.FontDescription("sans 12") + self.textview.modify_font(self.font_desc) + self.show_page(0) + self.scrolled_window.add(self.textview) + self.window.add(self.scrolled_window) + self.textview.show() + self.scrolled_window.show() + self.window.show() + Gtk.main() + +if __name__ == "__main__": + try: + opts, args = getopt.getopt(sys.argv[1:], "") + ReadEtextsActivity().main(args[0]) + except getopt.error, msg: + print msg + print "This program has no options" + sys.exit(2) diff --git a/Adding_TTS_gtk3/espeak.py b/Adding_TTS_gtk3/espeak.py new file mode 100755 index 0000000..fbb1b22 --- /dev/null +++ b/Adding_TTS_gtk3/espeak.py @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# +# espeak.py +# Use espeak, part of Make Your Own Sugar Activities Book Examples. +# Copyright (C) 2010 James D. Simmons +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import re +import subprocess + +PITCH_MAX = 99 +RATE_MAX = 99 +PITCH_DEFAULT = PITCH_MAX/2 +RATE_DEFAULT = RATE_MAX/3 + +def speak(text, rate=RATE_DEFAULT, pitch=PITCH_DEFAULT, voice="default"): + + # espeak uses 80 to 370 + rate = 80 + (370-80) * int(rate) / 100 + + subprocess.call(["espeak", "-p", str(pitch), + "-s", str(rate), "-v", voice, text], + stdout=subprocess.PIPE) + +def voices(): + out = [] + result = subprocess.Popen(["espeak", "--voices"], stdout=subprocess.PIPE) \ + .communicate()[0] + + for line in result.split('\n'): + m = re.match(r'\s*\d+\s+([\w-]+)\s+([MF])\s+([\w_-]+)\s+(.+)', line) + if not m: + continue + language, gender, name, stuff = m.groups() + if stuff.startswith('mb/') or \ + name in ('en-rhotic','english_rp','english_wmids'): + # these voices don't produce sound + continue + out.append((language, name)) + + return out + +def main(): + print voices() + speak("I'm afraid I can't do that, Dave.") + speak("Your mother was a hamster, and your father smelled of elderberries!", 30, 60, "fr") + +if __name__ == "__main__": + main() diff --git a/Adding_TTS_gtk3/gst_choir_example.py b/Adding_TTS_gtk3/gst_choir_example.py new file mode 100755 index 0000000..58a2534 --- /dev/null +++ b/Adding_TTS_gtk3/gst_choir_example.py @@ -0,0 +1,51 @@ +# +# +# Copyright (C) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +from gi.repository import Gtk +import gst +import random +from gettext import gettext as _ + +def gstmessage_cb(bus, message, pipe): + if message.type in (gst.MESSAGE_EOS, gst.MESSAGE_ERROR): + pipe.set_state(gst.STATE_NULL) + +def make_pipe(): + pipeline = 'espeak name=src ! autoaudiosink' + pipe = gst.parse_launch(pipeline) + + src = pipe.get_by_name('src') + src.props.text = _('Hello, World!') + src.props.pitch = random.randint(-100, 100) + src.props.rate = random.randint(-100, 100) + + voices = src.props.voices + voice = voices[random.randint(0, len(voices)-1)] + src.props.voice = voice[0] + + bus = pipe.get_bus() + bus.add_signal_watch() + bus.connect('message', gstmessage_cb, pipe) + + pipe.set_state(gst.STATE_PLAYING) + +for i in range(10): + make_pipe() + +Gtk.main() diff --git a/Adding_TTS_gtk3/gst_simple_example.py b/Adding_TTS_gtk3/gst_simple_example.py new file mode 100755 index 0000000..3962777 --- /dev/null +++ b/Adding_TTS_gtk3/gst_simple_example.py @@ -0,0 +1,36 @@ +# +# +# Copyright (C) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +from gi.repository import Gtk +import gst + +def gstmessage_cb(bus, message, pipe): + if message.type in (gst.MESSAGE_EOS, gst.MESSAGE_ERROR): + pipe.set_state(gst.STATE_NULL) + +pipeline = 'espeak text="Hello, World!" ! autoaudiosink' +pipe = gst.parse_launch(pipeline) + +bus = pipe.get_bus() +bus.add_signal_watch() +bus.connect('message', gstmessage_cb, pipe) + +pipe.set_state(gst.STATE_PLAYING) + +Gtk.main() diff --git a/Adding_TTS_gtk3/gst_simple_tts.py b/Adding_TTS_gtk3/gst_simple_tts.py new file mode 100755 index 0000000..2148ffe --- /dev/null +++ b/Adding_TTS_gtk3/gst_simple_tts.py @@ -0,0 +1,93 @@ +# gst_simple_tts.py +# Copyright (C) 2010 Aleksey Lim +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gi.repository import Gtk +import gst +from gi.repository import Pango + +window = Gtk.Window() +window.connect('destroy', + lambda sender: Gtk.main_quit()) + +workspace = Gtk.VBox() +window.add(workspace) + +# text widget + +scrolled = Gtk.ScrolledWindow() +workspace.pack_start(scrolled, True, True, 0) + +text = Gtk.TextView() +text.set_left_margin(50) +text.set_right_margin(50) +text.set_wrap_mode(Gtk.WrapMode.WORD) +scrolled.add(text) + +buffer = text.props.buffer +buffer.props.text = file("testtts.txt").read() + +tag = buffer.create_tag() +tag.props.weight = Pango.Weight.BOLD + +# play controls + +toolbar = Gtk.HBox() +workspace.pack_end(toolbar, False, True, 0) + +play = Gtk.Button('Play/Resume') +play.connect('clicked', + lambda sender: pipe.set_state(gst.STATE_PLAYING)) +toolbar.add(play) + +pause = Gtk.Button('Pause') +pause.connect('clicked', + lambda sender: pipe.set_state(gst.STATE_PAUSED)) +toolbar.add(pause) + +stop = Gtk.Button('Stop') +stop.connect('clicked', + lambda sender: pipe.set_state(gst.STATE_NULL)) +toolbar.add(stop) + +# gst code + +pipe = gst.parse_launch('espeak name=src ! autoaudiosink') + +src = pipe.get_by_name('src') +src.props.text = buffer.props.text +src.props.track = 1 # track for words + +def tts_cb(bus, message): + if message.structure.get_name() != 'espeak-word': + return + + offset = message.structure['offset'] + len = message.structure['len'] + + buffer.remove_tag(tag, buffer.get_start_iter(), buffer.get_end_iter()) + start = buffer.get_iter_at_offset(offset) + end = buffer.get_iter_at_offset(offset + len) + buffer.apply_tag(tag, start, end) + +bus = pipe.get_bus() +bus.add_signal_watch() +bus.connect('message::element', tts_cb) + +# gtk start + +window.show_all() +Gtk.main() diff --git a/Adding_TTS_gtk3/gst_track_marks.py b/Adding_TTS_gtk3/gst_track_marks.py new file mode 100755 index 0000000..a1e9f8e --- /dev/null +++ b/Adding_TTS_gtk3/gst_track_marks.py @@ -0,0 +1,54 @@ +# +# gst_track_marks.py +# Sample code for using the gstreamer espeak plugin. + +# Copyright (C) 2010 Aleksey Lim +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +from gi.repository import Gtk +import gst + +text = 'Hello, World!' + +def gstmessage_cb(bus, message, pipe): + if message.type in (gst.MESSAGE_EOS, gst.MESSAGE_ERROR): + pipe.set_state(gst.STATE_NULL) + elif message.type == gst.MESSAGE_ELEMENT and \ + message.structure.get_name() == 'espeak-mark': + offset = message.structure['offset'] + mark = message.structure['mark'] + print '%d:%s' % (offset, mark) + +pipe = gst.Pipeline('pipeline') + +src = gst.element_factory_make('espeak', 'src') +src.props.text = text +src.props.track = 2 +src.props.gap = 100 +pipe.add(src) + +sink = gst.element_factory_make('autoaudiosink', 'sink') +pipe.add(sink) +src.link(sink) + +bus = pipe.get_bus() +bus.add_signal_watch() +bus.connect('message', gstmessage_cb, pipe) + +pipe.set_state(gst.STATE_PLAYING) + +Gtk.main() diff --git a/Adding_TTS_gtk3/gst_track_words.py b/Adding_TTS_gtk3/gst_track_words.py new file mode 100755 index 0000000..86b99d2 --- /dev/null +++ b/Adding_TTS_gtk3/gst_track_words.py @@ -0,0 +1,50 @@ +# gst_track_words.py +# Sample code for using the gstreamer espeak plugin. +# Copyright (C) 2010 Aleksey Lim +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +from gi.repository import Gtk +import gst + +text = file(__file__, 'r').read() + +def gstmessage_cb(bus, message, pipe): + if message.type in (gst.MESSAGE_EOS, gst.MESSAGE_ERROR): + pipe.set_state(gst.STATE_NULL) + elif message.type == gst.MESSAGE_ELEMENT and \ + message.structure.get_name() == 'espeak-word': + offset = message.structure['offset'] + len = message.structure['len'] + print text[offset:offset+len] + +pipe = gst.Pipeline('pipeline') + +src = gst.element_factory_make('espeak', 'src') +src.props.text = text +src.props.track = 1 +pipe.add(src) + +sink = gst.element_factory_make('autoaudiosink', 'sink') +pipe.add(sink) +src.link(sink) + +bus = pipe.get_bus() +bus.add_signal_watch() +bus.connect('message', gstmessage_cb, pipe) + +pipe.set_state(gst.STATE_PLAYING) + +Gtk.main() diff --git a/Adding_TTS_gtk3/speech.py b/Adding_TTS_gtk3/speech.py new file mode 100644 index 0000000..cdab7d2 --- /dev/null +++ b/Adding_TTS_gtk3/speech.py @@ -0,0 +1,136 @@ +# speech.py + +# Copyright (C) 2010 Aleksey Lim and James D. Simmons +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gst + +voice = 'default' +pitch = 0 + +rate = -20 +highlight_cb = None + +def _create_pipe(): + pipeline = 'espeak name=source ! autoaudiosink' + pipe = gst.parse_launch(pipeline) + + def stop_cb(bus, message): + pipe.set_state(gst.STATE_NULL) + + def mark_cb(bus, message): + if message.structure.get_name() == 'espeak-mark': + mark = message.structure['mark'] + highlight_cb(int(mark)) + + bus = pipe.get_bus() + bus.add_signal_watch() + bus.connect('message::eos', stop_cb) + bus.connect('message::error', stop_cb) + bus.connect('message::element', mark_cb) + + return (pipe.get_by_name('source'), pipe) + +def _speech(source, pipe, words): + source.props.pitch = pitch + source.props.rate = rate + source.props.voice = voice + source.props.text = words; + pipe.set_state(gst.STATE_PLAYING) + +info_source, info_pipe = _create_pipe() +play_source, play_pipe = _create_pipe() + +# track for marks +play_source.props.track = 2 + +def voices(): + return info_source.props.voices + +def say(words): + _speech(info_source, info_pipe, words) + print words + +def play(words): + _speech(play_source, play_pipe, words) + +def is_stopped(): + for i in play_pipe.get_state(): + if isinstance(i, gst.State) and i == gst.STATE_NULL: + return True + return False + +def stop(): + play_pipe.set_state(gst.STATE_NULL) + +def is_paused(): + for i in play_pipe.get_state(): + if isinstance(i, gst.State) and i == gst.STATE_PAUSED: + return True + return False + +def pause(): + play_pipe.set_state(gst.STATE_PAUSED) + +def rate_up(): + global rate + rate = min(99, rate + 10) + +def rate_down(): + global rate + rate = max(-99, rate - 10) + +def pitch_up(): + global pitch + pitch = min(99, pitch + 10) + +def pitch_down(): + global pitch + pitch = max(-99, pitch - 10) + +def prepare_highlighting(label_text): + i = 0 + j = 0 + word_begin = 0 + word_end = 0 + current_word = 0 + word_tuples = [] + omitted = [' ', '\n', u'\r', '_', '[', '{', ']', '}', '|', '<',\ + '>', '*', '+', '/', '\\' ] + omitted_chars = set(omitted) + while i < len(label_text): + if label_text[i] not in omitted_chars: + word_begin = i + j = i + while j < len(label_text) and label_text[j] not in omitted_chars: + j = j + 1 + word_end = j + i = j + word_t = (word_begin, word_end, label_text[word_begin: word_end].strip()) + if word_t[2] != u'\r': + word_tuples.append(word_t) + i = i + 1 + return word_tuples + +def add_word_marks(word_tuples): + "Adds a mark between each word of text." + i = 0 + marked_up_text = ' ' + while i < len(word_tuples): + word_t = word_tuples[i] + marked_up_text = marked_up_text + '' + word_t[2] + i = i + 1 + return marked_up_text + '' diff --git a/Adding_TTS_gtk3/testtts.txt b/Adding_TTS_gtk3/testtts.txt new file mode 100644 index 0000000..4057792 --- /dev/null +++ b/Adding_TTS_gtk3/testtts.txt @@ -0,0 +1,21 @@ +Jane Austen? Why I go so far as to say that any library is a good library that does not contain a volume by Jane +Austen. Even if it contains no other book. + +- quoted in Remembered Yesterdays, Robert Underwood Johnson + +To me his prose is unreadable -- like Jane Austin's [sic]. No there is a difference. I could read his prose on salary, but +not Jane's. Jane is entirely impossible. It seems a great pity that they allowed her to die a natural death. + +- Letter to W. D. Howells, 18 January 1909 + +Jane Austen's books, too, are absent from this library. Just that one omission alone would make a +fairly good library out of a library that hadn't a book in it. + +- Following the Equator + +I haven't any right to criticise books, and I don't do it except when I hate them. I often want to criticise +Jane Austen, but her books madden me so that I can't conceal my frenzy from the reader; and therefore I have +to stop every time I begin. Everytime I read 'Pride and Prejudice' I want to dig her up and beat her over +the skull with her own shin-bone. + +- Letter to Joseph Twichell, 13 September 1898 -- cgit v0.9.1