HARK ライブラリ
C++ 版使用方法

NetCOMPASS
2021/05/31

0.概要
  本文書は、HARK ライブラリの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/xcode

    - Xcode の起動
      Xcode を開いてビルド準備をする
        $ open harklib.xcworkspace

    - Xcode 上で、通常のビルド方法(メニュー [Product]-[Build] 等)によりサンプル
      プログラム HARKlibSample をビルドする

    なお、プロジェクト設定により、eigen は
      /usr/local/include/eigen3
    にインストールされている想定となっている。
    異なるパスにインストールした場合は、適宜プロジェクト hark-lib の設定を変更されたい。

2.実行方法
    サンプルとなるネットワーク定義は
      ./HARKlibSample/HARKlibSample/NetworkDefs.swift
    に記述されている。

    なお、上記パスは、前項で示した
      $ cd hark-lib/xcode
    にて移動したディレクトリを起点とした相対パスとなっている（以下同じ）。

    このサンプルプログラムは、iOS が提供している AVFoundation の機能を用いて
    マイクから収音し、得られたPCMデータをHARKライブラリに渡すようになっている。
    また、HARKライブラリの処理結果を、サンプルプログラムで受け取る実装例にも
    なっている。

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

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

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

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

    SubscribeData は、HARKライブラリから、HARKライブラリ外部にデータを返す機能を
    持つノードである。具体的には、SubscribeDataDelegate を実装した上で、receive
    メソッドを実装することとなる。本サンプルプログラムでは、
      ./HARKlibSample/HARKlibSample/ViewController.swift:116
    にてデータを受け取るメソッドを実装しており、
      ./HARKlibSample/HARKlibSample/ViewController.swift:38
    にてネットワークから取得した SubscribeData ノードに delegate を指定している。


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

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

      また、SubscribeData は、現時点では以下の型の送受のみ対応している：
        - C++ 側: std::vector<float> または std::map<int, std::vector<float>>
        - swift 側: NSArray 型または NSDictionary 型

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

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

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

      ネットワーク内に配置するノードは、引数 network を用いて、メソッ
      ド create を呼び出すことで生成する。
        create<T, D>(_ type:T.Type, name:String, dispatcher:D.Type)
      なお、name あるいは dispatcher を省いたメソッドも定義されている。

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

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

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

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

      ここまでを踏まえた実装の例を以下に示す。
      class Extracting: HARKNetworkDef
      {
          override func build(_ network: HARKNetwork, input: HARKDataSourceMap, output: HARKDataSinkMap)
          {
              let node_multi_fft = network.create(HARKlib.MultiFFT.self, name: "MultiFFT")
              let node_feature_remover = network.create(HARKlib.FeatureRemover.self, name: "FeatureRemover")
              let node_delta = network.create(HARKlib.Delta.self, name: "Delta")
              let node_mel_filter_bank = network.create(HARKlib.MelFilterBank.self, name: "MelFilterBank")
              let node_msls_extraction = network.create(HARKlib.MSLSExtraction.self, name: "MSLSExtraction")
              let node_pre_emphasis = network.create(HARKlib.PreEmphasis.self, name: "PreEmphasis")
          ...

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

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

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

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

    - ネットワークを実行する
      ネットワークを実装するには、
        let network = HARKNetwork.create(Main.self, name:"MAIN")
        queue.async {
            network.execute();
        }
      のようにする。ここで、Main は、前項で述べた NetworkDef 継承クラスであり、
      queue はあらかじめ作成した DispatchQueue である。

以上
