最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++使用MIDI庫(kù)實(shí)現(xiàn)演奏晴天

 更新時(shí)間:2025年02月14日 09:27:06   作者:TwilightLemon  
這篇文章主要為大家詳細(xì)介紹了C++如何使用MIDI庫(kù)實(shí)現(xiàn)演奏晴天歌曲,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下

前言

那些在MIDI庫(kù)里徘徊的十六分音符

終究沒(méi)能拼成告白的主歌

我把周杰倫的《晴天》寫(xiě)成C++的類

在每個(gè)midiEvent里埋藏故事的小黃花

調(diào)試器的斷點(diǎn)比初戀更漫長(zhǎng)

而青春不過(guò)是一串未導(dǎo)出的cmake工程文件

在堆棧溢出的夜晚

終將明白

有些旋律永遠(yuǎn)停在#pragma once的注釋里

有些人永遠(yuǎn)停在未定義的引用里

或許你我的心跳終歸運(yùn)行在不同的時(shí)鐘頻率

卻愿始終記得如何編譯出一場(chǎng)永不落幕的晴天

--題記

就像在題記里說(shuō)的一樣,這是一個(gè)從未導(dǎo)出成功的工程文件。

所以如果你也想聽(tīng)聽(tīng),可以在PowerShell里運(yùn)行以下指令:

git clone https://github.com/TwilightLemon/SunnyDays
cd SunnyDays
mkdir build
cd build
cmake .. -G "MinGW Makefiles"
mingw32-make
./SunnyDays.exe

沒(méi)環(huán)境?巧了,她也如是說(shuō)。

幸運(yùn)的話能得到以下效果:

下面來(lái)簡(jiǎn)單講講如何使用C++和MIDI庫(kù)作曲吧。

開(kāi)始工作

1. 引入MIDI庫(kù)和相關(guān)控制類

CMakeLists.txt中:

target_link_libraries(SunnyDays winmm)

MIDIHelper.h中:

#include <windows.h>
#pragma comment(lib,"winmm.lib")

定義Scale(音階), Instrument(樂(lè)器, 僅包括部分)等枚舉。我把Drum單獨(dú)提了出來(lái)。

enum Scale
{
    X1 = 36, X2 = 38, X3 = 40, X4 = 41, X5 = 43, X6 = 45, X7 = 47,
    L1 = 48, L2 = 50, L3 = 52, L4 = 53, L5 = 55, L6 = 57, L7 = 59,
    M1 = 60, M2 = 62, M3 = 64, M4 = 65, M5 = 67, M6 = 69, M7 = 71,
    H1 = 72, H2 = 74, H3 = 76, H4 = 77, H5 = 79, H6 = 81, H7 = 83,
    LOW_SPEED = 500, MIDDLE_SPEED = 400, HIGH_SPEED = 300,
    _ = 0XFF
};
enum Drum{
    BassDrum = 36, SnareDrum = 38, ClosedHiHat = 42, OpenHiHat = 46
};
enum Instrument{
    AcousticGrandPiano = 0, BrightAcousticPiano = 1,
    ElectricGrandPiano = 2, HonkyTonkPiano = 3,
    ElectricPiano1 = 4, ElectricPiano2 = 5
};

一些基礎(chǔ)方法,包括初始化/關(guān)閉設(shè)備、設(shè)置參數(shù)、播放單個(gè)音符和播放和弦等。

void initDevice();
void closeDevice();
void setInstrument(int channel, int instrument);
void setVolume(int channel, int volume);

void PlayNote(HMIDIOUT handle, UINT channel, UINT note, UINT velocity);

void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT note4, UINT velocity);

void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT velocity);

MIDIHelper.cpp中:

void initDevice(){
    midiOutOpen(&hMidiOut, 0, 0, 0, CALLBACK_NULL);
}

void closeDevice(){
    midiOutClose(hMidiOut);
}

void setInstrument(int channel,int instrument){
    if (channel > 15 || instrument > 127) return;
    DWORD message = 0xC0 | channel | (instrument << 8);
    midiOutShortMsg(hMidiOut, message);
}

void setVolume(int channel,int volume){
    if (channel > 15 || volume > 127) return;
    DWORD message = 0xB0 | channel | (7 << 8) | (volume << 16);
    midiOutShortMsg(hMidiOut, message);
}

//播放單個(gè)音符,note是音符,velocity是力度
void PlayNote(HMIDIOUT handle, UINT channel, UINT note, UINT velocity) {
    if (channel > 15 || note > 127 || velocity > 127) return;
    DWORD message = 0x90 | channel | (note << 8) | (velocity << 16);
    midiOutShortMsg(handle, message);
}

//四指和弦
void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT note4, UINT velocity){
    if (channel > 15 || note1 > 127 || note2 > 127 || note3 > 127 || note4 > 127 || velocity > 127) return;
    DWORD message1 = 0x90 | channel | (note1 << 8) | (velocity << 16);
    DWORD message2 = 0x90 | channel | (note2 << 8) | (velocity << 16);
    DWORD message3 = 0x90 | channel | (note3 << 8) | (velocity << 16);
    DWORD message4 = 0x90 | channel | (note4 << 8) | (velocity << 16);
    midiOutShortMsg(handle, message1);
    midiOutShortMsg(handle, message2);
    midiOutShortMsg(handle, message3);
    midiOutShortMsg(handle, message4);
}

//三指和弦
void playChord(HMIDIOUT handle, UINT channel, UINT note1, UINT note2, UINT note3, UINT velocity) {
    if (channel > 15 || note1 > 127 || note2 > 127 || note3 > 127 || velocity > 127) return;
    DWORD message1 = 0x90 | channel | (note1 << 8) | (velocity << 16);
    DWORD message2 = 0x90 | channel | (note2 << 8) | (velocity << 16);
    DWORD message3 = 0x90 | channel | (note3 << 8) | (velocity << 16);
    midiOutShortMsg(handle, message1);
    midiOutShortMsg(handle, message2);
    midiOutShortMsg(handle, message3);
}

2. 初始化和結(jié)束

先在頭文件中定義一個(gè)全局MIDI句柄:

extern HMIDIOUT hMidiOut;

在入口處初始化MIDI設(shè)備并在結(jié)束時(shí)關(guān)閉:

HMIDIOUT hMidiOut;
int main() {
    initDevice();
    //...
    closeDevice();
    return 0;
}

初始化MIDI設(shè)備之后,為每一個(gè)樂(lè)器分配一個(gè)通道channel(0~15,通常9分配給打擊類樂(lè)器,例如鼓組),控制音量volume,然后就可以開(kāi)始演奏了。

自制簡(jiǎn)易樂(lè)譜

Voice.cpp為例,定義一個(gè)數(shù)組為頻譜,控制停頓和音符,遍歷數(shù)組播放:

 namespace SunnyDays{
      int channelVoice=1;
      void playVoice(int note, int velocity){
          PlayNote(hMidiOut, channelVoice, note, velocity);
      }
      void voice(){
         Sleep(13100);//等待前奏
          int sleep = 390;
          int data[] =
                 {
                     //故事的小黃花
                     -90,
                     300,M5,M5,M1,M1,_,M2,M3,_,
                     //從出生那年就飄著
                     -90,
                     M5,M5,M1,M1,0,M2,M3,300,M2,M1,_,
                    //童年的蕩秋千
                     -90,
                     300,M5,M5,M1,M1,_,M2,M3,_,
                     //隨記憶一直晃到現(xiàn)在
                     -90,  
                     M3,_,500,M2,M3,M4,M3,M2,M4,M3,700,M2,700,_,
                     //...
                 }
         for (auto i : data) {
            if(i==-30){logTime("Enter chorus");continue;}//調(diào)試用
             if(i==-90){NextLyric(); continue;}
             if (i == 0) { sleep = 180; continue; }
             //...
             if (i == _) {
                 Sleep(390);
                 continue;
             }
 
             playVoice(i, 80);
             Sleep(sleep);
         }
     }
 }

打個(gè)鼓:

 namespace SunnyDays{
      int channelBassDrum=9;
  
      void playDrum(int note, int velocity, int duration){
          PlayNote(hMidiOut, channelBassDrum, note, velocity);
          if(duration>0) {
              Sleep(duration);
              PlayNote(hMidiOut, channelBassDrum, note, 0);
         }
     }
 
     void bassDrum(){
         Sleep(11260);
         cout<<"Drum Bass Start!"<<endl;
         playDrum(SnareDrum,100,180);
         playDrum(SnareDrum,100,210);
         playDrum(BassDrum, 100, 210);
         playDrum(SnareDrum,100,190);
         playDrum(BassDrum, 100, 210);
         playDrum(SnareDrum,100,200);
         playDrum(SnareDrum,100,200);
         playDrum(OpenHiHat,100,-1);
         Sleep(200);
         //...
     }
 }

簡(jiǎn)易副歌和弦,是從B站一位up主那里學(xué)的(已經(jīng)忘記是哪位了qwq):

 namespace SunnyDays {
      int channelChord=2;
      void chordLevel(int level,int sleep,int repeat=2,int vel=70){
          repeat--;
          int down=8;
        if(level==1){
            //一級(jí)和弦 加右指
              playChord(hMidiOut, channelChord, M1, M3, M5, L1, vel);
              while(repeat--) {
               Sleep(sleep);
                playChord(hMidiOut, channelChord, M1, M3, M5, vel - down);
            }
         }else if(level==3){
             //三級(jí)和弦 加右指
             playChord(hMidiOut, channelChord, M3, M5, M7, L3, vel);
            while(repeat--) {
                Sleep(sleep);
                playChord(hMidiOut, channelChord, M3, M5, M7, vel - down);
             }
         }
         //...
     }
     void chord(){
         Sleep(63724);
         int sleep=740;
        int data[]={
                //刮風(fēng)這天 我試過(guò)握著你手
                 1,4,
                 6,4,
                //但偏偏 雨漸漸
                 4,2,
                5,2,
                 //大到我看你不見(jiàn)
                 1,4,
                 //還有多久 我才能
                 3,4,
                 //↑ 在你身邊
                 6,4,
                 //↓ 等到放晴的那天
                 4,4,
                 //↑ 也許我會(huì)比較好一點(diǎn)
                 5,4,
                 //..
         }
         int count=sizeof(data)/sizeof(int);
        for(int i=0;i<count;i+=2){
             cout<<"chord "<<data[i]<<"  x"<<data[i+1]<<endl;
             chordLevel(data[i],sleep,data[i+1]);
             Sleep(sleep);
         }
         //...
     }
 }

合成演奏

我用了一個(gè)笨蛋方法,用多線程單獨(dú)控制每一個(gè)通道,然后在主線程中調(diào)用:

int main(){
      //...
      initDevice();
     //設(shè)置音量
      setVolume(channelChord,80);
      setVolume(channelMainLine,80);
      setVolume(channelVoice,120);
      setVolume(channelBassDrum,80);
  
    //設(shè)置樂(lè)器(特定音色)
     setInstrument(channelChord,ElectricPiano1);
     setInstrument(channelMainLine,ElectricPiano1);
 
 
     system("pause");//按下回車,就開(kāi)始啦
     beginLogger();
 
 
     thread t0(voice);
     thread t1(mainLine);
     thread t2(bassDrum);
     thread t3(chord);
     t0.join();
     t1.join();
     t2.join();
     t3.join();
 
     closeDevice();
     //...
 }

(最后疊個(gè)甲,俺不懂音樂(lè)制作,更不會(huì)什么C++)

到此這篇關(guān)于C++使用MIDI庫(kù)實(shí)現(xiàn)演奏晴天的文章就介紹到這了,更多相關(guān)C++ MIDI演奏音樂(lè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • OpenCV基于距離變換和分水嶺實(shí)現(xiàn)圖像分割

    OpenCV基于距離變換和分水嶺實(shí)現(xiàn)圖像分割

    圖像分割是根據(jù)灰度、顏色、紋理和形狀等特征,把圖像分成若干個(gè)特定的、具有獨(dú)特性質(zhì)的區(qū)域。本文將基于距離變換和分水嶺實(shí)現(xiàn)圖像分割,需要的可以了解一下
    2022-09-09
  • C語(yǔ)言實(shí)現(xiàn)基于控制臺(tái)的電子時(shí)鐘

    C語(yǔ)言實(shí)現(xiàn)基于控制臺(tái)的電子時(shí)鐘

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)基于控制臺(tái)的電子時(shí)鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • VScode配置C語(yǔ)言環(huán)境完整版(親測(cè)可用)

    VScode配置C語(yǔ)言環(huán)境完整版(親測(cè)可用)

    這篇文章主要介紹了VScode配置C語(yǔ)言環(huán)境完整版,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • C++中的形參和實(shí)參區(qū)別解析

    C++中的形參和實(shí)參區(qū)別解析

    這篇文章主要介紹了C++中的形參和實(shí)參區(qū)別解析,本文將從定義、特性、傳遞方式等多個(gè)維度深入解析,幫你徹底掌握這對(duì)重要概念,需要的朋友可以參考下
    2025-09-09
  • C++實(shí)現(xiàn)讀取圖片長(zhǎng)度和寬度

    C++實(shí)現(xiàn)讀取圖片長(zhǎng)度和寬度

    這篇文章主要介紹了C++實(shí)現(xiàn)讀取圖片長(zhǎng)度和寬度,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-04-04
  • 詳解C語(yǔ)言中fseek函數(shù)和ftell函數(shù)的使用方法

    詳解C語(yǔ)言中fseek函數(shù)和ftell函數(shù)的使用方法

    這篇文章主要介紹了C語(yǔ)言中fseek函數(shù)和ftell函數(shù)的使用方法,兩個(gè)函數(shù)分別用于設(shè)置和返回文件指針stream的位置,需要的朋友可以參考下
    2016-03-03
  • OpenCV獲取圖像中直線上的數(shù)據(jù)具體流程

    OpenCV獲取圖像中直線上的數(shù)據(jù)具體流程

    對(duì)圖像進(jìn)行處理時(shí),經(jīng)常會(huì)有這類需求:客戶想要提取出圖像中某條直線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進(jìn)行重點(diǎn)關(guān)注,怎么操作呢,下面小編通過(guò)實(shí)例代碼介紹下OpenCV獲取圖像中直線上的數(shù)據(jù),一起看看吧
    2021-11-11
  • C語(yǔ)言實(shí)現(xiàn)輸出平均成績(jī)最高學(xué)生的信息

    C語(yǔ)言實(shí)現(xiàn)輸出平均成績(jī)最高學(xué)生的信息

    這篇文章主要介紹利用C語(yǔ)言實(shí)現(xiàn)輸出平均成績(jī)最高學(xué)生的信息,文章舉例說(shuō)明并附有詳細(xì)代碼,需要的朋友可以參考一下
    2021-10-10
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單餐飲管理與點(diǎn)餐系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單餐飲管理與點(diǎn)餐系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單餐飲管理與點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++?list模擬實(shí)現(xiàn)過(guò)程

    C++?list模擬實(shí)現(xiàn)過(guò)程

    list是基于雙向循環(huán)鏈表的順序容器,支持O(1)插入刪除,迭代器分類明確,insert不失效而erase導(dǎo)致當(dāng)前迭代器失效,與vector對(duì)比,list空間利用率低但靈活,適合頻繁增刪操作,通過(guò)迭代器封裝實(shí)現(xiàn)統(tǒng)一訪問(wèn)接口
    2025-09-09

最新評(píng)論

阜平县| 田林县| 密云县| 芮城县| 博罗县| 和硕县| 洛川县| 嫩江县| 伊宁县| 察隅县| 玛沁县| 新平| 锡林郭勒盟| 玛纳斯县| 上杭县| 全州县| 灵璧县| 元氏县| 黑水县| 乳源| 钟祥市| 峨山| 来安县| 贵南县| 平塘县| 太湖县| 玉门市| 朝阳县| 电白县| 武乡县| 山东省| 古浪县| 五峰| 鄂伦春自治旗| 衢州市| 朝阳区| 沁水县| 五寨县| 珲春市| 义乌市| 本溪|