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 を付けます。
VS Code で書くなら拡張があります。書きながら診断が出て、隣に図が出ます。 判定は下の「信号と互換判定」と同じもので、コンパイラがそのまま動いています。
code --install-extension love-rox.kumihimo-vscodeプレビューは ⌘K V(Windows / Linux は Ctrl+K V)、またはタイトルバーのボタンです。エディタの配色に追随し、図・ケーブル表・機器表・変換部材をタブで切り替えられます。補完はコンパイラの一覧そのものから出ます。文言も診断も VS Code の表示言語に追従します。詳しくは[トップページの VS Code の節](/ja#vscode)。
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まとめる
場所・ラック・サブシステムを枠で囲みます。
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"group は group を入れられます。会場の中にステージとラックがあり、ステージの中にカメラがある。現場を歩く人にとってはどちらの階層も実在し、箱が実際に置かれている場所を指すのは一番内側です。
diagram "会場" { direction: LR }
group venue "幕張メッセ" {
group stage "ステージ" {
device cam "FX3" as camera { out SDI : sdi }
}
group rack "ラック" {
device sw "ATEM" as switcher { in 1 : sdi }
}
}
cam.SDI -> sw.1 : sdi 30m "V-01"機材が属するのは、それが書かれた group であって親ではありません。機材表に出るのは一番内側の名前です。人が歩いていく先がそこだからです。
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使える単語
ここに出ている語は、このページが公開済みのパッケージから読み出したものです。手で書き写した一覧ではないので、コンパイラが受け付けない語が載ることも、受け付ける語が抜けることもありません。VS Code の補完も同じ出どころです。
信号種別 — ポートと結線の : の後に書きます。
映像
componentComponentBNC / RCAcompositeCompositeBNC / RCAdpDisplayPortDisplayPort / DisplayPort MinidviDVIDVI-D / DVI-IfiberFiberLC / SC / OpticalCONhdbasetHDBaseTRJ45hdmiHDMIHDMI / HDMI Mini / HDMI MicrondiNDIRJ45sdiSDIBNC / DIN 1.0/2.3st2110ST 2110RJ45 / SFPvgaVGAD-sub 15wireless-video無線映像コネクタなし音声
adatADATTOSLINKaesAES/EBUXLR / BNCdanteDanteRJ45iemIEMコネクタなしmadiMADIBNC / SCopticalOpticalTOSLINKrcaRCARCAspdifS/PDIFRCA / TOSLINKspeakonSpeakonNL4 / NL8trrsTRRS 1/4"TRRS 1/4"trrs35TRRS 3.5mmTRRS 3.5mmtrsTRS 1/4"TRS 1/4"trs35TRS 3.5mmTRS 3.5mmuhfワイヤレス (UHF)コネクタなしxlrXLRXLR-M / XLR-F / Mini XLR-M / Mini XLR-F / Mini XLR-4M / Mini XLR-4F制御
dmxDMXXLR-5 / XLR-3gpioGPIOTerminal / D-subirIRコネクタなしmidiMIDIDIN-5 / TRS 3.5mmrs232RS-232D-sub 9rs422RS-422D-sub 9rs485RS-485D-sub 9 / XLRwireless-dmx無線 DMXコネクタなしネットワーク
bluetoothBluetoothコネクタなしlanLANRJ45usbUSBUSB-A / USB-B / USB-C / USB Micro-B / USB Mini-BwifiWi-Fiコネクタなし電源
acACIEC C13 / IEC C14 / NEMAdcDCBarrel / XLR-4poePoERJ45usbpdUSB PDUSB-C同期
genlockGenlockBNCtimecodeTimecodeBNC / XLRwordclockWord ClockBNC汎用
generic一覧に無い信号は signal で自分で定義できます。「信号と互換判定」を参照してください。
機材種別 — device … as の後に書きます。
cameraswitchermixerrecorderplayerdisplayprojectorspeakermicrophoneamplifiercomputerconvertertransmitterreceivermatrixpatchbayrouterinterfacegenericケーブル色 — [color=…] に書きます。英語と日本語のどちらでも同じ色になります。#0af のような16進表記も使えます。
red赤blue青green緑yellow黄orange橙purple紫black黒white白graygrey灰brown茶pink桃長さの単位 — 数値に続けて書きます。30m 6ft のように空白を空けません。
mmcmminftテーマ — diagram { theme: … } に書きます。
lightdarkmonoblueprint08変換をはさむ
受動的なアダプタや変換ケーブルが途中に入ることを宣言します。
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 : hdmi09無線
無線区間はケーブルではないので、長さの代わりに周波数やチャンネルを持ちます。線は破線で描かれます。
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"受信機を通さずマイクを卓へ直結すれば、そこで指摘が出ます。無線と有線の境界も判定の対象です。
10何かに乗っている信号
NDI は映像ですが、線を流れているのは Ethernet です。無線 LAN なら電波です。図が語りたいのは NDI で、物理を決めるのは Ethernet や WiFi — over はこの2つを分けて書きます。
device cam "中継カメラ" as camera { out WIFI : ndi }
device ap "アクセスポイント" as router { io WIFI : ndi
out LAN : ndi }
device pc "収録PC" as recorder { in LAN : ndi }
cam.WIFI -> ap.WIFI : ndi over wifi [ch=36]
ap.LAN -> pc.LAN : ndi over lan 20m "N-01"乗り物が物理を決めます。 over wifi の区間はコネクタを持たずチャンネルを持ち、無線表に出ます。over lan の区間は RJ45 で長さと番号を持ち、ケーブル表に出ます。名前と色は中身が決めます。
ケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| N-01 | アクセスポイント | 収録PC | NDI | 20m | — | — |
無線表
| 番号 | 送出 | 受け | 信号 | 乗り物 | チャンネル |
|---|---|---|---|---|---|
| — | 中継カメラ | アクセスポイント | NDI | Wi-Fi | ch 36 |
over を書かなければ、信号は自分自身の乗り物です。これまでどおりの動きになります。
11箱に付いているコネクタ
信号種別が複数のコネクタを持つとき、口はどれが付いているかを書けます。性別はケーブルの性質ではなく、機器の口の性質だからです。
device dk "卓" as mixer { out CH[1..2] : xlr [connector=XLR-M] }
device sp "SP" as speaker { in IN : xlr [connector=XLR-F] }
dk.CH1 -> sp.IN : xlr 10m "A-01"ケーブルの端は導かれます。 プラグは逆の性別と噛み合うので、オスの出力にはメスの端が来ます。口に1回書けば、そこに届くすべてのケーブルが自動的に一致します。
ケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| A-01 | 卓 | SP | XLR | 10m | XLR-F | XLR-M |
xlr は組み込みで唯一、コネクタ欄が対である型です。他は「どれか」の意味で、usb は A か B か C。対でない型ではケーブル端は逆ではなく同じ名前になります。
殻の大小はコネクタであって、型ではありません。 カメラは micro HDMI、スイッチャーはフルサイズ、あいだのケーブルは何も変換していません。だから hdmi が3つとも持ちます。型を分ければ、成立している接続に不一致が出てしまいます。
device cam "SONY α7 IV" as camera { out HDMI : hdmi [connector="HDMI Micro"] }
device sw "ATEM Mini" as switcher { in HDMI1 : hdmi [connector=HDMI] }
cam.HDMI -> sw.HDMI1 : hdmi 3m "V-01"書く価値は表にあります。HDMI Micro → HDMI は HDMI → HDMI とは別のケーブルで、持って来たか来なかったかのどちらかです。DisplayPort Mini、USB Micro-B、DIN 1.0/2.3、3.5mm の MIDI、ボディパックの Mini XLR も同じです。
ケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| V-01 | SONY α7 IV | ATEM Mini | HDMI | 3m | HDMI Micro | HDMI |
殻を変えるだけの小物は adapter です。 こう書けばケーブルはふつうの HDMI-HDMI に戻り、アダプタは資材表に1個だけ載ります。なくせる部品はそちらに載るのが正しい書類です。
device cam "SONY α7 IV" as camera { out HDMI : hdmi [connector="HDMI Micro"] }
adapter mh "micro-HDMI 変換アダプタ" { in IN : hdmi [connector="HDMI Micro"] out OUT : hdmi [connector=HDMI] }
device sw "ATEM Mini" as switcher { in HDMI1 : hdmi [connector=HDMI] }
cam.HDMI -> mh.IN : hdmi
mh.OUT -> sw.HDMI1 : hdmi 3m "V-01"ケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| V-01 | micro-HDMI 変換アダプタ | ATEM Mini | HDMI | 3m | HDMI | HDMI |
部材表
| 部材 | 数 | つながる先 |
|---|---|---|
| micro-HDMI 変換アダプタ | 1 | SONY α7 IV / ATEM Mini |
空白を含む値は引用符で囲みます: [connector="HDMI Micro"]。殻の名前はたいてい空白を含みます。
12成型ケーブルと、長さ未定
尻尾が全部成型された分岐ケーブルは、部材であると同時にケーブルです。van に積まれ、番号が振られ、積む人が見るのはケーブル表です。as cable を足すと部材表ではなくケーブル表に出ます。
device dk "卓" as mixer { out MAIN : xlr }
adapter fan "XLR4分岐ケーブル" as cable 5m "C-01" {
in IN : xlr
out A : xlr
out B : xlr
}
device sp1 "SP1" as speaker { in IN : xlr }
device sp2 "SP2" as speaker { in IN : xlr }
dk.MAIN -> fan.IN : xlr
fan.A -> sp1.IN : xlr
fan.B -> sp2.IN : xlrケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| C-01 | XLR4分岐ケーブル | 卓 / SP1 / SP2 | XLR | 5m | — | — |
部材表
(空)
1個の物が1行です。プラグごとに3行にはなりません。部材表からは外れるので二重に数えられません。as cable のうしろには結線と同じく長さとケーブル番号を書けます。
長さがまだ決まっていないときは ?m と書きます。長さを省くこともできますが、その空欄は「まだ測っていない」と「誰も考えていない」の2つを同時に意味します。van に積む人が見る表では、片方だけが残っている仕事です。
device dk "卓" as mixer { out L, R, SUB : xlr }
device sp1 "SP1" as speaker { in IN : xlr }
device sp2 "SP2" as speaker { in IN : xlr }
device sub "サブ" as speaker { in IN : xlr }
dk.L -> sp1.IN : xlr 15m "A-01" # 測った
dk.R -> sp2.IN : xlr ?m "A-02" # ケーブルは要る。長さはこれから
dk.SUB -> sub.IN : xlr "A-03" # 長さについて何も言っていないケーブル表
| 番号 | 送出 | 受け | 信号 | 長さ | 送出端 | 受け端 |
|---|---|---|---|---|---|---|
| A-01 | 卓 | SP1 | XLR | 15m | — | — |
| A-02 | 卓 | SP2 | XLR | ?m | — | — |
| A-03 | 卓 | サブ | XLR | — | — | — |
単位は書きます。どの単位で測るかは未定の対象ではないからです。?m ?ft など、この言語が知っている単位すべてが使えます。
13機材ライブラリ
卓の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卓"14見た目
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: … } が勝ちます。図は自分がどう見えるべきかを知っていて、呼び出し側は既定値しか知らないからです。
15出力と埋め込み
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 を使えば、誤配線のきれいな絵をそのまま公開してしまうことを避けられます。