?欢迎互三?:程序猿方梓燚 ??
?关注博主,后期持续更新系列文章
?如果有错误感谢请大家批评指出,及时修改
?感谢大家点赞?收藏⭐评论✍
引言
今天是我的生日,我写了一段代码作为自己的生日礼物。
一、项目要求
主要功能是播放一段音乐旋律并展示一张图片,模拟 “祝我生日快乐” 的场景。
二、代码详解
一、包含的头文件及库
#include <iostream>// 引入输入输出流头文件,用于控制台的输入输出操作。#include <Windows.h>// 提供与 Windows 操作系统交互的功能,在这里用于播放 MIDI 音乐。#include<cstdio>// 提供标准输入输出函数。#include<cstdlib>// 提供通用的函数,如内存分配等。#include <graphics.h>// 用于图形绘制,可创建图形窗口和进行图像显示等操作。#include <conio.h>// 提供控制台输入输出相关功能,如获取用户输入等。#include <thread>// 支持多线程编程,允许同时执行多个任务。
这部分代码引入了多个头文件,为程序提供了不同的功能支持。iostream
用于输入输出操作,Windows.h
用于与 Windows
系统交互以播放 MIDI
音乐,cstdio
和cstdlib
提供了 C 风格的输入输出和通用函数,graphics.h
用于图形绘制,conio.h
用于控制台输入输出,thread
用于多线程编程。
二、枚举定义
音阶枚举Scale
enum Scale { Rest = 0, C8 = 108, B7 = 107, A7s = 106, A7 = 105, G7s = 104, G7 = 103, F7s = 102, F7 = 101, E7 = 100, D7s = 99, D7 = 98, C7s = 97, C7 = 96, B6 = 95, A6s = 94, A6 = 93, G6s = 92, G6 = 91, F6s = 90, F6 = 89, E6 = 88, D6s = 87, D6 = 86, C6s = 85, C6 = 84, B5 = 83, A5s = 82, A5 = 81, G5s = 80, G5 = 79, F5s = 78, F5 = 77, E5 = 76, D5s = 75, D5 = 74, C5s = 73, C5 = 72, B4 = 71, A4s = 70, A4 = 69, G4s = 68, G4 = 67, F4s = 66, F4 = 65, E4 = 64, D4s = 63, D4 = 62, C4s = 61, C4 = 60, B3 = 59, A3s = 58, A3 = 57, G3s = 56, G3 = 55, F3s = 54, F3 = 53, E3 = 52, D3s = 51, D3 = 50, C3s = 49, C3 = 48, B2 = 47, A2s = 46, A2 = 45, G2s = 44, G2 = 43, F2s = 42, F2 = 41, E2 = 40, D2s = 39, D2 = 38, C2s = 37, C2 = 36, B1 = 35, A1s = 34, A1 = 33, G1s = 32, G1 = 31, F1s = 30, F1 = 29, E1 = 28, D1s = 27, D1 = 26, C1s = 25, C1 = 24, B0 = 23, A0s = 22, A0 = 21};
这个枚举定义了不同的音阶,从最低音A0
到最高音C8
,还包括一个休止符Rest
。它为后续播放音乐时表示不同的音符提供了一种方便的方式。
声音枚举Voice
enum Voice { X1 = C2, X2 = D2, X3 = E2, X4 = F2, X5 = G2, X6 = A2, X7 = B2, L1 = C3, L2 = D3, L3 = E3, L4 = F3, L5 = G3, L6 = A3, L7 = B3, M1 = C4, M2 = D4, M3 = E4, M4 = F4, M5 = G4, M6 = A4, M7 = B4, H1 = C5, H2 = D5, H3 = E5, H4 = F5, H5 = G5, H6 = A5, H7 = B5, LOW_SPEED = 500, MIDDLE_SPEED = 400, HIGH_SPEED = 300, _ = 0XFF};
这个枚举定义了不同的音符以及一些特殊的值。其中,从X1
到H7
分别对应不同的音符,LOW_SPEED
、MIDDLE_SPEED
和HIGH_SPEED
表示不同的播放速度,_
是一个占位符。这个枚举为播放音乐提供了更多的控制选项。
枚举定义部分的作用是为程序中的不同音符和特殊值提供一种易于理解和使用的命名方式,使得代码更加清晰可读。同时,也方便在后续的代码中进行音乐播放和控制。
三、函数实现
播放音乐函数SRKL
void SRKL() { HMIDIOUT handle; // 打开一个 MIDI 输出设备,并将设备句柄存储在 handle 变量中。 midiOutOpen(&handle, 0, 0, 0, CALLBACK_NULL); int volume = 0x7f; // 定义音量为 0x7f。 int voice = 0x0; // 初始化音符编号为 0。 int sleep = 400; // 定义初始延迟时间为 400 毫秒。 int tmp = 6; // 定义一个临时变量 tmp 并初始化为 6。 int srkl[] = { L5, L5, L6, _, L5, _, M1, _, L7, _, _, _, L5, L5, L6, _, L5, _, M2, _, M1, _, _, L5, L5, M5, _, M3, _, M1, _, L7, _, L6, _, M4, M4, M3, _, M1, _, M2, _, M1, _, _, _, L5, L5, L6, _, L5, _, M1, _, L7, _, _, _, L5, L5, L6, _, L5, _, M2, _, M1, _, _, L5, L5, M5, _, M3, _, M1, _, L7, _, L6, _, M4, M4, M3, _, M1, _, M2, _, M1, _, _, _, }; // 定义一个数组 srkl,其中包含了一系列音符和特殊标识,用于播放音乐旋律。 for (int i : srkl) { if (i == LOW_SPEED || i == HIGH_SPEED || i == MIDDLE_SPEED) { sleep = i; // 如果当前元素是速度标识(LOW_SPEED、HIGH_SPEED、MIDDLE_SPEED),则更新延迟时间 sleep。 continue; } if (i == 0) { sleep = 200; // 如果当前元素是 0,则将延迟时间设置为 200 毫秒。 continue; } if (i == 700) { Sleep(200); // 如果当前元素是 700,则延迟 200 毫秒。 continue; } if (i == _) { Sleep(400); // 如果当前元素是占位符 _,则延迟 400 毫秒。 continue; } if (i == 1000) { tmp += 5; // 如果当前元素是 1000,则增加临时变量 tmp 的值。 continue; } voice = (volume << 16) + ((i + tmp) << 8) + 0x90; // 计算音符编号 voice。 midiOutShortMsg(handle, voice); // 通过 MIDI 输出设备发送音符编号播放音符。 Sleep(sleep); // 根据延迟时间 sleep 进行延迟。 } midiOutClose(handle); // 关闭 MIDI 输出设备。}
这个函数用于播放一段特定的音乐旋律。它通过打开 MIDI
输出设备,设置音量、延迟时间等参数,然后遍历一个音符数组,根据不同的音符值进行不同的操作,包括播放音符、设置延迟时间等。最后关闭 MIDI
输出设备。
展示图片函数photo
void photo() { initgraph(614, 446); // 初始化图形窗口,设置窗口大小为 614x446。 // 加载图片 IMAGE img; loadimage(&img, "res\\1.png"); // 加载名为“res\\1.png”的图片到 IMAGE 对象 img 中。 // 在窗口中绘制图片 putimage(0, 0, &img); // 在图形窗口的左上角(0,0)位置绘制图片。 _getch(); // 等待用户输入。 closegraph(); // 关闭图形窗口。}
这个函数用于在图形窗口中展示一张图片。它首先初始化图形窗口,然后加载指定的图片,将图片绘制在窗口中,等待用户输入后关闭图形窗口。
四、主函数main
int main() { system("title 祝我生日快乐"); // 设置控制台窗口标题为“祝我生日快乐”。 thread musicThread(SRKL); thread photoThread(photo); // 创建两个线程,一个线程执行 SRKL 函数播放音乐,另一个线程执行 photo 函数展示图片。 musicThread.join(); photoThread.join(); // 等待两个线程执行完毕。 return 0;}
主函数首先设置控制台窗口标题,然后创建两个线程分别用于播放音乐和展示图片。接着,等待这两个线程执行完毕,最后返回 0,表示程序正常结束。
三、完整代码
#include <iostream>// 引入输入输出流头文件,用于控制台的输入输出操作。#include <Windows.h>// 提供与 Windows 操作系统交互的功能,在这里用于播放 MIDI 音乐。#include<cstdio>// 提供标准输入输出函数。#include<cstdlib>// 提供通用的函数,如内存分配等。#include <graphics.h>// 用于图形绘制,可创建图形窗口和进行图像显示等操作。#include <conio.h>// 提供控制台输入输出相关功能,如获取用户输入等。#include <thread>// 支持多线程编程,允许同时执行多个任务。#pragma comment(lib,"winmm.lib")// 链接 winmm.lib 库,用于播放 MIDI 音乐。// 定义音阶枚举enum Scale { Rest = 0, // 表示休止符,值为 0。 C8 = 108, // 表示高音 C,音符值为 108。 B7 = 107, // 以下类似,分别表示不同的音符。 A7s = 106, A7 = 105, G7s = 104, G7 = 103, F7s = 102, F7 = 101, E7 = 100, D7s = 99, D7 = 98, C7s = 97, C7 = 96, B6 = 95, A6s = 94, A6 = 93, G6s = 92, G6 = 91, F6s = 90, F6 = 89, E6 = 88, D6s = 87, D6 = 86, C6s = 85, C6 = 84, B5 = 83, A5s = 82, A5 = 81, G5s = 80, G5 = 79, F5s = 78, F5 = 77, E5 = 76, D5s = 75, D5 = 74, C5s = 73, C5 = 72, B4 = 71, A4s = 70, A4 = 69, G4s = 68, G4 = 67, F4s = 66, F4 = 65, E4 = 64, D4s = 63, D4 = 62, C4s = 61, C4 = 60, B3 = 59, A3s = 58, A3 = 57, G3s = 56, G3 = 55, F3s = 54, F3 = 53, E3 = 52, D3s = 51, D3 = 50, C3s = 49, C3 = 48, B2 = 47, A2s = 46, A2 = 45, G2s = 44, G2 = 43, F2s = 42, F2 = 41, E2 = 40, D2s = 39, D2 = 38, C2s = 37, C2 = 36, B1 = 35, A1s = 34, A1 = 33, G1s = 32, G1 = 31, F1s = 30, F1 = 29, E1 = 28, D1s = 27, D1 = 26, C1s = 25, C1 = 24, B0 = 23, A0s = 22, A0 = 21};// 定义声音枚举enum Voice { X1 = C2, // 将 X1 定义为 C2 对应的音符值。 X X2 = D2, // 以下类似,分别对应不同的音符。 X3 = E2, X4 = F2, X5 = G2, X6 = A2, X7 = B2, L1 = C3, L2 = D3, L3 = E3, L4 = F3, L5 = G3, L6 = A3, L7 = B3, M1 = C4, M2 = D4, M3 = E4, M4 = F4, M5 = G4, M6 = A4, M7 = B4, H1 = C5, H2 = D5, H3 = E5, H4 = F5, H5 = G5, H6 = A5, H7 = B5, LOW_SPEED = 500, // 定义低速度播放对应的延迟时间为 500 毫秒。 MIDDLE_SPEED = 400, // 定义中速度播放对应的延迟时间为 400 毫秒。 HIGH_SPEED = 300, // 定义高速度播放对应的延迟时间为 300 毫秒。 _ = 0XFF // 定义一个占位符,值为 0XFF。};// 播放音乐的函数void SRKL() { HMIDIOUT handle; // 定义一个 MIDI 输出设备句柄。 midiOutOpen(&handle, 0, 0, 0, CALLBACK_NULL); // 打开 MIDI 输出设备,并将句柄存储在 handle 中。 int volume = 0x7f; // 定义音量为 0x7f。 int voice = 0x0; // 初始化音符编号为 0。 int sleep = 400; // 定义初始延迟时间为 400 毫秒。 int tmp = 6; // 定义一个临时变量 tmp 并初始化为 6。 int srkl[] = { L5, L5, L6, _, L5, _, M1, _, L7, _, _, _, L5, L5, L6, _, L5, _, M2, _, M1, _, _, L5, L5, M5, _, M3, _, M1, _, L7, _, L6, _, M4, M4, M3, _, M1, _, M2, _, M1, _, _, _, L5, L5, L6, _, L5, _, M1, _, L7, _, _, _, L5, L5, L6, _, L5, _, M2, _, M1, _, _, L5, L5, M5, _, M3, _, M1, _, L7, _, L6, _, M4, M4, M3, _, M1, _, M2, _, M1, _, _, _, }; // 定义一个数组 srkl,其中包含了一系列音符和特殊标识,用于播放音乐旋律。 for (int i : srkl) { if (i == LOW_SPEED || i == HIGH_SPEED || i == MIDDLE_SPEED) { sleep = i; // 如果当前元素是速度标识(LOW_SPEED、HIGH_SPEED、MIDDLE_SPEED),则更新延迟时间 sleep。 continue; } if (i == 0) { sleep = 200; // 如果当前元素是 0,则将延迟时间设置为 200 毫秒。 continue; } if (i == 700) { Sleep(200); // 如果当前元素是 700,则延迟 200 毫秒。 continue; } if (i == _) { Sleep(400); // 如果当前元素是占位符 _,则延迟 400 毫秒。 continue; } if (i == 1000) { tmp += 5; // 如果当前元素是 1000,则增加临时变量 tmp 的值。 continue; } voice = (volume << 16) + ((i + tmp) << 8) + 0x90; // 计算音符编号 voice。 midiOutShortMsg(handle, voice); // 通过 MIDI 输出设备发送音符编号播放音符。 Sleep(sleep); // 根据延迟时间 sleep 进行延迟。 } midiOutClose(handle); // 关闭 MIDI 输出设备。}void photo() { initgraph(614, 446); // 初始化图形窗口,设置窗口大小为 614x446。 // 加载图片 IMAGE img; loadimage(&img, "res\\1.png"); // 加载名为“res\\1.png”的图片到 IMAGE 对象 img 中。 // 在窗口中绘制图片 putimage(0, 0, &img); // 在图形窗口的左上角(0,0)位置绘制图片。 _getch(); // 等待用户输入。 closegraph(); // 关闭图形窗口。}int main() { system("title 祝我生日快乐"); // 设置控制台窗口标题为“祝我生日快乐”。 thread musicThread(SRKL); // 创建一个线程 musicThread,用于执行播放音乐的函数 SRKL。 thread photoThread(photo); // 创建一个线程 photoThread,用于执行展示图片的函数 photo。 musicThread.join(); // 等待 musicThread 线程执行完毕。 photoThread.join(); // 等待 photoThread 线程执行完毕。 return 0;}
四、总结
该程序通过结合音乐播放和图片展示,为用户提供了一种多媒体的体验。利用多线程技术确保音乐和图片的展示可以同时进行,提高了程序的效率和用户体验。同时,代码中的枚举定义使得音符和特殊值的表示更加清晰可读,方便了音乐播放的控制。
五、素材
高清无水印图片素材如下↓↓↓