[obsidian] vault backup: 2025-11-29 12:13:28[
All checks were successful
Build / build (push) Successful in 8m21s

This commit is contained in:
2025-11-29 12:13:28 +09:00
parent 64da0eb787
commit e1b45839ad
13 changed files with 73 additions and 45 deletions

View File

@@ -20,20 +20,19 @@ date: 2025-09-25 11:42
既存の信号処理をターゲットにした音楽プログラミング言語における問題の一つとして、コードの変更時に信号処理の内部状態がリセットされる問題がある。ディレイやフィルターは、内部状態メモリへの継続的な書き込みと読み込みを行うことで処理を実現しているが、その内部状態のインスタンスはコードのコンパイル後、信号処理を実際に始める前に0埋めで初期化されることが一般的である。
MaxMSP[@Max]やPureData[@puckettePureData1997]、SuperCollider[@McCartney2002]のJITLibにおける信号処理のように、信号処理のインスタンスのグラフ構成自体を実行中に変更できるような仕組みの場合、内部状態はキープされる。TidaiCycles[@McLean2014]やSonic Pi[@Aaron2013]のようなSuperColliderのクライアントとして実装される言語も同様である一方、信号処理を使った表現の幅はSuperColliderのプリミティブとして用意されたUnit Generatorの組み合わせに留まることになる。
MaxMSP[@Max]やPureData[@puckette_pure_1997]、SuperCollider[@McCartney2002]のJITLibにおける信号処理のように、信号処理のインスタンスのグラフ構成自体を実行中に変更できるような仕組みの場合、内部状態はキープされる。TidaiCycles[@McLean2014]やSonic Pi[@Aaron2013]のようなSuperColliderのクライアントとして実装される言語も同様である一方、信号処理を使った表現の幅はSuperColliderのプリミティブとして用意されたUnit Generatorの組み合わせに留まることになる。
Faust[@Orlarey2009]やMaxのGen、のように、サンプル単位レベルでの信号処理の記述ができるプログラミング言語の場合は、コードを一度低レベルな命令FaustであればLLVM IRなどに変換し、そのコードをインスタンス化してから実行するために、インスタンス化のタイミングで毎回内部状態はリセットされる。
同様に例えばChucK[@Wang2015]はShredという単位で信号処理インスタンスを実行中に追加、削除、更新ができるが、1つのShredが更新されるごとに内部状態はリセットされる。そのため、複数のShredが実行されていればどれか1つのShredを更新するたびに無音が挟まるようなことはないものの、Shredの中でディレイやリバーブを使用していた場合、そのディレイやリバーブのテールは更新時に途切れてしまう。
同様に例えばChucK[@wang_chuck_2015]はShredという単位で信号処理インスタンスを実行中に追加、削除、更新する形でライブコーディングを実現している言語だが、1つのShredが更新されるごとに内部状態はリセットされる。そのため、複数のShredが実行されていればどれか1つのShredを更新するたびに無音が挟まるようなことはないものの、Shredの中でディレイやリバーブを使用していた場合、そのディレイやリバーブのテールは更新時に途切れてしまう。
こうした特徴をまとめると、音楽プログラミング言語の設計には記述できる信号処理の最小単位を小さくしていくほど、コードの動的変更に対応することが難しくなるトレードオフがあるといえる。
こうした課題に対し、Reachは関数型のUnit Generatorを組み合わせて信号処理を記述する言語で、ソースコードの変更差分を解析して信号処理の内部状態を可能な限り保持する仕組み:**Incremental Functional Reactive Programming**(以下本稿ではIcFRPと呼ぶ)を提案している[@reachIncrementalFunctionalReactive2013]。この仕組みは、SuperColliderのJITLibのようなシステムと比べるとユーザーが現在の信号処理インスタンスに対して削除や追加などの命令を行うのではなく、常にその時のソースコードに望む信号処理を書けば必要な状態の更新はランタイム側が自動で担ってくれるという点で、ユーザーの演奏中の思考モデルが大きく異なると言える。
こうした課題に対し、Reachは関数型のUnit Generatorを組み合わせて信号処理を記述する言語で、ソースコードの変更差分解析して信号処理の内部状態を可能な限り保持する仕組み:**Incremental Functional Reactive Programming**(IcFRP)を提案している[@reach_incremental_2013]。この仕組みは、SuperColliderのJITLibのようなシステムと比べるとユーザーが現在の信号処理インスタンスに対して削除や追加などの命令を行うのではなく、常にその時のソースコードに望む信号処理を書けば必要な状態の更新はランタイム側が自動で担ってくれるという点で、ユーザーの演奏中の思考モデルが大きく異なると言える。
ただ、Reachによる実装としては、ソースコードの単なるテキスト差分解析では、複数の変更のパターンの可能性を絞り込めないため、テキストエディタEmacsの拡張機能として、切り取りや貼り付けといった操作の履歴を取得することで実装している。
ただ、Reachによる実装としては、ソースコードの単なるテキスト差分の解析では、複数の変更のパターンの可能性を絞り込めないため、テキストエディタEmacsの拡張として操作の履歴を取得することで実装しているため、実装は複雑なものと言える。
本稿では、筆者が開発してきた関数型音楽プログラミング言語mimiumに、IcFRPの考え方を応用したライブコーディングシステムを提案する。
本稿では、筆者が開発してきた関数型音楽プログラミング言語mimiumに、IcFRPの考え方を応用しつつ、単純なテキスト比較のみで動作するライブコーディングシステムを提案する。
以下、本論文はmimiumのこれまでの言語設計の簡単な説明と、導入される2種類の機能拡張について順番に説明する。
@@ -42,11 +41,11 @@ Faust[@Orlarey2009]やMaxのGen、のように、サンプル単位レベルで
## mimium and lambda-mmm
mimiumは、Rustに近いシンタックスを持った関数型の音楽信号処理をターゲットドメインにしたプログラミング言語である[@matsuura2021]。現在の内部実行モデルとして、値呼び単純型付きラムダ計算を拡張し、最小限の内部状態を持つプリミティブ操作ディレイとフィードバックを加えたLambda-mmm[@matsuura_lambda-mmm_2024]という計算体系を持っている。
mimiumは、Rustに近いシンタックスを持った関数型の音楽信号処理をターゲットドメインにしたプログラミング言語である[@matsuura_mimium_2021]。現在の内部実行モデルとして、値呼び単純型付きラムダ計算を拡張し、最小限の内部状態を持つプリミティブ操作ディレイとフィードバックを加えたLambda-mmm[@matsuuraLambdammmIntermediateRepresentation2024]という計算体系を持っている。
mimiumはコードを専用のVMバイトコードへコンパイルし実行する。実行モデルは、一般的なレジスタマシンの命令セットに、内部状態操作用の操作が加わったものとなる。ディレイやフィードバックで用いられる内部状態は、状態ストレージという1次元の配列領域と単一の読み出し位置ポインタを組み合わせたデータ領域に保存される。
コンパイラは、状態ストレージの読み出し位置ポインタを相対的に前後させる命令を適切に出力することで、VM実行時にはストレージの一部分をディレイ用のリングバッファとして解釈しデータを読み書きする。
コンパイラは、状態ストレージの読み出し位置ポインタを相対的に前後させる命令を適切に出力することで、VM実行時にはストレージの一部分をフィードバックの状態変数やディレイ用のリングバッファとして解釈しデータを読み書きする。
過去のmimiumでは高階関数などを使うことによって任意の数のフィルタバンクのような、パラメトリックなプロセッサを生成することもできたが、こうしたプロセッサは状態ストレージのレイアウトとメモリサイズを決定できなかった。そのため、グローバルな関数の呼び出しとクロージャ実行時に高階関数から生成される関数の呼び出しは区別され、クロージャのインスタンスに個別の状態ストレージを生成し、クロージャ呼び出し時に使用する状態ストレージそのものを切り替えることで対応していた。
@@ -56,13 +55,13 @@ mimiumはコードを専用のVMバイトコードへコンパイルし実行す
もう1つは、生成されたプログラム同士の状態ストレージのレイアウトを比較することで、可能な限り前の内部状態を引き継いだ新しい状態ストレージを計算する解析プログラムの導入である。
この2機能を組み合わせることで、mimiumは単純にソースコードを毎回最初からコンパイルし、前のプログラムとの比較を行い、新しい内部状態ストレージを含むVMを生成しインスタンスを入れ替えることで、エディタとの連携なしにライブコーディング機能を実現する。
この2機能を組み合わせることで、mimiumは単純にソースコードを毎回最初からコンパイルし、前のプログラムとの比較を行い、新しい内部状態ストレージを含むVMを生成しインスタンスを入れ替えることで、エディタとの連携なしにライブコーディング機能を実現する。
## 多段階計算
多段階計算は、型付きラムダ計算に対して、計算のステージを複数段階に分割する明示的なシンタックスを導入するものである。Lisp系言語のquote/splice機能[@lisp]のように、部分的に計算したコード片を埋め込むようなものを想定しているが、不正な値が埋め込まれないことを型システムとして保証することが特徴である。
実用的な例では、Scala 3でのマクロや、関数型組版処理エンジンSaTysFi[@suwa2024]のように、言語内DSLを型安全にライブラリとして実装することを想定しているものがある。
実用的な例では、Scala 3でのマクロや、関数型組版処理エンジンSaTysFi[@suwaMLStyleModuleSystem2024]のように、言語内DSLを型安全にライブラリとして実装することを想定しているものがある。
### シンタックス