眉山东坡论坛

 找回密码
 注册账号

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 74587|回复: 25
收起左侧

[原创作品] 用Arduino自制发颤音的DIY激光琴

[复制链接]
发表于 2014-10-25 08:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转东坡论坛

您需要 登录 才可以下载或查看,没有帐号?注册账号

x
最终外观如下,最终程序在七楼:


111.jpg


演奏视频因音乐功底不够,大家就别吐槽了:



预先画好琴弓样子和底座

2.JPG


 楼主| 发表于 2014-10-25 08:23 | 显示全部楼层
本帖最后由 hmke 于 2015-10-5 14:11 编辑

画好电路图:

2.jpg

1.jpg


买材料:亚克力板(厂家预割好)、亚克力粘胶、美工螺钉、定做木板、激光头、相关电子元件等
组装

IMG_20141024_025675.jpg IMG_20141024_025415.JPG

 楼主| 发表于 2014-10-25 08:25 | 显示全部楼层

测试 2014-10-22_20-46-45_746.jpg 2014-10-23_07-14-48_685.jpg 2014-10-23_07-14-21_342.jpg 2014-10-22_20-46-27_506.jpg 2014-10-22_20-45-34_472.jpg 2014-10-23_07-15-31_612.jpg 2014-10-23_07-15-59_222.jpg
发表于 2014-10-25 09:09 | 显示全部楼层
hmke做的东西还不错哈,只是稍专业了点,  此贴如果大众一些,完全可以加精的

点评

现在还是半成品,等有时间的时候继续弄完。  详情 回复 发表于 2014-10-25 09:22
 楼主| 发表于 2014-10-25 09:22 | 显示全部楼层
快来参与金币拍卖吧
东坡论坛管理员 发表于 2014-10-25 09:09
hmke做的东西还不错哈,只是稍专业了点,  此贴如果大众一些,完全可以加精的

现在还是半成品,正在继续不要离开.............

本楼层为测试楼层,非最终程序
测试颤音和声音渐淡部分,从5脚接了一根电阻到8050三极管,三极管基极对地接2.2u电解,再从三极管射极接喇叭,喇叭另一脚接4号端口。
其中发现这次购买的Arduino板子,3脚不能用来做PWM输出只能用5脚来做,真奇怪。


  1. int old_sound=0,tmp1,tmp2,time_long=1,laser=0,sound_hz;
  2. int key[]={0,0,0,0,0,0,0};
  3. int music[3][7]={
  4.   {262,293,329,349,392,440,494  } ,
  5.   {523,586,658,697,783,879,987  } ,
  6.   {1045,1171,1316,1393,1563,1755,1971  }};
  7. void setup() {               
  8.   Serial.begin(9600);
  9.   pinMode(4,OUTPUT);pinMode(5,OUTPUT);pinMode(6,INPUT);pinMode(7,INPUT);pinMode(8,INPUT);
  10.   pinMode(9,INPUT);pinMode(10,INPUT);pinMode(11,INPUT);pinMode(12,INPUT);
  11. }
  12. void play(int hz,int high){
  13.   double tmp3;
  14.   old_sound=music[high][hz];
  15.   tmp1=old_sound/100;//振幅
  16.   for (int i=1;i<255;i++){//100为弹一个音至少多长
  17.     //tmp3=old_sound+sin(3.1415*2*i/30)*old_sound/10
  18.     tmp3=sin(i*0.2)*tmp1+old_sound;//颤音数据
  19.     sound(tmp3);
  20.     tmp2=map(i,1,255,35,23);
  21.     analogWrite(5,tmp2);//把循环中的变量兑换成音量控制
  22.     //delay(2);
  23.     Serial.println(tmp3);
  24.   }
  25.   noTone(4);
  26. }
  27. void sound(int sound_hz){
  28.   tone(4,sound_hz);
  29. }
  30. void loop() {
  31.   for (int i=0;i<8;i++){key[i]=digitalRead(i+6);}
  32.   //int n =digitalRead(4);
  33.   play(0,0);
  34.   play(1,1);
  35.   play(2,1);
  36.   play(3,1);
  37.   play(4,1);
  38.   play(0,1);
  39.   delay(1000);
  40.   while(1);
  41. }

复制代码


改进一下结构:

QQ图片20150417152404.jpg
 楼主| 发表于 2014-12-10 17:45 | 显示全部楼层
本楼层为测试楼层,非最终程序
本来想同时发出七个音的,结果发现只要同时发出两个音就已经很别扭了,想来一是处理器的处理速度限制,二是声频曲线无使用两个频率形成包络线样式。没办法,暂时只有在有发音时把之前正在进行的发声关闭掉。
从6-12脚总共7端口用来做七根线的判断,从端口接47K电阻到+5V电压,以低电平为判断是否光柱被挡住,调试到延迟颤音长短部分:

  1. int music8,i,sum,tmp1,tmp2,laser=0;
  2. int tmp[7]={0,0,0,0,0,0,0};
  3. int key[7]={0,0,0,0,0,0,0};
  4. int music[3][7]={
  5.   {262,293,329,349,392,440,494  } ,
  6.   {523,586,658,697,783,879,987  } ,
  7.   {1045,1171,1316,1393,1563,1755,1971  }};
  8. void setup() {               
  9.   Serial.begin(9600);     //使用9600的波特率进行串口通讯
  10.   pinMode(4,OUTPUT);pinMode(5,OUTPUT);pinMode(6,INPUT);pinMode(7,INPUT);
  11.   pinMode(8,INPUT); pinMode(9,INPUT);pinMode(10,INPUT);pinMode(11,INPUT);
  12.   pinMode(12,INPUT);pinMode(12,INPUT);
  13. }
  14. void loop() {
  15.     music8=0;
  16.     if (digitalRead(laser+6) == 0) tmp[laser]=1;  //从头开始
  17.     if (tmp[laser] > 0) { //即使这时没有激活光柱,也延长一段时间
  18.       tmp1=music[music8][laser];
  19.       tmp1=tmp1+sin(0.35*tmp[laser])*tmp1/100;//tmp1=tmp1+sin(3.1415*2*tmp[laser]/18)*tmp1/100;(hz/100 is 振幅)
  20.       tone(4,tmp1);
  21. Serial.print(sum);
  22. Serial.println(":");
  23.       delay(10/(sum+1));
  24.       tmp[laser]=tmp[laser]+1;
  25.       if (tmp[laser]>100) tmp[laser]=0;
  26.     }
  27.     for (int i=0;i<7;i++) {
  28.        sum=0;
  29.        sum=sum+(tmp[i]>0);
  30.     }
  31.     //Serial.println(sum);
  32.     if (sum==0) noTone(4);
  33.     laser++;if (laser > 6) laser=0; //下次查下一只激光管,循环检测
  34. }
复制代码


 楼主| 发表于 2014-12-20 09:56 | 显示全部楼层
本帖最后由 hmke 于 2015-4-17 16:10 编辑

最终程序

根据初始光敏电阻赋值为低电平,被挡后呈高电平特性,把前面测试时的高低电平翻转了一下得出改进后得出,其中,声音输出做了更改不再使用外置三级管、电阻,直接使用2号端口与A5号端口之间来引出,如果只使用2号口引出的声音则没有渐弱的功能。
接口分配
2、A5:2号口为输出到光藕输入,光藕输入的地端接A5口并串联一个2000uf的电容再接地,意思很明确:A5口出来的信号大小用来举高光藕信号使其模拟出渐弱的效果。
5V、GND、USB:接电源(使用了计算机电源),并引出了USB口外接到外USB,以便接入调试
6-12:接七根5528光敏电阻,用于检测激光反射回来的光,一旦光被拦断,表示这个音被弹奏,光敏电阻另一端接地。
A1-A4(程序中命名为15-18):接4根5528光敏电阻,接收横向光柱以检测哪个八度音被弹奏,光敏电阻另一端接地。

七根绿光激光柱和四根横向红光激光管组成光网用于检测手所处的位置。做出的激光琴如果是大型的,则需要外接功放把声音进一步放大。

程序如下:



  1. int octave=0,old_octave,i,tmp1,laser=0,v1,v2,v3,v4,v_1,v_2,v_3,v_4;//定义所使用的变量:音乐频率倍率、倍率存档、临时变量等
  2. int tmp[7]={0,0,0,0,0,0,0};//临时变量,用于在循环中产生颤音用
  3. int music[7]={262,293,329,349,392,440,494};//基准音频率
  4. int old_music[7]={0,0,0,0,0,0,0};//记住每一次弹奏时的频率
  5. int mus_long[7]={127,127,127,127,127,127,127};
  6. void setup() { pinMode(2,OUTPUT);pinMode(5,OUTPUT);//Serial.begin(9600); //标记第2号口为输出音频到喇叭的数据口
  7.   pinMode(6,INPUT_PULLUP);pinMode(7,INPUT_PULLUP);pinMode(8,INPUT_PULLUP);//标记6-12为七个基准音(竖光柱)
  8.   pinMode(9,INPUT_PULLUP);pinMode(10,INPUT_PULLUP);pinMode(11,INPUT_PULLUP);
  9.   pinMode(12,INPUT_PULLUP);pinMode(15,INPUT_PULLUP);pinMode(16,INPUT_PULLUP);//标记15-17为八度(横光柱)
  10.   pinMode(17,INPUT_PULLUP);pinMode(18,INPUT_PULLUP);
  11.   for (i=0;i<7;i++) {//自检第1-7根竖光柱
  12.       if (digitalRead(i+6)==HIGH) {//如果有光柱没有光
  13.            for (tmp1=0;tmp1<=i;tmp1++) {//循环这根光柱的号码次数
  14.                analogWrite(5,0);
  15.                tone(2,music[i],30);delay(80); //发出光柱对应频率的声音30毫秒,并停50毫秒(据上一句会响号码声音的次数示警)
  16.            }
  17.            music[i]=-1;//把光柱做出错标记,以便后面用music[laser]>0来判断
  18.        }
  19.        delay(300);//延迟300毫秒
  20.   }
  21.   analogWrite(5,0);
  22.   v_1=analogRead(A1);v_2=analogRead(A2);v_3=analogRead(A3);v_4=analogRead(A4);//读取四根表示八度的横向激光
  23.       //Serial.print("V1-4=:");Serial.print(v_1);Serial.print(":");Serial.print(v_2);Serial.print(":");Serial.print(v_3);Serial.print(":");Serial.println(v_4);
  24.       if (v_1>1020) {analogWrite(5,0);tone(2,1200,30);delay(50);tone(2,1200,30);delay(50);} //自检1
  25.       if (v_2>1020) {analogWrite(5,0);tone(2,1500,30);delay(50);tone(2,1500,30);delay(50);} //自检2
  26.       if (v_3>1020) {analogWrite(5,0);tone(2,1900,30);delay(50);tone(2,1900,30);delay(50);} //自检3
  27.       if (v_4>1020) {analogWrite(5,0);tone(2,2300,30);delay(50);tone(2,2300,30);delay(50);} //自检4
  28.   for (i=1;i<999;i++) {analogWrite(5,0);tone(2,i*20,80);}//停止发声
  29. }//==============================上面为初始化,下面为程序正式开始=======================================

  30. void loop() {//注:每根八度被挡时,均可能会影响到其他几个八度的电压值,所以只能取变化最大的一根
  31.     tmp1=0;octave=0;//八度置零,电压变化最大值置零
  32.     v1=analogRead(A1)-v_1;if (v1<50) {v1=0;} else {tmp1=v1;octave=1;}//检测横向激光接收管电压,如有变化则记录到tmp1,octave为音乐频率倍率关系
  33.     v2=analogRead(A2)-v_2;if (v2<50) {v2=0;} else if (v2>tmp1) {tmp1=v2;octave=2;}//检测第二根激光管,如果大于tmp1则替换变量、八度(音乐频率)翻倍
  34.     v3=analogRead(A3)-v_3;if (v3<50) {v3=0;} else if (v3>tmp1) {tmp1=v3;octave=4;}//检测第三根激光管,同上,
  35.     v4=analogRead(A4)-v_4;if (v4<50) {v4=0;} else if (v4>tmp1) {tmp1=v4;octave=8;}//检测第四根激光管,同上,
  36.     //if (octave >0 ) {Serial.print(v1);Serial.print("-");Serial.print(v2);Serial.print("-");Serial.print(v3);Serial.print("-");Serial.print(v4);Serial.print("-");Serial.println(octave); }
  37.     if (digitalRead(laser+6)== HIGH and music[laser]>0 and octave>0) { //如果第laser根光柱被档(高电平),且本光柱未出错,且有八度音也被挡
  38.         if (octave!=old_octave) tmp[laser]=0;//只要八度被改变,当成是重新发一个音(有时是同一竖光柱不同八度)
  39.         if (tmp[laser]==0 ) {//如果本光柱不是上次挡住的光柱(之前被置零) if (tmp[laser]==0 or octave!=old_octave)
  40.             tmp[0]=0;tmp[1]=0;tmp[2]=0;tmp[3]=0;tmp[4]=0;tmp[5]=0;tmp[6]=0;//清空所有光柱发声标志
  41.             mus_long[0]=127;mus_long[1]=127;mus_long[2]=127;mus_long[3]=127;//其他光柱声长标志复位
  42.             mus_long[4]=127;mus_long[5]=127;mus_long[6]=127;
  43.             tmp[laser]=1;//上一句关掉其他音,置本光柱开始发声标志
  44.         } else if (tmp[laser]>40) mus_long[laser]=tmp[laser]+127;//是重复挡本光柱则音长加127
  45.     }
  46.     if (tmp[laser] > 0){ //如果该发声,即使这时没有激活光柱,也延长一段时间
  47.         if (tmp[laser]==1) {old_music[laser]=music[laser]*octave;  old_octave=octave;}//赋值初始音准;备份发声的这个八度以便后面判断
  48.         tmp1=old_music[laser]; //取出频率值Hz数
  49.         tmp1=tmp1+sin(0.35*tmp[laser])*tmp1/100;//sin(3.1415*2*tmp[laser]/18)*tmp1/100;
  50.         if (music[laser]>0) {
  51.             analogWrite(5,(127+tmp[laser]-mus_long[laser])*2);//把音长距离结束的时间兑换成音量控制模拟音量渐弱
  52.             tone(2,tmp1); //如果本光柱无故障则发出由上一句产生的颤音,(hz/100 是振幅)
  53.             delay(7); //本句延迟5毫秒,上一句中传出的模拟量程为0-255,但延时数量最大为100,但此数段应实测
  54.             }else delay(8);//不该发声时应延迟更久一些毫秒数
  55.         tmp[laser]=tmp[laser]+1;//准备检查下一根光柱
  56.         if (tmp[laser]>mus_long[laser]) {//如延时完成则
  57.             tmp[laser]=0;mus_long[laser]=127;//则本光柱清零、音长清零
  58.             noTone(2);//停止发声
  59.         }
  60.     }
  61.     laser++;if (laser > 6) laser=0; //下次查下一只激光管,循环检测
  62. }

复制代码

发表于 2014-12-20 10:27 | 显示全部楼层
完全看不懂。。。好高端,能不能告诉我,这个琴做好后,怎么弹

点评

用手挡住1-7根光柱,就会发出1-7的音,按上下分了三档共3个8度高阶。  详情 回复 发表于 2014-12-20 16:48
 楼主| 发表于 2014-12-20 16:48 | 显示全部楼层
本帖最后由 hmke 于 2015-4-17 16:12 编辑
恰恰 发表于 2014-12-20 10:27
完全看不懂。。。好高端,能不能告诉我,这个琴做好后,怎么弹

用手挡住1-7根光柱,就会发出1-7的音,按上下分了三档共3个8度高阶。


至此,程序部分调试完毕,经测试在UNO和Arduino nano V3上未删改通过:
IMG_20150126_160303.jpg IMG_20150126_160223.jpg IMG_20150126_160249.jpg

测试时其中使用到的电阻,左边的是一百多K,右边的是47K,用多大阻值并没有多大关系,只是耗电多少吧,能把电压拉动就行。
在实际运用时因光敏电阻的阻值不同,需要使用比光敏电阻大一些的电阻,比如我使用的光阻在无激光照着的时候每一批(接7根绿光柱)接近约是500K-1M,我就使用的200K的电阻,八度音这边使用的光阻未接光时约70K,我就只好弄个47K的。

前面有爱好者提出,复制程序过去时出现声音不连续,那可能是你嵌入了你自己的句子,总的来说是处理速度不够造成的,剔除那些语句后就可以更流畅了。


 楼主| 发表于 2014-12-21 11:45 | 显示全部楼层
本帖最后由 hmke 于 2014-12-21 11:53 编辑

转一个贴子过来,便于后期更改音效:





让arduino发声可以给项目增色不少,但产生音频不是arduino的长处。最常用的tone函数用PWM法生成方波脉冲刺耳而且单调,播放从SD卡读取的声音文件效果改善不少,但不能即兴产生效果音。这里介绍一种声音合成的算法,粒子合成法(Granular synthesis)。这个算法是产生一些短到刚好能分辨的声音小片段(声音粒子grain),然后对它们组合重排处理,在输入多变的参数时,可以形成丰满丰富的连续声音。
下面这个声音程序使用了粒子合成算法,它使用了预先算好的几个函数表,并直接操作寄存器,以节约运算时间。一共五个可以变化的输入参数,这里使用五个电位器来调节这些参数,实际运用时可以从各种传感器或预先编排的数据来作为参数输入,可以获得千变万化的音响效果。

硬件接法是五个电位器的两端都分别接Vcc和地,中间的活动端接arduino的A0到A4五个模拟口。音频信号在D3数字口输出(328芯片的板子),建议用8欧喇叭串联一个10uF的电容再接在输出口和地之间。下载程序运行后,通过调节这五个电位器来产生音效。



  1. // Auduino, the Lo-Fi granular synthesiser
  2. //
  3. // by Peter Knight, Tinker.it [url=http://jump.bdimg.com/safecheck/index?url=x+Z5mMbGPAvVbKP6NnNEManOcwEFTkHLIdHGT0yJuzYBMXTVXr+WbLwbDQ82+mlRsmc3ckXrBvk9ckzEAWbje2U74oDVAV+EK8ICzr0oohZ2Pah4egTNMg==][color=#0066cc]http://tinker.it[/color][/url]
  4. //
  5. // Help:       [url=http://jump.bdimg.com/safecheck/index?url=x+Z5mMbGPAsxDJaYzO8o1o+OCxuYYkjHF0BYrUCVRu/VbKP6NnNEMX33uQZ1xk1eYuwsTAQXQbvZSOmE6a5HeCHRxk9Mibs2ATF01V6/lmy8Gw0PNvppUbJnN3JF6wb5PXJMxAFm43tlO+KA1QFfhCvCAs69KKIWdj2oeHoEzTI=][color=#0066cc]http://code.google.com/p/tinkerit/wiki/Auduino[/color][/url]
  6. // More help: [url=http://jump.bdimg.com/safecheck/index?url=x+Z5mMbGPAvxrar22FaBrn6pQajRo6u6LZ1PQxgdYHjxrar22FaBrqYVsVIUIXkGNJ9+Xxlc3nU6w7spl8AiP6CmyydkgKRRAoS6PawD4UIqG1zcxP3VcsFqpvT1/f7Y4ER4w++mYwpWZhAVR4xoKDA8Zu4mdgY0][color=#0066cc]http://groups.google.com/group/auduino[/color][/url]
  7. //
  8. // Analog in 0: Grain 1 pitch
  9. // Analog in 1: Grain 2 decay
  10. // Analog in 2: Grain 1 decay
  11. // Analog in 3: Grain 2 pitch
  12. // Analog in 4: Grain repetition frequency
  13. //
  14. // Digital 3: Audio out (Digital 11 on ATmega8)
  15. //
  16. // Changelog:
  17. // 19 Nov 2008: Added support for ATmega8 boards
  18. // 21 Mar 2009: Added support for ATmega328 boards
  19. // 7 Apr 2009: Fixed interrupt vector for ATmega328 boards
  20. // 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)

  21. #include <avr/io.h>
  22. #include <avr/interrupt.h>

  23. uint16_t syncPhaseAcc;
  24. uint16_t syncPhaseInc;
  25. uint16_t grainPhaseAcc;
  26. uint16_t grainPhaseInc;
  27. uint16_t grainAmp;
  28. uint8_t grainDecay;
  29. uint16_t grain2PhaseAcc;
  30. uint16_t grain2PhaseInc;
  31. uint16_t grain2Amp;
  32. uint8_t grain2Decay;

  33. // Map Analogue channels
  34. #define SYNC_CONTROL          (4)
  35. #define GRAIN_FREQ_CONTROL    (0)
  36. #define GRAIN_DECAY_CONTROL  (2)
  37. #define GRAIN2_FREQ_CONTROL  (3)
  38. #define GRAIN2_DECAY_CONTROL (1)


  39. // Changing these will also requires rewriting audioOn()

  40. #if defined(__AVR_ATmega8__)
  41. //
  42. // On old ATmega8 boards.
  43. //     Output is on pin 11
  44. //
  45. #define LED_PIN        13
  46. #define LED_PORT       PORTB
  47. #define LED_BIT        5
  48. #define PWM_PIN        11
  49. #define PWM_VALUE      OCR2
  50. #define PWM_INTERRUPT TIMER2_OVF_vect
  51. #elif defined(__AVR_ATmega1280__)
  52. //
  53. // On the Arduino Mega
  54. //     Output is on pin 3
  55. //
  56. #define LED_PIN        13
  57. #define LED_PORT       PORTB
  58. #define LED_BIT        7
  59. #define PWM_PIN        3
  60. #define PWM_VALUE      OCR3C
  61. #define PWM_INTERRUPT TIMER3_OVF_vect
  62. #else
  63. //
  64. // For modern ATmega168 and ATmega328 boards
  65. //     Output is on pin 3
  66. //
  67. #define PWM_PIN        3
  68. #define PWM_VALUE      OCR2B
  69. #define LED_PIN        13
  70. #define LED_PORT       PORTB
  71. #define LED_BIT        5
  72. #define PWM_INTERRUPT TIMER2_OVF_vect
  73. #endif

  74. // Smooth logarithmic mapping
  75. //
  76. uint16_t antilogTable[] = {
  77.   64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,
  78.   54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,
  79.   45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,
  80.   38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768
  81. };
  82. uint16_t mapPhaseInc(uint16_t input) {
  83.   return (antilogTable[input & 0x3f]) >> (input >> 6);
  84. }

  85. // Stepped chromatic mapping
  86. //
  87. uint16_t midiTable[] = {
  88. 17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,
  89.   77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,
  90.   244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,
  91.   732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,
  92.   1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,
  93.   4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,
  94.   10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,
  95.   22121,23436,24830,26306
  96. };
  97. uint16_t mapMidi(uint16_t input) {
  98.   return (midiTable[(1023-input) >> 3]);
  99. }

  100. // Stepped Pentatonic mapping
  101. //
  102. uint16_t pentatonicTable[54] = {
  103.   0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,
  104.   411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,
  105.   3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306
  106. };

  107. uint16_t mapPentatonic(uint16_t input) {
  108.   uint8_t value = (1023-input) / (1024/53);
  109.   return (pentatonicTable[value]);
  110. }


  111. void audioOn() {
  112. #if defined(__AVR_ATmega8__)
  113.   // ATmega8 has different registers
  114.   TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
  115.   TIMSK = _BV(TOIE2);
  116. #elif defined(__AVR_ATmega1280__)
  117.   TCCR3A = _BV(COM3C1) | _BV(WGM30);
  118.   TCCR3B = _BV(CS30);
  119.   TIMSK3 = _BV(TOIE3);
  120. #else
  121.   // Set up PWM to 31.25kHz, phase accurate
  122.   TCCR2A = _BV(COM2B1) | _BV(WGM20);
  123.   TCCR2B = _BV(CS20);
  124.   TIMSK2 = _BV(TOIE2);
  125. #endif
  126. }


  127. void setup() {
  128.   pinMode(PWM_PIN,OUTPUT);
  129.   audioOn();
  130.   pinMode(LED_PIN,OUTPUT);
  131. }

  132. void loop() {
  133.   // The loop is pretty simple - it just updates the parameters for the oscillators.
  134.   //
  135.   // Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
  136.   // They will cause clicks and poops in the audio.

  137.   // Smooth frequency mapping
  138.   //syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;

  139.   // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
  140.   //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));

  141.   // Stepped pentatonic mapping: D, E, G, A, B
  142.   syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));

  143.   grainPhaseInc  = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
  144.   grainDecay      = analogRead(GRAIN_DECAY_CONTROL) / 8;
  145.   grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
  146.   grain2Decay     = analogRead(GRAIN2_DECAY_CONTROL) / 4;
  147. }

  148. SIGNAL(PWM_INTERRUPT)
  149. {
  150.   uint8_t value;
  151.   uint16_t output;

  152.   syncPhaseAcc += syncPhaseInc;
  153.   if (syncPhaseAcc < syncPhaseInc) {
  154.      // Time to start the next grain
  155.      grainPhaseAcc = 0;
  156.      grainAmp = 0x7fff;
  157.      grain2PhaseAcc = 0;
  158.      grain2Amp = 0x7fff;
  159.      LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite
  160.   }

  161.   // Increment the phase of the grain oscillators
  162.   grainPhaseAcc += grainPhaseInc;
  163.   grain2PhaseAcc += grain2PhaseInc;

  164.   // Convert phase into a triangle wave
  165.   value = (grainPhaseAcc >> 7) & 0xff;
  166.   if (grainPhaseAcc & 0x8000) value = ~value;
  167.   // Multiply by current grain amplitude to get sample
  168.   output = value * (grainAmp >> 8);

  169.   // Repeat for second grain
  170.   value = (grain2PhaseAcc >> 7) & 0xff;
  171.   if (grain2PhaseAcc & 0x8000) value = ~value;
  172.   output += value * (grain2Amp >> 8);

  173.   // Make the grain amplitudes decay by a factor every sample (exponential decay)
  174.   grainAmp -= (grainAmp >> 8) * grainDecay;
  175.   grain2Amp -= (grain2Amp >> 8) * grain2Decay;

  176.   // Scale output to the available range, clipping if necessary
  177.   output >>= 9;
  178.   if (output > 255) output = 255;

  179.   // Output to PWM (this is faster than using analogWrite)  
  180.   PWM_VALUE = output;
  181. }

复制代码

您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

QQ|网站地图|关于我们|小黑屋|爱好群|眉山东坡论坛 ( 蜀ICP备05001993号-1 )

GMT+8, 2018-1-20 10:47

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表