[Remotte開発] 音声解析系のアプリ(演習4)


前回 の演習3では、「ビデオ解析」系の構成要素について演習した。今回は音声解析系の構成要素について、「レベルメーター」を題材に説明する。音声解析系の構成要素は、ビデオ解析系とほぼ同じで、ビデオ解析では関数 new_video_frame( ) によって解析すべきデータがプラットフォームから与えられたのに対して、音声解析では関数 new_audio_frame( ) によって与えられることが違いだ。

アプリの新規作成

これまでの演習と同様に、アプリを新規作成する。

音声のソースを追加

「構成」画面の左下のメニューから、「メディア」を選択し、音声入力として使用する要素を追加する。

音声解析を追加

音声のソースを追加できたら、今度は音声解析の構成要素を追加する。「入出力タイプ」として、
  音声解析(値出力のみ)
  音声解析(音声出力のみ)
  音声解析(値と音声の両方を出力)
の3つがあるが、このレベルメーターの演習では、その解析後の出力として入力されたステレオ音声のレベル(フレーム内の振幅の最大値)を求めるだけであり、例えば音声そのものをオクターブ上下したり、雑音を取り除いた音声を出力するわけではないため、「音声出力(値出力のみ)」を選択する。また、解析結果として左右の最大レベルを出力するため、「互換タイプ」として「一般的な2値センス」を選択する。

次に「コード」画面で、Python コードを記述する。音声解析では、関数
  new_audio_frame(self, audio_frame, time_sec)
によって、解析すべき音声データが Python 側に通知され、この関数内で解析処理を行い、その結果をプラットフォーム側に通知する。ここで引数 audio_frame には音声データが numpy.ndarray 型で格納され、time_sec にはアプリ開始からの経過時間(秒)が float 型で格納される。音声データは16ビット、2チャンネル、サンプリングレートは48kHzで、関数 new_audio_frame( ) は20ミリ秒毎に呼ばれる。言い換えると、この関数は1秒間に50回呼び出され、audio_frame にはステレオの振幅値がチャンネル毎に960個格納されている。参考までに、関数 new_audio_frame( ) の先頭に、
  print(type(audio_frame), len(audio_frame), type(audio_frame[0]))
と記述してアプリを実行した場合、以下がコンソール出力される。
  <class 'numpy.ndarray'> 1920 <class 'numpy.int16'>
同様に、関数の先頭に、
  print(time_sec)
と記述して実行した場合、各秒毎に50件の時間情報が出力される。

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

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

    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


0.1秒毎に、左右それぞれのレベルの最大値を0から100までの範囲にスケーリングして出力する。

利用ページの編集

「表示項目」画面では、以下の様に設定する。

ここで注意したいのは、「レベルメーター」の最新値として、「表示数」を4つに設定してある。
次に、「レイアウト」画面を選択する。以下の様に、デフォルトでは、4つの表示項目共に「2つの数値表示」という表現形式が設定されている。

上記の Python コードから分かるように、解析結果として
  [左の最大値, 右の最大値]
が出力されるわけだが、この左の値と右の値を取り出す方法が用意されている。画面右側のオプション設定の中に、「高度な設定」というグループがある。

この中の「配列から取り出し」というオプションに整数値を設定することで、配列データの中から任意の位置の要素を取り出すことが出来る。例えばこのアプリの例では、「0」と入力すれば、キー名が「value」の値であって、それが配列だった場合に、インデックスが「0」の要素を取得でき、つまりは「左の最大値」を取得できる。同様に「1」を設定すれば、「右の最大値」を取得できる。このオプションを利用して、以下の様なレイアウトを作成してみよう。つまり、左のレベルと右のレベルにそれぞれ2つずつの表示項目を使い、1つには表現形式「1つの数値の表示」を使って0から100までの値を表示し、もう1つには「段階メーター(水平、領域色付き)」を使って、グラフィック表示する。

実行!

アプリを実行し、マイクに向かって大きな声を出すと、レベルが大きく振れる。

まとめ

本演習では、「音声解析」系の構成要素について体験した。関数 new_audio_frame( ) によってプラットフォームから音声データが与えられ、これを解析することを習得したい。この演習では使わなかったが、解析結果として、入力された音声と異なる音声を出力する場合は、set_audio_frame( ) 関数によって音声データをプラットフォーム側に通知する。

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