[注意] (2020年6月20日記)

macOS Mojave (10.14.x) からセキュリティが強化され、プログラムからマイクへのアクセスには特別な権限が必要になりました。 「システム環境設定」→「セキュリティとプライバシー」→「マイク」とすると マイクロフォンへのアクセスが許されているアプリケーションの一覧が表示されます。 ここに"+"ボタンがないというバグ(あえてバグと表記します)のため、Javaを追加することができず、 javaにマイクへのアクセス権限を付与できません。

このためjavaのプログラムからマイクへのアクセスができなくなりました。 エラーは起きませんが、データがとれず all 0 となります。 他のアプリケーションでは Info.plistをいじると一覧に追加できる場合もあるようですが、 OpenJDKのjavaではうまくいかないようです。 このバグは macOS Catalina (10.15.x) になっても解消されていません。

したがって、以下で解説しているJavaプログラムからマイクへのアクセス方法は、 WindowsおよびmacOS10.13以前のMacのみで通用するものです。



ネットワーク 演習


[注意] IPヘッダ中の「ヘッダ長フィールド(4bit)」が「ヘッダの長さ/4」を表していて、 通常の値は5でありIPヘッダ長が20byteであることを表します(IPヘッダの最大長は60byte)。 IPヘッダ中の「全データ長フィールド」はIPデータグラムの全長をバイト数で表しており、 これと「ヘッダ長フィールド」から 「IPデータグラム中のデータの始まりの位置とデータの長さ」がわかります。 全データ長フィールドは 16bit 幅なのでIPデータグラムの最大サイズは 65535 バイトです。 65535バイトのIPデータグラムを転送することは可能ですが、 普通はIP層でフラグメント化されて送られます。

実は、ホストは 576 バイト以上のデータグラムを受信するように要求されてはいません。 TCPはユーザのデータをフラグメント化するのでこの制限の影響を受けません。 UDPでは多くのアプケーション(RIP, TFTP, BOOTP, DNS, SNMP など)が ユーザのデータを 512バイト以下の長さにすることでこの影響から免れています (NFSを実装する多くのシステムでは、8192byte以上のIPデータグラムを許していますが)。

演習8aにおいて、ブロードキャスト・アドレスにUDPデータグラムを送信する場合は、 512byte以下のデータサイズになるように sec の値を小さくする方が安全です。 そうしないと、システムによっては送信時にerror が発生します。


課題提出〆切は次回の講義の始まる時刻です。

本来のIP電話は SIP (Session Initiation Protocol) を使ってセッションを生成・変更・切断し、 VoIP (Voice over IP) を用いて相手と通信します。 また、通信する音声データは圧縮をしてデータ量を減らすのが普通です。

今回は非常に単純に、IPアドレスとポート番号を指定して UDP で音声データを送るプログラムと、 UDPでポート番号を指定して送られてきた音声データを再生するプログラムを作ってみます。


課題8a: IP電話 (マイク側)

提出先 http://nw.tsuda.ac.jp/class/network/local/handin/list.php?id=kadai8a
提出ファイルUDPAudioClient.java
コメント欄なし

マイクから音声データを0.2秒分ずつ 「リニアPCM, 16bit, 1チャネル, 8000hz, 符号付き, little endian」 のフォーマットで取得し、ネットワーク上のサーバにUDP プロトコルで送る プログラム UDPAudioClient.java を作成しなさい。

サーバのホストのデフォルトは"localhost"で、 ポート番号のデフォルトは自分の学生番号(例 17901)とすること。 これらの値は、コマンドラインの引数(-s, -p)で変更できること。


UDPAudioClientの実行例
$ javac UDPAudioClient.java
$ java UDPAudioClient -s localhost -p 17901
コンピュータのマイクに向って喋る
^C
$ java UDPAudioClient -s 172.17.0.16 -p 17901
マイク音声を20501に送る
^C
$ java UDPAudioClient -s 172.17.1.255 -p 17901
マイク音声をブロードキャストアドレスに送る
^C

[ヒント] UDPClient.java と AudioInput.java を利用します。 UDPSendClient.java と TestAudioInput.java の青文字の部分が参考になるでしょう。


課題8b: IP電話 (スピーカ側)

提出先 http://nw.tsuda.ac.jp/class/network/local/handin/list.php?id=kadai8b
提出ファイルUDPAudioServer.jsp
コメント欄なし

ネットワークを経由して UDP プロトコルで送りつけられた音声データを 再生するサーバ UDPAudioServer.java を作成しなさい。

UDP プロトコルで送りつけられる音声データのフォーマットは 「リニアPCM, 16bit, 1チャネル, 8000hz, 符号付き, little endian」 であり、データグラムのサイズは0.2秒分、すなわち3200バイトを越えないと仮定してよい。

待ち受けるポート番号は、デフォルトは自分の学生番号 (例 17901) であるが、 コマンドライン引数 (-p) によって変更できること。


UDPAudioServerの実行例
$ javac UDPAudioServer.java
$ java UDPAudioServer -p 17901
ネットワーク経由で送られてきた音声が再生される。
再生された音声がマイクに入力されるとハウリングを起こすので注意すること。
物理的に離れた位置のPCでそれぞれサーバとクライアントを動かしてみるとよい。

[ヒント] UDPServer.java と AudioOutput.java を利用します。 UDPRecvServer.java と TestAudioOutput.java の青文字の部分が参考になるでしょう。


注意

0.2秒分の音声データをそのまま再生するには0.2秒かかります。 したがって、「ネットワークから音声データを受け取る」+「音声データを再生する」 という作業を逐次的に繰返し行うと、再生される音声の遅れがだんだん大きくなってしまいます。 本来は、「ネットワークから音声データ受け取ってバッファに貯める」処理と 「バッファ内の音声データを再生する」処理を別スレッドで並列動作作させるべきでしょう。 また、沈黙の音声データは送らないなどの、効率を高める工夫もした方がよいでしょう。

上記の「記述例 (抜粋)」では、そのような処理は行っていません。 今回みなさんが提出するプログラムも逐次的なもので構いません。