---
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
}
```

---

### FRPっぽい感じで考えてみる

MIDIのバイトストリームをフィルターして、ノートオンオフのストリームを作る

最終的にはそれをHoldしてセルにして、セルをsampleして値にすれば良い

なんかJSには[[midiguchi]]というライブラリがあるらしい

[[Haskell]]には[[Reactive-Banana]]使った[[ReactiveBalsa]]もある


```rust

fn voice_mono(notes:Stream<Note>){
  let cell = notes |> hold({pitch:60,velocity:0})
}

let midiinput = midi_open_port("from Max 1");
let notes = midiinput |> filter_map(_,is_byte_note)
let notes_cell = notes |> voice_mono //ここまでが今のbind_midi_note_monoに相当

fn dsp(){
	let {pitch,velocity} = notes_cell |> sample
	pitch |> midi2freq |> osc |> _ * velocty/127
}

```

[[Computation Expression]]使ってIOの値を`let!`でバインドしていく、とかの方がわかりやすいかも?

MIDI入力がトリガーになってクロージャのupvalueを書き換えることでCellの中身を書き換えるとなると、現実的にはオーディオスレッドとMIDI（イベント）スレッドを跨いでupvalueを操作することになる。`get_closure_mut`でクロージャをゲットして実行することはできるけど、その時には結局VMがオーディオスレッドにいるから、VMがクロージャ実行予約の命令を受け取るmpscチャネルを持っておいて、予約されたらオーディオブロックの頭で実行する、みたいな感じにする？？

Arcでupvalueを全部包んで、closed upvalueの中身をAtomicU64にする、みたいな方法もあるにはあるが、そうするとClosureのストレージはVMそのものというよりもVMインスタンス間で共有される感じになるのかな

