c语言简谱播放器添加暂停功能程序代码QZQ

#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>  // 用于多线程
#include <stdbool.h>  // 引入布尔类型

// 全局变量:MIDI 输出设备句柄
HMIDIOUT hMidiOut;
// 全局变量:播放速度,默认值为 0.5
float playSpeed = 0.5;
// 全局变量:停止标志
bool stopPlaying = false;
// 全局变量:暂停标志
bool pausePlaying = false;
// 全局变量:当前播放的音符索引
int currentNoteIndex = 0;

// 音符结构体
typedef struct {
    int pitch;  // MIDI 音高
    int duration; // 音符时长(毫秒)
} Note;

// 解析单个音符
Note parseNote(const char* noteStr) {
    Note note;
    note.pitch = -1;
    note.duration = 200; // 四分音符默认时长

    int octave = 4;
    int basePitch = -1;
    const char* p = noteStr;

    // 处理升降八度、升降半音
    while (*p == '+' || *p == '-' || *p == '#' || *p == '!') {
        if (*p == '+') {
            octave++;
        } else if (*p == '-') {
            octave--;
        } else if (*p == '#') {
            basePitch++;
        } else if (*p == '!') {
            basePitch--;
        }
        p++;
    }

    // 处理基本音符
    if (*p >= '0' && *p <= '7') {
        basePitch = *p - '0';
        if (basePitch == 0) {
            note.pitch = -1; // 休止符
        } else {
            static const int basePitches[] = { 60, 62, 64, 65, 67, 69, 71 };
            note.pitch = basePitches[basePitch - 1] + (octave - 4) * 12;
        }
        p++;
    }

    // 处理时值
    while (*p) {
        if (*p == '/') {
            note.duration /= 2;
            p++;
            if (*p == '/') {
                note.duration /= 2;
                p++;
            }
        } else if (*p == '-') {
            note.duration *= 2;
            p++;
        } else if (*p == '.') {
            note.duration += note.duration / 2;
            p++;
            if (*p == '.') {
                note.duration += note.duration / 4;
                p++;
            }
        } else {
            break;
        }
    }

    // 调试语句:输出解析得到的音符信息
    printf("Parsed Note - Pitch: %d, Duration: %d\n", note.pitch, note.duration);

    return note;
}

// 解析简谱字符串,处理换行符
void parseScore(const char* scoreStr, Note** notes, int* noteCount) {
    int capacity = 10;
    *notes = (Note*)malloc(capacity * sizeof(Note));
    *noteCount = 0;

    char tempStr[1024];
    strcpy(tempStr, scoreStr);
    // 处理换行符,将换行符替换为逗号
    for (int i = 0; tempStr[i] != '\0'; i++) {
        if (tempStr[i] == '\n') {
            tempStr[i] = ',';
        }
    }

    char* token = strtok(tempStr, ",");
    while (token != NULL) {
        if (*noteCount >= capacity) {
            capacity *= 2;
            *notes = (Note*)realloc(*notes, capacity * sizeof(Note));
        }
        (*notes)[*noteCount] = parseNote(token);
        (*noteCount)++;
        token = strtok(NULL, ",");
    }
}

// 播放简谱的线程函数
unsigned int __stdcall PlayScoreThread(void* param) {
    // 重新组织参数获取方式
    Note* notes = *(Note**)param;
    int noteCount = *((int*)param + 1);

    // 调试语句:线程开始
    printf("PlayScoreThread started\n");

    stopPlaying = false;
    pausePlaying = false;
    currentNoteIndex = 0;
    for (; currentNoteIndex < noteCount; currentNoteIndex++) {
        if (stopPlaying) {
            break;
        }
        while (pausePlaying) {
            Sleep(100); // 暂停时等待
        }
        int adjustedDuration = (int)(notes[currentNoteIndex].duration / playSpeed);
        if (notes[currentNoteIndex].pitch != -1) {
            // 输出当前播放的音符信息
            printf("Playing Note - Index: %d, Pitch: %d, Adjusted Duration: %d\n", currentNoteIndex, notes[currentNoteIndex].pitch, adjustedDuration);
            // 检查 midiOutShortMsg 函数调用的返回值
            MMRESULT result = midiOutShortMsg(hMidiOut, 0x90 | (notes[currentNoteIndex].pitch << 8) | (127 << 16));
            if (result != MMSYSERR_NOERROR) {
                char errorMsg[256];
                sprintf(errorMsg, "midiOutShortMsg error for note %d, error code: %d", currentNoteIndex, result);
                MessageBox(NULL, errorMsg, "Error", MB_OK | MB_ICONERROR);
            }
            Sleep(adjustedDuration);
            result = midiOutShortMsg(hMidiOut, 0x80 | (notes[currentNoteIndex].pitch << 8) | (0 << 16));
            if (result != MMSYSERR_NOERROR) {
                char errorMsg[256];
                sprintf(errorMsg, "midiOutShortMsg error for note %d, error code: %d", currentNoteIndex, result);
                MessageBox(NULL, errorMsg, "Error", MB_OK | MB_ICONERROR);
            }
        } else {
            // 输出休止符信息
            printf("Rest - Index: %d, Duration: %d\n", currentNoteIndex, adjustedDuration);
            Sleep(adjustedDuration);
        }
    }
    free(notes);
    free(param);  // 释放传递参数时分配的内存

    // 调试语句:线程结束
    printf("PlayScoreThread ended\n");

    _endthreadex(0);
    return 0;
}

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE: {
        // 创建编辑框用于输入简谱
        CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN,
            10, 10, 400, 200, hwnd, (HMENU)100, NULL, NULL);

        // 创建播放按钮
        CreateWindow("BUTTON", "播放", WS_VISIBLE | WS_CHILD,
            20, 230, 80, 30, hwnd, (HMENU)101, NULL, NULL);

        // 创建停止按钮
        CreateWindow("BUTTON", "停止", WS_VISIBLE | WS_CHILD,
            120, 230, 80, 30, hwnd, (HMENU)104, NULL, NULL);

        // 创建暂停/继续按钮
        CreateWindow("BUTTON", "暂停", WS_VISIBLE | WS_CHILD,
            220, 230, 80, 30, hwnd, (HMENU)105, NULL, NULL);

        // 创建标签用于提示输入速度
        CreateWindow("STATIC", "播放速度 (倍数):", WS_VISIBLE | WS_CHILD,
            20, 270, 120, 20, hwnd, (HMENU)102, NULL, NULL);

        // 创建编辑框用于输入速度
        CreateWindow("EDIT", "0.5", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
            160, 270, 50, 20, hwnd, (HMENU)103, NULL, NULL);

        // 打开 MIDI 输出设备
        if (midiOutOpen(&hMidiOut, MIDI_MAPPER, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) {
            // 输出具体的错误信息
            char errorMsg[256];
            sprintf(errorMsg, "无法打开 MIDI 输出设备,错误码: %d", midiOutOpen(&hMidiOut, MIDI_MAPPER, 0, 0, CALLBACK_NULL));
            MessageBox(hwnd, errorMsg, "错误", MB_OK | MB_ICONERROR);
        } else {
            // 设备打开成功的提示
            //MessageBox(hwnd, "MIDI 输出设备打开成功", "提示", MB_OK | MB_ICONINFORMATION);
        }

        break;
    }
    case WM_COMMAND: {
        switch (LOWORD(wParam)) {
        case 101: { // 播放按钮点击事件
            char buffer[1024];
            GetWindowText((HWND)GetDlgItem(hwnd, 100), buffer, sizeof(buffer));
            Note* notes;
            int noteCount;
            parseScore(buffer, &notes, &noteCount);

            char speedBuffer[20];
            GetWindowText((HWND)GetDlgItem(hwnd, 103), speedBuffer, sizeof(speedBuffer));
            playSpeed = (float)atof(speedBuffer);
            if (playSpeed <= 0) {
                playSpeed = 0.5; // 防止速度为负数或零
                MessageBox(hwnd, "速度必须为正数,已恢复默认速度 0.5", "错误", MB_OK | MB_ICONERROR);
            }

            // 为线程传递参数
            void* threadParam = malloc(sizeof(Note*) + sizeof(int));
            memcpy(threadParam, &notes, sizeof(Note*));
            memcpy((char*)threadParam + sizeof(Note*), &noteCount, sizeof(int));

            // 创建新线程播放简谱
            HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, PlayScoreThread, threadParam, 0, NULL);
            CloseHandle(hThread);
            break;
        }
        case 104: { // 停止按钮点击事件
            stopPlaying = true;
            pausePlaying = false;
            currentNoteIndex = 0;
            SetWindowText((HWND)GetDlgItem(hwnd, 105), "暂停");
            break;
        }
        case 105: { // 暂停/继续按钮点击事件
            if (pausePlaying) {
                pausePlaying = false;
                SetWindowText((HWND)GetDlgItem(hwnd, 105), "暂停");
            } else {
                pausePlaying = true;
                SetWindowText((HWND)GetDlgItem(hwnd, 105), "继续");
            }
            break;
        }
        }
        break;
    }
    case WM_DESTROY: {
        // 关闭 MIDI 输出设备
        midiOutClose(hMidiOut);
        PostQuitMessage(0);
        break;
    }
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    // 注册窗口类
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "SheetMusicEditorClass";
    RegisterClass(&wc);

    // 创建窗口
    HWND hwnd = CreateWindow(wc.lpszClassName, "简谱编辑程序", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 520, 420, NULL, NULL, hInstance, NULL);

    // 显示窗口
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EasySoft易软

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值