From 81afb843aa26934d0f22113c07a71e0da43df2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=BE=E6=B5=A6=20=E7=9F=A5=E4=B9=9F=20Matsuura=20Tomoy?= =?UTF-8?q?a?= Date: Fri, 29 Nov 2024 09:49:23 +0000 Subject: [PATCH] finished writing --- paper_info.txt | 17 ++++++++++++ src/main.tex | 68 ++++++++++++++++++++++++++++++++-------------- src/ref_bibtex.bib | 10 +++++++ 3 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 paper_info.txt diff --git a/paper_info.txt b/paper_info.txt new file mode 100644 index 0000000..9953f1d --- /dev/null +++ b/paper_info.txt @@ -0,0 +1,17 @@ +論文タイトル:ラムダ計算の拡張に基づく音楽プログラミング言語mimiumとそのVMの実装 + +論文タイトル英語:An Implementation of mimium, a Programming Language for Music and Its VM Based on An Extended Lambda Calculus + +キーワード:ドメイン固有言語,音楽,信号処理 + +著者名:松浦知也 + +著者名英語:MATSUURA Tomoya + +著者所属:東京藝術大学 芸術情報センター + +著者所属英語:Tokyo University of the Arts, Art Media Center + +論文抄録(日本語論文の場合):本発表では筆者の開発する音楽のためのプログラミング言語mimiumの理論的基盤について、音楽向け言語の歴史的文脈に沿って解説する。mimiumは、リアルタイム信号処理を想定した音楽用のDSLだが、既存の多くの言語とは異なり、Unit Generatorのような特定の音楽表現に基づくプリミティブを用意しない。代わりに、値呼びの単純型付きラムダ計算に遅延とフィードバックという2要素をプリミティブとして追加した中間表現を定義することで、その言語上でほとんどの信号処理アルゴリズムを関数のパイプとして表現できる。また、Luaを参考にしたVMを設計したことで、内部状態を持つ信号処理関数の高階関数を用いた複製や、ホスト環境の埋めこみを容易に可能にしている。音楽というドメインに特化しながらも、汎用性を失わない言語の意義について議論する。 + +論文抄録英語(英語論文の場合): diff --git a/src/main.tex b/src/main.tex index f269bdb..e3b9d63 100755 --- a/src/main.tex +++ b/src/main.tex @@ -5,7 +5,7 @@ \usepackage[dvipdfmx]{graphicx} \usepackage{latexsym} - +\usepackage{url} \usepackage{amsmath,amssymb,amsfonts,amsthm} @@ -18,7 +18,6 @@ \author{松浦 知也}{Matsuura Tomoya}{TOMOYA}[me@matsuuratomoya.com] - \begin{abstract} 本発表では筆者の開発する音楽のためのプログラミング言語mimiumの理論的基盤について、音楽向け言語の歴史的文脈に沿って解説する。mimiumは、リアルタイム信号処理を想定した音楽用のDSLだが、既存の多くの言語とは異なり、Unit Generatorのような特定の音楽表現に基づくプリミティブを用意しない。代わりに、値呼びの単純型付きラムダ計算に遅延とフィードバックという2要素をプリミティブとして追加した中間表現を定義することで、その言語上でほとんどの信号処理アルゴリズムを関数のパイプとして表現できる。また、Luaを参考にしたVMを設計したことで、内部状態を持つ信号処理関数の高階関数を用いた複製や、ホスト環境の埋めこみを容易に可能にしている。音楽というドメインに特化しながらも、汎用性を失わない言語の意義について議論する。 \end{abstract} @@ -34,7 +33,7 @@ 音楽のためのプログラミング言語・環境は、Cycling'74 MaxやPure Data、CSound、SuperCollider、ChucKといった言語を代表として様々なものが開発されてきている\cite{roads2001,Lazzarini2013,Nishino2016,Dannenberg2018}。こうした言語の多くは歴史を遡ると1950年代にベル研究所でマックス・マシューズらが開発したMUSICシリーズに遡ることができる\cite{mathews1963,park2009}。MUSICシリーズはパルス符号変調という今日のコンピューター上のでの音声処理のすべての基礎でもある理論、すなわち音圧波形を時間・音圧の2次元で離散化・量子化した数列として表すことで、計算によって任意の波形を生成するという考えに基づいて、計算機で音を出した最初の事例である。 -CやC++といった汎用プログラミング言語で音声合成を行う場合も、基本的にPCMの理論に基づいてプログラミングを行うことになるが、こうした汎用の言語はメモリ管理や並行処理といったハードウェアに近い処理への理解を要求されるため、一般に音楽の記述を行う場合にはより抽象化されたライブラリや、専用のドメイン固有言語を利用するのが普通である。 +CやC++といった汎用プログラミング言語で音声合成を行う場合もこのPCMの理論に基づいてプログラミングを行うことになるが、これら低レイヤーを対象にした言語はメモリ管理や並行処理といったハードウェアに近い処理への理解を要求されるため、音楽の記述を行う場合にはより抽象化されたライブラリやドメイン固有言語を利用するのが普通である。 % todo MUSICがUGENパラダイムを導入したという接続を入れる そうしたライブラリや言語では、MUSIC IIIで導入されたUnit Generator(UGen)と呼ばれる、オシレーターやフィルターといった基礎的な処理単位を、モジュラーシンセサイザーの様に組み合わせていくことで処理を行う考え方に基づく。\footnote{モジュラーシンセサイザーとUnit Generatorは実際には同時期に現れたコンセプトではある(\cite[p20]{park2009})が、その後の音楽プログラミング言語は積極的にビジュアル的なメタファーを物理的なシンセサイザーから取り入れてきている。} @@ -48,7 +47,7 @@ CやC++といった汎用プログラミング言語で音声合成を行う場 \subsection{関数型プログラミング言語に影響を受けた信号処理言語} -Unit Generatorコンセプトに基づく言語の問題点は、既存の音楽プログラミング言語の多くが実装に依存しており、言語としての意味論が形式化されているものがほとんど無いことにも起因する。 +Unit Generatorコンセプトに基づく言語の問題点は、既存の(信号処理を表現対象に含む)音楽プログラミング言語の多くが実装に依存しており、言語の意味論を形式化したものがほとんど無いことにも起因する。 この観点で、Faust\cite{Orlarey2004}は実用的に使われている音楽プログラミング言語の中でも例外的に強く形式化された言語である。Faustは、入出力を持つブロックを、並列、直列、分岐、合流、再帰の5つの合成子を用いて組み合わせていくブロックダイアグラム代数(Block Diagram Algebra-BDA)という体系を基礎に置く。バイパス、カット、基本的な算術演算、条件分岐、遅延をプリミティブなブロックとして提供することで、複雑な信号処理グラフを記述することができる。また後の拡張では、項書き換え系に基づくマクロを導入することで、任意の数の入出力を持つブロックの抽象化を可能にした\cite{graf2010}。 @@ -62,13 +61,12 @@ Kronos\cite{norilo2015}とW-calculus\cite{arias2021}は、それぞれFaustに W-calculusは、変数の過去の値にアクセスする機能(すなわち遅延)とともに、プリミティブ操作としてフィードバックを含む。W-calculusでは、定理証明支援システムCoqを用いた厳密な意味論の定義がなされている。他方で、W-calculusは記述する対象をフィルタやリバーブのような線形時不変システムに限定している。その代わりに、書かれたシステムではシステムの線形性が保証されるほか、異なる表記のシステム間の同一性を証明することなどが可能になる。またもう1つの制限として、W-calculusでは高階関数の使用は許されていない。 -筆者が開発している音楽のためのプログラミング言語mimium(MInimal-Musical-medIUM)\cite{Matsuura2021}では、ラムダ計算に遅延とフィードバックを基本構文として組み込むことで、汎用プログラミング言語と同様の構文を維持しながら、信号処理を簡潔に表現することができる。ただ、mimiumのこれまでの問題として、意味論が厳密に定義できていないために、遅延やフィードバックを含む内部状態を伴う関数と、再帰関数や高階関数の組み合わせを含むコードをコンパイルできないという点があった。 +筆者が開発している音楽のためのプログラミング言語mimium(\emph{MInimal-Musical-medIUM:ミミウム})\cite{Matsuura2021}では、ラムダ計算に遅延とフィードバックを基本構文として組み込むことで、汎用プログラミング言語と同様の構文を維持しながら、信号処理を簡潔に表現することができる。ただ、mimiumのこれまでの問題として、意味論が厳密に定義できていないために、遅延やフィードバックを含む内部状態を伴う関数と、再帰関数や高階関数の組み合わせを含むコードをコンパイルできないという点があった。 -本稿では、W-calculusの2つの制限(非線形システムと高階関数)を緩和する形で設計された、mimiumの中間表現となる計算モデル$\lambda_{mmm}$を提案する\footnote{本稿の内容は、2024年11月に行われたInternational Faust Conference 2024での筆者の発表\cite{matsuura2024}を、対象読者として日本語話者かつ言語設計コミュニティを想定し大幅に改稿したものである。}。以降の節では、まず第2節で$\lambda_{mmm}$のシンタックスと型付け規則、ナイーブな操作的意味論を簡単に紹介する。この操作的意味論は現実的に直接リアルタイム実行することが難しいため、実際のmimiumの実行環境では、$\lambda_{mmm}$を実際に実行するための仮想機械(VM)とその命令セットを持つ。第3章ではこのVMおよび命令セットの設計について概説する。第4節では、現状の$\lambda_{mmm}$を下敷きにしたmimiumの設計の今後の課題および展望を3つの視点で議論する。1つ目は、計算がグローバル環境の評価時に発生するのか、実際の信号処理中に発生するのかをユーザーが区別しなければならないという問題である。2つ目は、if式による部分的な内部状態更新を許すかどうかが信号処理の表現できる範囲およびユーザー体験に大きな影響を与えるという点である。3つ目は、$\lambda_{mmm}$の中間表現を取り入れることで外部関数の呼び出しが容易になるという点である。 +本稿では、W-calculusの2つの制限(非線形システムと高階関数)を緩和する形で設計された、mimiumの内部表現となる計算モデル$\lambda_{mmm}$について解説する\footnote{本稿の内容は、2024年11月に行われたInternational Faust Conference 2024での筆者の発表\cite{matsuura2024}を、対象読者として日本語話者かつ言語設計コミュニティを想定し大幅に改稿したものである。また現在のmimiumの開発リポジトリは{\url{https://github.com/tomoyanonymous/mimium-rs}}で公開されている。}。以降の節では、まず第2節で$\lambda_{mmm}$のシンタックスと型付け規則、ナイーブな操作的意味論を簡単に紹介する。この操作的意味論は現実的に直接リアルタイム実行することが難しいため、実際のmimiumの実行環境では、$\lambda_{mmm}$を実際に実行するための仮想機械(VM)とその命令セットを持つ。第3節ではこのVMおよび命令セットの設計について概説する。第4節では、現状の$\lambda_{mmm}$を下敷きにしたmimiumの設計の今後の課題および展望を3つの視点で議論する。1つ目は、計算がグローバル環境の評価時に発生するのか、実際の信号処理中に発生するのかをユーザーが区別しなければならないという問題である。2つ目は、if式による部分的な内部状態更新を許すかどうかが信号処理の表現できる範囲およびユーザー体験に大きな影響を与えるという点である。3つ目は、$\lambda_{mmm}$の中間表現を取り入れることで外部関数の呼び出しが容易になるという点である。 -% todo: githubへのリンク -\section{音楽のためのプログラミング言語mimiumの仕様} +\section{mimiumの内部表現$\lambda_{mmm}$の仕様} \input{syntax.tex} @@ -129,9 +127,9 @@ $\lambda_{mmm}$ を実行するための仮想マシン(VM)モデルとその命 ラムダ計算をベースとした計算モデルを実行する際の重要な課題は、クロージャと呼ばれるデータ構造を扱うことである。クロージャは、入れ子になった関数を定義した場所での変数環境をキャプチャし、外部関数のコンテキストにある変数を参照できるようにする。例えばクロージャを内側の関数の定義とそこで使われる変数と値の辞書との対として定義すれば、コンパイラ(もしくはインタプリタ)の実装は簡単だが、実行時の性能は制限される。 -逆に、クロージャ変換(ラムダ・リフティング)と呼ばれる、内部関数が参照するすべての外部変数を解析し、内部関数の隠し引数として追加するような処理を使えば、実行時のパフォーマンスを向上できる。しかし、コンパイラでのこの変換の実装は比較的複雑になる。 +逆に、クロージャ変換(ラムダ・リフティング)と呼ばれる、内部関数が参照するすべての外部変数を解析し、内部関数の隠し引数として追加するような処理を使えば、実行時のパフォーマンスを向上できる。しかし、コンパイラの実装においてこの変換は比較的複雑になる。 -LuaのVMは、2つの方法の中間的なアプローチである上位値(\textit{upvalue})という概念を採用しており、命令 \texttt{GETUPVALUE} と \texttt{SETUPVALUE} を追加することで、実行時に外部変数を動的に参照できるようになっている。上位値を使ったコンパイラとVMの実装は、クロージャの完全な変換よりも単純でありながら、大幅な性能低下を避けることができる。このアプローチでは、クロージャが元の関数の文脈からエスケープしない限り、上位値はヒープメモリではなくコールスタックへの間接的参照で読み取られる\cite{nystrom2021}。さらに、上位値の利用は他のプログラミング言語との相互運用性を促進する。例えばLua用の外部ライブラリをC言語で実装する場合、プログラマはC API経由でランタイムのコールスタック上の値だけでなく、Luaランタイムのupvalueにアクセスすることもできる。 +LuaのVMは、2つの方法の中間的なアプローチである上位値(\textit{upvalue})という概念を採用しており、命令 \texttt{GETUPVALUE} と \texttt{SETUPVALUE} を追加することで、実行時に外部変数を動的に参照できるようになっている。上位値を使ったコンパイラとVMの実装は、クロージャの完全な変換よりも単純でありながら、大幅な性能低下を避けることができる。このアプローチでは、クロージャが元の関数の文脈からエスケープしない限り、上位値はヒープメモリではなくコールスタックへの間接的参照で読み取られる\cite{nystrom2021}。さらに、上位値の利用は他のプログラミング言語との相互運用性を促進する。例えばLua用の外部ライブラリをC言語で実装する場合、プログラマはC API経由でランタイムのコールスタック上の値だけでなく、Luaランタイムの上位値にアクセスすることもできる。 \subsection{命令セット} \label{sec:instruction} @@ -281,9 +279,9 @@ fn dsp (x) 図\ref{fig:fbdelay_code} と 図\ref{fig:bytecodes_fbdelay}に、より複雑なコードの例とそのコンパイル結果のVM命令列の例を示した。このコードでは\texttt{fbdelay}としてフィードバック付きのディレイ関数を定義して、別の関数\texttt{twodelay}では異なる引数を使う2つのフィードバック付きディレイを呼び出す。さらに、 \texttt{dsp} は \texttt{twodelay} 関数を2つ使う。 -この例ではVM命令の中で、\texttt{GETSTATE} 命令で \texttt{self} を参照した後、または別の内部状態付き関数を呼び出した後に、\texttt{SHIFTSTATE} 命令が挿入され、次の(非クロージャ)関数呼び出しに備えて内部状態の読み書き位置を移動している。関数が終了する前には、再び\texttt{SHIFTSTATE}命令を使用して、読み書き位置を現在の関数の実行開始時の状態にリセットしている。図\ref{fig:fbdelay_spos}は、\texttt{twodelay}関数の実行中に\texttt{SHIFTSTATE}命令によって内部状態読み書きポインタがどのように遷移するかを示している。\texttt{SHIFTSTATE}操作の引数はワードサイズ(64ビットの数値)であり、ディレイのためのワードサイズは読み取り位置インデックス、書き込み位置インデックス、リングバッファの長さの値の3つが付加されるため、最大遅延時間+3となる。 +この例ではVM命令の中で、\texttt{GETSTATE} 命令で \texttt{self} を参照した後、または別の内部状態付き関数を呼び出した後に、\texttt{SHIFTSTATE} 命令が挿入され、次の(非クロージャ)関数呼び出しに備えて内部状態の読み書き位置を移動している。関数が終了する前には、再び\texttt{SHIFTSTATE}命令を使用して、読み書き位置を現在の関数の実行開始時の状態にリセットしている。図\ref{fig:fbdelay_spos}は、\texttt{twodelay}関数の実行中に\texttt{SHIFTSTATE}命令によって内部状態読み書きポインタがどのように遷移するかを示している。\texttt{SHIFTSTATE}操作の引数はワードサイズ(符号付き24ビットの数値)であり、ディレイのためのワードサイズは読み取り位置インデックス、書き込み位置インデックス、リングバッファの長さの値の3つが付加されるため、最大遅延時間+3となる\footnote{符号付き24bit整数の最大値はサンプリングレート192kHzなら42秒程度である。場合によってはより大きな値を扱う必要があるかもしれないが、その場合は\texttt{SHIFTSTATE}命令を複数回に分割して実行することで対応ができる。}。 -以前のmimiumの実装では、内部状態を関数の呼び出しに応じた木構造として生成していたが、命令上で内部状態の読み書き位置を相対的なオフセットとして表現することで、内部状態のメモリレイアウトは単純な配列として表現可能になり、各関数の命令列ではどの関数から呼び出されるのかについて知る必要がなくなる。これはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなく、upvalueとしてコールスタック上の相対的な位置として扱い、部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと似たようなアプローチと言える。 +以前のmimiumの実装では、内部状態を関数の呼び出しに応じた木構造として生成していたが、本VMの命令セットでは内部状態の読み書き位置を相対的なオフセットとして表現することで、メモリレイアウトは単純な配列として表現可能になり、各関数はどの関数から呼び出されるのかについて知る必要がなくなる。これはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなく、upvalueとしてコールスタック上の相対的な位置として扱い、部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと近しいアプローチと言える。 \begin{figure}[ht] \centering @@ -311,9 +309,9 @@ fn dsp(){ \end{figure} -さらに別の例として図\ref{fig:filterbank_good}に高階関数\texttt{filterbank}を使うものを示す。この関数は、別の関数\texttt{filter}(これは入力と周波数を引数として受け取る)を引数として受け取り、\texttt{filter}のインスタンスを\texttt{n}個複製し、それらを足し合わせるものである\footnote{以前のmimiumの仕様\cite{Matsuura2021}では、変数束縛と破壊的代入の構文は同じ(\texttt{x = a})だった。しかし、新しいバージョンの構文では、変数の束縛に\texttt{let}キーワードを使用している。}。 +さらに別の例として図\ref{fig:filterbank_good}に高階関数\texttt{filterbank}を使うものを示す。この関数は、別の関数\texttt{filter}(入力と周波数を引数として受け取り結果の数値を返す)を引数として受け取り、\texttt{filter}のインスタンスを\texttt{n}個複製し、それらをの計算結果を足し合わせるような関数を返すものである\footnote{以前のmimiumの仕様\cite{Matsuura2021}では、変数束縛と破壊的代入の構文は同じ(\texttt{x = a})だった。しかし、新しいバージョンの構文では、変数の束縛に\texttt{let}キーワードを使用している。}。 -以前のmimiumのコンパイラは、内部状態のツリー全体をコンパイル時に静的に決定する必要があったため、このような内部状態を引数に取る関数をコンパイルすることができなかった。しかし、$\lambda_{mmm}$ のVMではこれを動的に解決することができる。図\ref{fig:bytecode_filterbank}に、このコードをVM命令にコンパイルした例を示す。\texttt{filterbank}の1行目の再帰呼び出しや、引数として渡された関数や(\texttt{filter}のように)upvalueとして取得した関数の呼び出しは、\texttt{CALL}命令ではなく\texttt{CALLCLS}命令を使って実行される。ここで、\texttt{GETSTATE}命令や \texttt{SETSTATE} 命令はこの関数では使用されない。これは、\texttt{CALLCLS} 命令が実行される際に、使用する内部状態ストレージが動的に切り替わるためである。 +以前のmimiumのコンパイラは、内部状態のツリー全体をコンパイル時に静的に決定する必要があったため、このような内部状態を引数に取る関数をコンパイルすることができなかった。しかし、$\lambda_{mmm}$ のVMではこれを動的に解決することができる。図\ref{fig:bytecode_filterbank}に、このコードをVM命令にコンパイルした例を示す。\texttt{filterbank}の1行目の再帰呼び出しや、引数として渡された関数や(\texttt{filter}のように)upvalueとして取得した関数の呼び出しは、\texttt{CALL}命令ではなく\texttt{CALLCLS}命令を使って実行される。ここで、\texttt{GETSTATE}命令や \texttt{SETSTATE} 命令はこの関数では使用されない。これは、\texttt{CALLCLS} 命令が実行される際に、使用する内部状態ストレージが動的に切り替わるためである。内部状態を変更する命令は引数として与えられた\verb|filter|を呼び出す際に実行される。 \begin{figure}[ht] \centering @@ -372,7 +370,7 @@ fn filterbank(n,filter_factory) \texttt{filterbank}の例で示したように、mimiumではグローバル環境の評価においてフィルタの複製などのパラメトリックな信号処理内容を高階関数として計算し、\texttt{dsp}の中で生成された関数を利用して実際の信号処理を評価している。 -表\ref{tab:comparison}で示すように、Faustでは項書き換えマクロを、Kronosでは型レベルの計算を用いるように、既存の言語ではパラメトリックな信号処理内容の生成とその実行に異なる意味論を持つ体系を混在させているのに対して、mimiumではグローバル環境と実際の信号処理の実行に同じ値レベルの意味論を用いている。 +表\ref{tab:comparison}に示すように、既存の言語ではパラメトリックな信号処理内容の生成でFaustでは項書き換えマクロを、Kronosでは型レベルの計算を用い、その実行にはBDAや値レベルの計算を混在させているのに対して、mimiumではグローバル環境と実際の信号処理の実行に同じ値レベルの意味論を用いている。 この単一の意味論による体系の利点としては、初心者が言語の体系の理解を簡単にする、また他の汎用言語との実行時の相互運用性を高められる、といったことが考えられる。一方で、意味論が統一されていることによって、 $\lambda_{mmm}$ は普通のラムダ計算で期待される振る舞いから逸脱した挙動を見せる問題がある。 @@ -399,7 +397,7 @@ fn dsp(){ mimiumでは時間の経過とともに変化する内部状態を持つ関数を使用することで、一般的な関数型プログラミング言語と比較して、高階関数を使用した場合に直感に反する振る舞いを引き起こす。 -図\ref{fig:filterbank_bad}に、図\ref{fig:filterbank_good}のフィルタバンクのコード例を少しだけ変更して、間違った処理の例を示す。図\ref{fig:filterbank_bad}と図\ref{fig:filterbank_good}の主な違いは、\texttt{filterbank}関数内の再帰呼び出しを直接書いているか、内部関数の外側で\texttt{let}式で束縛しているかである。同様に、\texttt{dsp}関数(mimiumのオーディオドライバから呼び出される)でも、\texttt{filterbank}関数が\texttt{dsp}内で評価されるか、グローバル環境でで一度だけ\texttt{let}で束縛されているかという違いがある。 +図\ref{fig:filterbank_bad}に、図\ref{fig:filterbank_good}の\verb|filterbank|のコード例を少しだけ変更した、間違ったコードの例を示す。図\ref{fig:filterbank_bad}と図\ref{fig:filterbank_good}の主な違いは、\texttt{filterbank}関数内の再帰呼び出しを返却される関数の外側で一度\texttt{let}式で束縛しているか、内側で直接実行しているかである。同様に、\texttt{dsp}関数(mimiumのオーディオドライバから呼び出される)でも、\texttt{filterbank}関数が、グローバル環境でで一度\texttt{let}で束縛されているか、\texttt{dsp}内で直接評価されるかという違いがある。 典型的な関数型プログラミング言語では、関数の破壊的代入を伴わない限り、図\ref{fig:filterbank_good}から図\ref{fig:filterbank_bad}への変換に見られるように、\texttt{let}で束縛された変数をその項にで置き換えた(ベータ簡約)としても、計算処理の内容は変化しない。 @@ -429,19 +427,47 @@ fn dsp(){ \caption{\label{fig:filterbank_multi}{mimiumの将来的な仕様における、多段階計算を使用した\texttt{filterbank}関数の例。}} \end{figure} -\subsection{外部言語で定義される状態付き関数呼び出しの可能性} +\subsection{if式による部分的な内部状態更新の意味} + +現在の意味論では、if式のthen節、else節の中で関数あるいはクロージャの呼び出しを行った場合、部分的に内部状態が更新されたりされなかったりする。つまり、先程と同様、内部状態を更新した結果を\verb|let|で束縛してからif式の評価中にその結果を利用するのと、if式の中で直接内部状態を更新するのとでは計算結果が異なる。この部分的な内部状態の更新は、例えば高速に条件をスイッチした時に、実質的なサンプリングレートが変化することになるため、意図しない処理結果を招く可能性がある。 + +Faustは条件分岐先の両方をあらかじめ評価してから条件に合わない分岐先の結果に0を掛けて足し合わせる、という意味論を持っているのでこのサンプリングレートの変化は起きない一方、条件分岐先をすべて評価するため最終的には使用されない信号処理も必ず評価されるため、計算リソースの無駄な使用が発生するという新たな問題が発生する。Faustでは近年、新たな言語プリミティブとして、部分的な内部状態更新を明示的に使用する\verb|ondemand|プリミティブの導入が検討されている\cite{yannorlarey2023}。これは、リソースの有効活用という先程の問題の解決に加えて、ある一定間隔でバッファした入力をまとめて計算するマルチレート(例えばFFT)処理というこれまでのFaustの仕様で難しかった処理を実現可能にもしている。 + +mimiumもこれに倣って、部分的な内部更新を伴うべき時とそうでない時を区別できるような構文を導入することで、ユーザーの混乱を少なくしつつ信号処理の適用範囲を広げることが期待できる。 + +\subsection{外部言語で定義される状態付き関数の呼び出し} \label{sec:ffi} -クロージャのデータは、図\ref{fig:vmstructure}で示したように、関数と内部状態の組み合わせで表現されている。\verb|filterbank|の例で内部状態に対して特別な操作を必要としていないということは、mimiumからCやC++で書かれた発振器やフィルターなどのUGenを、通常のクロージャと同じように呼び出すことができることを意味する。さらに、外部UGenをパラメトリックに複製・合成することも可能になる。この機能はFaustや類似の言語では実装が難しいが、 $\lambda_{mmm}$ の設計では簡単に実現できる。 +\begin{figure}[ht] +\centering +\begin{verbatim} +let sampler = gen_sampler_mono("target.wav") +fn counter(){ + self+1.0 +} +fn dsp(){ + sampler(counter()) +} +\end{verbatim} +\caption{\label{fig:sampler}{mimiumでの外部関数\texttt{gen\_sampler\_mono}を使用したオーディオファイルの再生}} +\end{figure} -ただし、現在mimiumはサンプル単位の処理を基本にしており、バッファ単位の信号を扱うことができない。ほとんどのネイティブに実装されたUGenはバッファ単位でデータを処理するため、今のところ、既存の外部UGenが使用可能な実用的ケースは多くない。しかしその上で、厳密にサンプル単位の処理を必要とするのは$feed$ の項のみであるため、一度に1サンプルしか処理できない関数と、一括して複数サンプルを処理する関数とを型レベルで区別することは可能なはずである。Faustでマルチレートの仕様が検討されているように、バッファ単位で処理できる部分をコンパイラが自動的に判断することで、外部UGen間とバッファ単位での連携も可能かもしれない。 +クロージャのデータは、図\ref{fig:vmstructure}で示したように、関数と内部状態の組み合わせで表現されている。\verb|filterbank|の例で内部状態に対して特別な操作を必要としていないということは、mimiumからホスト言語として定義された発振器やフィルターなどのUGenを、通常の関数と同じように呼び出すことができることを意味する。また外部UGenのパラメトリックに複製・合成することも可能である。この機能はFaustでは実装が難しいが、 $\lambda_{mmm}$ の設計では簡単に実現できる。 + +実際、現在のmimiumでは、オーディオファイルのデコードなどmimium言語単体では現状難しい低レベルなバイト操作などを、Rustのライブラリ(Symphoniaクレート)を利用した外部モジュールとして実装している。図\ref{fig:sampler}はmimium側からの外部関数呼び出しの例である。\verb|gen_sampler_mono|関数は、ファイルパスを引数として実行すると新たな関数(クロージャのインスタンス)を返す。このインスタンスを数値型の引数を与えて呼び出すと、単に読み込んだオーディオデータの配列に引数をインデックスとして値を取得する。そのため、1サンプルごとに1増加する\verb|counter|関数と組み合わせると、オーディオファイルを1.0倍の速度で1度だけ再生することになる。 + +Rust側では関数名(\verb|gen_sampler_mono|)、その関数の型、Rustで実装された関数もしくはクロージャへの参照の3つ組をあらかじめ定義しておき、それをコンパイラとランタイムの初期化時にそれぞれ読み込ませることで呼び出すことが可能になる。実行用に、VMの命令には外部関数呼び出し用の\verb|CALLEXTFUN|が追加で実装されている。 + +ただし、現在のところmimiumはサンプル単位の処理を基本にしており、バッファ単位の信号を扱うことができない。ほとんどのネイティブで実装されたUGenはバッファ単位でデータを処理するため、今のところ、既存の外部UGenがそのまま使用可能なケースは少ない。この点は先述した部分的な内部状態の更新によるマルチレート処理の導入と共に解決し得る。 \section{結論} \label{sec:conclusion} -本稿では、音楽・信号処理用プログラミング言語の中間表現 $\lambda_{mmm}$ と、それを実行するための仮想マシン・命令セットを提案した。 $\lambda_{mmm}$ は、信号処理グラフの生成と、その実際の処理を統一された構文と意味論で記述可能にする。ただし、関数がグローバル環境で評価されるかDSPの繰り返し実行で評価されるかの判別はユーザーの責任となり、初学者にはその区別が難しいという欠点がある。 +本稿では、音楽・信号処理用プログラミング言語の中間表現 $\lambda_{mmm}$ と、それを実行するための仮想マシン・命令セットを提案した。 $\lambda_{mmm}$ は、信号処理グラフの生成と、その実際の処理を統一された構文と意味論で記述可能にした。また信号処理のプロセスを内部状態を伴う関数の適用として表現することで、外部で定義されたUGenの利用を単なる高階関数の呼び出しとして可能にしているように、既存の汎用言語との相互運用性も高めている。 -また本論文では、VMの動作を記述する擬似コードとに加えて、mimiumでのコードの例とそれに対応するバイトコードの例を示すことでコンパイル過程を説明したのみである。この過程を表示的意味論として形式化する作業は、現在反映されてない合成型の形式化に加えて今後多段階計算の導入と並行して検討する必要がある。 +一方で、関数がグローバル環境で評価されるかDSPの繰り返し実行で評価されるかの判別や、if文による内部状態更新をどの位置で評価するかなどはユーザーの責任となり、初学者にはその区別が難しいという欠点がある。 + +また本論文では、mimiumでのコードの例とそれに対応するVM命令列擬似コードの例を示すことでコンパイル過程を説明したのみである。現在反映されてない合成型の形式化や、今後の多段階計算の導入と並行してコンパイル過程を表示的意味論として形式化を進める必要がある。 この研究が、コンピュータ上での音・音楽のより一般的な表現に貢献し、音楽のための言語理論とプログラミング言語理論のより広範な分野との間のより深いつながりを促進することを期待する。\\ diff --git a/src/ref_bibtex.bib b/src/ref_bibtex.bib index caefafd..c3ce892 100644 --- a/src/ref_bibtex.bib +++ b/src/ref_bibtex.bib @@ -324,3 +324,13 @@ isbn = {978-1-4503-2326-0}, language = {en} } + +@misc{yannorlarey2023, + title = {Where Is {{Faust Going}} as a {{Programming Language}}? - {{Faust Day}} 2023}, + author = {{Yann Orlarey}}, + year = {2023}, + month = may, + urldate = {2024-11-29}, + abstract = {Faust Day, CCRMA, Stanford University, May 20th, 2023}, + howpublished = {\url{https://www.youtube.com/watch?v=hVWMn2joEl8}} +}