[Remotte開発] 音声解析系のアプリ（演習４）

[前回](https://qiita.com/remotte_jp/items/6f379b1f32f5c78f61ee) の演習３では、「ビデオ解析」系の構成要素について演習した。今回は音声解析系の構成要素について、「レベルメーター」を題材に説明する。音声解析系の構成要素は、ビデオ解析系とほぼ同じで、ビデオ解析では関数 new_video_frame( ) によって解析すべきデータがプラットフォームから与えられたのに対して、音声解析では関数 [new_audio_frame( )](https://www.remotte.jp/ja/user_guide/program/functions) によって与えられることが違いだ。
### アプリの新規作成
これまでの演習と同様に、アプリを新規作成する。
![1 アプリの新規作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/8b8794b7-c32d-96c6-66d5-70bfbb195432.png)
### 音声のソースを追加
「構成」画面の左下のメニューから、「メディア」を選択し、音声入力として使用する要素を追加する。
![2 標準マイクの追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/52499efd-df6a-5d13-566c-92ea563dcfc6.png)

### 音声解析を追加
音声のソースを追加できたら、今度は音声解析の構成要素を追加する。「入出力タイプ」として、
　　音声解析（値出力のみ）
　　音声解析（音声出力のみ）
　　音声解析（値と音声の両方を出力）
の３つがあるが、このレベルメーターの演習では、その解析後の出力として入力されたステレオ音声のレベル（フレーム内の振幅の最大値）を求めるだけであり、例えば音声そのものをオクターブ上下したり、雑音を取り除いた音声を出力するわけではないため、「音声出力（値出力のみ）」を選択する。また、解析結果として左右の最大レベルを出力するため、「互換タイプ」として「一般的な２値センス」を選択する。
![3 音声解析の追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/14c06aa9-c1ed-23b8-0515-19db68de52ca.png)
次に「コード」画面で、Python コードを記述する。音声解析では、関数
　　**[new_audio_frame(self, audio_frame, time_sec)](https://www.remotte.jp/ja/user_guide/program/functions)**
によって、解析すべき音声データが Python 側に通知され、この関数内で解析処理を行い、その結果をプラットフォーム側に通知する。ここで引数 audio_frame には音声データが numpy.ndarray 型で格納され、time_sec にはアプリ開始からの経過時間（秒）が float 型で格納される。音声データは１６ビット、２チャンネル、サンプリングレートは４８kHzで、関数 new_audio_frame( ) は２０ミリ秒毎に呼ばれる。言い換えると、この関数は１秒間に５０回呼び出され、audio_frame にはステレオの振幅値がチャンネル毎に９６０個格納されている。参考までに、関数 new_audio_frame( ) の先頭に、
　　print(type(audio_frame), len(audio_frame), type(audio_frame[0]))
と記述してアプリを実行した場合、以下がコンソール出力される。
　　\<class 'numpy.ndarray'> 1920 \<class 'numpy.int16'>
同様に、関数の先頭に、
　　print(time_sec)
と記述して実行した場合、各秒毎に５０件の時間情報が出力される。

<font color="Red">注意！
プラットフォームの仕様として、音声解析及びビデオ解析の構成要素では、関数 \_\_init\_\_( ) の中で print( ) を行なってもコンソールに表示されません。</font>

１秒間に５０回、音声レベルの最大値をブラウザの画面に表示するには負荷が大き過ぎるので、これを１０分の１とし、１秒間に１０回だけデータを更新するようにする制御を入れた場合のソースコードとして、以下を入力する。

```Python:
    def __init__(self, sys, opt, log):
        self._sys = sys
        self._opt = opt
        self._log = log
        self._count = 0

    def new_audio_frame(self, audio_frame, time_sec):
        if self._count == 0:
            left = audio_frame[0::2]
            right = audio_frame[1::2]
            max_left = int(left.max() / 32767 * 100)
            max_right = int(right.max() / 32767 * 100)
            self._sys.set_value({'value': [max_left, max_right]})
        self._count += 1
        if self._count == 5:
            self._count = 0
```

![4 コード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/d0ce7638-e884-4e13-5dd5-e554a6f217e4.png)
０．１秒毎に、左右それぞれのレベルの最大値を０から１００までの範囲にスケーリングして出力する。
### 利用ページの編集
「表示項目」画面では、以下の様に設定する。
![5 表示項目.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/ac920383-8c1a-dc65-ddaf-9c6cc9def785.png)
ここで注意したいのは、「レベルメーター」の最新値として、「表示数」を４つに設定してある。
次に、「レイアウト」画面を選択する。以下の様に、デフォルトでは、４つの表示項目共に「２つの数値表示」という表現形式が設定されている。
![6 デフォルトのレイアウト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/39f75ffb-a5a0-433c-0a7f-db192a9c23b2.png)
上記の Python コードから分かるように、解析結果として
　　[左の最大値, 右の最大値]
が出力されるわけだが、この左の値と右の値を取り出す方法が用意されている。画面右側のオプション設定の中に、「高度な設定」というグループがある。
![7 高度な設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/ee89509b-3d2f-8479-20a9-35ba68cf6d44.png)
この中の「配列から取り出し」というオプションに整数値を設定することで、配列データの中から任意の位置の要素を取り出すことが出来る。例えばこのアプリの例では、「０」と入力すれば、キー名が「value」の値であって、それが配列だった場合に、インデックスが「０」の要素を取得でき、つまりは「左の最大値」を取得できる。同様に「１」を設定すれば、「右の最大値」を取得できる。このオプションを利用して、以下の様なレイアウトを作成してみよう。つまり、左のレベルと右のレベルにそれぞれ２つずつの表示項目を使い、１つには表現形式「１つの数値の表示」を使って０から１００までの値を表示し、もう１つには「段階メーター（水平、領域色付き）」を使って、グラフィック表示する。
![8 １つの数値の表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/5848a466-0bcf-eeb7-e3c4-4c3b34784cd7.png)
![9 段階メーター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/eba85cb2-205e-84f2-bae2-9ae426554054.png)
### 実行！
アプリを実行し、マイクに向かって大きな声を出すと、レベルが大きく振れる。
![10 実行中.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491154/777b4eae-0133-6170-d65f-a720ecfb7cde.png)
### まとめ
本演習では、「音声解析」系の構成要素について体験した。関数 new_audio_frame( ) によってプラットフォームから音声データが与えられ、これを解析することを習得したい。この演習では使わなかったが、解析結果として、入力された音声と異なる音声を出力する場合は、set_audio_frame( ) 関数によって音声データをプラットフォーム側に通知する。

ここまで４つの演習にて「習うより慣れよ」というアプローチで、Remotte を使ったアプリ開発について解説した。次回はこれまで習得した知識をプログラミング技術情報として整理してみることにする。