DOCS
書き方
ポートを宣言して、つなぐ。それだけです。下の図はすべて、左のソースを公開済みパッケージでビルド時にコンパイルした結果で、描き直したものではありません。
01入れる
コマンドラインで使う場合は CLI を入れます。
pnpm add @love-rox/kumihimo-cli
kumihimo build studio.khm -o studio.svg # 描く
kumihimo check studio.khm # 検証だけ
kumihimo build studio.khm --watch # 保存で描き直す
kumihimo export studio.khm drawio # 編集できる draw.io ファイル
kumihimo export studio.khm cable --stdout # ケーブル表を TSV でkumihimo と khm の両方の名前で入ります。警告は既定ではビルドを失敗させません。失敗させたいときは --strict を付けます。
02最初の一枚
機材を宣言して、ポートどうしをつなぎます。拡張子は .khm です。
device pc "ノートPC" as computer { out HDMI : hdmi }
device mon "モニター" as display { in HDMI : hdmi }
pc.HDMI -> mon.HDMI : hdmi 5m "V-01"device <id> "<ラベル>" as <種別> の <id> は接続を書くときの名前で、図には出ません。図に出るのは "<ラベル>" の方です。
03機材とポート
device <id> "<ラベル>" as <種別> {
in <ポート指定> : <信号> # 入力
out <ポート指定> : <信号> # 出力
io <ポート指定> : <信号> # 双方向(Dante, Ethernet …)
@model "HyperDeck Studio HD Mini"
}ポート指定には4つの書き方があります。16ch の卓を16行書く必要はありません。
| 書き方 | 例 | 展開 |
|---|---|---|
| 単体 | SDI | SDI |
| 並べる | L, R | L, R |
| 数の範囲 | 1..4 | 1, 2, 3, 4 |
| 接頭辞つき範囲 | CH[1..16] | CH1 … CH16 |
device mixer "Yamaha DM3" as mixer {
in CH[1..16] : xlr
out L, R : xlr
io DANTE : dante
}
device amp "アンプ" as amplifier { in IN_L, IN_R : trs }
mixer.(L, R) -> amp.(IN_L, IN_R) : trs 3m "A-10"宣言した順序がそのまま描かれます。IN 1 が IN 2 の下に来ることはありません。機材の中での位置は意味を持つ情報なので、並べ替えません。
端子が等間隔で16個並ぶと、16個の同じものにしか見えません。実機はそうではなく、HDMI が4つ、SDI が4つ、アナログが2つ、と面板が余白で区切っています。gap の行を置くと、その次に宣言されるものの上に余白が入ります。
device sw "ATEM Mini Extreme" as switcher {
in 1..4 : hdmi
gap
in 5..8 : sdi
gap 2
in AUDIO_L, AUDIO_R : trs
out PGM : sdi
gap
out STREAM : lan
}gap ひとつがポート間隔の半分、gap 2 で1ポート分です。連続して書けば加算されます。複数のポートに展開される宣言でも、余白が入るのは最初の1つの前だけです。in CH[1..16] の上の gap は CH1 の前の1箇所であって、16箇所ではありません。
これは見た目だけの指定です。ポートも結線も一覧表も変わりません。すべての gap を消した図は、同じ系統を表します。
種別は形を決めます。camera switcher mixer recorder player display projector speaker microphone amplifier computer converter matrix patchbay router interface generic から選びます。省略すると generic です。
@ で始まる行は任意のメタデータです。図には出ませんが、機材表と各種エクスポートに載ります。
04つなぐ
<機材>.<ポート> <矢印> <機材>.<ポート> : <信号> <修飾子>*| 矢印 | 意味 |
|---|---|
-> | 一方向。信号が流れる向き |
<-> | 双方向(Dante, Ethernet, 制御 …) |
-- | 向きなし(電源 …) |
修飾子は順不同で、どれも省略できます。
| 書き方 | 意味 | 例 |
|---|---|---|
<長さ> | ケーブル長 | 10m 30cm 2.5m 3ft |
"<ラベル>" | ケーブル番号や名前 | "V-01" |
via "<部品>" | 変換ケーブルやアダプタ | via "HDMI-DVI変換" |
[k=v, …] | 任意の属性 | [connector=BNC, color=青] |
device mixer "卓" as mixer { out L, R : xlr io DANTE : dante }
device amp "アンプ" as amplifier { in IN_L, IN_R : trs in AC : ac }
device netsw "スイッチ" as router { io 1 : dante }
device pdu "電源" as generic { out 1 : ac }
mixer.(L, R) -> amp.(IN_L, IN_R) : trs 3m "A-10" [color=青]
mixer.DANTE <-> netsw.1 : dante 10m "N-01"
pdu.1 -- amp.AC : ac 2m両端が同じ数のポートを並べると、順に対応します。mixer.(L, R) -> amp.(IN_L, IN_R) は2行書くのと同じです。
[color=…] はケーブルの被覆の色です。信号種別の既定色を上書きします。これは装飾ではなく、現場で線を特定する手段(「青いのを1番へ」)なので、色はケーブル表にも持ち越されます。日本語名(赤・青・緑・黄・橙・桃・紫・黒・白・灰・茶)と英語名、#0af のような16進が使えます。解釈できない値は診断になり、図には届きません。
長さは数と単位だけの裸のトークンです。# は行コメント専用なので、長さの前に付くことはありません。
05まとめる
場所・ラック・サブシステムを枠で囲みます。入れ子は v0.1 では1段までです。
diagram "会場" { direction: LR }
group stage "ステージ" {
device cam1 "FX3" as camera { out SDI : sdi }
device mic1 "SM58" as microphone { out OUT : xlr }
}
group rack "ラック" {
device sw "ATEM" as switcher { in 1..2 : sdi out PGM : sdi }
device mixer "DM3" as mixer { in CH[1..2] : xlr out L, R : xlr }
}
cam1.SDI -> sw.1 : sdi 30m "V-01"
mic1.OUT -> mixer.CH1 : xlr 20m "A-01"06信号と互換判定
これが図を描くだけの道具との違うところです。すべての接続が「物理的に成立するか」を判定されます。
判定は両端のポートが宣言した型で行います。接続行の : <信号> が表すのはケーブルで、型を宣言していない端の穴埋めにしか使われません。ここを取り違えると、型を自分自身と比べることになり、どんな不一致も検出できなくなります。
| 判定 | 意味 | 扱い |
|---|---|---|
ok | 普通の接続 | 黙る |
lossy | 通るが何かを失う、または部品が要る | 警告 |
incompatible | 能動変換なしには成立しない | 報告 |
検出する価値があるのは、プラグがぴたりと嵌まり、見た目に何もおかしくなく、そして何も出ない組み合わせです。下は2本ともコネクタを共有しているだけの配線です。
device ext "HDBaseT受信器" as interface { out CAT : hdbaset }
device netsw "L2スイッチ" as router { in 1 : lan }
device cam "カメラ" as camera { out SDI : sdi }
device fs "フレームシンクロナイザ" as generic { in REF : genlock }
ext.CAT -> netsw.1 : hdbaset 20m "N-01"
cam.SDI -> fs.REF : sdi 5m "V-90"- warning
signal-mismatchext.CAT → netsw.1: HDBaseT は Cat ケーブルと RJ45 を使うが Ethernet ではない。スイッチには挿せない - warning
signal-mismatchcam.SDI → fs.REF: BNC を共有するだけ。同期基準入力に映像を入れてもロックしない
判定には必ず理由が付き、その理由はケーブル表にも残ります。同種の組み合わせは他にも dmx↔xlr、rca↔spdif、adat↔spdif、composite↔component、wordclock↔sdi があります。
現場の取り決めは compat で一度だけ、理由と一緒に書けます。理由は診断とケーブル表に流れるので、なぜ許したのかが図から失われません。
compat aes -> xlr : ok "社内基準:10m 未満なら可"
compat xlr -> rca : lossy "必ず DI を通す"主な診断コードです。既定の重さは設定で変えられます。
| コード | 意味 | 既定 |
|---|---|---|
signal-mismatch | 両端が信号について食い違っている | 警告 |
adapter-required | アダプタが要るのに宣言がない | 警告 |
adapter-insufficient | via はあるがケーブルでは橋渡しできない | エラー |
direction-mismatch | 出力どうし、入力どうし | エラー |
port-overbooked | 1つの入力に複数の送り出し | エラー |
implicit-device | 宣言していない機材を参照した | 警告 |
unconnected-port | 宣言したのに何もつながっていない | 既定オフ |
例外は投げません。どの段階も診断を集めて最善の結果を返すので、誤りのある図もちゃんと描かれます。欠陥を見つけるために必要なのは、まさにその欠陥が写った絵だからです。
07変換をはさむ
受動的なアダプタや変換ケーブルが途中に入ることを宣言します。
device pc "PC" as computer { out HDMI : hdmi }
device mon "モニター" as display { in DVI : dvi }
pc.HDMI -> mon.DVI : hdmi 2m "V-01" via "HDMI-DVI変換ケーブル"via は警告を黙らせる道具ではありません。部品を資材表に載せる宣言です。線には変換の印が付き、アダプタが明細に出ます。
ケーブルで本当に橋渡しできる組み合わせ(HDMI↔DVI、DP→HDMI など)なら via で診断が消えます。宣言しなければ報告され、必要な部品の名前が示されます。一方、どんなケーブルでも橋渡しできない組み合わせ(SDI→HDMI など)では via を書いても診断は消えません。それは電源の要る箱なので、ケーブルの属性ではなく機材として図に置くべきものです。
# 誤り:SDI を HDMI に変えるケーブルは存在しない
cam.SDI -> mon.HDMI : sdi via "SDI-HDMI変換"
# 正しい:変換器は機材
device conv "BMD Mini Converter SDI-HDMI" as converter {
in SDI : sdi
out HDMI : hdmi
}
cam.SDI -> conv.SDI : sdi
conv.HDMI -> mon.HDMI : hdmi08無線
無線区間はケーブルではないので、長さの代わりに周波数やチャンネルを持ちます。線は破線で描かれます。
device mic "ワイヤレスマイク" as microphone { out RF : uhf }
device rx "受信機" as interface {
in RF : uhf
out CH1 : xlr
}
device desk "卓" as mixer { in CH1 : xlr }
mic.RF -> rx.RF : uhf [ch=38]
rx.CH1 -> desk.CH1 : xlr 3m "A-01"受信機を通さずマイクを卓へ直結すれば、そこで指摘が出ます。無線と有線の境界も判定の対象です。
09機材ライブラリ
卓の16chを図ごとに書き直す必要はありません。model で一度定義し、device … from で何台でも実体化します。
model dm3 "Yamaha DM3" as mixer {
in CH[1..16] : xlr
out L, R : xlr
@vendor "Yamaha"
}
device foh from dm3
device mon from dm3 "モニター卓"別ファイルに置いたものは use で読み込みます。読み込んだファイルに機材や接続が書かれていた場合は無視され、警告になります。ライブラリは定義を置く場所であって、図を置く場所ではないためです。
use "lib/yamaha.khm"
device foh from dm3 "FOH卓"10見た目
diagram "Studio A" {
direction: LR # LR(左→右、既定)| TB(上→下)
theme: light # light(既定)| dark | mono | blueprint
spacing: 60 # 機材どうしの間隔(px)
}| テーマ | 用途 |
|---|---|
light | 既定。画面とカラー印刷 |
dark | 暗い画面 |
mono | 白黒印刷とコピー |
blueprint | 設備図面の青焼き調 |
mono は色をいっさい使いません。信号は線種で区別され、[color=…] で指定した被覆色は無視されます。コピーを通った色が残っているふりをしても誰の役にも立たないからです。
diagram "白黒印刷用" { theme: mono }
device cam "カメラ" as camera { out SDI : sdi }
device sw "スイッチャー" as switcher { in 1 : sdi out PGM : sdi }
device rec "レコーダー" as recorder { in SDI : sdi }
cam.SDI -> sw.1 : sdi 30m "V-01" [color=青]
sw.PGM -> rec.SDI : sdi 2m "V-10"テーマは -t/--theme でも渡せますが、ソース中の diagram { theme: … } が勝ちます。図は自分がどう見えるべきかを知っていて、呼び出し側は既定値しか知らないからです。
11出力と埋め込み
SVG のほか、編集できる draw.io ファイル、ケーブル表・機材表の TSV が出せます。
Markdown には kumihimo のコードフェンスをそのまま埋め込めます。ビルド時に SVG になります。
```kumihimo
cam.SDI -> sw.1 : sdi 30m "V-01"
```
import rehypeKumihimo from '@love-rox/kumihimo-rehype';
unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeKumihimo, { theme: "dark", onDiagnostics: report })
.use(rehypeStringify);React・Vue・Astro のコンポーネントもあります。コンパイルは非同期で、新しい図ができるまで前の図が画面に残り、遅れて終わった古いコンパイルが新しい結果を上書きすることはありません。
import { Kumihimo, useKumihimo } from '@love-rox/kumihimo-react';
<Kumihimo source={src} theme="dark" onDiagnostics={report} />;
const { svg, diagram, diagnostics, pending, error } = useKumihimo(src);onDiagnostics を使えば、誤配線のきれいな絵をそのまま公開してしまうことを避けられます。