驴友花雕 发表于 2025-6-21 18:43:43

【Arduino 动手做】32(线性)/11(伪对数)频谱分析仪

该项目基于 Shajeeb 的项目,使用 Arduino 制作音频(音乐)频谱分析仪/可视化器。

这是我第一次尝试搭建Arduino项目。我对任何与音频项目相关的内容都很感兴趣。我找到了Shajeeb的音频可视化工具,尝试让显示屏上的刻度有所不同。有点像对数函数,但我只是在Excel里创建了一些(对数/指数)表格,然后在项目代码中手动实现了数据映射。

添加/修改更改显示的按钮:单条(线性刻度显示/双条-伪对数)。

希望你喜欢它,享受构建。







驴友花雕 发表于 2025-6-21 18:45:54

【Arduino 动手做】32(线性)/11(伪对数)波段音频频...

项目代码

// Modified code by Christian Suryanto, from (c) 2019 Shajeeb TM
// HAZI TECH
// Updated by Christian Suryanto
//

#include <arduinoFFT.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <EEPROM.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW                     // Set display typeso thatMD_MAX72xx library treets it properly
#define CLK_PIN   13                                          // Clock pin to communicate with display
#define DATA_PIN11                                          // Data pin to communicate with display
#define CS_PIN    10                                          // Control pin to communicate with display

#define SAMPLES 64                                           // Must be a power of 2
#define MAX_DEVICES4                                        // Total number display modules
#definexres 32                                              // Total number ofcolumns in the display, must be <= SAMPLES/2
#defineyres 8                                             // Total number ofrows in the display

#define PREV 0xFF02FD // address is FFA25D but 0x is added because this is how the arduino is told that it is HEXADECIMAL.
#define NEXT 0xFFC23D // control stop code
#define PWR 0xFFA25D // control Power

int audio_response = 35;                                    // put a value between 10 and 80. Smaller the number, higher the audio response

double vReal;
//double vReal2;
double vImag;
char data_avgs;

int yvalue;
int displaycolumn , displayvalue;
int peaks;
const int buttonPin = 6;                                    // the number of the pushbutton pin
int state = HIGH;                                             // the current reading from the input pin
int previousState = LOW;                                    // the previous reading from the input pin
int displaymode;
unsigned long lastDebounceTime = 0;                           // the last time the output pin was toggled
unsigned long debounceDelay = 50;                           // the debounce time; increase if the output flickers

int MY_ARRAY[]={0, 128, 192, 224, 240, 248, 252, 254, 255};   // default = standard pattern
//int MY_MODE_1[]={0, 128, 192, 224, 240, 248, 252, 254, 255};// standard pattern
//int MY_MODE_2[]={0, 128, 64, 32, 16, 8, 4, 2, 1};             // only peak pattern
//int MY_MODE_3[]={0, 128, 192, 160, 144, 136, 132, 130, 129};// only peak +bottom point
//int MY_MODE_4[]={0, 128, 192, 160, 208, 232, 244, 250, 253};// one gap in the top , 3rd light onwards

bool EQ_ON = true; // set to false to disable eq

byte eq1 = {40, 45, 50, 60, 65, 70, 75, 95,
               110, 110, 110, 110, 110, 110, 110, 110,
               130, 130, 130, 130, 130, 130, 130, 130,
               145, 155, 170, 180, 215, 220, 245, 255
            };


byte eq2 = {40, 70, 75, 110, 110, 140, 145, 220, 220, 230, 250};


MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);   // display object
arduinoFFT FFT = arduinoFFT();                                    // FFT object


void setup() {
    EEPROM.update(1,1);                                           //(memory address, value), RUN THIS FOR THE FIRST TIME
    displaymode = EEPROM.read(1);
    //displaymode = 1;
    ADCSRA = 0b11100101;                                          // set ADC to free running mode and set pre-scalar to 32 (0xe5)
    ADMUX = 0b00000000;                                           // use pin A0 and external voltage reference
    pinMode(buttonPin, INPUT);
    mx.begin();                                                   // initialize display
    mx.control(MD_MAX72XX::INTENSITY, 0);                         // set LED intensity
    delay(50);                                                    // wait to get reference voltage stabilized
}

void loop() {
   // ++ Sampling

   int numData;
   double rSum;

   for(int i=0; i<SAMPLES; i++)
    {
      while(!(ADCSRA & 0x10));                                    // wait for ADC to complete current conversion ie ADIF bit set
      ADCSRA = 0b11110101 ;                                       // clear ADIF bit so that ADC can do next operation (0xf5)
      int value = ADC - 512 ;                                     // Read from ADC and subtract DC offset caused value
      value = value / 8;
      vReal= value;                                          // Copy to bins after compressing
      vImag = 0;                        
    }
    // -- Sampling


   //++ FFT
    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    // -- FFT

    int step = (SAMPLES)/xres;

// re-mapping data - Customize by Christian Suryanto ///
    switch (displaymode)
    {
    case 1 :
      {
      numData = 32;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal)/2;
      }
      break;

    case 2 :
      {
      numData = 11;
      data_avgs = (vReal + vReal)/2;
      data_avgs = (vReal + vReal + vReal + vReal) / 4;
      data_avgs = ( vReal + vReal + vReal + vReal + vReal + vReal)/6;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal)/6;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal)/6;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal)/8;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal)/8;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal)/6;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal)/8;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal)/10;
      data_avgs = (vReal + vReal + vReal + vReal + vReal + vReal + vReal + vReal)/8;
      }
      break;
    }
// re-mapping data - Customize by Christian Suryanto ///

    for(int i=0; i<numData; i++)
    {
      data_avgs = data_avgs / 2;
      if (EQ_ON)
      switch (displaymode)
      {
          case 1 : data_avgs = (data_avgs) * (float)(eq1) / 100; //apply eq filter
          break;
         
          case 2 : data_avgs = (data_avgs) * (float)(eq2) / 100; //apply eq filter
          break;
      }

      data_avgs = constrain(data_avgs,0,audio_response);            // set max & min values for buckets
      data_avgs = map(data_avgs, 0, audio_response, 0, yres);         // remap averaged values to yres
      
      yvalue=data_avgs;

      peaks = peaks-1;    // decay by one light
      if (yvalue > peaks)
          peaks = yvalue ;
      yvalue = peaks;   
      displayvalue=MY_ARRAY;
      
      switch (displaymode)
      {
      case 1:
      {
          displaycolumn=31-i;
          mx.setColumn(displaycolumn, displayvalue);                // for left to right
      }
      break;
      case 2:
      {
          displaycolumn=31-(3*i);
          mx.setColumn(displaycolumn-1, displayvalue);                // for left to right
          mx.setColumn(displaycolumn, displayvalue);                // for left to right
      }
      break;
      }
   }
   // -- send to display according measured value
   
    displayModeChange ();                                       // check if button pressed to change display mode
}

void displayModeChange() {
int reading = digitalRead(buttonPin);
if (reading == HIGH && previousState == LOW && millis() - lastDebounceTime > debounceDelay) // works only when pressed
{
    switch (displaymode)
   {
      case 1:    //       move from mode 1 to 2
      displaymode = 2;
      mx.clear();
      delay(200);
      EEPROM.update(1,2);
      break;
      case 2:    //       move from mode 2 to 3
      displaymode = 1;
      mx.clear();
      delay(200);
      EEPROM.update(1,1);
      break;
    }
    lastDebounceTime = millis();
}
previousState = reading;
}


驴友花雕 发表于 2025-6-21 18:49:12

【Arduino 动手做】32(线性)/11(伪对数)波段音频频...

【Arduino 动手做】32(线性)/11(伪对数)波段音频频谱分析仪
项目链接:https://www.hackster.io/chrissurya/32-linier-11-pseudo-log-band-audio-spectrum-analyzer-8e8c7b
项目作者:克里苏里亚

项目代码:https://www.hackster.io/code_files/559275/download



页: [1]
查看完整版本: 【Arduino 动手做】32(线性)/11(伪对数)频谱分析仪