quartz-research-note/content/mimiumでMIDIインプットを実装.md

119 lines
2.9 KiB
Markdown
Raw Normal View History

2024-11-04 08:04:35 +00:00
---
date: 2024-10-30 15:42
---
#mimium
## 要件
どこまでMIDIインプットをmimiumの世界の外側として捉えるか
### 先行例
[[SuperCollider]]や[[ChucK]]はコールバックを登録するようなイメージ
[Using MIDI | SuperCollider 3.12.2 Help](https://doc.sccode.org/Guides/UsingMIDI.html
```smalltalk
s.boot;
(
var notes, on, off;
MIDIClient.init;
MIDIIn.connectAll;
notes = Array.newClear(128); // array has one slot per possible MIDI note
on = MIDIFunc.noteOn({ |veloc, num, chan, src|
notes[num] = Synth(\default, [\freq, num.midicps,
\amp, veloc * 0.00315]);
});
off = MIDIFunc.noteOff({ |veloc, num, chan, src|
notes[num].release;
});
q = { on.free; off.free; };
)
// when done:
q.value;
```
まあこれはDSPアウトプットの合成が.playで暗黙的に行える加算で合成されるという想定だからできることかな、、、
ChuckもボイスごとにShredを生やす方向で対応してるからちょっと微妙だ
[Chuck - Input & Output](https://chuck.stanford.edu/doc/reference/io.html#MidiIn)
---
現実的には、[[Faust]]のように、ボイスアロケーターは外側で実装してしまい、非同期に更新されうるAtomicな値のセルをートやccのデータとして受け取れるようにすれば当面は十分
が、最終的にはMIDIエフェクトMIDI信号自体のディレイやクォンタイズ、スロットリングとかを[[FUnctional Reactive Programming|FRP]]っぽく書けると嬉しい
---
Noteのバインドの記法
```rust
let cell:()->float = bind_midi_note_mono(channel)
let (note,vel) = cell(); //値の取り出し
```
```rust
//rust
fn bind_midi_note_mono(&mut self ,vm:&mut Machine)->ReturnCode{
let ch = Machine::get_as::<f64>(vm.get_stack(0));
let cell = Arc::new((AtomicF64::new(),AtomicF64::new() ));
self.add_midi_listener(ch,|note,vel|{
let (note_c,vel_c) = cell.clone();
note_c.write(note);
vel_c.write(vel);
});
let cls = |vm:&mut Machine|->ReturnCode{
let (note_c,vel_c) = cell.clone();
vm.set_stack(0,Machine::to_value(note_c));
vm.set_stack(1,Machine::to_value(vel_c));
2
}
vm::set_stack(0, wrap_rust_closure(cls))
1
}
```
あとは頑張って`add_midi_listener`相当の何かを作ろうって感じだな(関数型っぽくはないけども)
---
IOの順序保証とか考える
正格評価だとIOモナドとかはそもそも考える必要がない
[Algebraic Effects入門](https://v2.aintek.xyz/posts/introduction-to-algebraic-effects)
> `type 'a io = unit -> 'a`
> 純粋な値は以下のようなIOモナドにリフトできる。
> `let return x = fun () -> x`
> 計算はバインド演算子によってつなげることができる。
> `let (>>=) c1 c2 = fun () -> c2 (c1 ())`
mimiumだとバインド演算子ってこうか(ジェネリクスが必要だけども)
```rust
fn bind(f1,f2){
| | f1() |> f2
}
```