ソフトウェア開発に限らず、自分から裏側とか経緯を話すのはいいわけっぽくなってしまうので好きではないのですが、オープン化するにあたって共有しておいたほうがよい部分もあるかなと思えてきたので、一度振り返ってみようかなと思います。
SugarCape開発の経緯
[nicodo]sm8046413[/nicodo]
開発の経緯はニコニコ動画にあがってるので、こちらを見ていただくのが手っ取り早いですが、三行でまとめると
- 夏休みで暇だったので、Macで歌唱合成ソフト作ったら面白いかも、と考えてニコニコ動画に動画をアップロードする。
- 声のサンプルとかアイコンとか色々なものを動画を見ていた方が送ってくださったりで開発が加速して、SugarCape Mac版とiPhone版のα版をリリース。
- プログラムの最初の設計が悪かったり、会社人になって時間がとりにくくなったりらじばんだりで停滞なう。
という感じです。
基本的なアイデア
実は当時すでにリリースされていた初音ミクもUTAUも触れた事がなく、歌唱合成がどういう技術かも正直よく知らなかったのですが、
日本語だったら単純に五十音を全部サンプリングして、ピッチを変えて再生すればそれっぽく聞こえるんじゃね
という安易な発想がSugarCapeのベースアイデアです。もう少し実装レベルでの話だと、
単純にサンプラーを作って文字に応じてサンプルを切り替えればいいんじゃないの
ということです。
プロトタイプの開発
どうでもいいことですが、僕の中にはプロトタイプ作成について二つのルールというか考えがあって、歌唱合成ソフトもこれにそって開発を始めました。
1. その日のうちに面白いプロトタイプができなかったらボツにする
それくらいの規模で開発できるものでないと個人では作っていけなそうだし。
2. 最初のプロトタイプは設計に拘らずただ急いで作る
大学の講義で誰か(忘れた)から聞いた話なのですが、
- 情報・経験が限られている中最初から良い設計をするのは難しい。従って最初のプロトタイプは大抵失敗する。
- 最初のプロトタイプでの反省を生かして、ゼロから作り直すと大抵対極に行き過ぎてバランスの悪い設計になる。
- 二つのプロトタイプの反省を生かして、ようやくバランスのとれたよい設計の成果物ができる。
といったような内容で、自分自身の体感的にもこれは結構正しいなと感じていて、であれば最初のプロトタイプは設計に悩むよりとにかく早く動くものを作って、後で再設計しようという考えで進めています。
で、今回はこの記事を書いたのは、随分時間がかかってしまいましたが最初のプロトタイプを反省して二個目のプロトタイプを作り始めるタイミングに到達したからです。
歌唱合成ソフト開発の要素
歌唱合成ソフトの開発は大きくわけて、下記の三つの要素があります。
- 歌唱合成アルゴリズムの実装(音声処理部分)
- 音声素材の準備
- エディタの実装(エディタ部分)
SugarCapeの開発では、幸い動画を投稿したら直ぐにひろせぬこさんが音声素材を提供してくださったことで音声処理部分とエディタ部分の実装に専念することができました。
SugarCape開発の反省点
高い依存性
SugarCapeの開発で途中から一番大きな問題となったのは、構造の分離をしっかりと行わなかった点です。音声処理部分とエディタ部分の構造分離、エディタ内でもMVCの分離を適切に行えなかった結果、様々なクラス間で複雑な依存性が生まれてしまい、機能追加もバグ修正も大変、ユニットテストを書くのも難しく、テストも書けないからリファクタリングも難しい、というまあありがちな展開に。
この問題は割と早い段階で兆候が見えてきていて、開発後一ヶ月くらいたったタイミングで一度大規模なリファクタリングを敢行したのですが、単純にその頃はまだ設計力が足りておらずそこまでよい設計に直すことができず、結局この依存性の高い設計による問題は最後まで尾をひくことになります。
早すぎる最適化
最初のプロトタイプであるSugarCapeの開発を行う数ヶ月前に、iOSで楽器アプリケーションをいくつか制作・リリースしていたのですが、当時のiPod touch 第一世代、iPhone 3Gはリソースが限られていたこともあり、音声処理の最適化にはかなり苦労しました。キャッシュを効率化したり部分的にアセンブラコードを直接書くなど、かなり泥臭い方法で対応せざるを得なかった経験があり、SugarCapeの開発でも音声合成部分は初期の段階からなるべく軽く動作するように開発をしてきました。
この方針は失敗で、コードの可読性保守性が低下して開発ペースが犠牲になった上、時間があいてしまうとコードをいじる心理的ストレスの増大につながってきました。しかも、iOSと違ってMacでは音声処理にある程度リソースを使っても全然パフォーマンスには問題がなかったので、本来最適化する必要がなかったのです。すくなくとも今までは。
まさにホーアの言う「早まった最適化は諸悪の根源」そのパターンです。
郷に入れば郷に従え
仕事でiPhoneとAndroidアプリの開発を行ったり来たりしていても思うのですが、プログラムを書くときにはそのプラットフォームにおけるデザインパターンを尊重するのが一番です。その方が最終的に実装しやすい場合が多いということもありますし、UIはパターンから外れた実装をしてしまうとユーザにとって使いにくいものになってしまうことがあります。SugarCapeは僕にとって最初のMacアプリケーション開発だったのですが、パターンを知らないうちに勢いではじめてしまったことで、非効率的な実装になってしまいました。
Cocoa Document-based Application
OS XのCocoaフレームワークとiOSのUIKitフレームワークは似ている部分もあるのですが、基本的にシングルウィンドウ前提のUIKitとマルチウィンドウ前提のCocoaではアプリのデザインパターンは結構違っています。SugarCapeのエディタ部分は、Cocoa Document-based Applicationというテンプレート(パターン)を使って実装すれば本来一般的なOS Xアプリと同じような操作性を最小限のコードで実現できたのですが、それを知らずに開発をはじめてしまったことで、結構無駄な実装をしてしまいました。残念ながら一度プロトタイプを作った後でDocument-based Applicationに置き換えていくのはかなり面倒で、これも新しく作り直す動機の一つとなりました。
CocoaにおけるMVC
MVCというデザインパターンはCocoaに限らず、広く普及した考え方だと思うのですが、実際のコーディングスタイルは言語・フレームワーク・人によって結構差異があると考えています。モデル・ビュー・コントローラそれぞれに役割を分担させるというのが基本的なアイデアですが、例えばRuby on Rails等はDRYとの兼ね合いもあってかモデルが能動的な振る舞いをするような実装が珍しくないように思います。CocoaはMVCをわけて実装することを前提として色々な機能が用意されているので、その部分の思想を理解せずに実装を進めていくと、これもまた不要で不自然な実装がどんどん増えていく事になりました。
もちろん実際にSugarCapeが動作しているように、これらの点を無視してもアプリケーションの実装は可能です。ただし、MacのようにOS側で操作に統一性を持たせるような工夫が用意されているOSを使っているユーザの方には、そこから外れた挙動・操作感は使っているうちにストレスになります。実際にα版を公開した後もその部分についての要望は多く、自分自身も開発しているうちに強く感じるようになってきました。
よほど大きな理由がなければ、変なところで独自の挙動を発生させないように注意するべきですね。
まとめると
- 音声処理部分とエディタ部分の連携は疎な実装にしなければならない。でないと規模が大きくなってきたときに身動きがとれなくなる。
- 先入観で最適化をするべきではない。開発ペースと保守性がが下がるだけ。
- Cocoaアプリのデザインパターンはどんどん取り入れるべき。特にUIはパターンから外れるとユーザにとっても使いにくい。
歌唱合成エンジンの実装
上の反省点は歌唱合成の出力というよりは、ソフトウェア開発全体を見た上での反省点でした。上述の通り、音声処理部分とエディタ部分が密になってしまったことが、コア部分となる歌唱合成エンジンの開発を進めていく上での大きな足枷となってしまっていましたが、歌唱合成エンジンの実装についても触れておきます。
サンプラーの実装
最初に書いた通り、SugarCapeは基本的には声に特化したただのサンプラーです。サンプラーとしての機能の実装が最初のステップです。
ピッチの変更
ピッチの変更の原理はテープを高速に再生させると音が高くなる効果と同じです。「ド」の高さで録音した「あ」という音を1.5倍のスピードで再生させると「ソ」(に近い音程)になります。ただし、単純に再生速度を変えるだけの実装ではフォルマントが変化してしまい、いわゆる「コ○助」のような声になってしまいます。
これを解決させるには一般的には次の用なアプローチがあります。
- フォルマントを補正する。フィルタをかけて、元のフォルマントに近い形に補正する
- マルチサンプル。音階ごとにサンプルを用意する。「ド」には「ド」用の「あ」、「レ」には「レ」用の「あ」…。
- その他。元のサンプルを解析した上で、シンセ的な方法で再合成したり、など。
技術的にはマルチサンプルが一番簡単です。そもそも、人間の声の場合低い「ド」と高い「ド」では元々フォルマントが一致しないので、単一のサンプルのフォルマントを保持したままピッチを変更したとしても自然に聞こえません。なので、SugarCapeでも最初にマルチサンプルに対応させました。ただし、素材の録音には限界があります。範囲を2オクターブに限定したとしても、単純計算でも24半音 * 50音 = 1200音。
SugarCapeの開発の過程で、実際に自分でも知人に協力してもらって何度か声のサンプルを用意しましたが、1音階分とるだけでもかなり大変です。(実際には50音以上ある)人間の身体なので、録音が進むにつれて喉のコンディションが変化してしまいますし、録音の最初と最後で声質が変わってしまうとサンプルとして使ったときに不自然になってしまうので、声素材の録音はかなり難易度が高いです。なので、この問題はマルチサンプルだけでは解決できません。
従って、マルチサンプルとフォルマント補正を併用して自然な音を目指していくことになります。SugarCapeでもマルチサンプルの対応後にフォルマント補正を適用したプロトタイプを何度か作成しているのですが、どうもよい効果をうむ状態に調整ができていないのが、音質がよくない一番の原因です。
ちなみにマルチサンプルとフォルマント補正は割と古典的な手法で、最新のサンプラーではこれらの手法に加えて、独自のアルゴリズムを用いてより自然にピッチを変更するための工夫がなされているようです。(ここらへんの詳細なアルゴリズムについてはよく知りません)
ストレッチ
サンプラーに必要な機能はピッチの変更だけではありません。音の長さの変更(ストレッチ)も必要です。
ドラムのようなアタックの重要度が高くサステインの重要度が低いサンプルであれば気にしなくてもよいケースもありますが、声のようなサンプルではサンプルを再生する長さがコントロールできないと使い物になりません。例えばピッチをあげるために2倍の速度で再生すれば、半分の時間で再生が終わってしまいます。10分くらい「あ」と言い続けるサンプルを用意すれば力技で解決できそうですが、残念ながらそういった方が友達にいないためこの手法は使えませんでした。
この問題に対する簡単なアプローチはサンプルを繰り返し再生することです。ただし、例えば「じゃーーー」というようなサンプル全体を繰り返して再生すると「じゃーーーじゃーーーじゃーーー」みたいな感じになってしまうのでサンプルの真ん中あたりの部分だけを繰り返して「じゃーーーーーー」となるようにします。
また、ただ波形の一部分をつなげて繰り返すだけだとつなぎ目が不自然になってブチブチいうので、SugarCapeではつなぎの部分をクロスフェードさせたり、音量のエンベロープを調整してブチブチを低減するようにしています。
声によるサンプルのスイッチ
普通のサンプラーと少し違うのは、発音に応じてサンプルを切り替えることです。これについては音声処理的な話はあまりなくて、メモリの管理とかコーディング上の話なので省略します。
SugarCapeとしての限界
上に書いた部分までが基本的なSugarCapeの実装です。未実装な部分もありますが、上に上げたような機能は時間をかければいくらでも改善できます。
ただ、どうも面白くないのです。ということで、音痴機能だったり声量変化などの独自の機能をつける方向性に進んでいったのですが、そうする上で先に書いた根本的なソフトウェアとしての設計の悪さによる弊害が目立つようになってきてしまい、この設計のまま開発を続行するのは嫌だなという結論に至りました。
SaltCase
というわけでゼロから作り直したのがSaltCaseです。
SugarCapeの反省を生かし、
- 音声処理部分とエディタ部分の連携を疎にする。
- Document-based Applicationテンプレートに従い、可能な限り一般的なMacアプリケーションと同じ操作感を求める。
という方向性でプロジェクトの根幹部分を設計し、さらにARC、GCDといった今時のMac OSの最新技術を取り入れて、変な最適化をせずに保守性とパフォーマンスを得られるようにしていこうと考えています。
オープン化について
yokoe/saltcase
SugarCapeは特にソースコードをオープンにしていなかったのですが、SaltCaseはgithubでプロジェクトをオープンソースにしています。理由は色々あるようでそんなに大きなものはないのですが、SugarCapeを作っていく過程でソースコード見てみたいという要望も何件かいただいていた事だったり、本当にゼロから実装してるの?っていうようなコメントをもらったりすることもあったりして、特に自分としてもソースコードを隠したい理由もないのでオープンにすることにしました。
もともとSugarCapeはただ実用的なソフトウェアを作るのが最終的な目的ではなく、作る過程も含めてなんでもいいから面白い・楽しいものにするのが目的なので、オープン化することでより新しい楽しみ方が増えればよいかなと考えています。
その中で、ただソースコードやコミット履歴を眺めるよりは、少し前提知識があった方が見ていて面白いんじゃないかな、ということで長くなってしまいましたがこのようなポストを書いてみました。どうも長くなりすぎてしまったので、後で少しずつ読みやすいように直していきます。
もちろんオープンにするとはいえ、従来通り動画とα版のリリースは続けていきます。最初からニコニコ動画ドリブン、あるいはTwitterドリブンで開発されているプロジェクトなので。
なお、記事冒頭でらじばんだりを使ってしまいましたが、実を言うと未だに元ネタをみたことがありません。
DTMマカーの夢、叶えてください。