如何用 C++ 演奏《起风了》

· · 科技·工程

如何用 C++ 演奏《起风了》

这是一个让学编程的和学音乐的都沉默的大问题。

理论基础

MIDI 音乐是一种古老的 Windows 文件格式,适用于当时存储空间很小的音乐存储。本项目使用 MIDI 格式乐谱来实现演奏。

(Mac 和 Linux 用户可以走了)

实现原理

理论上我们只要把乐谱存进代码或文件里,然后用 Windows 的 MIDI API 函数来演奏内存中的乐谱就可以了。

#include <Windows.h>          // 导入 Windows API
#pragma comment(lib, "winmm.lib") // 引用 MIDI API

MIDI 的乐谱是纯数字的,这时候学音乐的表示不同意:“乐谱总得让人能看懂吧!”为了解决这一个毫不起眼的小问题,我们可以使用枚举类型、宏定义,在这里笔者选择用 #define 宏定义。

#pragma once

#define Rest 0

#define C8  108

#define B7  107
#define A7s 106
#define A7  105
#define G7s 104
#define G7  103
#define F7s 102
#define F7  101
#define E7  100
#define D7s  99
#define D7   98
#define C7s  97
#define C7   96

#define B6   95
#define A6s  94
#define A6   93
#define G6s  92
#define G6   91
#define F6s  90
#define F6   89
#define E6   88
#define D6s  87
#define D6   86
#define C6s  85
#define C6   84

#define B5   83
#define A5s  82
#define A5   81
#define G5s  80
#define G5   79
#define F5s  78
#define F5   77
#define E5   76
#define D5s  75
#define D5   74
#define C5s  73
#define C5   72

#define B4   71
#define A4s  70
#define A4   69
#define G4s  68
#define G4   67
#define F4s  66
#define F4   65
#define E4   64
#define D4s  63
#define D4   62
#define C4s  61
#define C4   60

#define B3   59
#define A3s  58
#define A3   57
#define G3s  56
#define G3   55
#define F3s  54
#define F3   53
#define E3   52
#define D3s  51
#define D3   50
#define C3s  49
#define C3   48

#define B2   47
#define A2s  46
#define A2   45
#define G2s  44
#define G2   43
#define F2s  42
#define F2   41
#define E2   40
#define D2s  39
#define D2   38
#define C2s  37
#define C2   36

#define B1   35
#define A1s  34
#define A1   33
#define G1s  32
#define G1   31
#define F1s  30
#define F1   29
#define E1   28
#define D1s  27
#define D1   26
#define C1s  25
#define C1   24

#define B0   23
#define A0s  22
#define A0   21

#define X1   C2
#define X2   D2
#define X3   E2
#define X4   F2
#define X5   G2
#define X6   A2
#define X7   B2

#define L1   C3
#define L2   D3
#define L3   E3
#define L4   F3
#define L5   G3
#define L6   A3
#define L7   B3

#define M1   C4
#define M2   D4
#define M3   E4
#define M4   F4
#define M5   G4
#define M6   A4
#define M7   B4

#define H1   C5
#define H2   D5
#define H3   E5
#define H4   F5
#define H5   G5
#define H6   A5
#define H7   B5

#define LOW_SPEED    500
#define MIDDLE_SPEED 400
#define HIGH_SPEED   300

#define _         0x00ff

定义好这些音符后,我们就可以来写我们的乐谱啦!你问我用什么?当然用数组啊!

int wind[] =
{
     400,0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,0,L7,M1,M2,M3,300,L2,0,M5,M3,M2,M3,M1,M2,L7,M1,300,L5,0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,0,L7,M1,M2,M3,300,L2,0,M5,M3,M2,M3,M1,M2,L7,M1,300,L5,
     0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,0,L7,M1,M2,M3,300,L2,0,M5,M3,M2,M3,M1,M2,L7,M1,300,L5,0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,
     0,M6,M3,M2,L6,M3,L6,M2,M3,L6,_,_,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M1,300,M2,700,0,M1,M2,M3,M2,M1,300,L5,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M3,300,M2,0,M1,700,300,M2,_,_,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M3,300,M2,0,M1,700,300,L6,_,
     0,M3,M2,M1,M2,300,M1,_,0,M3,M2,M1,M2,300,M1,700,0,L5,M3,M2,M1,M2,300,M1,_,_,_,
     M1,M2,M3,M1,M6,0,M5,M6,300,_,700,0,M1,300,M7,0,M6,M7,300,_,_,M7,0,M6,M7,300,_,M3,0,H1,H2,H1,M7,300,M6,M5,M6,0,M5,M6,_,M5,M6,M5,300,M6,0,M5,M2,300,_,0,M5,700,300,M3,_,_,_,
     M1,M2,M3,M1,M6,0,M5,M6,300,_,700,0,M1,300,M7,0,M6,M7,300,_,_,M7,0,M6,M7,300,_,M3,0,H1,H2,H1,M7,300,M6,M5,M6,0,H3,H3,300,_,M5,M6,0,H3,H3,300,_,0,M5,700,300,M6,_,_,_,_,_,
     H1,H2,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H2,0,H1,M6,300,_,0,H1,H1,300,H2,0,H1,300,M6,700,0,_,300,H1,700,H3,_,0,H3,H4,H3,H2,H3,300,H2,700,
     H1,H2,H3,0,H6,H5,_,H6,H5,_,H6,H5,300,_,H3,H3,0,H6,H5,_,H6,H5,_,H6,H5,700,300,H3,700,H2,0,H1,M6,700,300,
     H3,700,H2,0,H1,300,M6,700,H1,H1,_,_,_,_,_,
     0,M6,300,H3,700,H2,0,H1,M6,700,300,H3,H2,700,300,0,H1,M6,300,700,H1,H1,_,_,
     0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,0,L7,M1,M2,M3,300,L2,0,M5,M3,M2,M3,M1,M2,L7,M1,300,L5,0,L7,M1,M2,M3,300,L3,0,M5,M3,300,L2,L5,2,_,
     0,M6,M3,M2,L6,M3,L6,M2,M3,L6,_,_,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M1,300,M2,700,0,M1,M2,M3,M2,M1,300,L5,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M3,300,M2,0,M1,700,300,M2,_,_,_,
     M2,700,0,M1,300,M2,700,0,M1,300,M2,M3,M5,0,M3,700,300,M2,700,0,M3,300,M2,0,M1,700,300,L6,_,
     0,M3,M2,M1,M2,300,M1,_,0,M3,M2,M1,M2,300,M1,700,0,L5,M3,M2,M1,M2,300,M1,_,_,_,
     M1,M2,M3,M1,M6,0,M5,M6,300,_,700,0,M1,300,M7,0,M6,M7,300,_,_,M7,0,M6,M7,300,_,M3,0,H1,H2,H1,M7,300,M6,M5,M6,0,M5,M6,_,M5,M6,M5,300,M6,0,M5,M2,300,_,0,M5,700,300,M3,_,_,_,
     M1,M2,M3,M1,M6,0,M5,M6,300,_,700,0,M1,300,M7,0,M6,M7,300,_,_,M7,0,M6,M7,300,_,M3,0,H1,H2,H1,M7,300,M6,M5,M6,0,H3,H3,300,_,M5,M6,0,H3,H3,300,_,0,M5,700,300,M6,_,_,_,_,_,
     H1,H2,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H2,0,H1,M6,300,_,0,H1,H1,300,H2,0,H1,300,M6,700,0,_,300,H1,700,H3,_,0,H3,H4,H3,H2,H3,300,H2,700,
     H1,H2,H3,0,H6,H5,_,H6,H5,_,H6,H5,300,_,H3,H3,0,H6,H5,_,H6,H5,_,H6,H5,700,300,H3,700,H2,0,H1,M6,700,300,
     H3,700,H2,0,H1,300,M6,700,H1,H1,_,_,_,_,_,
     H1,H2,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H2,0,H1,M6,300,_,0,H1,H1,300,H2,0,H1,300,M6,700,0,_,300,H1,700,H3,_,0,H3,H4,H3,H2,H3,300,H2,700,
     H1,H2,H3,0,H6,H5,_,H6,H5,_,H6,H5,300,_,H3,H3,0,H6,H5,_,H6,H5,_,H6,H5,700,300,H3,700,H2,0,H1,M6,700,300,
     H3,700,H2,0,H1,300,M6,700,H1,H1,_,_,_,_,_,
     H1,H2,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H3,0,H6,H5,300,_,0,H6,H5,300,_,0,H6,H5,300,_,0,H2,H3,300,H2,0,H1,M6,300,_,0,H1,H1,300,H2,0,H1,300,M6,700,0,_,300,H1,700,H3,_,0,H3,H4,H3,H2,H3,300,H2,700,
     H1,H2,H3,0,H6,H5,_,H6,H5,_,H6,H5,300,_,H3,H3,0,H6,H5,_,H6,H5,_,H6,H5,700,300,H3,700,H2,0,H1,M6,700,300,
     H3,700,H2,0,H1,300,M6,700,H1,H1,_,_,_,_,_,
     0,M6,300,H3,700,H2,0,H1,M6,700,300,H3,H2,700,300,0,H1,M6,300,700,H1,H1,_,_,_,_,_,_,_,
};

乐谱写好之后,我们编写一个 Wind(void) 函数,用来播放《起风了》这首歌。

void Wind(void)
{
    HMIDIOUT handle;
    midiOutOpen(&handle, 0, 0, 0, CALLBACK_NULL);
    int volume = 0x7f;
    int voice = 0x0;
    int sleep = 350;

    for (auto i : wind) { // 这里使用了 C++ 11 的特性
        if (i == 0) { sleep = 175; continue; }
        if (i == 700) { Sleep(175); continue; }
        if (i == 300) { sleep = 350; continue; }
        if (i == _) {
            Sleep(350); // 同下
            continue;
        }
    }

    voice = (volume << 16) + ((i) << 8) + 0x90;
    std::cout << voice << std::endl;
    midiOutShortMsg(handle, voice);
    Sleep(sleep); // 控制节奏
    midiOutClose(handle);
}

最后再编写一个 main 函数就大功告成啦!

int main(void)
{
    Wind();
    return 0; // 笔者喜欢 return 0;
}

编译运行!听到歌声了吗?这就是编程的魅力!
PS:如果编译错误,请注意一下 C++ 版本,一定要在 C++ 11 以上哦!

转载于 https://www.bilibili.com/video/BV1tv4y1D7iW/,可观看原视频:

点赞过 50 持续更新!