2018/03/21

MonitorianとWifinian

しばらく前にMonitarianWifinianをWindowsストアでリリースしました。どちらもWindows用で、OSの機能で行けてないところ(モニターの明るさ調整、Wi-Fi設定)を補完するものです。中身的にはC#によるWPFアプリ(ただし、Win32を多用)で、Desktop Bridgeを利用してストアに出しています。これらの使い方自体は、そんなに難しくないと思うので、開発について簡単に説明しておきます。

1. Monitorian

以前の環境光センサーに関するエントリで、Windows 10のアップデートでアクションセンターにモニターの明るさ調整のスライダーが入るらしいと書きましたが、これまでのところは入っていません。
Monitorianの設定中の「調整後の明るさを表示する」は、このエントリで触れた問題に対応するもので、(環境光センサーがある場合に)設定値に加えて実際の調整後の値も表示することで、設定を多少やりやすくします。

内部的には、モニターの明るさに関して利用可能なWin32、WMIの機能をほぼ全て動員し、Device Instance IDをキーに使って統合して利用しています。少々面倒なことに、それぞれに含まれる機能が断片的なので、統合しないと必要な機能が揃わないのですよね。

外部モニターについては、DDC/CIが有効であることが条件ですが、これはモニターとその設定によるので、開発側としてはどうしようもありません。この問題に引っ掛かったらしきレビューが付いてますが、必要条件には初めから書いてあるので。一応、それと直接表示する機能を追加しました。

名前については、昔アプリには機能が分かりやすい名前を付けるべしという話を読んだことがあって、それを考慮していたこともありますが、そんなお行儀にこだわる必要などないと悟ったので、造語しています。

2. Wifinian

Native Wifiのマネージド実装であるManaged Native Wifiを利用するアプリから機能を強化・リファインの上、改名したものです。
機能としては、前のアプリと比べて、Wi-Fiの状態変化に応じて自動的に更新するようにした、Auto ConnectとAuto Switchの設定に合わせて自動的に介入して接続先を切り変える機能(Engage)を付けた、というのが主な違いです。後者はWindows 7まではOS標準であった機能が、Windows 8以降で簡略化されたものを、再び使えるようにしたことになります。

この辺はMicrosoftのデザイン上の判断で、「Wi-Fiセンサー」の顛末も見るに、ユーザーはWi-Fiに繋がりさえすれば何だって気にしないだろうという判断があったものと想像しますが、まあ実際そうかもとは思いつつ、ユーザーの意図で決めたい場合もあるので。

内部的には、このためにManaged Native Wifiを強化し、WlanClientを保持してWi-Fiの状態変化を監視できるようにしています。引き続きReactivePropertyを使わせていただいてます。その点で再確認した注意事項は、ReactiveCommandに繋げる前にはUIスレッドに戻しておけということです。

名前については、同上。Wifiの後は語感です。

3. ScreenFrame

タスクバーの通知領域にアイコンを出しつつ、タスクバーに引っ付いたウィンドウと通知領域アイコンに重なるウィンドウを(NotifyIconのContextMenuと同じ位置に)出すためのライブラリです。Wifinianの前のアプリで開発したものをベースに、Monitorian用に開発し、後にWifinian用に拡充しています。Wifinianではウィンドウのタスクバーからの脱着ができるようになっています。

このコードの肝は、
  • WM_WINDOWPOSCHANGINGメッセージに引っ掛けてウィンドウのサイズ・位置調整をすること。これにより、ウィンドウのサイズ・位置変更を全て捕捉し、状態に応じてそのWINDOWPOSを改変することでサイズ・位置変更に自然に介入できる。

  • NotifyIcon中のNativeWindowへのWM_DPICHANGEDメッセージを監視することで、通知領域のあるスクリーンのDPI変化を捕捉すること。マルチモニター環境で通知領域アイコンのあるスクリーンのDPI変化をどう検知するかが課題になっていたが、これによりそのスクリーンにウィンドウがない場合でも検知できる。なお、NativeWindowへのウィンドウメッセージ監視は、そもそもNotifyIconがそうしていることに倣ったもの。
ちなみに、非公開メンバーにReflectionでアクセスしている部分がありますが、もはやほとんど開発は行われていないだろうし、実際上、気にする必要はないだろうと。NotifyIconがWinFormsだという点は、それを避けてWin32を直接使うよりマネージドの方がましです。

そういえば、先日、LGの42.5インチのモニターの実物を見てきましたが、広すぎてスクリーンの端に付いたタスクバーを起点とするUIにはもう無理があると感じました。既にこのサイズのモニターが普及価格帯に入っていますが、Microsoftはどうする気ですかね。

4. StartupAgency/StartupBridge

常駐アプリとして必要な自動起動のためのライブラリがStartupAgencyです。自動起動の方法は幾つかありますが、コード的に一番簡単なのはレジストリに登録することで、インストーラによるアンインストールで掃除できることを前提にすれば、それで十分なのでその方法を取っています。

ただし、Desktop Bridgeの場合はその方法ではダメで、UWPのStartupTaskを使う必要があるので、そのための別ライブラリがStartupBridgeという構成になっています。
問題は、これによる自動起動とユーザーによる手動起動をどう判別するかで(自動起動のときはウィンドウを出さないようにしたい)、自動起動のときにArgumentsを付けることができれば簡単ですが、StartupTaskでこれをやる方法が見つかりませんでした。

代替策として、完全な方法ではないですが、アプリの起動時間を記録するようにし、自動起動が有効な場合に、起動時に前回の起動時間とOSの起動時間(セッション開始時間)を比較して前者の方が前であれば自動起動と判断する方法を考えましたが、どこに記録するかという選択が残ります。

この記録には、ライブラリのモジュール性を高めたかったので、UWPのLocalSettingsを使うようにしました。Desktop Bridgeの場合は実はこの手が使えます。といっても、TaskIdはAppxManifest.xmlのものと一致する必要があるので、そこのアプリ本体への依存は避けられませんが。

5. Desktop Bridge

Desktop Bridgeのパッケージ作成は今ではVisual Studioでも直接できますが、Desktop App Converterのやり方に慣れたので、そちらを使っています。面倒な画像作成を自動でやってくれます(ただし、サイズが妙に奇数なので、輪郭がぼける)。コマンドはメモしておいてPowerShellに張ればいいので、さほどには。

このパッケージ作成を14393より新しいバージョンでやりつつ(BaseImageのバージョンは実際のOSのバージョンと一致している必要がある)最低動作バージョンを14393としておくことは可能で、パッケージ作成のためだけに14393の環境を維持する必要はありません。一応、14393のときのISOで環境を作って動作確認はしましたが。

ちなみに、Desktop Bridgeでインストールしたアプリには、UWP同様に以下のパスに専用フォルダーが作成されるので、この中を探せばAppDataに作成したファイルを直接見ることは可能です。
[system drive]\Users\[user name]\AppData\Local\Packages

Windowsストアに出すことのメリットは、正直それだけで認知度が上がる感じではないですが、クラッシュ報告が自動的に集計されてくるのは参考になります。

なお、2018年2月現在で、Desktop Bridgeはまだ専用窓口から申し込んでMicrosoftの担当者とのやり取りを経てから通常のストア申請に移る方式でした。その担当者は現在は既に日本Microsoftの方になっています。

2017/08/06

BluetoothヘッドホンとSurface

Bluetoothヘッドホン自体は以前からありましたが、まだ主力とするには足りてないという印象を持っていました。それが、気が付けば使用環境が整ってきていたので、自分の使用目的に合わせて確かめてみました。

1. aptX

有線ヘッドホンからBluetoothヘッドホンに移行する大前提として、Bluetoothオーディオの高音質コーデックとして最低限の標準であるaptXで接続できるかという問題があります。SBCでは微妙な遅延があるらしいので、とくに動画を見る場合には必須です。

Surface Pro 4

Windows 10が標準でaptXに対応していることは分かっていましたが、実際にSurface Pro 4でどうなるか直接の情報がなく、事前確認は取れませんでしたが、結果から言えばaptXに対応してました。

使用コーデックをプログラム的に判別できないか色々探ってみましたが、手掛かりになる情報は見つからず、最終的にBR1006(後述)をレシーバーモードにして接続した結果、「aptX」で接続と出ました(「aptX low latency」ではなく)。実際、MDR-1000X(後述)と接続して音声と映像をじっと比べても遅延が発生している感じはないので、そういうことなのでしょう。

Nexus 5X

Androidでも明示的にaptXに対応している機種もありますが、Googleブランドの機種はPixelを含めて対応情報がなく、BR1006と接続した結果、予想どおり「SBC」で接続と出ました(Android 7.1.2の状態)。まあ音楽を聴いている限りでは、とくに不満を感じることはないですが。

開発用途との兼ね合いが悩ましいところでしたが、次期バージョン(Android 8.0)の標準でaptX、aptX HD、LDACへの対応が決まったので、もはや問題ではなくなりました。Nexus 5Xはまだアップデート対象ですし。

ということで、PCあるいはスマートフォン側のaptX対応の問題はほぼ終了という状況です。

[追記] Android 8.0

Nexus 5XにAndroid 8.0が配信されたので、早速試したらMDR-1000XがLDACで接続されました。ということで対応完了。

2. Sony MDR-1000X

Bluetoothヘッドホンを見直した切っ掛けはSAOオーディナルスケールの作中で使われていたからで、Sonyのタイアップ企画にはまった感じですが、それは措くとして(ただ、NCヘッドホンをかけたまま料理するのは、台所の危険情報を聞き逃すおそれがあるので止めた方がいいです。自分もやかんが沸騰した音に気づかず、空焚きしかけました)。

で、コラボレーションモデルのMDR-100ABNでいいかと思ったのですが、実機を見にいったら、上位機種のMDR-1000Xは右ハウジングでタッチ操作が可能になっていたので、こちらにしました。

このタッチ操作で、音量調節のほか、曲の前後の頭出しができますが、結構便利です。ハウジングの形に沿って操作ポイントが直感的に分かるので、見えなくても問題はないです。ちなみに、この操作にはWindows 10標準のGrooveミュージックも対応しています。

ヘッドホンの音については、とくに語ることもなく(語るほどの耳を持ってない)。なお、NCヘッドホンとしてはBoseのQuietComfort 25も持っていますが、飛行機の機内でのノイズ抑制はQuietComfortの方が強くかかる感じでした。

3. Inateck BR1006

自分のNCヘッドホンの使用目的の一つは飛行機の機内での使用ですが、機内のヘッドホン端子の音声を無線化しようとすれば、Bluetoothトランスミッターで送信することになります。

Bluetoothトランスミッター/レシーバーの分野は中小の周辺機器ベンターの領域で、aptX対応にはばらつきがありますが、InateckのBR1006は現時点で最新の世代のもので、トランスミッター/レシーバーの両モードでaptX HDまで対応しています。

このBR1006はとてもコンパクトで(重量は単体で16g、附属ケーブルを含めても20gしかない)、ヘッドホンのキャリングケースの隙間に入れておけます。というか、小さすぎて、弾みでケーブルが抜けて座席の間に落ちたりすると回収が大変そうです(実際にそうなりかけた)。せめてケーブルとテープで固定しておいた方がいいかもしれません。

それでいて、使用する分にはとくに問題もなく。内蔵バッテリの持続時間は公称13時間なので、欧州便や北米東海岸便の西回りでも何とか持つぐらいでしょうか。なお、説明書にもありますが、給電中はハム音が混入するので(内部でシールドされてない?)、給電しながら使用するには向きません

ついでに、BR1006には、使用コーデックをLEDの点滅回数で示す機能があるので、確認用に役に立ちます。

4. まとめ

今更ですが、Bluetoothヘッドホンは予想以上に便利です。今や使用環境も整っているので、これを使わない選択肢はないなと思いました。と言いつつ、コンパクトさではBluetoothイヤホン(ケーブルのないもの)には負けるので、次はそちらかもしれませんが。