宮商定時制計算部

計算部日記190301

夏以来の更新になります。
今回は、計算部の昨年一年間の活動のうち、まだこのサイトに書いていないものを簡単に報告しておきたいと思います。

Linux用音声読み上げプログラム(2018年上半期の冬・C++)
テスト勉強などの音声学習のために制作しました。
従って、練習のためではなく、完全に実用のための制作です。
覚えたい事柄をテキストファイルに書き出し、このプログラムで処理すれば、それを読み上げた音声ファイルを作ってくれます。
その音声ファイルをスマホ等に入れ、通学途中などに聴いて学習します。
「こういうのを作りたい」と言い出たのは部員で、実際のコード書きは部員と顧問で分担しました。
ただし、読み上げる音声ファイルを生成する部分は自作ではなく、Open JTalkを使わせてもらいました。
また、できあがったwavファイルをmp3に変換したり、連結したり、ノーマライズをかけたりするのには、さらに別のソフト(ffmpeg、SoX)を使いました。
ですから、我々が書いたのは、それらの「外部アプリを制御する処理」と、「適当な区切り文字でテキストを分ける処理」ということになります。
前者は当然として、後者の処理がなぜ必要なのかというと、Open JTalkには1024バイト以内という制限があるからです。
1024バイトということになると、UTF-8の全角文字がだいたい3バイトの日本語の文章ですと、一度に300字程度しか処理できません。
これはかなり短いと言わざるを得ません。
長いテキストを300字以内で区切っていく作業を手動で行うのは非常に面倒ですから、自動で切り分ける処理がどうしても必要になります。
最初は「改行」と、「改行」がなかなか出てこない場合にはやむなく機械的に上限バイト数で切り分けていたのですが、それだと読み上げが「単語の途中」や、さらに悪いことには「文字の途中(=文字コードの途中)」で途切れてしまうことがあって、そのまま音声ファイルを連結すると、単語の途中で不自然なポーズが入ったり文字が抜けたりして、微妙に聴きにくくなりました(正岡子規が「まさおかし……き」になったり、「まさおかし」になったりw――文字コードの途中で切られてしまうと、そのテキストの最後と続くテキストの最初は不完全なコード=文字化け状態になります)。
やはり「改行」だけでなく、「。」=句点・「 、」=読点・「」」=閉じかぎ括弧・「)」=閉じかっこ などの「約物(受け約物)」でも区切って読ませる必要があるだろうということで、後からその処理を加えることになりました。
具体的には、
  1. テキストを1バイトずつ読み込みながら文字数をカウントしていく。
  2. あらかじめ設定した上限文字数に達する前に改行が出てきたら、そこで切る。
  3. 上限文字数に達しても改行が出てこない場合は、その終端から最も近い区切り文字(受け約物など)を探して、そこで切る。区切り文字は、「。」→「!」→「?」→「」」→「)」→「』」→「》」→「;」→「:」→「、」→「 」→……という具合に、まず「文の切れ目」を探し、それがなければ「句の切れ目」を探す。
  4. (以下繰り返し)
という処理としました。
UTF-8の文字をカウントするのは、それほど難しい処理ではありません。
次のような関数を作り、これに1バイトずつ文字データを渡すと、文字の先頭データの場合は1を返し、先頭データでない場合は0を返しますから、それをカウントしていけばいいのです。
int fc_ccount(unsigned char c) {
    if (c <= 0x20 || c == 0x7f) {   //制御コード等(ノーカウント)
        return 0;
    } else if (c <= 0x7e) {         //1バイト文字(カウント)
        return 1;
    } else if (c <= 0xbf) {         //2~6バイト文字2バイト目以降(ノーカウント)
        return 0;
    } else if (c <= 0xfd) {         //2~6バイト文字先頭(カウント)
        return 1;
    } else {                        //使われていない範囲(ノーカウント)
        return 0;
    }
}
こうして区切ったテキストをOpen JTalkに次々に読み込ませて、切り分けたテキストの数だけwavファイルを作ります。
それをffmpegやSoXなどを使ってmp3に変換し、一つのmp3ファイルに結合して、最後にノーマライズをかければ完成です。
このプログラムで作成した音声ファイルのサンプルをアップしておきます。
テキストは、ロジェ・ペルフィットの小説『特別な友情』(日本語訳) の一節です。
「僕もね、僕も考えたことがあるんだ」その子は言った。「僕らがしなきゃいけないことだよ。二人の血を少し交換するんだ、君と僕で。そうすれば、僕らは永遠に結ばれるだろう」
彼はポケットから折り畳みナイフを取り出して、袖を捲り、腕に軽い傷を付けた。わずかに滴がにじみ出た。彼はジョルジュにそれを飲ませるために近づいた。それから彼はナイフを差し出した。今度は彼が血を味わう番だった。彼らは寄り添い、傷が癒着するまでの間、しばらく無言のままでいた。
ジョルジュはこの場面に激しく心を揺さぶられていた。その素早さが、彼の目に対し、その価値を減少させることはなかった。自分の思考は、この子のそれに比べてかなり貧弱なように思われた。彼は自分が引用したユウェンティウスを恥じていた。自分では、アレクサンドルがしたばかりのことに、本物のキスを付け足す勇気など出てくるはずもない。彼は想像力では上を行かれたが、それを嘆くことはなかった。このような友人を持つことにうっとりしていたのだ。
彼は、アンドレと共に同じ儀式をしたリュシアンのことを考えた。ジョルジュは、彼の近くに到達したことがあまりにも遅くなったことをどんなに後悔したことか!そして、彼は今それに何と満足していることか!それは一度しか起こり得ないことなのだ。
ジョルジュは――永遠に、確実に――彼がかつて愛した誰よりも愛している者と結ばれたのである。引用やキスや手紙や金髪だけでなく、まさに彼ら自身の血によっても、二人は結ばれた。彼らはお互いへの入団を許した。それぞれが聖職者であり、いけにえでもあった。彼らの友情は宗教になった。彼らはそれを偶然安全な場所に保管した。自分たち自身の中にそれを同化したのだ。あの賛美歌の言葉に従って、それは彼らの傷の中に隠されたのである。
このサンプル・テキストは748字あるため、一度にOpen JTalkに読ませることはできませんが、このプログラムを使えば、テキストの切り分けからmp3作成まで自動でやってくれます。
このテキストは各パラグラフが比較的短いため、パラグラフの終端=改行ごとに区切って音声ファイルが作られます。
ここでは5段落あるので、まず5個の音声ファイルが生成され、後で結合されます。
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
誤読もありますが、まあこの程度読み上げてくれれば十分でしょう。
もう一つ、今度は改行がなかなか出てこないテキストを読ませてみます。
テキストは、ジョーン・リンジーの小説『ピクニック・アット・ハンギングロック』(日本語訳) の一節です(この翻訳は創元推理文庫のものではなく、ネット上にフリーで公開されているものです)。
イーディスの太った頬に数行の涙が伝った。なぜだろう、とアーマはいぶかしんだ。なぜ神は、人々の一部をこんなふうに不器量で不愉快に、ほかの一部をミランダのように美しく親切にお創りになったのだろう。愛しのミランダは、冷たい手で子供の熱い額を撫でようと屈んでいる。時々、パパの最高級のフランス・シャンパンや、春の午後の鳩のもの憂げな鳴き声によって生じてくるような、理屈を超えた優しい愛が、彼女の心を溢れんばかりに満たした。イーディスのたわごとを処理するミランダを、無情な微笑で待っているマリオンをも含む愛。涙が込み上げてきたが、悲しいわけではない。泣きたいなどとは全く思わない。ただ愛したいだけ。彼女は、自分がその影に横になっていた岩の上に立ち上がり、巻き毛を振り広げながら踊り始めた。いや、むしろ、温かく滑らかな石の上を滑り始めた、とでも言うべきかもしれない。イーディスを除く全員が、ストッキングと靴を脱いでいた。彼女は裸足で踊っていた。巻き毛とリボンを振り乱している、明るくうつろな目をしたバレリーナのように、小さなピンクの爪先が表面をほとんどかすることなく。彼女は、六歳のときに祖母に連れて行かれたコヴェント・ガーデン劇場にいた。舞台袖のファンにキスを投げ、一等席にブーケから抜いた花を放り込む。最後に彼女は、ユーカリの木の真ん中辺りのロイヤル・ボックスに向かって深々とお辞儀をした。イーディスは巨礫にもたれて、次の小さな坂に進もうとしているミランダとマリオンを指さした。「アーマ、ちょっと見て。あの人たち、靴も履かないで、いったいどこに行こうとしているのかしら?」困ったことに、アーマは笑っているだけだった。イーディスは不機嫌に言った。「気が狂ったに違いないわ」そのような放埒な愚かさは常に、幼いころにウールのルームソックスとガロッシュを与えられるような、イーディスや彼女の同類たちの理解を超えるものなのだ。精神的援助を求めてアーマの方を向くと、彼女もまた靴とストッキングを拾って腰に吊り下げているのが見え、イーディスはぞっとした。
このサンプル・テキストは866字あるため、やはり一度にOpen JTalkに読ませることはできません。
今回は、「300字」に達しても改行が出てこなければ、その終端からいちばん近い区切り文字で切り分ける設定にしてあります。
そうなると、このテキストの場合、冒頭から294字目の「ただ愛したいだけ」の句点と、その句点の直後から数えて297字目の「ロイヤル・ボックスに向かって深々とお辞儀をした」の句点の、二箇所で切り分けられることになります(もし句点が一つもなければ、次は「!」を、それもなければ「?」を……と探していき、万が一区切れる記号が一つも見つからなければ、やむを得ずエラーメッセージを表示して、ユーザーに適当に区切り記号を挿入するよう促します)。
それら二つのポイントで切って三つの音声ファイルを生成し、後で一つにまとめています。
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
う~ん、前のテキストよりも誤読が増えていますね……。
「巨礫」(きょれき)は読めずに飛ばしてしまっていますが、まあこの単語は仕方がないでしょう。
……実は、顧問は普段Windowsを使っているので、このプログラムは利用していません。
もし音声読み上げが必要なら、ジャストシステムの詠太か、下記のSofTalkに、読ませたいテキストをじかに貼り付けて読ませると思います。
これらはOpen JTalkよりもはるかに多くの字数を処理することができるので、そもそもこのようなプログラムは必要ないのです。
しかし、部員A君の方は逆にLinuxがメインであるため、このプログラムはその後の試験勉強に大活躍しているそうです。
そういうわけで、計算部としては初めての「実用的なプログラム」になりました。
国語の教員にはWordよりも一太郎を使う人が多く、顧問もその一人です。
詠太は、一太郎のパッケージのうち、プレミアム以上に同梱されています。

リスト構造の練習(2018年夏・C)
ここに、前の日記に書いた「リスト構造――可変にできるものは全部可変にしてみよう!」が入ります。
一時的にC言語に戻ってリスト構造の練習に取り組んでいたのは、だいたい6月から8月にかけてでした。

ドローイング練習ソフト(2018年秋・C++・Qt)
そこそこ有名な絵の練習ソフトに『30秒ドローイング』というのがあります。
あれに「秒数の設定を自由にできるようにする」「読み込む画像を選べるようにする」という機能を加えた、完全新作ソフトです。
これも部員が「こういうのが欲しい」と言い出したことから始まった制作です。
これはさすがにGUIソフト制作用のキットを使わないと難しいだろう、ということで、初めてQtで制作することにしました。
以前にも書いたように、Qtには、初心者がゼロから応用まで無理なく学べる日本語の参考書がありません。
これは、Qtの場合は、「前提としてC++の基礎知識が必須であること(GUIソフトはオブジェクト指向になるので、当然クラスの知識は必要で、これがけっこう敷居が高い)」「バージョンアップが早く、操作方法がすぐに変化してしまうこと」といった問題があるため、やむを得ないのだろうと想像しています。
それでも、もしかしたら英語の参考書になら完全初心者向きの良書があるのではないかと期待して、AmazonUSAで評価の高い本を何冊か購入してみたのですが、いずれもダメでした。
「ダメ」などと書くと叱られてしまいそうですが、何と言ったらいいのか、どの本も、「例題をコツコツ写経して練習しても、それを応用してオリジナルのアプリが制作できるようになる気がしない」のです。
仕方がないので、行き当たりばったりで制作に踏み切ることにしました。
新しいことをしようとするたびに英文ヘルプやネットで調べる必要があったため、ずいぶん苦労はしたけれども、結果としてそれで正解だったと思います。
英語の勉強になったし、ソフトもどうにか形になりましたから。
これも言い出しっぺの部員が主導して設計し、コード書きは部員と顧問で分担しました。
このあたりになってくると、部の活動の主導権は完全に部員が握っていて、顧問は付いていくだけという状態になっています。
「今度はこういうのが作りたいのですが」「なるほど。分かった、協力しよう」という感じです。
まさに後生畏るべし、ですね(笑)。
Windowsでビルド&実行した設定画面です(Qtらしく、Linuxでも同じコードでビルド&実行できます)。
表示画面です。
サンプル画像はルイス・キャロル『不思議の国のアリス』の挿絵より(ジョン・テニエル画、パブリックドメイン)
設定画面と表示画面は同じウインドウ上に表示されますが、これはQtのStacked Widgetで実現しています。
さて、このソフト、実はこれを書いている2019年2月現在、まだ完成していません。
うまくできない処理が残っているのです。
それは、インターバルで次の画像を先取りしているとき(上の(1)のとき)、画像を薄く表示して、カウントダウン数字の方を目立たせる処理です。
要するに、「画像の透明度を上げる処理」ですね。
画像は普通にQLabelで表示しているので、ウィジェットを透過させるQGraphicsOpacityEffectをそのQLabelに適用してみたのですが、適用と削除を繰り返すと不安定になってしまってダメでした。
となると、ウィジェットを透過させるのではなく、画像そのものを透過させる処理を書く必要がある、ということになるでしょうか。
しばらく調べてみたのですが、どうすればできるのか、まだ分かっていません。
そのほかにも、何となくうまくいってはいるけれど、自信が持てないでいる部分もあります。
このソフトは、実行中にマウスでウインドウのサイズを変更した場合、表示画像と表示文字もそれに連動して動的に拡大縮小する仕様になっています。
全画面表示にすれば、画像もモニタサイズいっぱい近くまで拡大表示されるわけですね。
これは、Qtのレイアウト機能を使えば自動的に実現してくれるのですが、上の(1)のように、インターバル時には画像の上にカウントダウンの数字を表示する必要があるため、画像用ラベルとカウント表示用ラベルを「重ねて」配置しなければならず、どうやらその場合はレイアウト機能が使えないようなのです。
下の図は、各表示用ラベルの配置&重なり状況です。
ラベルの重なり
レイアウト機能を使用すると、上図のようにウィジェット(ここではラベル)を重ねて配置しておいたとしても、強制的に分離されて並べ直されてしまいます。
仕方ないので、resizeEventでリサイズが発生した瞬間瞬間を捉えて、各ラベルの寸法をそのリサイズ量から計算して再描画するコードを書きました。
void MainWindow::resizeEvent(QResizeEvent *event)
{
    int dif_w = event->size().width() - v_wSize->width();
    int dif_h = event->size().height() - v_wSize->height();
    //カウント表示用ラベル群の動的サイズ変更。こちらを先にする必要あり?
    ui->lblIntervalNumber->setGeometry(event->size().width() / 2 
                                       - v_intvLblSize->width() / 2 - 10,
                                       event->size().height() / 2 
                                       - v_intvLblSize->height() + 40,
                                       v_intvLblSize->width(),
                                       v_intvLblSize->height());
    ui->lblUpperCountDown->setGeometry(v_cdUpLblSize->x(),
                                       v_cdUpLblSize->y(),
                                       v_cdUpLblSize->width() + dif_w,
                                       v_cdUpLblSize->height());
    ui->lblLowerCountDown->setGeometry(v_cdLoLblSize->x(),
                                       v_cdLoLblSize->y() + dif_h,
                                       v_cdLoLblSize->width() + dif_w,
                                       v_cdLoLblSize->height());
    //画像表示用ラベルの動的サイズ変更。これは必ず後?
    ui->lblImage->setGeometry(v_imgLblSize->x(),
                              v_imgLblSize->y(),
                              v_imgLblSize->width() + dif_w,
                              v_imgLblSize->height() + dif_h);
    if (!v_pix->isNull() && !ui->lblImage->pixmap()->isNull()) {
        ui->lblImage->setPixmap(v_pix->scaled(ui->lblImage->size(), 
        Qt::KeepAspectRatio));
    }

}
これでまあ期待どおりの動作はしてくれたのですけれども、この部分、コードの記載順をカウント表示用ラベル→画像用ラベルにしておかないと、なぜか動かなくなってしまうことがあるのです。
「重なっているウィジェット」の寸法を変えたい場合は、上になっているウィジェットから先に処理しないと負荷がかかる、ということなのでしょうか?
あるいは、そもそもウィジェットを重ねて配置すること自体が「邪道」ということ?
この部分のコードを最初に書いたときは、偶然上記の順番になっていたため、特に問題もなく期待どおりに動いてくれました。
しかし、これがもし逆に書かれていたとしたら、「処理順を逆にすれば不具合を回避できる」などとは思いもしなかったでしょう(処理を逆にするとうまくいかないことに気づいたのは、よく覚えていませんが、コピペに失敗するか何かして、たまたま該当部分が逆になったことがあったためだったと思います)。
こういう、原因がよく分からない問題が浮上してくると、「このコードの書き方は根本的に間違っているのではないだろうか?」と不安になるものですから、例えば職員室で「こんなのできたよ!」とほかの職員に見せたりすることにも、いまだに抵抗があるままなのです。
そういう次第で、まだすっきりしない部分が残ってはいるけれども、とりあえず自分たちで使う分には問題なく動作するようなので、それなりの達成感を味わうことはできました。
……これは顧問の感想ですが、繰り返し使ってみると、やはりGUIアプリはCUIプログラムよりも使いやすいと感じます。
直感的に操作することができますからね。
しかし、顧問よりもはるかにLinuxに慣れている部員A君は、あるいは違う感想を持っているかもしれません。
ただし、部員A君はちゃんと絵の練習という本来の用途で使用しているのに対し、顧問はもっぱらスライドショーとしての用途でしか使っていませんw。

珠算読み上げ算プログラム(2018年下半期の冬・C++)
顧問は大昔、日商珠算検定の1級に合格しています(70年代半ばのことで、まだ検定種目に「伝票算」があった時代です)。
担当教科は国語、すなわち文系人間ではあるけれども、加算と減算だけのごく単純な計算問題なら得意です。
で、部員A君がよく「暗算が苦手です」と言っているので、「それなら、『計算部』でもあることだし、そろばんでもやってみようか」ということになりました。
その後、しばらくそろばんの練習をするうちに、あるときA君が、「前に作った『音声読み上げプログラム』を使って、読み上げ算のプログラムが作れそうです!」と言い出したため、部活もそちらにシフトしました。
読み上げ算にルールのようなものがあるのかどうかは、実は顧問も知りません。
ネットで調べてみても、よく分かりませんでした。
しかし、顧問の昔の経験から考えて、だいたい次のような機能が必要だろうという結論に達しました。
  1. 読み上げ算の多くは10口のようだが、5口や15口など、問題の長さを自由に設定できるようにする。
  2. すべて加算にするか、減算を入れるかを選べるようにする。
  3. 減算が入る場合、減算の数を調整できるようにする(減算の数は3~5割くらいが普通らしい)。
  4. 減算が入る場合、最終解答も計算途中もマイナスにならないよう調整できるようにする。
  5. 初心者は1~2桁程度の計算で十分だが、上級者向きに極めて大きな桁を扱う問題も生成できるようにする。
  6. 数値の桁数が偏らないようにする。……例えば5~10桁の読み上げ算の場合、最小桁数の5桁の数値と最大桁数の10桁の数値が必ず一度は入るようにし、さらにすべての桁数の登場回数が平均的に散らばるようにする。
  7. 減算が連続する箇所で、「引いては」を最初に一度しか言ってくれない場合と、二番目以降も「なお引いては」といちいち言ってくれる場合とがある。どちらのスタイルも選べるようにしたい。
  8. 数値の途中に「0」がある場合、例えば「¥2004」の場合、普通に「にせんよ円なり」と読む場合と、「にせんれいれいよ円なり」「にせんぜろぜろよ円なり」「にせんとびとびよ円なり」などと「0」を読んでくれる場合とがある。音声読み上げプログラムに「2004円なり」というテキストを読ませると、当然前者になる。これを後者のように、「0」があることを知らせる読み方で読ませるプログラムを書くことも、できなくはなさそうである。しかし、これを実現させるとなるとかなり煩雑なプログラムになることが予想されたので、今回は見送ることにした。
  9. 読み上げのスピードを調整できるようにする。
プログラムを設計する上で、たぶんいちばん最初に考えなければいけないのは、5.の「上級者向きに極めて大きな桁を扱う」部分であろうと予測しました。
読み上げ算は、整数の加減算です。
それなら、int型を使って計算すればいいかというと、実はそうもいきません。
int型での計算だと、どの処理系でもだいたい20億(10桁)くらいまでの数値しか扱えないからです。
int型の上限値をオーバーしないよう、9桁以下の数値しか出てこないようにするとなると、その読み上げ算問題は、もはや1級の人間にはやや簡単すぎてしまうのです。
まして、全国レベルの競技会では「兆」だの「京」だのといったさらに大きな桁数が出てきますので、int型ではまったく足りません。
そうなると、本プログラムには、いわゆる「多倍長演算」の機能を実装する必要がある、ということになります。
long型を使っても、処理系によってはint型と同じ桁数しか扱えない場合もありますから、どの環境でも大きな桁数が等しく扱えるようにするためにも、多倍長演算のコードを書くことは不可欠です。
今回のプログラムにおける多倍長演算は、整数の加算と減算だけですから、実際のところそれほど難しいものではありません。
結局、顧問は普通の関数で、部員A君はクラスで、それぞれ多倍長演算処理を実現することができました。
これで読み上げ算を一つ作ってみると、次のようになります。
                            330,651,252,866,193,012,636,801,847,614
                      3,756,342,807,477,770,038,614,517,249,030,717
                     24,872,554,759,550,866,660,350,613,546,148,297
                    696,765,317,384,796,841,603,684,886,926,042,563
                                -84,565,804,493,475,724,824,040,899
                             22,250,809,556,700,412,851,782,163,777
                                                859,354,326,426,885
                  3,615,603,900,843,187,767,676,150,774,459,507,793
 20,698,852,819,225,993,678,740,183,717,684,011,318,533,053,118,265
                                           -532,965,604,592,347,487
           -536,219,707,854,544,340,847,771,526,196,332,945,142,877
       -372,544,117,735,998,526,831,821,880,592,485,031,747,238,371
             -2,654,632,680,594,646,003,052,419,471,524,742,041,195
                              9,291,390,814,141,688,322,850,279,783
                                             -8,463,698,715,167,664
-------------------------------------------------------------------
 20,698,479,736,238,258,143,552,268,945,657,453,034,573,428,587,201
こんなにも大きな桁数の読み上げ算は(これが見取り算だとしても)、さすがに全国大会でも出てこないでしょうが、int型では不可能な桁数の計算が可能であるということを示すために、あえてこうしてみました。
ご覧になれば分かるように、これは計算部分だけを、3桁ごとにカンマを挿入し、右揃えにしてテキスト出力したものです。
音声読み上げソフトに読ませるには、さらに「願いましては」「~円なり」「引いては」「加えて」「~では」等のセリフ部分も付け足さねばなりません。
しかし、これだけでも「肉声での読み上げ算用問題」や「見取り算問題」としてなら使うことができそうです。
処理速度については、この程度の問題なら一瞬で作成されます。
これが実用性無視のものすごく大きな問題、例えば「200桁・10000口の加減算」であっても、(環境にもよりますが)3秒もかかりません。
計算結果が合っているかどうかですが、この桁数ですとExcelを使っての計算も容易ではないので、完全な検算はしていません(Excelでは計算に使える上限は15桁です……少な!)。
ただ、下の方の数桁をそろばんで計算してみると、どうやら正解は出ているようです。
計算途中でも最終解答でも、一度も負の数にはなっていません(これは見れば分かりますよね)。
また、最小桁(7口目)と最大桁(9口目)が漏れなく入っており、各桁の数字の登場回数は次のように均等に分散しています(0回の桁数は省略しました)。
15 桁の数字     1 回(設定した最小桁)
16 桁の数字     1 回
18 桁の数字     1 回
26 桁の数字     1 回
28 桁の数字     1 回
29 桁の数字     1 回
30 桁の数字     1 回
34 桁の数字     1 回
35 桁の数字     1 回
36 桁の数字     1 回
37 桁の数字     1 回
40 桁の数字     1 回
42 桁の数字     1 回
45 桁の数字     1 回
50 桁の数字     1 回(設定した最大桁)
次は、実際の読み上げ算で使われそうな桁数で、かつExcelで簡単に検算できる問題を作ってみましょう。
        47,431,522
           -30,151
   307,289,548,921
    -9,835,922,405
           -90,475
      -124,655,503
    39,283,216,015
         7,418,864
           843,174
      -119,377,937
 5,265,348,494,633
  -563,680,933,672
    81,282,487,174
         1,343,109
       -23,351,882
------------------
 5,119,476,421,387
5 桁の数字      2 回(設定した最小桁)
6 桁の数字      1 回
7 桁の数字      2 回
8 桁の数字      2 回
9 桁の数字      2 回
10 桁の数字     1 回
11 桁の数字     2 回
12 桁の数字     2 回
13 桁の数字     1 回(設定した最大桁)
Excelでの検算結果と一致しましたので、大丈夫そうです。
Excelの2列目を見ると、途中で負の数になることも避けられていますね。
今回の試行では、減算の数が全体の「30~50%」の範囲になるように設定してあります(15口なら、そのうちの4~7口が引き算になる)。
上の実行では減算は7回入りましたので、設定範囲内に収まっています。
もちろん、減算の数を一つに指定することも、すべて加算にすることもできます。
さらにもう一つ、実用性は度外視して、30000口という長い問題で、やはりExcelで簡単に検算できる桁数で作ってみました。
         2,186,130
        60,457,897
          -340,489
        -4,222,634
        -8,042,129
            69,328
            -2,986
       382,062,921
          -324,560
               234
       (中略)
      -571,771,390
         4,947,965
              -427
       -60,768,627
           -52,580
        19,915,144
      -655,483,593
             6,650
           -28,728
              -189
------------------
 1,020,891,948,798
3 桁の数字      4286 回(設定した最小桁)
4 桁の数字      4286 回
5 桁の数字      4286 回
6 桁の数字      4285 回
7 桁の数字      4285 回
8 桁の数字      4286 回
9 桁の数字      4286 回(設定した最大桁)
これだけ長い問題になると、さすがに一瞬では終わりません。
体感的には、Windowsなら1秒、Linuxならその半分くらいといった感じでしょうか。
こちらもExcelでの検算結果と一致しました。
30000口全部は載せられないので、実行結果は最初と最後の10口ずつを、Excel画像は最後の16口を切り取っておきました。
減算の出現回数は9345回で、これは全体のおよそ31%に当たります。
ちゃんと30~50%の範囲内に収まっています。
……こうしてできた計算部分に、「願いましては」「~円なり」などの読み上げ算用のセリフを加えたものを、system関数で音声読み上げソフトに読ませれば出来上がりです。
音声読み上げソフトは、Linux版では上記「音声読み上げプログラム」を利用するため、Open JTalkを使うことになります。
また、今回はWindows版も作ってみようということで、そちらではSofTalkを使いました。
従って、外部ソフトの操作部分のみ、Linux用とWindows用の二通りのコードを書く必要がありました。
ここでは、三問ほど適当に問題を作成し、それを読み上げソフトが読んだ音声ファイルをアップしておきます。
Windows版で作成しましたので、読み上げソフトはSofTalkです。
読み上げ算:その1
  1
  7
  2
  7
 -1
  9
  8
 -3
 -1
 -9
---
 20
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
初心者向けです。
連続する減算に「なお引いては」が入る、親切な読み方バージョンで読んでいます。
読み上げ算:その2
               397,175
                64,502
 5,037,419,917,233,531
  -224,709,834,841,449
        57,458,367,135
    68,716,726,434,798
     8,834,747,857,414
      -273,048,150,995
             3,532,360
           467,625,885
         1,697,450,424
            33,794,672
  -475,931,328,829,828
               -29,748
        71,598,264,479
----------------------
 4,414,188,439,170,355
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
珠算競技会全国大会の「読み上げ『暗算』」のレベルです。
このレベルだと、顧問は「読み上げ『算』」(暗算ではなく)で遅く再生すれば、正解できることもあります。
読み上げ算:その3
      93,411,353,616,235,461,093,073,036,055,021,189,852,598,862,104,066,718,878
 813,280,150,617,299,232,320,383,821,551,100,635,291,957,698,410,494,184,364,803
    -605,611,191,647,069,072,644,181,672,605,623,088,506,184,021,135,922,351,044
   2,788,219,776,689,595,194,996,049,870,550,648,841,036,736,162,550,313,336,317
 -44,234,082,684,192,748,434,637,557,048,671,585,073,806,707,852,735,042,258,263
--------------------------------------------------------------------------------
 771,322,087,871,765,245,469,191,205,736,429,097,160,534,141,561,277,599,810,691
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
こんなに大きな桁数が入れられるそろばんは、市販品にはないと思います(要特注)。
これはあくまでも実験です。
この中で最も大きな数である二口目の読み方は、
8132阿僧祇(あそうぎ) 8015恒河沙(こうがしゃ) 0617極(ごく) 2992載(さい) 3232正(せい) 0383澗(かん) 8215溝(こう) 5110穣(じょう) 0635秭(じょ) 2919垓(がい) 5769京(けい) 8410兆 4941億 8436万 4803円なり
となります。
SofTalkに読ませたテキストの2行目は
「813280150617299232320383821551100635291957698410494184364803円なり」
でしたが、正しく読んでくれています。
正直、こんなに大きな桁数まで対応できるとは思いませんでした。
最近の音声読み上げソフトは素晴らしいですね。
……その後、試しにOpen JTalkを使うLinux版の方でも同じ問題を読ませてみました。
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
こちらも、「阿僧祇」の位まできちんと読んでくれていますね。
読み方の癖が異なるので好みは分かれるでしょうけれど、大きな数値の読み上げについては、Open JTalkもSofTalkも甲乙付けがたいといったところでしょう。
……さらに、試しにジャストシステムの詠太(8.0.1.0)にも読ませてみたところ、「きゅう、さん、よん、いち……」と、数字の羅列になってしまいました。
そこで、桁を少しずつ減らして試してみたところ、詠太はどうやら31桁(100穣の位)までは桁数付きで正しく読んでくれるけれど、32桁以上になると対応できず、数字の羅列になってしまうようです。
とはいえ、詠太は何と言っても有料ソフトですから、当然こちらの方が優れている点もあります。
最も大きな優位点は、英文に対応していることでしょう。
Open JTalkとSofTalkは、文中に英文が出てくるとほとんど対応できず、アルファベットをそのまま読んでしまうことが多いのです。
試しに、次のような文章をSofTalkと詠太に読ませてみました。
次の英文は、Marie Louise de la Ramee 作『ウルビーノの子供』の最後の文です。
But Luca heard not; he was still kneeling at the feet of Raffaelle, where the world has knelt ever since.
最初の But Luca heard not の部分に見えるルカとは、ラファエロの年上の友人です。
たった7歳の少年ラファエロが描いた作品のおかげで、ルカは愛するパシフィカと結婚できることになりました。
それで彼は、神様にひざまずくように、ラファエロの前にひざまずいたのです。
マリー・ルイーズ・ド・ラ・ラメー(ウィーダ)『ウルビーノの子供』『ランプブラック』(日本語訳)
まずはSofTalk。
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
……英文のほとんどがアルファベット読みになってしまっていますね。
これでは聴いていてストレスが溜まります。
次は詠太。
↑「エラー」「無効なソース」などが表示される場合は、 こちら をクリックしてみてください。
……こちらは英文をきちんと英文として読んでくれています。
3文目のような、日本語の文の中に英文が入ってくる場合も、その部分だけを英文として扱っています。
これなら安心して聴いていられますね。
さすが有料ソフトといったところでしょうか。