SyncSing Studio
Developers

拡張機能とアプリのやり取り(プロトコル・API 1.1)

C# の SDK(../dotnet/SyncSing.Plugin.Sdk)・Python の雛形(../templates/python-basic/plugin.py)は、ここに書いたことを実装しています。別の言語で作るときの決まりです。

1. 起動と接続

  1. アプリが拡張機能の EXE を起動します(作業フォルダ = manifest.json のあるフォルダ・ウィンドウなし・優先度は「通常より下」)。
  2. 標準入力の 1 行目 に接続情報(JSON)が届き、そのあと標準入力は閉じます。
    {"syncsing": {"protocol": 1, "pipe": "\\\\.\\pipe\\LinkStudio.SyncSing.studio.12345.9b1c…", "transport": "pipe",
                  "token": "…", "plugin_uuid": "…", "app": "studio", "app_version": "0.28.0", "channel": "stable",
                  "api_version": "1.0", "locale": "ja", "data_dir": "C:\\…\\data", "log_dir": "C:\\…\\logs"}}

    data_dir・log_dir は環境変数 SYNCSING_PLUGIN_DATA_DIR・SYNCSING_PLUGIN_LOG_DIR にも入っています。

  1. 10 秒以内 に pipe(名前付きパイプ)を開き(C# なら NamedPipeClientStream、ほかの言語ならファイルとして \\.\pipe\… を読み書き)、最初の 1 通として hello を送ります。
  2. パイプは今のユーザーだけが開け、アプリは接続してきたプロセスが自分の起動した EXE(またはその子プロセス)かを確かめます。

2. メッセージ

hello

{"id": 0, "method": "hello", "params": {"token": "(標準入力で受け取ったもの)",
  "plugin": {"uuid": "manifest の uuid", "version": "manifest の version"},
  "api": {"version": "1.0"}, "sdk": {"name": "my-sdk", "version": "0.1.0"}}}

返事の result: session・api_version・app {kind, version, channel, locale, features}・plugin {tier, name}・granted(許可された権限)・denied・settings・dirs {data, log}・limits・ping_interval_ms。 トークン・uuid・version・API の版が違うと error を返して切断し、再起動しません。

ホストからの要求

メソッドいつ返事
ping15 秒ごと{}
plugin.activatehello の直後{"ok": true}
plugin.shutdown停止・アプリの終了・無効化・更新(reason){"ok": true} を返して 5 秒以内に終了
command.invokeパネルのボタン・設定の button・Stream Deck・ミニUI・ショートカット(command・source・pressed。hold: true のコマンドは押したとき down・離したとき up){"ok": true} か {"ok": false, "message": "…"}
tool.runツール枠の「実行」(tool){"ok": true}
settings.changed利用者が設定を変えた(settings・changed){}

上の 6 つ(API 1.0 の要求)には必ず返事をしてください(command.invoke で失敗を伝えるときも {"ok": false, "message": …} の result で)。 command.invoke・tool.run は すぐ返事をしてください。時間のかかる処理(通信・計測・ファイルの書き出しなど)は、先に panel.update で「〜しています…」を出してから裏(別スレッド)で行い、終わったらもう一度 panel.update します。アプリは押したボタンを返事が来るまで「実行中…」にし、返事のあとすぐパネルを取り直します(tool.run は 10 秒で打ち切り)。 読み取り(返事を受け取る)スレッドの中で、アプリへの要求の返事を待たないでください。待つとその返事を読めず、必ず時間切れになって「押したのに何も起きない」になります。イベント(event)の処理も読み取りスレッドではなく別のスレッドで、届いた順に行ってください(1.2 の SDK はどちらも守ります)。 押した結果の伝え方(右下の通知): 返事が {"ok": false, "message": "…"} ならその文が出ます。{"ok": true, "message": "…"} なら成功の文としてその文が出ます。message を省いた {"ok": true} のときは、そのあと 8 秒のあいだにパネルの先頭の status 部品が変わればその文が出ます(warn / error は ⚠ 付き)。つまり「すぐ返事をして裏で確かめる」作りでも、裏で断った理由(「処理が止まっています」など)を先頭の status に出せば利用者に届きます。2 秒たっても状態が変わらなければ「実行しました」と出ます。 経過時間・残り時間(計測中のカウントなど)は timer 部品で出してください(elapsed_s・total_s・running)。画面は 2 秒ごとに取り直すので、秒数を文字として送り続けると飛び飛びに見えます。timer は画面が 1 秒ごとに自分で進めます。 panel.update を別々のスレッドから同時に送ると、届く順番が入れ替わって古い画面が残ることがあります。最後に作った内容だけを 1 本のスレッドで順番に送ってください(1.2 の SDK の Panel(...).Send / client.panel(...).send)。 押したのに画面が変わらないときの調べ方: アプリの app.log に command.invoke 〇〇 → ok(N ms) と、プラグインの plugin.log(plugins\<uuid>\logs\)に受け取った記録が残ります。アプリ側が ok でプラグイン側に記録が無ければ要求の処理、記録はあるが画面が変わらなければ panel.update(断られた理由も plugin.log に出ます)を疑ってください。 {"error": {"code": "not_supported"}} を返してよいのは、この版に無い 知らない要求 だけです(syncsing_plugin.py test も、6 つへの error は失敗として扱います)。

3. エラーコード

code意味もう一度試してよいか
parse_errorJSON として読めない(切断します)×
invalid_requestid・method・params の形が違う×
method_not_foundこの版に無いメソッド×
invalid_params引数の型・範囲・長さが違う(data.field)×
unauthorizedhello の前・トークンの不一致・改ざん×
permission_denied権限が無い(data.permission)×
rate_limited回数の上限(data.retry_after_ms)○
busy今は受け付けない(処理中のデバイス変更・配信中・録音中など。data.reason)○
locked利用者の設定で禁止(キー変更ロック・規約に未同意など)×
not_availableこの構成に無い(NC Lite で使えない機能・OBS 未接続など)×
not_found指定したものが無い×
user_declined利用者が断った×
timeout時間内に終わらなかった○
internalその他○
api_version_unsupportedAPI の版が合わない(hello だけ)×

4. 回数と量の上限

5. プロセスの決まり

6. API の版

7. 音専用のパイプ(API 1.1)

audio.open の返事の pipe を、命令用のパイプと同じように開きます(その拡張機能のプロセスだけが開けます。10 秒以内に開かないと閉じます)。JSON ではなく、次の 塊 を続けて流します。どちら向きも同じ形です。

位置大きさ中身
01 バイト種類: 1 = アプリ → 拡張機能(マイクの声)、2 = 拡張機能 → アプリ(乗せる音)
11 バイト速さ: 1 = 16000 Hz、2 = 24000 Hz、3 = 48000 Hz
22 バイト中身のバイト数(偶数・100 ms ぶんまで)
44 バイト順番(1 ずつ増える)
88 バイト時刻(µs。アプリの音の時計。拡張機能から送るときは 0 でよい)
16中身のバイト数符号付き 16 ビット・モノラル PCM

この資料は開発キットに入っているものと同じ内容です。拡張機能の作成・配布には拡張機能 開発者規約が適用されます。