PythonとVLCで音楽クイズを作って改善してきたのを振り返る

この記事は[N高等学校(1) Advent Calendar 2020](https://qiita.com/advent-calendar/2020/n-highschool) 3日目の記事です。
[去年](https://qiita.com/KjumanEnobikto/items/ae9458d0be283db1887b)に引き続き、今年も参加させていただきます。
私は現在3年生で、通学コースに通っています。今年も去年と同じくプログラミングの時間を使ってプログラムを組んで~~遊んで~~いました。
来年は[S高等学校](https://nnn.ed.jp/high_school_feature/s_high_school/)とのPV数・いいね数対決とかするんですかね？見ものです。

この記事では、タイトル通りPythonとVLCで作って改善した音楽クイズの話をします。~~去年よりちゃんとアドカレっぽい~~
すでに[作ってみた](https://qiita.com/KjumanEnobikto/items/0a87d45a3eb9c2d387a8)とか[改善してみた](https://qiita.com/KjumanEnobikto/items/3df4448cc5c6fb45f6f7)とかの記事は投稿しているのですが、今回はより詳細に思い出しつつ書いてみようと思います。

## 音楽クイズを作るきっかけ
2020年4月、世は緊急事態宣言の真っ最中。
ゲームセンターも多くが営業を停止してしまい、音ゲーマーとしてはなかなか辛い時期でした。
そんな中娯楽を探していると、ふと音ゲーのサウンドトラックが目に留まりました。
**このCDを使って、イントロクイズをやったら面白いのでは？**と考えたのです。
最初は親に頼み込んで、スマホでランダムに音楽を再生してもらいました。
可能ではありましたが、第三者がいなければプレイできないため、やりたいときにやれないという問題がありました。
そこで、プログラムを組んでそこを自動化してしまえば良いのではないか？と考えたのが始まりでした。

## どうやって作るか考える

### プログラミング言語
これに関しては、最初からPython一択でした。
個人的にPythonが一番使い慣れた言語だったためです。

### 音楽を再生する方法
適当に[検索](https://www.google.com/search?q=Python+%E9%9F%B3%E6%A5%BD+%E5%86%8D%E7%94%9F)してみただけでもそこそこ方法がありました。
その中で、私は[VLC](https://www.videolan.org/vlc/index.ja.html)を使うという手段を取りました。

#### なぜVLCなのか？
私はいつもCDからflacで取り込んでいるのですが、それ以外にもGoogle Play Musicやレコチョク等で購入したmp3やwav、m4aなどがありました。
wavやmp3はともかく、flacやm4aが再生できるライブラリは多くはなさそうに思えたのです。
その点VLCなら対応しているコーデックも豊富で、再生できなくなるということはないだろうと判断しました。
今考えればクイズ用の音楽はmp3に変換しておくとかしたほうが楽だった気もします。

## 仕様を決め、作ってみる

最初は以下の通りに作ることにしました。

- 同じディレクトリから音楽ファイルを1つランダムに選ぶ
- 10秒間再生したあと、曲名を入力させる
- 3文字以上合っていたら正解と見なす
- 完全に合っていたらパーフェクトにする
- ギブアップコマンドを用意しておく

この通りに作ったソースコードは[こちらのGist](https://gist.github.com/Qman11010101/cd328dd99e2b9c2e0ffd5005dd2efa4d)に載せてあります。

## 実際に使ってみて、改善する

友人に協力してもらい、Discordで通話を繋ぎながらイントロクイズに参加してもらいました。
なかなか盛り上がった一方で、[こちらの記事](https://qiita.com/KjumanEnobikto/items/3df4448cc5c6fb45f6f7)にある通り、改善すべき点もいくつか浮かび上がってきました。

1. リプレイ不可能
1. 大文字/小文字が区別される
1. 正解表示後一瞬で始まってしまう
1. イントロの無音区間が長い曲がある
1. 何度かやると覚えてしまう
1. 判定バグがある
1. タイトルが長い曲でも3文字で正解になってしまう

これらを改善するため、ギブアップやリプレイといった機能をコマンドとしてまとめたり、判定を3文字から1/3にしたりといった工夫を重ねました。
また、4番目の問題を解決するため、イントロクイズをやめて曲のランダムな部分を再生するようにしました。

これらが解決されたコードは[こちらのGist](https://gist.github.com/Qman11010101/ec771a34bab5cb815ebd83c1bd203df9)に置いてあります。

## さらに改善する

### 曲名直接取りたい問題

上で紹介した記事で、「曲名を直接取得したい」と述べました。
そもそも音楽ファイルには曲名やアーティスト名といったパラメータが存在しています。
例えばWindowsのエクスプローラーでは以下のように表示されます。
![SnapCrab_NoName_2020-11-25_0-38-43_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/b960aa2c-31eb-2270-7aad-631acbf575e6.png)
さて、「[TiamaT:F minor](https://www.youtube.com/watch?v=ynwbngCuJJ4)」という楽曲が存在します。しかしファイル名に`:`は使えません。
上の画像にもある通り、ファイル名は「TiamaT-F minor.flac」となってしまっています。
ファイル名から曲名を取得するという仕様では、この曲をクイズに含めることはできません。
さて、この曲名はどうやって取得すれば良いのでしょうか？

#### Windowsのプロパティから(失敗)

ファイルを右クリックして、メニューから「プロパティ」を選び「詳細」タブを開くとこのような表示が出ます。
![SnapCrab_NoName_2020-11-25_0-47-25_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/809df3fe-99c6-09f1-4bd3-887dccb1e696.png)
ここには曲名やアーティスト名も記載されています。
これをなんとか取得できればよいのでは？と考え、取得する方法を探しました。
結果としては、[こちら](https://letspowershell.blogspot.com/2015/11/powershell_18.html)のようにPowershell経由でプロパティにアクセスする方法はあるようでしたが、Pythonのsubprocessモジュールを用いてみたものの、どこを間違えたのか結局動作せず、挫折してしまいました。

#### mutagenモジュールを使う(成功)

詳しい友人に泣きついたところ、「mutagenモジュールを使えばいいのでは」という回答が返ってきました。
mutagenは、オーディオメタデータを扱うPythonモジュールです(参考: https://mutagen.readthedocs.io/en/latest/ )。

> It supports ASF, FLAC, MP4, Monkey’s Audio, MP3, Musepack, Ogg Opus, Ogg FLAC, Ogg Speex, Ogg Theora, Ogg Vorbis, True Audio, WavPack, OptimFROG, and AIFF audio files.

サポートされているファイルの種類も豊富です。できれば最初からこれを知りたかった……。

扱うファイルは数種類あるので、[こちら](https://mutagen.readthedocs.io/en/latest/user/gettingstarted.html)を参考にして実装しました。
こちらで種類を判別しなくても`mutagen.File()`に食わせておけばいい感じに扱ってくれます。
ちなみにこの方法でアーティスト名も取得できるので、ヒントの一つに加えることにしました。

### 不正解のときにどれくらい合ってるか知りたい問題

「[Lethaeus](https://www.youtube.com/watch?v=aRnrKsuV-Uo)」という曲があります。
遊んでいるときにもこの曲は何度か登場したのですが、綴りが覚えづらいのかやたらみんな間違えます。
しかも、1文字違うだけなのに「じゃあ全く違う曲か」と勘違いして答えから遠ざかってしまうこともしばしばありました。
そこで、入力した答えと正解がどれくらい違うのかがわかるようにしたら良いのではないかと考えました。
探してみると、[difflib](https://docs.python.org/ja/3.7/library/difflib.html)というまさに探していたものが標準モジュールにありました。
さすがはBattery includedという思想を標榜しているだけあって、素晴らしい充実度だと思います。

### 文字種多すぎ問題

曲名に記号が含まれていると厄介なことになります。
![SnapCrab_NoName_2020-11-25_1-51-39_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/ff1e0722-7b9e-1f10-a4ad-87ebe6270907.png)
「♡」と「♥」は別物ですが、さすがにその違いをいちいち指摘していては楽しめなくなるのでは？と思いました。
実装としては、replaceメソッドを鎖のように繋げて数種類の文字を消したり変換したりしています。

他にも、友人の指摘を受けて正解判定を1/3から1/2にするなどのマイナーアップデートを行いました。

上に挙げた数点の改善を施したソースコードは[こちらのGist](https://gist.github.com/Qman11010101/a5f7ff7c6f29c531fcdd53ec836604de)に載せています。

## 作ってみての感想
最初は自分が楽しむために作ったものでしたが、友人と楽しんでフィードバックをもらっていくと、至らない点もだんだん見えてくるようになりました。
プログラミングをする人たちは皆口を揃えて「アウトプットが大事」と言いますが、本当にその通りだと実感しました。
制作したものは、何らかの形で発表してみるべきでしょう。
12月も始まったばかりです。まだ空いてるアドベントカレンダーに滑り込むチャンスはありますよ！
こういう機会に、ぜひアウトプットしてみてください。
それではまた2日後、[N高等学校(2) Advent Calendar 2020](https://qiita.com/advent-calendar/2020/n-highschool-2)でお会いしましょう。

明日は @Souken521 さんの「HomeKit対応機器+HomeBridge+Nature Remoで家を快適にする話」です。スマートホーム、憧れますよね。