SPAM: reg@pg-staff.com AppleScript:自動消滅するダイアログ
9月 26

はじめに
 Sayコマンドより、日本語で話す方法は無いかと探してました。
 嬉しいことに、先人の方がsaykanjiで実装してましたので、ちょっと改造することにしました。
-2009.09.26 仮版
-2009.10.03 修正:マルチバイト文字列の不具合修正 (gsedに変更)
-2009.10.10 追記:アクセント付き文字列


目的

  • 英単語混じりな文章をsaykanaで発音可能にする。

改造概要

  • ベースはsaykanjiのスクリプトを改造する。

    • NADのカタカナ英語辞書 10.6+を使用する。
       役割、英単語をカタカナ読みに変換する辞書です。(素晴らしい)
       10.6+のバージョンアップ版を使用する。
       ~/Downloadsに展開してることを前提にしてます。
       このファイルは改行コードがmac仕様になってるので、変換が必要です。
       grep等で検索できないので、スクリプト内で変換します。
       (UNIX改行でUTF-8の出力を指定する)
  • kshの関数としてロード可能にする。
  • スクリプト指定でも実行可能にする。

動作環境

  • OS

    OS CPU 備考
    Mac OSX 10.6 以上 Intel Mac MacBook
    Mac OSX 10.5 以上 PowerPC PowerBook G4
  • ksh (pdksh,ksh93)が使用可能な環境
    OS 設定方法 説明
    Mac OSX cd /usr; ln -s /bin/ksh /usr/bin/ksh として使う
  • Macport で使用したソフト
    ソフト名 バージョン 用途
    nkf 2.0.9-20090426_0 辞書データ(.csv)のUTF/UNIX改行変換に使用
    mecab 0.96_0 UTF8使用可能にする
    mecab-ipadic-utf8 2.7.0-20070610_0 辞書データ
    gsed 4.2.1_0 マルチバイト文字列の不具合対策
    • mecab : /opt/local/lib/mecab/dic/ipadic-utf8/dicrcの修正
       「私は」の変換が「ワタクシハ」になるので「ワタクシワ」に変更する。

      定義内容 備考
      ;node-format-yomi = %pS%f[7] コメント
      node-format-yomi = %pS%f[8] こちらを有効にする。

機能

実装機能

  • 起動オプション:saykana の起動オプションが使用可能なこと。
  • 辞書登録:辞書に登録されていない単語を容易に登録できる様にすること。
  • その他

未実装機能

  • 記号の発音ができない。

起動オプション

 saykanji.sh スクリプトの初期値変更で動作変更が可能です。

オプション名 説明 備考
-S <n> 字幕表示 1:原文表示,2:1+音声文表示
-C 辞書に未登録な単語のデータを作成 ${SK_UNDICT}にテンプレートを作成する。デフォルト ON
-M -C で作成したデータを辞書に登録する 読みの「XX」は未登録として扱わない
-d 数値文字を桁表現で発音する。指定時on。 デフォルト off
-f <filel> 指定した<file>を行単位で読込み音声に変換する
-v <voice> SayKanaのオプション f1:女性,m1:男性 デフォルト m1
-s <speed> SayKanaのオプション [50-300] デフォルト100

使用例

  1. 未登録な単語を指定した場合

    $ saykana.sh 私の名前はhogehogeです。
    "XX","hogehoge","無品詞"
    ~/Develop/cvsroot/tool/saykanji/unknown.csv:[未登録データ件数: 1]
    1. hogehogeが未登録なので、~/Develop/cvsroot/tool/saykanji/unknown.csvをエディタで開き編集します。

      変更前 "XX","hogehoge","無品詞"
      変更後 "ほげほげ","hogehoge","無品詞"
    2. 再度実行する。
       $ saykana.sh 私の名前はhogehogeです。
  2. 字幕表示
    1. 原文表示

      $ saykana.sh -S 1 私の名前はhogehogeです。
      原文:私の名前はhogehogeです。
    2. 原文+音声文表示
      $ saykana.sh -S 2 私の名前はhogehogeです。
      原文:私の名前はhogehogeです。
      音声: ワタクシ ノ ナマエ ワ ほげほげ デス 。
  3. 数値の桁表現をする。(なんか変) (字幕表示)
     $ ./saykanji.sh -S 2 -d 缶ジュースは120円です
    原文:缶ジュースは120円です。
    音声: カン ジュース ワ <numk VAL=120> エン デス 。
  4. ファイルを読ませる
    $ ./saykanji.sh -f readme.txt
  5. Mac OSX: 青空文庫読み上げスクリプトで使ってみる。

アクセント付き文字列

 以下のサイトを拝見すると、簡単には作れなさそうです。
 要はSaykanaに渡すアクセント付きの文字列を取得手段が欲しいだけなんですが、
 ぼちぼちと作るには、荷が重そう。

備考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#!/usr/bin/ksh

# $Id: saykanji.sh,v 1.8 2009/10/03 11:15:04 shimizu Exp $
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/local/bin:/usr/local/bin:~/bin
SAYKANA=/usr/local/bin/saykana
# 元辞書
# 入手先: http://nadroom.blog.shinobi.jp/Entry/91/
DICT=~/Downloads/カタカナ英語辞書10.6+/NADのカタカナ英語辞書10.6.txt

# 辞書
# お使いの環境に応じて修正して下さい。
SK_DICT=~/Develop/cvsroot/tool/saykanji/kana.csv      # 辞書
SK_UNDICT=~/Develop/cvsroot/tool/saykanji/unknown.csv # 辞書未登録単語集


_sk_opt_setup=0     # 初回のみ、動くなら0にして下さい。

##############################################################################
_sk_opt=
_sk_opt_LOD=0       # -L   Load のみ
_sk_opt_VER=0       # -V   処理内容を表示
_sk_opt_SUB=0       # -S <n>    (字幕表示: 1:原文表示, 2:1+音声文字表示)
_sk_opt_CHK=1       # -C        (辞書未登録な単語のデータ作成: 0:on, 1:off)
            #     1) ${SK_UNDICT} の「読み」を編集して下さい。
            #        (デフォルトでは読み部分が、XX ($_sk_unknown)になってます。)
            #     2) -M 指定又はONの時、次回起動時に${SK_DICT}に登録します。
_sk_opt_MRG=1       # -M        (辞書未登録な単語を${SK_DICT}に登録する)
_sk_opt_digit=0     # -d        (数値桁表現音声: 0:on, 1:off)
_sk_opt_file=0      # -f <file> (指定ファイルの音声出力)
_sk_opt_voice="-v f1"   # -v        (SayKanaのオプション: f1=女性,m1=男性)
_sk_opt_speed="-s 100"  # -s <n>    (SayKanaのオプション: speed [50-300])
_sk_unknown_word="チョメチョメ" # 辞書未登録な単語の読み方 [発音します]
                                #   : -C 指定時に使用
_sk_unknown="\"XX\""            # 辞書未登録な単語のデータ作成
                                #   : ${SK_UNDICT}作成時に使用

# 使用するコマンドのチェック (簡易版)
function _sk_cmd_check # <command>
{
    typeset _leng=$(which $1);
   
    if [[ ${#_leng} -eq 0 ]]; then
    echo "Not Found : $1 : インストール又はパスを通して下さい"
    exit 2
    fi
}

# 辞書&コマンドの存在確認
# ${SK_DICT} 辞書が無かったら作成する
function _sk_setup #
{
    if [[ -r ${DICT} ]]; then
    if [[ ! -f ${SK_DICT} ]]; then
        _sk_cmd_check mecab
        _sk_cmd_check grep
        _sk_cmd_check mv
        _sk_cmd_check sort
        _sk_cmd_check nkf
        _sk_cmd_check gsed
        nkf -Lu -w ${DICT} > ${SK_DICT}
        touch ${SK_UNDICT}
    fi
    else
    echo "Not Found : ${DICT}"
    exit 2
    fi
}

# 英字判定
function _sk_isalpha #
{
    typeset _ret;
    _ret=$(echo $1 | tr -d '[[:alpha:]]')
    echo ${#_ret}
}

# 数字判定
function _sk_isdigit #
{
    typeset _ret;
    _ret=$(echo $1 | tr -d '[[:digit:]]')
    echo ${#_ret}
}

function _sk_lex_string  # <string>...
{
    typeset _result;
    typeset _word;
    typeset _yomi;

    (echo "$*" | mecab -O wakati | mecab -O yomi | gsed 's/ /\n/g' ) |
    while read  _word ; do
    if [[ $(_sk_isalpha ${_word}) -eq 0 ]]; then
        _yomi=$(grep -i "\"${_word}\"" $SK_DICT |
        head -1 | cut -d',' -f1 | gsed 's/"//g')
        if [[ -n ${_yomi} ]]; then
        _word=${_yomi};
        elif [[ ${_sk_opt_CHK} -eq 1 ]]; then
        echo "${_sk_unknown},\"${_word}\",\"無品詞\"" >> $SK_UNDICT
        [[ ${_sk_opt_VER} -eq 1 ]] &&
        echo "${_sk_unknown},\"${_word}\",\"無品詞\""
        _word=${_sk_unknown_word} # 知らない単語はxxになる。
        fi
    elif [[ $(_sk_isdigit ${_word}) -eq 0 && ${_sk_opt_digit} -eq 1 ]]; then

        _word="<numk VAL=${_word}>"
    fi
    _result="${_result} ${_word}"
    done
    (( ${_sk_opt_SUB} >= 1 )) && echo "原文:$*"
    (( ${_sk_opt_SUB} >= 2 )) && echo "音声:${_result}"
    (saykana ${_sk_opt} "${_result}")  2>&1 > /dev/null
}

function _sk_make_uniq_file # <file>
{
    typeset _file=$1;
    if [[ -f ${_file} ]]; then
    sort -u ${_file} > ${_file}.$$
    mv ${_file}.$$ ${_file}
    fi
}

function _sk_lex_file # </file><file>
{
    typeset _file=$1;
    typeset _buf;

    # 入力ファイルを出力し字句解析する。
    # (nkf -Lu -w ${_file)) として、UTFに変換した方がよいかも?
    # (cat ${_file}) |
    (nkf -Lu -w ${_file}) |
    while read _buf; do
    _sk_lex_string ${_buf}
    done
}

function _sk_word_regist # ${SK_DICT} ${SK_UNDICT}
{
    typeset _dict=$1;
    typeset _undict=$2;

    (grep -v "${_sk_unknown}" ${_undict}) |
    while read _buf; do
    echo "${_buf}" >> ${_dict}
    done

    (grep "${_sk_unknown}" ${_undict}) > ${_undict}.$$
    mv ${_undict}.$$ ${_undict}
}

function saykanji # <option> <string>...
{
    typeset _opt;
    typeset _word;
    typeset _tmp;

    ####################################
    #・前処理
    #  1) 起動オプションの確認
    while getopts ":LVS:CMdhf:v:s:" _opt; do
    case $_opt in
        L)  _sk_opt_LOD=1 ;;
        V)  _sk_opt_VER=1 ;;
        S)  _sk_opt_SUB="$OPTARG" ;;
        C)  _sk_opt_CHK=1 ;;
        M)  _sk_opt_MRG=1 ;;
        f)  _sk_opt_file="$OPTARG" ;;
        d)  _sk_opt_digit=1 ;;
        v)  _sk_opt_voice="-v $OPTARG"  ;;
        s)  _sk_opt_speed="-s $OPTARG"  ;;
        h|?) printf "Usage: %s: [-v {f1|m1}] [-s 50-300] [-f file]\n" $0
        exit 2;;
    esac
     done
     shift $(($OPTIND -1))
    _sk_opt="${_sk_opt_voice} ${_sk_opt_speed}"

    if [[ ${_sk_opt_LOD} -eq 1 ]]; then
    _sk_opt_LOD=0
    return
    fi

    #  2) 単語の辞書登録
    if (( ${_sk_opt_MRG} == 1 )); then
    _sk_word_regist ${SK_DICT} ${SK_UNDICT}
    fi

    ####################################
    #・処理
    # 1) 字句解析+音声
    if [[ -f ${_sk_opt_file} ]]; then
    _sk_lex_file ${_sk_opt_file}
    else
        _sk_lex_string $*
    fi

    ####################################
    #・後処理
    if [[ ${_sk_opt_VER} -eq 1 ]]; then
    # 同一単語の複数登録の抑止
    _sk_make_uniq_file ${SK_UNDICT}
    (wc -l ${SK_UNDICT} | gsed 's/  //g' ) |
    while read _word _tmp; do
        if [[ ${_word} -gt 0 ]]; then
        echo  "${SK_UNDICT}:[未登録データ件数: ${_word}]"
        fi
    done
    fi
}

if (( ${_sk_opt_setup} == 1 )); then
    _sk_setup
fi

if (( $# > 0 )); then
    saykanji $*
fi



コメントをどうぞ

preload preload preload