見出し画像

$9 Spectrum analyzer - DIY Eurorack Modular Synthesizer

背景

自作モジュラーシンセの80作品目。
モジュラーシンセの魅力の一つは見た目だ。特に、暗闇で光る演出を効果的に使っているプレイヤーも多い。richard devineの動画は、眺めているだけでも楽しくなる。
モジュラーシステムの見た目が楽しくなるモジュールを作りたいと思い、スペクトルアナライザーの企画をした。
また、ChatGPTに課金したので、プログラムはChatGPTを活用している。

スペクトルアナライザー

音声信号の周波数スペクトルを表示するための装置。音楽の再生機器にはこの機能を有する装置も多いが、意外にもモジュラーシンセでスペクトルアナライザーの機能を有するものは無いようだ。(私が探したりないだけかもしれない)
私もかつて、簡易的なオシロスコープモジュールを作成した際に、スペクトルアナライザーの機能を織り込んだが、あくまでオマケ的な機能で満足のいく出来栄えではなかった。

制作物のスペック

ユーロラック規格 3U 8HPサイズ
電源:35mA( +12V ),5mA( -12V ),1mA( +5V )

LED matrixのスペクトルアナライザー。
バンド数は7、レベル分解能は8。オーディオレートでの使用を想定している。

LED matrix:7band、分解能8。一番右の列のLEDは未使用で点灯しない。
ATTN POT:スペクトルアナライザーへの入力信号の減衰。レベルが過大だと、正しくスペクトルが表示できないため。
INPUT:波形入力。maxで10Vp-pの入力を想定している。
OUTPUT:入力波形をオペアンプのバッファを介してアウトプットする。

製作費

総額約$9
---------------------------------
MSGEQ7 $2.5
LED matrix OSL641501-AY $1
フロントパネル $1.5
TL072 $0.3
Arduino nano $2
他(汎用部品は下記リンク先参照)

MSGEQ7はスペクトルアナライザー専用IC。入力波形を7bandのスペクトルに分解し、アナログ信号で出力する便利なIC。日本では秋月電子で320円で購入できたが、現在は販売終了している。

ハードウェア

回路全体は以下の通り。

オペアンプ回路

左上。入力信号をスペクトルアナライザー用と、OUTPUT用に分岐している。

MSGEQ7回路

データシートに記載してある標準的な回路を使用。過電圧保護のため、ショットキーバリアダイオードを使用している。

Arduino & LED matrix回路

下部。LEDの制限抵抗は、使用するLED matrixの電力使用効率次第でチューニングする。スイッチングノイズを低減するために、可能な限り大きな抵抗値が好ましいが、抵抗値が大きすぎると光も弱くなるので注意が必要。

電源回路

最も工夫した点。Arduinoは、MSGEQ7への通信、及びLED matrixの点灯で、常に高速スイッチングをし続けている。このスイッチングノイズがArduino 5V電源電圧を揺らし、MSGEQ7の入力信号のノイズとなり誤表示を起こし、アウトプット音声への音声ノイズの原因になっていた。

2つのノイズ対策をしている。
1つ目は電源の分離。スイッチングノイズとなるArduinoは12V電源を使用。MSGEQ7は5V電源を使用することで、スイッチングノイズがICに回り込まないようにしている。
2つ目は電解コンデンサの追加。スイッチングノイズの大きいArduino 5V電源には47uFの電解コンデンサを追加している。

ソフトウェア

今回のソフトは、ChatGPT GPT-4oを活用している。
全60行のソースコードのうち、私が書いたのは6行だけだ。

いきなり複雑な命令をするとデバッグが難しいので、ステップバイステップでプログラムを追加していった。

Step1:LED matrixの点灯

まずは、LED matrixを点灯させるプログラムを生成した。
プロンプトは以下の通り。

Arduino nanoを使ってLEDマトリクスを光らせたいです。
・LEDマトリクスは外部ICを使わず、arduinoから直接光らせます
・100msecごとに、光らせるLEDを切り替える。
・全てのLEDが、順番に光っていっく。
・最後のLEDが点灯したら、最初に戻って動作を繰り返す

出力結果は以下の通り。正しく動作した。

Step2:オーディオレベルメータ

次にアナログ入力を正しく扱えるか確認するため、オーディオレベルメータを作成した。プロンプトは以下。

うまく動作しました。
プログラムを小修整して、別の動作をさせたいです。
・アナログピンA4から電圧を取得します。
・取得した電圧をレベルメータとしてLEDマトリクスに反映させます。
・レベルは行で表します。
 つまり、1~8列目はすべて同じ点灯となります。

出力結果は以下の通り。正しく動作した。

Step3:MSGEQ7を使用したスペクトルアナライザー

最終目的であるMSGEQ7を使ったスペクトルアナライザーの生成をする。プロンプトは以下の通り。

うまくいきました。
このプログラムを修正して、次の要望を満たしてください。
・レベルメーターの機能は削除します。
・代わりに、MSGEQ7からスペクトルを取得してLEDマトリクスに表示します。
・MSGEQ7からの電圧信号は、A7pinで受信します。
・1バンドあたり、1列でレベルを表示してください。
・LEDマトリクスは8列あるので、余った1列は何も表示しません。
・MSGEQ7のストロボはD18pinで駆動、リセットはD19pinを使って駆動してください

この結果は失敗。MSGEQ7へのストロボ入力とアナログ入力のタイミングに不整合があり、正しく表示ができなかった。

Step4:デバッグ&微修正

上記のStep3の対策として、タイミングの修正を指示した。
データシートのタイミングチャートの画像をChatGPTに読み込ませ、制御タイミングの修正を指示した。

やはり、MSGEQ7の制御タイミングが良くないようです。
添付の画像はデータシートのタイミングチャートです。
このタイミングチャートを守るように動作を修正してください。
delayではなく、タイマーを使うと良いです

今回のソースコードの最終系に近いプログラムが生成された。
(delayは使うなと指示したが、delayを使った様だ。そこは残念)

Step5:手作業による修正

LED matrixの点灯時間は、人間の視覚的な感性に影響する要素なので、良く光るように点灯時間と、電圧レベルのチューニングは手動でプログラミングした。
下記のコードは手動にて書き換えている。

      delayMicroseconds(500); // Short delay to stabilize the reset signal
      clearMatrix();
    int level = map(spectrumValues[col], 0, 1023, 0, 9); // Linearly map the value
    if(spectrumValues[col]==9){
      spectrumValues[col]=8;
    }

これでプログラムは完成。

ソースコード

先述の通り、ソースはChatGPTが書いている。正常に動作はしているが、コード的に正しいかは検証は出来ていない。特にMSGEQ7当たりのコードは、動作はしているが正しいかは不明だ。(つまり、データシートの要求を満たしていない可能性がある)

int ROWS[8] = {2, 3, 4, 5, 6, 7, 8, 9}; // Row pin numbers
int COLS[8] = {10, 11, 12, 13, A0, A1, A2, A3}; // Column pin numbers
int analogPin = A7; // Analog pin to receive MSGEQ7 voltage signal
int strobePin = 18; // MSGEQ7 strobe pin
int resetPin = 19; // MSGEQ7 reset pin
int spectrumValues[7]; // Array to store 7-band spectrum values

void setup() {
  for (int i = 0; i < 8; i++) {
    pinMode(ROWS[i], OUTPUT);
    pinMode(COLS[i], OUTPUT);
  }
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
  digitalWrite(strobePin, HIGH);
  digitalWrite(resetPin, LOW);
  clearMatrix();
}

void loop() {
  readMSGEQ7(); // Read spectrum from MSGEQ7
  for (int col = 0; col < 7; col++) {
    int level = map(spectrumValues[col], 0, 1023, 0, 9); // Linearly map the value
    if(spectrumValues[col]==9){
      spectrumValues[col]=8;
    }
    for (int row = 0; row < level; row++) {
      lightUpLED(row, col); // Light up LEDs according to the level
      delayMicroseconds(500); // Short delay to stabilize the reset signal
      clearMatrix();
    }
  }
}

void readMSGEQ7() {
  digitalWrite(resetPin, HIGH);
  delayMicroseconds(100); // Short delay to stabilize the reset signal
  digitalWrite(resetPin, LOW);
  delayMicroseconds(72); // Stability time after reset

  for (int i = 0; i < 7; i++) {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(18); // Strobe signal pulse width
    spectrumValues[i] = analogRead(analogPin); // Read spectrum value
    digitalWrite(strobePin, HIGH);
    delayMicroseconds(72); // Delay to stabilize signal before moving to the next band
  }
}

void clearMatrix() {
  for (int i = 0; i < 8; i++) {
    digitalWrite(ROWS[i], LOW);
    digitalWrite(COLS[i], HIGH);
  }
}

void lightUpLED(int row, int col) {
  digitalWrite(ROWS[row], HIGH);
  digitalWrite(COLS[col], LOW);
}


この記事が気に入ったらサポートをしてみませんか?