在本教程中,我将向您展示如何使用ArduinoUno制作基本的MIDI乐器。本乐器连续演奏三个音符(三和弦)。使用不同的电位器,您可以上下移动音符以及上下移动音阶。您还可以控制每个音符之间的延迟。我觉得我从这个项目中学到了一些关于MIDI控制器的有趣的东西,我希望你也会。这个乐器是琶音器的一个非常基本的版本。这是一个花哨的动作视频。琶音器利用和弦的自然音乐性,不费吹灰之力就发出许多美妙的声音。它们通常作为合成器的一部分出现,您只需按住想要琶音的和弦即可。在这个例子中,我们只有一个触发琶音的按钮和三个控制起始音高及其在音阶上的位置的按钮。对于arduinoUno,使用MIDI协议的最简单方法是通过串行端口。它需要一些配置才能在Windows上运行,我将介绍一下。我使用了FortySevenEffects的ArdunioMIDI库。我还遵循了这些优秀的youtube教程:Arduino的MIDI,如何使用Arduino构建MIDI控制器。我根据第二个视频系列的代码编写了用于控制按钮和电位器的代码。在制作MIDI控制器的整个免费课程的一部分中,它确实有很好的记录。在这个项目中,我将GroveStarterKitPlus用于RGB背光LCD屏幕。这不是绝对必要的,但很高兴看到您正在演奏的音符。补给品4个电位器(我用了我身边的任何东西)1个按钮1个1k电阻器用于按钮1ArduinoUno1Grove入门套件/RGB背光LCD(可选)连接件用电线电脑相关工具DAWHairlessMIDI(串行桥接器——将串行输出转换为MIDI输入的东西)LoopMIDI(为您的计算机创建虚拟环回端口。)ArduinoIDE第1步:设置电路板下面是我在没有LCD的情况下使用的布局图。该按钮将用作启动琶音的触发器。其中三个电位器将控制琶音的不同方面,最后一个电位器将用作MIDI控制。在我的实际电路布局中,我使用了Grove板(连接在ArduinoUno的顶部)。它带有一个按钮和一个不错的电位器,您可以在上图中看到它们已插入各自的端口。LCD屏幕与此连接非常容易,您只需将Grove套件随附的一根电缆从LCD屏幕连接到I2C端口。名称数量组件R51220Ω电阻U911ArduinoUnoR3Rpot9Rpot10Rpot11Rpot124250kΩ电位器R8110kΩ电阻S11按钮电路图原稿基本琶音器noLCD.ino第2步:熟悉MIDIMIDI是一种专门为80年代初创建的乐器而构建的通信协议。通过我们拥有的软件设置,我们可以使用MIDI进行输入和输出到我们的arduino,但为了简单起见,我们将专注于输出。具体我们将使用两个不同频道的语音消息:注意和控制器更改。NoteOn允许我们告诉DAW或我们用来打开或关闭音符的任何东西,以及以什么速度(MIDI说音量的奇怪方式)。控制器更改告诉DAW控制器的值已更改。您可以将其想象为在放大器上扭转刻度盘,无论是为了音量还是某些效果,如失真或混响。还有很多其他的消息类型,而且事情会变得非常复杂,非常快。我们使用的MIDI库有很多函数来支持这些不同的消息类型。有兴趣了解更多吗?我首先会向您介绍我放在教程开头的youtube链接。然后我会四处搜索,看看人们用MIDI做了什么,以了解可能性。对于NoteOn,这是指向音高列表及其分配的MIDI值的链接。例如,C4的midi值为60。每个数字都映射到一个半音,因此使用模除法来确定您有什么音符非常容易。鉴于C4=60,我知道C5=72,C6=84,无需查看表格,因为我知道八度音程相隔12个半音。当我们将MIDI值转换为音符以显示在LCD屏幕上时,这将派上用场。此外,如果我们将Cmaj音阶MIDI值保存到列表中,我们可以使用mod7除法来移动音阶中的值。第3步:代码如果您不需要/不需要LCD,我附上了一个版本,其中我注释掉了LCD组件。我在代码中进行了大量注释,但在这里我将指出一些事情,从顶部开始。初始化变量:按钮的变量“debounce_delay”以及变量“Timeout”和“var_threshold”可用于减少来自按钮和电位计的一些噪声和错误读数。如果您遇到问题,那将是一个很好的起点。由于我们将连续范围转换为离散整数,因此电位器可能会在边界之间变得有点不稳定。如果电位器在状态之间闪烁过多,有几种方法可以解决。我在此未实施的一种解决方案是,在进行任何进一步处理之前,先读取几次读数并取平均值。“音符”列表是我们要在琶音中演奏的音符序列。我将它设置为播放起始音符,并以大调的方式记录2个音符和4个音符。这将形成基本的大调、小调和减和弦。如果你想要一些更有趣的东西,你可以在“笔记”列表中添加更多的笔记。只是不要忘记更改常量“CHORD_LENGTH”。例如,您可以这样做:constintCHORD_LENGTH=6;//numberofnotesinchordintmidi_vals[CHORD_LENGTH]={};//arraytostoremidi-outputtoplayintnotes[CHORD_LENGTH]={0,2,4,2,5,7};//1st3rd,and5thnoteonscale,plusthe7th.Thisismorejazzy通过创建列表“Cmaj_scale”,我创建了一个模板,我可以上下移动来创建和弦。如果我想更改键,我需要做的就是使用来自第三个电位器的输入更改“midi_c_state[2]”。如果我只想从给定的起始音符移动大调,我只需从第一个电位计更改“midi_c_state[0]”。设置:您需要为MIDI设置正确的波特率才能正确读取您的串行输入。否则你会在无毛MIDI中得到错误,比如“得到了意外的数据字节”LCD屏幕带有16列数字和2行。循环和相关功能函数“pause_length”控制正在播放的音符之间的延迟。“starting_scale_position”允许您在不改变键的情况下上下移动大调。“起始音符”允许您将所有内容向上或向下移动一个半色调值,从而允许您更改调。这两个函数都调用“move_chord”,将您选择的音符重新映射到MIDI值。更改延迟、起始音阶位置、MIDI控制或音符将导致LCD刷新。基本琶音器.ino//BasicMIDIarpeggiatorinstrumentfortheArduinoUNObyJosephThompson.//Thisplaystriadsthatcanbeshiftedinpitchandpositiononthescale//Inspiredbyandusesthebutton/potentiometercheckingfromhttps://github.com/silveirago/DIY-Midi-Controller/blob/master/Code%20-%20c%C3%B3digo/en-DIY_midi_controller/en-DIY_midi_controller.ino//usesGrovergb_lcd,fromgrovestarterkit.https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/Thisisnotessentialandcanberemoved#include#includeMIDI_CREATE_DEFAULT_INSTANCE();//CreatesMIDIobjecttointeractwithSerial//BUTTONS//Onlyhave1buttontotriggerthearpeggioconstintN_BUTTONS=1;//*totalnumbersofbuttonsconstintBUTTON_ARDUINO_PIN[N_BUTTONS]={3};//*pinsofeachbuttonconnectedstraighttotheArduinointbutton_c_state[N_BUTTONS]={};//storesthebuttoncurrentvalueintbutton_p_state[N_BUTTONS]={};//storesthebuttonpreviousvalue//debounceunsignedlonglast_debounce_time[N_BUTTONS]={0};//thelasttimetheoutputpinwastoggledunsignedlongdebounce_delay=50;//*thedebouncetime;increaseiftheoutputflickers//PotentiometersconstintN_POTS=4;constintPOT_ARDUINO_PIN[N_POTS]={A0,A1,A2,A3};//*pinsofeachpotconnectedstraighttotheArduinointpot_c_state[N_POTS]={0,0,0,0};//Currentstateofthepotintpot_p_state[N_POTS]={0,0,0,0};//Previousstateofthepotintpot_var=0;//Differencebetweenthecurrentandpreviousstateofthepot/*midi_c_state[0]-->Positiononscale*midi_c_state[1]-->pauselength*midi_c_state[2]-->adjustspositiononMIDInotemap.i.e.C5=60,C5#=61...*midi_c_state[3]-->MIDIcontrol*/intmidi_c_state[N_POTS]={0,0,0,0};//Currentstateofthemidivalueintmidi_p_state[N_POTS]={0,0,0,0};//PreviousstateofthemidivalueconstintTIMEOUT=300;//*Amountoftimethepotentiometerwillbereadafteritexceedsthevar_thresholdconstintvar_threshold=10;//*Thresholdforthepotentiometersignalvariationbooleanpot_moving=true;//IfthepotentiometerismovingunsignedlongPTime[N_POTS]={0,0,0,0};//Previouslystoredtimeunsignedlongtimer[N_POTS]={0,0,0,0};//Storesthetimethathaselapsedsincethetimerwasreset//MIDIbytemidi_ch=1;//*MIDIchanneltobeusedbytecc=1;//*LowestMIDICCtobeused//ScaleinfoStringnote_names[]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};//fordisplayontheLCDintCmaj_scale[]={60,62,64,65,67,69,71,72};//MIDIcodeforCMajorscale.Inmidi,eachnumberisasemi-toneintscale_start=0;//startingpositioninscaleconstintCHORD_LENGTH=3;//numberofnotesinchordintmidi_vals[CHORD_LENGTH]={};//arraytostoremidi-outputtoplayintnotes[CHORD_LENGTH]={0,2,4};//1st3rd,and5thnoteonscale,makesbasictriadintnote_delay=100;//LCDrgb_lcdlcd;//createLCDinstanceconstintcolorR=255;//setbackgroundcolorsconstintcolorG=0;constintcolorB=100;voidsetup(){//putyoursetupcodehere,torunonce:Serial.begin(115200);for(inti=0;ipinMode(BUTTON_ARDUINO_PIN[i],INPUT_PULLUP);//LCDlcd.begin(16,2);//rowsandcolumnsofLCDlcd.setRGB(colorR,colorG,colorB);//startsbackgroundcolorlcd.print("GetReadytoJam!");}}voidloop(){//buttonsarp_trigger();//scale_startsthearpegio//potsstarting_note();//adjustsstartingnotestarting_scale_position();//adjustsscalepositionpause_length();//adjusttimebetweennotesinarppegiopot_midi();//adjustsMIDIcontrolpots}//BUTTONSvoidarp_trigger(){button_c_state[0]=digitalRead(BUTTON_ARDUINO_PIN[0]);//readpinfromarduinoif((millis()-last_debounce_time[0])>debounce_delay){//Stopsmultipletriggersintooshortofatimespan(debouncing)if(button_p_state[0]!=button_c_state[0]){//Checksifsomethingchangedlast_debounce_time[0]=millis();if(button_c_state[0]==LOW){//Playsthearpegiofor(inti=0;iMIDI.sendNoteOn(midi_vals[i],127,midi_ch);//note,velocity,channeldelay(note_delay);//delaycontrolledbypotentiometer1}}else{//stopsplayingarpegiofor(inti=0;iMIDI.sendNoteOn(midi_vals[i],0,midi_ch);}}button_p_state[0]=button_c_state[0];//savescurrentstateaspaststate}}}voidmove_chord(){/*helperfunctiontoshiftchordaroundthescale.midi_c_state[0]iscontrolledbypotentiometer0*/intnote;scale_start=midi_c_state[0];for(inti=0;inote=notes[i];if(scale_start+note>7){//Thisallowsustoplaynotesinnextoctavemidi_vals[i]=Cmaj_scale[(scale_start+note)%7]+12+midi_c_state[2];//convertsnotetodefaultscale,addstwelvesemitonestomoveitupanoctave}else{midi_vals[i]=Cmaj_scale[scale_start+note]+midi_c_state[2];//noteisincurrentoctave,wecanstoreitnormally}}}//POTENTIOMETERSvoidstarting_scale_position(){/*Thisfunctiondetermineswhereonscaleyoustart.Thisiscontrolledbypotentiometer0(0isstartofscale,7istheoctave)*/pot_c_state[0]=analogRead(POT_ARDUINO_PIN[0]);//readsthepinsfromarduinomidi_c_state[0]=map(pot_c_state[0],0,1023,0,7);//Mapsthereadingofthepot_c_statetoavaluethatwewilladdtothestartingmidivaluepot_var=abs(pot_c_state[0]-pot_p_state[0]);//Calculatestheabsolutevaluebetweenthedifferencebetweenthecurrentandpreviousstateofthepotif(pot_var>var_threshold){//OpensthegateifthepotentiometervariationisgreaterthanthethresholdPTime[0]=millis();//Storestheprevioustime}timer[0]=millis()-PTime[0];//Resetsthetimer11000-11000=0msif(timer[0]pot_moving=true;}else{pot_moving=false;}if(pot_moving==true){//Ifthepotentiometerisstillmoving,sendthechangecontrolif(midi_p_state[0]!=midi_c_state[0]){move_chord();//Callsmovechordfunction.Thisshiftsthechordtothevaluesmappedtothepotentiometersupdate_lcd();//updatesLCDtoreflectchangespot_p_state[0]=pot_c_state[0];//Storesthecurrentreadingofthepotentiometertocomparewiththenextmidi_p_state[0]=midi_c_state[0];//Storescurrentmapping}}}voidpause_length(){/*Thisfunctiondetermineslengthofpause.Controlledbypotentiometer1*/pot_c_state[1]=analogRead(POT_ARDUINO_PIN[1]);//readsthepinsfromarduinomidi_c_state[1]=map(pot_c_state[1],0,1023,0,1000);//Mapsthereadingofthepot_c_statetoadelayvaluebetween0and1secondpot_var=abs(pot_c_state[1]-pot_p_state[1]);//Calculatestheabsolutevaluebetweenthedifferencebetweenthecurrentandpreviousstateofthepotif(pot_var>var_threshold){//OpensthegateifthepotentiometervariationisgreaterthanthethresholdPTime[1]=millis();//Storestheprevioustime}timer[1]=millis()-PTime[1];//Resetsthetimer11000-11000=0msif(timer[1]pot_moving=true;}else{pot_moving=false;}if(pot_moving==true){//Ifthepotentiometerisstillmoving,sendthechangecontrolif(midi_p_state[1]!=midi_c_state[1]){note_delay=midi_c_state[1];update_lcd();//updatesLCDtodisplaynewinfopot_p_state[1]=pot_c_state[1];//Storesthecurrentreadingofthepotentiometertocomparewiththenextmidi_p_state[1]=midi_c_state[1];//Storescurrentmapping}}}voidstarting_note(){/*Thisfunctiondeterminesabsolutestartinglocation.Controlledbypotentiometer2*/pot_c_state[2]=analogRead(POT_ARDUINO_PIN[2]);//readsthepinsfromarduinomidi_c_state[2]=map(pot_c_state[2],0,1023,-10,30);//Mapsthereadingofthepot_c_statetoadelayvaluepot_var=abs(pot_c_state[2]-pot_p_state[2]);//Calculatestheabsolutevaluebetweenthedifferencebetweenthecurrentandpreviousstateofthepotif(pot_var>var_threshold){//OpensthegateifthepotentiometervariationisgreaterthanthethresholdPTime[2]=millis();//Storestheprevioustime}timer[2]=millis()-PTime[2];//Resetsthetimer11000-11000=0msif(timer[2]pot_moving=true;}else{pot_moving=false;}if(pot_moving==true){//Ifthepotentiometerisstillmoving,sendthechangecontrolif(pot_p_state[2]!=pot_c_state[2]){move_chord();//movesthenewchordupdate_lcd();//updatesLCDtodisplaynewinfopot_p_state[2]=pot_c_state[2];//Storesthecurrentreadingofthepotentiometertocomparewiththenext}}}voidpot_midi(){//thisfunctionsendstheMIDIcontrolvaluesfromthepottothecomputer.Youcanusethistocontroleffects,volume,etc.inDAWpot_c_state[3]=analogRead(POT_ARDUINO_PIN[3]);//readsthepinsfromarduinomidi_c_state[3]=map(pot_c_state[3],0,1023,0,127);//Mapsthereadingofthepot_c_statetoavalueusableinmidipot_var=abs(pot_c_state[3]-pot_p_state[3]);//Calculatestheabsolutevaluebetweenthedifferencebetweenthecurrentandpreviousstateofthepotif(pot_var>var_threshold){//OpensthegateifthepotentiometervariationisgreaterthanthethresholdPTime[3]=millis();//Storestheprevioustime}timer[3]=millis()-PTime[3];//Resetsthetimer11000-11000=0msif(timer[3]pot_moving=true;}else{pot_moving=false;}if(pot_moving==true){//Ifthepotentiometerisstillmoving,sendthechangecontrolif(midi_p_state[3]!=midi_c_state[3]){MIDI.sendControlChange(cc+3,midi_c_state[3],midi_ch);//ccnumber,ccvalue,midichannelupdate_lcd();pot_p_state[3]=pot_c_state[3];//Storesthecurrentreadingofthepotentiometertocomparewiththenextmidi_p_state[3]=midi_c_state[3];}}}//LCDvoidupdate_lcd(){/*ThisfunctionbuildsthetwolinestodisplayontheLCDscreen*/Stringfirst_line="notes:";Stringsecond_line="ms:";Stringnote_name;for(inti=0;inote_name=note_names[midi_vals[i]%12];//Usingmodulodivisiontomapmidinumbertonameofnotefirst_line=first_line+note_name;first_line=first_line+"";}second_line=second_line+note_delay;second_line=second_line+"MIDI:";second_line=second_line+pot_c_state[3];//lcd.clear();//ErasewhatwaspreviouslywrittenonLCDscreenlcd.setCursor(0,0);//Movecursortobegginningoffirstrowlcd.print(first_line);lcd.setCursor(0,1);//movecursortosecondrowlcd.noCursor();//removescursorsouserdoesnotseeitlcd.print(second_line);}基本琶音器noLCD.ino第4步:设置PC为此,您需要下载HairlessMIDI、DAW和LoopMIDI。他们的链接在开头。首先,打开LoopMIDI并单击左下角的加号按钮。这将创建一个名为“loopMIDI端口”的环回端口。如果您随后打开无毛MIDI,您应该在“串行端口”和“MIDI输出”的下拉菜单中看到它作为一个选项。对于此示例,您可以忽略“MIDIIn”。注意:如果您有很多来自控制器的输入,例如,如果电位计发出大量噪音,您的计算机将关闭端口,您将在LoopMIDI中看到它。您应该尝试降低电位计的噪音,然后使用屏幕左下方的“减号”按钮删除LoopMIDI上的端口。然后使用加号按钮创建一个新端口并重试。在HairlessMIDI中,如果操作正确,当您单击“调试MIDI消息”并触发MIDI控制器时,您应该会看到它是哪种MIDI消息,如图所示。如果您的波特率有问题,您将收到类似“警告:有一个状态字节...”之类的错误消息。您只需要进入首选项并选择正确的波特率。我附上了一张对我有用的截图。现在您需要做的就是选择您最喜欢的DAW并进行设置,以便它可以从LoopMIDI获取输入。这个视频是我在开头链接的系列之一的一部分,展示了它是如何为Ableton完成的,但不同的DAW可能会有所不同。第5步:项目展示下方是我的项目的演示。在本视频中,我将介绍基本功能。如果您只想知道它可以做什么,我还添加了它可以发出的一些声音的MP3。以下是对该项目的一些补充想法:一种在不同琶音模式之间进行选择的方法LCD上的屏幕滚动以适应更大的琶音模式更多MIDI控件/按钮一个排队系统,这样你就可以安排一个接一个的琶音