专栏名称: 失传技术研究所
失传技术研究所官方账号,专注各种技术原理讲解,科技制作
目录
相关文章推荐
51好读  ›  专栏  ›  失传技术研究所

用单片机开发板自制MIDI音乐盒(预算15)

失传技术研究所  · 公众号  ·  · 2020-01-17 20:58

正文

arduino从入门到创客带师第三弹 之自制音乐盒(编程放音乐)

传统艺能 自制音乐盒 从简易代码到 用NODE JS 扒MID然后用UNO 输出方波正弦波播放8bit风格音乐 还记得多年以前咱刚接触图吧福利群的时候20包邮买了个来图定制的音乐盒,当时是个钢琴,面板是定制图片印刷的纯玻璃制的音乐盒,底部有个发条可以用来蓄能,驱动机械音乐盒播放《天空之城》 当时咱很喜欢,但是最后咱把这套装备给老妹了 人永远是比东西重要的。得到或失去东西或者赠送都不算什么,只要人没事就好。

过两天把咱之前的六色打印机和扫描仪给送去……人高兴是最重要的

占有多少物资,拥有多少权利,都没有一家人平安喜乐来得实在。就像之前说的那样,在网上显示自己的技术只会给自己带来麻烦,与其去研究电磁弹射原理与技术不如自己变成做个音乐盒拿去给长辈拜寿。

首先大多数音乐蜡烛的结构是这样的:一个牛屎芯片封装的音乐IC通过电荷泵驱动压电陶瓷蜂鸣器,在有限的空间内产生巨大的声音,循环播放音乐(一般都是《生日快乐》)这种音乐IC一般成本都比较低,如果是纯模电制作的话这样的模块成本不超过3元,一般一个音乐IC1块钱左右,三极管TO92封装 一个普通的NPN三极管,一个蜂鸣器(可以是电磁的),一块PCB印刷电路板,整个的成本就是这么点。简单有效

就是这种东西,自己买套件可能也就两三块钱

但是这期我们依然是使用ARDUINO开发板来制作这个音乐盒。因为单片机作为可编程元件,利用开发板和现成的库与环境可以自由地通过编程了获得任何想要的乐曲,这是出厂就固定参数的音乐IC没法比的。

ARDUINO教程书已更新: 《Arduino程序设计基础》(第2版) 欢迎各位自学

这期我们需要准备的有:14包邮的ARDUINO UNO开发板基于ATMEGA328P(或者基于ESP 8266的D1 MINI也可以,10块钱包邮,但是需要自己焊IO的排针排插)

一个带线扬声器 1块钱(一般用0.5W 4Ω或者8Ω的电磁扬声器就行 当然16Ω或者32Ω的电磁蜂鸣器自己焊好线也可以 这里不能用压电陶瓷的蜂鸣器 会响不起来 压电陶瓷需要特殊的驱动器,直接用单片机开发板IO难以驱动)

对了, NodeMCU是有5V输出脚的,VU脚(板子较宽的CH340G版>或者VIN脚(板子较窄的CP2102版> 之前阿卡林的群里面说的NODEMCU没有5V输出实际上并不是这样的,所以也就不存在因为NODEMCU没5V而建议买D1 MINI,实际上来说吧D1 MINI倒是很便宜,而且同样基于ESP8266且带串口转USB,我的意见是如果将来打算好好玩ARDUINO,买个D1标准版18包邮也不是不行,除非对体积有特殊要求的场合,不然ARDUINO还是买标准版比较好用,毕竟各种扩展模块都是基于标准版设计的

咱以前搞8266的时候就没做过什么正经玩意,基本就是给模块刷上WIFI DEAUTHER的固件之后就出手了,所以对于IO还真没怎么用过……以后会好的吧,这个假期高产试试 这个系列好好更将来会用到的。

此外扬声器和蜂鸣器本身是有极性的,虽然正负怎么接都能响。但是蜂鸣器是存在有源蜂鸣器和无源蜂鸣器的区别的,区别是有源蜂鸣器自带驱动电路,只要外加直流电就会产生一定频率的声音,这显然不适合播放音乐,一般多用于简单电器的警报功能,比如洗衣机热水器UPS之类的。而无源蜂鸣器的阻抗一般比较高,和一般的小型功放阻抗不匹配使用有困难,但是对于用单片机开发板驱动来说还是可以的。

单片机开发板驱动小喇叭播放音乐的原理其实也很简单,让单片机的数字输出通过PWM脉冲宽度调制的方法来驱动扬声器发出声音。其实PWM这种方式解释起来很简单,就是通过开关元件(可不是咱理解的开关,这里指继电器电子管晶体管里面的三极管或者MOS管之类的东西,主要的功能在这里还是开关)不停的鬼畜开关来调节输出给负载的电压,简单来说就是通过改变扬声器的电压来控制扬声器线圈的电流从而就可以控制扬声器的振动产生声音了。相比传统功放开关可以开一半控制电流的方式,PWM是直接通过“快速鬼畜”通过调整开关时间占比来确定负载上的电压的。如图:

这种方式的好处是操作简便,只需要决定开关的时间,效率较高功耗很低。过去的传统功放,因为开关元件开一半的缘故,所以电能有一半是分在开关元件上了,白白消耗掉变成热了,因此过去的传统功放要么就是电子管滚烫碰一下就可以抹药了要么就是晶体管需要非常大的散热片散热片不比电路上的其他元件便宜,显然体积也很大。

而数字功放的缺点则是先天不足,毕竟不是模拟功放,输出的波形和音乐的波形相比之下无论如何都有失真,因此需要投入很多技术改进才能获得满意的效果。相比之下模拟功放的改进型功耗则没有那么吓人完全可以接受而且音质也不错,因此作为垃圾佬咱是更喜欢模拟功放的。毕竟数字功放这年头他们烧HIFI往里砸钱砸太多了,一个小黑盒子要成千上万垃圾佬是不可能要的……有这钱自己搓个环绕立体声都够了。

PS:其实HIFI没有那么高贵,像咱的手机,两年前380块钱买的华为P9上也搭载了HIFI模块,当年在发布会上这个模块非常低调的出现在示意图上,风头完全被莱卡双摄盖住了,甚至都没怎么宣传,但是实际上华为P9作为带3.5mm的手机实际上是完全支持HIFI的。

至于ARDUINO,本身的IO里面就带数字输出,支持PWM输出,所以完全可以利用ARDUINO自带的函数用来输出不同频率声音组合的音乐

这里是代码:

/*

使用无源蜂鸣器播放《葫芦娃》

*/


//对应音符和频率值 #define 是一个很有用的C语法,它允许程序员在程序编译之前给常量命名。在Arduino中,定义的常量不会占用芯片上的任何程序内存空间。在编译时编译器会用事先定义的值来取代这些常量。然而这样做会产生一些副作用,例如,一个已被定义的常量名已经包含在了其他常量名或者变量名中。在这种情况下,文本将被#defined 定义的数字或文本所取代。通常情况下,优先考虑使用 const 关键字替代 #define 来定义常量。

//Arduino 拥有和 C 相同的语法规范。 Arduino语法详解

#define NOTE_D0 -1

#define NOTE_D1 294

#define NOTE_D2 330

#define NOTE_D3 350

#define NOTE_D4 393

#define NOTE_D5 441

#define NOTE_D6 495

#define NOTE_D7 556


#define NOTE_DL1 147

#define NOTE_DL2 165

#define NOTE_DL3 175

#define NOTE_DL4 196

#define NOTE_DL5 221

#define NOTE_DL6 248

#define NOTE_DL7 278


#define NOTE_DH1 589

#define NOTE_DH2 661

#define NOTE_DH3 700

#define NOTE_DH4 786

#define NOTE_DH5 882

#define NOTE_DH6 990

#define NOTE_DH7 112


#define WHOLE 1

#define HALF 0.5

#define QUARTER 0.25

#define EIGHTH 0.25

#define SIXTEENTH 0.625


//整首曲子的音符部分

int tune[] =

{

NOTE_DH1, NOTE_D6, NOTE_D5, NOTE_D6, NOTE_D0,

NOTE_DH1, NOTE_D6, NOTE_D5, NOTE_DH1, NOTE_D6, NOTE_D0, NOTE_D6,

NOTE_D6, NOTE_D6, NOTE_D5, NOTE_D6, NOTE_D0, NOTE_D6,

NOTE_DH1, NOTE_D6, NOTE_D5, NOTE_DH1, NOTE_D6, NOTE_D0,


NOTE_D1, NOTE_D1, NOTE_D3,

NOTE_D1, NOTE_D1, NOTE_D3, NOTE_D0,

NOTE_D6, NOTE_D6, NOTE_D6, NOTE_D5, NOTE_D6,

NOTE_D5, NOTE_D1, NOTE_D3, NOTE_D0,

NOTE_DH1, NOTE_D6, NOTE_D6, NOTE_D5, NOTE_D6,

NOTE_D5, NOTE_D1, NOTE_D2, NOTE_D0,

NOTE_D7, NOTE_D7, NOTE_D5, NOTE_D3,

NOTE_D5,

NOTE_DH1, NOTE_D0, NOTE_D6, NOTE_D6, NOTE_D5, NOTE_D5, NOTE_D6, NOTE_D6,

NOTE_D0, NOTE_D5, NOTE_D1, NOTE_D3, NOTE_D0,

NOTE_DH1, NOTE_D0, NOTE_D6, NOTE_D6, NOTE_D5, NOTE_D5, NOTE_D6, NOTE_D6,

NOTE_D0, NOTE_D5, NOTE_D1, NOTE_D2, NOTE_D0,

NOTE_D3, NOTE_D3, NOTE_D1, NOTE_DL6,

NOTE_D1,

NOTE_D3, NOTE_D5, NOTE_D6, NOTE_D6,

NOTE_D3, NOTE_D5, NOTE_D6, NOTE_D6,

NOTE_DH1, NOTE_D0, NOTE_D7, NOTE_D5,

NOTE_D6,

};


//曲子的节拍,即音符持续时间

float duration[] =

{

1, 1, 0.5, 0.5, 1,

0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5,

0.5, 1, 0.5, 1, 0.5, 0.5,

0.5, 0.5, 0.5, 0.5, 1, 1,


1, 1, 1 + 1,

0.5, 1, 1 + 0.5, 1,

1, 1, 0.5, 0.5, 1,

0.5, 1, 1 + 0.5, 1,

0.5, 0.5, 0.5, 0.5, 1 + 1,

0.5, 1, 1 + 0.5, 1,

1 + 1, 0.5, 0.5, 1,

1 + 1 + 1 + 1,

0.5, 0.5, 0.5 + 0.25, 0.25, 0.5 + 0.25, 0.25, 0.5 + 0.25, 0.25,

0.5, 1, 0.5, 1, 1,

0.5, 0.5, 0.5 + 0.25, 0.25, 0.5 + 0.25, 0.25, 0.5 + 0.25, 0.25,

0.5, 1, 0.5, 1, 1,

1 + 1, 0.5, 0.5, 1,

1 + 1 + 1 + 1,

0.5, 1, 0.5, 1 + 1,

0.5, 1, 0.5, 1 + 1,

1 + 1, 0.5, 0.5, 1,

1 + 1 + 1 + 1

};


int length;//定义一个变量用来表示共有多少个音符

int tonePin = 8; //蜂鸣器的pin


void setup()

{

pinMode(tonePin, OUTPUT); //设置蜂鸣器的pin为输出模式

length = sizeof(tune) / sizeof(tune[0]); //这里用了一个sizeof函数,查出数组里有多少个音符

}


void loop()

{

for (int x = 0; x < length; x++) //循环音符的次数

{

tone(tonePin, tune[x]); //依次播放tune数组元素,即每个音符

delay(400 * duration[x]); //每个音符持续的时间,即节拍duration,400是调整时间的越大,曲子速度越慢,越小曲子速度越快

noTone(tonePin);//停止当前音符,进入下一音符

}

delay(5000);//等待5秒后,循环重新开始

}

扬声器正极接在数字引脚8,负极接GND就可以了,程序不需要调用什么库,编译无误上传到开发板就行了。

但是这种方法虽然简单,但是却存在缺点,也就是编曲非常麻烦,整首曲子需要被分成曲调和音符(节拍)也就是翻译成机器语言,这样也就表示为开发板用机器语言编曲非常困难,那么有什么办法可以简单的直接让ARDUINO不需要编曲就可以播放音乐吗?可以:

arduino-midi-player

这个项目允许用户在ARDUINO开发板上利用一套完整的代码播放MIDI音乐,下载方法在我们之前的 ARDUINO环境库的安装和使用GITHUB上的开源项目 说过,右上角CLONE OR DOWNLOAD里面选ZIP就行了,不需要登录

什么是MIDI音乐? MIDI (Musical Instrument Digital Interface)乐器数字接口 ,是20 世纪80 年代初为解决电声乐器之间的通信问题而提出的。 MIDI 是编曲界最广泛的音乐标准格式,可称为“计算机能理解的乐谱”。它用音符的数字控制信号来记录音乐。一首完整的 MIDI 音乐只有几十KB大,远远小于波形音乐。

比如这个《帝国时代》的主题曲就是MIDI格式的音乐。但是仔细听可以发现,MIDI音乐是不含人声的,因为MIDI音乐是利用系统自带的乐器演奏的,而不是记录录音。MIDI音乐的乐器不含人声,所以无法演唱人声。但是各位老二次元都知道大量使用MIDI技术的雅马哈推出了VOCALOID这款软件,允许利用机器演唱乐曲,在MIDI音乐中填上唱词就可以利用人声库中的人声演唱了,也有基于同样技术的开源软件可以使用。当然,这又是另一个故事了。

总之这个项目允许我们利用脚本自动化转换MIDI格式的音乐变成ARDUINO可以识别的音乐。但是这个脚本是基于NODE.JS的,因此我们需要上官网下载运行环境

下载哪个都行

下载后按正常软件WIZARD的方法安装就行了

安装结束了

然后我们启动CMD就可以了,WIN7 XP用运行 WIN+R CMD回车就行了 不需要运行NODE.JS的控制台

先运行node -v看看运行库情况,然后输入 CD 下载的ZIP解压出来的JS脚本所在的文件夹路径就可以了,WIN7可以直接点文件管理器的地址栏复制绝对路径,然后在CMD里面右键粘贴

然后我们需要输入node smf2seq.js

实践证明后边括号里的内容可以不需要,只需要把你想要转换的MIDI文件重命名为song.mid然后放在这个JS脚本同一个文件夹然后node smf2seq.js就行了

这样最后会得到两个文件,一个叫sequence.h,另一个叫midi.json 你需要做的是把这两个文件放在和arduino-midi-player.ino同一个文件夹里然后运行arduino-midi-player.ino

一开始运行arduino-midi-player.ino的时候ARDUINO会让你把这个项目文件单独建一个文件夹存放,一般是在原地新建一个同名文件夹然后移动项目文件进去,所以你需要把原来GITHUB下载的压缩包解压后的那个项目文件夹里面的所有文件都挪到ARDUINO给你新建的文件夹里去,和arduino-midi-player.ino一起。

smf2seq.js可以放在原地不动,方便我们将来日后整别的MIDI 当然如果移动了的话就再在CMD里面CD到smf2seq.js所在的文件夹就行了

关于MIDI文件的要求

这个项目对MIDI文件有一定的要求,首先MIDI文件不能是加密的,很多游戏自带的MIDI是加密的,在这里无法使用,比如《暴力摩托》的MIDI就是经典的加密MIDI,别说给ARDUINO用,正常播放器都放不了只能在游戏中用(还好有人通过录音转换成MIDI的方式解密了)。《帝国时代》这个也被报错说不支持了,MMP。

那么我们怎么得到MIDI文件呢?其实关于MIDI文件一直有很多网站在分享,过去不少网站下载MIDI格式音乐是不要钱的,那年头的FLASH同人小游戏都用的是这种免费公开的MIDI网站提供的MIDI格式音乐作为BGM。而咱作为老二次元会弹琴是传统艺能就更倾向于去找乐谱分享网站下载基于MIDI的乐谱

咱在虫虫钢琴网或者流行钢琴网这种曲谱网站是有号的,这些网站下载曲谱仍然保持着老论坛回复免积分的方法,非常适合咱这次这个项目。

OVE格式的乐谱下载回来之后需要用一个叫OVERTURE的软件打开,这个软件可以把乐谱导出MIDI格式

咱简单把它放在桌面就行了。

顺带说下,这款过去免费的软件现在也惨遭马克丁代理变成了收费软件了。以前免费的版本被强制更新成了收费版,网上也再也找不到过去的免费版了,还好我还有。各位如果需要的话我们所可以提供Overture 4.1.5 单文件中文版(popiano),我会把它放在群文件里

马克丁吃屎去吧

然后我们运行arduino-midi-player.ino

如果报错sequence.h: No such file or directory那就是咱刚才说的ARDUINO自动新建文件夹的问题了,把sequence.h:放在和arduino-midi-player.ino一个文件夹里。GITHUB上的那个项目文件最好还是解压出来之后把里面所有的文件都塞到和arduino-midi-player.ino一个文件夹

至于剩下的报错则是对MIDI文件转换成的sequence.h的问题了,貌似这个乐谱出现了ARDUINO不能理解的乐句

玩ARDUINO就是哪里不对删哪里 好在咱下载的是乐谱,咱可以通过修改乐谱的方式重新导出修正过的MIDI文件,咱先把后面复杂的部分去掉试试

编译成功

这会儿咱的开发板在11号数字端口插扬声器正极,扬声器负极接地(GND)就可以按照MIDI文件播放音乐了

咱最近看到微博上又有人转唢呐版的《恋爱循环》之后简单的 用半个小时的时间就吹出了同样的效果:







请到「今天看啥」查看全文