c/c++midi音乐编程——做一架自己的钢琴

时间:2025-03-03 17:23:49

在学习了小张同学c语言midi编程钢琴演奏-****博客后,我又突发奇想,既然我们能够使用midi音乐编程自己谱曲与演奏,那我们何不自己做一架钢琴出来呢?

于是乎,说干就干,小张同学根据已有知识,并查阅资料,很快就搓了一架自己的钢琴出来

一、开发工具

我们使用的是visual studio2022这款IDE。visual studio2022是由微软开发的一款集成开发环境(IDE),用于开发各种类型的软件应用程序。它提供了丰富的工具和功能,包括代码编辑器、调试器、编译器、版本控制等,能够帮助开发人员更高效地编写、调试和部署应用程序。VS支持多种编程语言,包括C++、C#、、JavaScript等,适用于Windows平台上的应用程序开发。这里默认大家成功安装, 不再一一赘述。

二、项目创建

这里我们选择c++的空项目,并将项目扩展名.cpp重置为.c

三、引入头文件

这里我们使用的头文件有<>、<> 、<>、<>

还要引入动态链接库""

#include <>  
#include <>  
#include <>
#include <>
​#pragma comment(lib,"")

四、函数介绍 

在开始之前,我们要先简单介绍一下midi音乐调用的一些函数

包括:

  1. HMIDIOUT函数
  2. midiOutOpen函数
  3. midiOutShortMsg函数
  4. midiOutClose函数

这四个函数是midi音乐编程里最主要的函数,详细介绍请参见 c语言midi编程钢琴演奏-****博客

这里小张同学只带领大家使用,就不再一一介绍了, 

五、项目规划

菜单栏

 我们想让用户打开后进入菜单栏所以写入函数

void begin() {
    system("cls");
    printf("==========================================================\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t1:选择乐器\t\t\t||\n");
    printf("||\t\t       2:light show\t\t\t||\n");
    printf("||\t\t\t3:使用说明\t\t\t||\n");
    printf("||\t\t\t4:退出程序\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("==========================================================\n");
    int x;
    x = _getch();
    switch (x)
    {
    case '1':
        
        break;
    case '2':
        begin();
        break;
    case '3':
        begin();
        break;
    case '4':
        
        break;
    default:
        begin();
        break;
    }
}
int main() {
    begin();
    system("cls");
    return 0;
}

我们这里主要做后端,所以就不去引入图形库去做什么厉害的界面了(doge)

我们预先设定了三个功能:选择乐器、lightShow(灯光秀)、使用说明三个函数,下面我会一一讲解

选择乐器函数

再说选择乐器函数

void musicInstrument() {
    system("cls");
    printf("==========================================================\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t1:钢琴\t\t\t\t||\n");
    printf("||\t\t\t2:吉他\t\t\t\t||\n");
    printf("||\t\t\t3:笛子\t\t\t\t||\n");
    printf("||\t\t\t4:music\t\t\t\t||\n");
    printf("||\t\t\t5:返回\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("||\t\t\t\t\t\t\t||\n");
    printf("==========================================================\n");
    int x;
    x = _getch();
    switch (x)
    {
    case '1':
        piano();
        musicInstrument();
        break;
    case '2':
        guitar();
        musicInstrument();
        break;
    case '3':
        flute();
        musicInstrument();
        break;
    case '4':
        music();
        musicInstrument();
        break;
    case '5':
        begin();
        break;
    default:
        musicInstrument();
        break;
    }
}

 这里我们设定四个函数,分别是:

  • 钢琴piano
  • 吉他guitar
  • 笛子flute
  • 音乐music

前三个分别对应三个不同的音色

而最后一个函数可以把自己谱好的曲子放进去以实现点歌功能

灯光秀函数(lightShow)

这里灯光秀并非传统意义上的灯光秀,只是通过改变控制台颜色来实现炫目的效果,我将代码放到下面,大家如果有想改进的可以自己设计

使用说明函数

大家可以将自己程序的使用说明用printf打印出来,以便游客理解。

亦可以改成创作日志函数,记录自己游戏的开发历程

这里不再过多赘述……

六、正式开始

lightShow函数

我们这里直接调用Windows系统库中的函数来改变控制台的背景颜色,然后再用Switch函数与_getch函数键盘操控颜色的改变:

int light() {
    char a;
    while (a = _getch()){
        system("cls"); 
        switch (a)
        {
        case '1':
            system("color 10");
            break;
        case '2':
            system("color 20");
            break;
        case '3':
            system("color 30");
            break;
        case '4':
            system("color 40");
            break;
        case '5':
            system("color 50");
            break;
        case '6':
            system("color 60");
            break;
        case '7':
            system("color 70");
            break;
        case '8':
            system("color 80");
            break;
        case '9':
            system("color 90");
            break;
        case 'q':
        case 'Q':
            system("color a0");
            break;
        case 'w':
        case 'W':
            system("color b0");
            break;
        case 'e':
        case 'E':
            system("color c0");
            break;
        case 'r':
        case 'R':
            system("color d0");
            break;
        case 't':
        case 'T':
            system("color e0");
            break;
        case 'y':
        case 'Y':
            system("color f0");
            break;
        case 'u':
        case 'U':
            system("color 01");
            break;
        case '*':
            system("color 07");
            return 0;
            break;
        default:
            system("color 07");
            break;
        }
    } 
}

只要狂按对应按键就可以做出晃眼的效果了(=゚ω゚)ノ ---===≡≡≡

钢琴函数

打开midi设备

首先我们打开midi设备并将主声道音色设置为钢琴

​
unsigned long result;
HMIDIOUT handle;
midiOutOpen(&handle, 0, 0, 0, CALLBACK_NULL);
midiOutShortMsg(handle, 0 << 8 | 0xC0); //设置通道0的乐器为 大钢琴

从键盘获取按键数据

通过while循环与switch选择语句来从键盘获取按键数据

char a
while (a = _getch()) {
    
    //构造Note On消息
    
    switch (a)
    {
    case'1':
        note = 36;    
        velocity = 100; 
        break;
    case'2':
        note = 38;    
        velocity = 100; 
        break;
    case'3':
        note = 40;    
        velocity = 100; 
        break;
    case'4':
        note = 41;    
        velocity = 100; 
        break;
    case'5':
        note = 43;    
        velocity = 100; 
        break;
    case'6':
        note = 45;    
        velocity = 100; 
        break;
    case'7':
        note = 47;    
        velocity = 100; 
        break;
    case'Q':
    case'q':
        note = 48;    
        velocity = 100; 
        break;
    case'W':
    case'w':
        note = 50;    
        velocity = 100; 
        break;
    case'E':
    case'e':
        note = 52;    
        velocity = 100; 
        break;
    case'R':
    case'r':
        note = 53;    
        velocity = 100; 
        break;
    case'T':
    case't':
        note = 55;    
        velocity = 100; 
        break;
    case'Y':
    case'y':
        note = 57;    
        velocity = 100; 
        break;
    case'U':
    case'u':
        note = 59;    
        velocity = 100; 
        break;
    case'A':
    case'a':
        note = 60;    
        velocity = 100; 
        break;
    case'S':
    case's':
        note = 62;    
        velocity = 100; 
        break;
    case'D':
    case'd':
        note = 64;    
        velocity = 100; 
        break;
    case'F':
    case'f':
        note = 65;    
        velocity = 100; 
        break;
    case'G':
    case'g':
        note = 67;    
        velocity = 100; 
        break;
    case'H':
    case'h':
        note = 69;    
        velocity = 100; 
        break;
    case'J':
    case'j':
        note = 71;    
        velocity = 100; 
        break;
    case'Z':
    case'z':
        note = 72;    
        velocity = 100; 
        break;
    case'X':
    case'x':
        note = 74;    
        velocity = 100; 
        break;
    case'C':
    case'c':
        note = 76;    
        velocity = 100; 
        break;
    case'V':
    case'v':
        note = 77;    
        velocity = 100; 
        break;
    case'B':
    case'b':
        note = 79;    
        velocity = 100; 
        break;
    case'N':
    case'n':
        note = 81;    
        velocity = 100; 
        break;
    case'M':
    case'm':
        note = 83;    
        velocity = 100; 
        break;
    case'*':
        midiOutClose(handle);
        return 0;
        break;
    default:
         break;
    }
    
    //打印所弹奏的音符

    //发送MIDI消息
}

填充备注处

构造Note On消息
unsigned short note = 0;    //音符编号
unsigned short velocity = 100; // 力度值,范围通常在0-127
unsigned char statusByte = 0x90 | (0 & 0x0F);//0为声道
打印所弹奏的音符
int h = note;
int x = (h - 24) % 12;
char y = 'C'; // 初始化为默认音名C
int z = (h - 12) / 12;
switch (x)
{
case 0:
    y = 'C';
    break;
case 2:
    y = 'D';
    break;
case 4:
    y = 'E';
    break;
case 5:
    y = 'F';
    break;
case 7:
    y = 'G';
    break;
case 9:
    y = 'A';
    break;
case 11:
    y = 'B';
    break;
default:
    
    break;
}
if (z != (-1)) {
    printf("\n您弹奏了%c%d音", y, z);
}
 发送midi消息
// 组合3个字节的消息到一个DWORD值
unsigned long noteOnMsg = (((unsigned long)velocity << 16) | (((unsigned long)note) << 8) | (unsigned long)statusByte);
// 发送MIDI消息
midiOutShortMsg(handle, noteOnMsg);

将以上代码填充至备注处,我们的C语言钢琴便做好了数据结构部分。接下来,只需要在循环开始处printf一个带颜色的钢琴块,我们的C语言钢琴就基本完工了

写入琴键

system("cls");
printfcolor(" 1+ ,2+ ,3+ ,4+ ,5+ ,6+ ,7+ \n", 1, 2);
printfcolor(" 1  ,2  ,3  ,4  ,5  ,6  ,7  \n\n", 6, 2);
printfcolor(" 1  ,2  ,3  ,4  ,5  ,6  ,7  \n", 1, 3);
printfcolor(" Q  ,W  ,E  ,R  ,T  ,Y  ,U  \n\n", 6, 3);
printfcolor(" 1- ,2- ,3- ,4- ,5- ,6- ,7- \n", 1, 4);
printfcolor("A  ,S  ,D  ,F  ,G  ,H  ,J  \n\n", 6, 4);
printfcolor("1--,2--,3--,4--,5--,6--,7--\n", 1, 5);
printfcolor("Z  ,X  ,C  ,V  ,B  ,N  ,M  \n\n", 6, 5);
printfcolor("按“*”键返回……", 7, 8);

这样,我们的钢琴就完成了。大家感兴趣的话快去尝试一下吧!