这个效果主要是依靠processing实现的:利用processing的minim库分析音频,在通过Serial将处理好的数据传给Arduino,Arduino只要负责把收到的数据显示在led上就可以了。下面给大家分步再现一下做这个项目的过程。
[下载并安装processing]
“Processing是一种具有革命前瞻性的新兴计算机语言,它的概念是在电子艺术的环境下介绍程序语言,并将电子艺术的概念介绍给
程序设计师。它是Java 语言的延伸,并支持许多现有的 Java 语言架构,不过在语法 (syntax) 上简易许多,并具有许多贴心及人性化的设计。Processing 可以在 Windows、MACOS X、MAC OS 9 、Linux 等操作系统上使用。目前最新版本为Processing 3。以 Processing 完成的作品可在个人本机端作用,或以Java Applets 的模式外输至网络上发布。” ——度娘
[分析音频]
首先需要下载minim库。processing的库可以直接从编辑器中下载。速写本->引用库文件 -> 添加库文件,搜索minim,下载。
- import ddf.minim.*;
- import ddf.minim.analysis.*;
-
- Minim minim;
- AudioPlayer player;
- AudioMetaData meta;
- BeatDetect beat;
- int r = 200;
- void setup()
- {
- size(displayWidth, displayHeight);
- minim = new Minim(this);
- //change the address to your file
- player = minim.loadFile("D:/Cisum/Ragni MMS 2/Baby Doll - Ragini MMS 2 - [SongsPk.CC].mp3");
- meta = player.getMetaData();
- beat = new BeatDetect();
- player.play();
- background(-1);
- noCursor();
- }
-
- void draw()
- {
- beat.detect(player.mix);
- fill(#1A1F18, 20);
- noStroke();
- rect(0, 0, width, height);
- translate(width/2, height/2);
- noFill();
- fill(-1, 10);
- stroke(-1, 50);
- int bsize = player.bufferSize();
- for (int i = 0; i < bsize - 1; i+=5)
- {
- float x = (r)*cos(i*2*PI/bsize);
- float y = (r)*sin(i*2*PI/bsize);
- float x2 = (r + player.left.get(i)*100)*cos(i*2*PI/bsize);
- float y2 = (r + player.left.get(i)*100)*sin(i*2*PI/bsize);
- line(x, y, x2, y2);
- }
- beginShape();
- noFill();
- stroke(-1, 50);
- for (int i = 0; i < bsize; i+=30)
- {
- float x2 = (r + player.left.get(i)*100)*cos(i*2*PI/bsize);
- float y2 = (r + player.left.get(i)*100)*sin(i*2*PI/bsize);
- vertex(x2, y2);
- pushStyle();
- stroke(-1);
- strokeWeight(2);
- point(x2, y2);
- popStyle();
- }
- endShape();
- }
复制代码
[处理数据]
将1024分别按照灯环的LED数量分成12、16、24份,并应用player.left.get()读取缓冲区里的值,这个值是介于-1到1之间,然而在这里我们只需要整数,原因会在下个步骤中详述。
- for (int i = 0; i < 12; i++)
- {
- int x = int(players.left.get(i*ring3)*average);
- if (x < 0) x = -x;
- if (x > 100) x = 100;
- rings[i] = x;
- }
-
- for (int i = 12; i < 28; i++)
- {
- int x = int(players.right.get((i-12)*ring4)*average);
- if (x < 0) x = -x;
- if (x < 40) x = 0;
- else x = x - 40;
- if (x > 100) x = 60;
- rings[i] = x;
- }
-
- for (int i = 28; i < 52; i++)
- {
- int x = int(players.left.get((i-28)*ring5)*average);
- if (x < 0) x = -x;
- if (x < 90) x = 0;
- else x = x - 90;
- if (x > 100) x = 10;
- rings[i] = x;
- }
复制代码
[传给Arduino]
Processing和arduino的交互主要有两种方法,一种是Serial,一种是Firmata。Firmata更适用于少量数据的交互,更适用于在arduino里没有引用库文件的情况。只需要给arduino烧录standard firmata这个程序,在processing端引用arduino库,就可以直接控制arduino了。而这个音乐led用的是Serial,就是通过串口在processing端用write()输出数据,用read()读取数据。Arduino连接在电脑不同的端口上,StringportName = Serial.list()[0]中的“0”也要根据端口进行变化,可以把整个Serial.list()[]都打印出来找到自己需要的那一个。Serial只可以传整数,所以在上一个步骤中进行了int x =int(players.right.get((i-12)*ring4)*average);取整处理。经过测试发现发出的数据和接收的数据之间有错位的现象所以增加了数列中第53个数:rings[52] = 255;用来矫正。
- import processing.serial.*;
- Serial myport;
- void setup(){
- String portName = Serial.list()[0];
- myport = new Serial(this, portName, 9600);
- }
-
- void draw(){
- rings[52] = 255;
- for (int i = 0; i < 53; i++) {
- myport.write(rings[i]);
- }
- }
复制代码
[接收并显示数据]
引用三个库用来控制灯环上led的亮度以及颜色,用ring[]来储存接收到的数据,用255/200=1.275用来校准。采用hsb的色彩模式,三个参数分别为色相,饱和度以及亮度,色相在0-360之间渐变,收到的数据用来调整亮度
- #include <Adafruit_NeoPixel.h>//the library can be found at https://github.com/adafruit/Adafruit_NeoPixel
- #include <DFRobot_utility.h>
- #include <Metro.h>
-
- #define PIN3 10
- #define PIN4 9
- #define PIN5 8
-
- Adafruit_NeoPixel round3 = Adafruit_NeoPixel(12, PIN3, NEO_GRB + NEO_KHZ800);
- Adafruit_NeoPixel round4 = Adafruit_NeoPixel(16, PIN4, NEO_GRB + NEO_KHZ800);
- Adafruit_NeoPixel round5 = Adafruit_NeoPixel(24, PIN5, NEO_GRB + NEO_KHZ800);
-
- float ring[53];
- int color = 0;
-
- void setup() {
- // put your setup code here, to run once:
- Serial.begin(9600);
- round3.begin();
- round4.begin();
- round5.begin();
- for (int j = 0; j < 52; j++) {
- ring[j] = 0;
- }
- round3.show();
- round4.show();
- round5.show();
- }
-
- void loop() {
- // put your main code here, to run repeatedly:
- if (Serial.available()) {
- if (color == 360) color = 0;
- static int i = 0;
-
- if (i == 52) i = 0;
-
- ring[i] = Serial.read();
- ring[i] = ring[i]/200;
- if (ring[i] == 1.275) {
- ring[52] = 1.275;
- i = 0;
- return;
- }
- if (i < 12) {
- round3.setPixelColor(i, hsbToColor (color, 1, ring[i]));
- } else if (i < 28) {
- round4.setPixelColor(i - 12, hsbToColor (color, 1, ring[i]));
- } else if (i < 52){
- round5.setPixelColor(i - 28, hsbToColor (color, 1, ring[i]));
- }
- if (i == 12) round3.show();
- if (i == 28) round4.show();
- if (i == 51) {
- round5.show();
- color += 5;
- }
- i ++;
- }
- }
复制代码
[添加播放器功能,多首歌曲]
主要是把之前的主要功能提出来,写成一个function,再加上void keyPressed()读取键盘的输入。
- AudioPlayer players;
- AudioPlayer player;
- AudioPlayer player1;
- int playernum=0;
- void setup(){
- player = minim.loadFile("I Really Like You.mp3", 1024);
- player1 = minim.loadFile("TFBOYS.mp3", 1024);
- }
- void draw(){
- switch (playernum) {
- case 0:
- drawLED (player);
- break;
- case 1:
- drawLED (player1);
- break;
- }
- }
-
- void keyPressed() {
- if (key==ESC) {
- myport.write(255);
- exit();
- }
- if (key == ' ') {
- if (pause) pause = false;
- else pause = true;
- }
- if (key == CODED) {
- if (keyCode == DOWN) {
- pause();
- playernum ++;
- }
- if (keyCode == UP) {
- pause();
- playernum --;
- }
- }
- }
-
- void pause() {
- switch (playernum) {
- case 0:
- player.pause();
- break;
- case 1:
- player1.pause();
- break;
- }
- }
-
- void drawLED(AudioPlayer players) {
- ……
- }
复制代码
程序都是一部分一部分介绍的,可能会有些小遗落,完整版的在这里: