根據鋼琴音調頻率對照表,使用sin函數構造對應頻率正弦波數據模擬各琴鍵聲音,實現簡易鋼琴效果,結果寫入wav文件中。
目錄
- 程序效果
- 實現過程
- 樣例代碼
- 測試用例
- 參考資料
程序效果
截圖1:鍵位圖 鋼琴鍵盤結構,包含3組Do Re Mi Fa So La Xi以及空格靜音代表的空拍,使用字符'z'結束。
截圖2:使用效果-歡樂頌 輸入音符後,使用'z'回車即可結束錄入,生成的文件位於D盤根目錄下,播放即可。
實現過程
截圖3:流程圖
主要流程如上,需要注意的細節有以下幾處:
0.每秒44100採樣時,一個音符想要延續0.5秒左右就需要一個長度爲22050的數組構造正弦波,不現實,採用s[1000]數組構造正弦波,使用for循環重複至30000左右。
1.使用有限長度數組構造正弦波,由於Π爲無理數,在實際計算數值時,很難取到完整週期的分界點。
截圖4:不連續現象
分界點選取間隔不大時,人耳聽不出明顯的噪音。
2.每個音符末尾需要插入一小段短暫靜音,否則連續2各相同音會聽成一個長音。
截圖5:單音末尾靜音
3.空拍使用完整靜音
截圖6:空拍的靜音
樣例代碼
1.共享鏈接:C語言實現簡易鋼琴-利用sin函數構造不同頻率波形模擬各琴鍵發音
https://download.csdn.net/download/u013025955/11174606
2.關鍵代碼
//鋼琴鍵盤結構,包含3組Do Re Mi Fa So La Xi以及空格靜音代表的空拍
struct MusicNote
{
char noteName; //音符名稱
int frequence; //音符頻率,根據鋼琴音高頻率對照表定義
DWORD noteLength; //在使用sin函數構造正弦波信號的單個音實際字節長度,用於計算總長度,填寫在wav文件頭部,同時該長度讓每個音能延續0.5秒左右,由nearZero*4*2*40算出,可以自定義時長,只要是nearZero的整數倍都行
DWORD nearZero; //在使用sin函數構造正弦波信號時,不同頻率採樣值最接近完整週期的數據點,避免取不到完整週期導致雜音出現
}note[NOTENUM] = {
{'a', 130, 108800, 680}, {'q', 261, 81600, 170}, {'1', 523, 80960, 253},
{'s', 146, 72640, 908}, {'w', 293, 60320, 754}, {'2', 587, 84320, 527},
{'d', 164, 86400, 270}, {'e', 329, 75200, 940}, {'3', 659, 69680, 871},
{'f', 174, 40460, 508}, {'r', 349, 101280, 633}, {'4', 698, 55680, 696},
{'g', 195, 72480, 906}, {'t', 391, 90400, 565}, {'5', 784, 72320, 226},
{'h', 220, 64320, 402}, {'y', 440, 64320, 402}, {'6', 880, 68240, 853},
{'j', 246, 86240, 539}, {'u', 493, 78800, 985}, {'7', 988, 78720, 492},
{' ', 0, 80000, 1000}
};
//正弦波生成函數
void sin_sound(WORD *p, int n, int f)
{
int i;
double x, temp1;
x = 2*PI*f/44100; //2Πf即可獲得對應頻率波形,44100爲音頻文件頭定義的採樣率,也需要考慮進去
for (i=0; i<n; i++) //n的值通過本地測試21次,分別確定的,確定方法爲查看正弦函數值從負數變成整數,經過x軸時最接近0的點,即最接近完整週期的點
{
temp1 = 5000 * sin(x*i);
p[i] = (WORD)temp1;
}
}
測試用例
截圖7:測試用例
參考資料
1.利用正弦波產生WAV文件
https://download.csdn.net/download/guo19910426/5697181
2.鋼琴的音高與頻率對照表
https://max.book118.com/html/2016/0314/37632175.shtm