Continue(s)

Twitter:@dn0t_ GitHub:@ogrew

oscl : Common Lispで作る最小限のOSC通信ツールキット

https://github.com/user-attachments/assets/7483f06c-d49a-4041-9fab-2e84f928bca1

概要

osclは、Open Sound Control(OSC)を扱うための最小限のコマンドラインツールキットです。

ターミナルからOSCメッセージを簡単に送受信できるため、スクリプト作成、テスト、OSC対応システムとの迅速な統合に便利です。さらに、異なるポートやホスト間でOSCメッセージをルーティングする軽量なプロキシ機能を備えています。

前回紹介したvispと同様、Common Lispの学習の一環として開発しましたが、クリエイティブ・コーディングやライブパフォーマンス、インスタレーションといったOSCを使ったワークフローを制御・監視するための実用的なツールへと進化しました。

基本的な使い方

send mode

> oscl send \
  --host 127.0.0.1 \
  --port 7000 \
  --address "/test" \
  --args "1 2.0 hello" \
  --interval 1000

--argsの内容から型を導出するので、引数に型指定を書く必要はありません。また--intervalの値で定期的に信号を送ることができます(Ctrl + Cで停止できます)。それから指定のフォーマットのjsonファイルからアドレスと値を読み込んでOSCを送信することも可能です。

recv mode

> oscl recv \
  --port 7000 \
  --filter "point"

フィルタリング機能がついています。上の例では信号のアドレスに「point」が含まれて"いる"ものだけを表示します。--filter -pointと書くと「point」が含まれて"いない"ものだけを表示することができます。

また、不完全ですが--raw機能もついており、これはOSCのバイト列のデータを一部表示することができます。(もともとデバッグ機能でしたが、使いたいケースもありそうなので残したという感じです。)

bridge mode

> oscl bridge \
  --in-host 127.0.0.1 \
  --in-port 7001 \
  --out-host 127.0.0.4 \
  --out-port 9000 \
  --filter "light"

受信したOSC信号をフィルタリングして、また別のホスト、別のポートへ送り返す機能です。地味にあると便利な機能です。

インストール方法

vispと同じようにhomebrewで配布しています。今回もApple SiliconのMacでしか動作検証をしていないのでご注意ください。

余談(ふたたび)

どこかで書いたかもしれませんが、6月はまるまる有給休暇を取っているので、vispだけでは物足りず、新たに oscl(一応「おすかる」と読みます)というツールも開発しました。

今回もRedditで紹介してみたのですが、vispほどの反応は得られませんでした。

というのも、osclでできることは、TouchDesignerのOSCまわりのCHOPやDATでほぼすべて代用できるため、目新しさやニッチな需要はあまりなかったのだと思います。

それでも、vispの開発を経ての次のステップとしてはちょうどよく、UDPソケットの扱いやCtrl+Cによる割り込み処理など、多くの学びがありました。

簡易なプロキシ機能を持つbridgeを開発できたのでツールを公開しましたが、ゆくゆくはrecordreplayの機能も実装したいと考えています。これが実装できれば、OSCを使ってパフォーマンスの収録・リプレイをできるようになると考えています。

約1ヶ月で2つのツールを作ることができました。おつかれさまでした。

github.com

visp: Common Lispで作る最小限のFFmpegツール

概要

vispFFmpeg の操作を簡単にするために開発したコマンドラインラッパーツールです。

もともとはCommon Lisp の学習プロジェクトの一環としてスタートしましたが、現在は実務でも十分に使えるツールへと成長し、ちょっとした動画変換やエンコードの手間を大幅に省けるようになりました。

解像度変更、音声の削除、コーデック変換、GIF生成など、FFmpegでよく行う作業を簡潔なコマンドで実行できます。出力ファイル名も自動で命名されるため、毎回ファイル名を考える必要もありません。つい最近、バッチ処理にも対応したため、複数の動画に同じ処理を行う際に、使い捨てのpythonコードをLLMに書かせる必要もなくなりました。

基本的な使い方

# 4K動画を1080pに縮小し、音声を削除する
visp --input sample_4k.mp4 --res fhd --mute
# => sample_4k_fhd_noSound.mp4

# 入力動画を半分の解像度にする
visp --input demo.mp4 --half
# => demo_Half.mp4

# 動画を逆再生する(音声は自動的にミュートされる)
visp --input intro.mov --reverse
# => intro_noSound_Reverse.mov

# 動画を4回繰り返す(オリジナル + 3回ループ)
visp --input loopclip.mp4 --loop 3
# => loopclip_x4.mp4

# 複数のオプションを組み合わせる
visp --input raw.mp4 --res hd --codec h265 --fps 24 --mute
# => raw_hd_24fps_noSound.mp4

# フォルダ内の動画すべてを一括で処理する
visp --input videos/ --mono --fps 24
# => videos/scene01_fps24_Gray.mp4
# => videos/scene02_fps24_Gray.mp4
# => videos/scene03_fps24_Gray.mp4
# => ...

基本的には、--inputで指定した動画ファイル、またはフォルダ(内の動画ファイル)に対して、指定したオプションに沿ったffmpegコマンドを生成して実行する、という流れになります。ただし、以下の2つは少し異なるモードとして実装されています。

・結合モード

複数の動画をひとつの動画に結合する機能です。出力ファイルは、最初に指定した動画ファイル(以下の例で言えばintro.mp4)の解像度とfpsに合わせられます。

visp --merge intro.mp4 scene.mp4 outro.mp4
# => intro_merged.mp4

・GIFモード

動画をアニメーションGIFに変換する機能です。私がGIFを必要とするのは、ドキュメントやチャットで気軽に雰囲気を共有したいときが多いため、解像度は横幅640px固定としています。ただし、なるべく綺麗に仕上げたいため、高品質なパレットと適切なディザリング(dither=bayer:bayer_scale=3:diff_mode=rectangle)を使用しています。

visp --gif teaser.mp4
# => teaser.gif

インストール方法

今回は初めて Homebrew で配布する形にしました。
Apple Silicon の MacBook で開発しているため、WindowsIntel Mac の方は申し訳ありませんが、ご自身でビルドしていただく必要があります。

余談(なぜ、どのように作ったか)

3月からCommon Lisp の独学を始め、『Land of Lisp』を最初から読み進めていました。ところが、8章の途中あたりで少し飽きが来てしまい、このままでは続けられないと感じました。そこで、手頃な目標を設定し、達成感を得られるような小さなプロジェクトに取り組むことにしました。

ちょうどその頃、まとまった数の動画ファイルを FFmpeg で一括処理する仕事が発生し、LLMの助けを借りてなんとか作業を終えました。しかし、毎回このような手間をかけるのは効率が悪く、「もっと簡単にできないだろうか」と考えるようになりました。

そこで、LLM を活用しながらツールの構想をまとめ、仕様をある程度固めた段階で GitHub にレポジトリを作成し、実装を開始しました。実際に作り始めたのは、4月中旬ごろだったと記憶しています。

最近は Cursor や Windsurf、Cline など、AI エージェントをフル活用したサービス開発が盛んです。

しかし今回の visp 開発は、あくまで Common Lisp の学習を目的としたものであり、それらには極力頼らない方針で進めました(バッチ処理の実装時に Windsurf を一部使用しましたが、正直なところ無くても何とかなったと思います)。

開発スタイルは、僕が生徒、ChatGPT が教師という形で一貫して進めました。

まず自分で全体の仕様をまとめ、実際にコードを書き、それを ChatGPT にレビューしてもらうという形で、visp の約9割は完成しています。 その過程では、『こういう組み込み関数がある』『この書き方は Lisp らしくない』といったアドバイスを受け、修正とフィードバックを繰り返しました。

関数レベルの設計や記述については、Common Lisp の理解がかなり深まったと感じています。 一方で、先ほど書いたようにツール全体の設計そのものは自分主導で行ったため、そこについて「Lisp らしさ」が薄かったり、そもそも CLI ツールとしてまだ洗練されていない部分はあるかもしれません。

何はともあれ、使えるツールとして世に出せたことは大きな達成感につながりました。 止まっていた『Land of Lisp』の写経も、これを機に再開できそうですし、気持ちも新たに取り組めそうです。8章といえば、まだマクロなど本格的な内容には入っていないので、まさにここからが本番というところですね。

また、完成した visp は Redditffmpeg 関連や Common Lisp 関連のサブレディットでも紹介してみました。 結果として、X(Twitter)で宣伝するよりも多くのポジティブな反応をいただき、非常に励みになりました。他の投稿も含め、実践的な知見や工夫が多く共有されており、学びの場としてもRedditは有用だと感じています。

それから、visp を作る過程で「こんなツールもあったらいいかも」と思いついたものが 2〜3個あります。そちらも、今後ぼちぼちと作り始める予定です。もっとも、いずれも visp 以上に学習目的が強く、実用性はそれほど高くならないかもしれません。

引き続き、よろしくお願いいたします。

github.com