HARK ライブラリ
C# 版使用方法(案)

NetCOMPASS
2021/05/31

0.概要
  本文書は、HARK ライブラリのC#版について使用方法等の案を述べたものである。
  C#版機能は設計途上であり、今後仕様が変更となる可能性があることに注意されたい。

1.使用方法
  - ビルド方法
    - ソースの展開
      以下の操作をコンソール等で実行する
        > git clone https://harkdev.backlog.jp/git/LIBHARK/hark-lib.git
        > cd hark-lib
        > git checkout feature-harklib-1-0-master
        > cd hark-lib\windows

        なお、上記 cd 後のディレクトリを、以降 PROJ_HOME と表記する

    - eigen の準備

      https://eigen.tuxfamily.org/ から、stable 版
      (本ドキュメント作成時は3.3.9が最新)をダウンロードし、
        <PROJ_HOME>/hark-lib/windows
      で展開する。

      <PROJ_HOME>/hark-lib/windows/eigen-3.3.9
      などが生成されるはずである(3.3.9 の箇所は実際のバージョン番号になる)。

      eigen-3.3.9 の名前を eigen に変更する。
      次項で生成されるプロジェクトファイルには上記名称でパス参照が設定されている
      ためである。

    - hark-lib プロジェクトの生成
        > cd hark-lib
        > cmake ..\..

        なお、上記操作を cd hark-lib を行わずに実行した場合、既存のファイルを
        上書きしてしまうため注意が必要である。

        以下に出力の例を示す：
        ```
        C:\hark-lib\hark-lib\windows>cd hark-lib

        C:\hark-lib\hark-lib\windows\hark-lib>cmake ../..
        -- Building for: Visual Studio 16 2019
        -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.19042.
        -- The C compiler identification is MSVC 19.28.29334.0
        -- The CXX compiler identification is MSVC 19.28.29334.0
        -- Detecting C compiler ABI info
        -- Detecting C compiler ABI info - done
        -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe - skipped
        -- Detecting C compile features
        -- Detecting C compile features - done
        -- Detecting CXX compiler ABI info
        -- Detecting CXX compiler ABI info - done
        -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe - skipped
        -- Detecting CXX compile features
        -- Detecting CXX compile features - done
        -- Looking for pthread.h
        -- Looking for pthread.h - not found
        -- Found Threads: TRUE
        -- Configuring done
        -- Generating done
        -- Build files have been written to: C:hark-lib/hark-lib/windows/hark-lib
        ```

    - VisualStudio2019 の起動
        > cd ..
        > start hark-lib.sln

    - VisualStudio 上で、通常のビルド方法によりサンプルプログラム HARKlibSample を
      ビルドする

2.実行方法
    サンプルとなるアプリケーションは
      <PROJ_HOME>/hark-lib/windows/HARKlibSample
    に記述されている。

    このサンプルプログラムは、NAudio を経由して
    マイクから収音し、得られたPCMデータをHARKライブラリに渡すようになっている。
    また、HARKライブラリの処理結果を、サンプルプログラムで受け取る実装例にも
    なっている。（現時点では未完成）

  - プログラムの方法
    現時点で提供されているHARKライブラリの機能を理解するには、以下のソースコードを
    参照するのが良い。
      <PROJ_HOME>/hark-lib/windows/HARKlibSample/NetworkDefs.cs
      <PROJ_HOME>/hark-lib/windows/HARKlibSample/MainWindow.xaml.cs

    NetworkDefs.cs では、C++ 版（別ドキュメント参照）と類似のネットワーク定義
    が実装されている。

    上記ネットワーク定義では、PublishData および SubscribeData という２種類の
    ノードが導入されている。

    PublishData は、HARKライブラリ外部から、HARKライブラリに対してデータを投入する
    機能を持つノードである。本サンプルプログラムでは、NAudio から受け取った
    PCMデータをHARKライブラリに投入するために用いている。具体的には、
      <PROJ_HOME>/hark-lib/windows/HARKlibSample/MainWindow.xaml.cs:133
    において、ネットワークから取得した PublishData ノードにデータを投入している。

    SubscribeData は、HARKライブラリから、HARKライブラリ外部にデータを返す機能を
    持つノードである。具体的には、イベントハンドラを実装した上で、Receive イベント
    をに紐づけることになる。
      <PROJ_HOME>/hark-lib/windows/HARKlibSample/MainWindow.xaml.cs:136
    にてデータを受け取るメソッドを実装しており、
      <PROJ_HOME>/hark-lib/windows/HARKlibSample/MainWindow.xaml.cs:57
    にてネットワークから取得した SubscribeData ノードにハンドラを指定している。

    - データの管理
      C# 版HARKライブラリでは、C++ 版における data に相当する形でのデータの管理は
      ライブラリに隠ぺいされており、前述の PublishData, SubscribeData を用いて
      ネットワークとの間でデータの授受をすることが想定されている。

      なお、PublishData は、現時点では以下の型の送受のみ対応している：
        - C# 側: 少なくとも int16 型の配列
        - C++ 側: 少なくとも std::vector<short int>

      また、SubscribeData は、現時点では以下の型の送受のみ対応している：
        - C++ 側: (未定)
        - C# 側: (未定)

    - ノードを実装する
      C# 版HARKライブラリでは、現時点では独自にノードを実装する機能は提供されて
      いない。

    - ネットワークを実装する
      サンプルとなるネットワーク定義は
        <PROJ_HOME>/hark-lib/windows/HARKlibSample/NetworkDefs.cs
      に記述されている。

      ネットワークを実装するには、NetworkDef を継承したクラスを定義し、
      build() メソッドの実装が必須となる。

      build() は、network, input, output の３つの引数を持つ。input, output そ
      れぞれ入出力を管理するオブジェクトであり、network はネットワークの処理状態
      を保持するオブジェクトとして HARK ライブラリ内部で構築され、引数として与え
      られる。

      ネットワーク内に配置するノードは、引数 network を用いて、ジェネリックメソッ
      ド create を呼び出すことで生成する。
        create<NODEDEF, DISPATCHER>(name)
      なお、name あるいは DISPATCHER を省くことも可能である。

      NODEDEF には、ノード定義(NodeDef 型)またはネットワーク定義(NetworkDef 型)を指定
      する。

      ノード定義としては、以下のものを指定することができる：
        MultiFFT
        FeatureRemover
        Delta
        MelFilterBank
        MSLSExtraction
        PreEmphasis
        SpeechRecognitionClient
        SpectralMeanNormalizationIncremental
        VAD_ZC
        SaveFeatures
        SaveWavePCM2
        AudioStreamFromWave
        AudioStreamFromMemory
        Constant
        InputStream
        PublishData
        SubscribeData

      ネットワーク定義は、本項で実装した NetworkDef 派生クラスが相当する。

      DISPATCHER には、ノードの実行タイミングを制御するクラスを指定する。
      ここで指定するクラスは、BaseDispatcher を継承したクラスでなければならないが、
      省略することも可能である。省略した場合には、既定の Dispatcher が用いられる。

      ここまでを踏まえた実装の例を以下に示す。
      public override void build(Network network, DataSourceMap input, DataSinkMap output)
      {
          Node node_multi_fft = network.create<MultiFFT>("MultiFFT");
          Node node_feature_remover = network.create<FeatureRemover>("FeatureRemover");
          Node node_delta = network.create<Delta>("Delta");
          Node node_mel_filter_bank = network.create<MelFilterBank>("MelFilterBank");
          Node node_msls_extraction = network.create< MSLSExtraction>("MSLSExtraction");
          Node node_pre_emphasis = network.create<PreEmphasis>("PreEmphasis");
          ...

      続いて、ノード間のデータ送受を定義する。
      ノードへの入力の定義は、上記で作成した Node オブジェクトに対して、
      add_input メソッドによってデータを「流し込む」ことによって行う。

      例えば、
      node_multi_fft
          .add_input("INPUT", input["CHANNEL_WAV"])
          .add_input("LENGTH", 512)
          .add_input("WINDOW", "CONJ")
          .add_input("WINDOW_LENGTH", 512);
      は、以下のようなデータの流れを定義するものである。
        - INPUT という名前の端子に、（他で定義した、あるいはネットワーク入力であ
          る）input が提供する CHANNEL_WAV という名前のデータを流し込む
        - LENGTH という名前の端子に、固定値 512 を流し込む、等

    　最後に、ネットワークの出力を、以下のように定義する。
      output
          .add_input("OUTPUT", node_feature_remover["OUTPUT"])

      この例では、ノード node_feature_remover の OUTPUT という名前の出力を、ネット
      ワーク全体の OUTPUT という名前の出力としてネットワーク外に提供することを意味
      する。

    - ネットワークを実行する
      ネットワークを実装するには、
        Network network = Network.create_network<Main>("MAIN")
        network.execute();
      のようにする。

以上
