===================================
Windows 版サンプルアプリケーション
===================================

サンプルアプリケーションとして配布している HARKlibSample について、その概要を説明する。

既定アプリケーションからの差分
===============================

HARKlibSample は、VisualStudio2019 で生成されるプロジェクトに、
HARK音響ライブラリを使用するための処理と、サンプル用にマイクデバイスから音声を取得するための処理を追記したものである。

以下、差分について説明する。
ただし、HARK音響ライブラリに特化した部分以外の記述は簡易なものに留めている。

UI の構築
^^^^^^^^^^^

UIを MainWindows.xaml に記述を追加することで定義する。具体的には、以下の通りである：

ユーザが操作するための以下の２つの Button を配置する。

* Start ボタン：Click イベントに StartButton_Click を割り当てる
* Stop ボタン：Click イベントに StopButton_Click を割り当てる

プログラムからの状態表示を目的として、以下の２つの TextBlock を配置する。

* Status 表示用：Text に Status をバインディングする
* Output 表示用：Text に OutputText をバインディングする

上記４つの要素は、MainWindow クラスと連携して動作する。具体的には、以下の通りである：

* Start ボタンがクリックされた際の処理を、StartButton_Click メソッドに記述する。
* Stop ボタンがクリックされた際の処理を、StopButton_Click メソッドに記述する。
* Status メンバを Data Binding に対応する形で定義する。
* OutputText メンバを Data Binding に対応する形で定義する。

なお、その他タイトル、画像、レイアウト等については割愛する。

HARK Library への参照の追加
^^^^^^^^^^^^^^^^^^^^^^^^^^^

HARK音響ライブラリの機能を用いるために、インストール済みのHARK音響ライブラリへの参照を設定する。

メニュー [プロジェクト] - [参照の追加] によって開く参照マネージャ画面から、
下部にある [参照...] ボタンをクリックしてダイアログを開く。
事前にインストールした、HARK音響ライブラリモジュールの DLL を選択して追加する。

（通常、``C:\Program Files (x86)\NetCOMPASS\HARK音響ライブラリ\HARKlib.dll`` としてインストールされている）

NAudio によるマイク入力の取得
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

マイクからの音声入力を行うために、外部ライブラリ NAudio をインストールする。
NuGet を用いてインストールが可能である（詳細については割愛する）。

NAudio に関連する処理は、サンプルアプリケーションでは以下のメソッドに記述されるほか、
MainWindow クラスのメンバとして NAudio.Wave.WaveIn オブジェクトを保持している。

* MainWindow.StartRecording(): マイクによる収音を開始する処理
* MainWindow.StopRecording(): 収音を終了する処理
* MainWindow.DataAvailable(): 収音された音声データを NAudio の機能から受け取る処理

ネットワーク定義の実装
^^^^^^^^^^^^^^^^^^^^^^^^^^^

HARK音響ライブラリの処理の定義として、ネットワークの構成を実装により定義する。
具体的には、HARKlib.NetworkDef クラスから派生したクラスを定義し、``build`` メソッドをオーバーライドすることとなる。

サンプルアプリケーションでは、``NetworkDefs.cs`` において、Main クラスとして実装されている。

Main クラスは以下のように定義されている。

.. code-block:: csharp

    class Main : NetworkDef
    {
        public override void build(Network network, DataSourceMap input, DataSinkMap output)
        {
            var loop = network.create<Recording>("CAS_Recording");
        }
    }

ここで、``build`` メソッドが override されて記述されている。

``build`` メソッドには3つの引数があり、``network`` はネットワークの実体に相当するオブジェクトであり、
ネットワークの構成を定義する際にはこのオブジェクトを用いてノードの構築等を行う。
``input`` および ``output`` はネットワークの入力および出力にあたる。
ネットワークの外部とデータの送受を行う場合には、これらを用いてデータのフローを定義する。

``Main.build()`` においては、 ``network.create`` メソッドを用いて1つのノードを構築している。
具体的には、 ``Recording`` クラスによって定義されるネットワークであり、ここでは、Recordingネットワークがあたかも1つのノードであるかのごとく構築されている。
このように、ノードを組み合わせて作ったネットワークを、それが単一のノードであるかのように抽象化して扱うことができるのも、HARK音響ライブラリの特徴の一つである。

また、 ``Main.build()`` では、その構成を定義するにあたって ``input`` および ``output`` が特に参照されていない。
つまり、Main はネットワーク外部との入出力を行わない（孤立した）ネットワークである、ということになる。

続いて、Recording クラスは以下のように定義されている（一部省略）。

.. code-block:: csharp

    class Recording : NetworkDef
    {
        public override void build(Network network, DataSourceMap input, DataSinkMap output)
        {
            //...(略)...

            var node_spectral_mean_normalization_incremental = network.create<SpectralMeanNormalizationIncremental>("SpectralMeanNormalizationIncremental");
            var node_audio_stream_from_memory = network.create<AudioStreamFromMemory, TriggeredMultiShotDispatcher>("AudioStreamFromMemory");
            var node_publisher = network.create<PublishData, RepeatDispatcher>("Publisher");
            var node_subscriber = network.create<SubscribeData>("Subscriber");

            node_audio_stream_from_memory
                .add_input("INPUT", node_publisher["OUTPUT"]);

            // ...(略)...

            node_subscriber
                .add_input("INPUT", node_spectral_mean_normalization_incremental["OUTPUT"]);
        }
    }

``Main.build()`` と同様に、ノードの構築を行うが、その後、ノード間の接続を定義する実装がなされている。

例えば、

.. code-block:: csharp

    node_audio_stream_from_memory
        .add_input("INPUT", node_publisher["OUTPUT"]);

は、ノード ``node_audio_stream_from_memory`` の ``INPUT`` という名前の入力に対し、ノード ``node_publisher`` の ``OUTPUT`` という名前の出力を接続する旨定義している。

続いて、Extracting クラスは以下の通りである（一部省略）。

.. code-block:: csharp

    class Extracting : NetworkDef
    {
        public override void build(Network network, DataSourceMap input, DataSinkMap output)
        {
            var node_multi_fft = network.create<MultiFFT>("MultiFFT");
            var node_feature_remover = network.create<FeatureRemover>("FeatureRemover");
            // ...(略)...

            node_multi_fft
                .add_input("INPUT", input["CHANNEL_WAV"]);

            node_feature_remover
                .add_input("INPUT", node_delta["OUTPUT"])
                .add_input("SELECTOR", "40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81");

            // ...(略)...

            output
                .add_input("OUTPUT", node_feature_remover["OUTPUT"]);
        }
    }

これまでの ``build`` メソッドの実装と同様に、ノードの構築とその接続を定義している。

ここで、

.. code-block:: csharp

    node_multi_fft
        .add_input("INPUT", input["CHANNEL_WAV"]);

のように、 ``build`` メソッドの引数 ``input`` を参照し、その ``CHANNEL_WAV`` という名前の入力を参照する形となっている。
これは、このネットワークに ``CHANNEL_WAV`` という名前の入力が備わっていることを意味する。
また、その入力が ``node_multi_fft`` の ``INPUT`` という名前の入力に接続される旨の定義であることは、前述のノード間の ``add_input`` と同様である。

また、

.. code-block:: csharp

    output
        .add_input("OUTPUT", node_feature_remover["OUTPUT"]);

のように、 ``build`` メソッドの引数 ``output`` に対して ``add_input`` を実行することで、
このネットワークに ``OUTPUT`` という名前の出力が備わっていることを意味する。
また、この出力に ``node_feature_remover`` の ``OUTPUT`` という名前の出力が接続される旨の定義であることは、前述のノード間の ``add_input`` と同様である。

HARK音響ライブラリとのデータ送受の実装
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

HARK音響ライブラリには、その外部とのデータ送受を目的として、PublishData および SubscribeData の２つのノードが定義されている。
サンプルアプリケーションでは、これらの２つのノードがネットワーク定義に組み込まれている。
具体的には、 ``Recording.build()`` の実装を参照されたい。

HARK音響ライブラリとアプリケーションの連携動作
==============================================

前項において、サンプルアプリケーションにおけるネットワーク定義と、データ送受の中継点としての PublishData および SubscribeData について言及した。
ここでは、これらの機能をアプリケーション側から利用する方法について説明する。

HARK音響ライブラリへのデータ送信
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

HARK音響ライブラリへのデータ送信は、PublishData 型のノードを介して行う。

ネットワーク定義において、PublishData 型ノードは、 ``Recording.build()`` にて

.. code-block:: csharp

    var node_publisher = network.create<PublishData, RepeatDispatcher>("Publisher");

のように構築されている。この処理は、PublishData 型ノードを構築して、構築されたノードの実体に ``Publisher`` という名前を付することを意味する。

アプリケーション側では、MainWindow のコンストラクタにて

.. code-block:: csharp

    this._publisher = this._network.query_nodedef("Publisher") as PublishData;

と記述されている。
これは、構築済みのネットワーク ``this._network`` から、 ``Publisher`` という名前のノードを検索し、得られたノード定義を ``PublishData`` 型にダウンキャストするものである。

その後、 ``this._publisher`` は、 ``MainWindow.DataAvailable()`` で参照されており、
このメソッドでは、NAudio から得られた音声データ（byte[]）を16bit整数値の配列（short[]）に変換した上で、

.. code-block:: csharp

    this._publisher.push(sdata);

によって PublishData ノードに送っている。

HARK音響ライブラリからのデータ受信
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

SubscribeData 型ノードは、PublishData と逆に、HARK音響ライブラリからデータを受け取るために用いる。

アプリケーション側では、PublishData と同様に、MainWindow のコンストラクタにて

.. code-block:: csharp

    this._subscriber = this._network.query_nodedef("Subscriber") as SubscribeData;

とネットワークから SubscribeData ノード定義を取得しており、さらに、

.. code-block:: csharp

    this._subscriber.Received += OnReceived;

と、イベントハンドラである ``OnReceived`` の割当を行っている。
これにより、HARK音響ライブラリ内部で SubscribeData 型ノードがデータを受け取った時には、
Received イベントが発行され、上記の割当により、 ``MainWindow.OnReceived`` が呼ばれることとなる。

HARK音響ライブラリによる処理の開始
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

定義済みのネットワークを用いて、以下のようにHARK音響ライブラリの処理を開始することができる。

.. code-block:: csharp

    private Network _network = Network.create_network<Main>("MAIN");
    //...(略)...
    this._network.execute();

ただし、 ``execute()`` はHARK音響ライブラリの処理が完了するまで制御を返さないため、実際には、

.. code-block:: csharp

    Task.Run(() =>
    {
        this._network.execute();
    });

のように、別スレッドでの実行が想定される。

HARK音響ライブラリによる処理の終了
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

実行中のHARK音響ライブラリの処理を止めるには、

.. code-block:: csharp

    this._network.stop();

と、 ``stop()`` メソッドを用いる。
これにより、ブロックしていた ``execute()`` メソッドは制御を返す。

制限事項
==========

現時点では、HARK音響ライブラリについて以下のような制限がある。

* 実装されているノードは限定的であり、サンプルアプリケーションから参照されているものに限られている。
* 検証されているノード構成は、サンプルアプリケーションで定義されているものに限られている。
* PublishData で送信できるデータは、short[] に限られている。
* SubscribeData で受信できるデータは、Dictionary<int, float[]> に限られている。
