声と想いのColorful LED Spectrum Analyzer


音と感情に反応するSpectrum Analyzerを、LEDテープライト使ってピカピカと作った。

最初にモゴモゴ喋ってるのは自分の声です。

途中のアナウンス声は↓から使わせてもらいました。
https://soundeffect-lab.info/sound/voice/

音楽は以前作った自作です。

概要

仕事でLEDを扱う機会があり、楽しくなってきたので自分でも何かやってみたいと思って作りました。
Spectrum Analyzerだけだとあまり面白くないかと思って、声の感情でLEDの色が変わるようにしました。
こんな構成になってます。

声の感情認識 Vokaturi

顔の感情認識は結構色々ありますが、声の感情認識って何かないかと探してみたらありました。

■Vokaturi
https://vokaturi.com/

ローカル処理だけで音声から感情認識してくれるスグレモノで、以下の種類があります。
・有償でDeepLearningベースのVokaturiPlus・VokaturiPro
・無料でGPLライセンスだけど仕組みが違ってちょっと精度が落ちる(らしい)OpenVokaturi

今回はOpenVokaturiを利用。
OpenVokaturiはGPLライセンスで、PythonとCのソースコードと、Windows・Mac・Linux・Android・iOS環境のライブラリが配布されてます。
PythonとCのコードはどちらも試してみたけど、今回はopenFrameworksで動作させることにしたので最終的にCの方を使いました。
(後で気づいたけど、openFrameworksの場合、ofxVokaturiというアドオンありました)

OpenVokaturiの感情分類

OpenVokaturiだと以下の5つの感情認識ができます。
・neutrality
・happiness
・sadness
・anger
・fear
(VokaturiProだと感情区分を変える手段があるらしい)

OpenVokaturiを使ってみただけの範囲だと、正直言うと、感情認識の精度はあまりピンとこない感じでした。
(自分の声はほとんど neutrality か sadness になった。あと、値がかなり荒れるので一定数データ貯めて平均値を使った)

DeepLearningベースの有償版は試してないので、そっちはまたちょっと違うかもしれません。
(精度差は公式サイトに載ってるのでご参照を)

あと、感情認識させる時間範囲が設定できるので、それによっても結果が変わります。(処理時間も変わります)
精度として何が最適かよくわからなかったですが、今回はリアルタイム認識させたかったので、数秒程度の時間範囲でやってます。

音楽の感情認識

音楽に対してもOpenVokaturiを使ってみました。

とはいえ、面白い結果になるかどうか試したくてやっただけで、技術的な意味はありません。

Vokaturiは声の感情を認識するための仕組みなので、音楽の感情認識には非対応です。
ただ、声も音楽も音データであることには違いがないのでとりあえずデータ突っ込んでみたら、なんか認識しました。
あまり意味はないことを承知の上でなら、曲の感情解析と言い張ることもできるかもしれません。
(音楽は音楽用のムード解析的な手法があるっぽいので、ちゃんとやるならそちらで)

光るSpectrum Analyzer

↓のLEDテープライトを5個連結して使ってます。
https://www.switch-science.com/catalog/1400/

本職でハードやってる人に怒られそうですが、はんだ持ってないのと、別用途に使い回せるように簡単に着脱したかったので、ワニ口クリップで5個を連結しました。
(実際に接触不良でうまく動かないこともあったので、ちゃんとやる場合ははんだするべし)

LEDの発色は、OpenVokaturiで最も高く認識された感情の色にしてます。
感情の色は、独断と偏見で以下の色にしました。
・neutrality -> 緑
・happiness -> 黄
・sadness -> 青
・anger -> 赤
・fear -> 紫

LED制御

Arduinoで制御してます。
ただ、PCからの通信が1byteのメッセージだと安定して通信できたのですが、5本のLED発色数とカラー情報という複数byteのメッセージだとメッセージがロストして(というより壊れて?)安定動作しないことに悩まされ、送信を個別に行いました。

具体的には、識別用の情報と値をセットにして、一つずつデータ送信しました。
1メッセージは2byteになりますが、全部まとめて1回で投げるよりは少しマシでした。
コードを抜粋するとこんな感じ。

送信側のopenFrameworks(抜粋)
#ヘッダー情報(処理分岐用)を値をまとめたデータ構造を用意しておく
struct SerialData{
    char header;
    char value;
};

#こんな感じのシリアル通信にデータ投げる関数を用意しておく
void ofApp::sendData(SerialData data){
    #シリアル通信するために unsigned char にする
    unsigned char bytes[sizeof(data)];
    memcpy(bytes, &data, sizeof(data));

    #serialはofSerialのインスタンス
    serial.writeBytes(bytes, sizeof(bytes));
}

#こんな感じでデータを代入
SerialData serialData_colorR;
serialData_colorR.header='r';
serialData_colorR.value=126;

SerialData serialData_colorG;
serialData_colorR.header='g';
serialData_colorR.value=54;

#投げる
sendData(serialData_colorR);
sendData(serialData_colorG);

受信側のArduino(抜粋)
void loop() {
    #ヘッダー情報で処理分岐する。
    #2byte分データを貯めてから処理するので Serial.available()>1 になってる
    while(Serial.available()>1){
        char header=Serial.read();
        switch(header){
            case 'r':colorR=Serial.read();break;
            case 'g':colorG=Serial.read();break;
            case 'b':colorB=Serial.read();break;
            case '1':frequency1=Serial.read();break;
            case '2':frequency2=Serial.read();break;
            case '3':frequency3=Serial.read();break;
            case '4':frequency4=Serial.read();break;
            case '5':frequency5=Serial.read();break;
        }
    }
}

ただ、今回のような挙動をPCで逐次処理する場合は、通信のメッセージが複雑になるとArduinoを使った制御はやりにくいことを実感...。

振る舞いをArduino側で決めてしまって、どの動作パターンにするかだけをPCから決めて通信する仕組みの場合はArduino制御が向いてます(負荷分散にもなるはず)。

LEDの場合は、FadeCandyというNeoPixelのLEDをUSB経由でPCから直接制御できる製品がありますが、Arduinoを使わずにFadeCandyを使えばもっと柔軟な制御もできそうです。
買うかどうかは検討中...

まとめ

感情認識できてるかはよくわからなかったが、ピカピカと反応するだけでとりあえず楽しい。