ホスト API
ここに挙げるものはすべて runRenderCode(ホスト)が直接注入 します。libs/codes/*.js より前にロードされ、ユーザコードや lib から上書きすることはできません。
このページの シグネチャ・値域・単位・サンプルコードは、アプリ内の契約定義 (hostApiSpec) と CI で照合 されています。実行時の振る舞いとここに書かれた内容がずれると、ビルドが失敗します。サンプルはすべて lib を 1 つも有効にしていない状態でそのまま貼り付けて Render できます。
euclid() / scale() / lfo() / clamp() などは ホスト API ではなく factory.js (lib) の関数です。ヘルパー を参照してください。
setNote()
Section titled “setNote()”setNote({ pitch?, velocity?, startBeat?, lengthBeats? }): void
ノートを 1 つ Render に出力します。すべてのフィールドは省略可能。
| フィールド | 必須 | 既定値 | 値域 / 単位 |
|---|---|---|---|
pitch | 任意 | 60(C4) | 0..127(MIDI ノート番号・整数) |
velocity | 任意 | 100 | 0..127(7bit・整数) |
startBeat | 任意 | CLIP_BEAT(現在のフェーズの値) | クランプなし(beat) |
lengthBeats | 任意 | 1 | 0.001 以上(beat) |
id は crypto.randomUUID() で自動付与されます。同じピッチ・同じビートで複数回呼んでもそれぞれ別ノートとして記録されます。
最小例
onInit(() => { setNote({ pitch: 60 });});実用例 — 元ノートにオクターブ上のレイヤーを velocity 70 で足す:
onNote((note) => { setNote({ ...note }); setNote({ ...note, pitch: note.pitch + 12, velocity: 70 });});[!CAUTION]
pitch/velocityは 整数に丸められた上でクランプ されます。lengthBeatsの最小値は0.001— それ未満を渡しても0.001になります(無音ノート防止)。
チャンネルメッセージ出力
Section titled “チャンネルメッセージ出力”CC・Pitch Bend・Channel Aftertouch は同じレーンの仕組みに乗ります。画面側の編集については CC / Pitch Bend / Aftertouch オートメーション を参照。
setCc()
Section titled “setCc()”setCc({ ccNumber, beat?, value, curve? }): void
CC イベントを 1 つ Render に出力します。同じ ccNumber への複数回の呼び出しは 1 つのレーンに集約 され、beat 昇順にソートされます。
| フィールド | 必須 | 既定値 | 値域 / 単位 |
|---|---|---|---|
ccNumber | 必須 | — | 0..127(整数) |
beat | 任意 | CLIP_BEAT | クランプなし(beat) |
value | 必須 | — | 0..127(7bit・整数) |
curve | 任意 | "linear" | カーブ種別(下表) |
最小例
onInit(() => { setCc({ ccNumber: 11, beat: 0, value: 64 });});カーブ指定 (P-2) — curve は このポイントで終わるセグメント の形を指定します(前のポイントから「自分」まで補間する際の形):
onInit(() => { setCc({ ccNumber: 1, beat: 0, value: 0 }); setCc({ ccNumber: 1, beat: 4, value: 100, curve: "scurve" });});curve | 形 |
|---|---|
"linear" | 直線(既定) |
"hold" | A の値を保持し、B.beat で B の値に階段状にジャンプ |
"exp" | ease-in(最初遅く後で急、t**2.7) |
"expInv" | ease-out(最初急で後で緩む、exp の鏡像) |
"scurve" | smoothstep(両端で接線がフラット、中央で対称) |
[!TIP] 同じ
ccNumberに複数回setCcを呼ぶと、1 つのレーン内のイベント列に集約されます。異なるccNumberならレーンが別々に作られます。
setCcResolution()
Section titled “setCcResolution()”setCcResolution(stepsPerBeat: number): void
onCc のサンプリンググリッド解像度(1 拍を何分割するか)を設定します。
| 引数 | 必須 | 値域 / 単位 |
|---|---|---|
stepsPerBeat | 必須 | 1..256(1 拍あたりの呼び出し回数) |
呼ばなかった場合の解像度は 16(≈ 1/64 音符 ≈ 31ms @120bpm)です。トップレベル または onInit 内 から呼んだときだけ反映され、onBeat / onNote / onCc 内からの呼び出しは黙って無視されます(グリッドは onCc 掃引の直前にスナップショット)。
onInit(() => setCcResolution(32));
onCc(({ phase }) => { const value = 64 + 32 * Math.sin(phase * 2 * Math.PI); setCc({ ccNumber: 1, value });});[!CAUTION] 解像度 × クリップ長が 32,768 ticks を超えると Render は
Runtime error (onCc): grid sweep would emit ... ticks, exceeding capを返します。setCcResolutionを下げるかクリップを短くしてください。
setPitchBend()
Section titled “setPitchBend()”setPitchBend({ beat?, value, curve? }): void
Pitch Bend のポイントを 1 つ出力します。1 クリップにつき Pitch Bend レーンは 1 本です。
| フィールド | 必須 | 既定値 | 値域 / 単位 |
|---|---|---|---|
beat | 任意 | CLIP_BEAT | クランプなし(beat) |
value | 必須 | — | 0..16383(14bit・中央 = 8192) |
curve | 任意 | "linear" | setCc と同じカーブ種別 |
curve に指定できる値は "linear" / "hold" / "exp" / "expInv" / "scurve" です。
onInit(() => { setPitchBend({ beat: 0, value: 16000 }); setPitchBend({ beat: 1, value: 8192, curve: "exp" });});[!CAUTION]
value: 0は「ベンドなし」ではなく 全下げ です。中央へ戻すときは8192を渡してください。
setChannelAftertouch()
Section titled “setChannelAftertouch()”setChannelAftertouch({ beat?, value, curve? }): void
Channel Aftertouch のポイントを 1 つ出力します。Pitch Bend と同じく 1 クリップに 1 本です。
| フィールド | 必須 | 既定値 | 値域 / 単位 |
|---|---|---|---|
beat | 任意 | CLIP_BEAT | クランプなし(beat) |
value | 必須 | — | 0..127(7bit・整数) |
curve | 任意 | "linear" | setCc と同じカーブ種別 |
curve に指定できる値は "linear" / "hold" / "exp" / "expInv" / "scurve" です。
onInit(() => { setChannelAftertouch({ beat: 0, value: 0 }); setChannelAftertouch({ beat: 0.5, value: 100 }); setChannelAftertouch({ beat: 1, value: 0 });});コールバック登録
Section titled “コールバック登録”各フェーズに任意個のコールバックを登録します。登録順 に呼ばれ、return 値は無視されます。例外を投げるとそのフェーズが中断 されます(前のフェーズの結果は保持)。詳しくは ライフサイクル:エラーが起きたら何が残るか を参照。
| 関数 | コールバック引数 | 呼ばれる回数 |
|---|---|---|
onInit(fn) | なし | レンダー先頭で 1 回 |
onBeat(fn) | (beat: number) | クリップ長 × stepsPerBeat(既定 4/拍) |
onNote(fn) | (note: MidiNote) | 元ノートの数 |
onCc(fn) | (ctx: { beat, time, phase }) | クリップ長 × CC 解像度(既定 16/拍。setCcResolution() で変更) |
onInit()
Section titled “onInit()”onInit(fn: () => void): void
レンダー開始時に 1 回だけ呼ばれます。クリップ全体を一度に組み立てる用途に向いています。
onInit(() => { setNote({ pitch: 60 });});onBeat()
Section titled “onBeat()”onBeat(fn: (beat: number) => void): void
stepsPerBeat で決まるステップグリッドを時間順に掃引します。引数の beat はクリップ先頭からのビート位置です。
onBeat((beat) => { if (beat % 1 === 0) { setNote({ pitch: 36, startBeat: beat, lengthBeats: 0.25 }); }});onNote()
Section titled “onNote()”onNote(fn: (note: MidiNote) => void): void
PianoRoll タブに置かれた元ノートを 1 つずつ、startBeat 昇順で渡します。
onNote((note) => { setNote({ ...note }); setNote({ ...note, pitch: note.pitch + 12, velocity: 70 });});onCc()
Section titled “onCc()”onCc(fn: (ctx: { beat: number; time: number; phase: number }) => void): void
元 CC レーンの有無に関わらず、setCcResolution で決まる固定グリッドで掃引されます。ctx.phase は beat / CLIP_LENGTH を 0..1 にクランプした値で、1 周期の LFO を書くときに便利です。
onInit(() => setCcResolution(32));
onCc(({ phase }) => { const value = 64 + 32 * Math.sin(phase * 2 * Math.PI); setCc({ ccNumber: 1, value });});events
Section titled “events”events: MidiNote[]
PianoRoll タブに置かれた 元ノート の配列。レンダー時の clip.notes が 読み取り専用 で渡されます。
interface MidiNote { id: string; pitch: number; // 0..127 velocity: number; // 0..127 startBeat: number; lengthBeats: number;}並びは、legacy な contract v1 では編集時のまま(onNote の引数だけが startBeat 昇順)。contract v2 では正規化された順序 で渡されるため、ID の振り直しや編集順の違いで並びが変わることはありません。
onInit(() => { console.log("source notes:", events.length);});ccLanes
Section titled “ccLanes”ccLanes: ClipCcLane[]
クリップに保存されている 元チャンネルメッセージレーン の配列(PianoRoll タブのレーン、または MIDI 入力で録音したもの)。
interface ClipCcLane { id: string; messageType?: "cc" | "pitchBend" | "channelAftertouch"; // 省略時は "cc" ccNumber: number; // 0..127(PB / CA では未使用) events: MidiCcEvent[]; // [{ id, beat, value, curve? }, ...]}onInit(() => { const expr = ccLanes.find((lane) => lane.ccNumber === 11); console.log(expr ? "CC11 events: " + expr.events.length : "no CC11");});sampleCc()
Section titled “sampleCc()”sampleCc(ccNumber: number, beat: number): number
入力 ccLanes から ccNumber(0..127)のレーンを探し、beat の値を線形補間して返します(float、整数化やクランプは呼び出し側で)。Rust エンジンの再生時補間と同じセマンティクスなので、sampleCc で読んだ値に揺らぎを足して setCc で書き戻すと、聴こえる結果と数値が一致します。
| 状況 | 戻り値 |
|---|---|
ccNumber のレーンが存在しない / events が空 | 0 |
beat が最初の点より前 | 最初の点の value(hold-back) |
beat が最後の点より後 | 最後の点の value(hold-forward) |
同じ beat に複数点 | 配列順で最後の点(last-wins) |
onCc(({ beat, phase }) => { const base = sampleCc(7, beat); const wobble = 10 * Math.sin(phase * 8 * Math.PI); setCc({ ccNumber: 7, value: Math.min(127, Math.max(0, base + wobble)) });});[!CAUTION]
sampleCcは 入力ccLanesからだけ読みます。同じレンダー中にsetCcで書いた値は読み返せません(鶏卵問題)。
samplePitchBend()
Section titled “samplePitchBend()”samplePitchBend(beat: number): number
入力の Pitch Bend レーンを beat で線形補間して返します。レーンが無い / 空のときは 8192(ベンドなし) を返します — 0 は全下げを意味するため、ここだけ既定値が違います。
onCc(({ beat }) => { const bend = samplePitchBend(beat); setCc({ ccNumber: 1, value: Math.abs(bend - 8192) / 64 });});sampleAftertouch()
Section titled “sampleAftertouch()”sampleAftertouch(beat: number): number
入力の Channel Aftertouch レーンを beat で線形補間して返します。レーンが無い / 空のときは 0 を返します。
onCc(({ beat }) => { setCc({ ccNumber: 74, value: sampleAftertouch(beat) });});時間グローバル
Section titled “時間グローバル”すべて クリップ先頭を 0 とするローカルな値 です。小節番号・PPQ・トランスポート位置ではありません。更新の仕組みについては ライフサイクル:CLIP_BEAT / CLIP_TIME / CLIP_LENGTH の更新メカニズム を参照。
CLIP_BEAT
Section titled “CLIP_BEAT”CLIP_BEAT: number
現在のフェーズに対応するビート(onInit = 0、onBeat = グリッド位置、onNote = そのノートの startBeat、onCc = サンプリンググリッドの位置)。
CLIP_TIME
Section titled “CLIP_TIME”CLIP_TIME: number
CLIP_BEAT / bpm * 60(秒)。bpm はスカラー値として扱われます(テンポマップ非対応)。
CLIP_LENGTH
Section titled “CLIP_LENGTH”CLIP_LENGTH: number
クリップ長(ビート数)。レンダー中は不変です。
onInit(() => { console.log("clip length:", CLIP_LENGTH, "beats"); console.log("start:", CLIP_BEAT, "beat /", CLIP_TIME, "sec");});
onNote((note) => { const isLast = note.startBeat >= CLIP_LENGTH - 1; setNote({ ...note, velocity: isLast ? 120 : note.velocity });});seededRandom()
Section titled “seededRandom()”seededRandom(): number
mulberry32 由来の [0, 1) の擬似乱数。再現可能 です。
デフォルトシードは contract バージョンで決まります。legacy な v1 は「コード本文 + 元ノート + ロード中の lib」のハッシュ、v2 は正規化した musical input から導出 されるため、ID の振り直しや並べ替えではシードが変わりません。
onBeat((beat) => { const pitch = 60 + Math.floor(seededRandom() * 12); setNote({ pitch, startBeat: beat, lengthBeats: 0.25 });});seed()
Section titled “seed()”seed(n: number | string): void
シードを上書きします。文字列は内部でハッシュされて 32 ビット整数シードになります。
onInit(() => { seed("variation-A"); console.log("first draw:", seededRandom());});Math: typeof Math
標準の Math と同じ関数群です。ただし contract v2 では Math.random() が seeded ストリームから引かれる ため、seededRandom() と同じく再現可能になります(v1 では通常の Math.random())。Math.random 以外のメンバーは標準実装のままです。
onBeat((beat) => { const pitch = 60 + Math.floor(Math.random() * 12); setNote({ pitch, startBeat: beat, lengthBeats: 0.25 });});[!IMPORTANT]
factory.jsのrandom()/randomInt()/pick()/chance()/weighted()/shuffle()/urn()/drunk()はMath.random()ベースの lib 関数です。v1 のクリップでは再現性がないので、確実に再現させたい場面ではseededRandom()を直接使ってください。 詳しくは ヘルパー:数値・乱数・モジュレーション > 乱数 を参照。
macros
Section titled “macros”macros: Readonly<Record<string, number>>
Project Macro のスナップショット(マクロ名 → 値)。読み取り専用 で、レンダー中に値が動くことはありません。ユーザコードから書き換えようとすると strict mode で例外になります。
未定義のマクロ名は undefined になるので、?? で既定値を用意してください。
onInit(() => { const density = macros.density ?? 4; for (let i = 0; i < density; i += 1) { setNote({ pitch: 48, startBeat: i, lengthBeats: 0.25 }); }});console
Section titled “console”console.log(...args: unknown[]): void
引数は文字列化されて Code タブのコンソールに表示されます。
- 文字列はそのまま
- それ以外は
JSON.stringifyで文字列化(循環参照・関数値は[object Object]等になる) console.warn/console.errorは提供されません
onNote((note) => { console.log("note:", note.pitch, "@", note.startBeat);});[!TIP] 大量のログを出すと Code タブの描画が重くなります。デバッグが終わったら消しましょう。
内部仕様メモ
Section titled “内部仕様メモ”これらは普段意識しなくても問題ありませんが、トラブルシュート時に役立ちます。
- ユーザコードは
new Function("__api", ...)でコンパイルされる「スクリプトコンテキスト」で動きます。thisはグローバル、argumentsは使えますが、モジュールスコープではありません __apiとその__sync/__beat/__timeは ホスト内部の配線 です。公開 API ではないので、補完候補にもこのリファレンスにも出てきません。直接触るコードは将来のバージョンで壊れますCLIP_BEAT/CLIP_TIMEはラッパー内のletバインディング。各コールバック直前に__sync()が更新するので、ユーザのクロージャは 呼び出し時点の値 を見ますsetNote/setCcのidはcrypto.randomUUID()で自動付与setCcの同じccNumberは内部Mapに bucket されたあと、レーンごとにbeat昇順でソートされて返されます