hudaoyuan163 发表于 2022-8-22 20:42

使用c++SDK写个钢琴,加上自动演奏外挂

本帖最后由 hudaoyuan163 于 2022-8-23 08:35 编辑

钢琴买不起,可以自己码一个。
来个C++ Piano,码砖累了还可以即兴演奏一首。
主要用到SDK知识,文件流,字符转,程序之间消息传送,window运行程序遍历等
音源和乐谱在压缩包中,一并奉上,望大佬多多指教
###### 1.实现播放本地音乐:

- 在项目中添加音乐文件,如果在vs中运行即以main数所在文件夹为当前文件夹,如果运行的是exe文件,则是以exe文件所在的文件夹为当前文件夹。

- 导入Windows.h头文件,#pragma comment(lib,"winmm.lib"),需要使用micString(),该函数用于将mci(媒体控制设备)有关指令发送到设备上。该函数有两种版本A版和W版,是否可以使用通用版?函数如果成功返回0失败则返回其他。

- ```
MCIERROR mciSendString(
   LPCTSTR lpszCommand,       //发送给MCI的指令
   LPTSTRlpszReturnString,//返回的结果
   UINT    cchReturn,         //返回结果大小
   HANDLEhwndCallback       //如果在命令字符串中指定了“通知”标志,则返回回调窗口的hwndCallback句柄
);
```

- 新建一个loadmusic函数导入所有音乐并设置别名mciSendStringA(("open sound\\start.mp3 alias START"), temp, 255, 0);这里alias是别名指令,将start.mp3打开为别名START。

- 新建一个PlayMusic函数用于播放已经打开的音乐,mciSendStringA("seek START to start", NULL, 0, 0);mciSendStringA("play START", NULL, 0, 0);               

###### 2.实现窗口对按键的反应

- 在窗口的回调函数中判断 Msg 如果是WM_CHAR消息,则获取wParam的值,以该值作为索引找出对应的音乐。
- 按下不同的按键就会打开对应音乐。

###### 3.实现外挂读取文件

- 包含windows,使用GetDesktopWindow()得到桌面句柄,使用GetWindow得到屏幕上第一个子窗口句柄,通过while循环和GetNextWindow遍历所有窗口,找到Piano程序,得到其句柄。
- 创建SendOpern函数发送曲谱,参数为句柄,第一步打开文件获取数据,使用ofstream读取文本文件。(无法读取空格)
- 将读取到的字符通过sendmessage发送给Pinao窗口,第一个参数为句柄,第二个参数为发送消息类型,第三个参数为wParam,第四个参数为lParam。 SendMessage( hd,WM_CHAR,k, 0)。

贴上源代码

Piano

```
#include<Windows.h>
#include<tchar.h>//为通用提供映射
#include< conio.h >
#include<stdio.h>
//该链接库用于音乐播放
#pragma comment(lib,"winmm.lib")

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void LoadMusic();
int APIENTRY _tWinMain(
      HINSTANCE hinstance, //实例句柄
      HINSTANCE hPrevInstance,
      LPTSTR lpCmdLine,//命令行参数
      int nCmdShow      //显示方式
)
{
      //1.设计窗口类
      WNDCLASS ws = {};
      //窗口类名
      ws.lpszClassName = _T("Music");
      //窗口回调函数
      ws.lpfnWndProc = WndProc;
      //背景颜色
      ws.hbrBackground = CreateSolidBrush(RGB(255, 180, 255));
      //光标图标菜单,
      ws.hInstance = hinstance;

      //2.注册窗口类
      RegisterClass(&ws);
      //3.创建窗口
      HWND hWnd = CreateWindow(
                _T("Music"),               //窗口类名
                _T("Piano"),//窗口名
                WS_OVERLAPPEDWINDOW,//窗口风格
                100, 100,   //窗口左上角的位置,以屏幕左上角为参考点
                600, 800,   //窗口的宽度和高度
                NULL,         //父窗口句柄
                NULL,          //菜单句柄
                hinstance,   //实例句柄
                NULL            //附加参数
      );
      //4.更新显示窗口
      ShowWindow(hWnd, SW_SHOW);
      UpdateWindow(hWnd);
      LoadMusic();

      //5.消息循环
      MSG msg = {};
      //GetMessage
      //第一个是消息结构体
      //第二个是窗口句柄,如果填0就是所有窗口的消息
      //第三个是最小消息数量
      //第四个是最大消息数量

      while (GetMessage(&msg, 0, 0, 0))
      {
                //将消息分发给回调函数
                TranslateMessage(&msg);
                DispatchMessage(&msg);

      }
      return 0;
}

LRESULT CALLBACKWndProc(HWND hWnd, UINT Msg, WPARAM wParam,
      LPARAM lParam)
{
      switch (Msg)
      {
      case WM_CHAR:
      {
                char szBuf = { 0 };
                char szBuf1 = { 0 };
                sprintf_s(szBuf, sizeof(szBuf), "seek %c to start", wParam);
                sprintf_s(szBuf1, sizeof(szBuf1), "play %c", wParam);
                mciSendStringA(szBuf, NULL, 0, 0);
                mciSendStringA(szBuf1, NULL, 0, 0);
      }break;
      case WM_KEYDOWN:
      {
                //MessageBox(0, buff, 0, 0);
      }break;
      case WM_CLOSE:
                PostQuitMessage(0);
                break;
      default:
                break;
      }
      //如果不想要处理的消息类型,就让他返回默认的处理函数
      return DefWindowProc(hWnd, Msg, wParam, lParam);
}

void LoadMusic()
{
      char A = 'A';
      for (int i = 1; i < 26; i++)
      {
                char szBuf = { 0 };
                sprintf_s(szBuf, sizeof(szBuf), "open sound\\Sound_%c.wav alias %c", A, A);//写入
                A++;
                mciSendStringA(szBuf, NULL, 0, 0);
      }
}
```

辅助,读取琴谱自动演奏

```
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <iostream>
#include<fstream>
void SendOpern(HWND hd);
void SendOpern2(HWND hd);
using namespace std;

int main()
{
    HWND hd = GetDesktopWindow();      //得到桌面窗口
    hd = GetWindow(hd, GW_CHILD);      //得到屏幕上第一个子窗口
    char s = {0};
    int num = 1;
    while (hd != NULL)                  //循环得到所有的子窗口
    {
      //memset(s, 0,200);
      GetWindowTextA(hd, s, 200);
      //cout << num++ << ": " << s <<sizeof(s) << endl;
      //由于字符串数组之间无法比较,所以需要转换为字符串
      std::string a(&s, &s);
      if (a == "Piano") {
            SendOpern2(hd);
            printf("演奏成功");
            break;
      }
      
      hd = GetNextWindow(hd, GW_HWNDNEXT);
    }
    SendOpern(hd);
    getchar();
    return 0;
}

void SendOpern(HWND hd)
{
    ifstream Opern;
    Opern.open("Sound.txt", ios::in);
    char k;
    while (Opern >> k) {
      Sleep(800);
      //此处空格和"\n"都被>>吃掉了
      SendMessage(
            hd,
            WM_CHAR,
            k, 0
      );
      printf("%c\n", k);
    }


}

void SendOpern2(HWND hd)
{
    ifstream Opern;
    Opern.open("Sound.txt", ios::in);
    string k;
    while (Opern >> k) {
      for (auto ele : k) {
            Sleep(1000);
            SendMessage(
                hd,
                WM_CHAR,
                ele, 0
            );
            printf("%c\n", ele);
      }
      Sleep(1000);
    }


}
```

losingstars 发表于 2022-8-23 00:54

需要音源和乐谱

DRLLL 发表于 2022-8-23 06:27

很有意思

hudaoyuan163 发表于 2022-8-23 08:37

losingstars 发表于 2022-8-23 00:54
需要音源和乐谱

已经更新,审核好了就可以下载了:lol

virsnow 发表于 2022-10-8 16:07

看起来有点复杂,收藏先,有空研究一下

mrtwzchi 发表于 2022-11-18 19:31

学习学习

曙光牛 发表于 2022-11-18 22:39

对于我目前来说很复杂,先放收藏夹吃灰

mrtwzchi 发表于 2022-11-24 23:13

有成品视频吗,看看效果

tianbail 发表于 2022-11-25 14:39


学习学习
页: [1]
查看完整版本: 使用c++SDK写个钢琴,加上自动演奏外挂