日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【日常篇】002_五线谱调式推导

發布時間:2023/12/20 编程问答 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【日常篇】002_五线谱调式推导 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

五線譜調式推導

??很早的時候就聽說過各種諸如“X大調”、“X小調”這樣的術語,但聽了這么多年也沒有搞明白這究竟是什么意思。
??直到最近兩個月,才有大佬提示,其實各種調式升降記號都是可以由鋼琴鍵位平移的方式得到的,這提供了一種推斷調式的方法:在最常規的C大調基礎上,每個音都提升一個音程,然后再看有哪些音跑到了黑鍵上,就可以知道有幾個升號或降號了。
??盡管這個過程用手算也是很容易完成的,但考慮到自己在C++公選結課后,已經有兩年多的時間沒有碰過C++了。為了再次熟練這個比較重要的語言,本次推導過程將使用C++來完成。

基本思路

從C大調出發

??C大調在五線譜中,是沒有任何升降號的存在的:

??而如果將鍵位整體右移兩個半音,則可以得到兩個升號的調(D大調):

??因為一個八度共有12個半音,所以這樣的平移一共可以得到十二種調式。只要對C大調的鍵位進行12次平移,就可以得到全部的十二種調式。

調式在程序中的表示

??一個八度有12個半音,而在推導過程中需要不斷地將這些鍵往一個方向進行平移,因此考慮到循環移位。使用12個bit分別對應一個八度中的12個半音C、C#|Db、D、D#|Eb、E、F、F#|Gb、G、G#|Ab、A、A#|Bb、B,若鍵位在那個半音上則置1,否則置0。每一次往高音處移位時,超出B的部分都將會回到C的位置。

??讓第0位表示C,第1位表示C#……以此類推,第11位表示B,如果向左移位的話,則第11位被移動至第0位:

??例如,C大調對應的七個鍵位分別為C、D、E、F、G、A、B,則對應的bit序列為:101010110101。而向高音處平移兩個半音到D大調,則對應向左循環移位兩次,得到:101011010110,對應C#、D、E、F#、G、A、B。

??有了比較明確的思路后,就可以開始寫代碼了。

代碼實現

初始狀態:C大調

??前面已經提到,C大調的bit表示方法,而初始狀態又確認為C大調,因此這個數值可以作為常量存放在頭文件中:

int MODE = 0xAB5; //101010110101 <- reverse(101011010101)

位與音符的對應關系

??類似于python的字典,C++的標準庫也有提供字典這個結構。該結構存放在map庫中,include這個庫即可用std::map進行使用(這里已經用了using std::map,所以在使用時直接寫的map):

map<int, string> NOTE_MAP = {{0, "C",},{1, "C#",},{2, "D",},{3, "D#",},{4, "E",},{5, "F",},{6, "F#",},{7, "G",},{8, "G#",},{9, "A",},{10, "A#",},{11, "B",},{12, "C",},{13, "Db",},{14, "D",},{15, "Eb",},{16, "E",},{17, "F",},{18, "Gb",},{19, "G",},{20, "Ab",},{21, "A",},{22, "Bb",},{23, "B",}};

??在這里給了24個詞條,主要是因為有些調式是使用降號的,對于這樣的調式,應該使用降號表示法(對應字典的第13-24條)。

循環移位

??循環移位的思路非常簡單,就是向左移位一次,然后將最高位移動到第一位。這里沒有對第12位及以上的bit進行截斷,但也沒有影響,因為后續的操作不會涉及到更高位的bit:

/** brief 對調式向左(高音方向)移動一個單位** @param orgMode 原有的調式*/ void shiftMode(int &orgMode){orgMode <<= 1;orgMode = orgMode | !!((1 << 12) & orgMode); }

打印調式信息

??調式有升號調的和降號調這兩種,為了區分開來,在這里使用降號調的flag標記:dFlag,來將它們區分開來。如果有降號表示,則會在字典中查詢第13-24項的詞條:

/** brief 打印調式的相關信息** @param mode 調式* @param dFlag 降號表示,默認為0,即升號*/ void printModeInfo(int &mode, bool dFlag = false){for (int i = 0; i < 12; i++){int mask = (1 << i);if (mode & mask){std::cout << NOTE_MAP[i+12*dFlag] << "\t";}}std::cout << std::endl; }

main函數

??對每一次移位的結果,都進行打印即可。由于先前已有經驗,升號調和降號調是交替出現的,所以在這里就可以很容易地判斷出dFlag的值是true還是false:

int main() {int mapSize = NOTE_MAP.size();int shift = 0;do{printModeInfo(MODE, (((shift <= 5) && (shift % 2)) || ((shift >= 8) && !(shift % 2))) ? true : false);shiftMode(MODE);shift += 1;}while (shift < 12);return 0; }

運行結果及結論

??運行后得到如下輸出:

C D E F G A B C Db Eb F Gb Ab Bb C# D E F# G A B C D Eb F G Ab Bb C# D# E F# G# A B C D E F G A Bb C# D# F F# G# A# B C D E F# G A B C Db Eb F G Ab Bb C# D E F# G# A B C D Eb F G A Bb C# D# E F# G# A# B

??此即推導得到的12種調式,注意到升號和降號的數量是關于C大調對稱的,用圖像表示則更為直觀:

??沿著這個圖像順時針看,調式依次從C大調、降D大調、D大調……演變到B大調,最后再回到C大調。而這些大調,每一個都又有著相對應的小調,由于還沒有找出規律,就不在這里描述了。

??而對于C大調向上移動6個半音的調式——升F大調,則暫時難以在輸出結果中得以理解。

??而經過在overture和網上的查詢,得知升F大調由六個升記號或六個降記號組成:

??并且,E#=F,因為E和F之間只差一個半音。

??由此一來,調式這個問題也就得到了一個比較清晰的解答。

完整代碼

main.h

#include <map> #include <string>using std::map; using std::string;map<int, string> NOTE_MAP = {{0, "C",},{1, "C#",},{2, "D",},{3, "D#",},{4, "E",},{5, "F",},{6, "F#",},{7, "G",},{8, "G#",},{9, "A",},{10, "A#",},{11, "B",},{12, "C",},{13, "Db",},{14, "D",},{15, "Eb",},{16, "E",},{17, "F",},{18, "Gb",},{19, "G",},{20, "Ab",},{21, "A",},{22, "Bb",},{23, "B",}};int MODE = 0xAB5; //101010110101 <- reverse(101011010101)

main.cpp

#include <iostream> #include <string> #include "main.h"using std::map; using std::string;void shiftMode(int &orgMode); void printModeInfo(int &mode, bool dFlag);int main() {int mapSize = NOTE_MAP.size();int shift = 0;do{printModeInfo(MODE, (((shift <= 5) && (shift % 2)) || ((shift >= 8) && !(shift % 2))) ? true : false);shiftMode(MODE);shift += 1;}while (shift < 12);return 0; }/** brief 對調式向左(高音方向)移動一個單位** @param orgMode 原有的調式*/ void shiftMode(int &orgMode){orgMode <<= 1;orgMode = orgMode | !!((1 << 12) & orgMode); }/** brief 打印調式的相關信息** @param mode 調式* @param dFlag 降號表示,默認為0,即升號*/ void printModeInfo(int &mode, bool dFlag = false){for (int i = 0; i < 12; i++){int mask = (1 << i);if (mode & mask){std::cout << NOTE_MAP[i+12*dFlag] << "\t";}}std::cout << std::endl; }

總結

以上是生活随笔為你收集整理的【日常篇】002_五线谱调式推导的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。