2023年1月17日にマクドナルドの公式Twitterよりポテトを揚げる音、通称「ティロリサウンド」が配布されました。
ツイートを見る感じだと好きに使って良さそうなので、何か面白いことに使えないかな...と考えていた所、こんなアイディアを思いつきました。
- ターミナルからコマンド経由で「ティロリ♪ ティロリ♪...」と流す
- マックのポテトが食べたくなる
- リモートワークで家に引きこもってるけど、マックに行かねばならなくなる
- 外出することで運動になる
風が吹けば桶屋が儲かるという言葉もあるので、さっそくelixirでティロリサウンドを再生するコマンドを作ってみました。
作ったもの
まずは完成品をご覧ください。
ターミナルに「poteto」と打ち込むと「ティロリ♪ティロリ♪...」とサウンドが再生されます。そのまま、30秒程度、サウンドが再生し終わるまで放置するか、「stop」と打ち込むことで再生を停止させることが出来ます。
では、どのように作ったかを順に紹介していきます。
プロジェクト作成
まずはmix
を使って新しいプロジェクトを作成します。今回はコマンド名に合わせて「poteto」としました。
$ mix new poteto
* creating README.md
* creating .formatter.exs
:
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd poteto
mix test
Run "mix help" for more commands.
処理の実装
ではlib/poteto.ex
にメインの処理を記述していきます。今回はescript
を使って、ビルドしてコマンドとして実行できるようにしたいので、main関数を定義します。
main関数は1つ引数を受け取る必要がありますが、今回はターミナルからコマンドを受け取る予定はありませんので、アンダーラインを付与して使用しない変数だと明示しておきます。
defmodule Poteto do
def main(_args) do
end
end
サウンドの再生
調査の結果、自力で頑張ってサウンドを再生させるのはかなり大変...ということが分かりました。
「なんとかならないかな?」と思案していた所、Macにはafplay
という指定したファイルパスの音声ファイルを再生するコマンドがあることを発見しました。
このコマンドをSysytem.cmd
経由で呼び出します。再生を停止させる場合は同様にSystem.cmd
経由でkillall afplay
で停止させます。欠点としてafplay
で再生した音声が全て停止してしまうという問題がありますが、今回は目を瞑ります。。。
def play_sound(sound_path) do
System.cmd("afplay", [sound_path])
end
def stop_sound do
System.cmd("killall", ["afplay"])
end
OSによる処理の分岐
Macの場合にはafplay
コマンドを使用しますが、他のOSでは別のコマンドを使用することを想定して、パターンマッチで処理を分岐できるようにしました。
もし興味があれば、レポジトリを公開しているので、別OSの実装のPRを投げて頂けると嬉しいです。
defp command_exec({ :win32, _ }, _function, _args), do: :pass
defp command_exec({ :unix, :darwin }, function, args), do: apply(OS.Darwin, function, args)
defp command_exec(_, _function, _args), do: :pass
モジュールはマッチしてOSの情報に合わせて、対応するモジュールの関数を動的に呼び出すようにしました。今回はdarwinに対応するOS.Darwin
が使用されます。
defmodule OS.Darwin do
def play_sound(sound_path) do
System.cmd("afplay", [sound_path])
end
def stop_sound do
System.cmd("killall", ["afplay"])
end
end
あとは「stop」させるためにplay_sound関数の呼び出しを別プロセスで行い、ユーザー入力を受け付けるreceiver関数を定義してあげれば出来上がりです。
defmodule Poteto do
def main(_args) do
spawn(fn -> command_exec(:os.type(), :play_sound, ["/Users/takamizawa46/sounds/poteto_sounds.mp3"]) end)
IO.puts("🍟「再生を停止したい場合は 'stop' と入力して下さい」")
receiver()
end
defp receiver do
case IO.gets("") do
"stop\n" -> command_exec(:os.type(), :stop_sound, [])
_ -> receiver()
end
end
defp command_exec({ :win32, _ }, _function, _args), do: :pass
defp command_exec({ :unix, :darwin }, function, args), do: apply(OS.Darwin, function, args)
defp command_exec(_, _function, _args), do: :pass
end
実行ファイルの作成
音声ファイルのファイルパスを絶対パスにしたのはビルド後にPATHを通した際に、ファイルが参照出来なくなるエラーを確認したためです。合わせてビルド出来ないか試しましたが、そういったオプションは自分が調べた限りでは発見出来ませんでしたので、この方法を採用しました。
あとはmix escript.build
を実行して、実行ファイル(poteto)を作成します。これでターミナルから./poteto
とすればティロリ音が再生されます。お好みでPATHを通して、どこからでも実行できるようにすれば完成です。
bashᕙ( ˙-˙ )ᕗ takamizawa46@ ~/elixir/poteto $ ./poteto
🍟「再生を停止したい場合は 'stop' と入力して下さい」
stop
コードの全体像はレポジトリをご覧ください。
最後に
思いつきのネタでしたが、無事に実装出来てよかったです。
特にサウンドを再生するということはやったことがなかったので良い勉強になりました。今回はafplay
という手段を選択しましたが、もし他にアイディアや知識をお持ちの方がいたら、ご教授頂ければと思います。
普段は個人ブログにリモートワークやチームビルディング、プログラミング言語などの記事を書いていますので、ぜひご覧ください。
では、ティロリ音を聞いてポテトが食べたくなったので、マックに行ってきます🏃♂️🏃♂️🏃♂️