https://github.com/tomoyanonymous/otopoiesis
DAWをプログラマブルにする試み
#[param("hoge",0..8)]
let p1 = 1.0;
思想
Brandt(2002) のTemporal Type Constructor(以下TTC)という概念を使う。
TTCはジェネリックなタイプA
に対して、以下の3つの型コンストラクタを用意することでジェネリックに時間信号を取り扱う思想。
以下はRustの擬似コード。
例えばMIDIの記録されたデータは
みたいになる
構造
基本的なイメージはこんな感じ?
type Project<V> = Vec<Track<_,__>> -> iVec<V>
type Track<I,O> = Device<I> * Device<O> //デバイス情報
*(
Vec<Region<O>>
| Vec<Event<I,O>>
| Generator<O>
)
type Region<V> = (time*time)* //start,duration
(Vec<V> // オーディオデータ
| Generator<V>
| Project<V>) //プロジェクトも再帰的に埋め込める
type Generator<T> = iVec<T>
なんだけど、TrackAで使われてるGeneratorの中のParameterとしてTrackBの値をアサインしたい、みたいなことを表現できたらプログラミングとして面白くなる、という話
//Freq440Hz,Gain1.0,Phase0.0
let Track1 = Generator::SineWave(Constant(440),Constant(1.0),Constant(0.0));
let Track2 = Generator::SineWave(Track1,Constant(1.0),Constant(0.0));
これをあんまり動的ディスパッチじゃない感じで実装したい。そしてこの辺までは別にMaxとかと同じレベルの話
ここからがDAWをプログラミングで操作できる面白いとこで、例えばリージョンに対するフェードインアウトとかをRegion<T>->Region<T>
の関数として定義できるところ
CubaseにおけるインストゥルメントトラックとかはMIDIトラック+シンセサイザーの合成なので、
Track<NOTE,NOTE>
にVec<NOTE>->iVec<Audio>
みたいなのを適用する関数としてあらわせ、、、る?
考えうるユースケース
リージョンに対するFadeInOut
リージョンに対するリバース
他
-
ドラッグしてるオブジェクトのスナップ対象は例えばCubaseなら固定のグリッドor既存のイベントorその両方とかだけど、例えばグリッドをジェネラティブに生成できる
- もちろんクオンタイズにも使える
-
非破壊的クォンタイズ。録音された時のイベント位置は覚えていて、一番近いグリッドに何%寄せるかも決められるし、ランダマイズも後から修正できる
-
プロジェクトをリージョンとして埋め込める
- その際、プロジェクトに与えられるグローバルなパラメーターみたいなものはどうしようね
-
結局内蔵スクリプトがフックされるタイミングって次の3つになるんよな
- 信号グラフ確定時(ルートのコンパイル)
- 再生前(prepareToPlay)
- 信号再生時(process)
以下は昔に考えていたこと
コードの例(モジュレーションされているサイン波+ディレイ)
project{
track: [
delay{
sinosc{
freq:
sinosc:{
freq: float{20..20000,1000,"freq"}
phase: 0.0},
phase: 0.0
},
time: 1000
}
]
}
UIは基本的にプロジェクトツリーのParam
型と、UIだけで使われるState
をそれぞれ可変参照として持つ
(Reference カウントするのではなく、有限なライフタイムを持つ可変参照で作る)
eguiはimmiditate モードだから毎フレームこのUI型を生成している(egui標準のSliderとかもこの方式)
オーディオプロセッサーもこのやり方にできるか?
開発メモ
クリップのサムネイル生成はgeneratorじゃなくてregion側でやろう
fileplayerのui実装もgeneratorからregionに移そう
そうなるとaudio側の実装もそっちに合わせるのが自然だよな・・・
完全にValueを64bitで静的型付けとして扱う時の、擬似的に動的型チェックする方法がないか
id_arenaのIDが128bitである限りちょっと厳しそう プロジェクト、トラック、リージョンが限られた数であることを前提にすればNaNBoxingもできなくはなさそうだが、、、
コンパイラのContextをアプリ中で引き回さなくちゃいけなくなるのがやだなー 少なくともこれやるとマルチスレッドはめっちゃ難しくなるな