やっぱりZOOT Nativeはやめた。

NVR510に切り替えてみた。で、Yamaha NVR510を導入したエントリを書きました。
その後、それまで契約していた ZOOT NEXT を解約して ZOOT Native に切り替える…つもりだったのですが。実は11月で ZOOT Native は解約しました。

切り替えるにはconfigをいちいちいじらないといけないのですが、ちゃんとテストしないとと思い、テストしてみました。
以下は現在の状況ですが、ダウンストリームで700Mbps、アップストリームで340Mbpsと、非常に高速です。一般的には PPPoE は IPoE よりも遅い、と言われているようなのですが、うちの場合にはどちらもまったく同じ程度の速度でした。
ただし、これは NVR510 に切り替えてからのことで、それまで使っていた ATermではここまでの速度は出ませんでした。だいたいこの5分の1程度でしょうか。
ともあれ、PPPoE でも IPoE でも速度はほとんど変わらないことがわかりました。

ではなぜ NEXT から Native にしなかったかというと、固定IPの DNS 登録が絡んでいます。

うちでは Gonbei で独自にドメインを取得しています。これまで、そのドメインでサーバを運用していました。ところが Native にすると IPアドレス自体は外部からもアクセスできるのですが、ドメイン名が引けません。なにやら技術上の問題があるようで、InterlinkのFAQにもあまり詳しく書いてませんが Native で固定IPサービスを利用しても、Gonbei への登録ができないようです。

ということで、NEXTに戻ったのでした。


SQLite3で正規表現してみる。

プライベートで作っているDB、別にMySQLでもMariaDBでもなんでもいいんですが、簡単なのでSQLite3を使うことにしました。
用途はビデオのタイトルと説明のリストです。録画したものがどこにあるか、的な。

ところが、登録されてるのに引っかかってこないものがあることに気づきました。まあずばり言ってしまうと "DUNE" なんですが。いわゆる全角アルファベットで、しかも文字エンコーディングは UTF-8 です。そして要望としては、"dune" でも "DUNE" でも "DUNE" でも "デューン" でも見つかってほしいな、と。

そうなると、プログラマ的には「正規表現で探せばいいじゃん」と思いつきます。関係ないですが「じゃん」を使うのは東京近郊らしいですね。関係ないですけど。

ところで、ふつうにSQL文だと LIKE を使いますが、最近は REGEXP 演算子なるものもあるようです。が、SQLite3 で
SELECT * FROM vedeo_list WHERE title REGEXP "DUNE|DUNE|dune|デューン";
などとやると、
no such function: regexp
と言われます。

SQLiteのドキュメントを見ると、
The REGEXP operator is a special syntax for the regexp() user function. No regexp() user function is defined by default and so use of the REGEXP operator will normally result in an error message. If an application-defined SQL function named "regexp" is added at run-time, then the "X REGEXP Y" operator will be implemented as a call to "regexp(Y,X)".

とあります。 Google翻訳さんにお願いすると、

REGEXP 演算子は、regexp() ユーザー関数の特別な構文です。 デフォルトでは regexp() ユーザー関数は定義されていないため、REGEXP 演算子を使用すると、通常はエラー メッセージが表示されます。 「regexp」というアプリケーション定義の SQL 関数が実行時に追加されると、「X REGEXP Y」演算子が「regexp(Y,X)」の呼び出しとして実装されます。

と訳してくれました。どうやらユーザ関数を定義しないとだめっぽいです。 一応 ext/misc/regexp.c というファイルはソースツリーにあるので、DLLかなんかの形であるのかな?とPythonのディレクトリを見てみると、DLLsディレクトリに sqlite3.dll と regexp.dll がありました。これを dumpbin してみると、

>dumpbin /exports regexp.dll
Microsoft (R) COFF/PE Dumper Version 14.34.31937.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Apps\Python311\DLLs\regexp.dll

File Type: DLL

  Section contains the following exports for regexp.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00003160 sqlite3_regexp_init

  Summary

        2000 .data
        2000 .pdata
        A000 .rdata
        1000 .reloc
       10000 .text
        1000 _RDATA
となっているので、このDLLを読み込んでやればきっといいんでしょうね。
検索してみたら、Run-Time Loadable Extensions というページになにか書いてありました。が、いろいろ書いてあるんですが、じゃあどうすればいいの?がよくわかりません。

An SQLite extension is a shared library or DLL. To load it, you need to supply SQLite with the name of the file containing the shared library or DLL and an entry point to initialize the extension. In C code, this information is supplied using the sqlite3_load_extension() API. See the documentation on that routine for additional information.

Note that different operating systems use different filename suffixes for their shared libraries. Windows uses ".dll", Mac uses ".dylib", and most unixes other than mac use ".so". If you want to make your code portable, you can omit the suffix from the shared library filename and the appropriate suffix will be added automatically by the sqlite3_load_extension() interface.

There is also an SQL function that can be used to load extensions: load_extension(X,Y). It works just like the sqlite3_load_extension() C interface.

Both methods for loading an extension allow you to specify the name of an entry point for the extension. You can leave this argument blank - passing in a NULL pointer for the sqlite3_load_extension() C-language interface or omitting the second argument for the load_extension() SQL interface - and the extension loader logic will attempt to figure out the entry point on its own. It will first try the generic extension name "sqlite3_extension_init". If that does not work, it constructs a entry point using the template "sqlite3_X_init" where the X is replaced by the lowercase equivalent of every ASCII character in the filename after the last "/" and before the first following "." omitting the first three characters if they happen to be "lib". So, for example, if the filename is "/usr/lib/libmathfunc-4.8.so" the entry point name would be "sqlite3_mathfunc_init". Or if the filename is "./SpellFixExt.dll" then the entry point would be called "sqlite3_spellfixext_init".
For security reasons, extension loading is turned off by default. In order to use either the C-language or SQL extension loading functions, one must first enable extension loading using the sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,1,NULL) C-language API in your application.
C言語から使うときには、sqlite3_load_extension() APIを使うといいよ、あとSQL関数の load_extension(X, Y) もあるよ、Xにはダイナミックライブラリ(WindowsではDLL)を指定すればいいよ、そうすればエントリポイントは勝手に実行されるよ、みたいなことが書いてあります。また、セキュリティ上の理由から拡張ローディングはデフォルトでは無効になってるので、sqlite3_db_config() でEnableしてやらないとだめだよ、とあります。
そのちょっとしたに、CLIではデフォルトでenableになってるよ、ともあります。残念ながらあまりいいドキュメントじゃないかも…。

ということなので、やってみました。
> sqlite3
SQLite version 3.40.0 2022-11-16 12:10:08
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .dbconfig
          defensive off
            dqs_ddl on
            dqs_dml on
        enable_fkey off
        enable_qpsg off
     enable_trigger on
        enable_view on
     fts3_tokenizer off
 legacy_alter_table off
 legacy_file_format off
     load_extension on
   no_ckpt_on_close off
     reset_database off
        trigger_eqp off
     trusted_schema on
    writable_schema off
sqlite>
下から6番目に "load_extension on" とあります。有効化はされているようです。ところが、CLI から load_extension("regexp.dll") とかやってもエラーになるばかり。
うーん、うーん。

ここで Python のSQLite3 のドキュメントを見てみました。そしたらなんと、enable_load_extension(enabled, /)にありました。そこの注意書きには、sqlite3モジュールはデフォルトではローダブルエクステンションをサポートしてません、と書いてありますが…でもなぜか DLLs には regexp.dll があります
regexp.dllをどこからか持ってこないといけないので、Pythonに付属のSQLite 3.39.4のソースを拾ってきてVisual Studioでコンパイルしてみました。
コンパイルオプションはいろいろあるのですが、regexpはext/misc/regexpにあります。
何はともあれ試してみました。

> python
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec  6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> con=sqlite3.connect("videolist.db")
>>> cur=con.cursor()
>>> con.enable_load_extension(True)
>>> con.load_extension("c:/Apps/python311/DLLs/regexp.dll")
>>> cur.execute("SELECT * FROM videolist WHERE title REGEXP 'DUNE|DUNE'")
<sqlite3.Cursor object at 0x000001C72971A140>
>>> row = cur.fetchall()
え、なんかできてるし。 つまり、Python からならこれだけで SQLite3 の正規表現は使える、ということですね。 ついでに、さっき見たソースコードに正規表現ルールが書いてあったので引用しておきます。
The following regular expression syntax is supported:

    X*      zero or more occurrences of X
    X+      one or more occurrences of X
    X?      zero or one occurrences of X
    X{p,q}  between p and q occurrences of X
    (X)     match X
    X|Y     X or Y
    ^X      X occurring at the beginning of the string
    X$      X occurring at the end of the string
    .       Match any single character
    \c      Character c where c is one of \{}()[]|*+?.
    \c      C-language escapes for c in afnrtv.  ex: \t or \n
    \uXXXX  Where XXXX is exactly 4 hex digits, unicode value XXXX
    \xXX    Where XX is exactly 2 hex digits, unicode value XX
    [abc]   Any single character from the set abc
    [^abc]  Any single character not in the set abc
    [a-z]   Any single character in the range a-z
    [^a-z]  Any single character not in the range a-z
    \b      Word boundary
    \w      Word character.  [A-Za-z0-9_]
    \W      Non-word character
    \d      Digit
    \D      Non-digit
    \s      Whitespace character
    \S      Non-whitespace character

A nondeterministic finite automaton (NFA) is used for matching, so the
performance is bounded by O(N*M) where N is the size of the regular
expression and M is the size of the input string.  The matcher never
exhibits exponential behavior.  Note that the X{p,q} operator expands
to p copies of X following by q-p copies of X? and that the size of the
regular expression in the O(N*M) performance bound is computed after
this expansion.
非決定論的有限オートマトンなのでパフォーマンスは O(N*M) に制限される、とのことですが、とにかく動くことが大事なので見なかったことにします。 Python の re モジュールとはちょっと違うようですし、PCRE とも違いますが、ここまでわかればなんとかなりそうです。 実際のコードは、
import sqlite3
from sys import exec_prefix

    conn = sqlite3.connect(db_file)
    conn.enable_load_extension(True)
    conn.load_extension(exec_prefix + "/DLLs/regexp.dll")
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
みたいに使えばいいでしょう。

CDリッピングと音楽ファイル化。

一昔前はiRiverとかのデジタルオーディオプレイヤー、またはMP3プレイヤーとステレオイヤホンいうのが巷を席巻していましたが、今はみんなワイヤレスヘッドホンでスマホで、iTunesとかYouTubeとかAmazonミュージックとかのストリーム再生なんでしょうか。

自分の場合はCDを購入して聴くことが多いのですが、外出時には当然CDプレイヤーを持っていくことはないです。スマホのストレージに音楽ファイルを入れておいて、お気に入りのプレイヤーアプリで再生という使い方が多いです。

で、久しぶりにCDを買ったので、これまでのようにリッピングしようかと思い、新しいメインPCに定番で使っていたCDexをインストールしようとNASのフォルダを開いたところ、Windowsセキュリティが反応してPUABundler:Win32/FusionCoreが入ってるから検疫する?と聞いてきました。

このFusionCoreはいわゆるマルウェアやアドウェアというタイプで、不要な広告を表示するもの。無害といえば無害だけど、ユーザの許可なくインストールされて動作するという点ではお行儀の悪いソフトです。

CDex自体は2020年以降のアップデートはされていないようです。なので他にいいのがないかどうかを探してみることにしました。

目的はCDから音楽データを吸い出してスマホで再生可能な音楽ファイルに変換すること。変換は最近使っているFDK_AACが使えればいいな、という程度です。それからもうひとつ重要なのが、吸い出しの際にCDDBからアーティストや曲名を拾ってこれること。これがないとFile001.wavなどというものになってしまい、ファイル名からなんの曲なのかがわからなくなってしまいます。

ちなみにこれまで長い間利用していたCDデータベースの freedb.org は2020年3月末で終了していたのに今さら気づきました。ということは2年以上も音楽CDを購入していなかったってことかしら。

その代わりではないですが、MusicBrainzという音楽Wikipediaみたいなサービスがあり、そちらを使うことでfreedb.orgと同じことができるようです。


最初はWindows用で探したら、Monkey's Audioというものが見つかりました。が、どうやらこれ単体ではCDからのリッピングはできないようです。リッピングツールは別に用意して、WAV形式で吸い出したものをMonkeysAudioで圧縮するという手順になるのですが、リッピングツールとしては非常に評価の高いExact Audio Copyが使えます。音楽CDはデータCDのようなエラー訂正の仕組みはないのですが、音楽データを正確にリッピングすることができるらしい、です。

Exact Audio CopyはWAVデータとして保存することもできますが、同時にエンコードもできるようです。デフォルトでは以下のような外部エンコーダに対応しているようです。



外部エンコーダなので、別途エンコーダ自体を用意しないといけません。以前にFFmpeg 5.0をコンパイルしてみる。でFDK_AACのエンコーダをビルドしているのでたぶんこれが使えるとは思いますが、もうちょっと安直にできないかな、と探してみました。

最後に見つけたのがfre:acです。
fre:ac is a free audio converter and CD ripper with support for various popular formats and encoders.
ということで、いろんなフォーマットやエンコーダをサポートしています。CDDBもGnuDbという音楽データベースに対応しているようです。
Gnudb.org a new home for the freedb.org database to make sure it stays free.

 freedb.orgのデータベースを移行したんでしょうか。GNUプロジェクトとの直接の関係はないように見受けられますが、Wikipediaには次のように書いてあります。

2020年3月31日をもってサービス終了することが告知され、全てのサービスが終了し、gnudb.orgへ自動的に転送されるようになっている。

どうやらfreedb.orgへのアクセスはgnudb.orgに転送されるようです。

それはさておき、fre:acはデフォルトで以下のようなエンコーダをサポートしています。


FDK-AACが入ってますね。ラッキーです。ワンストップでリッピングからエンコードまででき、かつ <アーティスト名>/<>/nn-曲名.m4a のように出力ファイルの場所と名前を指定できるので、これでいこうと思います。

そのうち、倉庫に保管してるCDもエンコードし直すかな。


ちなみにちなみに、Linuxではどうかと思って検索してみたら、abcdeというのが見つかって、このabcdeはshスクリプトでツール類を適宜起動してリッピングからエンコードまでやってくるようなんですが、WSL2でのArchLinuxにcdparanoia入れてabcdeで動くか、と思ったらそもそもcdparanoia自体が動きませんでした。
正確に言うと、cdparanoiaは起動時に /dev/cdrom や /dev/hd0a などのブロックデバイスを探しにいくのですが、WSL2では親側のCDドライブ(DVDでもBDでも)をマウントするには

mount -t drvfs q: /mnt/q

みたいにマウントして参照する以外はまだできていないようで、この時点でアウトっぽいです。

NVR510に切り替えてみた。

9月3日に、ルーターをこれまで使っていた NEC Aterm WR8700N から YAMAHA NVR510 に置き換えました。この際だから ONU も NVR510 にセットできる小型 ONU に変更しようかと思いつつ、今のところはそのまま。

IPv6のPPPoEとIPv4のPPPoEをそれぞれセッションとして登録し、これ自体はちゃんと動いたのですが、どうもスタティックルーティングの Flets サービス情報サイトへの接続がうまくいかないのでどうしたもんかと思いつつ。

ZOOT NATIVE IPv4固定IP1個サービスが2ヶ月無料、月額基本料金2,200円(税込)ということだったので、どうせ IPv6 IPoE に切り替えるつもりだったこともあり、9月4日に申し込みしました。

IPoEを申し込むには、自分が使用している回線のお客様ID(CAFxxxxみたいな番号)とアクセスキーが必要になります。IPoEでは、ユーザ認証は網側にこの情報を登録するので、PPPoE とは異なりルータの設定にIDとパスワードは不要になります。

実はこの ID はNTT回線開通時に送られてくる契約書に書かれているのですが、保管してるのは確かなんだけど見つからなかったので116に電話して契約書の再発行をしてもらいました。そのため ZOOT NATIVE 自体の申込みが9月にずれこんだのでした。

それはともかく、ZOOT NATIVE IPv4固定IP1個サービスに申し込むと1時間ほどで登録完了メールが送られてきます。そこには、ルーターの設定に必要な契約サービス情報の

  • 固定IPトンネル終端装置IPv6アドレス
  • インタフェースID
  • グローバル固定IPv4アドレス
  • アップデートサーバーユーザー名
  • アップデートサーバーパスワード
  • アップデートサーバーURL

が記載されていて、これを NVR510 の簡単設定で登録してやればすぐに繋がりました。IPフィルタ設定やDNSサーバなどもそのままで、サービス情報サイト(v6)にもそのまま繋がりました。

また、ZOOTのサーバー構築支援ツールのページから自分のIPアドレスにpingを飛ばすと、ちゃんと応答があります。

固定IPが確認できたので、gonbeiのDNSサーバーに新しいIPを登録し直してとりあえずの作業は完了。一通り設定が終わったので、1週間くらい様子を見て、ZOOT NEXTを解約しようと思います。

自分メモ: ManjaroLinux を pacman -Syu したらいろいろ変わってた点。

 たぶん数カ月ぶりくらいになると思いますが、NVR510 を導入して ZOOT NEXT から ZOOT native に切り替えたのを機に、またサーバ環境を構築しようかと ManjaroLinux を pacman -Syu してみましたが、いろいろ変わってそうなので備忘録。

locale.conf, locale.gen:
locale.conf.pacsaveとして保存されました、とメッセージが出たけど見当たらないので気にしない。locale.genはいくつか新しいlocaleが追加されてました。

/etc/security/limits.d/10-gcr.conf:
10-gcr.conf.pacsaveとして保存されました、と出ていたので、新しいと思われる方を残しておきました。

gnome-keyring:
Symlinkを作ってました。
Created symlink /etc/systemd/user/sockets.target.wants/gnome-keyring-daemon.socket → /usr/lib/systemd/user/gnome-keyring-daemon.socket.

kea:
だいぶ前から出ているメッセージなのでメモだけ。以前のバージョンは2.0、今回のバージョンは2.0.3なので非該当ということで。
## WARNING!##
Upgrading from Kea version <1.6 to version >= 1.6 requires special attention
Please refer to the following web page for details:
[Upgrading to Kea 1.6](https://kb.isc.org/docs/en/upgrading-to-kea-16)
Notable changes include:
 - Files in /var/kea have moved to (/var)/run/kea
 - State Info: now in /usr/lib/kea
 - Hooks: /usr/lib/hooks has moved to /usr/lib/kea/hooks
 - Admin commands lease-{init,version,upgrade} have been renamed
   to db-{init,version,upgrade}
 - Logger configuration changed scope thus a manual configuration
   file intervention is needed)

lightdm.conf:
/etc/lightdm/lightdm.conf.pacnetとしてインストールされました、というメッセージが出たので、内容を確認してマージ。

mkinitcpio.conf:
/etc/mkinitcpio.conf.pacnew としてインストールされました、というメッセージが出たので、内容を確認してマージ。

mariadb:
dbをアップグレードしてね、と出ていたのでアップグレード。
"systemctl restart mariadb.service && mariadb-upgrade -u root -p"

sudo:
/etc/sudoers.pacnew としてインストールされました、と出ていたので内容を確認してマージ。

unbound:
/etc/unbound/unbound.conf.pacnew としてインストールされました、と出ていたので内容を確認。IPv6関係を見直し。

perl:
5.34のmoduleはシステムでは使ってないけど依存関係があったので削除されてないから、
pacman -Qqo '/usr/lib/perl5/5.34'
で調べて対応してちょうだい、と言われたので調べてみたら、x2goserverが呼び出している様子。x2goserverは2021/5/24にビルドされた4.1.0.3-10が最新のようで、perlモジュールをいくつか使用していて、たぶんperlモジュールのバージョンを上げても大丈夫だろうとは思うけど、念のためそのまま残すことに。

ざっと以上です。

NVR510を買ってみた。

 どうも会社PC(VPN)の通信速度が遅いので、プロバイダが悪いのかルータが悪いのかそれともIPv6にすれば速くなるのか(IPv4 over IPv6)、とりあえず参考までにPCをONUに直結して東日本のフレッツサービス情報サイトに繋いでみました。

これはフレッツ網内の速度なので、プロバイダを通した速度とは異なるとは思いますが、まずは倍以上速くなりそうという感触は得られます。


うちはプロバイダはインターリンクで、ZOOT native 固定IP1個というタイプになります。

実はこれまでAterm WR8700nといういささか古いモデルをルータとして使っていましたが、これだとIPoEとかIPIPとかはできないんですよね。なので、ZOOTがIPv6を通してくれるようになったのに恩恵にれていませんでした。

ということで一念発起、YAMAHA NVR510を注文してしまいました。

ちょっと光回線(フレッツNEXTギガファミリー)の契約情報が、これまでサービス情報サイトにちゃんと接続できていなかったのでクラブ登録もできず、NTTにお願いして開通書類の再発行をしてもらっているので、本格的に繋げられるのはもう少し先になりそうです。

スタートアップに登録されているものを削除する。

 CTRL+Shift+ESC でタスクマネージャーを起動してスタートアップのタブを開くと、結構たくさん登録されていて、ここで有効/無効を切り替えてあげればスタートアップ時に実行されることはなくなります。

なくなりますが、この項目をこのタブから削除することはできません。


たとえばターミナルとターミナルプレビューは、現在プレビューを使っていて、以前のターミナルは使っていません。同じような項目が並んでいるのは気持ち悪い。

その他にもシュナイダーのStartup Notification Moduleが2つあったり、ScanSnap Homeが2つあったり。

この項目の実体は複数の方法で登録されていて、一箇所から管理できません。なので個別に見ていく必要があります。

ショートカットのもの

  • C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
  • C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
レジストリのもの

  • コンピューター\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  • コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

  • コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
ここらへんから削除してしまえば、スタートアップタブから項目が削除できます。

tkinterでボタンとメニューに同じ関数を割り当てる。

 tkinterでGUIアプリを作っていて、Menubarがあるのでそこにメニューを追加、画面上にボタンも付けて、ボタンを押してもメニューから選んでも同じ処理をしたい、となったんだけれど、ボタンの方は動くのに、メニューの方からは「引数が足りません!」と怒られました。

怒られたコードはざっくりこんな感じ。

    # メニューバー
    menubar = tk.Menu(root, tearoff=False)
    root["menu"] = menubar
    filemenu = tk.Menu(menubar, tearoff=False)
    menubar.add_cascade(label="File", menu=filemenu, accelerator="F")
    filemenu.add_command(label="開く", command=self.open_pyfile)
    
    # ボタン
    self.btn1_text = tk.StringVar()
    self.btn1_text.set(gui_text["button1"])
    button1 = ttk.Button(frame1, textvariable=self.btn1_text, style="TButton")
    button1.bind("<ButtonPress>", self.open_pyfile)

def open_pyfile(self, event):
    """ファイルを開く

    Args:
        event (_type_): _description_

    Returns:
        Path: 選択されたファイル
    """
    ofile = filedialog.askopenfilename(
        initialdir=Path.cwd(),
        title="ファイルを開く",
        filetypes=(
            ("Python files", "*.py;*.pyw"),
            ("JSON files", "*.json"),
            ("All files", "*.*"),
        ),
    )

    self.file1.set(ofile)
    return Path(ofile)
ボタンの方はちゃんと動くけれど、メニューから「開く」を選んだ瞬間に引数が足りないと言われます。 ググってみても検索語の選び方がうまくないのか、lambda: するといいよ、とかいろいろ引っかかるんですが、こうすれば解決できました。
def open_pyfile(self, event=None):
要は、eventが引き数にあればそれでいいし、ない場合にはNoneにしちゃえ、ということです。実際にevent変数を使用した処理をしているなら、event==Noneならメニューから呼ばれたものとして扱えばいいわけですし。

自分的には目からウロコでした。

Path.glob()でExplorer風にソートしてみる。

 Path.globでファイル名に[]を含む場合のエスケープ。で、ワイルドカードに[]が入っていると動作がおかしくなるのでそれをエスケープするというのをやりましたが、同じく glob ネタ。

Explorerではひらがな・カタカナを区別せずにソートしているっぽいですが、Path.glob()とかバッチファイルだとワイルドカードが文字コード順にソートされるので、処理順序がExplorerでの表示順と異なります。ちょっと気持ち悪いので、それっぽくできないかと考えてみました。ちなみに、Explorerでの並び順と正確に同じかどうかは検証してません。それっぽければ問題ないので。

sortedglob.py

#!python3
# -*- coding: utf-8 -*-
# vim:fenc=utf-8 ff=unix ft=python ts=4 sw=4 sts=4 si et fdm fdl=99:
# vim:cinw=if,elif,else,for,while,try,except,finally,def,class:
"""
sortedglob.py:
与えられたパターンに一致するファイル名を取得して、
Explorer風のソートを行い、リストを返す

created : Jun 26, 2022
"""
from pathlib import Path
from jaconv import z2h, kata2hira


def sortedglob(pattern: str):
    """Explorer風にソートするglob.

    patternで渡されたワイルドカードパターンを展開して、
    Explorer風にソートした順番で返す。

    Args:
        pattern (str): globに渡すパターン

    Yields:
        Path: ソートされたPathオブジェクト
    """
    dir = Path(pattern).absolute().parent
    p = Path(pattern).name
    pathlist = {}
    for f in dir.glob(p):
        key = kata2hira(z2h(str(f.absolute()), kana=False, ascii=True, digit=True))
        pathlist[key] = f
    result = sorted(pathlist.items(), key=lambda x: x[0])
    for f in result:
        yield f[1]


if __name__ == "__main__":
    for s in sortedglob("M:/Files/Documents/*"):
        print(s)
jaconvで全角英数を半角にして、カタカナは全部ひらがなに揃えて、それをキーにしてソートします。
さらに漢数字対応とかしてもいいかも。

マルチスレッドとgenerator。

 通常の線形の処理なら問題ないのですが、処理を効率化しようとマルチスレッドにすると、処理をする対象をどうやってスレッドに渡そうかと思うことがあります。

たとえば、たこ焼きを焼いています。

たこ焼き器の凹みをなんて呼ぶのかは知らないのですが、タコツボとでも呼びましょう。

タコツボにはかならず1つのタコを入れます。2つ入れてはいけないし、1つも入ってないのもだめです。

たこ焼き職人が1人なら問題ありません。順序よくタコツボにタコを落としていきます。

職人が2人になると、問題が起こります。タコツボに入れたタコは沈んでしまうので、もうひとりの職人が間違ってもう1個のタコを入れてしまうかもしれません。

それを防ぐためにはタコツボを管理する人が必要です。タコツボを差し出しておいて、どちらかの職人がタコを入れたらすっと引っ込めて次のタコツボを差し出します。そうすればどちらの職人がタコを入れようが、タコツボには1つのタコだけが入ります。


なんて、よくわからない喩え話ですが、要は処理したいシーケンスがあり、そのシーケンスのアイテムは一度だけ処理されればよい、なのでスレッド外で誰かが管理しないといけない、ということです。

リストですと、シングルスレッドならば

for item in itemlist:

で片付きます。ところがマルチスレッドにするといきなりずっこけます。というか、どう言うコードを書いたらいいものかと考えてしまいます。

1つは明らかにitemlistのインデックスを管理するカウント変数を用意して、各スレッドは自分がitemを受け取ったらそのカウント変数を+1する方法です。当然ならそのカウント変数はグローバルになるでしょうから、スレッドセーフではありません。なので、itemを受け取ってカウント変数を+1するまでは、ロックしておかないといけません。

ところがこれがインデックスで管理できるものならよいのですが、たとえばディレクトリツリーを探索する、みたいな感じだとそうも言っていられません。ツリーが巨大になると、そのツリーをリスト化するだけでも結構な時間がかかります。

そこでgeneratorです。アイテムを返すジェネレータを1つだけ用意し、各スレッドから排他的にデータを受け取れれば、二重に処理されるということもありません。

ということで、こんな風にしてみました。


import threading
from random import random
from time import sleep



def get_word():
	l = """
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
	s = l.split(" ")
    for t in s:
        yield t

def tthread(name=""):
    waittime = random()
    while True:
        with lock:
            try:
                t = gen.__next__()
            except StopIteration as e:
                break
        print(f"{t}", end=" ")
        sleep(waittime)


def main():
    t1 = threading.Thread(target=tthread)
    t2 = threading.Thread(target=tthread)
    t1.start()
    t2.start()

gen= get_word()
lock = threading.Lock()
main()
もとの文は PEP 20です。
lockを取得できたら1単語もらって表示する、というだけですが、__next__()がいい仕事してくれます。

IntelのドライバがWindowsUpdateで巻き戻される件。

ffmpeg や QSVEnc の関係で、Intel の Quick Sync Video を使っているので、ドライバのバージョンを勝手に上げられたり巻き戻されたりするのはすごくやっかいです。

デバイスドライバだけ Windows Update の対象外にできないか、と思ったのですが、検索してみたら次の記事が引っかかりました。


これによると、PowerShellの管理者モードで、レジストリに以下のキーと値を設定することで回避できる、とのこと。

reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /v ExcludeWUDriversInQualityUpdate /t REG_DWORD /d 1 /f

最近ちょくちょく PowerShell は使っているのですが、ここはオーソドックスに Regedit でやってみました。

  1. HKLM\SOFTWARE\Policies\Microsoft\Windows に行く。
  2. 新規にキー WindowsUpdate を作成する。
  3. キーに移動して、新規に DWORD で ExcludeWUDriversInQualityUpdate を作成し、値を 1 にする。
これでほんとに回避できるかどうか、しばらく見てみようと思います。

Path.globでファイル名に[]を含む場合のエスケープ。

 Pythonのpathlib.Pathにあるglob()ですが、どうしても引っ掛けられないファイル名が出てきて、どうすればよいか調べ中です。

具体的には、globの引数に "*[123].txt" というような、[] で囲まれた文字があるファイル名を指定したいのですが、それが引っかかってこない。どうやらワイルドカードの特殊文字になっているようです。

別のライブラリ glob には glob.escape() というメソッドがあるのですが、それを使うと今度は "*" がエスケープされてしまい、やっぱりファイルが引っかかりません。

はてさてどうしたものか……。

足りない頭で考えましたが、結局pathlib.Path.glob()で扱うことは諦めて、glob.glob()とglob.escape()を組み合わせて使うことにしました。

from glob import glob, escape
from pathlib import Path

def bracketglob(s: str):
    """generate a list of filenames from a string that contains "[]".

    []で囲まれた文字列を含むワイルドカードを展開する。

    Args:
        s(str): ファイル名文字列

    Returns:
        f(Path): glob展開されたファイル名

    """
    #
    # [] で囲まれた文字とそれ以外を別扱いにするひつようがありそう
    #
    lsqbpos = rsqbpos = -1
    l1 = l2 = l3 = ""
    if (lsqbpos := s.find("[")) != -1:
        l1 = s[0:lsqbpos]
    if (rsqbpos := s.rfind("]")) != -1:
        l3 = s[rsqbpos + 1 :]
    l2 = s[lsqbpos : rsqbpos + 1]
    if (rsqbpos - lsqbpos > 0) and (lsqbpos > 0):
        for f in glob(l1 + escape(l2) + l3):
            yield Path(f)
    else:
        for f in glob(s):
            yield Path(f)
なんとなく yield 使ってますが、そもそも for ループで glob() 使ってるからいらないんじゃね?的なギモンはあるし、力技でエレガントではない感じがジンジョーじゃない……。
処理としては、"[]" で囲まれた文字列とそれ以外を分離して3つに分けて、"[]" 文字列をescape します。"[]"は最大長で解釈させているので、

"吾輩ハ猫デアル [夏目漱石][上][大倉書店][1905].doc"

などという場合、"[夏目漱石][上][大倉書店][1905]" はすべてエスケープされるので、たぶんこの中の文字も検索対象になる…と思います。検索したいパターンに "*[大倉*]*" などとするときっと無理ですが。

CKLauのブザー音をなんとかしてみる。準備編

 ゴールデンウィークに届きまして、調子良く動いてくれてますCKLauの4ポートHDMI KVMですが、ポート切り替え時に鳴るブザーの音がけっこう耳障りです。

かん高いピーっという音と音量が結構うるさい。なのでこれをマイルドな感じにできないかと開けてみました。


ひっくり返すと赤丸の4箇所にネジがあります。これは2番の+が必要です。左のシールは製造年月表示なんですが、ピンセットで持ち上げたら破れました。でもどうせマークされていないので気にしません。

開けてみると中はシンプルです。


シルクが天地逆ですが、右端にあるのがブザーです。マグネチックブザーというタイプで、型番はTMB12A05。定格は5V/30mAで音圧が85dB以上、周波数2300±500Hz。駆動信号を与えるのではなくて、電流を流してやれば自己励振して発音するようです。周波数低めの音の代替製品があればいいのですが、村田製作所などの圧電ブザーは3.6kHzとか。

自作するならNE555で無安定マルチバイブレータ作って、出力でトランジスタを駆動してスピーカを鳴らすといい音がするかもしれません。

ちなみにこの回路では発振周波数は480Hzくらいになるので、いわゆるA音(A3)の 440Hz よりは高めですが、どうでしょうか。鳴ればいいだけなら圧電スピーカを使うほうが安上がりですね。

その他の方法はというと、ブザーのケースを壊せば音は小さくなるかなとか、穴塞げばいいかなとか。

ついでに KVM スイッチの本体である LSI ですが、写真中央部の四角い QFP チップで、GSCoolink GSV2007 とあります。検索してみると、HDMI 2.0 RX4/TX1 で HDCP非対応のようです。

また左上の USBコネクタ付近の正方形のチップはRealtek RTS5411 USB3.0ハブコントローラです。


裏面。

左上に4つ並んだ IC は中央2つが無印、両側が WCH CH551G で E8051コアの1チップマイコンのようです。また、右上の正方形の IC は WCH CH559L で、これも E8051コアのUSBホストコントローラ。

結構しっかり作られてる感じです。

現在のところ不満点は切り替え時の音だけですが、NE555のマルチバイブレータ自作キットが AliExpress で送料込300円くらいであるんですが、どうしようかなぁ。

Mercurial から Git へ引越し。

母艦(という言い方もあまりしなくなってきたみたいですが)を切り替えるのにあわせて、これまでのメイン PC でやってきた各種設定や Python スクリプトをコピーして使おうとしたのですが、環境が違うと手直しも必要になってきます。

これまではバージョン管理はメイン PC の、たとえば c:\Apps\bin にリポジトリを作って、そこにコミットしたりしてたのですが、新規の PC で手直ししたりするとその変更をメイン側に反映するのも結構手間です。さらに、その結果としてメイン側でちょこっと変えて、それを新規側に戻して、とかになってくると、やっぱりリポジトリはサーバにおいたほうがいいかな、と。ちょー今更ですが。

実はかなり前になりますが Bitbucket にアカウントを作って、そちらをリポジトリにしようかなとしていたことがあります。なんだかんだでちょっとお試しでいじって放置してました。ところが Bitbucket は2010年に Atlassian に買収され、2020年6月で Mercurial のすべてのサポートを終了し、Mercurial リポジトリを削除し、Git のみにするということになっていたようです。そしてアカウントもいつのまにか Atlassian に移行していました。

一方、実は GitHub にもアカウントを作っていまして、こちらのほうはちょっとだけ使ったのですがやっぱり放置していました。Mercurial のローカルリポジトリで当面の用は足りていたのと、Python をメインで使うんだから VCS も Python 系の Mercurial がいいし、むしろ Git のみのために Perl をインストールするのがなんとなく嫌だったりしたもので。

といいつつ、やっぱり Visual Studio は Git を組み込んでそのまま VCS が使えたり、VSCode も Git リポジトリを見に行ったり、兎にも角にも Git がないと始まらない世の中になってきていたりするので、そろそろ Mercurial じゃなきゃ嫌だ!みたいなことやってると面倒だったりするんだろうなということで、すぱっと Git に切り替えて、リポジトリも GitHub を利用させてもらえばいろいろスッキリするじゃん、と。

ということで、移行することにしたのですが、たぶんステップとしては2つ。

  1. ローカルの hg リポジトリを git リポジトリに変換
  2. ローカルの git リポジトリを GitHub にインポート
手順については、9.2 Gitとその他のシステムの連携 - Git へ移行する にまとめられているので、これに沿ってやっていきます。

ローカルの hg リポジトリを git リポジトリに変換

まずは懸案の /Apps/bin から取り掛かります。

hg-fast-export をクローンします。
$ git clone http://repo.or.cz/r/fast-export.git /Apps/fast-export
説明では '/tmp/fast-export' となっていますが、ここではわかりやすいところとして '/Apps/fast-export' を指定します。クローン後に中身を見ると、 hg-fast-export.sh と hg-fast-export.py があります。この hg-fast-export.py は hg-fast-export.sh から呼び出されるため、sh 系のシェルで実行する必要があります。そのため、此処から先は MSYS2 で作業を行います。

次に、コマンドラインから Mercurial を使えるようにします。TortoiseHG はインストールしてありますが、ここでは hg-fast-export.sh を実行するので、MSYS2 に Python3 をインストールし、さらに Mercurial もインストールして hg コマンドを使えるようにしておきます。
$ pacman -S python mercurial
そうしたら次に author の正規化を行います。具体的には、コミットしたユーザ名を "ユーザ名" <メールアドレス> の形式に修正します。
$ cd /Apps/bin
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors
ここでは authors をみると全部同じ名前だったけれどメールアドレスが入っていなかったので(なんたってローカルリポジトリですから……)
bob=Bob Jones <bob@company.com>
の例にあるように '=' に続けて正しいユーザ名形式で記述しておきます。

次は Mercurial リポジトリから Git リポジトリへの変換の準備です。
$ cd /c/Apps
$ git init converted
$ cd converted
$ ../fast-export/hg-fast-export.sh -r ../bin -A ../authors
ここでもしかしたら以下のようなエラーが出るかもしれません。
/c/Apps/converted% ../fast-export/hg-fast-export.sh -r ../bin -A ../authors
Error: The option core.ignoreCase is set to true in the git
repository. This will produce empty changesets for renames that just
change the case of the file name.
Use --force to skip this check or change the option with
git config core.ignoreCase false
/c/Apps/converted% 

なぜか core.ignoreCase が true になってるよ、ということですので、最後の行にあるように
/c/Apps/converted% git config core.ignoreCase false

で設定を追加しておきます。

いよいよコンバートです。
/c/Apps/converted% ../fast-export/hg-fast-export.sh -r ../bin -A ../authors
Invalid file format in [../authors], line 1
Loaded 0 authors
master: Exporting full revision 1/144 with 32/0/0 added/changed/removed files
master: Exporting simple delta revision 2/144 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 3/144 with 0/1/0 added/changed/removed files
[...]
master: Exporting simple delta revision 143/144 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 144/144 with 0/2/0 added/changed/removed files
Issued 144 commands
fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:          490 (         0 duplicates                  )
      blobs  :          202 (         0 duplicates        160 deltas of        200 attempts)
      trees  :          144 (         0 duplicates        141 deltas of        141 attempts)
      commits:          144 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (       144 unique    )
      atoms:             45
Memory total:          2493 KiB
       pools:          2141 KiB
     objects:           351 KiB


---------------------------------------------------------------------
pack_report: getpagesize()            =      65536
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 35184372088832
pack_report: pack_used_ctr            =          2
pack_report: pack_mmap_calls          =          1
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =     277981 /     277981
---------------------------------------------------------------------
なんか最初にエラーが出てます。どうもauthorsファイルがダメって言ってるようですが。このままだとユーザ名が正しくありません。

ということで別の方法を取ります。
'/c/Apps/bin/.hg/hgrc' を開き、
[extensions]
hgext.convert=

を追加します。そして、
/c/Apps/bin% hg convert --authors ../authors . ../bak
initializing destination ../bak repository
scanning source...
sorting...
converting...
143 initial import
[...]
writing author map file C:\Apps\bak\.hg\authormap

/c/Apps/bin% 

としてやれば、'../bak' にリポジトリを作り直してくれます。
'../convert' をいったん削除してから、再度コンバートしてみます。
/c/Apps% rm -rf converted
/c/Apps% git init converted
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /c/Apps/converted/.git/
/c/Apps% cd converted
/c/Apps/converted% git config core.ignoreCase false
/c/Apps/converted% ../fast-export/hg-fast-export.sh -r ../bak
master: Exporting full revision 1/144 with 32/0/0 added/changed/removed files
master: Exporting simple delta revision 2/144 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 3/144 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 4/144 with 1/0/0 added/changed/removed files
master: Exporting simple delta revision 5/144 with 0/1/0 added/changed/removed files
[...]
master: Exporting simple delta revision 144/144 with 1/2/1 added/changed/removed files
Issued 144 commands
fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:          490 (         1 duplicates                  )
      blobs  :          202 (         1 duplicates        160 deltas of        200 attempts)
      trees  :          144 (         0 duplicates        141 deltas of        141 attempts)
      commits:          144 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (       144 unique    )
      atoms:             46
Memory total:          2493 KiB
       pools:          2141 KiB
     objects:           351 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =      65536
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 35184372088832
pack_report: pack_used_ctr            =          2
pack_report: pack_mmap_calls          =          1
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =     278859 /     278859
---------------------------------------------------------------------
/c/Apps/converted% git shortlog -sn
   144  Chiyosuke Fujii
/c/Apps/converted%

以上でローカル側での作業は終わりで、あとはできあがったリポジトリを GitHub にプッシュするだけです。
$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all
嘘でした。ここから苦難の道が始まりました。


Hyper-VとWindowsハイパーバイザープラットフォームと仮想マシンプラットフォーム。

https://docs.microsoft.com/ja-jp/windows/wsl/installPC を Core-i7 12700K で組み直したので、Windows11 もクリーンインストールし、環境構築中です。

UNIX 系のコマンドラインツールには Windows が持っていないものがたくさんあって、コンソール(Windows Terminal)を多用する自分としてはインストールしておきたい。Cygwin とか MSYS2 もあるけど、できれば使い慣れた ArchLinux がいい。そうすると WSL2 を使ってインストールしてしまえばいいのかな、と思ってたんですが……。

WSL2 を利用するには、Microsoft 公式の Windows Subsystem for Linux に関するドキュメント を読みながら進めるのが良さそうなんですが、ここで「Windows の機能の有効化または無効化」で、サービスを有効にしておく必要があります。なのでその設定を開いてみたのですが…。


なんか混乱します。ハイライトされている「Linux 用 Windows サブシステム」を有効化したいんだけれど、そのためには、「Hyper-V」と「Windows ハイパーバイザープラットフォーム」と「仮想マシンプラットフォーム」のどれを使えばいいの?しかも「コンテナー」なんてものまであるし。

  • Hyper-V
    ハードウェア上でハイパーバイザが動作し、その上にOSが乗る Type 1 ハイパーバイザ。Intel VT や AMD-V などのハードウェア支援が必須。高速。Windows もハイパーバイザの上で動作する形になる。
  • Windows ハイパーバイザープラットフォーム
    Hyper-V が管理するハイパーバイザー上でのパーティションの作成と管理、構成を行うプラットフォーム。
  • 仮想マシンプラットフォーム
    よくわからないけど、複数の仮想マシンの作成と管理のためのプラットフォームなんでしょう。違いがよくわかりませんが。

WSL2 はどれが必要なのか、やっぱりわかりません。WSL のインストール を見てもその辺は書いていないので、まずはこのページにあることをやってみましょう。

管理者の PowerShell または Windows コマンド プロンプトに次のコマンドを入力し、コンピューターを再起動することによって、Linux 用 Windows サブシステム (WSL) を実行するために必要なすべてをインストールできるようになりました。

 

wsl --install

このコマンドを実行すると、必要なオプション コンポーネントの有効化、最新 Linux カーネルのダウンロード、WSL 2 の既定としての設定、および Linux ディストリビューションのインストールが実行されます " (既定では Ubuntu です。変更するには以下を参照してください) "。

だそうなので、上記の「Windows の機能」の有効無効は気にせずにやってみます。
PowerShell 7 を管理者モードで開き、



なんとなんと!仮想マシンプラットフォームをインストールしています。それでいいの? Hyper-V はどこいったの?
ともあれ、リブートしてみます。
再起動して「Windowsの機能」を開いてみると、「Linux 用 Windows サブシステム」と「仮想マシンプラットフォーム」にチェックが入っていました。
そしてPowerShellが自動的に開き、Ubuntuをインストールし始めました。


やっぱり Hyper-V はノーチェックです。それだけでいいのかしら。ほんとに?
と思ってタスクマネージャーを開いてみると、

なんか動いてます。
でもまあ、標準の方法らしいですから、それでいいことにしましょう。よしよし。

CKLauのUSB3.0+HDMIのKVMスイッチ。

これまでのメイン PC(Core-i9 9900K)と新しいメイン予定の PC(Core-i7 12700K)、それにテレワーク用の PC と、PC の切り替えが大変になってきたので、これまで使っていた 2ポートの KVM スイッチから、新しく 4ポートのKVMスイッチを検討することにしました。

現在使用しているのが、corega の CG-PC2KDLMCA という DVI 2入力1出力のものです。これは Amazon で2010年2月に購入したもので、なんと12年も使っていたことになります。

途中、ACアダプタのケーブル断線?で調子が悪くなり、同等仕様のアダプタ(5.3V)を購入しています。サンワサプライの P-VGA-AC4で、5.3V/2.4Aというもの。CG-PC2KDLMCA はもともと ATEN CS1782A のOEMで、ボタンデザインなどがちょっと変わっているだけのものです。サンワサプライからも ATEN OEM の KVM スイッチが出ているので、アダプタも互換なのでした。ただ、ATEN からファームウェアアップデートがあったときに適用できなかった事はありました。もともと DVI-D のデュアルリンクで 2,560×1,600@60Hz の能力があったので、2560x1440@60Hzの IO-Data GigaCrysta EX-LDGCQ271DB にも対応できていました。なにげにすごいやつです。しかも HDMI オーディオも通していたので、モニタ側スピーカ(音はヘロヘロですが)からも HDMI 接続だけで音が出ます。

今では PC のマザーボード出力から DVI がほとんどなくなり、モニタ側の入力も HDMI か DisplayPort のみになってきていますが、幸い信号的には DVI と HDMI はほとんど互換なので、DVI-HDMI ケーブルを用意することで使用できていました。


と、能書きが長くなりましたが CKLau です。Amazon で購入しました。


届いた箱を開けたらこんな感じ。ごちゃごちゃ詰まってます。ケーブルを巻いてあるポリが擦り切れたのでしょうか、ちょっと裂けていますが、問題はありませんでした。


ケーブルはこんな感じ。


アダプタの箱が潰れています。これも箱だけで中身はちゃんとしてました。


上の写真の細長いダンボールの中に本体があります。ちなみにアダプタは USB 出力のもので、5V/2.0Aでした。



表と裏。USB 3.0 の B タイプで、二段重ねのやつです。左端に電源スイッチとアダプタジャック、それに有線リモコン用の USB miniB のコネクタがあります。ちなみに箱から出した写真のリモコンの隣りにある青いスリーブのケーブルが miniB です。

日本だと製造銘板があって、消費電力が明示されているのですが、この製品は Made in China と CE、FCC、RoHS、それに製造年月日を表すシール(ただしマーキングしてないのでいつ製造されたのか不明)しかありませんでした。ついでに言えば、型番もないようです。型式番号がないのにどうやって輸出通してるのかしら。ちなみに、箱には "Model: CKLau-64HUA-3N" と書かれたシールが貼ってありました。それはともかく、あとでワットチェッカーで電力測ります。


ケーブルは HDMI と USB 3.0 が各 4 本。1本にまとめているのではなく、2本ずつ束ねて使うようです。しかも長さが違っています。

HDMIケーブルはコネクタの内側(ケーブル部分のみ)で 140cm、一方 USBケーブルは 170cmと、30cmも違います。これはびっくり。しかも、現在のデスク周りの配置だとメイン PC にぎりぎり届かない感じ。PC を動かしての作業などがあれば確実に届きません。これはちょっとまずいので、急遽ケーブル調達。Amazon Basicsの 1.8m USBケーブルとHDMI 2.0ケーブルを3本ずつ購入しました。なぜ3本ずつかというと、HDMIケーブルが2本セット(1,206円)と3本セット(1,207円)で1円しか違わなかったので、どうせなら3本にしてしまえという、あまり良く考えていない理由。


これをコードプロテクターという名前の、ワイヤ織管ケーブルスリーブで包んでいきます。これも Amazon で購入してたものです。

とりあえずメイン系のタワー型2セット分を新規購入のケーブルで、残り2セットを添付のケーブルで組んでセッティングしてみます。

セッティングが終わってワットチェッカーを見てみると、表示が01。消費電力1Wと出ています。このあたりの数値は精度的にちょっと微妙なところなのですが、PCサスペンド時には04(4W)とか出ているので、でたらめな数値ではなさそうです。

……とここまでやって、計測も終わったからとアダプタをワットチェッカーから抜いても、スイッチは動作しっぱなし。そりゃそうです、USBからも給電されてるのですから。

ともあれ、どうやらちゃんと動いてくれているようです。もともと4K対応ということですから、WQHDごときは屁の河童なのでしょう。

機材もないので、アイパターンのチェックとか CEC の波形確認とかはしていませんが、無事使えましたということで。

あとは、機能としてはホットキーで切り替えもできるし、有線リモコンでもできるということですが、キーボードの向こう、モニタの下に本体を設置しているので、ホットキーは無効、リモコンは使わないので箱の中にしまっておきます。

ともあれ、これで4ポートKVMが動きました。ユーザレポートも少ないようなので、購入を検討されている方の参考になれば。

Intel Arc GPU。

 今朝、Intel の Driver & Support Assistant が Bluetooth ドライバのアップデートがあります、ついでにあなたのシステムは Intel Arc に対応しています、というメッセージを出していました。

先日やっと Core-i7 12700Kが届いて、少しずつセットアップ作業を進めているところです。マザーボードは Asrock Z690 Extreme、CPU クーラーは水冷の Corsair iCUE H115i RGB PRO XT で、メモリは DDR4-3200 32GB です。ブートドライブは M.2 の NVMe 規格の Crucial CT1000P1SSD8 で、レスポンスはめちゃくちゃいいです。

でもって、ARC に対応していますなんて言われたらちょっと気になるので、自分用のまとめ。

  • Intel Arc は単体GPUとして開発され、Alder Lake とは Deep Link と呼ばれるバス?で接続して機能する。これによってディスクリートGPU (Arc)と内蔵GPU UHD770 とを連携させることができる。
  • Deep Link は H シリーズに対応という噂があるが、デスクトップ向けの S シリーズでも利用できる。
  • Hyper Encode は Alder Lake の Iris Xe エンコードエンジンと Arc を Deep Link で連携させて高速エンコードを行う。アプリケーションにもよるが、Cyberlink PowerDirectorでは1.6倍高速になるらしい。HEVC なのか H.264 かは不明。
  • AV1 HWエンコード対応の Xe メディアエンジンを搭載している。最大で12bit HDR の 8K/60fps の動画をリアルタイムデコード(エンコードではない)できる。エンコードについては、10bit HDR 8K に対応しているが速度は不明。もちろん、VP9、H.264(AVC)、HEVC のハードウェアエンコードもサポートしている。
  • Arc は製品名で、アーキテクチャコードネームは Alchemist。
  • Arc A シリーズというモジュールとしてリリースされるが、これを搭載したアドオンカード(いわゆるグラボ)では、大きいダイの ACM-G10 を搭載して TDP が 250W 程度のハイエンドカードを設計できるのではないか。
現状こんな感じでしょうか。

PythonでWordファイルをいじりたいとき。

 PythonでMicrosoft Wordファイルをいじりたいとき、グーグルさんに聞いてみるとpython-docxってのがあるよ、って言われるんですが。

実はこのpython-docx、マクロ入りドキュメントの.docmファイルは弾いちゃうんですね。

で、さらに検索してみたら、GitHubのほうで、#716 added support for .docm filesていうpull requestがあるんですが、このパッチがマージされないまま放置されてしまっています。

たぶんその放置が理由だと思うんですが、このpython-docxをフォークしてpython-docx-docmというのがあって、このパッケージをダウンロードしてpython-docxと比較してみたら、上記の#716の改善版みたいだったので、.docmファイルまで含めてWordドキュメントをいじりたければこちらを使ったほうがいいのかな、と思ったら、本家のほうがその後バージョンアップしてて、差分をとってセルフパッチしたほうがよさそうな感じ。

Intelドライバー&ソフトウェアアシスタントが変なメッセージ出してる件。

 Intelのドライバー&ソフトウェアアシスタント(IDSA)にアクセスすると、以下のメッセージが表示されます。



でもって右側の「Learn more」をクリックすると、エラーページに飛びます。

community.intel.com/t5/Intel-NUCs/This-system-is-incompatible-with-Intel-Arc-graphics-NUC9i9QNX/m-p/1366874#M89254

に、

I did some more digging and found out that this messaging should not be shown on the IDSA scan page anymore; please refresh your browser or click on the "Refresh Results" on the IDSA left-hand menu in order for that message to be removed.

というのがありました。左側の「結果の更新」をクリックしてみてくれとのことですが、今のところ結果は変わりません。 

コマンドプロンプトのタイトルバーをPythonから変更する。

 ちょっと調べればわかることですが、WindowsTerminalでコマンドプロンプトも扱うようにしたので、タブがいくつもあってどれが何のタブなのかわかりにくくなってしまい、実行しているPythonスクリプトからタブに表示されるタイトルを変更できないかと思いまして。

調べてみると、コンソールからコマンドで "title" というのがあり、Pythonからは

subprocess.run(["cmd", "/c", "title", "アブラカタブラ"])

みたいにすればできることがわかりました。

ところでこのtitleコマンドがどこにあるのか調べてみたら、cmd.exeのビルトインコマンドだということがわかりました。

C:\Users\kats$ wsl strings /mnt/c/Windows/System32/cmd.exe | grep -i title
GetConsoleTitleW
SetConsoleTitleW

ここで、Win32 APIのSetConsoleTitleW()というのが呼ばれていることがわかりました。wslマジ有能。

それじゃあこのWin32 APIを叩けるPythonモジュールはないかと探したら、Pywin32にありました。

Python 3.10.1 (tags/v3.10.1:2cd268a, Dec 6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from win32api import SetConsoleTitle
>>> SetConsoleTitle("アブラカタブラ")
>>>

とまあこれだけでタイトルが変わりました。

どっとはらい。

Windows 11でシステムロケールをUnicodeにしてみる。

 だいぶ前(2019年5月9日)にWindowsロケールをUTF-8にして不具合のあるアプリ。でちょっとお試ししたものの、ヘタレなのでもとに戻してしばらく立ちます。その後どうなったかを見てみようかということで再度設定、確認してみました。


追記: 結論から先にいうと、やっぱりやめたほうが良さそうです。

影響があったソフトで今も使っているものは、Python3とcmd.exe、筆王くらいでしょうか。

まずはPython。バージョンは3.10.1で、Windows Terminal Preview上のコマンドプロンプトから実行してみます。

以前の画面キャプチャがないですが、どうやら使えそうな雰囲気です。reとか試してませんが。
前回はだめだったIDLEも起動します。

cmd.exe。こちらはコードページが65001になって、メッセージも英語になってしまいます。

こんな感じ。まあ、これで問題なければ使えます。

次に筆王。これはだめでした。

スプラッシュ画面は表示されますが、起動しません。たぶん、なんかキーになるものを見てるんでしょうけど、よろしくないですね。

あと、Aviutlではプラグインがやっぱりどうやらだめみたいで、前回はなにか勘違いしていたみたいですね。

ということで、やっぱりUnicode UTF-8はまた封印することにしました。

NeovimからWSL2のMeCabを使ってみる。

Neovimで日本語のファイル内をうろついているときに、もっといい感じで移動できるといいのに、と常々感じてはいたのですが。実はだいぶ前に deton/jasentence.vim と deton/jasegment.vim というのを見つけてまして、もうちょっとちゃんといじってみようかなと。

jasentence.vim は )( を使って「。」や「、」への移動(センテンス移動)を行えるプラグインです。"、。,.?!" を見てくれるらしいので、ついでに "「" や "」" も見てくれればいいのにな、と思ったり思わなかったり。まあそれはさておき。

英語だと w や e での移動は単語単位になりますが、文の構造上単語を切り出すのが難しい日本語では、漢字・カタカナ・ひらがな・その他という感じで大雑把な単位での移動になってしまいます。:help word-motions では、

  • w: 次のwordの先頭に移動
  • b: 直前のwordの先頭に移動
  • e: 次のwordの末尾に移動
  • W: 次のWORDの先頭に移動
  • B: 直前のWORDの先頭に移動
  • E: 次のWORDの末尾に移動
  • word: 空白(space, tab, <EOL>)で区切られた文字の連続。空白行もwordとして扱われる。
  • WORD: 空白(space)で区切られた文字の連続。空白行もWORDとして扱われる。

などとなっています。

当然、日本語でも同じキーで移動したいわけで、じゃあやっぱり形態素解析で文を見てもらったほうがいい感じで動作するのかな、と。

jasegment.vim がそのへんをうまくやってくれるようで、後ろで動いてくれるエンジンには CaboCha や TinySegmenter、MeCab などを使えるようです。デフォルトでは KNBコーパスで文節を学習させたデータが使われるようで、それでもいいんですが、ここは MeCab を使ってみようかと。

Windows用バイナリは MeCab: Yet Another Part-of-Speech and Morphological Analyzer に用意されているのですが、せっかくなので WSL2 上の ArchLinux で動作させてみようかなと。幸い、AUR をmecabで検索すると mecab、mecab-git、mecab-ipadic、python-mecab、mecab-ipadic-neologd-git などが登録されているようですので、ビルドとインストールは makepkg でうまいこといきそうです。ちなみにソースは mecab だと tarball を拾ってきて、mecab-git だと github から拾ってくるようです。github のほうは ipadic と jumandic をソースツリーに取り込んでるように見えます。ここでは github からのほうを使ってみます。

ということで早速始めます。

git clone https://aur.archlinux.org/mecab-git.git
cd mecab-git
makepkg -sri

でも辞書はインストールされていません。なので mecab-ipadic をインストールします。

git clone https://aur.archlinux.org/mecab-ipadic.git
cd mecab-ipadic
makepkg -sri

辞書は /usr/lib/mecab/dic/ipadic 以下にインストールされました。さらに mecab-ipadic-neologd-git もインストールします。

git clone https://aur.archlinux.org/mecab-ipadic-neologd-git.git
cd mecab-ipadic-neologd-git
makepkg -sri

こちらの辞書は /usr/lib/mecab/dic/mecab-ipadic-neologd にインストールされました。

MeCab の辞書指定は /etc/mecabrc で行います。

dicdir =  /usr/lib/mecab/dic/ipadic

標準では ipadic が指定されていますが、不満が出てこなければこのままでもいいか、と手を付けずに置きます。

念の為、mecab になにか食わせてみます。

% mecab
念の為、mecab になにか食わせてみます。
念 名詞,一般,*,*,*,*,念,ネン,ネン
の 助詞,連体化,*,*,*,*,の,ノ,ノ
為 名詞,非自立,副詞可能,*,*,*,為,タメ,タメ
、 記号,読点,*,*,*,*,、,、,、
mecab 名詞,一般,*,*,*,*,*
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
なにか 副詞,助詞類接続,*,*,*,*,なにか,ナニカ,ナニカ
食わ 動詞,自立,*,*,五段・ワ行促音便,未然形,食う,クワ,クワ
せ 動詞,接尾,*,*,一段,連用形,せる,セ,セ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
み 動詞,非自立,*,*,一段,連用形,みる,ミ,ミ
ます 助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS

次にコマンドプロンプトから食わせてみます。

C:\Users\kats$ wsl mecab
念の為、mecab になにか食わせてみます。
念 名詞,一般,*,*,*,*,念,ネン,ネン
の 助詞,連体化,*,*,*,*,の,ノ,ノ
為 名詞,非自立,副詞可能,*,*,*,為,タメ,タメ
、 記号,読点,*,*,*,*,、,、,、
mecab 名詞,一般,*,*,*,*,*
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
なにか 副詞,助詞類接続,*,*,*,*,なにか,ナニカ,ナニカ
食わ 動詞,自立,*,*,五段・ワ行促音便,未然形,食う,クワ,クワ
せ 動詞,接尾,*,*,一段,連用形,せる,セ,セ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
み 動詞,非自立,*,*,一段,連用形,みる,ミ,ミ
ます 助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS

ちゃんとできてますね。

では、Neovimでやってみます。


まず knbc_bunsetu から。


次に、以下のコマンドを実行して mecab で動かします。

let g:jasegment#mecab#cmd = 'wsl mecab'
let g:jasegment#model = 'mecab'


できてるようですね。パチパチパチ。


でも、どちらかというとカーソル移動では knbc_bunsetu のほうが好みかも。

二ヶ国語のMP4ファイルをエンコードする。

TSファイルで二ヶ国語(音声多重)のものを二ヶ国語MP4にエンコードするのは、Aviutlだとひと手間必要です。Handbrakeなど他のツールは使っていないのでわかりませんが、自分は以下のような手順で行っています。
 
もちろん他にもいろいろやり方はあるでしょうが、バッチファイル化してTS(うちの場合はTMSRで編集したm2tsファイル)を食わせてやれば自動的にdemuxしてくれますので、この方法でずっとやっています。

  1. M2TSファイルを用意します。
  2. ffmpeg でストリーム0(映像部)を切り出して、m2v にします。
  3. ffmepg でストリーム1(音声1)を切り出して、m4a にします。
  4. ffmpeg でストリーム2(音声2)を切り出して、m4a にします。
  5. m2v は Aviutl に食わせて mp4 にします。
  6. m4a はそれぞれ音声コーデックに食わせて、aacにします。
  7. 最後に remuxer に食わせて結合します。
2と3は、そのまま結合するとなぜかTMSRが「MP4で標準的な音声ストリームではない」と言われてしまうため、あえて入れています。
また、5の音声コーデックはffmpegでもいいしQAACでもいいです。音質的には ffmpeg + libfdk_aac と qaac と、どちらでも非常によいです。また、5.1ch音声でもちゃんとエンコードしてくれます。remの部分でqaacかffmpegを選択して使います。
ffmpeg -i "%~nx1" -map 0:0 -c copy "%~dpn1.m2v"
ffmpeg -i "%~nx1" -map 0:1 -c copy "%~dpn1-1101.aac"
C:\Apps\aviutl\exe_files\qaac64 -V 127 "%~dpn1-1101.aac"
rem ffmpeg -i "%~nx1" -map 0:1 -c:a libfdk_aac -vbr 5 "%~dpn1-1101.m4a"
ffmpeg -i "%~nx1" -map 0:2 -c copy "%~dpn1-1102.aac"
C:\Apps\aviutl\exe_files\qaac64 -V 127 "%~dpn1-1102.aac"
rem ffmpeg -i "%~nx1" -map 0:2 -c:a libfdk_aac -vbr 5 "%~dpn1-1102.m4a"

ffmpeg は libfdk_aac を使えるように自分でビルドしています。
結合は remuxer を使います。

remuxer -i {video} -i {audio1} -i {audio2} -o {mp4file}

です。
以前はmp4box使ってたんですが、ファイル名の扱いの部分でちょっとおかしい(たぶん文字コード関係)ことがあったので、こちらに切り替えました。

4790KのPCでWindows11する。

現在のメインPCはCore i9-9900Kなので、すでにWindows11にアップグレード済なのですが、以前使っていた2台のPCはどちらもCore i7-4790Kで、Windows11の対象外になってしまっています。

2015年に購入したものですが、4790Kは今でも十分に使える能力は持っているので、このまま置いておくのはもったいない。ということでなんとかWindows11にできないかとやってみました。

まず、マイクロソフトのWindows 11をインストールする方法のページを参考にレジストリを追加します。こちら(マイナビニュース)にも同様の記事があります。
reg add HKLM\SYSTEM\Setup\MoSetup /v AllowUpgradesWithUnsupportedTPMOrCPU /t REG_DWORD /d 1 /f
を管理者用コマンドプロンプトで実行します。これでCPUがサポート外でもアップグレードが可能になります。

ところが、使用しているマザーボードがAsrock Z97 Extreme 4で、これはTPM2.0に対応していません。これだけだとWindows11セットアップではねられてしまいます。

CPUのほうはこれでスキップできましたが、どうにもTPM2.0のところで引っかかってしまい、先に進みませんでした。

そこで、こうなったらTPM2.0モジュールを購入しようと決心し、ダメでもともと、一番安そうなところを探してみました。Asrockが出しているTPMモジュールが購入できればよいのですが、これがなんと1万円以上しているのです。そもそも出荷数が少ないのか、それともWin11特需でなくなっているのかはわかりませんが、そこまでお金は出せません。このページによれば、Nuvoton NPCT650またはInfineon SLB9665なら使えそうです。

Amazonで購入したのはこちら。Extreme 4のマニュアルを見ればわかる通り、TPMモジュール用ヘッダは18ピンで13ピンが抜けているタイプです。AmazonのページにはType AからType Iまで9種類がありますが、Type Cが当てはまります。もちろん信号名なども照らし合わせて確認しています。注文すると品物は上海から発送されましたが、2週間ほどで手元に届きました。ちなみにチップはInfineon SLB9665TT20です。
ということで早速PCにセットして、起動してみたのですが…。
BIOSのUEFIでIntel Platform Trust Technologyの項目が出てきません。

これもよく調べてみるとBIOSで対応していないようで、最新の2.60でもダメでした。ところが下の方にベータBIOSがあり、そこにある2.60Aで「Support TPM 2.0」とあります。ということで早速Instant FlashでBIOSをアップデートし、TPMモジュールを接続してみました。

…が、それでも出てきません。Win+Rキーで「tpm.msc」と入力してTPMの管理パネルを開いても、モジュールが見つからないというメッセージです。

ここでデバイスマネージャーを見ると不明なデバイスが増えていて、よくよく見てみるとこれはTPMモジュールではなくIntel Smart Connect Technologyデバイスでした。これをBIOSでDisableしてやると、UEFIのAdvancedタブのところにIntel Platform Trust Technology関係の項目が増えていました。

その状態でWindows10を起動し、「tpm.msc」を開いてみると…。
認識されました。これなら勝つる!
ちなみにMBRからGPTへの変更はWindows 11のための準備を整える。の手順ですでに終わっています。
そのままWindows 11のセットアッププログラムを実行してみると…。
おお!
どっこいしょ!
そらきた!
ということで、ずんずん進み、最終的にインストールは完了しました。
CPUはCore i7-4790Kで、OSはWindows 11 Proです。

FFmpeg 5.0をコンパイルしてみる。

ビデオファイルをいじったりエンコードしたり加工したりするのに必須なffmpegの5.0がリリースされたので、コンパイルしてみました。

記事はこちら。「FFmpeg 5.0」が公開 ~大規模なAPIの変更を実施した長期サポート(LTS)リリース

環境はWSL2上にインストールされたArchLinuxを使います。別にUbuntuとか他のディストロでも問題ないと思いますが、うちはArchしかないので…。

まず、ffmpeg-windows-build-helperをcloneします。

$ git clone https://github.com/rdp/ffmpeg-windows-build-helpers

そしたらそのディレクトリに移動します。

$ cd ffmpeg-windows-build-helpers

指定オプションの意味は、

$ ./cross_compile_ffmpeg.sh -h

すれば出てきます。英語のみですけど。

でもってビルドします。

$ ./cross_compile_ffmpeg.sh --build-ffmpeg-shared=y --ffmpeg-git-checkout-version=n5.0 --fdk-aac-git-checkout-version=v2.0.2 --gcc-cpu-count=8 --disable-nonfree=n --build-intel-qsv=y --build-lsw=y --compiler-flavors=win64

すると怒られます。

    windows WSL detected: you must first disable 'binfmt' by running this
        sudo bash -c 'echo 0 > /proc/sys/fs/binfmt_misc/WSLInterop'
        then try again

言われたとおりにします。

$ sudo bash -c 'echo 0 > /proc/sys/fs/binfmt_misc/WSLInterop'

再度ビルドします。

$ ./cross_compile_ffmpeg.sh --build-ffmpeg-shared=y --ffmpeg-git-checkout-version=n5.0 --fdk-aac-git-checkout-version=v2.0.2 --gcc-cpu-count=8 --disable-nonfree=n --build-intel-qsv=y --build-lsw=y --compiler-flavors=win64

しばらく待ちます。
また怒られます。
仕方ないので、cross_compile_ffmpeg.shを編集し、

    config_options+=--enable-libsvthevc

をコメントアウトして、--enable-libbluray --enable-fontconfig --enable-libfreetypeを削除します。たぶんこれは字幕に必要だからだと思いますが、うちでは不要なので問題なしです。
再度ビルドします。
また怒られました・・・QSV関係なので、これはコマンドラインオプションから--build-intel-qsv=yを削除します。

再度ビルドします。

すると、

  Done! You will find 64-bit shared non-redistributable binaries in
  /home/kats/work/ffmpeg-windows-build-helpers/sandbox/win64/ffmpeg_git_with_fdk_aac_n5.0_shared/bin
  

なので、次のようにしてアーカイブし、mntしてるWindowsのフォルダに放り込みます。

  $ cd /home/kats/work/ffmpeg-windows-build-helpers/sandbox/win64/ffmpeg_git_with_fdk_aac_n5.0_shared
  $ 7z a ffmpeg_with_fdk_aac_n5.0_share.7z bin doc include lib presets LICENSE.md README.md
  $ cp ffmpeg_with_fdk_aac_n5.0_share.7z /mnt/c/Users/kats/work
  

でもって、パスの通ったところに展開すれば完了です。

Vimの補完プラグインをインストール。その4

Vimの補完プラグインをインストール。その3 で、 ddc-tabnine が使えそうです、などと書いたのですが、早速やってみました。 まず、tabnineのバイナリを用意しないといけません。がどうにもTabNineのサイトがわかりにくいので、 tabnine-nvim にあるダ...