Arduinoとスペクトラムシールドで楽曲認識 #3

前回はArduino用スペクトラムシールドを使う際のsetup()関数について説明しました。今回はloop()関数においてライン入力を用いて音声を取得する部分のスケッチについて書いていこうと思います。

Front_Spectrum

スペクトラムシールドは画像の下の部分を見るとわかる通りイヤホンジャックを持っています。Input側にイヤホンジャックを挿して音声を流すことで、音声に含まれる周波数の成分(特定の帯域の周波数が含まれる頻度)を解析することができます。

今回の目的:イヤホンジャックから垂れ流しの音楽をリアルタイムで周波数解析して、そのデータをArduinoで取得する

スペクトラムシールドで解析した結果を取得する処理の流れは次のようにします。

  1. イヤフォンジャックからのステレオ音声をスペクトラムシールドでRight(右耳)とLeft(左耳)別々に解析
  2. 解析結果をArduinoで取得し、右耳から出る音声の解析結果をFrequencies_Right[7]へ、左耳から出る音声の解析結果をFrequencies_Left[7]へ一時的に格納
  3. 右耳の音声と左耳の音声の2つのデータを平均化し、擬似的にステレオ→モノラル変換を行いある瞬間の音の周波数解析を行う

このような感じで進めていきます。

それでは、解析した結果をArduinoに取り込むためのスケッチを書いていきましょう。

周波数のデータを格納する変数としてdouble型の3種類の変数を定義します。

//グローバル変数
double Frequencies_Left[7];
double Frequencies_Right[7];
double Frequencies_Sum[7];

 

今回使用するスペクトラムシールドは7つの帯域に周波数を分割できるので各配列の要素数は7になっています。

配列の要素番号と周波数の帯域の対応は次のようになっています。

  • Frequencies_***[0] → 63Hz
  • Frequencies_***[1] → 160Hz
  • Frequencies_***[2] → 400Hz
  • Frequencies_***[3] → 1kHz
  • Frequencies_***[4] → 2.5kHz
  • Frequencies_***[5] → 6.25kHz
  • Frequencies_***[6]  → 16kHz

(Frequencies_***[  ] の***はLeftとかRightとかSumとか…適当に補完してください)

次にsetup()関数です。

void setup(){
  for (int i = 0; i < 7; i++) {
    Frequencies_Right[i] = 0;
    Frequencies_Left[i] = 0;
    Frequencies_Sum[i] = 0;
  }
}

ここでは変数の初期化を行います。

次にloop()関数です。

void loop(){  
  //周波数解析
  Serial.println("scan begin");
  Read_Freq();
  Print_FreqS();
  for (int i = 0; i < 7; i++) {
    Frequencies_Right[i] = 0;
    Frequencies_Left[i] = 0;
    Frequencies_Sum[i] = 0;
  }
}


void Read_Freq() {
  //Read frequencies for each band
  for (int i = 0; i < 7; i++) {
    digitalWrite(STROBE, LOW);
    delayMicroseconds(72);//36
    Frequencies_Left[i] = analogRead(DC_Zero);
    Frequencies_Right[i] = analogRead(DC_One);
    Frequencies_Sum[i] = (Frequencies_Left[i] + Frequencies_Right[i]) / 2;
    digitalWrite(STROBE, HIGH);
    delayMicroseconds(72);//36
  }
}

void Print_FreqS() {
  for (int i = 0; i < 7; i++) {
    Serial.print("freq[");
    Serial.print(i);
    Serial.print("]:");
    Serial.print(Frequencies_Sum[i]);
    Serial.print("   ");
  }
  Serial.println();
}

音声の周波数解析結果を取得し、モノラル化を行う処理をRead_Freq()関数として実装しました。

DC_Zero(A0ピン)からは左耳の音の解析結果が、DC_One(A1ピン)からは右耳の音の解析結果が出力されるのでanalogRead()関数で読み込み、一時保管場所であるFrequencies_LeftとFrequencies_Rightに格納します。

Frequencies_Left[i] = analogRead(DC_Zero);
Frequencies_Right[i] = analogRead(DC_One);

ステレオ音声のまま使いたい人はこの後のモノラル化の処理をせず、このFrequencies_LeftとFrequencies_Rightのデータを使うといいでしょう。

ステレオ音声だとデータ量や処理が複雑になるため、今回は今後の処理を簡略化するためにモノラル音声に変換します。

ステレオ→モノラル変換は単純に両耳の音の平均を取るだけです。

Frequencies_Sum[i] += (Frequencies_Left[i] + Frequencies_Right[i]) / 2;

 

ちなみにこの処理の始まりには次のようなコードがあります。

digitalWrite(STROBE, LOW);
delayMicroseconds(72);//36

また、終わりの部分には次のようなコードがあります。

digitalWrite(STROBE, HIGH);
delayMicroseconds(72);//36

これらのコードは音声処理用のICチップに「出力する解析結果を違う帯域のものに切り替えてください」という指示をするものなので忘れないでください。

Print_FreqS()関数は処理結果をシリアルモニターに出力するものです。デバッグ用にお使いください。

Read_Freq()関数で取得した解析結果のデータはグローバル関数なのでどこからでもアクセスできます。あとは煮るなり焼くなり自由です。

Frequencies_LeftとFrequencies_Rightはステレオ音声の左耳の音声と右耳の音声の解析結果、Frequencies_Sumはステレオ音声の解析結果を擬似的にモノラルにしたものです。

実装部分はよくわからなくてもこれらのデータの意味が分かれば、このモジュールを使って自分の作品につなげていくことができます。

ここで前回のスケッチとドッキングして実際に動作するスケッチにしてみましょう。

//スペクトルアナライザ制御用のピン
#define STROBE 4
#define RESET 5
#define DC_Zero A0
#define DC_One A1

//グローバル変数
double Frequencies_Left[7];
double Frequencies_Right[7];
double Frequencies_Sum[7];

/********************Setup Loop*************************/
void setup() {
  Serial.begin(9600);

  for (int i = 0; i < 7; i++) {
    Frequencies_Right[i] = 0;
    Frequencies_Left[i] = 0;
    Frequencies_Sum[i] = 0;
 }

  pinMode(STROBE, OUTPUT);
  pinMode(RESET, OUTPUT);
  pinMode(DC_Zero, INPUT);
  pinMode(DC_One, INPUT);

  //スペクトラムシールドの初期化
  digitalWrite(STROBE, LOW);
  digitalWrite(RESET, LOW);
  digitalWrite(RESET, HIGH);
  digitalWrite(STROBE, HIGH);
  delayMicroseconds(36);//18
  digitalWrite(RESET, HIGH);
  digitalWrite(STROBE, LOW);
  delayMicroseconds(36);//18
  digitalWrite(RESET, LOW);
  digitalWrite(STROBE, HIGH);
  delayMicroseconds(144);//72
}

void loop(){  
  //周波数解析
  Serial.println("scan begin");
  Read_Freq();
  Print_FreqS();
  for (int i = 0; i < 7; i++) {
    Frequencies_Right[i] = 0;
    Frequencies_Left[i] = 0;
    Frequencies_Sum[i] = 0;
  }
}


void Read_Freq() {
  //Read frequencies for each band
  for (int i = 0; i < 7; i++) {
    digitalWrite(STROBE, LOW);
    delayMicroseconds(72);//36
    Frequencies_Left[i] = analogRead(DC_Zero);
    Frequencies_Right[i] = analogRead(DC_One);
    Frequencies_Sum[i] = (Frequencies_Left[i] + Frequencies_Right[i]) / 2;
    digitalWrite(STROBE, HIGH);
    delayMicroseconds(72);//36
  }
}

void Print_FreqS() {
  for (int i = 0; i < 7; i++) {
    Serial.print("freq[");
    Serial.print(i);
    Serial.print("]:");
    Serial.print(Frequencies_Sum[i]);
    Serial.print("   ");
  }
  Serial.println();
}

今回のスケッチは垂れ流しの音声をただただ周波数解析しその結果を返すというものでしたが、次回は音声のスキャンにスキャン時間を設け、データの統計をとる部分の実装を考えていきたいと思います。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to top