コンテンツにスキップ

ホスト API

ここに挙げるものはすべて runRenderCode(ホスト)が直接注入 します。libs/codes/*.js より前にロードされ、ユーザコードや lib から上書きすることはできません。

setNote({ pitch, velocity, startBeat, lengthBeats })

Section titled “setNote({ pitch, velocity, startBeat, lengthBeats })”

ノートを 1 つ Render に出力します。すべてのフィールドは省略可能。

フィールド既定値クランプ範囲
pitch60(C4)0..127(整数に丸め)
velocity1000..127(整数に丸め)
startBeatCLIP_BEAT(現在のフェーズの値)クランプなし
lengthBeats10.001 以上

idcrypto.randomUUID() で自動付与されます。同じピッチ・同じビートで複数回呼んでもそれぞれ別ノートとして記録されます。

最小例

onInit(() => setNote({ pitch: 60 })); // C4、1 拍、velocity=100、startBeat=0

実用例

// 元ノートにオクターブ上のレイヤーを 70% velocity で足す
onNote((n) => {
setNote({ ...n }); // 元ノート保持
setNote({ ...n, pitch: n.pitch + 12, velocity: 70 }); // オクターブ上
});

[!CAUTION] pitch / velocity整数に丸められた上でクランプ されます。 lengthBeats の最小値は 0.001 — それ未満を渡しても 0.001 になります(無音ノート防止)。

CC イベントを 1 つ Render に出力します。同じ ccNumber への複数回の呼び出しは 1 つのレーンに集約 され、beat 昇順にソートされます。

フィールド既定値クランプ範囲
ccNumber(必須)0..127(整数に丸め)
beatCLIP_BEATクランプなし
value(必須)0..127(整数に丸め)
curve"linear""linear" / "hold" / "exp" / "expInv" / "scurve"

最小例

onInit(() => setCc({ ccNumber: 11, beat: 0, value: 64 }));

実用例

// 1 拍ごとに正弦波で Expression(CC11)を揺らす
onBeat((b) => {
const v = 64 + 32 * lfo(b / CLIP_LENGTH, "sin");
setCc({ ccNumber: 11, beat: b, value: v });
});

カーブ指定 (P-2)

curveこのポイントで終わるセグメント の形を指定します(前のポイントから「自分」まで補間する際の形)。

onInit(() => {
// 0→100 までを smoothstep でゆっくり立ち上げる
setCc({ ccNumber: 1, beat: 0, value: 0 });
setCc({ ccNumber: 1, beat: 4, value: 100, curve: "scurve" });
// ステップ状にホールドしてから跳ね上げる
setCc({ ccNumber: 1, beat: 6, value: 30, curve: "hold" });
});
curve
"linear"直線(既定)
"hold"A の値を保持し、B.beat で B の値に階段状にジャンプ
"exp"ease-in(最初遅く後で急、t**2.7
"expInv"ease-out(最初急で後で緩む、exp の鏡像)
"scurve"smoothstep(両端で接線がフラット、中央で対称)

[!TIP] 同じ ccNumber に複数回 setCc を呼ぶと、1 つのレーン内のイベント列に集約されます。異なる ccNumber ならレーンが別々に作られます。

onCc のサンプリンググリッド解像度(1 拍を何分割するか)を設定します。[1, 256] にクランプ。トップレベル または onInit から呼んだときだけ反映され、onBeat / onNote / onCc 内からの呼び出しは黙って無視されます(グリッドは onCc 掃引の直前にスナップショット)。

引数意味既定値
stepsPerBeat1 拍あたりの onCc 呼び出し回数16(≈ 1/64 音符 ≈ 31ms @120bpm)
onInit(() => setCcResolution(32)); // 1/32拍 ≈ 15.6ms @120bpm
onCc(({ phase }) =>
setCc({ ccNumber: 1, value: 64 + 32 * Math.sin(phase * 2 * Math.PI) }),
);

[!CAUTION] 解像度 × クリップ長が 32,768 ticks を超えると Render は Runtime error (onCc): grid sweep would emit ... ticks, exceeding cap を返します。setCcResolution を下げるかクリップを短くしてください。

onInit(fn) / onBeat(fn) / onNote(fn) / onCc(fn)

Section titled “onInit(fn) / onBeat(fn) / onNote(fn) / onCc(fn)”

それぞれのフェーズに任意個のコールバックを登録します。登録順 に呼ばれます。return 値は無視されます。

関数コールバック引数呼ばれる回数
onInit(fn)なしレンダー先頭で 1 回
onBeat(fn)(beat: number)クリップ長 × stepsPerBeat(既定 16/拍)
onNote(fn)(note: MidiNote)元ノートの数
onCc(fn)(ctx: { beat, time, phase })クリップ長 × CC 解像度(既定 16/拍。setCcResolution() で変更)

例外を投げるとそのフェーズが中断 されます(前のフェーズの結果は保持)。詳しくは ライフサイクル:エラーが起きたら何が残るか を参照。

// 5 度上のハモリを足す(登録順に評価される)
onNote((n) => setNote({ ...n })); // 1 番目:元ノート保持
onNote((n) => setNote({ ...n, pitch: n.pitch + 7, velocity: 70 })); // 2 番目:5 度上

[!TIP] onCc は元 CC レーンの有無に関わらず、setCcResolution で決まる固定グリッドで掃引されます。CC をゼロから生成する LFO もこの中で書けます。入力 CC を読みたいときは sampleCc を使ってください。

PianoRoll タブに置かれた 元ノート の配列。レンダー時の clip.notes読み取り専用 で渡されます(並びは編集時のまま — onNote の引数だけが startBeat 昇順)。

interface MidiNote {
id: string;
pitch: number; // 0..127
velocity: number; // 0..127
startBeat: number;
lengthBeats: number;
}
// 元ノートの数だけを記録
onInit(() => console.log("source notes:", events.length));

クリップに保存されている 元 CC レーン の配列(PianoRoll タブの CC レーン、または手動入力)。

interface ClipCcLane {
id: string;
ccNumber: number; // 0..127
events: MidiCcEvent[]; // [{ id, beat, value }, ...]
}
// CC11 のレーンが存在するかチェック
onInit(() => {
const expr = ccLanes.find((l) => l.ccNumber === 11);
console.log(expr ? `CC11 has ${expr.events.length} events` : "no CC11");
});

入力 ccLanes から ccNumber のレーンを探し、beat の値を線形補間して返します(float、整数化やクランプは呼び出し側で)。Rust エンジンの再生時補間と同じセマンティクスなので、sampleCc で読んだ値に LFO を足して setCc で書き戻すと、聴こえる結果と数値が一致します。

状況戻り値
ccNumber のレーンが存在しない / events が空0
beat が最初の点より前最初の点の value(hold-back)
beat が最後の点より後最後の点の value(hold-forward)
同じ beat に複数点配列順で最後の点(last-wins)
// 既存 CC7 にゆらぎを足して書き戻す
onCc(({ beat, phase }) => {
const base = sampleCc(7, beat);
setCc({ ccNumber: 7, value: clamp(base + 10 * lfo(phase, "sin"), 0, 127) });
});

[!CAUTION] sampleCc入力 ccLanes からだけ読みます。同じレンダー中に setCc で書いた値は読み返せません(鶏卵問題)。

名前内容
CLIP_BEATnumber現在のフェーズに対応するビート(onInit=0, onBeat=グリッド位置, onNote=startBeat, onCc=サンプリンググリッドの位置)
CLIP_TIMEnumberCLIP_BEAT / bpm * 60(秒)
CLIP_LENGTHnumberクリップ長(ビート数)。レンダー中は不変

更新の仕組みについては ライフサイクル:CLIP_BEAT / CLIP_TIME / CLIP_LENGTH の更新メカニズム を参照。

// クリップの最後の 4 分音符だけ強調
onNote((n) => {
const isLast = n.startBeat >= CLIP_LENGTH - 1;
setNote({ ...n, velocity: isLast ? 120 : n.velocity });
});

mulberry32 由来の [0, 1) の擬似乱数。デフォルトシードは「コード本文 + 元ノート + ロード中の lib」のハッシュ。再現可能

// 同じコード・同じ元ノート・同じ lib なら毎回同じ並び
const r1 = seededRandom();
const r2 = seededRandom();

シードを上書き。文字列は内部でハッシュされて 32 ビット整数シードになります。

seed("variation-A");
const a = seededRandom(); // 同じ文字列なら毎回同じ値
seed(42);
const b = seededRandom();

[!IMPORTANT] factory.jsrandom() / randomInt() / pick() / chance() / weighted() / shuffle() / urn() / drunk()Math.random() ベース でシードの影響を受けません。再現性が必要な場面では seededRandom() を直接使ってください。 詳しくは ヘルパー:数値・乱数・モジュレーション > 乱数 を参照。

引数は文字列化されて Code タブのコンソールに表示されます。

  • 文字列はそのまま
  • それ以外は JSON.stringify で文字列化(循環参照・関数値は [object Object] 等になる)
  • console.warn / console.error は提供されません
onNote((n) => console.log("note:", n.pitch, "@", n.startBeat));
// → "note: 60 @ 0.5"

[!TIP] 大量のログを出すと Code タブの描画が重くなります。デバッグが終わったら消しましょう。

これらは普段意識しなくても問題ありませんが、トラブルシュート時に役立ちます。

  • ユーザコードは new Function("__api", ...) でコンパイルされる「スクリプトコンテキスト」で動きます。this はグローバル、arguments は使えますが、モジュールスコープではありません
  • CLIP_BEAT / CLIP_TIME はラッパー内の let バインディング。各コールバック直前に __sync() が更新するので、ユーザのクロージャは 呼び出し時点の値 を見ます
  • setNote / setCcidcrypto.randomUUID() で自動付与
  • setCc の同じ ccNumber は内部 Map に bucket されたあと、レーンごとに beat 昇順でソートされて返されます