前几天在洛谷日报征文中看到了这样一篇文章:C++不止能做题。作为原来校管弦乐队的一名成员,而后因为信息完全放弃了管弦乐队,我看完是又激动又怀念。于是我自行去研究了一下:C++ 如何让蜂鸣器叫出乐曲。

由于本人乐理只有五线谱D2-2级,数学成绩中游,信息也只有普及组水平。如果有哪里写错了,望大家指正、轻 D。


首先需要知道两个函数:

#include <windows.h>

Beep( f, t );
Sleep( t );

Beep() 函数可以让蜂鸣器发出频率为 \(f\) 赫兹,音长大约为 \(2t\) 毫秒的音。(注意是 \(2t\))

Sleep() 函数可以当做休止符用。它可以让程序停止运行 \(t\) 毫秒的时间。

如果想知道每个音对应的频率,可以自行百度十二平均律频率表。以下是三个最常用的八度的频率:

// _在前表示低音, 在后表示高音
// o表示升
const int _oC = 277, _oD = 311, _oF = 370, _oG = 415, _oA = 466;
const int _C = 262, _D = 294, _E = 330, _F = 349, _G = 392, _A = 440, _B = 494;
const int oC = 554, oD = 622, oF = 740, oG = 831, oA = 932;
const int C = 523, D = 578, E = 659, F = 698, G = 784, A = 880, B = 988;
const int C_ = 1047, D_ = 1175, E_ = 1319, F_ = 1397, G_ = 1568, A_ = 1760, B_ = 1976;
const int oC_ = 1109, oD_ = 1245, oF_ = 1480, oG_ = 1661, oA_ = 1865;

(为了偷懒方便,这当中我统一把降音变成了升音……比如降 \(\text{B}\) 我就用升 \(\text{A}\) 代替。)

接下来考虑如何计算 \(t\)。在编程中,我们可以设置一个常量 \(T\) 表示单拍子的时长的一半(因为 Beep 函数中调用的 \(t\) 就是时长的一半)。这样,如果一个音符占半拍,那么它的时长 \(t = \frac{T}{2}\);如果一个音符占两拍,那么它的时长 \(t = 2T\);如果一个休止符占一拍,那么它的时长 \(t = 2T\)(请特别注意 Sleep 函数与 Beep 函数中 \(t\) 的区别)。另外,在乐曲结尾经常有无限延长记号。无限延长记号一般是延长到 \(6\) 到 \(8\) 拍,最好不要超过 \(10\) 拍。

那么如何计算这个常量 \(T\) 呢?在一般谱子的左上角,都会有 ♩ = 76、♩ = 84 之类的符号,这意味着该曲以四分音符为一拍,每分钟 76 / 84 拍(取决于等号后面的数字)。

接下来就是小学数学题了。以 ♩ = 76 为例。每分钟 \(60000\) 毫秒,每分钟 \(76\) 拍,那么每拍占 \(\frac{60000}{76}\) 毫秒,可以近似取到 \(800\)。又因为 \(T\) 是时长的一半,所以 \(T\) 就应该取 \(\frac{800}{2}\),也就是 \(400\)。

知道了这些以后,我们看一个例子(小星星):

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <windows.h>
using namespace std; // _在前表示低音, 在后表示高音
// o表示升
const int _oC = 277, _oD = 311, _oF = 370, _oG = 415, _oA = 466;
const int _C = 262, _D = 294, _E = 330, _F = 349, _G = 392, _A = 440, _B = 494;
const int oC = 554, oD = 622, oF = 740, oG = 831, oA = 932;
const int C = 523, D = 578, E = 659, F = 698, G = 784, A = 880, B = 988;
const int C_ = 1047, D_ = 1175, E_ = 1319, F_ = 1397, G_ = 1568, A_ = 1760, B_ = 1976;
const int oC_ = 1109, oD_ = 1245, oF_ = 1480, oG_ = 1661, oA_ = 1865; const int T = 400; //一拍的长度
const int Stop = 800; //一拍休止符的长度 int main()
{
Beep( C, T );
Beep( C, T );
Beep( G, T );
Beep( G, T );
Beep( A, T );
Beep( A, T );
Beep( G, T * 2 ); Beep( F, T );
Beep( F, T );
Beep( E, T );
Beep( E, T );
Beep( D, T );
Beep( D, T );
Beep( C, T * 2 ); Beep( G, T );
Beep( G, T );
Beep( F, T );
Beep( F, T );
Beep( E, T );
Beep( E, T );
Beep( D, T * 2 ); Beep( G, T );
Beep( G, T );
Beep( F, T );
Beep( F, T );
Beep( E, T );
Beep( E, T );
Beep( D, T * 2 ); Beep( C, T );
Beep( C, T );
Beep( G, T );
Beep( G, T );
Beep( A, T );
Beep( A, T );
Beep( G, T * 2 ); Beep( F, T );
Beep( F, T );
Beep( E, T );
Beep( E, T );
Beep( D, T );
Beep( D, T );
Beep( C, T * 4 );
return 0;
}

这里面多了一个常量 \(Stop\),用于表示休止符的一拍,这样书写起来可以方便许多,调用 Sleep 函数的时候不需要一直换算。

我还无聊地打了另外几首歌。

《团结就是力量》

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <windows.h>
using namespace std; // _在前表示低音, 在后表示高音
// o表示升
const int _oC = 277, _oD = 311, _oF = 370, _oG = 415, _oA = 466;
const int _C = 262, _D = 294, _E = 330, _F = 349, _G = 392, _A = 440, _B = 494;
const int oC = 554, oD = 622, oF = 740, oG = 831, oA = 932;
const int C = 523, D = 578, E = 659, F = 698, G = 784, A = 880, B = 988;
const int C_ = 1047, D_ = 1175, E_ = 1319, F_ = 1397, G_ = 1568, A_ = 1760, B_ = 1976;
const int oC_ = 1109, oD_ = 1245, oF_ = 1480, oG_ = 1661, oA_ = 1865; const int T = 400;
const int Stop = 800; int main()
{
Beep( C_, T * 2 );
Beep( G, T );
Beep( E, T / 4 * 3 );
Beep( D, T / 4 );
Beep( C, T );
Beep( G, T );
Beep( E, T );
Sleep( Stop ); Beep( C_, T * 2 );
Beep( G, T );
Beep( E, T / 4 * 3 );
Beep( D, T / 4 );
Beep( C, T );
Beep( A, T );
Beep( G, T );
Sleep( Stop / 2 ); Beep( G, T / 2 );
Beep( C_, T );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( C_, T );
Sleep( Stop / 2 );
Beep( G, T / 2 );
Beep( C_, T );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( A, T );
Sleep( Stop / 2 ); Beep( E, T / 2 );
Beep( C_, T / 2 * 3 );
Beep( A, T / 2 );
Beep( G, T );
Beep( E, T );
Beep( C_, T / 2 * 3 );
Beep( A, T / 2 );
Beep( C_, T );
Sleep( Stop ); Beep( C_, T );
Beep( G, T );
Beep( E, T / 2 );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( E, T / 2 );
Beep( D, T / 2 * 3 );
Beep( C, T / 2 );
Beep( E, T );
Sleep( Stop / 2 ); Beep( G, T / 2 );
Beep( C_, T );
Beep( G, T );
Beep( A, T / 2 );
Beep( C_, T / 2 );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( E, T );
Beep( E, T / 2 );
Beep( C, T / 2 );
Beep( A, T * 2 );
Beep( A, T );
Sleep( Stop ); Beep( C_, T / 4 * 3 );
Beep( C_, T / 4 );
Beep( G, T / 2 );
Beep( G, T / 2 );
Beep( D, T / 4 * 3 );
Beep( E, T / 4 );
Beep( G, T / 2 );
Beep( G, T / 2 ); Beep( A, T / 2 * 3 );
Beep( G, T / 2 );
Beep( A, T );
Beep( D_, T );
Beep( C_, T );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( C_, T );
Beep( A, T / 2 );
Beep( G, T / 2 );
Beep( E, T * 2 );
Beep( C_, T );
Sleep( Stop ); return 0;
}

可以明显听出,当节奏快起来的时候,蜂鸣器发音的速度就有点赶不上了。所以这种方法局限性还是很强的,只适合打小夜曲之类的……

最后附上我们学校的校歌(原曲是 G 大调的,但是我太弱了不知道怎么弄……于是降成 C 大调写了)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <windows.h>
using namespace std; // _在前表示低音, 在后表示高音
// o表示升
const int _oC = 277, _oD = 311, _oF = 370, _oG = 415, _oA = 466;
const int _C = 262, _D = 294, _E = 330, _F = 349, _G = 392, _A = 440, _B = 494;
const int oC = 554, oD = 622, oF = 740, oG = 831, oA = 932;
const int C = 523, D = 578, E = 659, F = 698, G = 784, A = 880, B = 988;
const int C_ = 1047, D_ = 1175, E_ = 1319, F_ = 1397, G_ = 1568, A_ = 1760, B_ = 1976;
const int oC_ = 1109, oD_ = 1245, oF_ = 1480, oG_ = 1661, oA_ = 1865; const int T = 400;
const int Stop = 800; int main()
{
Beep( C, T );
Beep( A, T * 2 );
Beep( A, T );
Beep( F, T );
Beep( C, T );
Beep( C, T );
Beep( F, T * 2 );
Beep( E, T );
Beep( D, T * 3 );
Beep( _B, T / 2 );
Beep( _B, T / 2 );
Beep( _A, T );
Beep( E, T );
Beep( D, T * 2 );
Beep( _A, T );
Beep( _B, T );
Beep( C, T * 3 ); Beep( _E, T );
Beep( _A, T / 2 );
Beep( _B, T / 2 );
Beep( C, T / 2 );
Beep( D, T / 2 ); for ( int i = 1; i <= 2; ++i ) {
Beep( E, T );
Beep( E, T );
Beep( E, T );
Beep( D, T / 2 * 3 );
Beep( E, T / 2 );
Beep( C, T );
Beep( _B, T * 2 );
Beep( _A, T / 2 );
Beep( _G, T / 2 );
Beep( _A, T * 5 ); Beep( _A, T );
Beep( _G, T );
Beep( C, T );
Beep( E, T );
Beep( G, T * 2 );
Beep( E, T );
Beep( F, T / 2 * 3 );
Beep( E, T / 2 );
Beep( D, T / 2 );
Beep( _A, T / 2 );
Beep( D, T * 6 ); Beep( E, T );
Beep( E, T );
Beep( E, T );
Beep( D, T / 2 * 3 );
Beep( E, T / 2 );
Beep( C, T );
Beep( _B, T );
Beep( _B, T / 2 );
Beep( _B, T / 2 );
Beep( _A, T / 2 );
Beep( _G, T / 2 );
Beep( _A, T * 2 ); Beep( _A, T );
Beep( _G, T );
Beep( C, T );
Beep( E, T );
Beep( G, T * 3 );
Beep( F, T * 2 );
Beep( F, T );
Beep( E, T / 2 * 3 );
Beep( E, T / 2 );
Beep( D, T / 4 * 3 );
Beep( E, T / 4 );
Beep( C, T * 5 ); Beep( C, T / 2 );
Beep( C, T / 2 );
Beep( A, T );
Beep( A, T );
Beep( A, T );
Beep( G, T );
Beep( F, T );
Beep( E, T );
Beep( D, T * 2 );
Beep( C, T );
Beep( G, T * 3 );
Beep( F, T / 2 );
Beep( E, T / 2 );
Beep( D, T );
Beep( _A, T / 2 );
Beep( _B, T / 2 * 3 );
Beep( _A, T / 2 );
Beep( _B, T / 2 );
Beep( C, T / 2 );
Beep( D, T * 5 ); Beep( C, T / 2 );
Beep( C, T / 2 );
Beep( A, T );
Beep( A, T );
Beep( A, T );
Beep( G, T );
Beep( F, T );
Beep( E, T );
Beep( D, T * 2 );
Beep( E, T );
Beep( _A, T * 3 );
Beep( _B, T / 2 );
Beep( _B, T / 2 );
Beep( _A, T );
Beep( E, T );
Beep( D, T / 2 * 3 );
Sleep( Stop / 2 );
Beep( _A, T / 2 );
Beep( _B, T / 2 );
Beep( C, T * 6 );
} Beep( G, T * 3 );
Beep( G, T * 3 );
Beep( C_, T * 10 );
Sleep( Stop * 2 ); return 0;
}

当 C++ 遇上音乐的更多相关文章

  1. 当c++遇上音乐

    运用到的函数 #include <windows.h> Beep( f, t ); Sleep( t ); eep() 函数可以让蜂鸣器发出频率为f赫兹,音长大约为 2t 毫秒的音.(注意 ...

  2. MVC遇上bootstrap后的ajax表单模型验证

    MVC遇上bootstrap后的ajax表单验证 使用bootstrap后他由他自带的样式has-error,想要使用它就会比较麻烦,往常使用jqueyr.validate的话只有使用他自己的样式了, ...

  3. 敏捷遇上UML-需求分析及软件设计最佳实践(郑州站 2014-6-7)

      邀请函: 尊敬的阁下:我们将在郑州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实 ...

  4. 敏捷遇上UML—软创基地马年大会(广州站 2014-4-19)

        我们将在广州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战技巧. 时间:2 ...

  5. 敏捷遇上UML——软创基地马年大会(深圳站 2014-3-15)

    邀请函: 尊敬的阁下: 我们将在深圳为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战 ...

  6. 初识genymotion安装遇上的VirtualBox问题

    想必做过Android开发的都讨厌那慢如蜗牛的 eclipse原生Android模拟器吧! 光是启动这个模拟器都得花上两三分钟,慢慢的用起来手机来调试,但那毕竟不是长久之计,也确实不方便,后来知道了g ...

  7. SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案

    SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...

  8. 当创业遇上O2O,新一批死亡名单,看完震惊了!

    当创业遇上O2O,故事就开始了,总投入1.6亿.半年开7家便利店.会员猛增至10万……2015半年过去后,很多故事在后面变成了一场创业“事故”,是模式错误还是烧钱过度?这些项目的失败能给国内创业者带来 ...

  9. LoadRunner - 当DiscuzNT遇上了Loadrunner(下) (转发)

    当DiscuzNT遇上了Loadrunner(下) 在之前的两篇文章中,基本上介绍了如何录制脚本和生成并发用户,同时还对测试报告中的几个图表做了简单的说明.今天这篇文章做为这个系列的最后一篇,将会介绍 ...

随机推荐

  1. CST2017 安装问题

    1.需要修改破解文件license 中的电脑名称 2.若lincense 中有时间限制  需要把时间都修改   比如  到期为  1-jan-2018   则修改为1-jan-2019   所有的都需 ...

  2. PHP生成当前月份包括最近12个月内的月份

    直接上代码: $time=array(); $currentTime = time(); $cyear = floor(date("Y",$currentTime)); $cMon ...

  3. ES6新特性-函数的简写(箭头函数)

    通常函数的定义方法 var fn = function(...){ ...... } //例如: var add = function(a,b){ return a+b; } //或者: functi ...

  4. 20175211 2018-2019-2 《Java程序设计》第五周学习总结

    目录 教材学习内容总结 第六章 接口与实现 教材学习中的问题和解决过程 代码调试中的问题和解决过程 代码托管 上周考试错题总结 学习进度条 参考资料 教材学习内容总结 第六章 接口与实现 6.1 接口 ...

  5. PyAutoGUI——让所有GUI都自动化

    2015-08-17:输入中文bug没有解决,目前的解决方案是Python 2.X环境下安装pyperclip和pyautogui,用复制粘贴来实现. In [ ]: import pyperclip ...

  6. python虚拟环境迁移

    python虚拟环境迁移: 注意事项:直接将虚拟环境复制到另一台机器,直接执行是会有问题的. 那么可以采用以下办法: 思路:将机器1虚拟环境下的包信息打包,之后到机器2上进行安装: (有两种情况要考虑 ...

  7. 《linux就该这么学》第十三节课:第11章和第12章,vsftpd服务与samba和nfs服务

    第十一章 (借鉴请改动) 11.1.文件传输协议 FTP文件穿数协议,端口20用于数据传输,21端口用于传输相关FTP命令 ftp协议的两种工作模式: 主动模式:ftp向客户端发起 被动模式(默认): ...

  8. web_app框架

    web app 建立在asyncio的基础上,因此用aiohttp写一个基本的app.py import logging; logging.basicConfig(level=logging.INFO ...

  9. CentOS7 配置ISCSI targetcli 共享存储

  10. DIJKSTRA 临接表

    #include <iostream> #include <algorithm> #include <cmath> #include <cstdio> ...