偶然心血来潮,想做一系列声音可视化的话题。这个话题有点难,涉及面广,相关FFT和FHT算法也很复杂,但我仍然计划从最简单的开始,实际做实验,耐心尝试各种解决方案,逐渐积累一些有用的音乐频谱可视化数据,并努力形成一些实用和有趣的音乐可视器项目。
找到的报废灯管,准备用,做一米二的快节奏灯
两个头都锯掉了,把里面拿出来LED灯片
找到两个花盆底盘,当灯管座
采用多级扩孔钻头
灯管座位3秒扩孔288mm
合宙用于开发板CORE ESP32-C3
主要特色 (1)功能强,应用广泛 合宙ESP32-C3开发板高集成设计,板载设计 Wi-Fi/BLE天线、4MB Flash,支持UART、GPIO、SPI、I2C、ADC、PWM等接口,满足大多数应用;板载CH343P芯片实现更高的速率USB固件下载更快更稳定。 (2)材料充足,信号好 合宙ESP32-C4层开发板PCB,板载2.4G调试后,天线达到了较好的使用效果。.38到2.48GHz驻波比在范围内(VSWR)值较好,能有效地将发射功率辐射到无线空间。 (3)多元化发展更方便 合宙ESP32-C目前支持3开发板:LuatOS/乐鑫IDF/Arduino/MicroPython等多样化开发方式,使用更便捷。LuatOS在社区大神梦的努力下,固件支持以下功能,其他内容不断更新。
ESP32-C3核心板 IO引脚图
1路SPI FLASH,板载4MB,支持最高 16MB 2路UART接口,UART0~UART1.下载口为UART0 6 路 12 比特 ADC,最高采样率 100KSPS 1路低速SPI支持主模式的接口 1路IIC控制器 4路PWM接口 GPIO外管脚15路,可复用 2路贴片LED指示灯 1路复位按键 1路BOOT按键 1路USB转TTL下载调试口 2.4G PCB板载天线
使用声音模块MAX9814
MAX9814是一种自动增益控制的低成本高性能麦克风放大器(AGC)偏离低噪声麦克风。该装置具有低噪声前端放大器和可变增益放大器(VGA)、输出放大器、麦克风偏置电压发生器和AGC控制电路。 ●自动增益控制(AGC) ●三种增益设置(40)dB、50dB、60dB) ●可编程动作时间 ●可编程动作和释放时间比 ●电源电压范围2.7V~5.5V ●低THD:0.04% (典型值) ●低功耗关闭模式 ●内置2V低噪声麦克风偏置
彩灯使用WS2812B
其主要特点 智能反接保护,电源反接不会损坏IC。 IC控制电路与LED点光源公用一个电源。 控制电路与RGB芯片集成在5050包装元件中,形成完整的外部控制像素点。 内置信号整形电路,任何像素点收到信号后通过波形整形输出,确保线路波形畸变不会累积。 内置上电复位和下电复位电路。 每个像素点的三基色可显示256亮度,1677216种颜色的全真色可显示,扫描频率不低于400Hz/s。 串行级联接口可以通过信号线接收和解码数据。 当传输距离不超过5米时,不需要增加任何电路。 当刷新速率30帧/秒时,级联数不小于1024点。 数据发送速度可达8000Kbps。 光的颜色高度一致,性价比高。
主要应用领域 LED全彩发光字灯串,LED全彩模组, LED全彩软灯条硬灯条,LED护栏管。 LED点光源,LED像素屏,LED异形屏幕,各种电子产品,电气设备跑马灯。
WS2812B灯带选用每米60灯黑底裸板
WS2812模块电原理图
WS2812B集控制电路和发光电路于一体LED控制光源元件IC为WS2812B,发光元件是5050RGBLED,电压为5V,各单位峰值电流为60ma,灯带为三线制,VCC GND DIN分别为电源 、当使用外部电源时,外部电源-需要单片机GND相连。
把灯带放进灯管里(这里我剪了70颗灯珠)
灯管底座配重后采用大理石方案,零成本,性价比高
项目使用Arduino IDE烧录程序
开发板的详细配置如图所示
安装相关驱动库
有趣有趣的音乐可视化(12)–米管快节奏灯 相关程序:MegunoLink音乐反应式LED灯带 模块接线:WS2812B接D9 MAX9814 ESP32_C3 VCC 5V GND GND OUT D4(ADC4)
/* 有趣有趣的音乐可视化(12)-米管快节奏灯 相关程序:MegunoLink音乐反应式LED灯带 模块接线:WS2812B接D9 MAX9814 ESP32_C3 VCC 5V GND GND OUT D4(ADC4) */ #include<FastLED.h> #include<MegunoLink.h> #include<Filter.h> #define N_PIXELS 70 #define MIC_PIN 4 #define LED_PIN 9 #define NOISE 150 #define TOP (N_PIXELS+2)
#define LED_TYPE WS2811
#define BRIGHTNESS 18
#define COLOR_ORDER GRB
CRGB leds[N_PIXELS];
int lvl = 0, minLvl = 0, maxLvl = 100;
ExponentialFilter<long> ADCFilter(5,0);
void setup() {
Serial.begin(115200);
FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds,N_PIXELS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
}
void loop() {
int n, height;
n = analogRead(MIC_PIN);
n = abs(1023 - n);
n = (n <= NOISE) ? 0 : abs(n - NOISE);
ADCFilter.Filter(n);
lvl = ADCFilter.Current();
height = TOP * (lvl - minLvl) / (long)(maxLvl - minLvl);
if(height < 0L) height = 0;
else if(height > TOP) height = TOP;
for(uint8_t i = 0; i < N_PIXELS; i++) {
if(i >= height) leds[i] = CRGB(0,0,0);
// otherwise, turn them on!
else leds[i] = Wheel( map( i, 0, N_PIXELS-1, 30, 150 ) );
}
FastLED.show();
}
CRGB Wheel(byte WheelPos) {
if(WheelPos < 85)
return CRGB(WheelPos * 3, 255 - WheelPos * 3, 0);
else if(WheelPos < 170) {
WheelPos -= 85;
return CRGB(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return CRGB(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
实验场景图
实验场景图 动态图
https://img.mydigit.cn/forum/202207/24/155142ahnctgez2npccenr.gif
实验的视频记录(4分57秒)
https://v.youku.com/v_show/id_XNTg4ODE1MTAwOA==.html?spm=a2hcb.playlsit.page.1
实验场景图
实验场景图 动态图
https://img.mydigit.cn/forum/202207/25/092209rcucyzgrntvcz5cy.gif
实验的视频记录
https://v.youku.com/v_show/id_XNTg4ODE4NTY0MA==.html?spm=a2hcb.playlsit.page.1
实验的视频记录2
https://v.youku.com/v_show/id_XNTg4OTMxMTUyOA==.html?spm=a2hcb.playlsit.page.1
实验的视频记录3
https://v.youku.com/v_show/id_XNTg4OTMxMTk3Mg==.html?spm=a2hcb.playlsit.page.1
实验的视频记录4
https://v.youku.com/v_show/id_XNTg4OTMxMTcxMg==.html?spm=a2hcb.playlsit.page.5
【花雕动手做】有趣好玩音乐可视化(12)–米管快速节奏灯 相关程序之二:SoftwareSerial米管音乐频谱灯 模块接线:WS2812B接D6 MAX9814 UNO VCC 5V GND GND OUT A0
/* 【花雕动手做】有趣好玩音乐可视化(12)--米管快速节奏灯 相关程序之二:SoftwareSerial米管音乐频谱灯 模块接线:WS2812B接D6 MAX9814 UNO VCC 5V GND GND OUT A0 */
#include <Adafruit_NeoPixel.h>
#include <FastLED.h>
#include <math.h>
#include <SoftwareSerial.h>
#define N_PIXELS 70
#define N_PIXELS_HALF (N_PIXELS/2)
#define MIC_PIN A0
#define LED_PIN 6
#define SAMPLE_WINDOW 10
#define PEAK_HANG 24
#define PEAK_FALL 20
#define PEAK_FALL2 8
#define INPUT_FLOOR 10
#define INPUT_CEILING 300
#define DC_OFFSET 0
#define NOISE 10
#define SAMPLES 60
#define TOP (N_PIXELS + 2)
#define SPEED .20
#define TOP2 (N_PIXELS + 1)
#define LAST_PIXEL_OFFSET N_PIXELS-1
#define PEAK_FALL_MILLIS 10
#define POT_PIN 4
#define BG 0
#define LAST_PIXEL_OFFSET N_PIXELS-1
#if FASTLED_VERSION < 3001000
#error "Requires FastLED 3.1 or later; check github for latest code."
#endif
#define BRIGHTNESS 255
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define COLOR_MIN 0
#define COLOR_MAX 255
#define DRAW_MAX 100
#define SEGMENTS 4
#define COLOR_WAIT_CYCLES 10
#define qsubd(x, b) ((x>b)?b:0)
#define qsuba(x, b) ((x>b)?x-b:0)
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
struct CRGB leds[N_PIXELS];
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
static uint16_t dist;
uint16_t scale = 30;
uint8_t maxChanges = 48;
CRGBPalette16 currentPalette(OceanColors_p);
CRGBPalette16 targetPalette(CloudColors_p);
uint8_t timeval = 20;
uint16_t loops = 0;
bool samplepeak = 0;
uint16_t oldsample = 0;
bool thisdir = 0;
enum
{
} MODE;
bool reverse = true;
int BRIGHTNESS_MAX = 80;
int brightness = 20;
byte
// peak = 0,
// dotCount = 0,
volCount = 0;
int
reading,
vol[SAMPLES],
lvl = 10,
minLvlAvg = 0,
maxLvlAvg = 512;
float
greenOffset = 30,
blueOffset = 150;
int CYCLE_MIN_MILLIS = 2;
int CYCLE_MAX_MILLIS = 1000;
int cycleMillis = 20;
bool paused = false;
long lastTime = 0;
bool boring = true;
bool gReverseDirection = false;
int myhue = 0;
uint8_t colour;
uint8_t myfade = 255;
#define maxsteps 16
int peakspersec = 0;
int peakcount = 0;
uint8_t bgcol = 0;
int thisdelay = 20;
uint8_t max_bright = 255;
unsigned int sample;
#define NSAMPLES 64
unsigned int samplearray[NSAMPLES];
unsigned long samplesum = 0;
unsigned int sampleavg = 0;
int samplecount = 0;
//unsigned int sample = 0;
unsigned long oldtime = 0;
unsigned long newtime = 0;
int color;
int center = 0;
int step = -1;
int maxSteps = 16;
float fadeRate = 0.80;
int diff;
int
origin = 0,
color_wait_count = 0,
scroll_color = COLOR_MIN,
last_intensity = 0,
intensity_max = 0,
origin_at_flip = 0;
uint32_t
draw[DRAW_MAX];
boolean
growing = false,
fall_from_left = true;
uint32_t currentBg = random(256);
uint32_t nextBg = currentBg;
TBlendType currentBlending;
const int buttonPin = 0;
int buttonPushCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
byte peak = 16;
byte dotCount = 0;
byte dotHangCount = 0;
void setup() {
analogReference(EXTERNAL);
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH);
// Serial.begin(9600);
strip.begin();
strip.show(); // all pixels to 'off'
Serial.begin(57600);
delay(3000);
LEDS.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, N_PIXELS).setCorrection(TypicalLEDStrip);
LEDS.setBrightness(BRIGHTNESS);
dist = random16(12345);
}
float fscale( float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve) {
float OriginalRange = 0;
float NewRange = 0;
float zeroRefCurVal = 0;
float normalizedCurVal = 0;
float rangedValue = 0;
boolean invFlag = 0;
if (curve > 10) curve = 10;
if (curve < -10) curve = -10;
curve = (curve * -.1) ;
curve = pow(10, curve);
if (inputValue < originalMin) {
inputValue = originalMin;
}
if (inputValue > originalMax) {
inputValue = originalMax;
}
OriginalRange = originalMax - originalMin;
if (newEnd > newBegin) {
NewRange = newEnd - newBegin;
}
else
{
NewRange = newBegin - newEnd;
invFlag = 1;
}
zeroRefCurVal = inputValue - originalMin;
normalizedCurVal = zeroRefCurVal / OriginalRange;
if (originalMin > originalMax ) {
return 0;
}
if (invFlag == 0) {
rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin;
}
else
{
rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange);
}
return rangedValue;
}
void loop() {
uint8_t i;
uint16_t minLvl, maxLvl;
int n, height;
buttonState = digitalRead(buttonPin);
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
buttonPushCounter++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
if (buttonPushCounter == 16) {
buttonPushCounter = 1;
}
}
else {
Serial.println("off");
}
}
lastButtonState = buttonState;
switch (buttonPushCounter) {
case 1:
buttonPushCounter == 1; {
All2();
break;
}
case 2:
buttonPushCounter == 2; {
vu();
break;
}
case 3:
buttonPushCounter == 3; {
vu1();
break;
}
case 4:
buttonPushCounter == 4; {
vu2();
break;
}
case 5:
buttonPushCounter == 5; {
Vu3();
break;
}
case 6:
buttonPushCounter == 6; {
Vu4();
break;
}
case 7:
buttonPushCounter == 7; {
Vu5();
break;
}
case 8:
buttonPushCounter == 8; {
Vu6();
break;
}
case 9:
buttonPushCounter == 9; {
vu7();
break;
}
case 10:
buttonPushCounter == 10; {
vu8();
break;
}
case 11:
buttonPushCounter == 11; {
vu9();
break;
}
case 12:
buttonPushCounter == 12; {
vu10();
bre