当前位置: 首页 > news >正文

python语言网页版MIDI钢琴软件代码QZQ

```python

#pip install flask pygame

 

from flask import Flask, render_template, request
import pygame.midi

app = Flask(__name__)

# 初始化 pygame.midi
pygame.midi.init()

# 获取 MIDI 输出设备
try:
midi_out = pygame.midi.Output(0)
print("MIDI输出设备初始化成功")
except:
print("无法初始化MIDI输出设备,使用虚拟设备")


# 创建一个虚拟的MIDI输出类用于测试
class VirtualMidiOut:
def set_instrument(self, instrument):
print(f"设置乐器: {instrument}")

def note_on(self, note, velocity):
print(f"播放音符: {note}, 力度: {velocity}")

def note_off(self, note, velocity):
print(f"停止音符: {note}")

def close(self):
print("关闭MIDI设备")


midi_out = VirtualMidiOut()

# 设置默认乐器
midi_out.set_instrument(2) # 默认电钢琴

# 音符对应的 MIDI 编号
notes = [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
76, 77, 78, 79]

# 乐器名称映射
instrument_names = {
0: "大钢琴(声学钢琴)",
1: "明亮的钢琴",
2: "电钢琴",
3: "酒吧钢琴",
4: "柔和的电钢琴",
5: "加合唱效果的电钢琴",
6: "羽管键琴(拨弦古钢琴)",
7: "科拉维科特琴(击弦古钢琴)",
8: "钢片琴",
9: "钟琴",
10: "八音盒",
11: "颤音琴",
12: "马林巴",
13: "木琴",
14: "管钟",
15: "大扬琴",
16: "击杆风琴",
17: "打击式风琴",
18: "摇滚风琴",
19: "教堂风琴",
20: "簧管风琴",
21: "手风琴",
22: "口琴",
23: "探戈手风琴",
24: "尼龙弦吉他",
25: "钢弦吉他",
26: "爵士电吉他",
27: "清音电吉他",
28: "闷音电吉他",
29: "加驱动效果的电吉他",
30: "加失真效果的电吉他",
31: "吉他和音",
32: "大贝司(声学贝司)",
33: "电贝司(指弹)",
34: "电贝司(拨片)",
35: "无品贝司",
36: "掌击贝司",
37: "掌击贝司",
38: "电子合成贝司",
39: "电子合成贝司",
40: "小提琴",
41: "中提琴",
42: "大提琴",
43: "低音大提琴",
44: "弦乐群颤音音色",
45: "弦乐群拨弦音色",
46: "竖琴",
47: "定音鼓",
48: "弦乐合奏音色",
49: "弦乐合奏音色",
50: "合成弦乐合奏音色",
51: "合成弦乐合奏音色",
52: "人声合唱啊",
53: "人声嘟",
54: "合成人声",
55: "管弦乐敲击齐奏",
56: "小号",
57: "长号",
58: "大号",
59: "加弱音器小号",
60: "法国号(圆号)",
61: "铜管组(铜管乐器合奏音色)",
62: "合成铜管音色",
63: "合成铜管音色",
64: "高音萨克斯风",
65: "次中音萨克斯风",
66: "中音萨克斯风",
67: "低音萨克斯风",
68: "双簧管",
69: "英国管",
70: "巴松(大管)",
71: "单簧管(黑管)",
72: "短笛",
73: "长笛",
74: "竖笛",
75: "排箫",
76: "吹瓶声",
77: "日本尺八",
78: "口哨声",
79: "奥卡雷那",
80: "合成主音(方波)",
81: "合成主音(锯齿波)",
82: "合成主音",
83: "合成主音",
84: "合成主音",
85: "合成主音(人声)",
86: "合成主音(平行五度)",
87: "合成主音(贝司加主音)",
88: "合成音色(新世纪)",
89: "合成音色(温暖)",
90: "合成音色",
91: "合成音色(合唱)",
92: "合成音色",
93: "合成音色(金属声)",
94: "合成音色(光环)",
95: "合成音色",
96: "合成效果雨声",
97: "合成效果音轨",
98: "合成效果水晶",
99: "合成效果大气",
100: "合成效果明亮",
101: "合成效果鬼怪",
102: "合成效果回声",
103: "合成效果科幻",
104: "西塔尔(印度)",
105: "班卓琴(美洲)",
106: "三昧线(日本)",
107: "十三弦筝(日本)",
108: "卡林巴",
109: "风笛",
110: "民族提琴",
111: "唢呐",
112: "叮当铃",
113: "阿哥哥鼓",
114: "钢鼓",
115: "木鱼",
116: "太鼓",
117: "古高音鼓",
118: "合成鼓",
119: "铜钹",
120: "磨弦",
121: "呼吸声",
122: "海浪声",
123: "鸟鸣",
124: "电话铃",
125: "直升机",
126: "鼓掌声",
127: "枪声"
}


# 设置音量为 125
def play_note(note_number, instrument=2):
# 先设置乐器
midi_out.set_instrument(instrument)
# 播放音符
midi_out.note_on(note_number, 125)


def stop_note(note_number):
midi_out.note_off(note_number, 125)


# 添加PianoKey类定义
class PianoKey:
def __init__(self, note_number):
# 根据 MIDI 编号获取音符名称
note_names = {
50: 'C3',
51: 'C#3',
52: 'D3',
53: 'D#3',
54: 'E3',
55: 'F3',
56: 'F#3',
57: 'G3',
58: 'G#3',
59: 'A3',
60: 'A#3',
61: 'B3',
62: 'C4',
63: 'C#4',
64: 'D4',
65: 'D#4',
66: 'E4',
67: 'F4',
68: 'F#4',
69: 'G4',
70: 'G#4',
71: 'A4',
72: 'A#4',
73: 'B4',
74: 'C5',
75: 'C#5',
76: 'D5',
77: 'D#5',
78: 'E5',
79: 'F5'
}
note_name = note_names.get(note_number, 'Unknown')
self.note_number = note_number
self.note_name = note_name

def on_click(self):
play_note(self.note_number)


@app.route('/')
def index():
# 设置钢琴键
keys = [PianoKey(note_number) for note_number in notes]
instrument_options = [f"{i} - {instrument_names.get(i, 'Unknown')}" for i in range(128)]
default_main_instrument = 2
return render_template('index.html', keys=keys, instrument_options=instrument_options,
selected_instrument=f"{default_main_instrument} - {instrument_names.get(default_main_instrument, 'Unknown')}")


@app.route('/play_note/<int:note_number>')
def play_note_route(note_number):
# 获取当前乐器参数
instrument = request.args.get('instrument', 2, type=int)
play_note(note_number, instrument)
return 'Note played'


@app.route('/change_instrument/<int:instrument_value>')
def change_instrument_route(instrument_value):
# 设置乐器
midi_out.set_instrument(instrument_value)
return 'Instrument changed'


if __name__ == '__main__':
app.run(debug=True)


# 在程序结束时关闭 MIDI 输出设备
@app.teardown_appcontext
def close_midi(exception=None):
midi_out.close()
pygame.midi.quit()
```

 

 

```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小钢琴 Web 应用</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
color: #fff;
min-height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}

.container {
width: 100%;
max-width: 1000px;
background: rgba(0, 0, 0, 0.7);
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
margin-top: 20px;
}

header {
text-align: center;
margin-bottom: 25px;
}

h1 {
font-size: 2.8rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
color: #ffd700;
}

.description {
font-size: 1.1rem;
margin-bottom: 20px;
color: #e0e0e0;
max-width: 800px;
line-height: 1.6;
}

.control-panel {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
flex-wrap: wrap;
gap: 15px;
background: rgba(0, 0, 0, 0.4);
padding: 15px;
border-radius: 10px;
}

.instrument-selector-container {
flex: 1;
min-width: 300px;
}

.instrument-selector {
width: 100%;
padding: 12px 15px;
font-size: 16px;
border-radius: 8px;
border: 2px solid #5dade2;
background: rgba(0, 0, 0, 0.8);
color: #fff;
cursor: pointer;
transition: all 0.3s;
}

.instrument-selector:hover {
border-color: #3498db;
background: rgba(0, 0, 0, 0.9);
}

.instrument-selector:focus {
outline: none;
border-color: #2ecc71;
box-shadow: 0 0 10px rgba(46, 204, 113, 0.6);
}

.current-instrument {
font-size: 1.2rem;
font-weight: bold;
color: #2ecc71;
text-align: center;
padding: 10px 15px;
background: rgba(0, 0, 0, 0.5);
border-radius: 8px;
min-width: 250px;
}

.piano-container {
position: relative;
width: 100%;
height: 220px;
margin: 0 auto;
background: #2c3e50;
border-radius: 10px;
padding: 15px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
overflow-x: auto;
overflow-y: hidden;
}

.white-key {
background: linear-gradient(to bottom, #fff 0%, #f5f5f5 100%);
width: 40px;
height: 180px;
position: absolute;
border: 1px solid #bbb;
border-radius: 0 0 6px 6px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
z-index: 1;
transition: background 0.1s;
}

.white-key:hover {
background: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
}

.white-key:active, .white-key.active {
background: linear-gradient(to bottom, #ffd700 0%, #ffa500 100%);
}

.black-key {
background: linear-gradient(to bottom, #000 0%, #333 100%);
width: 25px;
height: 110px;
position: absolute;
border-radius: 0 0 4px 4px;
cursor: pointer;
z-index: 2;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.5);
transition: background 0.1s;
}

.black-key:hover {
background: linear-gradient(to bottom, #000 0%, #444 100%);
}

.black-key:active, .black-key.active {
background: linear-gradient(to bottom, #ff8c00 0%, #ff4500 100%);
}

.key-label {
position: absolute;
bottom: 10px;
width: 100%;
text-align: center;
font-size: 11px;
font-weight: bold;
pointer-events: none;
}

.white-key .key-label {
color: #333;
}

.black-key .key-label {
color: #fff;
}

.instructions {
margin-top: 30px;
padding: 15px;
background: rgba(0, 0, 0, 0.4);
border-radius: 8px;
text-align: center;
color: #e0e0e0;
font-size: 1rem;
line-height: 1.5;
}

.keyboard-shortcuts {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin-top: 20px;
}

.shortcut {
background: rgba(52, 152, 219, 0.3);
padding: 8px 15px;
border-radius: 5px;
font-size: 0.9rem;
}

footer {
margin-top: 30px;
text-align: center;
color: #ccc;
font-size: 0.9rem;
}

@media (max-width: 768px) {
.control-panel {
flex-direction: column;
}

.instrument-selector-container {
width: 100%;
}

.current-instrument {
width: 100%;
}

h1 {
font-size: 2.2rem;
}
}
</style>
</head>
<body>
<header>
<h1>🎹 小钢琴 Web 应用</h1>
<p class="description">这是一个基于Web的钢琴模拟应用,您可以选择128种不同音色,使用鼠标或键盘进行演奏</p>
</header>

<div class="container">
<div class="control-panel">
<div class="instrument-selector-container">
<select class="instrument-selector" id="instrumentSelector" onchange="changeInstrument(this.value); updateCurrentInstrument()">
<!-- 音色选项将通过JavaScript动态生成 -->
</select>
</div>
<div class="current-instrument" id="currentInstrument">
当前音色: 电钢琴
</div>
</div>

<div class="piano-container" id="piano">
<!-- 钢琴键将通过JavaScript动态生成 -->
</div>

<div class="instructions">
<p>点击琴键或使用键盘按键(A,S,D,F,G,H,J,K,L等)来演奏音乐</p>
<div class="keyboard-shortcuts">
<div class="shortcut">A = C3</div>
<div class="shortcut">W = C#3</div>
<div class="shortcut">S = D3</div>
<div class="shortcut">E = D#3</div>
<div class="shortcut">D = E3</div>
<div class="shortcut">F = F3</div>
<div class="shortcut">T = F#3</div>
<div class="shortcut">G = G3</div>
<div class="shortcut">Y = G#3</div>
<div class="shortcut">H = A3</div>
</div>
</div>
</div>

<footer>
<p>使用键盘上方的数字行可以切换不同音色组 (0-9)</p>
</footer>

<script>
// 音色名称映射
const instrumentNames = {
0: "大钢琴(声学钢琴)",
1: "明亮的钢琴",
2: "电钢琴",
3: "酒吧钢琴",
4: "柔和的电钢琴",
5: "加合唱效果的电钢琴",
6: "羽管键琴(拨弦古钢琴)",
7: "科拉维科特琴(击弦古钢琴)",
8: "钢片琴",
9: "钟琴",
10: "八音盒",
11: "颤音琴",
12: "马林巴",
13: "木琴",
14: "管钟",
15: "大扬琴",
16: "击杆风琴",
17: "打击式风琴",
18: "摇滚风琴",
19: "教堂风琴",
20: "簧管风琴",
21: "手风琴",
22: "口琴",
23: "探戈手风琴",
24: "尼龙弦吉他",
25: "钢弦吉他",
26: "爵士电吉他",
27: "清音电吉他",
28: "闷音电吉他",
29: "加驱动效果的电吉他",
30: "加失真效果的电吉他",
31: "吉他和音",
32: "大贝司(声学贝司)",
33: "电贝司(指弹)",
34: "电贝司(拨片)",
35: "无品贝司",
36: "掌击贝司",
37: "掌击贝司",
38: "电子合成贝司",
39: "电子合成贝司",
40: "小提琴",
41: "中提琴",
42: "大提琴",
43: "低音大提琴",
44: "弦乐群颤音音色",
45: "弦乐群拨弦音色",
46: "竖琴",
47: "定音鼓",
48: "弦乐合奏音色",
49: "弦乐合奏音色",
50: "合成弦乐合奏音色",
51: "合成弦乐合奏音色",
52: "人声合唱啊",
53: "人声嘟",
54: "合成人声",
55: "管弦乐敲击齐奏",
56: "小号",
57: "长号",
58: "大号",
59: "加弱音器小号",
60: "法国号(圆号)",
61: "铜管组(铜管乐器合奏音色)",
62: "合成铜管音色",
63: "合成铜管音色",
64: "高音萨克斯风",
65: "次中音萨克斯风",
66: "中音萨克斯风",
67: "低音萨克斯风",
68: "双簧管",
69: "英国管",
70: "巴松(大管)",
71: "单簧管(黑管)",
72: "短笛",
73: "长笛",
74: "竖笛",
75: "排箫",
76: "吹瓶声",
77: "日本尺八",
78: "口哨声",
79: "奥卡雷那",
80: "合成主音(方波)",
81: "合成主音(锯齿波)",
82: "合成主音",
83: "合成主音",
84: "合成主音",
85: "合成主音(人声)",
86: "合成主音(平行五度)",
87: "合成主音(贝司加主音)",
88: "合成音色(新世纪)",
89: "合成音色(温暖)",
90: "合成音色",
91: "合成音色(合唱)",
92: "合成音色",
93: "合成音色(金属声)",
94: "合成音色(光环)",
95: "合成音色",
96: "合成效果雨声",
97: "合成效果音轨",
98: "合成效果水晶",
99: "合成效果大气",
100: "合成效果明亮",
101: "合成效果鬼怪",
102: "合成效果回声",
103: "合成效果科幻",
104: "西塔尔(印度)",
105: "班卓琴(美洲)",
106: "三昧线(日本)",
107: "十三弦筝(日本)",
108: "卡林巴",
109: "风笛",
110: "民族提琴",
111: "唢呐",
112: "叮当铃",
113: "阿哥哥鼓",
114: "钢鼓",
115: "木鱼",
116: "太鼓",
117: "古高音鼓",
118: "合成鼓",
119: "铜钹",
120: "磨弦",
121: "呼吸声",
122: "海浪声",
123: "鸟鸣",
124: "电话铃",
125: "直升机",
126: "鼓掌声",
127: "枪声"
};

// 钢琴键数据
const pianoKeys = [
{ note: 50, name: 'C3', type: 'white', keyCode: 65 }, // A
{ note: 51, name: 'C#3', type: 'black', keyCode: 87 }, // W
{ note: 52, name: 'D3', type: 'white', keyCode: 83 }, // S
{ note: 53, name: 'D#3', type: 'black', keyCode: 69 }, // E
{ note: 54, name: 'E3', type: 'white', keyCode: 68 }, // D
{ note: 55, name: 'F3', type: 'white', keyCode: 70 }, // F
{ note: 56, name: 'F#3', type: 'black', keyCode: 84 }, // T
{ note: 57, name: 'G3', type: 'white', keyCode: 71 }, // G
{ note: 58, name: 'G#3', type: 'black', keyCode: 89 }, // Y
{ note: 59, name: 'A3', type: 'white', keyCode: 72 }, // H
{ note: 60, name: 'A#3', type: 'black', keyCode: 85 }, // U
{ note: 61, name: 'B3', type: 'white', keyCode: 74 }, // J
{ note: 62, name: 'C4', type: 'white', keyCode: 75 }, // K
{ note: 63, name: 'C#4', type: 'black', keyCode: 79 }, // O
{ note: 64, name: 'D4', type: 'white', keyCode: 76 }, // L
{ note: 65, name: 'D#4', type: 'black', keyCode: 80 }, // P
{ note: 66, name: 'E4', type: 'white', keyCode: 186 }, // ;
{ note: 67, name: 'F4', type: 'white', keyCode: 222 }, // '
{ note: 68, name: 'F#4', type: 'black', keyCode: 221 }, // ]
{ note: 69, name: 'G4', type: 'white', keyCode: 220 }, // \
{ note: 70, name: 'G#4', type: 'black', keyCode: 90 }, // Z
{ note: 71, name: 'A4', type: 'white', keyCode: 88 }, // X
{ note: 72, name: 'A#4', type: 'black', keyCode: 67 }, // C
{ note: 73, name: 'B4', type: 'white', keyCode: 86 }, // V
{ note: 74, name: 'C5', type: 'white', keyCode: 66 }, // B
{ note: 75, name: 'C#5', type: 'black', keyCode: 72 }, // H
{ note: 76, name: 'D5', type: 'white', keyCode: 78 }, // N
{ note: 77, name: 'D#5', type: 'black', keyCode: 77 }, // M
{ note: 78, name: 'E5', type: 'white', keyCode: 188 }, // ,
{ note: 79, name: 'F5', type: 'white', keyCode: 190 } // .
];

// 当前选择的乐器
let currentInstrument = 2; // 默认电钢琴

// 初始化音色选择器
function initInstrumentSelector() {
const selector = document.getElementById('instrumentSelector');

for (let i = 0; i < 128; i++) {
const option = document.createElement('option');
option.value = i;
option.textContent = `${i}. ${instrumentNames[i]}`;

// 默认选择电钢琴
if (i === 2) {
option.selected = true;
}

selector.appendChild(option);
}
}

// 初始化钢琴
function initPiano() {
const pianoContainer = document.getElementById('piano');
let whiteKeyCount = 0;

pianoKeys.forEach(key => {
let element;

if (key.type === 'white') {
element = document.createElement('div');
element.className = 'white-key';
element.style.left = (whiteKeyCount * 40) + 'px';
whiteKeyCount++;
} else {
element = document.createElement('div');
element.className = 'black-key';
// 黑键位置在白键之间
element.style.left = ((whiteKeyCount - 1) * 40 + 28) + 'px';
}

const label = document.createElement('div');
label.className = 'key-label';
label.textContent = key.name;
element.appendChild(label);

element.setAttribute('data-note', key.note);
element.addEventListener('mousedown', () => {
keyOn(key.note);
element.classList.add('active');
});

element.addEventListener('mouseup', () => {
element.classList.remove('active');
});

element.addEventListener('mouseleave', () => {
element.classList.remove('active');
});

pianoContainer.appendChild(element);
});

// 设置钢琴容器宽度
pianoContainer.style.width = (whiteKeyCount * 40 + 40) + 'px';
}

// 更新当前音色显示
function updateCurrentInstrument() {
const selector = document.getElementById('instrumentSelector');
const currentInstrumentElement = document.getElementById('currentInstrument');
currentInstrument = parseInt(selector.value);
const instrumentName = instrumentNames[currentInstrument];

currentInstrumentElement.textContent = `当前音色: ${instrumentName}`;
}

// 播放音符
function keyOn(noteNumber) {
// 发送请求到后端播放音符,同时传递当前选择的乐器
fetch(`/play_note/${noteNumber}?instrument=${currentInstrument}`)
.then(response => {
if (!response.ok) {
console.error('播放音符失败');
}
})
.catch(error => {
console.error('网络错误:', error);
});
}

// 更改乐器
function changeInstrument(instrumentValue) {
// 更新当前乐器
currentInstrument = parseInt(instrumentValue);
const instrumentName = instrumentNames[currentInstrument];
document.getElementById('currentInstrument').textContent = `当前音色: ${instrumentName}`;

// 发送请求到后端更改乐器
fetch(`/change_instrument/${currentInstrument}`)
.then(response => {
if (!response.ok) {
console.error('更改乐器失败');
}
})
.catch(error => {
console.error('网络错误:', error);
});
}

// 键盘控制
document.addEventListener('keydown', function(event) {
// 防止重复触发
if (event.repeat) return;

// 数字键切换音色组
if (event.keyCode >= 48 && event.keyCode <= 57) {
const groupIndex = event.keyCode - 48;
const selector = document.getElementById('instrumentSelector');
// 选择对应组的第一个音色
const instrumentIndex = Math.min(groupIndex * 13, 127);
selector.value = instrumentIndex;
changeInstrument(instrumentIndex);
return;
}

const key = pianoKeys.find(k => k.keyCode === event.keyCode);
if (key) {
keyOn(key.note);

// 添加视觉反馈
const keyElement = document.querySelector(`[data-note="${key.note}"]`);
if (keyElement) {
keyElement.classList.add('active');
}
}
});

document.addEventListener('keyup', function(event) {
const key = pianoKeys.find(k => k.keyCode === event.keyCode);
if (key) {
// 恢复原始颜色
const keyElement = document.querySelector(`[data-note="${key.note}"]`);
if (keyElement) {
keyElement.classList.remove('active');
}
}
});

// 初始化应用
window.onload = function() {
initInstrumentSelector();
initPiano();
updateCurrentInstrument();
};
</script>
</body>
</html>


```

 

 

软件截图:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3396fd25a90f428eafbf67923c27bb12.png#pic_center)

 屏幕截图 2025-09-08 072622

 

 


源代码下载地址:

wangyebanchengxu.zip

[https://download.csdn.net/download/qq_32257509/91895153](https://download.csdn.net/download/qq_32257509/91895153)

 

http://www.agseo.cn/news/477/

相关文章:

  • 【2024-2025第二学期】助教工作学期总结(算法与数据结构)
  • p型编码
  • 赣江游记
  • OTA 升级问题的分析
  • 初识Dataset
  • Day15可变参数
  • Nacos
  • 单词的长度
  • Python模块之 subprocess 具有可访问I/O流的子流程 子进程管理
  • 因爱而……(和谐版)
  • 初探CTF
  • P3195 [HNOI2008] 玩具装箱
  • Python模块之execjs
  • 模拟题
  • 软工第一次作业-自我介绍
  • 111
  • Vibe Coding,这种技术面试形式会成为新的趋势吗?
  • qt之捕获键盘组合键事件
  • ???记录?
  • LIN 的调度表周期和应用任务周期不一致的问题分析
  • 自我介绍与软工五问
  • 关于我的大三生活
  • CSP 赛前周记#2
  • 建立本地仓库
  • 厨房小白学做饭——2.苦瓜炒蛋
  • DAY2
  • Go
  • Discipline
  • 长乐一中 CSP-S 2025 提高级模拟赛 Day1
  • 做题记录