uirouのひとりごと

不定期に何か書こうと思います。

ことせかい:小説家になろう 読み上げアプリ 開発記

ことせかい小説家になろう というサイトにある小説をダウンロードして、 Siriさん(のエンジン)に読み上げてもらうという iPhone用アプリ です。

つまり、ナンジャラホイ? という人は紹介用の動画を作ったので、見ていただけるとわかるかもしれません。


ことせかい: iPhone 小説家になろう 読み上げアプリの紹介 - YouTube

動画ではまだAppleの審査を通っていないっぽいことを言っていますが、先日 Apple の審査を通ったので 今はダウンロードできます。

 

 

ことせかい は私の初のiPhoneアプリで、だいたい三ヶ月位かけてリリースまでこぎつけた感じです。良い節目なので ことせかい の開発にあたってのいろんな事柄を振り返ってみたいと思います。

作り始めるまでの話

私にとって初めての iPhoneiPhone 3GS(2009年6月発売) でした。 つまり今(2014年9月)からすると5年位前の話です。 プログラムが書けるっていうととりあえず書いてみたい私としては、iPhone 3GS を買ったすぐ後に Mac mini を買ってきて、iOS Developper への登録(年間9千円位?)をしたのでした。

それで、とりあえず書いてみようとして

  • Xcode 触ったこと無いからワカラン
  • Objective-C 触ったこと無いからワカラン
  • Mac OS 全然しらないからそもそもフォルダ名の変え方からしてワカラン

と、何もわからなすぎて急速にやる気が萎えて放置する、ということをしました。 それで、半年とか一年とか経って「またやってみようかな」と思うのだけれど全然やらない、 やったとしてもWebから持ってきたcodeを少し動かしてテストする、程度しかやらない、 それでも何か作るとしたらこんなものかなーとか考えるだけはする、 という黒歴史バンザイなことを続けていました。

そうです。やりたいけれどやらない駄目な子でした。 自分でも駄目だなーと思っていたのですが、どうにもやる気にならないまま、時間が経っていったのでした。

そんなこんなで5年は経ったであろう2014年の6月、Appleさんは Swift という言語を発表して、 「これからは Objective-C を使わないでもこのモダンな Swift を使えばいいよ!」 と言ってくれたのです。

わぁすごい。毎年作りもしないのに iOS Developper Program にお布施してきたけれどこれはいいかもしれん!

と少しやる気になった私は、早速 mac に入れて遊んでみようとしたのでした。 第一印象としては、

  • なんか Objective-C の上にかぶせてある感じ(wrapperみたいな感じ)だな
  • けれど、摩訶不思議な の世界は完全に見えなくなってるからいいかも

というもので、 もういい加減一つ位はアプリ書いてみようよ俺、という気持ちと、 これなら行けるかも、という期待感、 あと職場が変わって気持ちに余裕が出てきたという追い風もあって、 この期を逃したらまた長いこと作らない事になりそうだなーと思ったのでした。

ということで、やるだけやってみようと思ったわけです。

作るものを考える

その時、自分が作ってみたいものは幾つかありました。

  • 職場で話してた画像○×クイズアプリ
  • 運動中(エアロバイクでよく運動をしているのでその間)にやらされる「聞く脳トレアプリ」
  • 小説の読み上げアプリ
  • RSS2ch の読み上げアプリ
  • カーチャンが使ってた iOS の家計簿アプリが公開停止したのでそれの代わりのアプリ
  • トーチャンカーチャンが双方で野菜を買ってきてダブるとか買ったけれど忘れて腐るとかを解消するアプリ

これらは上のものの方が作りやすそうで、下のものは作りにくそうだなぁと思っていた感じです。

ここで、5年も何もできなかった私としては、 途中で投げ出さないものを選ぶのがいいだろうなぁということで、 「自分が欲しい」と思っているもので完成形が簡単に思い浮かぶ(つまりは難しそうな所が少ない) 小説の読み上げアプリ を作ることにしました。

小説の読み上げアプリ

小説の読み上げアプリが欲しいというのは、

  • 老眼が進んできて iPad mini の画面でも結構文字を大きくしないと小説を読むのが辛くなってきた
  • 歩きスマホはよくないよねーと思うけれど歩いてる間の時間は無駄多いなーと思ってた

というもので、目を使わずに小説が読めるであるとか、歩いてる時にも使える暇つぶしが欲しいであるといった目的でした。

それだけであれば単に KindleiPhone版 を取り出して ちょこっと設定を変えると読み上げアプリに早変わりするので、 それでいいじゃん、という話がありました。

ただ、今回はアプリを作るというのが主目的ですので、 ~があるからイラナイよねー、イラナイイラナイ、みたいな事は言わない事にします。 じゃぁっていうんで、Kindle は売り物の本を扱うんだから私の作るアプリは 売り物じゃないものを読み上げるようにしよう、と考えました。

売り物じゃないもので読み上げるというと、青空文庫既に読み上げアプリがありました

あら残念。

ということで、その時期に読み始めていた 小説家になろう の読み上げアプリ (こちらは存在しないように見えました)を作る事にしました。

まずは Siriさん に喋ってもらう

小説家になろう についてどうこう言う前に、Siriさん(のエンジン) に喋ってもらえないと読み上げアプリは作れません。 ということで Siriさん に喋ってもらうための方法を google先生 に聞いてみます。

どうやら iOS で使える日本語の読み上げソリューションは いくつかあるらしい のですが、ここの人のまとめている所によると iOS 7 から使えるようになった AVSpeech より 音質の良いものは iSpeech か Acapela 位とのことで、iSpeech はオフラインで使えないであるとか、 Acapela は有料らしいので、まぁとりあえずは Siriさん のエンジンに読み上げてもらえば良いかな、 ということを考えます。

Swift からの挫折

ということで、AVSpeechSynthesizer というものを使えば良いよ、 という話だったので、これまた google先生 にお伺いを立てて使い方を教えてもらい、 Objective-C では読み上げてもらえるというところまでできました。

じゃぁというので Swift でやってみようと Xcode 6 の beta を起動して Objective-C の code を Swift で書き換えていきます。 ビルドしてリンクまで出来て実行してみると、 何故か読み上げてもらえない上に、ハングしてしまいます。 AVSpeechSynthesizer を使わないプログラムについてはマトモに動くので、 これは何か AVFoundation を使うあたりで何か勘違いをしているのだろうなぁとは思うのですが、 何を勘違いしているのかさっぱりわからなかったので、早々に Swift を使うのは諦めました。

後からわかったのですが、これは iOS 8 のシミュレータのバグらしく、 ことせかい がリリースされた 9月 まで直っていませんので、 この時点で Swift に見切りをつけたのは良い判断だったようです。

とにもかくにも、Objective-C ではちゃんと発音してくれるわけなので、 まぁとりあえずは発音周りはやり方がわかった、ということで先に進むことにしました。

なろう小説API

発音ができるようになったら次は小説をダウンロードする部分を作りたくなります。

小説家になろう のサイトでは、なろう小説API というもので、 小説家になろう に登録されている小説の検索用のものを用意してくれています。

ただ、このAPIで検索はできるのですが、小説そのものを取得するためのAPIは定義してくれていないようでした。 そこで、この API経由 で手に入れた Ncode (小説家になろうのサイトでの個々の小説につけられたID)を使って、 通常の 小説家になろう の Webサイト のテキスト形式ダウンロードの form を使って小説の本編をダウンロードするようにしました。

やっていることとしては、

  1. なろう小説API を HTTP で GET して JSON を取得
  2. JSON から Ncode やその他の情報を取り出す
  3. Ncode から個々の章の テキストダウンロードForm を推測
  4. テキストダウンロードForm からテキストをダウンロード

という作業をするだけですので、一通りのものを関数化して UI を作る時のためにとっておきました。

データの保存

読み上げを行う時に毎回小説をダウンロードしていたのでは面倒くさいです。 ということで、取得してきたデータを保存するための方法を google先生 にお伺いします。

google先生 によると、CoreData というものを使うと良いらしいので、 そのあたりを調べて使ってみました。 CoreData というのはどうやら sqlite への OR mapper 的なものらしいです。 テーブル定義のバージョン毎のマイグレーションも定義できるようなので、 とりあえずはこれを使う事にしました。

これで小説のデータ保存ができるようになったので、小説毎に読み上げ位置(栞のようなもの)であるとか、読み上げ用の発音設定の保存などもできるようにしました。

UI

小説も読み込めて、読み上げもできるようになったので、そろそろ UI をつけてやります。 多分これができれば自分の iPhone に載せて小説を聞く事ができるようになりそうです。

UI はどうやらプログラムから指定するか、Storyboard という GUI なインタフェースから作る方法があるようです。

UI の配置等をプログラムから指定するのは Xt や GTK の時代にいっぱいやって飽きたので、 .NetWindows.Forms みたいな感じに見える Storyboard を使う事にしました。

ただ、Storyboard のレイアウタであるところの AutoLayout というものは指定したものと 動きが私の理解を超えていて、何故このようなレイアウトになってしまうのか、といった事が頻発してしまい、 (特に UIScrollView が絡んだりすると大変)この記事を書いている今でもまだわかっていません。 なので、とりあえずは iPhone だけで縦画面しか使えない、 ということにしてレイアウタが余計な事をしないでくれることを祈っている、という状態です。

このあたりで、UI を Storyboard で設計するよりは、何かもっと簡単なもので設計したほうがいいんじゃなかろうか、 と思って fluidui という物を使いました。これは結構簡単にページのレイアウトやページ遷移のシミュレートができていい感じでした。

ということで、

  1. 小説家になろう で検索
  2. 検索結果を確認
  3. ダウンロード
  4. ダウンロードされた小説を表示

というあたりまで UI をコツコツと作っていきました。 ここまでできたので、自分の iPhone に入れて動かしてみて読み上げてもらいながら歩いてみる、 などをし始めました。

ブラッシュアップ

小説家になろうの読み上げアプリ としてのだいたいが出来たので、 後はバグ修正と使い勝手の向上だよね、という事で、日々の生活で気づいた事を github の issue に追加してそれを潰す、 という事をプチプチと繰り返しました。

ことせかい という名前の通り、Siriさん(のエンジン)は読み上げの文字を間違える事が結構あるので、「異世界」を「いせかい」と読むのだと変換するようなテーブルを作ったり、 単純に同じトーンで読み上げられてしまうと通常の文なのか会話文なのかがわかりにくい書き方の場合もあったので、 それに対応できるように会話文については声のトーンを変えられるような機能をつけたりしました。

また、バックグラウンド再生中にコントロールセンターやロック画面での再生・停止の機能も入れた時には、 再生させ続けている時にすぐに再生を停止できるようになり、(自分の)使い勝手が向上しました。

アプリアイコン

それまではアプリのアイコンが無く、殺風景な丸と線のアイコンだったので、リリースするまでには作らねばならないアイコンを作ることにしました。アイコンはアプリの顔なんだろうなぁと思うとまじめに作らないと駄目なのかな、と思ってクラウドソーシングサービスとかでアプリアイコンをデザインしてもらおうかしらん、とも思ったのですが、そもそも「どんなイメージの物を期待しています」というイメージすら無い状態で「こんなアプリなのでアイコンを考えてください」とか丸投げしてもしょうがないよな、と思って、だいたいのイメージを自分で考え始めました。

で、その頃にはもうアプリの名前を「ことせかい」にしようと思っていたので、小説家になろうには異世界ファンタジーが多いんだからとりあえず剣の絵は必要だよね、ということで剣の絵を持ってきて画面に置いて、読み上げアプリで「ながら聴き」を目的としているのがわかるとするとイヤホンの絵が欲しいかな、と思ったけれどイヤホンは小さすぎてアイコンだとわかりづらそうなのでヘッドホンかな、ということでヘッドホンの絵を貼り付け、剣とヘッドホンの絵をつなげればいいんじゃねっていうか剣が表紙の本とヘッドホンをつなげよう、ということでこのアイコンのイメージができました。

 

 で、テキトーに本の大きさやら剣の大きさやらヘッドホンの大きさやらをいじっていたら、なにやらこのアイコンで必要十分な気がしてきたのでそのままアプリのアイコンにしてしまうことにして、結局クラウドソーシングサービスには頼らずにアイコンを決めてしまいました。まぁあんまり考えていないですがシンプルで識別しやすいアイコンなんじゃないかなぁと思っています。

リリース

初期段階のものとしてはこれで十分だろう、と思う所まで作り込めたので、 本来の目的であるリリースまでやるぞということで AppStore へのリリースを申請をするための準備を始めました。

申請をするにあたって、サポートのWebPageが必要だということで github pages でそれらしいページを作りました。github pages は markdown で書けば後はテンプレートを選ぶだけでほとんど完成したので簡単でよかったです。

また、申請時にはアプリの説明文であるとかイメージであるとかを登録できるので、それらをシミュレータで撮りました。これらは 3.5インチの iPhone用 と 4インチの iPhone用 (と最近では iPhone 6iPhone 6 Plus のサイズのものも必要になったようです)のものを撮っています。なんとなく、自分のtwitterアカウントのフォロアで小説家になろうの小説を書いている人の小説をキャプチャ画面に入れたりしてみています。

一応日本語と英語の2つの言語をサポートしているので、それぞれの説明文や画面を登録して、だいたい要素は揃ったので AppleStore へのレビュー申請を行いました。 

レビューは一回 Reject はされたのですが、これはどうやら iOS8 でと iOS7 で動きが変わった API があったらしかったので、 表示が崩れてしまった、ということらしく、計算で求めていた値を固定値に書き換える事で 出し直して審査を通してもらいました。

紹介動画

レビュー待ちをしている時に ことせかい を作っている時に応援してくれていた人から、 紹介動画を作ってみたらどうだろうというアイディアを頂いたので、 あーそれはいいね、という事で紹介動画を撮りました。

動画は実機で動かしながら Reflector を使って Air Play で Mac に表示しつつ、QuickTime で録画したのですが、読み上げを行っていない時などは無音で寂しい感じだったので BGM を入れようと思いました。その時、この動画はニコニコ動画にもアップロードするんだぜ、という事でせっかくだから ニコニコ技術部で使われている「てってってー」っていう音楽にしようかなーと思ったのですが、 どうやらあの「てってってー」という音楽は

  • 元の動画(これ?) には「使って良いですよ」とは書かれていない
  • そもそも てってってー は JASRAC の管理曲なので何かいろいろありそう

であり、なにやらライセンス的に使う事ができそうになかったので、 煉獄庭園 さんの曲を使わせてもらうことにしました。煉獄庭園さんには本当に感謝です。

ということで出来たのはこれ(冒頭にあるものと一緒)です。


ことせかい: iPhone 小説家になろう 読み上げアプリの紹介 - YouTube

振り返り

とりあえずやり切るのを目的にした

まずなによりも、5年経っても何のアプリもリリースしてないとかいくらなんでもやる気なさすぎでしょうというわけで、 まずはアプリが AppStore に並ぶまではやりきろう、ということを決意したのは良かったと思います。

毎日一時間

やりきろう、と思っただけでは全然駄目なんだろうな、というのは5年も何もできなかったのでよく知っていました。 ということで、毎日最低でも一時間はこのアプリのために時間を割く、という事を決めました。

もちろんその一時間を守れなかった日も無いわけではなかったですが、ほとんどの日は一時間は時間を割きました。 ただ、一時間必ずみっちりやらないと駄目、とかやるとやる気が無い時にすごく負担になるので、 一時間机に座っていればよい、程度のルールのつもりでやっていました。 実際、一時間机に座って AVSpeech関連 の事を書いた WebPage を眺めていただけ、という日などもありました。

とはいえ、やる気が無くても一時間座っていればまぁ何かは進むわけで、 一週間もすると7時間はかけているのでだいたい何かが進んだなと感じられる位には進んでいて、 一歩一歩着実に「できてきた」のを感じられたのは良いと思います。 まぁ、何もできていなくても「一週間で七時間も使ったよ」と考えるだけでも「俺commitしてる感」はありました。

あと、土日などの時は朝にその一時間を持ってくるように心がけました。 これが守れなかった時はだいたい23時頃になってやっと「ヤバい一時間やってない」と始めるので、 せっかく長い時間のとれる休日が(ことせかい の開発としては)無為に過ごしてしまった、 ということになることが多かったためです。

また、朝から「一時間」をはじめているとだいたいは 「まだ時間かけられるんだから平日やってないこの時間かかりそうな作業でもやるか」熱が 沸点を超えて始められる、という事がよくありました。何も手に付かない気がしていても、手につけてしまうとなんとなくできるものなんだなぁというのを思います。

結構辛い。遊びたいし無駄に時間使いたい

とはいえ、この毎日一時間というのを決めてやっているのは振り返ってみると結構辛かったと言って良いと思います。 実際、今までいっぱい遊んでたんだなーというか、積みゲーがすごく増えましたし、 無為に時間を過ごしてたというのは安らぎの時間だったんだなーと思う位、 ぼーっとしてる時間が全然ないなと辛さを感じたりしました。

その他

github

今回のプログラムは github に登録してみました。 https://github.com/limura/NovelSpeaker

元々無料のアプリにするつもり(お金を取るとなると多分低いクオリティの物では自分が納得できないだろうと思った)であったので、 無料なんだったら source も公開するよね、source を公開するならいまどきなら github だよね、 という流れで github に突っ込んだ、という感じです。

結果的には github pages が使える事でアプリの申請時に必要なサポートページを用意することができたり、 「小説家になろうの18禁の小説は読めないのかよ」と言われても、 「えっ、github に source あるから自分で書き換えれば?(でもSiriさんの棒読みで18禁とか上級者過ぎないか)」 という切り返しが使えるから安心だよねーとか思ったりで、 github にして困ったこともないし、良かったなーと思っています。

Siriさん読み間違い多いなぁ

ことせかい という名前からもわかるように、 Siriさん(のエンジン) は結構読み間違いをします。 例えば、

  • 異世界」は「ことせかい」
  • 「美味い」は「びみい」
  • 「己」は「つちのと」

といった感じです。これらは読み上げの変更用のテーブルでなんとか回避できないかな、 と思って実装はしたのですが、 「己」を「つちのと」と読み上げてしまうものを 変換テーブルに「己」→「おのれ」と登録してしまうと、 「自己」を「じおのれ」と読み上げられてしまうようになってしまって困ってしまいます。

ただ、Siriさん(のエンジン) は文脈によって読み替える事をしているようにみえる (例えば「辛い」を「からい」と読んだり「つらい」と読んだりする)ので、 多分 Siriさん側 が賢くなれば解決するんじゃないかなと思って案外放置してるだけで直ったりしないだろうか と期待していたりします。 といっても、iOS 7 の Siriさん と iOS 7.1 の Siriさん では読み上げの声質が全然違って 7.1 の方が流暢なのですが、 自分が使っている AVSpeechSynthesizer では iOS 7.1 の上でも iOS 7 時代の Siriさん が読み上げてくれているので、 案外駄目かもわかりませんが。

ということで、他の音声読み上げエンジンではどうなのだろうと思ったのですが、 Acapela を使っているらしい VoicePaper でも結構 読み間違えている ので今の時代はまだまだこれでもしょうがないのかもしれないですね。

Objective-C びっくりしたこと

Objective-C はまずは のあたりで書き方がわからなくていろいろビビっていたのですが、 とりあえずだいたいは書けるようになってきたあたりで気になった事がいくつかありました。

NSArray, NSDictionary には object の id しか入らない

ということでした。これは標準の NSなんたら系 のものでも NSArray* を返してくるので、 その配列の中に入っているものが何のobjectなのかを自分が把握していないとコンパイルエラーにしてくれない、 というのが気になってしょうがない感じでした。せっかくのコンパイル言語なのになぁ。 ジェネリクス的なものが無いみたいなのでそうなってるのかなぁ。と思っています。

ARC結構便利

Xcode での Objective-C では ARC(Automatic Reference Counting) というもので object のリファレンスカウンタをコンパイラが自動でやってくれる仕組みがあります。なのでだいたいはメモリ管理についてあまり気にせずに書くことができてイイカンジでした。

blocks便利だけれどちょっと落とし穴が

Xcode での Objective-C では blocks というもので thread周り が少し楽に書ける仕組みが入っています。これは結構便利に使えるのですが、一つの dispatch queue を作って別の thread から同じ dispatch queue で実行すれば全て同じ thread で動くのかな、と思っていたらそういうわけではなく実行される thread 自体はそれぞれの元の thread で実行されるのだ、というのに気付かずに不思議なハングを経験してデバッグが大変でした。

具体的には CoreData の save周り は一つの thread でしか行ってはならず、別thread から呼び出すなとあったので、save用 の dispatch queue を作ってそこで実行しているので大丈夫だろうと思っていたらそんなことはなかった、という感じです。

生活に余裕を

今の職場は去年の末になってからなのですが、 iOS Developer program に入っていて、プログラムがかけていなかったのはだいたいは 前の職場 に居た時と時を同じくしている気がします。

これは別に前の職場がマズいと言いたいわけでもないのですが、 前の職場と比べると今の職場は切羽詰まった感じが少なく、心にかなりの余裕ができました。 そういう所は思ったより良い働きをしたらしく、 前の職場の時は「自由な時間を作るのが大変」と思っていたのが、 今の職場に移ってしばらくして心の余裕に慣れてきた頃から 「自由時間結構あるじゃん少し位何かに使おう」と思えるようになったという感じです。そういう意味では前の職場では多分作りはじめようと思えなかったと思います。

もちろん、前の会社でも、同人ソフトとかを自力で作ってコミケで売ってる人とかもいるわけで、 単に自分がやる気が出てなかっただけなのは間違いないのですけれども。

ことせかい を思いの外使っている

歩いている時の暇つぶしのつもりで作った ことせかい ですが、最近では歩いている時だけでなく、

  • 風呂場で読み上げてもらいながら体洗ったりしている
  • 日課になってしまっているロードラ (iPhoneで遊んでるスタミナ制のゲーム) をいじりながら聞いてる
  • 家から駅までの歩きの道中だけでなく電車内でも聞いてる
  • ラーメンや昼飯を作る時など、手を動かしてるだけで頭を使ってない時にはだいたい聞いてる

と、結構中毒的に使っているような気がします。

小説家になろうの作家さんたちありがとうございます

元々誰も作っていないから、という理由で 小説家になろう の小説の読み上げアプリを作っていたというのはあるのですが、実際に作って使っている時に読み上げられる小説がつまらなかったら多分作り上げる元気が沸かなかっただろうと思います。そういう点において、小説家になろう の小説は面白かったです。小説家の人達には感謝致します。

 

また、ことせかい を使っていただける方がおられましたら、本当に感謝です。自分が欲しいなぁという事で作り始めたようなものですが、気に入っていただければ幸いです。修正案やバグ報告などは github でも AppStore の評価でも書いていただければと思います。ありがとうございました。