風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

eppfun AK3040Pro MAX関連の備忘録

はじめに

Amazonブラックフライデーセールで音質特化型TWSイヤホンであるDenon PerL Proを購入したのだが、せっかくaptX Losslessをサポートしているので手持ちのWindows 10のPC(Surface Book 2)でも体験したいと思い、ついついUSB-C LE Audio対応Bluetoothオーディオトランスミッター eppfun AK3040Pro MAX まで買ってしまった。

こちら、性能面ではわりあい満足できたものの、UI面では

  • アプリなしで、たった一つのボタンのみでLEDのみを参照しながら操作するようになっているため、操作方法を覚えるのが面倒
  • 付属の操作説明書(ファイルとしてはeppfun AK3040Pro MAX Japan User Manual for v2.0.pdf)が簡素すぎてわかりづらい

という不満点があった。

そこで、自分用に各種操作とそれにともなう状態変化の概要を図にまとめてみたので、備忘として記事にしておく。

eppfun AK3040Pro MAX動作概略図

eppfun AK3040Pro MAX動作概略図

  • 操作説明書v2.0の操作ガイドを元に、自分の備忘用として作成したもの
  • 一部実際の動作に基づいて推測した内容が含まれているが、内容の正確性については保証しない(間違いや過不足などについての指摘歓迎)
  • 再配布については特に制限は設けないが、良識に基づいての利用を期待

eppfun AK3040Pro MAXに関するFAQや誤解など

自分がはまった点や、Twitter(自称X)上などでAK3040 Pro MAXに対して言及しているもので、あきらかに誤解っぽいな? と思ったものなどに関するメモ。

ファームウェアの更新ができないものがある?

Amazonの製品ページ上では

aptx lossはアップグレードすることで、問題を解決することができます。Webアドレス:https://www.eppfun.com/file-share >AK3040ProMAXをダウンロード。

とあり、リンクされているページからはAK3040Pro MAX用のファームウェア(2024/12/13時点では「eppfun AK3040 Pro MAX V3.0(0528).zip」)がダウンロードできる。
ところが、これを適用しようとしても適用できないケースがある(実際に自分の購入したものも正しく適用できなかった)。

具体的には

  • 更新前のバージョンが「1.0.1」なのに、更新後は「0.0.0」と表示されてしまう
  • 再度ファームウェアを適用しようとしても、更新前のバージョンは「1.0.1」のまま

AK3040 Pro MAXのファームウェアが正常に更新出来ない様子

ただ、ファームウェアの日付は最新(V3.0)で2024/5/28となっており、その一方で自分の購入したものだとUSB Device Viewerで確認するとiSerialNumberのところが"20240822V1.0.8"となっており、これが製造日近辺を示す数字だと仮定すると、既に更新済みのファームウェアになっているのかも知れない。

上記Amazonのページでもよく見ると

製品が正常に使用されている場合は、ソフトウェアをアップグレードする必要はありません。アップグレード前にお問い合わせください。問題を確認してから操作してください。

とあるので、とくに問題なく動作しているようであれば、気にしなくてよいのかも(もちろん、気になる方はお問い合わせを)。

ボタンを押してもモードが切り替わらない?

aptX Adaptive対応のイヤホンを接続したところ、ペアリングは成功して接続されたものの、LEDが青のまま、ドングル(AK3040Pro MAX)のボタンを押しても色が変わらなかった。
とりあえず、音声再生ができるかを確認しようと音楽を鳴らすと、LEDの色が変わり、その後はドングルのボタンで切り替え可能となった。
どうやら、いったん何らかの音を再生した後でないと、モード切り替えが出来ない(ことがある?)模様。

aptX Losslessモードにできない?

せっかくDenon PerL ProをaptX Losslessで使うことを期待して購入したのに、最初はなぜかaptX Adaptive HD(96k/48k)までしか接続できなかった(LEDが赤にならない)。

いろいろ調べたところ、aptX Losslessにするためには、以下の設定が必要だった模様。

マルチペアリングはできない?

実際には複数のデバイス(SNK)とペアリング可能(同時接続はひとつのみ(Auracast時除く)*3)。
自分が試した範囲では、少なくともClassic Bluetoothデバイス4台+LE Audioデバイス1台ののべ5台分*4は記憶された模様。

ドングル(AK3040Pro MAX)経由で接続したイヤホンはヘッドセットとしては使えない?

eppfun AK3040Pro MAXの場合(説明書には明記されていないが)どうやらHFPプロファイルはサポートされているようで、イヤホンのマイクを通しての音声入力は利用可能。
入力デバイスとして「ヘッドセット マイク (eppfun AK3040Pro MAX)」を選択すればよい。

ただし注意点として、通常は音声再生専用のモードで動作しているため、マイクを使うためには(これは説明書に記載の通り)ドングルのボタンをダブルクリックして通話機能モードに切り替える必要がある(このとき、ドングルのLEDは青色の点滅(1.5s間隔)となる)。
また、ドングルとしてはaptX Voiceにも対応しているため(これも説明書には記載されている)、イヤホン側も対応しているのであれば、質の良い音声入力になることが期待できる。

なお、LE Audioモードでも同様の通話機能モードが用意されている。
ただし、自分のところでCear pavéで試してみたところではマイク入力することはできなかったため、実動作については未確認*5。

距離の問題などで切断した後、再接続されない

イヤホン等のデバイス(SNK)と切断された際、再接続待機状態(LED青色点滅(0.5s))になる場合があり、このときには(通信圏内に戻っても)うまく再接続されないことがある模様。
この場合、ドングルのボタンを1回クリックすると、通常の待機状態(LED青色点滅(2s))に切り替わるので、通信圏内なら再接続される模様。

最初普通に使えていたのに途中からホワイトノイズが入り続けて使えなくなる

自分の場合はPC側のUSBハブとの相性だったらしく、違う端子に変えたら現象が発生しなくなった。
汎用的な解決方法があるのかは不明。

LE Audioの「Low latency gaming mode」ってなに?

操作説明書の「4.LE AUDIO Bluetooth a.コーデックの切り替え:ボタンをクリック」のところで、

Low latency gaming mode: White

なるものがあるが、これはどういうものなのか。

確証はないが、同じチップセットを使っていると思しきCreative BT-W6に搭載されているGaming Audio Profile(GMAP)と同じものではないかと推測できる。

Gaming Audio Profile(GMAP)は、Bluetoothデバイスがゲームオーディオをうまく処理できるようにするための特別な命令のセットです。LEオーディオモードのGMAP対応デバイスで低遅延を実現するには、この設定をオンにしてください。

LE Audio対応のデバイス(SNK)なのにLE Audioが使えない

操作説明書の「注意事項(正式に使用する前に必ずお読みください)」に

2.現在、LE AUDIOの世界的な標準は完全に普及しておらず、本製品は現在、 クアルコムチップ方案のLE AUDIOのみをサポートしているが、他の方案のLE AUDIO はしばらくサポートしておらず、他のブランド標準のLE AUDIO受信側と接続する と、ミスマッチの問題がある可能性がある。

とあり、少なくとも現状では、デバイス(SNK)側もQualcommのチップセットでないとLE Audioは利用できないくらいに考えておいたほうがよさそう*6。

なお、このあたりの事情は、Creative BT-W6でも同様らしい。

LE AudioモードでCear pavéには繋げる?

ユニークなポータブルスピーカーであるCear pavé 2nd Generation(CP-4000)は、CearLINK(Auracastをベースにしたシーイヤー株式会社の独自技術)により、複数台と同時に通信することでCear Fieldという仮想音源を作りだすことができるが、そのためにはCearLINK専用トランスミッターであるCear coreが必要。
ただし2024/12/14現在、CearLINKは一般販売はされていない*7。

Cear pavéはQualcomm Snapdragon Soundに対応しているため、aptX LosslessをはじめとするAK3040Pro MAXで設定可能な各種コーデックをサポートしている。
また、LE Audio(LC3)も対応しており、チップセットが同じQualcomm製であるためか、AK3040Pro MAXとの相性も問題なく、接続可能。また、CearLINKはAuracastの拡張であるため、AK3040Pro MAXからのAuracastによるブロードキャスト機能にも対応できる。

自分はCear pavéを1台しか所持していないため、LE Audio/Auracastは単体での動作しか確認していないが、こちらの記事(「シーイヤーパヴェ Cear pavéのAuracast複数台接続機器や方法について」)では2台接続できた事例が紹介されている。

AK3040Pro MAXでは素のAuracast(ブロードキャスト)となるため、複数台に同じ音声データが送信されることになるが、専用アプリ(Cear pavé App)のCear Link Positionにて各スピーカーそれぞれに位置取りを設定しておけばサラウンドのような音場を構成できそうではある。

なお、上に記したように自分のところではCear pavéのマイク入力はうまく使えていない。
どなたかマイク入力がうまく利用できている方は方法や確認すべきポイントをご教示願いたい。


*1:なおCreative BT-W6においても、Denon PerL Proを名指しで、空間オーディオを無効にする旨のFAQがある

*2:サンプルレートを96000Hzにしていると、aptX Adaptive HQ 96Kでの接続となり、aptX Losslessモードにはならない・これはCreative BT-W6でも同様のようなので、aptX Losslessの仕様(チップセットの制限?)な模様

*3:Auracast(ブロードキャスト)での複数動作については、対応するデバイスを複数所持していないため未確認

*4:イヤホン3台(Denon PerL Pro・OpenRock Pro・Hamcoc T06)+スピーカー1台(Cear pavé)・ただしCear pavéはClassicとLE Audioそれぞれのモードでペアリングしたため、2台分としている

*5:Cear pavéの場合はClassic Bluetoothでもダメだったので、自分の設定が誤っている可能性もある・なお、USB Audio ModeはVOICE CHATに切り替えても試してはいる

*6:実はQualcommさんはあまりLE Audioに対して積極的じゃないのか?……などと考えてしまうのは、下衆の勘繰りかな……

*7:現在販売されたのは2023〜2024年のクラウドファンディングと、2024年の仮予約経由で申し込まれたもののみで、仮予約も終了している

Twitter(X)関連拡張機能の開発中止と公開停止について/Discontinuation of Development and Publication of Twitter(X)-related Extensions

2024/02/15追記 / Added on February 15, 2024

Edge アドオンやChrome ウェブストアで「X メディアダウンローダ」といった名前で配布されているアドオンや拡張機能が存在するようです。
どうやら中身は自分がかつて配布していたものとほぼ同じもののようですが、当然ながら自分はこれらの配布にはまったく関わっておりませんので、ご留意願います。
なお、オリジナルはオープンソースソフトウェア(MIT License)として配布していたものですので、流用を止めることはできません。
It seems that there are add-ons and extensions available on the Edge Add-ons and Chrome Web Store named "X Media Downloader" that closely resemble something I once distributed.
However, I must clarify that I am in no way involved in the distribution of these current versions. Please be aware of this.
Additionally, the original was distributed as open-source software under the MIT License, so I have no means to prevent its reuse.

2023/09/26追記 / Added on September 26, 2023

Twitter(X)関連の拡張機能等の開発に関するメールやDM等での個別のご相談(開発継続・リポジトリ公開・買い取り等のご要望、他)には応じかねますので、ご了承願います。
Please note that I cannot accommodate individual inquiries via email or DM etc regarding the development of extensions related to Twitter(X), including requests for continued development, repository publication, or acquisition. I appreciate your understanding.


2023/09/16追記 / Added on September 16, 2023

拙作Twitter(X)関連のブラウザ拡張機能(ユーザースクリプト含む)を、各ストアおよびGitHubリポジトリにて非公開にしました。
My Twitter(X)-related browser extensions (including userscripts) are now private on the respective stores and GitHub repository.


残念なお知らせ/Unfortunate Announcement

諸般の事情により

をはじめとした、拙作Twitter(X)関連のブラウザ拡張機能(ユーザースクリプト含む)の開発および公開を中止致します。

各ストアやGitHubリポジトリ等は2023年9月16日を目処に順次非公開(配信停止)とする予定です。

ご愛顧ありがとうございました。

Due to various circumstances, I will be discontinuing the development and release of my Twitter(X)-related browser extensions, including:

I plan to gradually make them unavailable (stop distribution) on stores and GitHub repositories around September 16th, 2023.

Thank you for your patronage!

ひとりごと

2014年に近傍ツイート検索を作成して以来、断続的とはいえ9年以上に渡って続けてきたTwitter(X)系のブラウザ拡張機能の開発を中止とするというのは自分でも大変残念ではありますが……まぁいろいろな意味で無理になってきたので、潮時かなと。

……察して頂けると幸いです。

「有料でもいいから」というお気持ちはわかるのですが……


正規のAPIはこちらのページによると、
コース Basic Pro
月額 100ドル 5000ドル
取得ツイート数上限 1万 100万

これは1プロジェクトあたりにかかる制限なので、ユーザー毎に煩雑なプロジェクトの設定と、料金の(X社への)支払いが発生してしまうことになります。
しかし…Basicの月1万ツイートなんて、拡張機能のヘビーユーザーだった方なら確実に足りなくなる分量ですよねぇ……。
で、そこまでしても

  • 正規のAPIではWeb版やアプリでは使用できる機能が使えないことが往々にしてあるため、拡張機能のために必要な情報が取得できない可能性がある
  • サードパーティ製のTwitterクライアントが否定されたように、そもそも不特定多数のエンドユーザーが使用するような使い方はおそらくもはや想定されていない(開発者規約上も、拙作拡張機能のようなものは認められそうにない)

などなど、どうしても無理があるのですよね……。

AES-GCMでの暗号化/復号の例(JavaScript(Web Crypto API)←→Python(PyCryptodome)相互変換可)

JavaScript(Web Crypto API)で記述されたAES-GCMを用いた暗号化/復号処理をPython(PyCryptodome)にデータ互換性をもたせながら移植しようとしたところ、いくつかハマりどころがあったのでメモしておく。




ハマりどころ

Pythonに入れたPyCryptodomeが動作しない!?

Windows 10 Pro上にインストールしてあるPython 3.11に普通に

$ pip install pycryptodome

で入れているのに、

from Crypto.Cipher import AES

とかしても

ModuleNotFoundError: No module named 'Crypto'

となってしまう現象に悩まされてしまった。

結論としては、PyCryptodomeが入ったフォルダ名が

C:\Python311\Lib\site-packages\crypto

になっていたという……これを手動でcrypto→Cryptoに変更したら、無事に動くようになった。
どうも、ときどき発生する現象である模様……以前に入れたり削除したりしたモジュールの中に小文字でフォルダを作ったものがあったからか?

MAC tagはどこ?

PyCryptodomeではcipher.encrypt_and_digest()で暗号化したときの戻り値としてciphertextの他にMAC tagが存在する。
ところが、JavaScriptのcrypto.subtle.encrypt()では、戻り値はciphertext(を含むArrayBuffer)のみで、MAC tagが見当たらない。
でも、AesGcmParamsではtagLengthを指定できる(未指定の場合は128ビット)ので、MAC tagの作成自体はしているはず……(実際、得られたciphertextをそのままMAC Tagを指定しないでdecryptしようとしてもうまくいかない)。

いろいろと調べてみた結果、crypto.subtle.encrypt()の戻り値の最終16オクテット(tagLength(128)ビット)がMAC tagに相当し、正味のciphertextはこれを除いたものであるらしい。
PyCryptodomeでcipher.decrypt_and_digest()にわたす際にはこの点を留意する必要がある。

nonceはどこ?

PyCryptodomeだと、AES-GCMの場合にはAES.new()でnonceパラメータを指定する必要があるが、JavaScriptのコードを一見したところ、nonceが見当たらない。
どうも、JavaScriptのcrypto.subtle.encrypt()の第1引数(AesGcmParams)にある「iv」というパラメータ(初期化ベクトル(IV=Initialization Vector))が、nonceに相当するらしい。
AES.new()ではivとnonceは別パラメータとして存在し、MODEによってどちらを設定するか決まっている模様(MODE_GCMではnonceを設定)

additionalDataってどう扱えば?

JavaScriptのcrypto.subtle.encrypt()の第1引数(AesGcmParams)にはadditionalDataというパラメータがあるが、これはPyCryptodomeでどう扱えばよいのか?
そもそもadditionalDataは、それ自体は暗号化されないAAD(additional authenticated data、追加認証データ)であるらしい。
こちらも調べてみると、どうやらcipher.update()で指定すればよいらしいことが判明。

その他

今回調査した実装だけの話だけれど、cipher.decrypt_and_verify()を呼び出した際に、"ValueError: MAC check failed"が出たり出なかったり不安定という謎の現象が発生。
かなり悩んだけれど……実はpasswordを複数の候補からタイムスタンプを元に選択するような仕組みになっており、かつ、一連のシーケンス中で得られるデータ中に微妙に異なるタイムスタンプが複数箇所にあるけれど(仕様書もないために)どれを使えばよいのか不明で、間違ったものを使うと誤ったpasswordを選択してしまうことがある(たまたま一致していて通ることもある)というややこしい状況だった(その後、正しいものを特定するのにもかなり時間を要した……仕様書は大事)。

実装例

JavaScriptとPythonの具体的な実装例を掲載しておく。
それぞれで定義しているaes_gcm_encrypt()で暗号化したデータは、お互いのaes_gcm_decrypt()でも復号することが可能となっている(はず)。

JavaScript(Web Crypto API)用

関数仕様

ソースコード

Python(PyCryptodome)用

関数仕様

ソースコード

Surface Book 2は4K・60Hz・10ビット・HDRの夢を見るか?

PCを買い替えるような余裕が無いのもあって、いまだに2019年10月に購入したSurface Book 2 (15")をメインマシンとして愛用しているわけなんですが。
自宅ではいただきものの27インチ4Kモニタ(Dell U2720QM)に接続しているのですけれど、実はこれまでその性能を活かしきれていなかった……という衝撃の事実が発覚しました。

試行錯誤した結果、(限定的な条件ですが)Surface Book 2でも、4Kモニタに60Hz・色深度10ビット・HDR(High Dynamic Range)で出力可能、ということがわかり、現在は安定して使えております。

Surface Book 2を4Kモニタに60Hz・色深度10ビット・HDRで接続した状態

いまさらという話ですけれど、備忘のために記録しておきます。




結論

Surface Book 2から4Kモニタへの、60Hz・色深度10ビット・HDRでの出力は、

  • 本体のUSB-C端子から、DP Alt Mode(DisplayPort over alternate mode)対応のケーブル(DP1.4以上対応なら安心)でモニタのUSB-CもしくはDP端子に接続
  • その他の機器も接続したい場合、Surface Dock 2を使う(Surface Dock(無印)の方では、繋ぐだけで、本体側USB-C端子のDP Alt Modeが使えなくなってしまうので不可)
  • Surface Dock 2背面のUSB-C端子(ビデオディスプレイ対応)には、モニタを接続しないこと(接続してしまうと、モニタの方で入力信号として選択しなくても、本体側USB-C端子からモニタに出力されなくなる模様)

という条件でならば可能です。
当然ながらモニタやケーブルの方もそれぞれの規格に対応している必要があります
自分はWindows 10 Pro環境で下記のような機器で試しましたが、環境や設定が違うなどの要因で、実はDock/Dock2からでもできるよといった情報があれば、ご教示ください

経緯

最近になって、Surface Dockに直接接続しているものの動作が不安定(特にイーサネットが突然落ちてしまい、再起動するまで繋がらなくなる不具合発生)なため、やむを得ずSurface Dock 2を購入して入れ替えました。

その際、Surface Dock 2のUSB-C端子(DP Alt Mode対応)からモニタに繋いだら30Hzでしか繋がらないから絶望しかけたけど、色々と試しているといつの間にか60Hzで繋がるようになった……とツイートしたのですが、


ここでとあるリプライを受けたのが調査するきっかけとなりました。

具体的には、モニタ(Dell U2720QM)の方はリフレッシュレート:60Hz/色深度(ビット深度):10ビット(10.7億色)/色空間:HDR 400に対応しているのに、8ビット/SDRになっているね、というご指摘でした。

せっかくモニタが対応しているのにそのモードで利用していないのはもったいないので、利用可能な条件を探してみた次第です。

試行

使用機器類

試行の際に使用した機器類は以下のようなものです。

略称 機器
PC Surface Book 2
モニタ Dell U2720QM
Dock Microsoft Surface Dock
Dock2 Microsoft Surface Dock 2
USB-Cケーブル モニタ付属のUSB Type-C(CーC)ケーブル
HDMIケーブル モニタ付属のHDMIーHDMIケーブル
DPケーブル iVANKY Mini DisplayPortーDisplayPortケーブル
DP変換ケーブル Silkland USB Type-CーDisplayPort 変換ケーブル
HDMIアダプタ・SANWA SUPPLY サンワサプライ Mini DisplayPortーHDMI 変換アダプタ
HDMIアダプタ・Dockteck Dockteck USB Type-CーHDMI アダプタ
HDMIアダプタ・Freesun Freesun Steam Deck(Nintendo Switch対応ドック・USB Type-CーHDMI変換機能を含む)
試行前の注意点

Dell U2720QMの場合、モニタ側の設定でディスプレイ>Smart HDRを「オフ」(初期値)以外にしておかないと、HDRが有効になってくれないようです。

また、ディスプレイ キャッシュをクリアするためのレジストリファイルを予めダウンロードしておき、接続方法を変更する際には(特にSurface Dock(無印)で試す場合や試した後には)随時クリアするようにしたほうがよいと思われます(クリアしないでやると、ときどき問題ないはずの条件でうまくいかない場合があるようです)。

試行結果
PC側端子 モニタ側 4K表示 60Hz 10ビット HDR 備考
本体USB-C(USB-Cケーブル) USB-C ○ ○ ○ ○ [^1][^2]
本体USB-C(DP変換ケーブル) DP ○ ○ ○ ○ [^1]
本体USB-C(HDMIアダプタ・DockteckーHDMIケーブル) HDMI ○ × × ○ [^1][^3]
本体USB-C(HDMIアダプタ・FreesunーHDMIケーブル) HDMI ○ × × × [^1]
Dock Mini DP(DPケーブル) DP ○ ○ × ×
Dock Mini DP(HDMIアダプタ・SANWA SUPPLYーHDMIケーブル) HDMI ○ ○ × ×
Dock2 USB-C(DP)(USB-Cケーブル) USB-C ○ ○ × ○ [^2][^3]
Dock2 USB-C(DP)(DP変換ケーブル) DP ○ ○ × ○ [^3]
Dock2 USB-C(DP)(HDMIアダプタ・DockteckーHDMIケーブル) HDMI × × × × [^4]
Dock2 USB-C(DP)(HDMIアダプタ・FreesunーHDMIケーブル) HDMI ○ × × ×

[^1] Surface Dock接続時や、Surface Dock 2接続時でかつ裏面USB-Cポート(ビデオディスプレイ対応)にモニタを接続した場合には(モニタ側で入力信号として選ぶ/選ばないにかかわらず)、本体USB-Cからの映像出力は無効化されてしまう
[^2] USBとしては2.0の機能しか使えなくなる(=モニタ側のUSBハブとしての機能も実質使えなくなってしまう・ただしPower Delivery機能の方はどうなるかは未検証)
[^3] HDR使用時、色深度が「ディザリング使用時、8ビット」となってしまう
[^4] 本体側は認識しているが、モニタ側で「接続されていません」となってしまい、画面が表示できない

結果として、実質「本体側のUSB-C端子にモニタを接続する」しか選択肢がありませんでした。
本当はSurface Dock 2の背面USB-Cで使いたかったのですが、どうやっても色深度を10ビットにする方法がわからず、断念しました

また、モニタのUSB-C端子につなげてしまうと、USBとしての機能は2.0に制限されてしまうので、USB-CーDisplayPort 変換ケーブルを用いてDisplayPort端子の方に繋げるようにしています。
別途、Surface Dock 2の前面USB-C端子(ビデオディスプレイに対応していない方)をモニタのUSB-C端子に繋ぐことにより、モニタのUSBハブとしての機能も確保しています。
なお、背面USB-C端子に繋いでしまうと、本体USB-C側のDP Alt Modeが使えなくなってしまってハマります……
ちなみにこの構成だとモニタのUSB Power Delivery機能は活かすことはできませんが、まぁ問題ないでしょう……

付録

Surface Book 2|3・Surface Dock・Surface Dock 2のUSB-C(DisplayPort over alternate mode)/Mini DisplayPort仕様
機器 DPバージョン 伝送速度(名称) 伝送速度 DSC(圧縮) モニターx1 モニターx2
Surface Book 2 1.2 HBR2 17.28 Gbps - 4096x2304@60Hz 4096x2304@30Hz
2560x1600@60Hz
Surface Book 3 1.4a HBR3 25.92 Gbps 1.2 4096x2304@120Hz 4096x2304@60Hz
Surface Dock 1.2 HBR2 17.28 Gbps - 4096x2304@60Hz 4096x2304@30Hz
2560x1600@60Hz
Surface Dock 2 1.4a HBR3 25.92 Gbps 1.2 7680x4320@30Hz
3840x2160@120Hz
3840x2160@60Hz

当然ながら、BookとDockとを組み合わせて使う場合、低い方の性能に合わされてしまうことになります。
いろいろなページを参考にまとめましたが、推測で書いているところもあるため、不正確な情報があるかもしれません。
また、公式ページにおいては色深度10ビット対応やHDR対応等の情報は見つけられず、Surface Connect端子に関する仕様も見当たらないという……なんともユーザーに対して不親切だと思うのは自分だけですかね……

DPの伝送速度の規格そのものはUSB-Cのもの(3.0/3.1 Gen1/3.2 Gen1x1は5Gbps・3.1 Gen2/3.2 Gen2x1は10Gbps等)とは別物ですが、同じ信号線(SuperSpeed信号線)を利用しているため、無関係というわけではありません。

たとえばDP1.2ではHBR2(最大17.28Gbps)まで使えますが、これは4レーン(SuperSpeed信号線4本)全てを使った場合であり、USB 3.1 Gen1用にSuperSpeed信号線2本を確保する場合には2レーンしか使えず伝送速度は半分となるため、4K表示だと30Hzまでしか使えなくなることになります。
逆に、4K・60Hz・色深度10ビットだと15.68Gbps必要となるため、4レーン全てが必要となり、USB 3.1用にSuperSpeed線が確保できない結果、USBの機能としては2.0までしか使えない、ということになります。

ちなみにSurface Book 3はDP1.4aで4K・120Hzにも対応となっていますが、色深度10ビットにすると32.27Gbps必要となるため、HBR3(25.92 Gbps)だと足りなくなってしまいますね。実際はどうなっているのでしょうね?
色深度10ビットは不可だったりするのか、それともDSC(Display Stream Compression)で対応しているのか……Surface Book 3をお持ちの方、情報をお待ちしております(笑)→ここを見る限り、Windows 11で4K・120Hz・10ビット・HDRで表示できているようですね(DSCが使われているかはわかりませんが)

セルの右横にユーザーフォームを出したかっただけなのに……

軽い気持ちというかほとんどネタで、VBAのユーザーフォームを用いたなんちゃってモダンなDatePickerを作ってみたのですが、その際に「セルの右横にユーザーフォームを表示する」という一見簡単そうなことが一筋縄ではいきませんでした。

試行錯誤の結果、ひとまずは実用にできそうな手法がわかったので、備忘として記事にしておきます。

セルの右横にユーザーフォームを表示したかっただけなのに……




せっかちな方へ

「能書きはいいから具体例を出せ」という向きは、

[Excel][VBA] ユーザーフォームをアクティブセルの右横に表示する実装例 · GitHub

こちらに実装例(マクロ有効ワークシート(*.xlsm))とソースコードを置いてあるので、ご自分で読み解いてください。

ちなみにこれを利用して作ったDatePicker(カレンダー)はこちら。

[Excel][VBA] Excel用DatePicker · GitHub

前提知識

  • エクセルVBAでは、オブジェクト(Window、Range、UserForm等)の位置や大きさは、基本的に「ポイント」という単位で管理されている(Top/Left/Width/Height等のプロパティの数値は全てポイント単位)
  • 位置を表す座標系には、大きく分けて、画面*1の左上を原点とする「画面座標系」(WindowオブジェクトやUserFormオブジェクト等はこちらで考える)と、ワークシートの左上*2を原点とする「ドキュメント座標系」(Rangeオブジェクト等はこちらで考える)とが存在する*3
  • 「画面座標系」と「ドキュメント座標系」とでは、同じ1ポイントであっても、画面上における大きさは異なる場合がある(例えばウィンドウの拡大率などに左右される)
  • ポイントとピクセル(ドット・画素)との比率も、環境によって異なる場合がある(画面解像度や拡大率に左右される)*4

アクティブセルの右側にユーザーフォームを表示したいときの座標計算方法の具体例

ユーザーフォームをアクティブセルの右側に表示する場合の座標の求め方

アクティブウィンドウ上にあるアクティブなセルのすぐ右側にユーザーフォームを表示する際の、ユーザーフォームに指定する座標(Top/Leftプロパティ)の値(ポイント)の具体的な求め方を記します。

なお、ここでは例として、

画面 横3840ピクセル✕縦2160ピクセル(4K)・拡大率150%
アクティブウィンドウ Top:84.0・Left:221.5・Width:1379.0・Height:822.0・拡大率200%
ウィンドウ枠の固定 有り(B3の位置)
アクティブセル アドレス:BR118・Top:2106.0・Left:3726.0・Width:54.0・Height:18.0

のようになっているものとします。

アクティブセルの位置を画面座標系に変換

アクティブセルのプロパティ(Top/Left)は、ドキュメント座標系上のポイント単位の数値です。
ひとまずこれを、画面座標系へと変換します。

これには、ペイン(Pane)オブジェクトのPointsToScreenPixelsY()/PointsToScreenPixelsX()メソッドを使用します。

このとき注意するのが、アクティブセルが属しているPaneオブジェクトを指定する必要があることです。
ウィンドウ枠の固定をしている場合、ActiveWindow.Panes.Countは最大4まで存在するため、アクティブセルがPanes(1)~Panes(4)のいずれの下にあるかを調べる必要があります。
もっとも、アクティブセルが前提なので、この場合は、PaneオブジェクトとしてActiveWindow.ActivePaneオブジェクトを指定するのが簡単でしょう。

これで、Top: 2106.0ポイント→1003ピクセル、Left: 3726.0ポイント→1399ピクセルのように、アクティブセルの左上の、画面座標上の位置をピクセル単位で求めることができます。

画面座標系上の位置をピクセル単位からポイント単位に変換

求まった値はピクセル単位ですが、ユーザーフォームのプロパティ(Top/Left)に設定しようと思うと、ポイント単位への変換が必要になります。

画面座標系におけるピクセル→ポイントの変換方法として、自分は、ActiveWindowのHeight・Widthプロパティ(ポイント単位)と、WinAPIであるGetWindowRect()により得られるActiveWindowの座標(Top/Bottom/Left/Right・ピクセル単位)から求まる高さ・幅との比率を求めて使う、という方法を思いつきました。
もっと簡便な方法があればご教示願います。

これにより、上記の例では1ポイント=2.0ピクセルと出ましたので、Top:1003ピクセル→501.5ポイント、Left:1399ピクセル→699.5ポイントのように、アクティブセルの左上の、画面座標上の位置をポイント単位に変換できました。

セル幅を画面座標上のポイント単位に変換

セルの「右」に表示するので、横方向の座標にはセル幅(Widthプロパティ)分を加える必要があります。

Widthプロパティはポイント単位なので、これを足せばいいのか……と思いきや、ドキュメント座標上のポイントと画面座標上のポイントでは実サイズが異なってきます。
具体的には、ウィンドウの拡大率に左右されます。
アクティブウィンドウの拡大率は、ActiveWindow.Zoom÷100で求まり、上記の例(200%)だと2.0となります。

よって、ドキュメント座標上のWidth:54.0ポイントは、画面座標上では108.0ポイントとなります。

ユーザーフォームのプロパティを設定

以上により、ユーザーフォームの設定すべき画面座標上の位置は計算上(ポイント値で)Top:501.5、Left:699.5+108.0=807.5となります。
あとは、ユーザーフォームのStartUpPositionプロパティã‚’0(Manual)にして、TopとLeftプロパティに値を設定すればいいわけですが……さらにいくつかハマりどころがあるのが困ったところですね。

ハマりどころ

第3の座標系が存在!?(ユーザーフォームの位置が想定よりも右下にずれていく問題)

環境によっては(画面解像度や拡大率、マルチモニタ等が影響?)上記のようにして計算した値をユーザーフォームのプロパティにセットしても、想定位置からずれてしまう場合があります。

計算値と実際の表示位置がずれる

しかも、画面の右下に行くに連れてズレも広がっていくようです。

この現象に最初に遭遇したときにいろいろと試してみたところ、上記の計算値に一定の係数(試したケースでは14/15=0.93…)を掛けたものをTop/Leftプロパティに設定すると、想定位置に表示されることがわかりました。
いわば、画面座標系と原点は共通ですが、目盛(1ポイント当たりの大きさ)の大きい座標系(ユーザーフォーム座標系?)が存在するようなイメージです。

この係数は環境により異なってくるため、定数にすることはできません。
そこで、

  1. GetWindowRect()でユーザーフォームのピクセル単位での座標を取得し、これから高さと幅を求める(実測値)
  2. ユーザーフォームのHeight/Widthプロパティに、ActiveWindowから求めたピクセル/ポイント比をかけて、ピクセル単位の高さと幅を求める(想定値)

のように二種の方法で高さと幅を求め、想定値と実測値の比を割り出し、これを係数として使用することで、対応することにしました。

とりあえず、この補正を行うことで、状況は改善されたようです。

計算値と実際の表示位置のズレを補正
画面の右の方にいくと、突然ユーザーフォームが左側にずれるようになる

環境によっては(おそらくマルチモニタ環境でかつ特定の解像度や拡大率の場合に)、画面の左側だと問題なく表示されているのに、あるところから突然ユーザーフォームが想定位置よりも左に大きくずれ始める、という現象が発生することがあります。

画面の右の方でいきなりずれだす

調べてみると、Pane.PointsToScreenPixelsX()が返す値が、ある列より右側では明らかにおかしくなっていました。

PoinstsToScreenPixelsXテスト(4K・150%)

しばらく原因がわからず途方にくれていたのですが、その後、たまたま気がついたエクセルの設定を変更することで改善されることがわかりました。

ファイル>オプション>設定>全般>ユーザー インターフェイスのオプション>複数ディスプレイを使用する場合
で、

◉ 表示を優先した最適化 (アプリケーションの再起動が必要)

にしていると発生することがあるようで、試しに

◉ 互換性に対応した最適化

に変更(これもエクセルの再起動は必要)すると、

全般>複数ディスプレイを使用する場合は「互換性に対応した最適化」を選択

正常動作するようになったようです。

PoinstsToScreenPixelsXテスト(4K・150%)・「互換性に対応した最適化」設定後
その他注意点・制限事項など
  • PoinstsToScreenPixelsY()/PoinstsToScreenPixelsX()は、Panesのどこに所属しているかを意識しないといけないし、そもそも「ウィンドウ上に見えている(表示されている)」もののポイントからしか変換できないため、隠れているセルの座標を指定したりするとエラーになることに注意
  • マルチモニタ環境は鬼門(特にモニタの境界線付近にユーザーフォームを表示したいようなケースでは正常に動作しないものと思うべき)
    解像度や拡大率の異なるモニタ間の境界線付近でも問題なく動作するような方法があればご教示ください
  • 環境依存の部分が大きい(WinAPIを使用しているためWindows以外では動作しない・Windows 10 Pro+Microsoft 365 Excel(32ビット)で開発・確認しているが、Excelのバージョンの他、OSのバージョン等にも依存しそう、等)
  • 「シートを右から左へ表示する」(Excelのオプション>詳細設定>次のシートで作業するときの表示設定)が有効になっている場合には、正常に動作しない(Pane.PointsToScreenPixelsX()が適切な値を返してくれない)

処理を簡便化するモジュール

上記の処理の一部を簡便化するためのプロシージャ群を標準モジュールとしてまとめてあります。

ConvertToScreenPosition()
Type ScreenPosition
    x As Double
    y As Double
End Type

Function ConvertToScreenPosition(TargetTop As Double, TargetLeft As Double, Optional TargetWindow As Window) As ScreenPosition

ドキュメント座標系の座標(TargetTop/TargetLeft・ポイント)を、画面座標上の座標(ピクセル単位)に変換して返します(変換不可な場合にはx=0・y=0が返ります)。
TargetWindowで対象となるWindowオブジェクトを指定可能です(省略時はActiveWindowになります)。
例えば対象となるセルのTop/Leftを指定するだけでよく、Panesのどれに属しているかは意識しなくても済むようにひと工夫してあります。

GetDisplayDotsPerPoint()
Type DotsPerPoint
    x As Double
    y As Double
End Type

Function GetDisplayDotsPerPoint(Optional TargetWindow As Window) As DotsPerPoint

画面座標系上の1ポイントあたりのドット(ピクセル)数を返します。
TargetWindowで対象となるWindowオブジェクトを指定可能です(省略時はActiveWindowになります)。

SetUserFormPosition()
Type CoordinateFactor
    x As Double
    y As Double
End Type

Function SetUserFormPosition(TargetForm, Top As Double, Left As Double, Optional Calibration As Boolean = True) As CoordinateFactor

TargetFormで指定したユーザーフォームを、画面座標系上の指定位置(Top/Left・ポイント)に移動します(このプロシージャでは表示は行わないことに注意してください。別途、TargetForm.Showプロシージャで表示する必要があります)。
このとき、位置ずれ補正(画面の右下に行くほどにずれる現象に対する補正)も自動で行います(Calibration:=Falseで補正を無効化することもできます)。
戻り値として、適用した補正係数を返します(Calibration:=False時はx=1・y=1が返ります)。

具体的な使用方法は、実装例を参照してください。

ひとりごと

ユーザーフォームってあまり使った経験がないのですけれど、まさかセルの右横にユーザーフォームを表示するだけでこんなに苦労することになろうとは思いもよりませんでした……。
よりよい方法があれば、ご教示願います。


*1:マルチモニタ環境における「画面」は、すべての有効なモニタ画面を包括するような仮想的な矩形となると思われる

*2:セルの設定で非表示になっているものは含まないため、必ずしもA1ではないことに注意

*3:座標系の名前は便宜上のもので、自分のソースコード内では「ディスプレイ座標系」「ワークシート座標系」などと書いてある場合もある(統一が取れていなくてすみません……)

*4:ネットではDPI(Dots Per Inch)ã‚’96として決め打ちしている実装をよくみかけるが、あまり望ましくないと考えられる(ポイント(DTPポイント)の方は1インチあたり72ポイントがもともとの定義。なお、これもPoints Per Inchを略してPPIと書いてしまうと、Pixels Per Inchと混同してしまうため(こちらがより一般的なPPI)混乱の元なので避けたほうがよさそう)