|
可以简化为只记最基本的频率,高几个8度和低8度都用算法:
int music_array[8]={0,262,293,329,349,392,440,494};//基准音频率,第一个除开数组从0开始以便对应1-7的音
我多年前写的单片机激光琴程序代码片段:
- void Read_Laser(){//读竖横光柱以分析演奏者意图,play[i]的[0]是当前光柱值,[1-10]最后10次,11方向判断,12峰值位置,13峰值,14谷值位置,15谷值。程序段运行40毫秒左右,约30次/秒
- //starttime = millis();
- natural=analogRead(54);//54(A0)-正常无激光光柱时的环境光读数,无遮栏光值,暂时没想到用来做什么
- play[i][13]=0;play[i][15]=0;//本轮演奏最小值、最大值
- for(i=1;i<8;i++) { //为便于理解,使用1-7而不是0-6
- play[i][11]=0; //这个位置是累计增加次数用来判断方向,10都向同一方向则为10,都为相反为-10,否则为其他数
- tmp=analogRead(i+54);//55-61(A1-A7)竖光柱,//下面第3句读出8度到变量octave,默认优先最高音,因为下方有可能是衣袖挡住
- play_current=(abs(tmp<300)*(300-tmp)+tmp-abs(tmp>681)*(tmp-681)-300)/3;//用0-127表达手挡的多少,低于300算300,高于681算681,381除以3刚好映射为0-127
- octave=0;if (digitalRead(3)==HIGH) octave=1;if (digitalRead(4)==HIGH) octave=2;if (digitalRead(5)==HIGH) octave=3;if (digitalRead(6)==HIGH) octave=4;
- if(test>0) {//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<测试才有
- if(random(0,101)>0) octave=octave_last; else octave= random(0,5);//100分之1的机率产生随机数,100分之99与上次相同的8度
- }
- for(j=7;j>0;j--) { //记录下光柱变化,并保存到数组中,数组记录最后10次的值,此处的循环用于移动变量形成队列
- tmp=play[i][j]-play[i][j-1];//得出两数值之差,用于下一句的判断
- play[i][11]=play[i][11]+(tmp>0)-(tmp<0);//如果前面一个值比后面一个值大,则tmp为1,相同为0,较小为-1
- play[i][j]=play[i][j-1];//变量后移形成队列
- }
- if(play_current<play[i][13]) {play[i][12]=0;play[i][13]=play_current;} else play[i][12]++ ;//峰值随数组变动位置
- if(play_current>play[i][15]) {play[i][14]=0;play[i][15]=play_current;} else play[i][14]++ ;//谷值随数组变动位置
- play[i][0]=play_current;//55-61(A1-A7)竖光柱,54(A0)-环境光只在开机和适当的时候再读取,放入play[0][0]
- play_wave=play[i][14]-play[i][12];//峰值到谷值的频率为抖动频率
- //play_size=play[i][15]-play[i][13];//峰值到谷值的幅度为抖动幅度
- channel=36+octave*12+i*2-2-(i>3);//把第几根光柱和八度换算成12度音阶数值代码以便后续调用
- for(tmp=1;i<7;i++) if (play[tmp][1]>play_size) play_size=play[tmp][1];//最后几个数中的的最大值
- play_size=play_size*3;if(play_size>126) play_size=127;//音量最大只能127
- if (play_current<1) //-----------------------------------------满激光,表示手伸出去了,没有手掌或手指挡住-------------------------------------
- if (play[i][1]==0) for(j=15;j>0;j--) play[i][j]=0;//上次就已经是0 则消除所有记录重新来
- else{
- if (channel>35) {//有发声标志则发声,参数为光柱、8度、轻重、弯音<<<<<<<<<<<<<<唯一的发声入口
- mySerial.write(144);mySerial.write(channel);mySerial.write(play_size);
- }
- }
- else { //-----------------------------------------激光被阻挡,表示手伸进来了-------------------------------------
- //if(play[i][11]== 10);//10个变量都渐变增大此为入
- if(play[i][11]=7) {//最后7次都是向里面伸手,应该压低声音
- mySerial.write(128);mySerial.write(channel);mySerial.write(play_size);
- }
- else {//手已进来,但在抖动,应产生颤音
- //升半音或者降半音
- //跟随手指的抖动频率来升降半音
- }
- }
- octave_last=octave;
- } //Serial.print("Read_Laser: ");Serial.println(millis() - starttime); delay(1000);//计算执行一次的时间
- }
复制代码 |
|