festival command

Linux で Mac OS の say コマンドのようなことをやりたい。
筆者は Mac を持っていないのだが検索すると面白そう。

厳密には GTK+ アプリ等で選択した文字列なんかを喋るようにしたい。

この英語スペルはどう発音するか等の簡易チェックに使いたい。
頻繁に使うわけではないので組み込みではなく単独アプリのほうがいい。
chrome 等から DnD で文字列を受け取ると読み上げるみたいな感じで。

GNOME3 には orca というアプリがある。
フォーカスのあるウインドウ文字列を読み上げる。
だけなのか。
つまり全部読み上げるので超鬱陶しい、求めているのはコレじゃない!
PyGI がエラーを吐いているし、日本語部はチャイニーズうんたらって何よ。

orca

というか、検索すると皆日本語を読ませようと四苦八苦しているみたい。
業務用途なら当然そうなるけど個人利用なら必要性をまったく感じないんだが。
英語の発音チェックに使いたいと考える筆者みたいな人はあまりいないのね。

ということで。

探してみると festival というコマンドがあるようだ。
Festival

私の Fedora 18 には最初から入っていた。

# stdout
echo "Read and Write" | festival --tts
# Read file
festival --tts read.txt

こんな感じで使える、これは使えそうだ。
英語で一定速しか読み上げできないけど私的には充分だ。

文字列のドロップで読み上げる PyGI ウインドウを作ってみる。
ついでにファイルドロップでも中身を読み上げできるように。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk, Gdk, GLib
import subprocess

class SayWin(Gtk.Window):
    def __init__(self):
        """
            Speech at the Drop string
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        self.set_title("Say")
        # DnD
        dnd_list = [Gtk.TargetEntry.new("text/plane", 0, 0),
                    Gtk.TargetEntry.new("text/uri-list", 0, 1) ]
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                dnd_list,
                Gdk.DragAction.COPY )
        self.drag_dest_add_uri_targets()
        self.drag_dest_add_text_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        # GtkLabel
        self.label = Gtk.Label("Please drop your Selection Text or File")
        self.add(self.label)
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        if info == 0:
            # text/plane
            p1 = subprocess.Popen(['echo', data.get_data()], stdout=subprocess.PIPE)
            p2 = subprocess.Popen(['festival', '--tts'], stdin=p1.stdout)
            p1.stdout.close()
            p2.communicate()[0]
        elif info == 1:
            # text/uri-list
            uris = data.get_uris()
            filename = GLib.filename_from_uri(uris[0], None)
            subprocess.call(['festival', '--tts', filename])

SayWin()
Gtk.main()

say_app

GLib.spawn_command_line_async でパイプを使う方法が解らなかった。
subprocess モジュールでマニュアルどおりにしたけど何か冗長な感じ。
もう少しスマートにできそうだけど。

イマイチな感じもするけど目的どおりにはなった。
orca のほうが発音が解りやすいのでソッチが利用できればなと思うけど。