コンテンツにスキップ
ドキュメント
ダウンロード
日本語

状態とイベント

Sound Show は認証後と状態変更時に現在のショーの状態を送信します。このスナップショットからコントローラーを構築し、コマンドリファレンスを使って変更を要求してください。

state メッセージはローカルの状態全体を置き換えます。hello_ok の後、全体的な変更やプロジェクト変更後、および request_state への応答として自動的に届きます。この簡略例は主なフィールドを示しています。実際のプロジェクトには値の入った配列や追加のメタデータが含まれます。

{
"type": "state",
"protocol_version": 2,
"server_time_ms": 1712345678901,
"state": {
"project": { "name": "My show", "categories": [] },
"players": [],
"playable_runtime": [],
"playlists": { "current_index": 0, "playlists": [] },
"instructions": { "current_index": 0, "players": [] },
"timers": [],
"logical_devices": [],
"control": {
"next_sound_volume": 70,
"selected_category_id": null,
"low_volume_active": false,
"volume_level_presets": [],
"active_volume_level_ids": [],
"effective_volume_level_id": null
},
"capabilities": { "audio_controls": [], "manual_grid_layout": false },
"audio_controls": { "revision": 1, "logical_devices": [], "players": [] },
"lights": { "revision": 1, "lights": [] }
}
}

プロジェクトの欠落や null、空の配列、任意フィールド、新しく追加された未知のフィールドに対応してください。本書は現在の v2 実装を説明しています。古い Sound Show 2 ではフィールド、コマンド、機能が少ない場合があります。任意機能を使う前に hello_ok.features と状態内の機能情報を確認してください。

ドメインごとのライブスナップショット

「ドメインごとのライブスナップショット」セクション

完全な状態の後は、ほとんどの実行時更新が一つのドメイン(名前の付いた状態の一部分)のみを置き換えます。空の配列にも意味があります。例えば players: [] は現在のプレーヤーをすべて消去します。配列要素を古い一覧にマージしないでください。

メッセージの type 置き換えるローカルフィールド 送信条件
control_snapshot control 音量レベルコマンドへの直接応答。runtime_snapshots があれば全操作者の制御変更。
players_snapshot players プレーヤーの変更。
instructions_snapshot instructions runtime_snapshots がある場合。
playlists_snapshot playlists runtime_snapshots がある場合。
playable_runtime_snapshot playable_runtime runtime_snapshots がある場合。
logical_devices_snapshot logical_devices 出力の変更。
timers_snapshot timers タイマーの変更。
audio_controls_snapshot audio_controls エフェクトの変更。revision を比較。
lights_snapshot lights 照明の変更。revision を比較。

各ドメインメッセージには、対応する名前のペイロードフィールドと server_time_ms があります。例:

{
"type": "players_snapshot",
"players": [],
"server_time_ms": 1712345678901
}

音声コントロールと照明のメッセージには、ドメインのリビジョンも含まれます。

{
"type": "audio_controls_snapshot",
"revision": 5,
"audio_controls": { "revision": 5, "logical_devices": [], "players": [] },
"server_time_ms": 1712345678901
}

この二つのドメインでは、適用済みのリビジョンより小さいメッセージを無視してください。異なるドメイン間で比較してはいけません。完全な state は基準をリセットします。切断時にはローカル状態とリビジョン追跡をすべて消去します。その他のドメインは受信順に適用します。

hello.features で runtime_snapshots を要求してください。指定しない場合、直接の制御応答を除き、コントロール、命令、プレイリストの変更はまとめられた完全な状態で届きます。この機能のないクライアントには、再生要素のアクティビティだけの変更では完全な状態を送信しません。

実行時の変更は 50 ms 周期でまとめられます。50 ms ごとの送信や、途中の変更すべてを記録するイベントログを保証するものではありません。完全な状態が保留中のドメイン更新を置き換える場合があります。全種類のスナップショットを扱うリデューサーは JavaScript の例を参照してください。

目的 使用する値
プロジェクトのボタンを起動 project.categories[].playables[].id を playable_id として使用。
カテゴリーを選択 project.categories[].id を category_id として使用。
実行中の一つのインスタンスを停止、一時停止、シーク、調整 players[].id を player_id として使用。
出力を操作 logical_devices[].id を logical_device_id として使用。
音量プリセットを有効化 control.volume_level_presets[].id を level_id として使用。
エフェクトを調整 ディスクリプターの id を control_id とし、現在のプレーヤーまたは論理デバイスのターゲットを指定。
照明レベルを設定 lights.lights[].id を project_light_id として使用。
プレイリストの項目を再生 現在のプレイリストとキューの 0 始まりの位置。
命令を選択 現在の命令プレーヤーの 0 始まりの位置と項目の path。
タイマーを操作 現在の 0 始まりの位置を timer_index として使用。

プレーヤー ID は実行中のインスタンスを表し、恒久的なプロジェクト ID として保存してはいけません。一つの再生要素から複数のプレーヤーが実行される場合があります。現在のスナップショットで一致する source_playable_id または state_playable_id を探し、操作するインスタンスを明示的に選んでください。名前は表示に便利ですが、一意とは限りません。

project.categories[] にはカテゴリーの id、name、color、is_hidden、任意の icon_url、playables が含まれます。再生要素には次の情報があります。

{
"id": "playable-id",
"name": "Wind",
"kind": "sound",
"color": "#3b82f6",
"tags": [],
"is_searchable": true,
"runtime_state_key": "playable-id",
"target_category_id": null
}

一般的な種類は sound、video、image、random、timer、playlist、action、webbrowser、category_link、instruction です。新しい種類や unsupported にも対応してください。任意のメタデータには icon_url、キーボードショートカット、設定されたクリックアクション ID があります。

playable_runtime の各項目には state_playable_id、play_count、is_locked、is_playing があります。再生要素の runtime_state_key(なければ id)を state_playable_id に対応付けます。リンクされた再生要素はアクティビティとロック状態を共有できます。このプロジェクトボタンのロックは、実行中プレーヤーの操作ロックとは別です。

カテゴリーには任意の grid_layout が含まれ、その中に enabled、columns、rows、pages、positions、revision があります。利用前に capabilities.manual_grid_layout を確認してください。クライアントはこのメタデータを無視して通常の再生要素一覧を表示しても構いません。レイアウトを編集する遠隔コマンドはありません。

{
"id": "runtime-player-id",
"kind": "sound",
"title": "Wind",
"color": "#3b82f6",
"panel_position": 0,
"position_seconds": 12.3,
"duration_seconds": 95,
"start_time_seconds": 5,
"stop_time_seconds": 90,
"timeline_duration_seconds": 95,
"volume_percent": 70,
"muted": false,
"is_playing": true,
"is_locked": false,
"loop_enabled": true,
"output_logical_device_id": "main",
"source_playable_id": "playable-id",
"state_playable_id": "playable-id"
}

表示には volume_percent をそのまま使います。別の生データフィールド volume があっても、100 倍して百分率にする値ではありません。アプリは非線形の音量スケールを使用する場合があります。

タイムラインの単位は秒です。start_time_seconds、stop_time_seconds、timeline_duration_seconds は存在しないか null の場合があります。シーク時にトリミングされた再生範囲をソースのタイムラインへ対応付けるために使います。他の任意フィールドは remaining_playback_seconds、markers、icon_url です。

logical_devices[] は id、label、display_label、color、output_role、available、selected、volume_percent を提供します。表示には display_label、コマンドには id を使います。

control は次の起動音量、選択カテゴリー、volume_level_presets、active_volume_level_ids、effective_volume_level_id を公開します。プリセットには id、name、direction(both、up_only、down_only のいずれか)、target_volume_percent、fade_down_ms、fade_up_ms が含まれます。low_volume_active は古いコントローラー用です。他の操作者も変更できるため、有効な ID はスナップショットから読み取ってください。

プレイリスト、命令、タイマー

「プレイリスト、命令、タイマー」セクション
ドメイン 主なフィールド
playlists current_index と playlists[]。各プレイリストは id、title、autoplay、loop_enabled、queue を持ちます。キュー項目には playable_id、title、任意の icon_url があります。実行時メタデータには active_item_id、active_item_title、active_player_id が含まれる場合があります。
instructions current_index と players[]。プレーヤーは id、title、has_instruction、waiting_for_go、next_instruction_text、items を持ちます。項目は item_id、title、kind、depth、path、can_set_next、is_next_to_play、is_visibly_playing を公開します。
timers 数値の id、mode、is_running、is_locked、elapsed_ms、duration_ms、remaining_ms、is_over_time を持つ配列。タイマーの時間はミリ秒です。

GO のプレビューには、path が空でなく kind が group 以外の、最も深い次の項目を優先します。なければ最も深い次の項目のいずれかを使います。次の項目を選ぶ際は、タイトルや深さから推測せず、実際の path を使用します。

capabilities.audio_controls[] は利用可能なエフェクトコントロールを示します。各ディスクリプターの内容:

フィールド 意味
id, label プロトコルの識別子と表示ラベル。
scopes 対応するターゲット種類:logical_device、player、または両方。
min, max, default_value ネイティブ単位の数値範囲とリセット値。
suggested_step 推奨する調整幅。
unit, scale 表示単位とコントロールのスケール。log2 はオクターブ単位の幅を表します。
display_multiplier 表示時にネイティブ値に掛けます。ユーザー入力は送信前にこの値で割ります。

例えば、比率 0.5 に display_multiplier: 100 がある場合、表示は 50% です。範囲を固定せず、公開されたカタログを使ってください。

audio_controls には revision、logical_devices[]、players[] があります。各ターゲットは kind、id、label、available、locked と、コントロール ID をネイティブ数値に対応付ける values オブジェクトを持ちます。線形コントロールは線形に、log2 コントロールは幾何級数的にフェードします。

lights は revision と lights[] を含みます。各照明には id、name、capability(現在は level)、available があります。接続先アダプターが利用できない場合は value_percent が省略されます。ゼロとして扱わず、利用不可と表示してください。

スナップショットには、/asset/file?id=opaque-asset-id のような不透明な相対 icon_url が含まれる場合があります。リモートサーバーの HTTP アドレスを基準に解決し、HTTP で取得します。アイコンは WebSocket メッセージではありません。

ID を組み立てたりファイルパスを送信したりせず、提供された URL を使ってください。現在のサーバーセッション中はキャッシュし、再起動後は ID が変わることを想定します。プロジェクト変更で取得できなくなる場合に備え、画像取得失敗時の代替表示を用意してください。スナップショットにローカルのファイルパスは公開されません。

認証済みの接続では、次を受信する場合があります。

{
"type": "error",
"code": "invalid_value",
"command_id": "eq-change-1",
"message": "Human-readable error description"
}

code と command_id は任意です。音声・照明のコマンドエラーには unknown_control、target_locked、target_not_found、invalid_value、target_unavailable、command_failed が含まれる場合があります。古いコマンド処理は message だけを返すことがあります。人間向けの文面を安定したエラーコードとして解析しないでください。エラー後も接続が維持されることがあるため、スナップショットの処理を続けます。

制限 クライアントの対応
最初のメッセージは 10 秒以内 接続直後に hello を送信。
WebSocket 同時接続数は 16 接続を再利用し、未使用のクライアントを閉じる。未認証ソケットも含まれます。
受信メッセージ/フレームは 64 KiB まで 一つのメッセージに小さな JSON コマンドを一つ送信。
最大 60 コマンドのバースト、接続ごとに毎秒 20 補充 スライダーを間引き、保留値をまとめる。ping と更新要求もこの枠を使います。
同じ送信元から 60 秒以内に 5 回のパスワード誤り パスワードを修正して待つ。新規アップグレードは 60 秒間拒否され、HTTP 429 と Retry-After が返ります。

レート制限で拒否されたコマンドは適用されず、code: "rate_limited" を返します。超過が続くと接続が閉じる場合があります。HTTP 応答を取得できるクライアントは Retry-After に従ってください。認証失敗が続いた後にブラウザーが単なる接続失敗しか報告しない場合は、再試行まで最低 60 秒待ちます。再生、切り替え、GO を無条件で再送しないでください。二重実行になる可能性があります。

hello_ok.features に ping が含まれる場合、認証済みクライアントは {"type":"ping"} を送り {"type":"pong"} を期待できます。内蔵リモコンは 10 秒ごとに ping を送り、その確認時に有効なメッセージを 25 秒以上受信していなければ再接続します。変更のないドメインは、それ以外では無通信のことがあります。

常時接続するコントローラーでは:

  1. ソケットが閉じるかハートビートに失敗したら、直ちにコマンド送信を無効にします。
  2. キャッシュした状態、プレーヤー ID、リビジョンの追跡を消去します。
  3. 上限付きの待機時間で再接続し、失敗が続けば待機時間を延ばします。認証エラーとレート制限を守り、連続再試行しないでください。
  4. 新しい hello を送り、新しい完全な state を待ち、ターゲットを再取得します。
  5. その状態で操作を再有効化します。保留していた起動、切り替え、GO コマンドは再接続後に再実行しません。

接続に属する音量の押下状態は切断時に解除されます。サーバーアドレスが同じでも、再接続は新しいセッションとして扱ってください。

Sound Show の外部オリジンを許可を変更すると、即座に適用され、現在のクライアントが切断されます。無効にした後は、別オリジンのブラウザーから再接続できません。設定とブラウザーの制約はカスタムブラウザーリモコンを参照してください。