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

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

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

一、开发工具

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

二、项目创建

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

三、引入头文件

这里我们使用的头文件有<stdio.h>、<conio.h> 、<mmsystem.h>、<windows.h>

还要引入动态链接库"winmm.lib"

#include <windows.h>  
#include <mmsystem.h>  
#include <stdio.h>
#include <conio.h>
​#pragma comment(lib,"winmm.lib")

四、函数介绍 

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

包括:

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

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

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

五、项目规划

菜单栏

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

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);

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

  • 54
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 本章主要针对初学者,简要介绍了c/c++的基本语法和常见的编译错误。首先,介绍了程序的基本结构和常用的数据类型。c语言的基本语句包括赋值语句、条件语句和循环语句等,这些语句都可以组成程序的逻辑结构。接下来,介绍了函数的定义和调用,以及参数传递的方式。对于c++,还介绍了一些面向对象的概念,例如类、对象、成员函数等。同时,也提到了头文件和命名空间的使用方法。 在编程过程中,常常会出现各种编译错误,例如语法错误、类型不匹配、语义错误等,需要学会如何查看和解决这些错误。此外,还介绍了调试工具和各种常用的运算符和表达式,这些都是初学者必须掌握的基础知识。 总的来说,本章是关于c/c++快速入门的一篇简介性文章。虽然只是涉及到了基础的语法和知识点,但对于初学者而言是一个很好的起点。在学习过程中需要不断实践,积累经验,并不断深入了解更高级的编程技术和工具。 ### 回答2: C/C++是一种广泛使用的编程语言,它具有高效、灵活、可移植等特点,在各个领域得到广泛应用。C语言是C++语言的基础,在学习C++之前,需要先掌握C语言的基础知识。 本文介绍的是C/C++教程中的第二章——快速入门C/C++。在这一章节中,我们将介绍C语言的基本语法、变量、运算符和流程控制语句等基础知识。以下是C语言的一些基本知识点。 C语言的基本语法: C语言程序由多个函数组成,其中一个函数必须命名为main(),程序从该函数开始执行。C程序中的语句以分号结束,注释使用“//”表示单行注释,“/* */”表示多行注释。 变量和数据类型: C语言中变量的定义格式为“数据类型 变量名”;数据类型包括基本类型和用户自定义类型。C语言中的基本类型有int类型、char类型、float类型和double类型等。其中,int类型表示整型,char类型表示字符型,float类型和double类型表示浮点型。 运算符: C语言中的运算符包括算术运算符、关系运算符、逻辑运算符等。例如,“+”表示加法运算符,“>=”表示大于等于运算符,“&&”表示逻辑与运算符。 流程控制语句: C语言中的流程控制语句包括if语句、switch语句、while语句、do-while语句和for语句等。这些语句可以根据条件执行相应的语句块。 总之,本章节的快速入门C/C++,具有基本语法、变量、运算符和流程控制语句等基础知识。初学者可以通过这些基础知识,轻松入门C/C++,为后续学习打下基础。同时,要注意编写代码的规范和逻辑性,才能更好的理解和使用C/C++语言。 ### 回答3: C语言是一门广泛使用的编程语言,具有高效、灵活、稳定等特点,被广泛应用于嵌入式系统、操作系统、驱动程序、多媒体应用等领域。学习C语言是程序员的必备技能之一。 第二章的快速入门C/C++教程,主要介绍了C/C++语言的基础知识,重点是程序的结构和输入输出。其中程序结构包括函数、语句、变量、表达式等,而输入输出则包括scanf、printf、getchar和putchar等函数。 # 程序结构 程序结构是指程序的基本构成单元,包括函数、语句、变量、表达式等。C语言中的程序结构主要包含以下几个方面。 ## 函数 C语言中,函数是程序的基本组成单元。一个C程序可以由一个或多个函数组成,每个函数可以完成一个任务。函数的格式如下: ```c 返回类型 函数名(参数1, 参数2, ...){ // 函数体 return 返回值; } ``` 其中,返回类型指函数执行后的返回值类型;函数名是由程序员定义的,用于调用函数时识别函数;参数列表是函数的输入参数,可以有多个参数,每个参数由类型和变量名组成;函数体是函数要执行的代码块;return语句可以返回函数的执行结果。 ## 语句 语句是完成特定功能的一组指令。C语言中的语句包括赋值语句、条件语句、循环语句等。C语言中通常使用花括号来表示语句块。例如,下面是一个if语句的例子。 ```c if(条件){ // if语句块 }else{ // else语句块 } ``` 如果条件为真,则执行if语句块中的代码;否则执行else语句块中的代码。 ## 变量 变量是用于存储数据的一种容器。在C语言中,一个变量包括变量名、类型和值。变量名由程序员定义,用于识别变量;类型指变量的数据类型,如整型、字符型、实型等;值是存储在变量中的数据。变量的定义格式如下。 ```c 数据类型 变量名 = 值; ``` 例如,下面是一个整型变量的定义。 ```c int num = 10; ``` ## 表达式 表达式是由变量、运算符和常量组成的一个具有返回值的语句。C语言中的运算符分为算术运算符、关系运算符、逻辑运算符等,例如加号、减号、乘号、除号等。下面是一个简单的表达式。 ```c a = 5 + 6 * 3 / 2 - 1; ``` 这个表达式将计算5加6乘3除以2减1的值,并将结果赋给a变量。 # 输入输出 输入输出是程序中非常重要的部分,可以让程序与用户进行交互。C语言中有多种输入输出函数,其中一些最常用的是scanf、printf、getchar和putchar函数。 ## scanf函数 scanf函数用于从标准输入读取格式化数据,并将读取的数据存储到变量中。它的格式如下。 ```c scanf("格式控制字符串", 变量列表); ``` 其中,格式控制字符串指示scanf函数需要读取的数据类型和格式,变量列表指向要读取的变量。下面是一个scanf函数的例子。 ```c int num; scanf("%d", &num); ``` 这个代码段将从标准输入读取一个整数,并将其存储到num变量中。 ## printf函数 printf函数用于将格式化数据输出到标准输出。它的格式如下。 ```c printf("格式控制字符串", 参数列表); ``` 其中,格式控制字符串指示printf函数需要输出的数据类型和格式,参数列表包含要输出的变量和常量。下面是一个printf函数的例子。 ```c int num = 5; printf("num的值是%d\n", num); ``` 这个代码段将输出“num的值是5”。 ## getchar和putchar函数 getchar函数用于从标准输入读取一个字符,putchar函数用于将一个字符输出到标准输出。它们的用法非常简单,例如下面的代码将读取一个字符并将其转换成大写字母后输出。 ```c char c = getchar(); putchar(toupper(c)); ``` 以上就是第二章中的快速入门C/C++教程的主要内容,包括程序结构和输入输出方面的基础知识。熟练掌握这些内容,对于学习C语言来说是非常重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值