コンテンツにスキップ
JA

Command vs Clip

同じ play(id) 呼び出しが、kit manifest の bucket に応じて 2 つの再生モードに 自動で分かれます。呼ぶ側のコードはどちらでも 1 行で、モードを意識しません。

分岐は EventMap(触覚ファイル = kit manifest) の情報で決まります。考え方の共通解説は Fire と Clip の違い も参照してください。

manifest の bucketモード何が起きるか事前 deploy
eventscommandSDK は PLAY を送り、デバイスが内蔵 clip を再生必要(Studio で kit 書込)
stream_eventsclipSDK が WAV を読み込み(clipBase + clipLoaderUDP ストリーム不要
import { connect, EventMap } from "@hapbeat/sdk";
const manifest = await fetch("/my-kit/my-kit-manifest.json").then((r) => r.json());
const hb = await connect({
eventMap: EventMap.fromManifest(manifest),
clipBase: "/my-kit/stream-clips/", // clip モードの WAV 置き場
});
hb.play("sample-kit.sine_100hz"); // command → デバイスが内蔵 clip を再生
hb.play("rain.loop"); // clip → SDK が WAV をストリーム
hb.stop("rain.loop"); // clip は再生中のストリームを終了

eventMap を渡していなければ、SDK は bucket の情報を持たないためすべて command として送られます(このとき gain の既定値は 1.0)。

commandclip
推奨量産・本番運用の短い one-shot試作・長尺・差し替えの多い段階
遅延小・安定やや大きめ・環境依存
事前 deploy要(Studio で kit 書込)不要(WAV を置くだけ)
触覚の差し替えkit を再 deployWAV を置き換えるだけ

迷ったら 試作は clip、固まったら command が基本です。command はデバイス内蔵の clip を呼ぶだけなので最も軽量で安定し、clip は WAV をその場で流せるので配備の手間 なくイテレーションできます。

clip モードでは、SDK が manifest の clip ファイル名を clipBase に連結し、 clipLoader で WAV を読み込んでからストリームします。既定の loader は環境ごとに 切り替わります。

環境clipBase既定 clipLoader
Nodeディレクトリパスfs.readFile
BrowserURL プレフィックスfetch
// Browser: 静的アセットとして配信した kit を URL で解決
const hb = await connect({
eventMap: EventMap.fromManifest(manifest),
clipBase: "/my-kit/stream-clips/",
});
// Node: ディレクトリパスで解決
const hb = await connect({
eventMap: EventMap.fromManifest(manifest),
clipBase: "./kits/my-kit/stream-clips/",
});

バンドルや IndexedDB から読み込みたい場合は clipLoader を上書きします。

const hb = await connect({
eventMap: EventMap.fromManifest(manifest),
clipLoader: async (ref) => loadFromBundle(ref), // ArrayBuffer | Uint8Array を返す
});

clipBase / clipLoader を設定していない clip イベントを play すると、SDK は WAV を読めず警告を出して何も鳴らしません。command モードのイベントには不要です。

gain の既定値は kit manifest の intensity です。playgain を明示すると それで上書きされ、二重には掛かりません。clip モードでも SDK は PCM を加工せず、 gain を STREAM_BEGIN.gain にのみ畳んで送り、デバイスが一度だけ適用します (command の gain と同じ意味)。

hb.play("rain.loop"); // intensity(kit の既定値)で発火
hb.play("rain.loop", { gain: 0.3 }); // 呼び出し側で上書き

gain は絶対値 0..1 で、SDK 側で clamp されます。再生中の clip に対して gain を 途中で変調する API はありません(Not implemented の連続変調は openStream を使います)。

  • clip WAV は 16 kHz モノ PCM16 で用意してください。デバイスは 16 kHz 再生で、 SDK は resample しません(非 16 kHz は警告のみ)。
  • 1 度に流せる stream は 1 本(session 単位)。新しい clip・streamPcmopenStream を始めると、前の stream は自動的に終了します。
  • 同じ id を eventsstream_events の両方に書いた場合(Studio の BOTH)は、 manifest 読み込み時に stream_events が後勝ちで上書きするため clip 側が優先 されます。

Browser と Node の clip ターゲティング差

Section titled “Browser と Node の clip ターゲティング差”

command モードの target は両トランスポートで同じように効きます。一方 clip モード は helper(Browser)経由だと挙動が異なります。

  • Browser(helper WS 経由): clip 再生は helper が認識している全デバイスに 届きます(デバイス単位の clip ターゲティングは未対応)。また targetTimeUs は helper 経由では無視され、常に即時再生になります。
  • Node(直接 UDP): clip も command と同様に target でアドレッシングできます。

ターゲット構文の詳細は Address の仕組み を参照してください。