define-macro処理系をできるだけHygieneにする

追記: これsyntax-rulesに展開されるsyntax-rulesがダメなんじゃないかという気がする。もっとも、何度リネームしても実害は無い気がするけど。。
前回( http://d.hatena.ne.jp/mjt/20181209/p1 )、define-macro処理系上のsyntax-rulesでシンボルのリネームを実現するために __1 とか __2 のような予約シンボルを導入する方法を考えたが、既存のコードを書き換えるのは超たいへんなのでテンプレートの方を暗黙に書き換えるというインチキで乗り切ることにした。
とりあえず手元のアプリを分析したところ、lambdaとlet、defineさえ救えば十分なことがわかったのでそれらだけ救う対応を入れた。

救われるコード

lambda、let、defineでのbindingは暗黙にリネームされるようになった。つまり、

(import (yuni scheme))

(define-syntax out
  (syntax-rules ()
    ((_ x code)
     ((lambda (a) (code a)) x))))

(define-syntax out2
  (syntax-rules ()
    ((_ x code)
     (let ((a x))
      (code a)))))

(define-syntax out3
  (syntax-rules ()
    ((_ x code)
     (begin
       (define a x)
       (code a)))))

(define a 10) ;; それぞれsyntax-rulesの中ではaに20を代入している
(out3 20 (lambda (b) (display (list a b)) (newline)))
(out 20 (lambda (b) (display (list a b)) (newline)))
(out2 20 (lambda (b) (display (list a b)) (newline)))

のようなコードがs7やBiwaSchemeでも正常に"(10 20)"を出すようになった。これは、例えば上記のdefineであれば、

(define-syntax out3
  (syntax-rules ()
    ((_ x code)
     (begin
       (define __1 x)
       (code __1)))))

のように暗黙に書き換えるのと同じ操作を実装している。

前提

この対応によって救うことができるのはletやdefineといった予約語がsyntax-rulesのテンプレート内に完全な形で出現している必要がある。つまり、

  • letやdefineの定義を置き換えていない。yuniでは構文のリネームは許さない方向で制約しているが、依然letとかlambdaで別のものを束縛するのは合法となっている。今のところ、define-syntaxをトップレベル以外では許していない(= define-syntaxされる文脈は常にtop-levelなので他の構文がbindされようが無い -- (define let 10) とかしない限り)が、今後define-syntaxもスコープできるようにすると問題になるかもしれない。
  • syntax-rulesテンプレート中に完全な形で構文を使用する。syntax-rules内のテンプレートを見てテンプレートを直接書き換える手法は、2段以上のマクロ展開を使った場合に成立しない可能性がある。letやlet*のような構文シンボルをマクロの外部から与えること自体は合法だが、今回のsyntax-rulesではこのようなケースでは内部でletが行われることを知りようが無い。

前者はともかく、後者はついうっかりやってしまいそうな気はする。
もちろん、処理系がdefine-record-typeとかその他bindをする非標準の構文を持っていたら、それぞれの対応を導入する必要がある。

実装

実装は、syntax-rulesのテンプレートをスキャンし、letとかdefineでbindされる位置にあるシンボルが有ったら、そのシンボルをテンプレート変数の1つとして昇格している。Generic runtimeではchibi-schemeのsyntax-rules実装を改造してsyntax-rulesを実装しているので、それをパッチする形で実装した。

  1. letとかdefineに対する、bindされる可能性があるシンボルを抽出する手続きをそれぞれ用意する https://github.com/okuoku/yuni/commit/19495050f7ea03e9f461be43a2007e6e925351d6#diff-6fc3666a46e36a52d314bb3257b9b2edR117
  2. expand-pattern に引数を追加し、テンプレート内でbindされる可能性があるシンボルのリスト(potential-binds)を渡せるようにする https://github.com/okuoku/yuni/commit/19495050f7ea03e9f461be43a2007e6e925351d6#diff-9e4667071efa1a5846f19ee88540ae32R272
  3. expand-pattern 先頭で、bindされる可能性があるシンボルのリストを map してそれぞれgensymしておく https://github.com/okuoku/yuni/commit/19495050f7ea03e9f461be43a2007e6e925351d6#diff-9e4667071efa1a5846f19ee88540ae32R198
  4. テンプレート変数を展開するときに、テンプレート変数のシンボルにマッチしなかったものをpotential-bindsと突き合わせて、マッチした場合はgensymしたシンボルに差し替える https://github.com/okuoku/yuni/commit/19495050f7ea03e9f461be43a2007e6e925351d6#diff-9e4667071efa1a5846f19ee88540ae32R217

現状の実装は処理系独自のscan手続きを追加することができない。

意義

ここまでしてsyntax-rules""の構文を使うことに何の意味があるのかというのは微妙な問題だが、これ(define-macroでエミュレートできるようにsyntax-rulesの機能を制約する)が今のところ各種Schemeでマクロを記述する最大公約数なのではないかと思っている。もちろんexpanderを載せて真面目にsyntax-rulesなりsyntax-caseなりを実装する手も有るが。。
完全にテンプレート展開のみに絞って明示的に __1 のようにgensym位置を書かせるのと、今回のように暗黙にリネームを挟むのとどちらが良いのかはなんとも言えない。ただ、個人的には暗黙にリネームを挟む方がUXとしては優れているのではないかと考えている。bindされるシンボルに意味のある名前を付けられるし、処理系が対応していればそれなりに見易いエラーを出力することもできる。
例えば、

(import (yuni scheme))

(define-syntax check
  (syntax-rules ()
    ((_ temp)
     (define (temp in)
       (car in)))))

(check check2) ;; 手続きcheck2を定義
(check2 10) ;; check2にペアでない値を渡す(エラー)

のようなコードをyuniのGeneric runtimeをloadしたs7で実行すると

;car argument, 10, is an integer but should be a pair
;  check.sps[10]
;
; check2: (car {in}-16)                      ; {in}-16: 10
; ((load prog)); ((cdr args*) (%%extract-program-args (cdr...
; ((set! %%selfboot-yuniroot "."))

のように、ちゃんと引数名が表示される。
syntax-rulesじゃなくてdefine-macroの方に寄せないのかよという意見も有るかもしれないけど、fomentのようにsyntax-rulesしかマクロを持たない処理系も有るし、何よりletくらい普通に書かせてくれた方が便利だし。。

"Generic Scheme"仕様を考える

yuniのBiwaScheme対応を進める上で用意した"Generic Scheme"仕様が意外と便利な気がしてきたので一般化してSRFI-96( https://srfi.schemers.org/srfi-96/srfi-96.html )のようなrequirement仕様にできないか考えてみる。
"Generic Scheme"はexpanderが簡略化されるぶんR7RS Smallよりも更に小さなScheme仕様になるので、syntax-rulesのサブセット実装をyuniに丸投げすることで簡単に市場の他のScheme処理系と共通のライブラリを使える環境を目指したい。

BiwaSchemeの制約

BiwaScheme( https://www.biwascheme.org/ )はJavaScript上に実装されたSchemeインタプリタで、R6RSの多くの手続きを実装している。(Bytevector等のバイナリ手続きは無いのでyuniでは専用のforkであるbiwasyuni https://github.com/yuniscm/biwasyuni/blob/0e5339ecbce44b148137070bfc7acf285da958b4/biwasyuni_core.js を使用している)
yuniは基本的にR6RS/R7RS処理系専用だが、GambitのようにAlexpanderを使って無理矢理サポートしている処理系もいくつかある。BiwaSchemeでもAlexpanderを使うことを考えたが、Alexpanderはちょっと遅いためBiwaSchemeの仕様に合わせて適度に規模縮小を図って実装することにした。
重要な制約には以下がある:

  • syntax-rulesなどの健全マクロは無く、define-macroのみ備える。yuniのGambitサポートはAlexpanderを使って完全にexpandしてから処理系に渡すアプローチを取っているが、これはexpandのコストが掛かるため避けたい。このため、syntax-rulesを適当に制約を付けてdefine-macroで実現する( http://d.hatena.ne.jp/mjt/20180521/p1 )方向を取った。
  • define-macroをトップレベルにしか書けない。Gambit等のdefine-macro処理系は通常defineとの混ぜ書きが可能で、let等によってスコープすることができる。BiwaSchemeではmacroはスコープすることができない。

マクロがスコープできないということは、let-syntaxが実装できないという点が問題になり得る。しかし、yuniでlet-syntaxを使っているのはidentifierと ... (ellipsis)の検出に使っているOlegのテクニック https://github.com/okuoku/yuni/blob/dcffed4556cdbccef46bba0a9c1198a8be2fe527/lib/yuni/base/match.sls#L566 くらいなので、ここだけライブラリ化してしまえば事足りる。
もっと直接的な問題は構文をリネームする方法が無い点が有るが、通常のシチュエーションではユースケースは無い、と思う。
これをユーザに見える制約にすると、

  • define-syntaxはライブラリ/プログラムのトップレベルにしか書けない。手元のSchemeアプリだとたまにletの中でdefine-syntaxしているものが有り、そういうコードはトップレベルに移動してやる必要がある。マクロを使ってトップレベル以外で定義したものを挿入できないという制約とも言える。
  • define-syntaxしているライブラリはトップレベルにしかimportできない。逆に言うと、define-syntaxを含まないライブラリは(let () ...)で囲むことでグローバル定義を汚さずにimportできる。Gambitやs7のようなdefine-macroをスコープできる処理系にはこの問題は無い。
  • syntax-rulesは特殊なサブセット仕様となる。これはちょっと複雑。

syntax-rulesサブセット仕様

define-macroを使って、かつ、(letやdefineのような)組込み構文の知識無しでsyntax-rulesを実装する場合、どうしても避けて通れないバインディングの生成問題がある。syntax-rulesで新しいバインディングを導入する場合は、字面上の名前が同じであってもマクロが展開される度に新しいバインディングを生成しなければならない。

  • Generic Schemeでは上手くいかないケース
(import (yuni scheme))

(define-syntax chk
  (syntax-rules ()
    ((_ nam val)
     (begin
       (define tmp val) ;; ★ Generic schemeではそのまま "tmp" をdefineしてしまう
       (define (nam) tmp)))))

(chk a 10) ;; (a) => 10 のはず
(chk b 20) ;; (b) => 20 のはず

(display (list (a) (b))) (newline) ;; ★ (20 20) を出力

ここで定義している構文 chk は、引数に指定した値を返す手続きをdefineするが、テンポラリなシンボルとして使用したtmpがそのままコードに出力されてしまうため、2回目の chk の使用でtmpが上書きされてしまい、Generic Scheme処理系ではうまく動かない。これはsyntax-rulesを適切に実装した処理系ではちゃんと (10 20) を返す。

(define-syntax chk
  (syntax-rules ()
    ((_ nam val)
     (begin
       (define __1 val)
       (define (nam) __1)))))

そこで、Generic Schemeではsyntax-rulesの_ とか ... のようなリテラルに加えて、 __1 〜 __9 を予約して、tmpのような仮置きのシンボルとして使えるようにした。(R7RSやSRFI-46では ... を置き換えることができるが、__1 〜 __9 を置き換える方法は提供しない。) これは内部的にはよくある gensym 手続きを呼び出すだけとなっている。
... たぶん、letやlambdaのような標準のbind構文は全て自動的にこれらを置き換える機能を用意してあげた方が良い気はしている。現状だと

(lambda (a) a)

のような記述をsyntax-rules内に書く場合、

(lambda (__1) __1)

のように書き換えてやらないと、マクロ展開の外で識別子 a がbindされていた場合に意図せずマスクしてしまう。

  • 上手くいかない例
(import (yuni scheme))

(define-syntax out
  (syntax-rules ()
    ((_ x code)
     ((lambda (a) (code a)) x))))

(define a 10)
(out 20 (lambda (b) (display (list a b)) (newline))) ;; ★ やっぱり (20 20) と出力される
  • 手動で書き換えた例
(define-syntax out
  (syntax-rules ()
    ((_ x code)
     ((lambda (__1) (code __1)) x))))

通常の展開器実装では、define-syntaxした位置で出力テンプレートがbindされていなければ適宜リネームという戦略を取ることができるが、今回はdefine-macroを使う縛りなのでsyntax-rulesの側にlambdaとかletといった標準構文の知識が必要になる。

Generic Schemeの意義

こんなサブセットをしてまでdefine-macro処理系を取り込む必要が有るのかはちょっと何とも言えないが、ちゃんとしたライブラリのサポートとsyntax-rulesの実装は意外と面倒で、かつ、どうしても処理系のバリエーションを増やしてしまうので、それをyuniに丸投げして処理系本体をコンパクトにできるなら言うほど悪くないんじゃないかという気がしている。
今のところGeneric Schemeターゲットになり得るのは:

あたりが有る。Gambitにはpsyntaxベースのsyntax-rulesが有るが、Gambit本体のマクロ展開器とよく連携しないので、直接Gambitのマクロ展開器を使うにはGeneric Schemeのようなアプローチが必要になる。
もちろん上にライブラリシステムやexpanderを被せてこれらの処理系をちゃんとしたR7RS処理系にすることも不可能ではないと思うが、処理系本来のdefine-macroとマクロ展開器を活用した、可能な限り薄い互換レイヤというのも方向性としては有りなんじゃないかと思う。すくなくともこの3つとも非常に魅力的な処理系で、syntax-rulesが無いからといって非サポートにするのは非常に惜しい。

プリチャンはどこに向うか


桃山。
前回( http://d.hatena.ne.jp/mjt/20180401/p1 )の予想は新加入キャラが紫ってのしか当たってない!しかもクロスオーバーは初年度である今年からゲームの方で展開されている。VTuberアイカツがやった( http://nlab.itmedia.co.jp/nl/articles/1806/07/news141.html )けどストーリーキャラクタではない。マイルームは"プリ☆チャン わちゃわちゃ会"としてコミュニケーションパートが実装されたがチーム構成がephemeralなのは変わっていない。宇宙はモチーフとしてアニメに登場しているが、さすがにもっと現実的な方面の切り方となった。
来年どうすんのかというのは非常に難しいところで、"キラっと"だけ替えるのかタイトル総替えか。。個人的には総替えすべきと思うけど。。

うまくいっていると思うところ

プロジェクト全体のコスト効率はかなり改善されたと感じる。
アーケード筐体はプリント筐体になっているが、このプリントはおそらく40〜50円/プレイのコストが掛かっていてこれを無くすことはできないので、どうしても開発や運用のコストを削ることでの利益確保になる。プリパラでは各キャラクタ毎に筐体オリジナル曲を制作していたが、プリチャンではこれを原則的に廃し、"歌ってみたシリーズ"としてインフルエンサー(乃木坂46)、LOVEマシーン(モーニング娘。)のカバーを制作している。...なぜこのラインナップになるのかはちょっと解らないが、アニメ楽曲がavex制作なのに、こちらはSMEの楽曲というあたりに事情を感じる。
コーデはブランド推しをやめて2チーム両方に同じものを着せ、更に限定カラーなどを駆使してバリエーションを出している。パーツや楽曲、モーションも含め相当量のアセットは前作プリパラからの再利用で構成されており、物量感を出すことにはある程度成功していると思う。
おもちゃ類はなんと1種に抑え、タカラトミー(!= アーツ)として出しているおもちゃ(カード/収納除く)はプリチャンキャストただ一種、しかも過去のシリーズとは異なりデジタルトイではない。筐体連動も印刷されたコードのみ。
運用面でも重篤な不具合(cf. http://nlab.itmedia.co.jp/nl/articles/1812/06/news115.html )を出すこともなく比較的おだやかに進行した印象。プリパラでは雑誌付録のコードがredeemできない等課金系の問題が見られたがシステム側の成熟があるのかもしれない。クラッシュ等のソフト品質はデータが無いので謎。

好みの問題

個人的にはプリチャンのデザイン方面は絵が若い感じというかなんというかフレッシュで良いと思っているけど、世間的にはあんまりそういう評価を聞かないので感覚がズレてるかもしれない。
アニメも当初見られた暴力的な傾向は鳴りを潜めていて日曜朝らしい良いアニメになっていると感じる。ただ、一貫した目的の無いドラえもんのようなアニメで良いのかというのは否定的意見が多い印象がある。

うまくいっていないと思うところ

着地点の無いコンテンツは売りようが無いのは前回ので散々書いたけど、それ以上にプロモーションが不味いんじゃないかという気がする。たとえば公式YouTubeチャンネルの登録者数は1万程度で、アイカツの公式チャンネルの1/10以下となっている。開設期間や視聴層(= 国籍など)の差は有るものの、予測されるプレイヤー数にここまで差は無い。公式の略称で"プリ☆チャン"(間に☆を入れるぶん検索性が低くTwitterのようなSNSでのトレンドになりづらい)を使うといった小さな問題から、そもそもIPホルダーの分散がそのままコミュニケーションの分散になってしまっているという大きな問題まで、さまざまな問題を解決していく必要がある。
コスト面の努力は感じるが、逆に言えば"そこまでしないと生き残れないのか"というのが正直な感想でもある。...まぁそれでもアイカツフレンズよりは贅沢を感じるし、オトカドールに至っては今年はアップデートが無いし。。

シリーズはどこに向うのか

まったく良いアイデアが無い。ハイターゲット(いわゆる大型女児)向けに営業を続けるのか、プリパラのようにもっと低年齢層に振るのかは決断が要るポイントだけど、正直リスクを取ってまで営業を拡大することを支持できるポイントが無い。
アニメキャラクタの商売はプリティーオールフレンズとして主に筐体外で展開している。逆にマイキャラの商売は(グッズ販売は設定されているものの)筐体内に閉じているので、展開のしようは有るかもしれない。プリチケメーカーのようなアプリは過去に展開していたので、同様にWebサイト等外部でマイキャラを作らせて誘引するなど。
マイキャラの音声は当初ボーカロイドを使うことが企画されていた( https://twitter.com/SHINOHBA/status/976571267942834176 )など、まだまだマイキャラのシステムには技術的制約で構想を実現できていないポイントが数多くあり、技術的なブレークスルーによって"ウリ"が産まれる可能性は大いにあると考えている。アニメキャラクタと同じプラットフォームで活躍するマイキャラの存在はコンテンツとの新たな関わり方になる可能性がある。
逆に言えば、これらの技術的なブレークスルーを取り入れていないうちは、新規のコンテンツが入り込むスキのあるニッチが存在すると言える。例えば変身前のマイキャラを含めた2世代キャラクの実装には衣装のスケーラビリティを実現する必要があるが、そのためには衣装のあらたなオーサリング方法論が必要になる。

Anker Soundsync A3341

AptX sink機器が手元に無いので購入。Windows10以降は標準でAptX codecがシステムに搭載されているのでAptXで再生された(2回点滅)。
光デジタル入出力を備えていて、光デジタル機器を無線化する用途に使える。また、アナログ入出力も備えている -- 説明書には"AUX input"としか記載されていないライン入力端子は実は出力にも対応しておりケータイWatchのレビューにも有るように適当なスピーカーを無線化する用途にも使うことができる。ただし、充電しながらの使用を推奨していないので設置用途に向かない。ヤマハYBA-11( https://jp.yamaha.com/products/audio_visual/accessories/yba-11/index.html )とかelecom LBT-AVWAR700( http://www2.elecom.co.jp/products/LBT-AVWAR700.html )みたいにBluetoothのデジタルレシーバって存在しないわけでは無いんだけど。。
説明書記載の点滅回数とCODECの対応は以下のようになっていて、

  • 1 SBC
  • 2 AptX
  • 3 AptX LL(LowLatency)
  • 4 AptX HD
  • 6 AAC

なぜか5番が無い。
S/PDIF出力周波数はBluetooth側の入力と一致。手元の環境では44100Hzでの出力になる。バッテリ駆動の製品だけあって比較的真面目に出力制御を実装していて、PCのS/PDIF入力で聞くと曲間等でノイズが入る(出力が途切れる?ためと考えている)。例えばAUXに入出力デバイスを接続した場合はちゃんとS/PDIF出力は消灯する。
持ち歩きを想定したバッテリ搭載だけど、ちょっとその意義はわからず。入出力ともにマルチペアリングは2台まで、同時処理は1台のみなのでシェアリングには使えないし、公式サイトの画像だって明かに据え置きで使ってるし。。
AptX codecは既にAOSP( https://android.googlesource.com/platform/system/bt/+/3a3ec66a1bb7f5c99b17239021d6d184a3abd4ee%5E%21/ )やffmpegに入っている( https://patchwork.ffmpeg.org/patch/5879/ )。

オブジェクトベースオーディオのためのE-AC-3拡張の規格書を読む

Dolby AtmosのようなオブジェクトベースオーディオをSTBで動作するアプリから出力するにはE-AC-3くらいしか現状選択肢がない。E-AC-3(いわゆるDolby Digital Plus)上のJOC(Joint Object Coding)は一応AppleTVとAmazon FireTVの両方がサポートしている。


"Nominally 16"(16チャンネル相当)って微妙な表現だな。。
で、このDolby Digital Plus上のオブジェクトオーディオは一応標準化(ETSI TS 103 420)されていて、Dolbyのかなり簡潔なKBにURLが有る。

Yes. ETSI TS 103 420 v1.1.1

いきなりコレを読むよりも、AESでのDolbyの発表スライドにある図を見た方が良い気がする。

(エンコーダ)

(デコーダ)

規格は図中の、OAMD(Object Audio MetaData)やJOCの仕様を規定している。この図では16 chを出力するように書かれているが、規格自体は最終的なスピーカーマッピング等は規定していないので、もっと多くのPCMチャンネルにデコードする実装も考えられる(一般家庭に16個以上スピーカーを置くのは非現実的だが、バーチャルサラウンドではずっと多くの仮想スピーカーを使用できる)。要するに元の5.1 / 7.1ストリームを16とかそれ以上の数のオブジェクトに"拡張"するのがJOCで、ETSI TS 103 420は16chのストリームを直接扱えるようにAC-3を拡張するわけではない。
オブジェクトのプロパティとしてはSizeがWidth、Height、Depthの3値を持つ直方体で規定されるのが目を引く。全てが0なら点音源、1なら無指向でリスニングルーム全体を占めるサイズの音源ということになる。
... これ直接Ambisonicsとかを伝送できるようにした方が良くない。。?何か特許を採用する必要があったとかでどうしてもJOCを採用しなければいけない事情が有ったのかもしれないけど。。JOCによって各オブジェクトの特徴量として出てくるのはQMFフィルタバンクの係数マトリクスなので、オーディオエンジンはオブジェクトの位置だけでなく各チャンネルへのダウンミックス係数やオブジェクトの元波形やミックス先から抽出した各サブバンド毎のゲインを保持しておく必要がある。そこまで複雑なデータを用意するなら、最初から7.1ミックスとヘッドホン用のミックスに絞った方がマシだな。
一応この規格ができたのが2016年で、そろそろFOSS実装が有っても良さそうなもんだけど、MediaInfo( https://mediaarea.net/en/MediaInfo )のライブラリ( https://github.com/MediaArea/MediaInfoLib )がフォーマット情報の表示に対応しているくらいで、デコーダやエンコーダは未だ無いようだ。

Intensity Pro 4K

Amazonでほぼ定価購入。

Blackmagic Design(BMD)は結局このIntensityのライン、要するに一般消費者向けのキャプチャ製品は最近出していないが、まぁアナログSD/HDソースや2Kソースであればコレで十分。。
いつものプリパラ。キャプチャによってできたTGAをPNGに変換している。
( http://storage.osdev.info/pub/idmjt/diaryimage/1810/neta181028l1.png )

良いところ

もういくつHDMIキャプチャ買ってるんだという気になってくるけど:

  • デジタルソースはDot-by-dot正確にキャプチャできる。前買ったAVT-C878は右端1ラインが欠けるのが超気になったけど、Intensityではちゃんと全画面キャプチャできる。
  • SDKが有る。BMDのプロ用キャプチャ製品と同じDecklink Desktop Video SDKが使える。
  • マルチチャンネルPCMをキャプチャできる。実は市場の製品でマルチチャンネルPCMをサポートしたキャプチャはあまり数が無く貴重と言える。。

とにかく決め手になったのはSDKが有るところで、逆に言うとあんまり通常の人間には薦めづらい。例えば、付属のキャプチャソフトは圧縮録画がMotion JPEGくらいしか選択肢が無く(Macであれば更にProResが使える)、チャンネル順も謎だったりする。
MotionJPEGで撮ったソースを元にYouTubeに上げてみた。

GStreamerを使用してコマンドラインでキャプチャする

GStreamerにはDecklinkのプラグインが既に存在するので、それを使うことでコマンドラインからキャプチャすることもできる。手元の環境ではアマレココ等では正常にキャプチャできなかったが、Decklinkを直接使うこれらのプラグインではまぁまぁの信頼性でキャプチャできる(が、たまに失敗するし10bit RGBには対応していない)。
適当なバージョンのGStreamerを導入し、以下のようなコマンドラインで再生できる:

gst-launch-1.0.exe -m decklinkvideosrc connection=hdmi mode=1080p60 video-format=8bit-bgra buffer-size=20 ! \
    autovideosink \
    decklinkaudiosrc connection=embedded ! audioconvert ! audioresample ! autoaudiosink

modeやvideo-formatはキャプチャするソース毎に手で設定する必要がある。ココではNintendo Switchの設定なので60Hzで8bit-bgraとなっている。手元のWindows環境はWASAPI共有モードを192kHzで運用しているため、オーディオを再生するためにはaudioresampleエレメントを挟む必要があった。
また、どうもbuffer-sizeは20くらい無いと安定してキャプチャできないようだ。。単純に最初の方のフレームを脱落させれば良いと思うんだけど、やり方がわからず。

サラウンド音声のキャプチャ

... LPCMに関しては普通にできる。が、そもそもサラウンド音声をキャプチャしても公開するプラットフォームが無いのであんまり需要が無いのではないかという気はする。
手元の機器だと、他にHauppaugeのHD PVRがS/PDIF経由でDolby Digitalをキャプチャできたので、Dolby Digital Liveに一旦エンコードして録画するというクッソ面倒な方法で一応キャプチャできていた。Intensityの場合は普通にHDMIの8chオーディオを直接キャプチャできるため、そういう面倒が無いのは素晴しい。
PC以外だとなかなか難しい状況で、(HDCPを抜きにしても)AppleTVやFireTVのようなサラウンド対応STBはそもそも事実上Dolby Digital(と、同Plus)が唯一のサラウンド方法論になるため、その辺のコンシューマ機器でサラウンド音声を取れる物は実はゲーム機くらいしか無い。逆にゲーム機はXbox、Switch、PS4の全てがLPCM出力に対応していていづれもサラウンド音声をIntensityでキャプチャできる。

ビルドログ保持/分析ポリシのメモ

軽い気持ちで考えてたけどコレ意外と難しいぞ。。

やりたいこと

  • "コンパイラ警告が出現したコミットをO(1)で特定する"
  • → 全リビジョンをビルドしてコンパイラ警告を保存しておき、行番号情報を除いた上でそのblameを取る

しかし、現状の開発はGitでやっているので当然マージ等も発生し、"全リビジョンのビルド"自体が現実的にはできない。もっと悪いのはlibpngのような外部ライブラリをリポジトリにマージの形で抱えていることで、当然libpng等の変更をいちいちビルドしていたらいくらお金があっても足りないのでビルドの間引きはどうしても考える必要がある。
最新版のビルド警告だけを保持してblameだけで良い? → ダメ。警告の出現タイミング != ソース行の導入タイミング。ヘッダやプラットフォームSDK自体の非互換修正、ツールチェーンの更新等によって新たに警告が出現するのはよくあること。

履歴の分類(Main/Sub)による直線化ヒストリの定義

まず、マージによって導入される変更を細かく追うのは諦める。
Gitのようなバージョンコントロールシステムの履歴はDAGになるが、このうち"左側"の履歴だけをMain historyと呼び、そうでない履歴をSub historyと呼ぶことにする。Gitではマージの左右の概念が無いが、一般的には左がMainというかpublicにpushされていたリビジョンになることが多いように思う。残念ながらGitはrefにログが無いのでマージが有った場合履歴の左右どちらがメジャーなのかを決定するための情報が無い。(GitHubのNetwork graph https://github.com/ashinn/chibi-scheme/network はおそらくpush履歴を使用しているのではないだろうか。)
Sub historyはビルドログ自体を保持しない。このため、Sub history側で新規に増えたコンパイラ警告は、Main historyと交差するマージコミットにattributeされることになる。しかし、Sub history側のblameによって結果を近似できる可能性がある。
一方、Main historyは全リビジョンをビルドする。Main history、ようするにメイン開発者である自分のコミットは全リビジョンをビルドするのは現実的と言える -- そもそもCIしているので常に達成されている。Main history直線になるという重要な特徴がある。Gitはファイルヒストリを持たないためこのようにしてシミュレートしてやる必要があるが、SubversionのようなVCSでは、ancestryとしてこの概念を直接的に持っている(マージは後付けで付与された機能であり、本来のリポジトリフォーマットとしては、あるファイルには高々1つの祖先が常に存在する)。
この分類の導入によって、"Main historyはパーフェクト、Sub historyはblameによる近似"という目標に再設定できる。

取り組む課題

Main/Sub historyの概念の導入で問題が多少シンプルになった。次に取り組む課題としては、
blameメタデータをGitリポジトリに突っ込むフォーマットを考える。blameをO(1)またはそれに近いパフォーマンスにするためには、blameデータのキャッシュが必須になる。Gitはまぁまぁの品質のデルタ圧縮をコンテンツに対して行えるため、blameデータ自体もテキストデータにしてGitリポジトリにしておけば、容量を節約しつつデータベースとして機能させることができる。はず。
ホワイトスペースの扱いコンパイラ警告の出現場所の特定という意味では、(Pythonのようなホワイトスペースが意味を持つ言語を除くと)ホワイトスペースの変更はblameに影響させないのが望ましい。しかし、ホワイトスペースの変更自体もそこまで頻繁に起こるものでもないので、ヒューリスティクスの失敗ということで無視してしまっても良いかもしれない。
Sub historyの漸近的な解決。バグを追うようなケースで、sub historyをビルドしたいようなケースも考えられる。その結果をblameデータリポジトリに投入するためにはどのようなフォーマットが考えられるだろうか?
revert処理のモデリング = 行編集モデルの拡張。Revertによって一旦削除された行を再導入した場合、警告の発生したリビジョンも実際に書かれたリビジョンに戻したい。このためには、現在 "新規"/ "削除" の2操作しか存在しない編集モデルに、"復帰"を導入する必要がある。おそらく、他ファイルへのコピーのような操作も拡張できるだろう。