用Matlab来放音乐,和用单片机加蜂鸣器放音乐的原理都差不多,就是把连续的声音信号事先转换成用数字信号,然后用扬声器按照一定的节奏放出来。换句话说,演唱者是把声音经过麦克风转换成电信号,录音设备对这个电信号按照一定的时间间隔(采样频率)进行采样,得到一长串数字。如果采样的频率高,即单位时间采样的点数多,同样长度的一首歌,得到的这串数字也越长。数字的大小表示电压的高低,也就是录制时声音的大小。这串数字就是原始的音频信号。

链接里给出的那段Matlab代码的功能,就是模拟产生那串代表音频信号的数字,把这串数字交给数字模拟转换器,生成对应的电压信号,再由扬声器把这个电信号(电能)转变成声音信号(机械能)。

我不懂音乐,就从信号的角度来解释吧。
%%----------------------------------------------------------------------
fs = 44100; % sample rate
dt = 1/fs; 
这一行定义了采样频率为44100赫兹,也就是说每秒钟需要播放44100个数字。为什么是44100Hz,或者44.1KHz,而不是其他更高或者更低的频率?因为我们的耳朵能够感受到的声音频率大约是20Hz ~ 20KHz(我最多只能听到17KHz左右),对于更高频率的声音我们的耳朵只能“呵呵”了。然后根据采样定理,“当采样频率大于信号中最高频率的2倍时,采样之后的数字信号可以完整地保留了原始信号中的信息”,所以理论上20KHz的信号至少用40KHz去采样。44.1KHz这个数字按照维基百科的说法是Sony公司在1979年先开始使用,然后被公司慢慢接受了。dt就是fs的倒数,即每两个数据点之间的时间间隔。

%%----------------------------------------------------------------------
T16 = 0.125; 
t16 = [0:dt:T16]; 
t4 = linspace(0,4*T16,4*k); 
t8 = linspace(0,2*T16,2*k); 
t16, t4, t8都是数组,他们的长度不一样。k表示t16数组的长度,是5513个点,也即44100的八分之一。i和j表示t4和t8数组的长度,分别是44100的二分之一和四分之一,这三个数组定义了每个音符播放的时间。以44100点为一秒钟,5513个点就表示持续八分之一秒的长度。

%%----------------------------------------------------------------------
mod4=(t4.^4).*exp(-30*(t4.^0.5)); 
mod4=mod4*(1/max(mod4)); 
mod8=(t8.^4).*exp(-50*(t8.^0.5)); 
mod8=mod8*(1/max(mod8)); 
mod16=(t16.^4).*exp(-90*(t16.^0.5)); 
mod16=mod16*(1/max(mod16)); 
f0 = 2*146.8; % reference frequency

ScaleTable = [2/3 3/4 5/6 15/16 ... 
1 9/8 5/4 4/3 3/2 5/3 9/5 15/8 ... 
2 9/4 5/2 8/3 3 10/3 15/4 4 ... 
1/2 9/16 5/8];

% 1/4 notes 
do0f = mod4.*cos(2*pi*ScaleTable(21)*f0*t4); 
这一步相当于是生成一个调幅的包络函数,我们可以单独把mod4画出来看一看。
最上面的图形就是mod4的函数图形,中间是cos(2*pi*ScaleTable(21)*f0*t4)的图形,下面是前两个的乘积,也就是do0f的图形。f0的频率是2*146.8 = 293.6Hz,查表是知道是D4,把他当做一个基准频率(我严重怀疑这个频率偏低了,听上去很不舒服,把f0改成880Hz就好听很多)。那么中间的图形是频率为ScaleTable(21)*f0 = 0.5*293.6 = 146.8Hz的正弦波。两个相乘得到了do0f的函数图形。do0f是1/4拍,它是一个22052个元素的数组,表示这个音符的频率是146.8Hz(this is weird!),播放0.5秒长度。
但是,这是一个频率单一的信号,且信号的幅度都一样,音符间的切换都很生硬。你可以动手试一试把每个音符前面相乘的mod4, mod8, mod16注释掉,然后再播放,你会发现真的很难听。如果大伯大妈每天晚上在广场上这个音乐跳舞,环保局的投诉电话肯定打爆了。所以,就用最上面的包络函数和正弦信号相乘,让每个音符柔和的飘过来,再缓缓的离开,这样的音乐就好听很多。
包络函数一般使用一个叫做ADSR的方法,就是把整个包络线分成四段,attack time, decay time, sustain level and relearse time. 每一段都根据需要有各自的函数和参数。

Synthesizer

%%----------------------------------------------------------------------
% 1/4 notes 
do0f = mod4.*cos(2*pi*ScaleTable(21)*f0*t4); 
re0f = mod4.*cos(2*pi*ScaleTable(22)*f0*t4); 
mi0f = mod4.*cos(2*pi*ScaleTable(23)*f0*t4);
......
...... 
接下去的就是给每个音符定义一个数组,主要的参数就是节拍和频率。懂音乐的同学可以帮忙解释一下频率之间的关系,像我这样的乐盲就只能直接查表。

%%----------------------------------------------------------------------
% Melody by Schau_mal 
part0 = [mi1f la0e la0e do1f mi1f ... 
re1e re1s mi1s re1e do1e re1e do1e la0f ... 
......
......
在接下去的一步是根据乐谱来生成一个大数组。对照着《最炫民族风》的乐谱,一一对应的写到数组里保存。这一步就相当于演唱者把他的声音录在了磁带里存放。下面的图是V1数组最开始的一段,就好比是磁带的一小段,或者说光盘的几圈。如果继续放大图像,看到的就是一个个点连成的折现,每44100个点需要一秒钟的时间播放。

%%----------------------------------------------------------------------
s = v1; 
s = s/max(s);

sound(s,fs); 
最后把数值归一化到[-1,1]区间。用Matlab自带的sound()函数播放。sound()函数需要两个参数,一个是音频信号的一个数组,另一个采样频率千万不要忘记。同一段信号用44100Hz播放是正常效果,用88200Hz就成了快进播放。

http://www.zhihu.com/question/20248007/answer/21230323

matlab演奏最炫民族风的代码注释的更多相关文章

  1. Matlab 代码注释

    Matlab 代码注释 一直在找类似doxygen一样将程序注释发表成手册的方法,现在发现,Matlab的publish功能自己就能做到. Publish 简介 并非所有注释都能作为文本进行输出,MA ...

  2. IT荐书|10个最“牛叉”的代码注释

    下面是 网友针对“你看到过的最好的代码注释是什么样的?”这个问题给出的回答的前10条: 1. // 亲爱的维护者: // 如果你尝试了对这段程序进行‘优化’, // 并认识到这种企图是大错特错,请增加 ...

  3. java代码注释规范

    java代码注释规范   代码注释是架起程序设计者与程序阅读者之间的通信桥梁,最大限度的提高团队开发合作效率.也是程序代码可维护性的重要环节之一.所以我们不是为写注释而写注释.下面说一下我们在诉求网二 ...

  4. PHPDocument 代码注释规范总结

    PHPDocument 代码注释规范 1. 安装phpDocumentor(不推荐命令行安装)在http://manual.phpdoc.org/下载最新版本的PhpDoc放在web服务器目录下使得通 ...

  5. [转]java代码注释规范

    代码注释是架起程序设计者与程序阅读者之间的通信桥梁,最大限度的提高团队开发合作效率.也是程序代码可维护性的重要环节之一.所以我们不是为写注释而写注释.下面说一下我们在诉求网二期开发中使用的代码注释规范 ...

  6. MATLAB Coder从MATLAB生成C/C++代码步骤

    MATLAB Coder可以从MATLAB代码生成独立的.可读性强.可移植的C/C++代码. 使用MATLAB Coder产生代码的3个步骤: 准备用于产生代码的MATLAB算法: 检查MATLAB代 ...

  7. vs2010代码注释自动生成api文档

    最近做了一些接口,提供其他人调用,要写个api文档,可是我想代码注释已经写了说明,能不能直接把代码注释生成api?于是找到以下方法 环境:vs2010 先下载安装Sandcastle 和Sandcas ...

  8. 【转】Objective-C代码注释和文档输出的工具和方法

    http://blog.xcodev.com/blog/2013/11/01/code-comment-and-doc-gen-tools-for-objc/ 代码注释可以让代码更容易接受和使用,特别 ...

  9. VVDocumenter - Xcod代码注释工具

    刚接触IOS开发时,发现XCODE非常的强大的,后续的代码实践中发现XOCDE的代码文档注释非常的差, 每次都要用手敲,蛋疼至极: 随着不断学习发现XCODE有代码片段内嵌一说(如:for .bloc ...

随机推荐

  1. 青蛙的烦恼(dp好题)

    有n片荷叶正好在一凸多边形顶点上 有一只小青蛙恰好站在1号荷叶的点 小青蛙可以从一片荷叶上跳到另外任意一片荷叶上 给出N个点的坐标N<800 求小青蛙想通过最短的路程遍历所有的荷叶一次且仅一次的 ...

  2. [开发笔记]-使用bat命令来快速安装和卸载Service服务

    一般我们在编写完Service服务程序后,都是通过cmd命令提示窗口来安装或卸载服务,但频繁的在cmd窗口中去“拼”文件的路径着实让人“不能忍”.所以,我们需要一钟“更快捷”的方式来进行安装或者卸载操 ...

  3. TADOTable 用过滤事件 后 记录数据和 记录的内容

    用 过滤事件,过滤后 ADOTbTrade.RecordCount 是总数, 但是,记录内容是 过滤后的 ADOTbTrade.First; while not ADOTbTrade.Eof do b ...

  4. 数据结构-Stack和Queue

    实现: #include "c2_list.h" template <typename object> class Stack{ public: bool isEmpt ...

  5. Java与.NET DES加密解密互转

    上代码: Java代码: import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKe ...

  6. [vijos P1014] 旅行商简化版

    昨天早上上课讲旅行商问题,有点难,这周抽空把3^n的算法码码看.不过这个简化版已经够折腾人了. 其一不看解析不知道这是双进程动态规划,不过我看的解析停留在f[i,j]表示第一个人走到i.第二个人走到j ...

  7. xlistview的(java)

    package com.bwie.xlistviews; import java.text.SimpleDateFormat;import java.util.Date; import com.bwi ...

  8. iOS NSDictionary、NSData、JSON数据类型相互转换

    iOS经常需要用到数据类型的转换,下面列举一下常用类型的转换. 1.NSDictionary类型转换为NSData类型: //NSDictionary -> NSData: NSDictiona ...

  9. JS 跨域问题浅析及解决方法优缺点对比(转)

    1.所谓 JS 跨域问题,是指在一个域下的页面中通过js访问另一个不同域下 的数据对象, 出于安全性考 虑,几乎所有浏览器都不允许这种跨域访问,这就导致在一些ajax应用中, 使用跨域的web ser ...

  10. ResultSet结果集判断是否为空

    目前亲测过能用的一个方法是: if(rs.next())//当前行有内容 { msg2 = "有这个活动!"; } else //rs对象为空表示查无此活动 { msg2 = &q ...