Gaucheでも日本語を喋らせる(3)
最近のテレビは、朝30分程見ると、夜見て欲しい番組の主演者が出てきて、 どうでもいいような事を、べらべら喋ってる。
朝だけかと思うと、昼も出てきて、まだ喋ってる。ひどいのになると、夕方の けだるい時間帯にも(TV局もけだるいようで、ひたすら再放送してる)、その人が 昔出てた番組をひたすら、流している。
TVは終わったな。キー局全部だぞ。その点、12CHは、旅行とグルメに徹していて 偉いぞ。いつ、見始めて、見終わっても、後悔なんて、ちっとも無いから。
Gaucheで喋らせる、一応完成
;; say japanse (using extarnal application mecab and gsay)
;; -*- coding: utf-8 -*-
;; Usage: gosh say.scm sjis-text-file
(use gauche.process)
(use gauche.charconv)
(define MECAB ;; wakati and yomi mode
'("mecab" "--eos-format=\n"
"--unk-format=\\s%m"
"--node-format=\\s%pS%f[7]" ))
(define ALPHA '((#\a . "えー") (#\b . "びー") (#\c . "しー")
(#\d . "でー") (#\e . "いー") (#\f . "えふ")
(#\g . "じー") (#\h . "えっち") (#\i . "あい")
(#\j . "じぇい") (#\k . "けー") (#\l . "える")
(#\m . "えむ") (#\n . "えぬ") (#\o . "おー")
(#\p . "ぴー") (#\q . "きゅー") (#\r . "あーる")
(#\s . "えす") (#\t . "てー") (#\u . "ゆー")
(#\v . "ぶい") (#\w . "だぶる") (#\x . "えくす")
(#\y . "わい") (#\z . "ぜっと") ))
(define PUNCT '((#\. . "どっと") (#\/ . "すら") (#\? . "はてな")
(#\: . "ころん") (#\- . "まいなす") (#\+ . "ぷらす")
(#\> . "さんかくこっか") (#\@ . "あっと") (#\& . "あんど")
(#\( . "かっこ") (#\) . "こっか") (#\! . "びっくり")
(#\< . "さんかくかっこ") (#\$ . "だら") (#\% "ぱーせんと")
(#\~ . "ちるだ") (#\* . "こめ") (#\^ . "はっと")
))
(define MECABTMP "_mecab.tmp") ; Quick hack for mecab input
(define (j->k str)
(with-output-to-file MECABTMP
(lambda ()
(display str)))
; (display (ces-convert str 'utf-8 'sjis)))) ; for inner debug
(let1 p (run-process MECAB :input MECABTMP :output :pipe)
(cdr (string-split
(ces-convert (read-line (process-output p) #t) 'sjis)
"\\s"))))
(define (say str)
(let1 for-gsay (ces-convert str 'utf-8 'sjis)
(sys-putenv "AquesTalk" for-gsay)
(print for-gsay) ; monitor
(process-output->string `("gsay"))))
(define (csub cl tbl)
(string-join
(map (lambda (c)
(cond
((assoc (char-downcase c) tbl) => (lambda (v) (cdr v)))
(else "しらないもじよー")))
cl)
","))
(define (conv li)
(map (lambda (str)
(let1 cl (string->list str)
(cond
((char-set-contains? #[[:digit:]] (car cl)) #`"<NUMK VAL=,|str|>")
((char-set-contains? #[[:alpha:]] (car cl)) (csub cl ALPHA))
((char-set-contains? #[[:punct:]] (car cl)) (csub cl PUNCT))
(else str))))
li))
(define (speak str)
(let1 li (j->k str)
(say (string-join (conv li) ","))))
(define (main args)
(define (scan-line line p)
(if (eof-object? line)
(sys-unlink MECABTMP)
(begin
(print line) ; monitor
(speak line)
(scan-line (read-line p #t) p))))
(call-with-input-file (cadr args)
(lambda (p)
(scan-line (read-line p #t) p))))
使い方
上記が、say.scm とすると
c:\sakae\gsay>gosh say.scm smpl.txt 7月22日(水)に、eclispeがありました。 <NUMK VAL=7>,ツキ,<NUMK VAL=22>,ニチ,かっこ,スイ,こっか,ニ,、,いー,しー,える,あ い,えす,ぴー,いー,ガ,アリ,マシ,タ,。 次回は、イースター島で、見られます。 ジカイ,ハ,、,イースター,トウ,デ,、,ミ,ラレ,マス,。
2行づつペアになっていて、先の行は、原稿。後の行は、gsay経由で、AquesTalk に渡す データだ。これぐらいになってると、あの人でも、読めるだろう。喋りも棒読みだし。
この子は、まだひ弱です。変なものを食べさせると、即死してしまいますので、鍛えて 頂けたら幸いです。
ちょっとはまった事
最初、PUNCHの定義を、rubyっぽく
(define PUNCT '((:. . "どっと") (:/ . "すら") (:? . "はてな") (:: . "ころん") (:- . "まいなす") (:+ . "ぷらす") (:' . "くぉーと") (:@ . "あっと") (:& . "あんど") (:( . "かっこ") (:) . "こっか") (:! . "びっくり") (:# . "いげた") (:$ . "だら") (:% "ぱーせんと") (:~ . "ちるだ") (:* . "こめ") (:^ . "はっと") ))
にしてたんだ。この状態で起動すると
c:\sakae\gsay\old>gosh say.scm ../smpl.txt gosh: "read-error": Read error at "./say.scm":line 25: dot in wrong context
こんなエラーを喰らってしまう。":'" とか、":(" の部分が、お気にめさない らしい。emacsから見ると、:? は、灰色で表示され、いかにも認識しましたよって 風になるんだけど、:( の部分は、普通のテキストの扱い。
早く、手を打つべきでしたね。