欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

那些在MIDI庫里徘徊的十六分音符

終究沒能拼成告白的主歌

我把周杰倫的《晴天》寫成C++的類

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

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

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

在堆棧溢出的夜晚

終將明白

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

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

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

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

--題記

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

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

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

沒環(huán)境?巧了,她也如是說。

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

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

開始工作

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

CMakeLists.txt中:

target_link_libraries(SunnyDays winmm)

MIDIHelper.h中:

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

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

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è)樂器分配一個(gè)通道channel(0~15,通常9分配給打擊類樂器,例如鼓組),控制音量volume,然后就可以開始演奏了。

自制簡(jiǎn)易樂譜

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)這天 我試過握著你手
                 1,4,
                 6,4,
                //但偏偏 雨漸漸
                 4,2,
                5,2,
                 //大到我看你不見
                 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è)置樂器(特定音色)
     setInstrument(channelChord,ElectricPiano1);
     setInstrument(channelMainLine,ElectricPiano1);
 
 
     system("pause");//按下回車,就開始啦
     beginLogger();
 
 
     thread t0(voice);
     thread t1(mainLine);
     thread t2(bassDrum);
     thread t3(chord);
     t0.join();
     t1.join();
     t2.join();
     t3.join();
 
     closeDevice();
     //...
 }

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

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

相關(guān)文章

  • C++四種cast使用詳細(xì)介紹

    C++四種cast使用詳細(xì)介紹

    本文主要介紹了C++四種cast使用詳細(xì)介紹,今天我們要來講的是顯式類型轉(zhuǎn)換,C++提供了四種顯式類型轉(zhuǎn)換,分別是:static_cast、dynamic_cast、const_cast、reinterpret_cast,感興趣的可以了解一下
    2022-07-07
  • Matlab實(shí)現(xiàn)帶豎線散點(diǎn)的核密度圖的繪制

    Matlab實(shí)現(xiàn)帶豎線散點(diǎn)的核密度圖的繪制

    核密度估計(jì)是用于估計(jì)隨機(jī)變量概率密度函數(shù)的一種非參數(shù)方法。核密度圖不失為一種用來觀察連續(xù)型變量分布的有效方法。本文將用Matlab實(shí)現(xiàn)帶豎線散點(diǎn)的核密度圖的繪制,感興趣的可以了解一下
    2022-08-08
  • C語言中動(dòng)態(tài)內(nèi)存管理圖文詳解

    C語言中動(dòng)態(tài)內(nèi)存管理圖文詳解

    在編寫程序時(shí),通常并不知道需要處理的數(shù)據(jù)量,或者難以評(píng)估所需處理數(shù)據(jù)量的變動(dòng)程度,下面這篇文章主要給大家介紹了關(guān)于C語言中動(dòng)態(tài)內(nèi)存管理的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 淺談C++/C關(guān)于#define的那些奇奇怪怪的用法

    淺談C++/C關(guān)于#define的那些奇奇怪怪的用法

    本文主要介紹了C++/C關(guān)于#define的那些奇奇怪怪的用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • VSCode配置C語言環(huán)境的方法

    VSCode配置C語言環(huán)境的方法

    這篇文章主要介紹了VSCode配置C語言環(huán)境的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 關(guān)于C++面向?qū)ο笤O(shè)計(jì)的訪問性問題詳解

    關(guān)于C++面向?qū)ο笤O(shè)計(jì)的訪問性問題詳解

    這篇文章主要給大家介紹了關(guān)于C++面向?qū)ο笤O(shè)計(jì)的訪問性問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟

    Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟

    這篇文章主要介紹了Visual Studio 2019 如何新建 Win32項(xiàng)目的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C++11 <future>中std::promise 介紹

    C++11 <future>中std::promise 介紹

    這篇文章主要介紹了C++11 <future>中std::promise 介紹,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • C++之boost::array的用法

    C++之boost::array的用法

    這篇文章主要介紹了C++之boost::array的用法,以實(shí)例的形式簡(jiǎn)單講述了靜態(tài)數(shù)組的容器boost::array的使用技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-10-10
  • QT使用QML實(shí)現(xiàn)地圖繪制虛線的示例代碼

    QT使用QML實(shí)現(xiàn)地圖繪制虛線的示例代碼

    QML提供了MapPolyline用于在地圖上繪制線段,這篇文章主要為大家詳細(xì)介紹了QT如何使用QML實(shí)現(xiàn)在地圖上繪制虛線,需要的小伙伴可以參考一下
    2023-07-07

最新評(píng)論