$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);
}
この記事が気に入ったらサポートをしてみませんか?