2022年7月7日木曜日

iOSでn倍速機能をリリース(東方VGS)

東方VGSのiOS版でn倍速再生機能を追加したアップデートを審査提出しました。

実装はこんな感じです。

Androidは追従中です...しばらくお待ち下さい。

倍速設定は「Settings」の「再生速度」で 0.25倍速 〜 2.00倍速の範囲で(0.05刻みで)設定できる仕様にしました。

イチイチ Settings に切り替える操作は野暮ったいので、シークバーに再生速度(xn.nn)のプレビューを表示しつつ、そこをタップすれば Settings へ遷移しなくても再生設定(マスターボリューム & 再生速度)をカスタマイズできるようにしてみました。

要するに、動画サイトと同じようにプレイヤ上で切り替えることができるイメージに近づけました。

デザイン的には同じ設定を複数箇所に分散させるのは宜しくないこと(設定類は原則一元的にすることが望ましい)ですが、倍速再生は動画サイトではお馴染みな機能なので、UIをそれに近い形でデザインしておけば、少ない学習コストで機能を理解して使ってもらえる確率が上がるかなということで。

ただし、東方VGSの倍速再生は動画サイトの倍速再生とは若干異なります。

一般的な動画サイト(YouTubeやニコニコ動画など)の倍速再生だと、倍率を変更すると音程が変わってしまいます。

一方、東方VGSでは音程を一切変えることなく再生速度を変更することができます。

AppleMusicやSpotifyなどの音楽配信サービスでも、もしかするとこういう機能に対する潜在的なニーズはあるかもしれません。(広い括りでみれば音楽も動画と同じコンテンツなので)

ところが、音楽配信サービスだと音程が変わってしまうのは致命的(音楽クリエイターから猛反発を受けそう)なので、理想的には音程を変えずにテンポを変えることができる必要があると思います。

しかし、昨日の日記でも書きましたが、録音済みの音声波形でピッチを変えずにテンポを変えることは困難です。Logic Pro X の VariSpeed という機能で一応できますが完璧ではありませんし、CPU負荷がかなり掛かります。

仮に、音楽も動画と同様、1.5倍速再生されることが常態化してしまえば、恐らく「音程が変わってしまっても良いから倍速対応するぞ!」みたいな(乱暴な)形で実装されていくのかもしれませんが...

ただ、個人的には高速再生してコンテンツをスピーディーに消費するよりも、低速再生でじっくりねっとり楽しんでいただきたいかも。

2022年7月6日水曜日

n倍速再生機能(東方VGS)

東方VGSで倍速再生機能の導入を検討中です。

アプリ版に先立ってCLI(コマンドライン版)に以下の commit で導入しました。

https://github.com/suzukiplan/tohovgs-cli/pull/41/commits/fb5f0216aac60f10d9995de62f02552f8d186470

./tohovgs -t 75 path/to/file.mml

上記のように -t オプションで再生速度のパーセンテージを指定することができます。(上記の例なら 75% の速度で再生)

YouTube や ニコニコ動画 など、動画サイトではお馴染みの機能ですね。

ただし、東方VGS の倍速再生機能は、動画サイトの倍速再生機能と違い、倍率をイジってもピッチ(音程)が変化しません。

動画サイトの場合、2倍速再生すると全ての音が1オクターブ高くなり、0.5倍速で再生すると1オクターブ低くなります。これは、単純に波形データをそのまま伸縮しているために起こります。

ピッチを変えずに再生速度を変化させるには、波形を離散フーリエ変換して音毎の波に分解した後、周波数を変えないようにしてデュレーションを伸縮した後再合成する計算が必要になります。

Cubase Artist/Pro や Logic Pro X などのDAWのオーディオトラックでは、そういった計算方法でピッチを変えずにテンポを変えることができます。ただし、波形を完全に分解することは出来ないので、完全にピッチを変えずに再生速度を変えることができないケースが散見されます。

ピッチを変えずに再生速度を変える一番理想的な方法は、目的のテンポで楽器を演奏し直すことです。

録音された波形データではそれができませんが、ソフトウェア音源トラックの場合、プログラムが毎回譜面データに基づいて楽器をリアルタイムに演奏しているので、テンポを変えるだけでそれを実現できます。

東方VGSはソフトウェア音源方式で音声を出力しているので、ピッチを変えずに再生速度を変えることの方が簡単にできることになります。

この機能を作った目的は、東方Projectにはアップテンポな曲が多いので、コマンドで再生チェックする時にミスノートを探すのが大変なためです。(手軽にテンポを落としてチェックしたかった)

また、この機能がアプリ版でも入れば、東方VGSを使って楽器演奏の練習がしやすくなるかも?と思ったので、アプリ版にも導入する方向で検討中です。

楽器演奏をするとIQ上昇、うつ予防、認知症予防などの効果があるらしいという噂があります。

https://www.lifehacker.jp/article/160718raise_iq/

IQが上がっても多分あまり良いことなんて無いと思いますが、うつ予防や認知症予防ができるかも?というのは中々魅力的ですね。

何ならスクフェス、バンドリ、ダンカグとかでも良いかもしれません。

ただし、音ゲーだとお膳立てが整いすぎていて演奏自体が簡単(※変態譜面をクリアできるかは別問題^^;)なので、リアル楽器よりも達成感がイマイチな気がしないでもないです。私はピアノで初めて平均律クラヴィーア曲集1巻1番のフーガが弾けるようになるまで2〜3ヶ月ぐらい掛かりました...音ゲー勢に分かるように例えると、最高難度の曲をノーツ速度最低でフルコンボするようなイメージ。

最高難度の曲をノーツ速度最低でフルコンボというと絶望感しか湧きませんが、リアル楽器なら何回ミスってもゲームオーバーにならないし、テンポを落として練習することもできるので、根気よく練習すれば割と何とかなります。

という訳で、テンポを落として再生できる機能はもしかするとニーズあるかも?ということで。楽器演奏しなくても、ローテンポで聴くと音がハッキリ全部捉えやすくなるので、音楽の輪郭線がより鮮明になったりします。

2022年7月1日金曜日

お気に入り編集機能を検討中(東方VGS)

先日追加した東方VGSのお気に入り機能ですが、さっそくユーザーから「旧バージョンの頃(マイリスト)の方が使いやすかった」というご指摘をAppStore上のレビューで頂いたので、何がマズかったのかな...と考えてみましたが、お気に入り追加が曲を再生しないとできないので、リストエディット的な用途としては確かに不便かもしれません。

ある程度意図的にそうしてみたのですが、不満が出てしまった以上は改善検討します。

という訳で「お気に入り編集」機能を考え中です。

Favorites ページに【+】ボタンを追加して、

【+】ボタンを押せば纏めて編集できる感じのもを検討中です。

プラスだけでなくマイナスもできるので正確には【±】ですが、初見ユーザーが最初に Favorites ページを開いた時、リストが空っぽの確率が高いので、【+】があれば「あぁ、これで追加できるのか」という感じになり、その時【±】だと一定数「なんじゃこりゃ?」となるリスクがあるかも...ということで【+】にしてみました。

✎でも良いかもしれませんが。



2022年6月30日木曜日

Android版東方VGSでワイヤレスイヤフォンが使えない件の調査状況

ここ数日、Android版の東方VGSがワイヤレスイヤフォンを使うと再生できないという不具合の調査をしています。

https://github.com/suzukiplan/tohovgs4-android/issues/34

現象は モダンUI でのみ発生して RETRO UI では発生しません。

モダンUI と RETRO UI では内部的に音声の再生方法が異なります。

  • モダンUI = AudioTrack
  • RETRO UI = OpenSL/ES

という訳で、AudioTrack の使い方が誤っているのかも?と疑って調査をしたのですが、実装レベルでの誤りは見つかっていません。

「AudioTrackの初期化が失敗している」といった分りやすい事象が起こってくれれば良いのですが、そのような形跡はなく、疑わしいログ出力もありませんでした。

どうやら、ワイヤレスイヤフォンを接続するとAudioTrackが見た目上は正常に動くけど、バッファリングしたデータが勝手に破棄されるような挙動をしているようです。現象発生時の adb logcat を確認したところ、OS の bluetooth インタフェースと思しきものが一時停止(suspend)の状態になっている事が確認できす。

06-29 21:11:18.367  2320  2728 I btif_av : system/bt/btif/src/btif_av.cc:3098 btif_av_stream_suspend: btif_av_stream_suspend
06-29 21:11:18.368  2320  3010 I bt_bta_av: system/bt/bta/av/bta_av_api.cc:281 BTA_AvStop: BTA_AvStop: bta_handle=0x41 suspend=true
06-29 21:11:18.403  2320  3010 I bt_btif_a2dp: system/bt/btif/src/btif_a2dp.cc:131 btif_a2dp_on_suspended: btif_a2dp_on_suspended: ## ON A2DP SUSPENDED ## p_av_suspend=0x6e002963c0
06-29 21:11:18.403  2320  3010 I bt_btif_a2dp_source: system/bt/btif/src/btif_a2dp_source.cc:733 btif_a2dp_source_on_suspended: btif_a2dp_source_on_suspended: state=STATE_RUNNING
06-29 21:11:18.403  2320  3010 I bt_stack: [INFO:a2dp_encoding.cc(503)] ack_stream_suspended: result=SUCCESS_FINISHED
06-29 21:11:18.403  1123  1756 I BTAudioProviderSession: ReportControlStatus - status=SUCCESS for SessionType=A2DP_HARDWARE_OFFLOAD_DATAPATH, bluetooth_audio=0x0200 suspended

多分、アプリ → AudioTrack::write → AudioFlinger → bluetooth I/F → bluetooth earphone という流れで音声ビットストリームが流れていく筈で、suspend になった bluetooth インタフェースが音声ビットストリームを破棄しているから AudioFlinger が永久に stream end を検出できず、その結果 AudioTrack の PlaybackPositionUpdateLister が永久に発火しない(発火しないと音楽の再生が進まない)状況に陥っているのではないかと推理しました。

今回のテストで3000円ぐらいの激安ワイヤレスイヤフォンを調達したので、ハード(ワイヤレスイヤフォン側)の問題かも?と疑って、そのワイヤレスイヤフォンをiPhoneに接続してテストしてみましたが、iPhoneでは全く問題無く再生できました。

なので、ハードではなくソフト(アプリ or OS)側の問題であることは間違いありません。

幸い、OpenSL/ESは問題無く動作するので、モダンUI も RETRO UI と同様、OpenSL/ESを使うようにするという回避策もあるかもしれませんが、OpenSL/ES には別の問題があります。(2016年以前のAndroid版東方VGSでは長い間この問題に悩まされていました...)

OpenSL/ESだと何故か、バックグラウンド再生をすると音飛びが発生する仕様です。これはAndroid 8以降で発生するようになったOS側の仕様変更によるもので、Android 12でもまだ治っていません。(OpenSL/ES自体がマニアックなAPIなので多分治ることは無いと想定しています)

※RETRO UI のバックグラウンド再生を制限しているのはこれが理由です(RETRO UI は C言語で作られている関係で、C言語で使えるオーディオ・インタフェースを使わなければならないため、AudioTrackに置き換えることができません)

念の為、AudioTrack→OpenSL/ESに実装を書き換えてテストもしてみましたが、やはりバックグラウンドの音飛び問題は健在でした...

つまり、OpenSL/ESを使ってしまうと事実上バックグラウンド再生に対応できないため、対応したければAudioTrackを使うしかないというのが実情です。

なお、exo-player という Google が公開しているAndroid用動画再生ライブラリ(OSS)があって、その実装でも AudioTrack が使われています。

exo-player は Android版YouTubeでも使われているので、問題のワイヤレスイヤフォンを使ってAndroid版YouTubeで動作確認をしてみましたが、特に問題無く再生できるようです。

東方VGSとYouTubeの音声再生では、音声ビットストリームのエンコーディング形式に違いがあります。

  • 東方VGS: 22050Hz 16bit mono の raw PCM
  • YouTube: AACなど(動画のコーデックを見て切り替え)

bluetoothの帯域はかなり狭いので、リアルタイムに音声データを通信しようとすると raw PCM だと十分なバッファを確保できません。そのため、AAC等の圧縮形式でデータパケットを流す必要があるのですが、もしかするとAudioTrackはその辺を気にせず raw PCM でそのまま bluetooth にデータを流しているかもしれません。(Androidのベーシック層の設計思想は割とこういうパターンが多いので...)

その場合、AudioTrackのエンコーディングを raw PCM から AAC にして、MediaCodec で AAC エンコードしたデータをwriteしてあげれば何とかなるのかな?(OpenSL/ESの場合、そもそもraw PCM以外 buffer write できない仕様だから、bluetoothへ流す時にOS側でAACなりにエンコードしているから正常に動作している...という推測が成り立ちそうな気がします)

AudioTrackをAPI仕様通りに扱っていて正常に動かないので、本件はOS側の不具合だと言える気がするので、一旦この件の対策はせずにOS側の対策を待つしかないかもしれません。

まぁ、そんなこと言ってたらAndroidでプログラムなんか作れない訳ですが...

APIが必ずしも仕様通りに動くと思ってはならない

という訳で内心は早々にギブアップしたいところではありますが、もう少し粘って調査します。

追記: 無事回避策が見つかったので qiita で詳述してみました。(これで、同じ問題に躓く人が出てきた時、ググれば助かる...かもしれない)


 

2022年6月29日水曜日

中学2年生の時に書いた暗黒日記を二十歳になってから読むあの感じ

約7年前に書いた「東方VGSができるまで」 という記事が最近上位にサジェストされてくるので、内容が気になって恐る恐る読み返してみたのですが、中々面白かったです。

Bloggerのサジェストは直近のPVの多い順にされるようで、管理ツールでその記事のPV数を見たら7691でした。

当時と今で思想や考え方が変わっているかな?と思ったのですが、大筋では変わってないようです。

まるで成長してない...

中学2年生の時に書いた暗黒日記を二十歳になってから読むあの感じをイメージしていたのですが、良い意味で期待はずれでした。

WTC1 on VGS も完成させたいですね...東方VGSが終わったら。

まだまだ先は長いですが、健康を損なわなければ恐らく原作に追いつけると思います。(ちなみに私の年齢はZUNさんより少し若いぐらいでだいたい同年代)

若くはないので健康には気をつけてます。

先月受けた人間ドッグで、血圧(B: 低すぎ)と視力(C: 悪い)以外はオールAでした。血液検査でオールAは初めてです。(LDLコレステロールが低い体質なので総コレステロール値が低すぎてCがデフォルト)

健康に悪い(?)ことといえば、若干ワーカーホリック気味なのと喫煙習慣があることぐらい。

タバコは10年ぐらい前に半年間ぐらい禁煙したことがあるのですが、やめたら逆に体が悪くなってストレスで円形脱毛症になったりしたので、健康のために再開しました。再開したら無事毛が生えてきたので幸いまだハゲてませんが、円形脱毛症になったところから生えてくる毛が全部白髪になり若干後悔してます。「禁煙が健康」みたいな風潮があって、それ自体を否定するつもりはありませんが。

ちなみに1年ぐらい前からほぼ禁酒してるところ(誘われたら機会飲酒する程度)で、こちらは特に健康上の問題が出てなくて、逆に血液検査の数値が改善しているところから見て禁酒は健康に良さそうです。

禁酒したから血液検査が良くなった訳ではなく、ここ2年ぐらい(フルリモートになってから)毎週サイクリングをするようになったことが大きな要因かもしれません。荒川サイクリングロードをゆっくり走って自宅から18kmぐらいのところのマクドナルドでランチを食べて帰るという、健康に良いのか悪いのかイマイチ分からない謎ルーチンをほぼ毎週やっています。昔、実家で飼っていたペットの犬が、毎回ほぼ同じコースでも散歩を心待ちにしていた気持ちをようやく理解できた気がします。

あとは、人間とリアルに接するのが苦手で可能な限り家から出たくない引き篭もりタイプなので、2年間フルリモートを続けたことでストレスが溜まらなくなったことも大きいかもしれません。もう、イーロン・マスクの配下では働くことができない体になってしまったようです。

2022年6月28日火曜日

Android版のUI大幅改修(東方VGS)

 Android版のUIをiOS版に寄せる修正を入れました。

下図の左→右に変わります。

内部構造は色々改善しましたが、ユーザーエクスペリエンスが改善できたかは未知数...

内部構造的には、画面下部メニューを独自Viewから BottomNavigationView に変更してます。本当は画面下部メニューにインジケーター(カーソル)を表示したかったのですが、BottomNavigationViewにはその機能が無かったので、現在選択中のページを色の濃淡で表示してます。(色弱者に優しくないかも?)

iOS版に寄せたのは、ユーザーさんのRR(維持率)が明らかにiOSの方が高く、UI以外の機能は等価なので、これはもうUIが原因だろう...ということで。

直近5週間のRRの傾向でも、やはりiOSの方が3割増しぐらいで高い傾向があります。

海外のPlayStore(Android)上のレビューで「UIが残念」という指摘が何件か頂いていて、これが一番の決め手です。(数字は飽くまでも行動を納得させるためのオマケ)

OS標準UIのマテリアルデザインで設計することで、アプリのデザイン手間がいくらか楽になる反面、安っぽくなってしまうという根拠のない仮説もありますが、ただ、見た目のカッコ良さだけで1ヶ月後のRRに差が出てくるとは思えないという疑心も未だにあります。

デザインが共通化されていればエンハンス時のデザインコストを圧縮できますが、脳内設計なのでデザインコストはせいぜい10分間もあれば事足ります。つまり、その作業工程を圧縮するメリット・デメリットはほぼ無い※です。(だから約半年も様子見していた訳です...)

※ワンオペで企画・設計・開発をしている場合に限る

2022年6月27日月曜日

お気に入り機能(東方VGS)

東方VGSにお気に入り機能を入れてみました。

楽曲再生中に右側のハートマークを押すとファボることができ、ALLの「お気に入り」タブでファボった曲がリストアップされ、シャッフル再生とかもできる感じの機能です。

元々はAndroid版のレビューで「プレイリスト機能が欲しい」という要望を頂いたので、要望通りそのままプレイリスト(MyList)を追加しようと思ったのですが、ボトムメニューを6個以上にするとデザイン的に破綻するので迷っていました。

ユーザーさんとTwitterでやりとりしていたら「曲をお気に入りにすることができ、ALL内のお気に入りタブでフィルタすればOK」というナイスアイディアに帰結。

お気に入り機能って何かSNSっぽいですね。

ただ、SNS的な要素は無いシンプルなスタンドアローン機能です。

スマホアプリや機能を開発する時、

  1. W0 (Standalone)
  2. W1 (C/S model = Web 1.0)
  3. W2 (SNS model = Web 2.0)

という3つの選択肢のどれにしようか迷うかもしれませんが、私は迷うこと無くまずW0(Standalone)で作ります。

最近は「Web3.0」なんてものもありますね。

Web 3.0 は簡単に言うと「分散管理台帳を使った非C/Sのピア通信モデル」で、個人的には「それってそもそもWebなの?」というツッコミを入れたい点はとりあえずさておき、仮に選択肢にWeb3.0があってもW0一択です。

というより、ソフトウェアサービス全般に言えることですが、まずはアイディアが思いついたらW0の前提で作ってみることを強くオススメします。

理由はシンプルに「安い」ので。

  • サーバ不要なので初期投資が安い
  • サーバ不要なのでランニングコストが掛からない

W0でニーズが低いものは、W1にしてもW2にしても(恐らくW3にしても)ニーズが低いので、まずはW0で作ってリリースしてみてウケたら次(W1)、更にウケたら次(W2)という形で発展させるのが良いと思っています。

実際、東方VGSがその考え方で開発していて、

  • 2013〜2016年以前の東方VGSはW0(Standalone)モデル
  • 2022年以降の東方VGSはW1(C/S model)モデル

という具合にアップデートしてます。

2022年のアップデートでようやく自前のコンテンツ配信サーバ(GitHub actions + Firebase Hosting)を持ち、サーバ経由で配信した最新楽曲をクライアントからダウンロードできる Web 1.0 になりました。

東方VGSのW1までの進化は時間を掛けすぎですが、ここから W2 へ進化させるべきかについては悩ましいです。

技術やコストの面では問題無いのですが「そもそもやる意味があるのか?」と Web 2.0(SNS)に対してまだまだ懐疑的です。

ちなみに、SNSという略語は英語圏でほぼ使わなくて、英語圏では「Social Media」と呼びますが、Social Mediaは直訳すると「社交媒体」で、C2CでコミュニケーションができるWebサービスを指します。

東方VGS自体がSNSになるのは無意味で、SNSを使って上手いこと東方VGSを拡散できるなにがしかを考えるのは有用だと思いますが...

という訳で東方VGS関連のSNSリンクをSettingsに片っ端から載せてみたりしました。

TwitterとGitHubは元々載せてましたが、YouTubeとTikTokも載せてみました。もちろん、これは単なる雑なリンク貼り付け対応なので、これで「Web2.0は完璧です」とドヤろうとは思っていません。

もう少し高度なWeb2.0化の路線も考えてはいるのですが、考えれば考えるほど「Twitterで良いじゃん」「YouTubeで良いじゃん」という結論に帰結してしまいます。(例えば、アプリ内にVGS版の楽曲ランキングとか入れて、ニコニコ動画の広告みたいな感じの曲毎に運営に投げ銭できる機能とかは割とすぐに思いつきますが、その辺のインフラストラクチャは既にYouTubeで整いつつある...みたいな)

W2については自前で持つより既存のW2サービスを利用していくスタイルが、何やかや一番筋が良いというのが現在の私見です。


合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...