Credits(Frums)をマジのターミナルで
2021/04/29Creditsは,FrumsさんがBOFU2015という音ゲー関連の音楽イベントに投稿した楽曲です.2018年には商業音ゲーである ドラム式洗濯機 maimaiに収録されたことでも有名になりました.
【ラッタッタ】3/6(火)より、大人気のniconico楽曲と、マニアックなVARIETY楽曲が合わせて4曲追加されます!VARIETY楽曲は譜面もかなりマニアック!?3月もみんなでmaimai! https://t.co/xEUAWfjTPZ #maimai 【ラッタッタ】 pic.twitter.com/NUakuZZrcJ
— maimaiでらっくす公式@4/28(水) 楽曲&「BLACK ROSEちほー4」追加! (@maimai_official) March 5, 2018
この楽曲で特徴的なのは,1960〜70年代くらいのコンピュータを思わせる独特なBGA(Back Ground Animation ≒ ミュージックビデオ)です.
かっけぇ〜
私はWebフロントエンドエンジニアですが,日常的にターミナルを触りますし,DOS(PC-98)で東方旧作を遊んだり,組み込みLinuxのBusyBox上で研究作業をしたりもしていました.そういう経緯もあり,こういうレトロな雰囲気が好きです.
この動画を初めてみたときから 「これをマジのターミナル上で再現してみたい」 と思っていました.今回は,WindowsコマンドプロンプトやWindows Terminal,iTerm2,GNOME Terminalといったターミナル エミュレータ ではなく,カーネルが持っている マジの ターミナル上でCredits / Frumsを動作させることを目指します.私の身近にある本物のターミナルは,Linux TTYでした.ほとんどのLinuxディストリビューションにおいてCtrl + Alt + F[123456]で表示されるやつです.
技術的課題
「Credits / Frumsをターミナルで再現するぞ!」
とはいったものの,そもそもあの動画を本当にターミナルで表現可能なのか?という不安があります.
文字コードの問題
動画には,絵文字や罫線素片といった明らかにASCIIには無い文字が登場します.
特に 絵文字(emoji) はその名の通り日本発祥で,Unicodeによって初めて世界的に利用されるようになった…という認識です.Unicode以前につくられたLinux TTYは,これらの文字を表示できるのでしょうか?
ターミナル上で音楽を再生できるか?
せっかくなら文字を表示するだけではなくて,それに合わせて音楽も再生したいところです.例えば,デコードの必要がないWAVを再生するとしても,LinuxのサウンドAPI(JACKとかALSAとか?)を叩かなければならないはずです….
また,WAVは容量が大きいので,できればMP3を使いたいところですが,その場合はMP3デコーダーも必要になると思います….大丈夫でしょうか?
描画性能の懸念
ターミナル上でも,動画と同じく30FPS(できれば60FPS)で画面を駆動したいのですが,性能が足りるか心配です.また,画面のチラツキ等を抑えるために,ダブルバッファリングなどの工夫が必要かもしれません.
1. フォントを探す
まずは,Credits / Frumsを描画できるフォントを探したいと思います.
FrumsさんはこのテキストアートをPlaysciiというテキストベースのゲームエンジンで制作したと,Creditsの楽曲コメントで言っています.しかし,Playsciiに関して調べてみても,そのフォントの由来がどこからなのかはよくわかりませんでした.
というわけで「legacy console font」とか「IBM font」とかの検索結果を目grepしてそれっぽいフォントを探しました.その結果,十中八九,IBM PCで使用されていたVGA用フォントだろうとわかりました.
以下は,IBM PCのフォントをVGAで描画したものです.
出典: https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Codepage-437.png
例の顔や音符,罫線素片などが含まれていますね!
調べていてわかったのですが,IBM PCがサポートしている文字はASCIIではなく,それよりも広いコードページ437(拡張ASCIIとも呼ばれる)であるそうです.さらに,このコードページは1950年代のコンピュータ会社であるワング・ラボラトリーズが販売していたワープロから取ったと,1950年のビジネス誌Fortuneでビル・ゲイツが答えたそうです.というわけで,動画内に登場するアイコンは,日本がiモードを開発する前から存在するものであり,正確には 絵文字 とは呼ばないほうが良いだろうなということもわかりました.
本題に戻りますが,ではこれらの文字をLinux TTYで使用するためにはどうすれば良いでしょうか.
Linux TTYではビットマップ形式のフォントが使用されており,これは現在一般的に使用されているベクター形式のフォント(.ttfや.woff)とは異なります.したがって,例えばThe Oldschool PC Font Resourceが IBM VGA 8x16 という,まさに今回使いたいフォントをベクター形式で配布していますが,これはガチのターミナルでは使えません.
ベクターフォントからビットマップフォントを生成するということも 技術的には可能 ですが,今回は私の使用しているUbuntu 20.04に含まれるビットマップフォントから一番近いものを使うという方針にしました.そこで,「各Linuxディストリビューションのターミナルフォントに関する高品質なドキュメントを提供する」というなんともニッチな目的を持つ,The ZAP GroupのConsole Fonts from Linux and BSD Distributionsプロジェクトのドキュメントを参考にしました.
Ubuntu 20.04には374個ものビットマップフォントが含まれているため,次の条件によって採用するフォントを絞り込みました.
- フォントサイズが8x16ピクセルである
- 動画のフォントサイズを目視で数えた
- 必要なアイコンがすべて揃っている
- ☺(U+263A WHITE SMILING FACE)
- ☻(U+263B BLACK SMILING FACE)
- ♪(U+266A EIGHTH NOTE)
- ♫(U+266B BEAMED EIGHTH NOTES)
- ≈(U+2248 ALMOST EQUAL TO)
- ±(U+00B1 PLUS-MINUS SIGN)
- Ω(U+2126 OHM SIGN)
- 二重罫線素片がある
- 4段階の濃さのBlockがある
- ゼロの真ん中に中点がある
- SMILING FACEが口を開いている
その結果,FullCyrAsia-TerminusBoldVGA16.psf
が最もそれらしいだろうと判断しました.ゼロの形が少し違いますが,今回は妥協します.
出典: Console Fonts from Ubuntu 20.04
2. BGAの表現方法を決める
テキストベースのアニメーションを表現するための,CSF(Credits Score Format)という形式を定義してみました.以下は,楽曲冒頭の歌詞の表示をCSFで表したものです.
---
# MOVETO 3 14
Fun
Funding
Funding for
Funding for this
pro
program
program was
program was made
---
#
から始まる行はコマンドであり,この例では3列14行目に移動せよという意味になります.他にも文字を左右反転せよ(# FLIP
)とか,画面の描画順序(# ZINDEX
)といったコマンドもあります.
---
は小節の区切りを表します.第1小節にはコマンドと空行以外の行が8つあるので,各行は8分音符の間隔で「Fun-ding-for-this pro-gram-was-made」と表示されます.また,ここでは1行の文字列を表示するだけですが,複数行のテキストを表示したり,テキストを宣言して使い回したりする仕組みもあります.
3. CSFを再生するツールを作る
Credits / FrumsのBGAをターミナル上で再現するツールであるfrums-credits-cliをGolangで作りました.(ちょうどGolangの素振りがしたかった)
全体的な構成は次のようになりました.
画面の描画
ターミナルへの文字の描画には,gdamore/tcellを使いました.私が「画面のy行x列目に"hogehoge"
を描画したい!」というと,制御文字やら何やらを駆使して画面を操作してくれるやつです.有名なところだと,fzf や micro もtcellを使っているらしいです.
開発の初期段階で画面の描画性能を検証しました.正確な値は覚えていないのですが,Credits / Frums BGAの12倍くらいの領域を30FPSで駆動させられました.BGAの範囲だけで言えば,60FPSでも余裕で動作していました.さすがガチのターミナル,動作もサックサクですね.なお,Ubuntu上のterminatorでも性能をチェックしたところ,Linux TTYよりはカクつきますが,ほぼ問題ない描画性能でした.すごい!
また,tcellが提供する画面バッファ取り回すことで自然とダブルバッファリングが実現でき,ちらつかないアニメーションとなりました.
tcellで曖昧幅の文字を狙ったとおりに描画する
特に何も気にせずtcellを使っていると,♫ などの曖昧幅文字が全角幅として扱われ,画面の表示が崩れてしまいます.そんなときはRUNEWIDTH_EASTASIAN
環境変数を0
にしてやれば良いです.
こうすると,tcellが依存するmattn/go-runewidthが曖昧幅を半角幅として扱ってくれるようになります.
音声の再生
mp3の再生には,tosone/minimp3(mp3をデコードしてPCMデータにしてくれるやつ)とhajimehoshi/oto(OSのAPIを叩いてPCMを再生してくれるやつ)を使いました.
便利なライブラリがあって助かりました.
hajimehoshi/otoで任意の秒数スキップしたい
PCMデータを任意のバイトだけ読み飛ばせばいいです.飛ばすバイト数は,
となります.ここで,はスキップしたい秒数,は1秒あたりのサンプル数(44100とか),は量子化 バイト数 (2とか),はチャンネル数(2とか)です.単位系で表すと,
となります.スキップを行っている実際のコードも良ければ参考にしてください.
完成!
リポジトリ:frums-credits-cli-nosound
※著作権に配慮して,動画の楽曲部分にはノイズを入れています.また,リポジトリは楽曲データを含みません.
余談
アメリカ出身のアーティストであるFrumsさんは「2000年ごろの懐かしいアイキャッチをベースにした」とCreditsの楽曲コメントで言っていますが,そのサンプリング元を見つけました.
これはアメリカ公共放送社(日本でいえばNHK?)のアイキャッチらしく,この「Funding for this…」とか「…viewers like you.」というフレーズは,アメリカ人にとっては定番という感じみたいです.
まとめ
マジのターミナルでCredits / Frumsが動かせました.エモい!
また,Golangの素振りもできて満足です.
2021/05/04追記
すっ、素晴らしい!!!!
— Frums (@frumsdotxyz) May 3, 2021
Frumsさん本人に見ていただいて嬉しいです.
ありがとうございました.