前幾天在洛谷日報征文中看到了這樣一篇文章: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;
}