7483浏览
查看: 7483|回复: 0

[进阶] arduino与MIDI控制器

[复制链接]
前言:作为一个吉他手和伪电工以及穷苦人民,一直喜欢使用吉他软件效果器(如amplitube3),以及其他的DAW(数字音频工作站)软件 ,这些音乐软件都有一个十分普及的通讯协议--MIDI通讯协议,各种midi键盘、midi控制发出的midi信号可以对软件进行全方位的控制,midi外设在演出中特别重要

电吉他演奏中,常常需要对于效果器的参数进行控制,例如最常用的哇音踏板,就是通过脚踩踏板输出不同的音色效果。手头正好有一个超声波测距模块,再看了叉叉同志的超声波测距贴,就打算用超声波测量手的运动之后输出midi信号去控制效果器软件。
首先去研究了midi协议

MIDI(Musical Instrument Digital Interface)乐器数字接口 ,是20 世纪80 年代初为解决电声乐器之间的通信问题而提出的。MIDI 传输的不是声音信号, 而是音符、控制参数等指令, 它指示MIDI 设备要做什么,怎么做, 如演奏哪个音符、多大音量等。它们被统一表示成MIDI 消息(MIDI Message) 。传输时采用异步串行通信, 标准通信波特率为31.25×( 1±0.01) KBaud。

MIDI文件有很多信息构成的指令。一些信息,只由1字节构成,有些有2个字节,还有一些有3个字节。有一类的MIDI信息,甚至可以包含无限的字节数。所有的信息有一点是共同的,那就是第一个字节的信息是状态。
状态字节的0x80到0xef是可以在16个MIDI通道的任何一个出现的信息。正因为如此,这些是所谓的声音信息。这些状态字节有8位二进制数,可以把8个二进制位分成两个 4位,即一个高位和一个低位 。例如,一个状态字节的0x92可细分成9 (高位 )和2 (低位 ) 。高位告诉你是什么类型的MIDI信息,低位说明信息操作的MIDI通道序号。以下是所有可能的高位值,每个代表的声音信息类型:
8 =停止发声
9 =开始发声
a =轮指
b =改变控制器
c =改变音色
d =通道演奏压力(可近似认为是音量)
e =音高


看得出,midi只是一个简单的串行通讯而已,主要问题就是要解决传输数据是什么,在网上没有太多的信息,就直接使用了一个midi监视器,研究了一下我原来的一个midi控制器的midi数据


第一个数据 B0   B是控制器,0代表通道,后一位数据 07 还是通道 最后data2 是控制器的值,在0到127之间,这里就打算使用超声波测距的数据送到data2的midi信号。

然后开始搞程序....

  1. #define LED 13    // LED pin on Arduino board
  2. #define switch1 10              // 1st Switch
  3. #define switch2 6               // 2nd Switch
  4. #define MIDI_COMMAND_CONTROL_CHANGE 0xB0
  5. #define MIDI_COMMAND_NOTE_ON 0x90
  6. #define MIDI_COMMAND_NOTE_OF 0x80
  7. //Variables
  8. int switch1LastState = 0;
  9. int switch1CurrentState = 0;
  10. int switch2LastState = 0;
  11. int switch2CurrentState = 0;
  12.                                                                      //照抄了叉叉同学的帖子
  13. int ssgnd = 5; //首先为了程序看着方便,定义若干针脚。gnd是5, echo是4, trig 是3, vcc是2.
  14. int ssecho = 4;
  15. int sstrig = 3;
  16. int ssvcc = 2;
  17. //前缀ss是supersonic的意思,不加也可以。
  18. void setup() {               
  19.   pinMode(ssgnd,OUTPUT);
  20.   pinMode(sstrig,OUTPUT);
  21.   pinMode(ssecho,INPUT); //除了echo脚设成输入模式(INPUT),其他都设为输出模式。因为我们要检测echo脚的电平变化,所以设成输入模式。
  22.   pinMode(ssvcc,OUTPUT);
  23.   pinMode(LED, OUTPUT);
  24.   pinMode(switch1,INPUT);
  25.   pinMode(switch2, INPUT);
  26.   Serial.begin(31250);
  27.   
  28.   blinkLed(3);
  29.   digitalWrite(ssvcc,HIGH);
  30.   }
  31.   
  32. int supersonicread()
  33. //这里我定义了一个函数,叫做supersonicread,顾名思义就是读取超声波传感器数值。
  34. //函数前面的int表示这个函数返回一个整数。
  35. {
  36.    digitalWrite(sstrig,HIGH); // 将trig脚电压设为高
  37.   digitalWrite(sstrig,LOW); //将trig脚电压设为低,这样就向模块发送了一个信号,让模块发射超声波。
  38.   int echotime=pulseIn(ssecho,HIGH);
  39.    return echotime; //return 命令 表示把一个数作为这个函数的返回值。我们把echotime,也就是回声时间,作为这个函数返回的数值。
  40. }
  41. // the format of the message to send Via serial
  42. typedef union {
  43.     struct {
  44. uint8_t command;
  45. uint8_t channel;
  46. uint8_t data2;
  47. uint8_t data3;
  48.     } msg;
  49.     uint8_t raw[4];
  50. } t_midiMsg;
  51. void blinkLed(byte num) {  // 当有midi数据发送的时候led闪烁
  52.   for (byte i=0;i<num;i++) {
  53.     digitalWrite(LED,HIGH);
  54.     delay(50);
  55.     digitalWrite(LED,LOW);
  56.     delay(50);
  57.   }
  58. }
  59. void loop() {
  60.   int microseconds=supersonicread(); //定义一个叫microseconds变量,令它的值等于supersonicread函数的返回值。我们知道supersonicread的返回值,是超声波从发射到回声所经过的时间,单位是微秒。所以现在microseconds变量里面就保存了这个时间。
  61.   
  62. int value=microseconds*0.084;//定义了value变量,value在0-127之间,而超声波控制的距离打算在20cm左右,即1500毫秒,1500除以127既得0.084
  63.   delay(100); //延迟100毫秒,然后循环。
  64. t_midiMsg midiMsg1;  // MIDI message for Switch 1
  65. t_midiMsg midiMsg2;  //MIDI message for Swtich 2
  66. switch1CurrentState = digitalRead(switch1);
  67. switch2CurrentState = digitalRead(switch2);
  68. if (switch1CurrentState == 1){
  69.    
  70.                   midiMsg1.msg.command = MIDI_COMMAND_CONTROL_CHANGE;
  71.            midiMsg1.msg.channel = 1;
  72.            midiMsg1.msg.data2   = value;
  73.            midiMsg1.msg.data3   = 0; /* Velocity */
  74.            /* Send note on */
  75.            Serial.write(midiMsg1.raw, sizeof(midiMsg1));                           
  76.          blinkLed(2);           
  77.                                  }
  78. switch1LastState = switch1CurrentState;
  79. if (switch2CurrentState == 1){
  80.    
  81.                   midiMsg2.msg.command = MIDI_COMMAND_CONTROL_CHANGE;  //这里是打算做第二个脚踏
  82.            midiMsg2.msg.channel = 2;
  83.            midiMsg2.msg.data2   = 127;
  84.            midiMsg2.msg.data3   = 0; /* Velocity */
  85.            /* Send note on */
  86.            Serial.write(midiMsg2.raw, sizeof(midiMsg2)); //发送数据                           
  87.          blinkLed(2);           
  88.                                  }
  89.     switch2LastState = switch2CurrentState;
  90. }
复制代码


使用视频



下一步?
下一步主要会提高整体的稳定性(舞台上对于稳定性的要求是很高的),制作一个符合人机工学的外壳(实用才是正道),以及制作无线midi接口等~~~
欢迎讨论~~

(不想当吉他手的贝司手不是好程序员)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail