はつねの日記

Kinect, Windows 10 UWP, Windows Azure, IoT, 電子工作

同じソースコードを.NET Core 3.1と.NET 5でビルドした結果

[EN]

Have you guys started using .NET 5, which was GA last November?
docs.microsoft.com
I am preparing to migrate my WPF application to .

.NET Core 3.1, I found that the size of the exe was reduced even though I didn't modify the source code at all.

.NET Core 3.1 (171KB) -> .NET 5 (140KB)

.NET 5 (140KB) It became about 20% slimmer.

[JP]

昨年11月にGA(正式リリース)された.NET 5つかっていますか?
docs.microsoft.com
私は.NET FrameworkWPFアプリを.NET 5 WPFへ移行する準備をゆるゆると進めています。

以前、.NET Core 3.1でつくった小さなツールを.NET 5でビルドしてみたら、ソースコードは一切手を入れてないのに作成されたexeサイズが減っていたので報告します。

.NET Core 3.1 (171KB) → .NET 5 (140KB)

20%くらいスリムになりました。

Azure Cognitive Services - Speech Servicesの対応言語が増えました。

ちょっと油断していたたら、音声認識対応言語が9言語ぐらい増えてた。

英語(ガーナ)
英語(ケニア
英語(タンザニア
フィリピン語
フランス語(スイス)
ドイツ語(オーストリア
インドネシア語
ベトナム語
マレーシア語

Microsoft Group TranscribeがiOSアプリとして登場

www.microsoft.com
Microsoft Translatorアプリでも実現でしていましたが、よりリアルタイム性が高い感じで翻訳ができるMicrosoft Group Transcribeアプリが登場してきました。まずはiOSアプリから。
バイスのマイクを使ってその場で音声を取得するので「スマホは発話者のそばで!」みたいな使い方Tipsが書かれていますね。
回線経由の音声じゃない分、音声認識率はよさそうですね。

どうやって会議室をつくるの?

同じ部屋の中にいる人通しではアプリを立ち上げるとbluetoothでみつけてワンタップで参加できます。
あとは5桁のコード、もしくは、QRコードでも同じ会議室にさんかできるので、遠く離れた人同士は5桁のコードで参加ですね。

どこが便利なの?

5桁のコードが判ってしまうと世界中の誰でも参加できるし、近くにいる人とはbluetoothでサクッと参加できますね。
とにかく不特定多数の人との会話の文字起こしには便利ですね。
一方、企業内の会話とかに外に漏れたくない場合なんかはちょっとダメそうですね。適材適所というところでしょうか。

動作ってどんな感じ?

日本語端末で話をすると、自分の発話の日本語のみが表示されます

英語端末に届くと日本語がでて、その下に英語で翻訳されます。

英語端末で英語を話すと、自分の発話の英語のみが表示されます。

日本語端末に届くと英語が出て、その下に日本語で翻訳されます。

同時に話すとどうなるの?

同時に話すと誰か1人の端末の音声認識だけがONになるようです。
つまり複数人が同時にどんどん喋ったとしても、少しでも早く話し始めた人以外は認識がONにならないようです。
でも、ちょっとでも息継ぎで間を開けると認識していた端末以外の音声をその瞬間だけ取得するような動きです。
「ちょっとまって」とか入れずらいので、話す側が少し工夫したりするといいんじゃないでしょうか。

会議が終わった後のデータは?

会議中の発言(つまり表示されていた状態)は、いつでも履歴から参照できます。
そこから共有もできますね。
端末ごとに履歴の内容が違う(言語ごとに、他国語から自国語への翻訳表示部分が違う)ので端末に保存されているのかな。

使ってみてどうだった?

話し始めて、最初の文字がでてくるまで若干待たされる感じはしますが、そこからは比較的スムーズです。
類似の無料アプリがいろいろありましたが、そのあたりを使っている場合は、これで充分かな。

協力しよう

発話した内容は「会議の録音は、複数の話者の会話を理解して、会議中に同じ言語を話す全員のMicrosoft オンライン音声認識技術の品質向上に役立つ可能性があります。」
ということで会議の録音を投稿する機能があります。
自国語の音声認識率をあげたいときは、社外秘の会話ではないのであれば、投稿するのもいいかもしれないですね。

Microsoft Ignite 2021に合わせてUpdateされた私が注目するAzrue関連について

Ignite 2021が始まりましたね。
開発者していると分かるのですが自社イベントに合わせて自プロダクトの新機能の提供を開始するとかはよくある話です。
ギリギリスケジュールでとりあえず形だけってケースも多いと思うけれど、Microsoftさんの場合は、Private Preview→Public Preview→GAみたいなマイルストーンで確実に提供する感じなのでイベント合わせのリリースもすごくきっちりしていますね。
今回もたくさんのUpdateがありましたが、その中から注目している分野に絞ってご紹介します。

Azure Communication Services

.NETラボ 勉強会 2020年11月 - connpass
.NETラボ 勉強会 2020年10月 - connpass
.NETラボでも何回か登壇させてもらったネタであるACSことAzure Communication ServicesのUpdateをご紹介します。
azure.microsoft.com
ACSの裏側はTeamsと同じアークテクチャーであることは公言されていましたし、ACSでビデオ会議を作るとそのURLはTeamsのそれと非常に似た構造ですし、繋がるようになるよとは書かれていたのですが、いよいよPublic Previewとなって誰でも試せるようになりました。

何がうれしいの?

何がうれしいかといえば、自分のアプリからTeamsにつながることですね(そのままですね)。
Windowsだったら「自分のアプリとTeamsアプリ」みたいな感じで2つアプリを立ち上げればいいのであまり嬉しさは感じられないかもですね。
でも、スマホだったらどうでしょうか。2つアプリ同時起動して使うのはなかなか面倒ですし、自アプリとTeamsの両方で音声を使おうとするとTeams側に全部マイクを持ってかれちゃうなんてこともあります。
そこで登場するのが、ACSを組み入れて自分のアプリの中でTeams会議への参加部分も完結しちゃうアプローチですね。

ACSってどんな技術なの?
ACSからTeams会議にはどうつなぐの?

docs.microsoft.com
ACSのビデオ会議に接続するAPIで接続先をTeams会議に合わせて次のように指定する感じですね。

const locator = {
threadId: [thread id],
organizerId: [organizer id],
tenantId: [tenant id],
messageId: [message id]
}

Teams会議の会議URLの構造は次のようになっているので、そこから抜き出して設定ですね。

http://teams.microsoft.com/l/meetup-join/[thread id]/[message id]?context={"Tid":"[tenant id]","Oid":"[organizer id]"}

Azure Object Anchors

azure.microsoft.com
現実空間とデジタル空間のオブジェクトの位置合わせをAzure上で行うAzure Object AnchorsがPublic Previewになりましたね。
いままでは限られた範囲での公開でイベントで事例などがあっても自分たちで試せませんでしたが、これで誰でも試せます!

Azure Remote Rendering

azure.microsoft.com
3DモデルのレンダリングをAzure側で行って、結果を受信できるAzure Remote RenderingがGAとなりました。
ポリゴン数が多いような場合でもAzureパワーでなんとかなってしまいます。
東日本でも使えるので、より低レイテンシーな使い方ができるようになりましたね。

Microsoft Mesh

techcommunity.microsoft.com
マルチデバイスでリアルとバーチャルを融合するMicrosoft Meshが突然Previewしてきました。
詳細はまだよく調べていませんが、HoloLens 2アプリとAltspaceVRアプリがすでに提供されていて、その間でいろいろ共有できるみたいです。

Cognitive Services

Speech ServicesとかTranslatorとかは特にUpdateないみたいです。

それではDay2も眠いけど楽しみですね。
Day1も日本の昼間にリピートセッションあるので、ぜひ!

Xamarinで作成したAndroid App Bundleを端末にインストールする

2021年8月からは、Google Playに新しくアプリを登録するには、従来のapkではなくAndroid App Bundle(aab)で登録することが必要になります。
Visual Studio + XamarinでAndroid向けアプリを作成するときも、もちろん、apkだけではなくaabで作成できます。
初期のころはaab作成がVisual Studioからはできず、MSBuildを使ってコマンドラインでaabファイルをつくらなければなりませんでしたが、最近のVisual Studioではアーカイブでも可能です。

Androidオプションを設定する

aabを作成するには、Androidアプリのプロジェクトのプロパティを表示して、Androidオプション画面で設定します。
aab作成には本当に時間がかかるので、Release構成のみの設定が推奨されています。

Release構成で設定する

そのため、[構成]を「Release」にしてから設定を変更します。
f:id:hatsune_a:20210218073903p:plain
設定は簡単で、Androidパッケージ形式を「バンドル」にするとaab作成となります。このとき次の2つのチェックボックスが、値固定でグレーアウトします。

  • [選択したABIごとに1つのパッケージにします]はOFF
  • [インクリメンタルAndroidパッケージシステム(aapt2)を使用します]はON
aapt2とは

aapt2(Android Asset Packaging Tool)は、アプリのリソースをコンパイル、パッケージ化するために使用するビルドツールです。AAPT2 はリソースを解析してインデックスに登録し、Android プラットフォーム向けに最適化されたバイナリ形式にコンパイルします。

Debug構成で設定する

2つの設定が固定化されるのであれば、Debug構成でも同じ設定にしておいてデバッグしておくことが安全です。
aapt2については新規プロジェクト作成時はデフォルトONになっていますが、以前から使っているプロジェクトではOFFになっているものも多いでしょう。
それでは、[構成]を「Debug」にしてaapt2をONに設定しましょう。
f:id:hatsune_a:20210218075244p:plain
Visual Studioが至れり尽くせりだなーと思うのは、Release構成側で「バンドル」を指定すると、Debug構成側も自動的に2つのチェックボックスが同じ値でグレーアウトしていました。
もちろん、パッケージ形式は「apk」のままです。

アーカイブを実行する

構成ごとの設定がおわったら、Release構成をアクティブにしてアーカイブします。
f:id:hatsune_a:20210218080119p:plain
アーカイブが成功すると、アーカイブマネージャーにバンドル形式「aab」のエントリが表示されます。
f:id:hatsune_a:20210218080434p:plain

配布用に署名する

アーカイブができたら、[配布]ボタンをクリックして「アドホック」でファイル作成して手動でGoogle Play Consoleにアップロードするか、「Google Play」でアップロードまで行うかを選んで、署名付きaabを作成します。
実機にインストールするためには、「アドホック」を選択して署名付きaabファイルを作成しましょう。

aabを実機にインストールする

署名付きaabを実機にインストールしたいときは、adbコマンドを使って実行します。
f:id:hatsune_a:20210218100427p:plain
Visual Studioのメニューからコマンドラインを起動して、USBに実機を接続してUSBデバッグを許可したらadb installします。
f:id:hatsune_a:20210218100918p:plain
Successが出たら終了です。
実機で動作を確認してみましょう。

Google Play Consoleにアップロードする

実機確認できたら、署名付きaabをGoogle Playに登録してみましょう。
試しに、登録済のapkと同じバージョンとバージョンコードで作成して登録してみると次のようなエラーとなりました。
f:id:hatsune_a:20210218090633p:plain
当然ですが、apkとaabで同じバージョンをアップロードはできないようですね。

Google Playでアプリを署名する

ついでといっては何ですが、配布するならば「Google Playで署名されています」マークを付けておきたいですよね。
新規に登録するときは自動的にGoogle Play Consoleで次のような表示になります。
f:id:hatsune_a:20210218090543p:plain
既存登録済でこの表示がない場合は、手動で設定しないといけないのですが、次のQuitaが分かりやすかったです。
qiita.com
qiita.com
上から順番に読んでみると理解しやすくお勧めです。

それではVisual StudioでのAndroidアプリ開発、楽しんでいきましょう!

NAudioをUWPで使うときの注意点

以下、NAudio v1.10での確認となります。
NAudioですが、音声入出力に非常に便利なライブラリで、.NET Framework以外にも様々なプラットフォームで動作します。
github.com

しかし、UWPで使用する場合、いつくかの注意点があります。

.NETネイティブコンパイル時の注意点

それは、ARM / ARM64に限らずx86をプラットフォームターゲットとしたときも.NETネイティブコンパイル時には次のような実行時エラーが発生します。

System.ArgumentException: 'Unsupported Wave Format'

この実行時エラーは、NAudioのサンプルコードでNAudioUniversalDemoがあるので、そちらで確認することができます。
このサンプルコードを.NETネイティブコンパイルして実行して「Record」ボタンをクリックすると、NAudioの中、具体的には、NAudio.Wave.WasapiCaptureRT.InitializeCaptureDeviceの中で下記のエラーが発生します。
こちらは録音デバイスがサポートしていないWaveフォーマットを指定したからだということです。
こちら、.NETネイティブではないときには、WaveFormatとしてサンプリングレート48000、32ビット、1チャンネルのIeeeFloatなフォーマットが自動的に指定されますが、.NETネイティブコンパイル時はその自動指定が行われず、ソースコードを読む限りはAudioClient.csの中のpublic WaveFormat MixFormatプロパティのGetの中でaudioClientInterface.GetMixFormatのところで取得ができていないようです。

注:Visual Studio 2019を使用し、ターゲットバージョン=2004 (ビルド19041)、最小=1803(ビルド17134)を指定

.NETネイティブコンパイルは必須

それでは、.NETネイティブコンパイルしなければいいのでは?となりますが、ストアに登録して配布するときは.NETネイティブコンパイル必須となっているので、配布手段はどうあれ.NETネイティブコンパイルで動作しないというのは、今後を考えると避けたいところです。

対応策

対応策としては、サンプルコードNAudioUniversalDemoでいえばMainPageViewModel.csのなかでrecorder.StartRecording()を実行する前に、明示的にWaveFormatを指定してしまうことです。
NAudio/MainPageViewModel.cs at master · naudio/NAudio · GitHub

recorder.WaveFormat = new WaveFormat(48000, 32, 1);

なお、この場合、IeeeFloatではなくPCMのサンプリングレート48000、32ビット、1チャンネルとなります。
また、万が一、指定したサンプリングレートおよびチャンネル数が、該当のデバイスでサポートしていない録音デバイスだったときも実行時エラーとなります。
こうなってくるとストアに登録して広く公開するのはなかなか難しい状況だといえるでしょう。
例えば、ARM64 Windows機のLenovo C630のInternal Microphoneは、サンプリングレート48000Hz、16ビット、1チャンネルですが、インテルWindows機だと2チャンネルの場合があります。

解決方法

解決補法としては3つ

  1. NAudioを使わない方法を考える
  2. ストア配布対象をx86に絞る
  3. NAudioのMixFormat取得ロジックを.NETネイティブ対応する

まずは、1か2の検討をして、なんとか3が解決される(もしくはOSSなので自力で解決してプルリクする)のを待つ形になります。

本当の解決を目指して

NAudioのMixFormat取得ロジックは.NETネイティブコンパイルしていないときと、.NETネイティブコンパイルしたときではどのように違うか確認してみましょう。
NAudioのGitHubからソースコードを取得して、AudioClient.csのMixFormatクラスにブレイクポイントを貼って、デバッグ実行をしてみます。

非.NETネイティブコンパイル時の動き

48000Hz、16bit、2チャンネルの録音デバイス
非ネイティブのときは、MixFormatの戻り値は、サンプリングレート48000Hz、32bit PCM、2チャンネルとなります。

{32 bit PCM: 48kHz 2 channels wBitsPerSample:32 dwChannelMask:3 subFormat:00000003-0000-0010-8000-00aa00389b71 extraSize:22}
AverageBytesPerSecond: 384000
BitsPerSample: 32
BlockAlign: 8
Channels: 2
Encoding: Extensible
ExtraSize: 22
SampleRate: 48000
SubFormat: {00000003-0000-0010-8000-00aa00389b71}
averageBytesPerSecond: 384000
bitsPerSample: 32
blockAlign: 8
channels: 2
dwChannelMask: 3
extraSize: 22
sampleRate: 48000
subFormat: {00000003-0000-0010-8000-00aa00389b71}
wValidBitsPerSample: 32
waveFormatTag: Extensible

この中のsubFormatの値がWAVE_FORMAT_IEEE_FLOATを意味しています。
この内部値は外から見えないので、外から見えるrecorder.WaveFormatの値を見てみましょう。
こちらは、IeeeFloatとして取り出せます。

{IeeeFloat}

WaveFormat未指定時に本来期待されている動きは、この動きになります。

.NETネイティブコンパイル時の動き

それでは.NETネイティブコンパイルしたときの値をみてみましょう。
MixFormatの戻り値は、サンプリングレート48000Hz、32bit PCM、2チャンネルとなりますが、クラスの型値がExtensibleのままでPCM表記になりません。

{NAudio.Wave.WaveFormatExtensible}
AverageBytesPerSecond: 384000
BitsPerSample: 32
BlockAlign: 8
Channels: 2
Encoding: Extensible
ExtraSize: 22
SampleRate: 48000
SubFormat: {System.Guid}
averageBytesPerSecond: 384000
bitsPerSample: 32
blockAlign: 8
channels: 2
dwChannelMask: 3
extraSize: 22
sampleRate: 48000
subFormat: {System.Guid}
wValidBitsPerSample: 32
waveFormatTag: Extensible

値を確認すると今回の原因はSubFormatにWAVE_FORMAT_IEEE_FLOATを意味する値が入っていないことがわかりました。
しかしここで注目すべきは、サンプリングレート、ビット数、チャンネル数などは取得ができてる点です。もしこれらの値を取得できれば、new WaveFormat(48000,32,1)のように指定してNAudioに該当録音デバイスのフォーマットが指定できることになります。

さあ、NAudioを書き換えよう

NAudioの内部状態のところでエラー判定されているので、NAudioの外側からどうにかできる問題じゃなさそうです。
そうなってくるとNAudioのどのクラスで対応するかですが、UWPのみの問題だと考えると、WasapiCaptureRTクラスの中で対応するのがよさそうです。

gist.github.com

audioClient.MixFormatメソッドの戻り値を変数名mixにいれて、それがWaveFormatExtensibleであればビット数に応じてIeeeFloatかPCMか明示してフォーマットを作成することにします。
例えば、先ほどの48000Hz、16bit、2チャンネルの録音デバイスであれば、内部変数のwaveFormatは下記のようにIeeeFloatとして処理される内容になります。

{NAudio.Wave.WaveFormat}
AverageBytesPerSecond: 192000
BitsPerSample: 32
BlockAlign: 4
Channels: 1
Encoding: IeeeFloat
ExtraSize: 0
SampleRate: 48000
averageBytesPerSecond: 192000
bitsPerSample: 32
blockAlign: 4
channels: 1
extraSize: 0
sampleRate: 48000
waveFormatTag: IeeeFloat

このオリジナルのNAudio.dllは、Releaseビルドすれば、NAudio\bin\Release\uap10.0フォルダに出力されます。
自分のUWPプロジェクトでnugetではなくからそのDLLを参照するように切り替えれば、.NETネイティブコンパイルしたUWPアプリも正常にNAudioが動作します。
どうせならばと、この件は本家にプルリクしてみました。

Azure Web Appsのランタイムスタックの分類について

[en]
When I try to create a web app in Azure and try to specify the runtime stack, I get the following message.
f:id:hatsune_a:20210104130352p:plain
I think ”.NET 5" should be on the ".NET Core" side, but what do you think?

    • -

[jp]
AzureでWebアプリを作成しようとしてランタイムスタックを指定しようとすると以下のような表示になります。
f:id:hatsune_a:20210104130352p:plain
「.NET 5」は、「.NET Core」側にあった方がよいと思うのですが、どうでしょうか。

      • -