序言:
这是以我为项目组长所做的一个训练项目,项目结题之后笔者决定拿出来跟诸位分享,希望对你对我都有所帮助

一.成果简单介绍:

(1)Reading-LED(作品)

(2)Listening-LED(作品)

(3)SmartLED(作品)

二.课题研究目的

目前人们的需求已经从简单的衣食住行扩展到了精神文化层面的要求。为了更好的享受音乐给我们带来的快乐,我们小组想到了通过创作音乐节奏LED智能台灯来实现音乐可视化的效果,该台灯能感知外界音乐节奏的律动,同时手机app端也可以调节灯的亮度和灯的颜色的变化,添加用超声波传感器或红外传感器通过判断人的去留来控制灯的开灭,准备依托于,Arduino Nano和声音传感器,超声波传感器,物联网技术等来实现,预期困难是单片机技术和 Arduino语言对于我们来说都是全新的,未接触过的,手机控制所用的app也是从未接触,物联网技术与我们更是全新的挑战。此智能灯包含了我组同学很多新颖的想法,通过手机app控制智能台灯将其纳入智能家居,加
入当下实行的物联网技术又是一个大胆的尝试。

三.课题背景:

目前人们的需求已经从简单的衣食住行层面扩展到了精神文化层面。用户要求更好的体验,科技的高速发展催生着新的产业,音乐可视化就是这众多方兴未艾产业中的一个。音乐可视化,是指一种以视觉为核心,以音乐为载体,以大众为诉求对象,借助多种新媒体技术等传播媒介,通过画面、影像来诠释音乐内容的、视听结合的大众化传播方式。它能为理解、分析和比较音乐艺术作品形态的表现力和内外部结构提供的一种直观视觉呈现的技术。音乐可视化的大背景,是视觉工业时代的到来。视觉工业是以生产视觉产品、提供视觉服务为主要内容的产业形态。,以"一切信息可视化"为发展目标,是一种战略性新兴文化产业。大力发展视觉工业,是文化产业升级的重要支点。
我们小组想到了通过创作音乐节奏 LED智能台灯来实现音乐可视化,该台灯除了拥有作为台灯的基础照明功能外,该台灯能感知外界音乐节奏的律动,同时手机app端也可以调节灯的亮度和灯的颜色的变化,让无形的音乐通过可见的灯光闪动展现出来,让音乐变得目光可及,无疑可以让用户更好的感受音乐的魅力,因而这不仅是一个简单的音乐台灯,而且实现了将台灯纳入智能家居的行列,在物联网技术日新月异,迅速普及的今天,无疑是大胆的一步。整个台灯所有功能的实现,细想来着实是一件浩大的工程,首先要学习 Arduino开发板学会其相应的编程技术,了解其相应的开发环境点焊电路学会对电路的调试和实现控制,学习声音传感器的使用,了解与之对应的物联网技术,学习blinker的用法并成功将其与手机实现连接,将其向智能化的方向靠拢。

四.课题研究的主要内容:

项目一 Reading-LED

关键词:Computer+ESP8266 WIFI各种固件灯带

(1)简易框图展示:

(2)知识储备:
【1】关于ESP8266(芯片介绍):
是一个典型的物联网WIFI模块,但是自身就具有独立的处理内核和CPU,本身就是一个单片机。
硬件特性:
(1)性能稳定:ESP8266EX的工作温度范围大,能够保持稳定的性能
(2)高度集成:ESP8266EX集成了 32位 Tensilica处理器、标准数字外设接口、天线开关、功率放大器、低噪放大器、过滤器和电源管理模块等,仅需很少的外围电路,可将所占PCB空间降低。
(3)低功耗:ESP8266专为移动设备,可穿戴电子产品和物联网应用而设计,ESP8266具有省电模式适用于各种低功耗应用场景
(4)32位 Tensilica处理器:CPU处理速度最高可达 160MHz,支持实时操作系统和 Wi-Fi协议栈,可将高达80%的处理能力留给应用编程与项目开发


(3)硬件连接
a.所需材料:ESP8266开发板,LED灯带,我们小组选用120灯灯带,灯座,PVC管,灯罩,杜邦线,MicroUSB数据线
b.线路连接:ESP8266RX口接灯带D0口,灯座的正极接ESP8266的vin口,同时也接灯带的+5V接口,灯座的负极接ESP8266的地端(G口),同时也接灯带的地端GND
c.在供电方面:我们就用普通的5V手机充电器有时也直接插在电脑上,(由于每颗灯需要60mA的电流,这个普通充电器很难做到所有灯亮度到最亮,效果不是特别好,下一步要还用更强力的电源


软件设置:
首先搭建 Python运行环境:我选择使用 Anaconda编译平台,将 Anaconda加入系统路径,再在 Anaconda中搭建 Python的运行环境,安装相应的 Python的基础包,打开 Python的命令行,安装一些我需要的 Python库,numpy scipy(用于数字信号处理) pyqtgraph(用于图形用户窗口的可视化)和 pyaudio(用于麦克风录音)若干个库
处理 Arduino部分,由于我们使用 ESP8266,就要为附加开发板添加网址,安装我所需的ESP8266开发板。之后为 Arduino添加 websock库,我选择的开发板是 Sparkfun ESP8266 ThingDev到这里在 PC端的运行环境已经搭建完成

(5)分析电脑声卡的作用:
a.配合我的可视化程序,相当于代替了声音传感器的工作,可视化程序从 PC机默认的音频输入设备(由操作系统设置)中传输音频。即意味着可以在计算机上播放音乐,并将回放直接连接到可视化程序中。
b.基于此我启用立体声混音,并将其设置为默认设备。那么我的音频回放现在应该用作可视化程序的音频输入源。启用程序后将自动使用我的默认录音设备(麦克风)作为音频输入。
(6)python代码的核心内容:
A.配置和设置部分(config.py)为 ESP8266设置好 IP地址,设置灯带中 LED的数量,设置好相
应的麦克风采样率(我设置 FPS恒为 60)
B .录音与麦克风部分(microphone.py):配置项目调用麦克风,用麦克风记录音频;
C .数字信号处理部分(dsp.py)设计简易的指数平滑滤波器,处理成低延迟信号;
D .三维可视化部分(visualiz.py)在 PC端创建一个可视化窗口,实现音频可视化;
E .通过 WiFi向 ESP8266发送像素信息(led.py);
(7)Arduino代码核心内容:
A.为ESP8266设置相应的IP地址,网关和子午掩码,使芯片连接到电脑的Wi-Fi
B.编写接收数据包的程序
C.添加时间抖动和伽马矫正,以获得更加自然的光强轮廓



(8)项目一实现的功能
结合PC机和ESP8266,PC机的声音处理对于我们来说就是一个天然完美的声音传感器,我们从PC机的默认音频输入设备的音频回放接入程序中用作我程序的音频输入,就是数字信号处理和音乐可视化,得到的结果是一个较为即时的像素信息,而我们已经写好了ESP8266的底层通过配置和设置(IP地址,子午掩码,默认网关等内容)使得ESP8266处于和PC所在的同一个局域网中,从而使得PC端Python处理所得的像素信号通过WIFI传入了ESP8266,ESP8266端内部程序运行,开始执行像素信号数据包裹的接收和处理,并最终转化为led灯色彩的跳转和抖动,同时产生相应的灯带轨迹变化及混叠效果(与音频的高低潮匹配),实现音乐可视化效果
(9)项目一的不足之处:
a.ESP8266支持最多256个led灯,这就造成了整体效果没有那么完美。
b.我们发现这个像素信息也不是那么完美,有时候会太过于集中分布,造成整体效果不佳
c.我们发现先开启程序与先开启音频播放的效果相差较大,其中缘由仍未解决
d.之前用普通5v手机充电器效果不是很理想,出现灯的亮度不均甚至局部熄灭,选用什么样的电源能保证既不烧毁芯片,又不损坏灯带,同时经济适用。

项目二 Listening-LED

                                                                    **关键词:声音传感器 Arduino Nano各种固件灯带**

(1)简易框图展示

(2)知识储备:
【1】关于Arduino(了解平台)
Arduino是一款便捷灵活,方便上手的开源电子原型平台。包含硬件(各种型号的 Arduino开发板)软件(ArduinoIDE),Arduino可以通过各种各样的传感器来感知环境,基于Arduino编程语言,板子上的微控制器将程序烧录成二进制文件写入芯片。
平台特点:
a.跨平台:Arduino芯片可以在Windows, OS, Linux,三大主流操作系统运行;
b.简单清晰:语言简单,应用灵活,容易上手,可快速进行开发;
c.开放性:Arduino的硬件原理图,电路图,核心库文件都是开源的,资源共享性好;
这也是我选择Arduino开发平台的原因

【2】主板介绍(确定主板)
Arduino的型号有很多,如 Arduino Uno,Arduino Nano,Arduino LilyPad,Arduino Mega 2560Arduino Ethernet,Arduino Due,Arduino Leonardo,Arduino Yún等等

着重介绍Arduino Nano开发板:
ArduinoNano是ArduinoUSB接口的微型版本,最大的不同是没有电源插座以及USB接口是Mini-B型插座。Arduino Nano是尺寸非常小的而且可以直接插在面包板上使用。其处理器核心是ATmega168(Nano2.x)和ATmega328(Nano3.0),,同时具有14路数字输入/输出口(其中6路可作为PWM输出),8路模拟输入,一个16MHz晶体振荡器,一个mini-B USB口,一个ICSP header和一个复位按钮。
总而言之 Nano整体小巧,价格合适,没有致命的缺点,特别适合体积小的 DIY产品使用

(3)硬件连接:


(4)思考点----难点----特殊处理方法
4.1.多大的声音叫大声(局部与整体的关系);
a.项目二要用声音传感器感知周围的音频环境再做到音频与视觉效果上的实时匹配,这个灯可能会用在不同的环绕声系统之中(例如KTV,演唱会,露天音乐节等等)
b.我们不妨从一首歌入手,前奏,间奏,收尾一般都会比较安静,最大音量值不应该是一个常值,应该让程序追踪检测并实时做出更新,定期调整最大音量(maxVol)调整其视觉强度。
4.2.声音响度与LED亮度的关系:
笔者查阅了相关资料,无外乎有以下几种关系:

在仿真软件做仿真结果如下:

4.3.关于平均值(“顺序”平均值or常规平均值)
顺序平均值:此概念也是查资料所得,我们常规的平均值,就是一次性对所有数据求和取平均,而顺序平均值是按顺序计算当前值和上次计算的平均值的平均值,为了解释得更加明确,我们不妨假设一组数据:0,1,2,3,4,5,6,7,8,9,10,
真实平均值:
与数据输入顺序无关!
顺序平均值:
a.若数据的输入顺序为 0,1,2,3,4,5,6,7,8,9,10,则顺序平均值为:

b.若数据的输入顺序为 10,9,8,7,6,5,4,3,2,1,0,则顺序平均值为:

我们不难发现,顺序平均值与序列的输入顺序有关,输入的数据越来越大,顺序平均的结果也会越来越大,而输入的数据越来越小,顺序平均的结果也会越来越小(面对的都是同一段序列)

为了更清晰的说明对本项目的影响,进行以下仿真

由仿真我们很容易得出结论:
1)顺序平均值的方法对声音响度的响应更快,更敏感,同时我们也可以看到,顺序平均值让我们很真切的感受到高潮和低谷
2)而常规平均值由于长时间的读取,数据的大量累积,即使大的声音波动也几乎没有变化,系统无法及时的做出响应,是很不利的
因而我们最终采用顺序平均值的概念!

4.4.关于灯亮度的衰减(主函数调用此模块做光强淡化)(Fade()函数)

void fade(float damper) {for (int i = 0; i < strand.numPixels(); i++) {uint32_t col = strand.getPixelColor(i);
if (col == 0) continue;
float colors[3]; //Array of the three RGB values
for (int j = 0; j < 3; j++) colors[j] = split(col, j) * damper;
strand.setPixelColor(i, strand.Color(colors[0] , colors[1], colors[2]));

上述的就是此模块的原函数:
A.首先定义变量damper必须在0~1之间,保证每一次都是淡化的;
B.接着检索当前位置的颜色;
C.如果是黑色的,就不能再褪色了,直接跳出循环;
D.如果不是黑色,用for语句将RGB三基色循环淡化,之后将丹化后的颜色归位;
E.整体嵌套for循环,使等待上每一个LED都被遍历淡化到;

4.5.近似模拟频谱分析(变量bump)
因为我们做这个项目的时候,并没有用到频谱分析仪,使用的是音量强度和音量波动,起初
以为可能会出现与拍子不对称的情况,但是实际效果还是很让人满意的。
Bump的基本概念就是一个相对大的,正的音量变化,Bump其实就是在模仿歌曲的节拍,意
图实现节奏的跟随。
如何能更好的实现节奏的跟随,查阅相关资料:

套用在“关于平均”中的思想,是平均音量的正变化,而不仅仅是音量本身。代码之中就是

if (volume - last > 0) avgBump = (avgBump + (volume - last)) / 2.0;

实际上我们使用dump的平方创造了更好的亮度体验:

damp = pow(damp, 2.0);

4.6.脉冲的起始位置和终止位置的确定(PalettePulse()函数和Pulse函数)
注意我的脉冲的起止位置并不是固定不变的,而是根据音量强度来准确定位的,为了效果跟
好,我们是将整条灯带一分为二:

int start = LED_HALF - (LED_HALF * (volume / maxVol));
int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 2;`

代码核心思想:
a.首先确定Pulse脉冲的起止位置(由音量确定);
b.For循环从start到finish遍历,并加上damp效果;
c.考虑静音时候实现一种淡入效果;
d.利用for循环,将预期效果与当前RGB对比,实现亮度的选择性覆盖,褪色效果更明显;

4.7.还有很多为满足不同功能的自定义函数篇幅原因,不再一一细说:
a. traffic()函数:扫描整个数组,判断左移右移,创建每一个点的轨迹效果;
b. Snake()函数:使点随着节拍前后摆动,调整数据流迭代速度,适应乐曲节奏;
c. PaletteDance()函数:比例颜色拟合及正弦亮度调光,数学移位改变亮度实现循环;
d. Glitter()函数:创建RGB闪烁的视觉效果,覆盖或者迭代时出现碰撞火花效果;
e. pointball()函数:创建颜色混合效果,制造颜色炸弹



5.项目二实现的功能:
脱离了PC端的自带声卡处理,用声音传感器进行外界音频信息的采集,没有频谱分析仪我们需要设计巧妙地算法实现音乐节奏的可视化跟随,为了流畅的视觉体验,我们对最大音量(maxVol)进行调整同时引入了“顺序平均”及fade()函数让led灯带颜色变化的更为顺畅,为了丰富视觉体验,我们引入多种轨迹和颜色拟合混合模块,增强视觉冲击力,最终实现对每一个LED色彩,频率,亮度的精准把控,对音乐节奏的合适跟随,对音乐高低潮的可视化展现

项目三 手机端控制 【SmartLED】

                                                                ***关键词:手机app:blynk物联网远程控制***

注:参考Ian Buckley[U]的GitHub文章
Add Wi-Fi Controlled Lighting to Your Computer With NodeMCU

(一)知识储备
a.我们所使用的开发平台依旧是项目二之中的 Arduino IDE,使用的开发板依旧是项目一之中的ESP8266,关于二者在此处不再赘述;
b.关于 Blynk:
具有以下特性:
1.其由服务器端、app端、设备端组成,可以部署到几乎所有物联网平台
2. app端支持 ios、android
3.设备端可以使用蓝牙、WiFi、MQTT等方式接入,支持 Arduino、freeRTOS、mbedOS、Linux等开发平台
4.服务器端可以部署到阿里云、腾讯云、 OneNET、百度云、AWS、google cloud等平台,通过界面布局器,DIY用户可自己拖拽布局设备控制界面,自由打造物联网设备

(二)所需材料及电路连接
5v像素LED灯带WS2811,NodeMCU,220-500欧姆电阻,100-1000微法电容器,拨动开关或断路器,Molex母头连接器,面包板, 5v电源(用于测试)原型板和电线(用于安装电路),各种连接线



(四)编程代码核心思想阐述:
a.引用必要的库文件,选择正确的板和端口,包含库FastLED,BlynkSimpleEsp8266等;

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include "FastLED.h"
#include <BlynkSimpleEsp8266.h>

b.添加定义,声明引脚,灯的数量,基础亮度;

#define LED_PIN 3
#define LED_TYPE WS2812B
#define NUM_LEDS 44
#define BRIGHTNESS 64
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

c.定义LED的RGB变量,及模式切换开关;

int r = 500;
int g = 500;
int b = 500;
int masterSwitch = 1;
int autoMode = 1;
uint8_t gHue = 0;

d.设置网关信息,连接上个人Wi-Fi,与Blynk建立通信;

char auth[] = "SMARTLED";
char ssid[] = "zhangyibo";
char pass[] = "zhangyibo123";

e.保存移动端数据,接收不同模式下执行不同的操作,同时接收Blynk端对RGB的调整;

void loop() {}
Blynk.run();
}
if(masterSwitch == 0) {if(autoMode == 1 && masterSwitch ==
1) {for (int i = 0; i < NUM_LEDS; i++)
{fill_rainbow(  leds,   NUM_LEDS,
gHue, 7);
leds[i] = CRGB::Black;
FastLED.show();
FastLED.show();
delay(30);
}
FastLED.delay(1000/FRAMES_PER_SECON
}
D);
if(autoMode == 0 && masterSwitch ==
1) {EVERY_N_MILLISECONDS(20) {gHue++;
for(inti=0;i<NUM_LEDS;i++){leds[i] = CRGB(r, g, b);
FastLED.show();
}
}
}
delay(30);

f.将手机端对像素信息的操作反映到灯带上的每一个 LED

BLYNK_WRITE(V0)
g = param.asInt();
BLYNK_WRITE(V3)
masterSwitch = param.asInt();
BLYNK_WRITE(V1)
b = param.asInt();
BLYNK_WRITE(V4)
r = param.asInt();
BLYNK_WRITE(V2)
autoMode = param.asInt();

(五)实现的功能:
手机 app Blynk连接上本地 Wi-Fi,同时 ESP8266也连接上了同一个 Wi-Fi,从而实现了两者之间的物联交互,我们可以通过操控移动终端,调正我们想要的RGB三基色混搭,数据包由Blynk发出,通过Wi-Fi传到ESP8266芯片,芯片接受之后经过相应的算法处理,将数据信息再传至灯带,从而精准实现对灯带上每一个灯的远程调节控制

四.成果汇总介绍:

(1)项目(一) Reading-LED
结合PC机和ESP8266,PC机的声音处理对于我们来说就是一个天然完美的声音传感器,我们从PC机的默认音频输入设备的音频回放接入程序中用作我程序的音频输入,就是数字信号处理和音乐可视化,得到的结果是一个较为即时的像素信息,而我们已经写好了ESP8266的底层通过配置和设置(IP地址,子午掩码,默认网关等内容)使得ESP8266处于和PC所在的同一个局域网中,从而使得PC端Python处理所得的像素信号通过WIFI传入了ESP8266,ESP8266端内部程序运行,开始执行像素信号数据包裹的接收和处理,并最终转化为led灯色彩的跳转和抖动,从而达到我们最终的目的
(2)项目(二)Listening-LED
脱离了PC端的自带声卡处理,用声音传感器进行外界音频信息的采集,没有频谱分析仪我们需要设计巧妙地算法实现音乐节奏的可视化跟随,为了流畅的视觉体验,我们对最大音量(maxVol)进行调整同时引入了“顺序平均”及fade()函数让led灯带颜色变化的更为顺畅,为了丰富视觉体验,我们引入多种轨迹和颜色拟合混合模块,增强视觉冲击力,最终实现对每一个LED色彩,频率,亮度的精准把控,对音乐节奏的合适跟随,对音乐高低潮的可视化展现




(3)项目(三)Smart-LED
手机app Blynk连接上本地Wi-Fi,同时ESP8266也连接上了同一个Wi-Fi,从而实现了两
者之间的物联交互,我们可以通过操控移动终端,调正我们想要的 RGB三基色混搭,数据包由
Blynk发出,通过Wi-Fi传到ESP8266芯片,芯片接受之后经过相应的算法处理,将数据信息再
传至灯带,从而精准实现对灯带上每一个灯的远程调节控制

五.存在的问题和不足:

(1)关于项目一:Reading-LED
a.ESP8266支持最多256个led灯,这就造成了整体效果没有那么完美。
b.我们发现这个像素信息也不是那么完美,有时候会太过于集中分布,造成整体效果不佳
c.我们发现先开启程序与先开启音频播放的效果相差较大,其中缘由仍未解决
d.之前用普通5v手机充电器效果不是很理想,出现灯的亮度不均甚至局部熄灭,选用什么
样的电源能保证既不烧毁芯片,又不损坏灯带,同时经济适用。
(2)关于项目二:Listening-LED
声音主要因素是响度,音调,音色,此部分仅仅由声音传感器收集到的响度(即信号强度大小)来控制 LED灯的闪烁,即使我们的理论再完美,算法再优化也无法将一首歌曲的信息完美展现,音调(即频率)由于声音传感器的灵敏度问题,毛刺现象非常严重,噪音影响不可忽视,所以如何将频率更好的作为一个控制要素纳入我们的指标体系就显得很关键
(3)关于项目三:SmartLED
就项目三本身来说,其完美的实现了我们预期设定的目标,没有什么根本性的问题,
本身是很实用,但是没有集成更多的其他功能,总体而言是比较单一,不能很好得适应当
今时代的需求,我们也试图将SmartLED和Listening-LED合二为一,也尝试过添加其他
的外设功能模块,但是因为技术水平和学业压力,运营社团等诸多限制,未能如愿。

六.收获与体会

在项目进展过程中,我们了解了许多单片机接口方面的知识,同时通过github,电子发烧友网等网站接触了许许多多类似的开源代码和装置案例,对python语言和arduino开发板的编程语言有了大致的了解,收获颇丰。同时还遇到了许许多多的问题,比如声音传感器的灵敏度,接收频率的稳定程度,声音的三频如何对应颜色的三基色,信号毛刺如何进行优化等许许多多的问题,有些已经通过网上查找资料,询问老师和讨论解决,而有些仍尚存疑虑,问题亟待解决。虽然部分问题仍然存在,但是项目主体框架已然搭建完成,部分功能已经实现,但DIY就是这样,随着思考探索的深入,就会想到很多之前没有考虑到的问题,触及到很多自己没有接触到的领域,有很多专业认知和问题处理方式在短时间之内不能接受,但毫无疑问的是,笔者成熟了很多,学会了很多,知道了如何处理未知的问题,找到了很多问题诉诸及解决的渠道,也感受到了CSDN,GitHub等论坛大佬的天马行空奇思妙想,当把创意付诸实际的完成,可能就会是一个创新。
时光如匆匆流水,转眼间创新创业训练就要结题了,结题论文也进入了收尾阶段,从开始构思项目课题,到中期答辩,从获得国家级评定到结题论文的顺利完成,一直离不开学校老师,小组同学,朋友们的热情帮助,在这里请接受我诚挚的谢意!“长风破浪会有时,直挂云帆济沧海”,论文的结尾,也是一个故事一段旅程的终结,希望自己能够继续年少的梦想,不负韶光不负己。

七.参考文献

【1】单片机在音乐节奏识别灯效系统中的智能控制 DENG He-lian梁平原-《机械工程及其自动
化》-2008年4期
【2】一种智能音乐节奏闪烁灯 [专利]实用新型 CN201521064888.1罗启才 2016-05-11
【3】一种基于音乐节奏的灯光控制方法 [专利]发明专利 CN201910326775.0姚斌 2019-07-30
【4】计算机音乐可视化表征谱设计 [期刊论文]韦岗曹燕王一歌赵明剑 -《现代信息科技》- 2019年14期
【5】音乐轨迹曲线拟合可视化分析法 [期刊论文]孙剑 -《音乐艺术》 CSSCI北大核心 -2018年4期
【6】基于 MATLAB的音乐旋律二维可视化方法 [期刊论文]张岩吕梦儒 -《沈阳师范大学学报(自然科学版)》 CSTPCD - 2018年4期
【7】音乐可视化研究特征选择及表达方式综述 [期刊论文]黄宗珊饶志双 -《科技视界》 -2018年5期

八.少部分代码如下:

#include <Adafruit_NeoPixel.h>
#define LED_PIN   A5
#define LED_TOTAL 120
//LED灯数量
#define AUDIO_PIN A0
#define KNOB_PIN  A1
#define BUTTON_1  6
#define BUTTON_2  2
#define BUTTON_3  4
#define LED_HALF  LED_TOTAL/2
#define VISUALS   6
Adafruit_NeoPixel strand = Adafruit_NeoPixel(LED_TOTAL, LED_PIN, NEO_GRB + NEO_KHZ800); //LED strand
objetcs
uint16_t gradient = 0;
//十分重要的点
//
//
//
这个数组包含每个颜色函数的“阈值”(即它们在重复之前取的最大数字)。
这些值的顺序与ColorPalette()的switch  case中的顺序相同(Rainbow() is first,等等)。这只是为了
防止“渐变”溢出,颜色函数本身可以取任何正值。例如,
//循环之前取的最大值Rainbow()是1529,所以“gradient”应该在1529之后重置,如下所示。
// 如果在ColorPalette()的switch-case中添加/删除颜色函数,请确保相应地添加/删除值。
uint16_t thresholds[] = {1529, 1019, 764, 764, 764, 1274};
uint8_t palette = 0;  //保存当前调色板。
uint8_t visual = 0;   //保存当前显示的可视内容。
uint8_t volume = 0;   //保持从声音检测器读取的音量级别。
uint8_t last = 0;
//保存上一个loop()传递的卷的值。
float maxVol = 15;
//保持迄今为止记录的最大音量,按比例调整视觉的反应能力。
float knob = 1023.0;  //保存修剪器扭曲程度的百分比。用于调节最大亮度。
float avgBump = 0;    //保持“平均average”音量变化以触发“颠簸bump”。
float avgVol = 0;     //保持“平均average”音量水平,按比例调整视觉体验。
float shuffleTime = 0;  //保存上次洗牌(shuffle)前运行时的秒数(如果洗牌模式(shuffle mode)打开).
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//NOTE:之所以引用“平均数”,是因为它不是一个真正的数学平均数。这是因为我有
//我发现我所谓的“序列平均”在执行上比实际平均更成功。的区别
//是排序后的平均值没有使用到目前为止记录的所有值的池,而是平均
//最近的平均值和当前接收到的值(按顺序)。具体:
(1 + 2 + 3) / 3 = 2
(1 + 2) / 2 = 1.5 --> (1.5 + 3) / 2 = 2.25 (如果1、2、3是接收值的顺序)
//
程序中的所有“平均值”都是这样操作的。两者之间的差别很细微,但原因在于它们是按顺序排列的
//平均值更能适应总体成交量的变化。换句话说,如果你从大声到安静,
//排序后的平均值更有可能显示出更准确和更流畅的比例调整。
///////////////////////////////////////////////////////////////////////////////////////////////////
/////////////
bool shuffle = true;  //切换为洗牌模式。shuffle mode.
bool bump = false;     //用于在音量有“起伏bump”时通过
//For Traffic() visual
int8_t pos[LED_TOTAL] = { -2};    //存储一组颜色“点”来遍历LED线。
uint8_t rgb[LED_TOTAL][3] = {0};  //存储每个点的特定RGB值。
//For Snake() visual
bool left = false;  //确定迭代的方向。
int8_t dotPos = 0;  //确定dot的位置
float timeBump = 0; //.保存最后一次“bump”发生的时间(以运行时秒为单位)。
float avgTime = 0;  //保持每个“bump”之间的“平均”时间量(用于为dot圆点的移动定速)。.
//////////</Globals>
//////////<用于设定标准的函数>
void setup() {Serial.begin(9600); //设置串行数据传输的数据速率。.也就是设置波特率
//定义用于输入的button pin
pinMode(BUTTON_1, INPUT); pinMode(BUTTON_2, INPUT); pinMode(BUTTON_3, INPUT);
//写一个“高”值的引脚。
digitalWrite(BUTTON_1, HIGH); digitalWrite(BUTTON_2, HIGH); digitalWrite(BUTTON_3, HIGH);
strand.begin(); //初始化LED灯带线。
strand.show();  //显示一个空白的线,只是为了让LED准备好使用。
}
void loop() {  //这就是奇迹发生的地方。这个循环产生视觉的每一帧
volume = analogRead(AUDIO_PIN);
knob = 204.6 / 1023.0; //记录多远
//Sets a threshold for volume.设置音量阈值。
//从声音检测器记录音量级别
//
//
在实践中,我发现噪音可以达到15,所以如果它更低,视觉认为它是无声的。
此外,如果音量小于平均音量/ 2(本质上是0的平均音量),则被认为是无声的。
if (volume < avgVol / 2.0 || volume < 15) volume = 0;
else avgVol = (avgVol + volume) / 2.0; //如果没有zeo,则取“平均”音量
//如果当前音量大于所记录的最大音量值,则重写
if (volume > maxVol) maxVol = volume;
//.如果在设计中没有包含button,请检查Cycle*函数以获得特定的说明。
////////////////////////////////////////////////////////////////////////////////////////////////////
CyclePalette();  //改变调色板换成洗牌模式或按下按钮
CycleVisual();   //改变显示 .洗牌模式或按下按钮
////////////////////////////////////////////////////////////////////////////////////////////////////
//" 调制这就是调节“渐变”以防止溢出的地方
if (gradient > thresholds[palette]) {gradient %= thresholds[palette] + 1;//.每次调色板完成时,都是重新调整“maxVol”的好时机,以防万一
//歌声越来越安静;我们也不想永远失去亮度强度
//因为一声迷途的巨响。
maxVol = (maxVol + volume) / 2.0;
}
//如果自上一遍以来,音量volume有较大的变化,将其平均为“avgBump”
if (volume - last > 10) avgBump = (avgBump + (volume - last)) / 2.0;
//如果音量有显著变化,则触发“bump”
//avgbump稍微降低了一点,以使视觉对节拍更敏感。
bump = (volume - last > avgBump * .9);
//如果一个“颠簸bump”被触发trigger,平均颠簸bump之间的时间
if (bump) {avgTime = (((millis() / 1000.0) - timeBump) + avgTime) / 2.0;
timeBump = millis() / 1000.0;
}
Visualize();   //调用要与全局变量一起显示的适当可视化。
gradient++;
//增量梯度
last = volume; //记录当前音量为下一遍做准备
//设定视觉效果的速度,这样它们就不会太快而令人不快
delay(30);
}
//////////</Standard Functions>
//////////<Visual Functions>
//这个函数根据“visual”的值调用适当的可视化
void Visualize() {switch (visual) {case 0: return Pulse();
case 1: return PalettePulse();
case 2: return Traffic();
//case 3: return Snake();
case 3: return PaletteDance();
case 4: return Glitter();
case 5: return Paintball();
default: return Pulse();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////
该串将RGB值显示为32位无符号整数(uint32_t),这就是为什么ColorPalette()
//所有相关颜色函数的返回类型都是uint32_t。这个值是3的合数
//无符号8位整数(uint8_t)值(红色、蓝色和绿色各0-255)。你会注意到
// function split()(如下所示)用于从32位颜色值中分析这些8位值。
///////////////////////////////////////////////////////////////////////////////////////////////////
///////
此函数调用基于“调色板”的适当调色板。
//如果传递了一个负值,返回传递“渐变”的适当调色板。
//否则返回带有传递值的调色板(用于在链上拟合整个调色板)。
uint32_t ColorPalette(float num) {switch (palette) {case 0: return (num < 0) ? Rainbow(gradient) : Rainbow(num);
case 1: return (num < 0) ? Sunset(gradient) : Sunset(num);
case 2: return (num < 0) ? Ocean(gradient) : Ocean(num);
case 3: return (num < 0) ? PinaColada(gradient) : PinaColada(num);
case 4: return (num < 0) ? Sulfur(gradient) : Sulfur(num);
case 5: return (num < 0) ? NoGreen(gradient) : NoGreen(num);
default: return Rainbow(gradient);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
所有这些可视化的特征都是基于相对体积影响亮度的某个方面
// maxVol,让声音更大=更亮。最初,我做了简单的比例(volume/maxvol),但是我发现了这个
//视觉模糊。然后,我尝试了一种指数方法(将值的幂提高到
/ /体积/ maxvol)。虽然这在视觉上更令人满意,但我选择了两者之间的平衡。你会
//注意下面函数中的pow(volume/maxVol, 2.0)。这个简单的平方
//音量到maxVol得到一个更大的指数曲线,但不像实际的指数曲线那样夸张。
//从本质上讲,这使得较大的音量更亮,较小的音量更暗,从而在视觉上更清晰。
///////////////////////////////////////////////////////////////////////////////////////////////////
////////////
//从灯带线的中心发出脉冲
void Pulse() {fade(0.75);   //这个函数只是在循环()的每一次遍历中将颜色稍微调暗一点
//如果有一个“颠簸bump”,将调色板推进到下一个值得注意的颜色。
if (bump) gradient += thresholds[palette] / 24;
//如果某时刻是静音的,我们想要淡入效果,因此这个If语句
if (volume > 0) {uint32_t col = ColorPalette(-1); //我们重新取回的是32-bit
//这些变量决定了脉冲pulse从哪里开始和结束,因为它开始于链的中间
//  .这些量存储在变量中,因此只需计算一次(加上我们在循环中使用它们)。
int start = LED_HALF - (LED_HALF * (volume / maxVol));
int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 2;
//上面列出的LED_HALF只是您的线段上led数量的一半。
for (int i = start; i < finish; i++) {//“潮湿damp”会产生一种渐暗效果fade  effect,即像素pixel离线束中心越远,亮度就越低。
//它返回一个介于0和1之间的值,该值在灯带链的中心1处达到峰值,在灯带链的末端为0。
float damp = sin((i - start) * PI / float(finish - start));
//Squaring damp平方潮湿创造了更独特的亮度。distinctive brightness
damp = pow(damp, 2.0);
//获取当前像素current pixel的颜色,这样我们就可以看到它是否暗到可以覆盖。
uint32_t col2 = strand.getPixelColor(i);
//利用一个for循环执行以下操作
//使用位置音量和“旋钮knob”调整像素的亮度
//取预期颜色和现有颜色的平均RGB值进行比较
uint8_t colors[3];
float avgCol = 0, avgCol2 = 0;
for (int k = 0; k < 3; k++) {colors[k] = split(col, k) * damp * knob * pow(volume / maxVol, 2);
avgCol += colors[k];
avgCol2 += split(col2, k);
}
avgCol /= 3.0, avgCol2 /= 3.0;
将平均颜色作为“亮度”进行比较。只覆盖暗淡的颜色,使褪色效果更明显。
if (avgCol > avgCol2) strand.setPixelColor(i, strand.Color(colors[0], colors[1], colors[2]));
}
}
//这个命令实际上显示了灯光。如果你做了一个新的可视化,不要忘记这一点!
strand.show();
}
//PALETTEPULSE
调色板脉冲
与Pulse()相同,但是为整个托盘着色,而不是使用单一的纯色
void PalettePulse() {fade(0.75);
if (bump) gradient += thresholds[palette] / 24;
if (volume > 0) {int start = LED_HALF - (LED_HALF * (volume / maxVol));
int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 2;
for (int i = start; i < finish; i++) {float damp = sin((i - start) * PI / float(finish - start));damp = pow(damp, 2.0);
//这是与Pulse()惟一的区别。每个像素pixel的颜色并不相同,而是
//整个梯度与脉冲的传播相适应,有些是从“梯度”上偏移的。
int val = thresholds[palette] * (i - start) / (finish - start);
val += gradient;
uint32_t col = ColorPalette(val);
uint32_t col2 = strand.getPixelColor(i);
uint8_t colors[3];
float avgCol = 0, avgCol2 = 0;
for (int k = 0; k < 3; k++) {colors[k] = split(col, k) * damp * knob * pow(volume / maxVol, 2);
avgCol += colors[k];
avgCol2 += split(col2, k);
}
avgCol /= 3.0, avgCol2 /= 3.0;
if (avgCol > avgCol2) strand.setPixelColor(i, strand.Color(colors[0], colors[1], colors[2]));
}
}
strand.show();
}
//TRAFFIC交通灯
//点的相互碰撞和移动
void Traffic() {//fade()实际上在这里创建了每个点后面的轨迹,所以包含它很重要。
fade(0.8);
//如果检测到一个bump,创建一个要显示的点dot
if (bump) {//这个bump只是检查pos[]数组中是否有一个打开位置open   slot(-2)。
int8_t slot = 0;
for (slot; slot < sizeof(pos); slot++) {if (pos[slot] < -1) break;
else if (slot + 1 >= sizeof(pos)) {slot = -3;
break;
}
}
//如果有一个slot,把它设置在灯带线的初始位置。
if (slot != -3) {//.Evens偶数向右,  odds奇数向左,所以  evens从0开始,奇数始终最大。
pos[slot] = (slot % 2 == 0) ? -1 : strand.numPixels();
//during its birth.在它诞生的时候,根据“渐变gradient”的值给它一个颜色color。
uint32_t col = ColorPalette(-1);
gradient += thresholds[palette] / 24;
for (int j = 0; j < 3; j++) {rgb[slot][j] = split(col, j);
}
}
}
//同样,如果它是静音silent的,我们希望颜色淡出  fade out。
if (volume > 0) {//如果有声音,沿着这条灯带线适当地迭代每个点dot。
for (int i = 0; i < sizeof(pos); i++) {//如果一个点是-2,这意味着它是一个开放的位置 open slot,最终另一个点dot会占据这个位置。
if (pos[i] < -1) continue;
//如上所述,偶数向右(+1),奇数向左(-1)
pos[i] += (i % 2) ? -1 : 1;
//通过减法,奇数将达到-2,但如果一个偶数点超过LED条(led  strip),它将被清除。
if (pos[i] >= strand.numPixels()) pos[i] = -2;
//.将点dot设置为它的新位置和相应的颜色。
//
由于fade(),i的旧位置的颜色会逐渐淡入,留下痕迹。
strand.setPixelColor( pos[i], strand.Color(
float(rgb[i][0]) * pow(volume / maxVol, 2.0) * knob,float(rgb[i][1]) * pow(volume / maxVol, 2.0) * knob,
float(rgb[i][2]) * pow(volume / maxVol, 2.0) * knob)
);
}
}
strand.show(); //同样,不要忘记实际显示灯光!(actually show the lights)
}
//SNAKE
//dot随着节拍前后摆动
void Snake() {if (bump) {//在bump上稍微改变一下颜色
gradient += thresholds[palette] / 30;
//改变dot的方向会产生“跳舞”的错觉。
left = !left;
}
fade(0.975); //在dot后面留下痕迹。
uint32_t col = ColorPalette(-1); //获取当前“gradient”的颜色。
//只有在有声音的情况下,dot才会移动。
//否则,如果噪音noise开始并且一直在移动,它就会出现传送。
if (volume > 0) {//将dot设置为适当的颜色和强度
strand.setPixelColor(dotPos, strand.Color(
float(split(col, 0)) * pow(volume / maxVol, 1.5) * knob,
float(split(col, 1)) * pow(volume / maxVol, 1.5) * knob,
float(split(col, 2)) * pow(volume / maxVol, 1.5) * knob)
);
//这就是“平均时间avgTime”发挥作用的地方。
//
//
//
该变量是检测到的每个“碰撞”之间的“平均”时间量。
所以我们可以用它来确定点移动的速度,使它与音乐的节奏the tempo of the music相匹配。
点在正常循环速度下移动非常快,所以如果avgTime< 0.15秒,它就是最大速度。
//慢下来会导致颜色更新,但只会改变每隔一段循环的位置。
if (avgTime < 0.15)
dotPos += (left) ? -1 : 1;
else if (avgTime >= 0.15 && avgTime < 0.5 && gradient % 2 == 0)   dotPos += (left) ? -1 : 1;
else if (avgTime >= 0.5 && avgTime < 1.0 && gradient % 3 == 0)
else if (gradient % 4 == 0)
dotPos += (left) ? -1 : 1;
dotPos += (left) ? -1 : 1;
}
strand.show(); //显示灯,让灯实际亮起来
//检查点位置dot  position是否超出界限。
if (dotPos < 0)    dotPos = strand.numPixels() - 1;
else if (dotPos >= strand.numPixels())  dotPos = 0;
}
//调色板舞曲
//项目的整个调色板,振荡的节拍,类似于蛇,但整个梯度,而不是一个点
//GLITTER
//在调色板上创建与节拍匹配的白色亮片 white sparkles
void Glitter() {//这种视觉效果也适用于整个灯带上的整个调色板
//这只会使调色板循环得更快,因此更美观
gradient += thresholds[palette] / 204;
//“val”再次用作传递到ColorPalette()以适应整个调色板的比例值。
for (int i = 0; i < strand.numPixels(); i++) {unsigned int val = float(thresholds[palette] + 1) *
(float(i) / float(strand.numPixels()))
+ (gradient);
val %= thresholds[palette];
uint32_t  col = ColorPalette(val);
//我们希望亮片是明显的,所以我们调暗了背景颜色。(
strand.setPixelColor(i, strand.Color(
split(col, 0) / 6.0 * knob,
split(col, 1) / 6.0 * knob,
split(col, 2) / 6.0 * knob));
}
//每一个bump都要有sparkles
if (bump) {//随机生成器需要一个种子,而micros()提供了很大范围的值。
// micros()是程序开始运行以来的微秒数。
randomSeed(micros());
//在灯带上随机选一个点。
dotPos = random(strand.numPixels() - 1);
//以适当的亮度在任意位置draw  sparkle。
strand.setPixelColor(dotPos, strand.Color(
255.0 * pow(volume / maxVol, 2.0) * knob,
255.0 * pow(volume / maxVol, 2.0) * knob,
255.0 * pow(volume / maxVol, 2.0) * knob
));
}
bleed(dotPos);
strand.show(); //Show the lights.
}
//PAINTBALL//彩弹
//回收Glitter()的随机定位;模拟“彩弹”的颜色随机飞溅在灯带上和混在一起。
void Paintball()
//如果它是自上次“bump”以来“bump”平均时间的两倍,则开始衰减。
if ((millis() / 1000.0) - timeBump > avgTime * 2.0) fade(0.99);
//颜色混在一起。操作类似于淡入。有关更多信息,请参见下面的定义
bleed(dotPos);
//如果有一个bump,创建一个新的彩弹(如Glitter()中的sparkles)
if (bump) {//随机生成器(random generator)需要一个种子,而micros()提供了很大范围的值。
//micros()是程序开始运行以来的微秒数microseconds。
randomSeed(micros());
//在灯带线上随便选一个点spot。Random已经在上面重新播种了,所以没有必要再做一次
dotPos = random(strand.numPixels() - 1);
//从调色板(palette)中随机选择一种颜色。
uint32_t col = ColorPalette(random(thresholds[palette]));
//array数组来保存最终的RGB值
uint8_t colors[3];
//将颜色的亮度与相对体积和电位器值联系起来。
for (int i = 0; i < 3; i++) colors[i] = split(col, i) * pow(volume / maxVol, 2.0) * knob;
//将“paintball”溅射到随机位置。
strand.setPixelColor(dotPos, strand.Color(colors[0], colors[1], colors[2]));
//下面一部分是在原位置的左右两边放置一个不太明亮的同色版本,这样bleed effect更强,颜色更鲜艳。
for (int i = 0; i < 3; i++) colors[i] *= .8;
strand.setPixelColor(dotPos - 1, strand.Color(colors[0], colors[1], colors[2]));
strand.setPixelColor(dotPos + 1, strand.Color(colors[0], colors[1], colors[2]));
}
strand.show(); //Show lights.
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
void Cycle() {for (int i = 0; i < strand.numPixels(); i++) {float val = float(thresholds[palette]) * (float(i) / float(strand.numPixels())) + (gradient);
val = int(val) % thresholds[palette];
strand.setPixelColor(i, ColorPalette(val));
}
strand.show();
gradient += 32;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////</Visual Functions>//////////<Helper Functions>
void CyclePalette()
//如果palette大于阈值的总体,则从0开始
//这就是为什么在添加调色板时向数组添加阈值threshold很重要,否则程序将在到达之前返回Rainbow()。
if (palette >= sizeof(thresholds) / 2) palette = 0;
gradient %= thresholds[palette]; //调节gradiennt以防止可能发生的溢出overflow。
//在我的设置中,按钮 button靠近麦克风  microphone,所以按下按钮的声音对声音检测器 the sound detector
来说相对较大。
//这导致视觉上认为发生了很大的噪音,因此延迟只允许按钮的声音不减弱地通过。
delay(350);
maxVol = avgVol;  //将最大音量maxVol设置为平均音量avgVol,以获得全新的体验。
}
///////////////////////////////////////////////////////////////////////////////////////////////////
如果洗牌模式(shuffle mode)开启,距离上次洗牌已经过了 30秒,然后使用渐变模块在调色板和可视化洗牌之间
做出随机选择
if (shuffle && millis() / 1000.0 - shuffleTime > 30 && gradient % 2) {shuffleTime = millis() / 1000.0; //记录洗牌shuffle发生的时间。
palette++;
if (palette >= sizeof(thresholds) / 2) palette = 0;
gradient %= thresholds[palette];
maxVol = avgVol;  //将最大音量maxVol设置为平均音量avgVol,以获得全新的体验。
}
}
void CycleVisual() {/如果没有更多的视觉效果可以循环,则重置“visual”。
if (visual > VISUALS) visual = 0;
//这就是为什么你应该改变“视觉VISUALS”,如果你添加一个视觉visual,或程序循环它。
//如果您循环到Traffic() visual,则将所有点的位置重置为不存在(-2)。
if (visual == 1) memset(pos, -2, sizeof(pos));
//如果将Snake()和PaletteDance()循环到,则为它们提供一个随机的起点。
if (visual == 2 || visual == 3) {randomSeed(analogRead(0));
dotPos = random(strand.numPixels());
}
//和之前一样,这个延迟是为了防止按钮按下影响“maxVol”。
delay(350);
maxVol = avgVol; //将最大音量(maxVol)设置为平均音量(avgVol),以获得全新的体验
}
void fade(float damper) {//“damper”必须在0和1之间,否则你最终会点亮灯或什么都不做。
for (int i = 0; i < strand.numPixels(); i++) {//检索当前位置的颜色。
uint32_t col = strand.getPixelColor(i);
//如果它是黑色的,你就不能再褪色了。
if (col == 0) continue;
float colors[3]; //Array of the three RGB values
//将每个值乘以“damper”
for (int j = 0; j < 3; j++) colors[j] = split(col, j) * damper;
//把dampered color放回原位。
strand.setPixelColor(i, strand.Color(colors[0] , colors[1], colors[2]));
}
}
通过从指定的“point”平均当前灯带线中的“bleeds”颜色
void bleed(uint8_t Point) {for (int i = 1; i < strand.numPixels(); i++) {//首先观察“point”的左右像素,然后慢慢地找出它的出路
int sides[] = {Point - i, Point + i};
for (int i = 0; i < 2; i++) {//对于每Point+iandPoint-i,左边和右边的像素,加上它们本身,被平均在一起。plusthemselves,are
averaged together.//基本上,它将一个像素设置为它和它的邻居的平均值,从起始点的左右开始,移动到灯带线的末端
int point = sides[i];
uint32_t   colors[]   =   {strand.getPixelColor(point   -   1),   strand.getPixelColor(point),
strand.getPixelColor(point + 1)  };
//将新的平均值设置为中心点,而不是左、右点。
strand.setPixelColor(point, strand.Color(
float( split(colors[0], 0) + split(colors[1], 0) + split(colors[2], 0) ) / 3.0,
float( split(colors[0], 1) + split(colors[1], 1) + split(colors[2], 1) ) / 3.0,
float( split(colors[0], 2) + split(colors[1], 2) + split(colors[2], 2) ) / 3.0)
);
}
}
}
//来自NeoPixel处理的复合32位值。
//这是通过右移位操作符“>>”来实现的
uint8_t split(uint32_t color, uint8_t i ) {//0 = Red, 1 = Green, 2 = Blue
if (i == 0) return color >> 16;
if (i == 1) return color >> 8;
if (i == 2) return color >> 0;
return -1;
}
/////////</Helper Functions辅助函数>
//////////<Palette Functions调色板函数群>
这些函数只是获取一个值并以无符号32位整数的形式返回一个渐变颜色
渐变为255的倍数返回不同的、变化的颜色
这是因为这3个RGB值中的任何一个的最大值都是255,所以在下一个颜色开始出现时,
这是一个直观的截止时间。
梯度也应该循环回到它们的初始颜色,所以在颜色上没有跳跃。
uint32_t Rainbow(unsigned int i) {if (i > 1529) return Rainbow(i % 1530);
if (i > 1274) return strand.Color(255, 0, 255 - (i % 255));   //violet -> red
if (i > 1019) return strand.Color((i % 255), 0, 255);
if (i > 764) return strand.Color(0, 255 - (i % 255), 255);
if (i > 509) return strand.Color(0, 255, (i % 255));
if (i > 255) return strand.Color(255 - (i % 255), 255, 0);
return strand.Color(255, i, 0);
//blue -> violet
//aqua -> blue
//green -> aqua
//yellow -> green
//red -> yellow
}
uint32_t Sunset(unsigned int i) {if (i > 1019) return Sunset(i % 1020);
if (i > 764) return strand.Color((i % 255), 0, 255 - (i % 255));
if (i > 509) return strand.Color(255 - (i % 255), 0, 255);
if (i > 255) return strand.Color(255, 128 - (i % 255) / 2, (i % 255));
return strand.Color(255, i / 2, 0);
//blue -> red
//purple -> blue
//orange -> purple
//red -> orange
}
uint32_t Ocean(unsigned int i) {if (i > 764) return Ocean(i % 765);
if (i > 509) return strand.Color(0, i % 255, 255 - (i % 255));  //blue -> green
if (i > 255) return strand.Color(0, 255 - (i % 255), 255);
return strand.Color(0, 255, i);
//aqua -> blue
//green -> aqua
}
uint32_t PinaColada(unsigned int i) {if (i > 764) return PinaColada(i % 765);
if (i > 509) return strand.Color(255 - (i % 255) / 2, (i % 255) / 2, (i % 255) / 2); //red -> half
white
if (i > 255) return strand.Color(255, 255 - (i % 255), 0);
return strand.Color(128 + (i / 2), 128 + (i / 2), 128 - i / 2);
//yellow -> red
//half white -> yellow
}
uint32_t Sulfur(unsigned int i) {if (i > 764) return Sulfur(i % 765);
if (i > 509) return strand.Color(i % 255, 255, 255 - (i % 255));   //aqua -> yellow
if (i > 255) return strand.Color(0, 255, i % 255);                 //green -> aquareturn strand.Color(255 - i, 255, 0);
//yellow -> green
}
uint32_t NoGreen(unsigned int i) {if (i > 1274) return NoGreen(i % 1275);
if (i > 1019) return strand.Color(255, 0, 255 - (i % 255));
if (i > 764) return strand.Color((i % 255), 0, 255);
if (i > 509) return strand.Color(0, 255 - (i % 255), 255);
if (i > 255) return strand.Color(255 - (i % 255), 255, i % 255);
return strand.Color(255, i, 0);
//violet -> red
//blue -> violet
//aqua -> blue
//yellow -> aqua
//red -> yellow
}

(音乐可视化项目)Light-Dancer为音乐而生相关推荐

  1. Windows Media Player + 音乐可视化 + 音乐倒放

    ◎ 音乐可视化 利用 Windows 自带的多媒体播放器 Windows Media Player,呈现多彩,超强空间感的音乐可视化效果,另外还有音乐倒放

  2. 【花雕动手做】有趣好玩的音乐可视化系列项目(32)--P10矩阵LED单元板

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  3. 【花雕动手做】有趣好玩的音乐可视化系列项目(27)--磁搅LED水旋灯

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  4. 【花雕动手做】有趣好玩的音乐可视化系列项目(31)--LCD1602液晶屏

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  5. 【花雕动手做】有趣好玩的音乐可视化系列小项目(17)--光导纤维灯

    偶然心血来潮,想要做一个声音可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  6. 【花雕动手做】有趣好玩的音乐可视化系列小项目(19)--通体光纤灯

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  7. 【花雕动手做】有趣好玩的音乐可视化系列小项目(18)--LED平面板灯

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  8. 【花雕动手做】有趣好玩的音乐可视化系列项目(28)--LED乒乓球灯

    偶然心血来潮,想要做一个音乐可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

  9. 【花雕动手做】有趣好玩的音乐可视化系列小项目(14)---水杯水瓶灯

    偶然心血来潮,想要做一个声音可视化的系列专题.这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累 ...

最新文章

  1. Python:使用ctypes库调用外部DLL 数据类型对应
  2. Java编程思想 学习笔记7
  3. ogre绘制3d图形_R语言统计与绘图:绘制饼图
  4. 【项目管理】各种常用工具图表说明和示例
  5. lunix 命令积累
  6. make找不到linux内核函数,linux内核make menuconfig出错
  7. a good website to test OTP
  8. ecshop 模板页php,解决ecshop新建页面分页问题
  9. 历经8年双11流量洗礼,淘宝开放平台架构和技术难点解密
  10. JVM Specification 9th Edition (4) Chapter 3. Compiling for the Java Virtual Machine
  11. Mac OS X 上用 otool 代替 ldd
  12. 浅谈弱网测试及QNET
  13. 【Java】用类描述计算机中CPU的速度和硬盘的容量,要求Java应用程序由4个类,名字分别是PC、CPU、HardDisk、和Test,其中Test是主类。
  14. C#里的InputBox
  15. 分布式 Git - 为项目做贡献
  16. CSS的特殊性 (specificity)
  17. Zend_Cache
  18. 已知两点和切线如何确定圆心和半径长度
  19. Java 模板引擎主流,freemaker ,thymeleaf (技术的介绍,重要是理解)(学成在线day04)
  20. 02-蘑菇街爬虫mw-sign参数破解

热门文章

  1. 智慧医疗信息系统HIS短信模板
  2. 这几个好用的去图片水印免费软件,你们知道吗?
  3. 化学结构式检索obabel
  4. 3.3 SQL---单表查询
  5. Signal ()函数用法和总结
  6. 奋斗吧,程序员——第二十七章 鸿雁在云鱼在水,惆怅此情难寄
  7. 用计算机0和1表达难舍之情,表达对友人的难舍和思念之情的诗句.
  8. word查找替换正则式的一例
  9. 【51单片机】键控流水灯
  10. 魔都要用最先进的神经网络预测交通?前排围观