手机端C语言钢琴软件代码QZQ

发布时间:2026/7/27 19:27:30

手机端C语言钢琴软件代码QZQ #define TSF_IMPLEMENTATION #include tsf.h #include SDL2/SDL.h #include SDL2/SDL_ttf.h #include stdio.h #include stdlib.h #include string.h #include stdbool.h // 全局合成器 tsf* g_tsf NULL; const int SAMPLE_RATE 44100; // ★★★ 通道号就是音色选择 ★★★ int currentChannel 0; // 0-127 范围对应不同的音色 // 输入状态 bool isInputMode false; char inputNumber[4] {0}; // 最多3位数字 \0 // TTF字体 TTF_Font* font NULL; // 音色名称列表通道对应的音色 const char* instrumentNames[128] { 钢琴1, 钢琴2, 钢琴3, 钢琴4, 电钢琴1, 电钢琴2, 电钢琴3, 电钢琴4, 钢片琴, 钟琴, 八音盒, 颤音琴, 马林巴, 木琴, 管钟, 竖琴, 手风琴, 口琴, 班多纽, 弦乐合奏1, 弦乐合奏2, 合成弦乐1, 合成弦乐2, 唱诗班, 人声合唱, 合成人声, 小号, 长号, 大号, 小号2, 法国号, 铜管合奏, 合成铜管1, 合成铜管2, 高音萨克斯, 中音萨克斯, 次中音萨克斯, 上低音萨克斯, 双簧管, 英国管, 巴松管, 单簧管, 短笛, 长笛, 竖笛, 排箫, 口哨, 陶笛, 合成主音1, 合成主音2, 合成主音3, 合成主音4, 合成主音5, 合成主音6, 合成主音7, 合成主音8, 合成垫音1, 合成垫音2, 合成垫音3, 合成垫音4, 合成垫音5, 合成垫音6, 合成垫音7, 合成垫音8, 合成特效1, 合成特效2, 合成特效3, 合成特效4, 合成特效5, 合成特效6, 合成特效7, 合成特效8, 锡塔尔, 班卓琴, 三味线, 筝, 卡林巴, 风笛, 小提琴, 大提琴, 低音提琴, 颤音弦乐, 定音鼓, 管弦乐, 吉他, 清音吉他, 失真吉他, 过载吉他, 吉他泛音, 爵士吉他, 电吉他, 低音吉他, 击弦贝斯, 拨弦贝斯, 无品贝斯, 拍打贝斯, 合成贝斯1, 合成贝斯2, 大鼓, 军鼓, 镲片, 打击乐, 民族打击乐, 合成鼓, 合成打击乐, 音效, 音效2, 音效3, 音效4, 音效5, 音效6, 音效7, 音效8, 音效9, 音效10, 音效11, 音效12, 音效13, 音效14, 音效15, 吉他反馈, 音效16, 音效17, 音效18, 音效19, 音效20, 直升机, 掌声 }; // 21键映射SDL键码 - MIDI音符号 #define KEY_COUNT 21 SDL_Keycode keyCodes[KEY_COUNT] { SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_w, SDLK_e, SDLK_t, SDLK_y, SDLK_u, SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_BACKSLASH }; int midiNotes[KEY_COUNT] { 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 61, 63, 66, 68, 70, 73, 75, 78, 80, 82 }; // 当前按下的音符最多同时按下 KEY_COUNT 个 int activeNotes[KEY_COUNT]; int activeNoteCount 0; // 键盘矩形区域 typedef struct { SDL_Rect rect; int midiNote; bool isBlack; } KeyRect; KeyRect keyRects[KEY_COUNT]; int keyRectCount 0; // 辅助函数根据SDL键码查找MIDI音符号未找到返回 -1 int findMidiNote(SDL_Keycode key) { for (int i 0; i KEY_COUNT; i) { if (keyCodes[i] key) return midiNotes[i]; } return -1; } // 渲染文字 void renderText(SDL_Renderer* renderer, const char* text, int x, int y, SDL_Color color) { if (!font) return; SDL_Surface* surface TTF_RenderUTF8_Blended(font, text, color); if (!surface) return; SDL_Texture* texture SDL_CreateTextureFromSurface(renderer, surface); if (!texture) { SDL_FreeSurface(surface); return; } SDL_Rect rect {x, y, surface-w, surface-h}; SDL_RenderCopy(renderer, texture, NULL, rect); SDL_DestroyTexture(texture); SDL_FreeSurface(surface); } // ★★★ 切换音色直接修改通道号★★★ void changeInstrument(int channel) { if (channel 0 || channel 127) return; currentChannel channel; // 停止所有正在播放的音符 for (int i 0; i activeNoteCount; i) { tsf_note_off(g_tsf, currentChannel, activeNotes[i]); } activeNoteCount 0; const char* name instrumentNames[currentChannel]; if (!name) name 未知; printf( 切换音色: 通道 %d - %s\n, currentChannel, name); } // 音频回调 void audioCallback(void* userdata, Uint8* stream, int len) { if (!g_tsf) return; SDL_memset(stream, 0, len); int sampleCount len / sizeof(short) / 2; tsf_render_short(g_tsf, (short*)stream, sampleCount, 1); } // 播放音符 void playNote(int midiNote, bool on) { if (on) { // 检查是否已经按下 for (int i 0; i activeNoteCount; i) { if (activeNotes[i] midiNote) return; } if (activeNoteCount KEY_COUNT) { activeNotes[activeNoteCount] midiNote; tsf_note_on(g_tsf, currentChannel, midiNote, 0.8f); } } else { for (int i 0; i activeNoteCount; i) { if (activeNotes[i] midiNote) { // 移除该音符用最后一个覆盖 activeNotes[i] activeNotes[--activeNoteCount]; tsf_note_off(g_tsf, currentChannel, midiNote); break; } } } } // 更新键盘区域 void updateKeyRects(int windowWidth, int windowHeight) { keyRectCount 0; const int whiteKeys 14; const int startKey 60; int keyWidth windowWidth / whiteKeys; int whiteKeyHeight windowHeight - 200; int blackKeyHeight (int)(whiteKeyHeight * 0.6); int whiteIndex 0; for (int i 0; i whiteKeys; i) { int midiNote startKey i; int noteInOctave (midiNote - 60) % 12; if (noteInOctave 1 || noteInOctave 3 || noteInOctave 6 || noteInOctave 8 || noteInOctave 10) continue; int x whiteIndex * keyWidth; keyRects[keyRectCount].rect (SDL_Rect) { x, 0, keyWidth - 1, whiteKeyHeight }; keyRects[keyRectCount].midiNote midiNote; keyRects[keyRectCount].isBlack false; keyRectCount; whiteIndex; } int blackIndex 0; for (int i 0; i whiteKeys; i) { int midiNote startKey i; int noteInOctave (midiNote - 60) % 12; if (noteInOctave 1 || noteInOctave 3 || noteInOctave 6 || noteInOctave 8 || noteInOctave 10) { int x blackIndex * keyWidth keyWidth * 2 / 3; keyRects[keyRectCount].rect (SDL_Rect) { x, 0, keyWidth * 2 / 3, blackKeyHeight }; keyRects[keyRectCount].midiNote midiNote; keyRects[keyRectCount].isBlack true; keyRectCount; blackIndex; } } } // 绘制钢琴键盘 void drawPiano(SDL_Renderer* renderer, int windowWidth, int windowHeight) { SDL_SetRenderDrawColor(renderer, 20, 20, 30, 255); SDL_RenderClear(renderer); const int whiteKeys 14; const int startKey 60; int keyWidth windowWidth / whiteKeys; int whiteKeyHeight windowHeight - 200; int blackKeyHeight (int)(whiteKeyHeight * 0.6); // 绘制白键 int whiteIndex 0; for (int i 0; i whiteKeys; i) { int midiNote startKey i; int noteInOctave (midiNote - 60) % 12; if (noteInOctave 1 || noteInOctave 3 || noteInOctave 6 || noteInOctave 8 || noteInOctave 10) continue; int x whiteIndex * keyWidth; bool isPressed false; for (int j 0; j activeNoteCount; j) { if (activeNotes[j] midiNote) { isPressed true; break; } } SDL_Rect rect {x, 0, keyWidth - 1, whiteKeyHeight}; if (isPressed) { SDL_SetRenderDrawColor(renderer, 200, 200, 100, 255); } else { SDL_SetRenderDrawColor(renderer, 240, 240, 240, 255); } SDL_RenderFillRect(renderer, rect); SDL_SetRenderDrawColor(renderer, 80, 80, 80, 255); SDL_RenderDrawRect(renderer, rect); whiteIndex; } // 绘制黑键 int blackIndex 0; for (int i 0; i whiteKeys; i) { int midiNote startKey i; int noteInOctave (midiNote - 60) % 12; if (noteInOctave 1 || noteInOctave 3 || noteInOctave 6 || noteInOctave 8 || noteInOctave 10) { int x blackIndex * keyWidth keyWidth * 2 / 3; bool isPressed false; for (int j 0; j activeNoteCount; j) { if (activeNotes[j] midiNote) { isPressed true; break; } } SDL_Rect rect {x, 0, keyWidth * 2 / 3, blackKeyHeight}; if (isPressed) { SDL_SetRenderDrawColor(renderer, 150, 150, 50, 255); } else { SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); } SDL_RenderFillRect(renderer, rect); SDL_SetRenderDrawColor(renderer, 60, 60, 60, 255); SDL_RenderDrawRect(renderer, rect); blackIndex; } } // 底部信息栏 int infoY windowHeight - 200; SDL_Rect infoRect {0, infoY, windowWidth, 200}; SDL_SetRenderDrawColor(renderer, 30, 30, 45, 255); SDL_RenderFillRect(renderer, infoRect); // 绘制输入框 int inputBoxX 20; int inputBoxY infoY 10; int inputBoxW windowWidth - 40; int inputBoxH 55; SDL_Rect inputRect {inputBoxX, inputBoxY, inputBoxW, inputBoxH}; if (isInputMode) { SDL_SetRenderDrawColor(renderer, 60, 60, 100, 255); if ((SDL_GetTicks() / 500) % 2 0) { SDL_SetRenderDrawColor(renderer, 255, 255, 100, 200); int cursorX inputBoxX 15 (int)(strlen(inputNumber) * 15); SDL_Rect cursorRect {cursorX, inputBoxY 8, 3, 40}; SDL_RenderFillRect(renderer, cursorRect); } } else { SDL_SetRenderDrawColor(renderer, 40, 40, 60, 255); } SDL_RenderFillRect(renderer, inputRect); if (isInputMode) { SDL_SetRenderDrawColor(renderer, 255, 200, 50, 255); for (int i 0; i 3; i) { SDL_Rect borderRect {inputBoxX - i, inputBoxY - i, inputBoxW i * 2, inputBoxH i * 2}; SDL_RenderDrawRect(renderer, borderRect); } } else { SDL_SetRenderDrawColor(renderer, 80, 80, 100, 255); SDL_RenderDrawRect(renderer, inputRect); } SDL_Color white {255, 255, 255, 255}; SDL_Color yellow {255, 255, 100, 255}; SDL_Color gray {180, 180, 180, 255}; SDL_Color cyan {100, 255, 255, 255}; if (isInputMode) { char displayText[100]; snprintf(displayText, sizeof(displayText), 输入通道号(0-127): %s, inputNumber); renderText(renderer, displayText, inputBoxX 15, inputBoxY 15, yellow); renderText(renderer, 按 Enter 确认 | 点击空白取消, inputBoxX 15, inputBoxY inputBoxH 8, gray); } else { const char* instName instrumentNames[currentChannel]; if (!instName) instName 未知; char text[100]; snprintf(text, sizeof(text), 当前通道: %d - %s, currentChannel, instName); renderText(renderer, text, inputBoxX 15, inputBoxY 15, cyan); renderText(renderer, 点击输入框弹出键盘 | 按 I 键, inputBoxX 15, inputBoxY inputBoxH 8, gray); } // 按钮 int btnY infoY 135; SDL_Rect btn1 {20, btnY, 110, 40}; SDL_SetRenderDrawColor(renderer, 50, 180, 50, 200); SDL_RenderFillRect(renderer, btn1); SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); SDL_RenderDrawRect(renderer, btn1); renderText(renderer, ✅ 确认, 40, btnY 10, white); SDL_Rect btn2 {140, btnY, 110, 40}; SDL_SetRenderDrawColor(renderer, 180, 50, 50, 200); SDL_RenderFillRect(renderer, btn2); SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); SDL_RenderDrawRect(renderer, btn2); renderText(renderer, ❌ 取消, 160, btnY 10, white); SDL_Rect btn3 {260, btnY, 110, 40}; SDL_SetRenderDrawColor(renderer, 50, 50, 180, 200); SDL_RenderFillRect(renderer, btn3); SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); SDL_RenderDrawRect(renderer, btn3); renderText(renderer, 常用, 285, btnY 10, white); renderText(renderer, 0-9快速切换 | /- 加减 | I输入, 20, btnY 50, gray); SDL_RenderPresent(renderer); } int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO); TTF_Init(); SDL_StartTextInput(); SDL_DisplayMode DM; SDL_GetCurrentDisplayMode(0, DM); int windowWidth (int)(DM.w * 0.92); int windowHeight (int)(DM.h * 0.78); SDL_Window* window SDL_CreateWindow( 钢琴 - 通道选择音色, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE ); if (!window) { printf(窗口创建失败%s\n, SDL_GetError()); return -1; } SDL_Renderer* renderer SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ); // 加载字体 font TTF_OpenFont(SimHei.ttf, 22); if (!font) { font TTF_OpenFont(/system/fonts/NotoSansCJK-Regular.ttc, 22); if (!font) { font TTF_OpenFont(/system/fonts/DroidSansFallback.ttf, 22); } if (!font) { printf(⚠️ 字体加载失败\n); } } // 加载SF2 g_tsf tsf_load_filename(sndfnt.sf2); if (!g_tsf) { printf(❌ SF2音色文件加载失败\n); printf(请将 sndfnt.sf2 放在程序同目录下\n); SDL_Delay(3000); return -1; } tsf_set_output(g_tsf, TSF_STEREO_INTERLEAVED, SAMPLE_RATE, 0); // ★★★ 初始化音色 (通道0) ★★★ changeInstrument(0); // 初始化音频 SDL_AudioSpec spec; SDL_memset(spec, 0, sizeof(spec)); spec.freq SAMPLE_RATE; spec.format AUDIO_S16SYS; spec.channels 2; spec.samples 1024; spec.callback audioCallback; if (SDL_OpenAudio(spec, NULL) 0) { printf(❌ 音频打开失败%s\n, SDL_GetError()); tsf_close(g_tsf); return -1; } SDL_PauseAudio(0); updateKeyRects(windowWidth, windowHeight); printf( 钢琴已启动\n); printf(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n); printf( 音色选择 (通道号 0-127)\n); printf( 点击输入框弹出手机虚拟键盘\n); printf( ⌨️ 按 I 键进入输入模式\n); printf( 0-9 快速切换音色\n); printf( ➕➖ /- 音色加减\n); printf( 通道号 音色编号\n); printf(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n); bool running true; SDL_Event event; bool mouseDown false; int lastMidiNote -1; while (running) { while (SDL_PollEvent(event)) { switch (event.type) { case SDL_QUIT: running false; break; case SDL_TEXTINPUT: if (isInputMode) { const char* text event.text.text; for (int i 0; text[i] ! \0; i) { if (text[i] 0 text[i] 9) { if (strlen(inputNumber) 3) { int len strlen(inputNumber); inputNumber[len] text[i]; inputNumber[len 1] \0; printf(输入: %s\n, inputNumber); } } } } break; case SDL_MOUSEBUTTONDOWN: if (event.button.button SDL_BUTTON_LEFT) { int mouseX event.button.x; int mouseY event.button.y; int infoY windowHeight - 200; // 点击输入框切换输入模式 if (mouseX 20 mouseX windowWidth - 20 mouseY infoY 10 mouseY infoY 65) { isInputMode !isInputMode; if (isInputMode) { inputNumber[0] \0; SDL_StartTextInput(); printf( 输入模式开启\n); } else { SDL_StopTextInput(); printf(关闭输入模式\n); } break; } // 按钮点击 if (mouseY infoY 135 mouseY infoY 175) { if (mouseX 20 mouseX 130) { if (isInputMode strlen(inputNumber) 0) { int channel atoi(inputNumber); if (channel 0 channel 127) { changeInstrument(channel); } else { printf(❌ 通道号必须在 0-127 之间\n); } isInputMode false; SDL_StopTextInput(); inputNumber[0] \0; } break; } if (mouseX 140 mouseX 250) { isInputMode false; SDL_StopTextInput(); inputNumber[0] \0; printf(取消输入\n); break; } if (mouseX 260 mouseX 370) { printf( 常用音色: 0钢琴 40巴松管 48合成主音 78小提琴 84吉他\n); break; } } // 输入模式下点击空白区域取消 if (isInputMode) { bool clickedKey false; for (int i 0; i keyRectCount; i) { if (mouseX keyRects[i].rect.x mouseX keyRects[i].rect.x keyRects[i].rect.w mouseY keyRects[i].rect.y mouseY keyRects[i].rect.y keyRects[i].rect.h) { clickedKey true; break; } } if (!clickedKey !(mouseX 20 mouseX windowWidth - 20 mouseY infoY 10 mouseY infoY 65) !(mouseY infoY 135 mouseY infoY 175)) { isInputMode false; SDL_StopTextInput(); inputNumber[0] \0; printf(取消输入\n); break; } } // 非输入模式下按下琴键 if (!isInputMode) { mouseDown true; for (int i keyRectCount - 1; i 0; i--) { if (mouseX keyRects[i].rect.x mouseX keyRects[i].rect.x keyRects[i].rect.w mouseY keyRects[i].rect.y mouseY keyRects[i].rect.y keyRects[i].rect.h) { playNote(keyRects[i].midiNote, true); lastMidiNote keyRects[i].midiNote; break; } } } } break; case SDL_MOUSEBUTTONUP: if (event.button.button SDL_BUTTON_LEFT) { mouseDown false; if (lastMidiNote ! -1) { playNote(lastMidiNote, false); lastMidiNote -1; } } break; case SDL_MOUSEMOTION: if (mouseDown !isInputMode) { int mouseX event.motion.x; int mouseY event.motion.y; int foundNote -1; for (int i keyRectCount - 1; i 0; i--) { if (mouseX keyRects[i].rect.x mouseX keyRects[i].rect.x keyRects[i].rect.w mouseY keyRects[i].rect.y mouseY keyRects[i].rect.y keyRects[i].rect.h) { foundNote keyRects[i].midiNote; break; } } if (foundNote ! -1 foundNote ! lastMidiNote) { if (lastMidiNote ! -1) playNote(lastMidiNote, false); playNote(foundNote, true); lastMidiNote foundNote; } else if (foundNote -1 lastMidiNote ! -1) { playNote(lastMidiNote, false); lastMidiNote -1; } } break; case SDL_KEYDOWN: { if (event.key.keysym.sym SDLK_ESCAPE) { if (isInputMode) { isInputMode false; SDL_StopTextInput(); inputNumber[0] \0; printf(取消输入\n); } else { running false; } break; } if (isInputMode) { if (event.key.keysym.sym SDLK_RETURN || event.key.keysym.sym SDLK_KP_ENTER) { if (strlen(inputNumber) 0) { int channel atoi(inputNumber); if (channel 0 channel 127) { changeInstrument(channel); } else { printf(❌ 通道号必须在 0-127 之间\n); } } isInputMode false; SDL_StopTextInput(); inputNumber[0] \0; break; } if (event.key.keysym.sym SDLK_BACKSPACE) { int len strlen(inputNumber); if (len 0) inputNumber[len - 1] \0; break; } if (event.key.keysym.sym SDLK_0 event.key.keysym.sym SDLK_9) { if (strlen(inputNumber) 3) { int len strlen(inputNumber); inputNumber[len] (char)(0 (event.key.keysym.sym - SDLK_0)); inputNumber[len 1] \0; printf(输入: %s\n, inputNumber); } break; } break; } if (event.key.keysym.sym SDLK_i) { isInputMode true; inputNumber[0] \0; SDL_StartTextInput(); printf( 输入通道号 (0-127)按Enter确认: ); break; } if (event.key.keysym.sym SDLK_0 event.key.keysym.sym SDLK_9) { int channel event.key.keysym.sym - SDLK_0; changeInstrument(channel); break; } if (event.key.keysym.sym SDLK_PLUS || event.key.keysym.sym SDLK_EQUALS) { changeInstrument((currentChannel 1) % 128); break; } if (event.key.keysym.sym SDLK_MINUS) { changeInstrument((currentChannel - 1 128) % 128); break; } int midi findMidiNote(event.key.keysym.sym); if (midi ! -1) { playNote(midi, true); } break; } case SDL_KEYUP: { if (!isInputMode) { int midi findMidiNote(event.key.keysym.sym); if (midi ! -1) { playNote(midi, false); } } break; } case SDL_WINDOWEVENT: if (event.window.event SDL_WINDOWEVENT_RESIZED) { SDL_GetWindowSize(window, windowWidth, windowHeight); updateKeyRects(windowWidth, windowHeight); } break; } } drawPiano(renderer, windowWidth, windowHeight); SDL_Delay(16); } // 清理 for (int i 0; i activeNoteCount; i) { tsf_note_off(g_tsf, currentChannel, activeNotes[i]); } activeNoteCount 0; SDL_StopTextInput(); if (font) TTF_CloseFont(font); SDL_CloseAudio(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); TTF_Quit(); SDL_Quit(); tsf_close(g_tsf); printf( 再见\n); return 0; }

相关新闻