# 试验田 **Repository Path**: gavin-chan/Testfield ## Basic Information - **Project Name**: 试验田 - **Description**: python学习试验田 - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-03-02 - **Last Updated**: 2026-01-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 🎵 `test.py` 文件详细分析与用法指南 ## 📋 文件概览 这是一个使用 Python 创作、生成和播放 MIDI 音乐的完整脚本,包含了从音乐理论到实际代码的全面实现。 **核心功能**: - 使用 `midiutil` 库生成多轨道 MIDI 文件 - 使用 `pygame` 库播放生成的 MIDI 音乐 - 实现完整的流行歌曲结构:鼓组 + 贝斯 + 吉他 + 钢琴 - 采用 C 大调经典和弦进行 ## 🔧 技术依赖 | 库名 | 版本 | 用途 | 安装命令 | |------|------|------|----------| | midiutil | - | 生成 MIDI 文件 | `pip install midiutil` | | pygame | 2.6.1+ | 播放 MIDI 文件 | `pip install pygame` | | time | 标准库 | 控制播放流程 | 内置 | ## 🎼 代码结构与详细注释 ### 1. 导入核心库 ```python # 导入核心库:midiutil生成MIDI,pygame播放MIDI from midiutil import MIDIFile import pygame import time ``` **说明**: - `MIDIFile`:用于创建和编辑 MIDI 文件的核心类 - `pygame`:用于播放生成的 MIDI 文件 - `time`:用于控制播放过程中的延迟 ### 2. 初始化 MIDI 音乐文件 ```python # ===================== 1. 初始化MIDI音乐文件 ===================== track_count = 1 # 音轨数:单音轨即可实现多乐器(不同通道对应不同乐器) channel_num = 16 # MIDI标准16个通道,足够使用 midi = MIDIFile(track_count, adjust_origin=True) # 创建MIDI对象 tempo = 120 # 歌曲速度 BPM,120=中等节奏,流行歌常用 volume = 100 # 整体音量 0~127,100适中不刺耳 midi.addTempo(0, 0, tempo) # 设置速度 ``` **参数详解**: - `track_count`:音轨数量,单音轨即可通过不同通道实现多乐器 - `adjust_origin=True`:自动调整时间原点,使音乐从0开始 - `tempo`:速度(BPM),120为流行歌曲常用速度 - `addTempo`:设置速度,参数为 (音轨索引, 时间, 速度) ### 3. 定义乐器音色与鼓组通道 ```python # ===================== 2. 定义【乐器音色】+【鼓组】通道 ===================== # MIDI通道编号 0~15,其中 9号通道是固定的【打击乐鼓组通道】,不能改! CHANNEL_DRUM = 9 # 9号通道:专属鼓组(底鼓、军鼓、踩镲) CHANNEL_PIANO = 0 # 0号通道:钢琴(主旋律) CHANNEL_BASS = 1 # 1号通道:贝斯(低音铺垫) CHANNEL_GUITAR = 2 # 2号通道:吉他(和弦伴奏) # 设置各通道的【乐器音色】 midi.addProgramChange(0, CHANNEL_PIANO, 0, 0) # 0=钢琴 midi.addProgramChange(0, CHANNEL_BASS, 0, 33) # 33=原声贝斯 midi.addProgramChange(0, CHANNEL_GUITAR, 0, 24) # 24=原声吉他 # 鼓组通道无需设置音色,9号通道默认就是鼓组 ``` **MIDI 通道知识**: - 通道 0-8, 10-15:可设置各种乐器音色 - 通道 9:固定为打击乐鼓组通道,音符值对应不同鼓点 - `addProgramChange`:设置乐器音色,参数为 (音轨, 通道, 时间, 乐器编号) ### 4. 鼓组音符对照表 ```python # ===================== 3. 定义【鼓组音符对照表】(核心)===================== # 9号鼓组通道:【音符编号】对应【鼓点音色】,固定规则,常用鼓点如下,直接用! DRUM_BASS = 36 # 底鼓(咚):歌曲核心节奏,重拍必用 DRUM_SNARE = 38 # 军鼓(哒):2/4拍、4/4拍的弱拍用,节奏灵魂 DRUM_HIHAT_CLOSE = 42 # 闭合踩镲(擦擦擦):高频节奏,填充空隙,最常用 DRUM_HIHAT_OPEN = 46 # 开放踩镲(擦——):点缀用,增加层次感 ``` **鼓组音符表**: | 音符编号 | 鼓点类型 | 用途 | |----------|----------|------| | 36 | 底鼓 | 重拍节奏,提供低频基础 | | 38 | 军鼓 | 弱拍节奏,增加节奏感 | | 42 | 闭合踩镲 | 高频填充,增加层次感 | | 46 | 开放踩镲 | 特殊效果,增加变化 | ### 5. 编写各乐器旋律/节奏 #### 5.1 鼓组节奏(4/4拍,4小节) ```python # ===== 4.1 编写【鼓组节奏】(4/4拍,循环4小节,最经典的流行鼓点)===== drum_volume = 110 # 底鼓:重拍在 0、2 拍(咚 空 咚 空) midi.addNote(0, CHANNEL_DRUM, DRUM_BASS, 0, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_BASS, 2, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_BASS, 4, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_BASS, 6, 0.5, drum_volume) # 军鼓:弱拍在 1、3 拍(空 哒 空 哒) midi.addNote(0, CHANNEL_DRUM, DRUM_SNARE, 1, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_SNARE, 3, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_SNARE, 5, 0.5, drum_volume) midi.addNote(0, CHANNEL_DRUM, DRUM_SNARE, 7, 0.5, drum_volume) # 闭合踩镲:每半拍1次,高频填充,让节奏更饱满(最常用的鼓点技巧) for beat in [0,0.5,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5]: midi.addNote(0, CHANNEL_DRUM, DRUM_HIHAT_CLOSE, beat, 0.2, drum_volume-10) ``` **节奏分析**: - 采用标准 4/4 拍流行鼓点 - 底鼓:强拍(0、2、4、6) - 军鼓:弱拍(1、3、5、7) - 踩镲:每半拍一次,提供持续的高频节奏 #### 5.2 贝斯低音旋律 ```python # ===== 4.2 编写【贝斯低音旋律】(低音铺垫,跟鼓组底鼓同步,稳节奏)===== bass_volume = 105 bass_notes = [36,38,36,40, 36,38,36,42] # 贝斯低音音符序列 bass_times = [0,1,2,3, 4,5,6,7] # 每拍1个低音 for t, note in zip(bass_times, bass_notes): midi.addNote(0, CHANNEL_BASS, note, t, 1, bass_volume) ``` **贝斯编配技巧**: - 与底鼓同步,强化节奏感 - 采用根音为主的低音线条 - 每拍一个音符,保持稳定的节奏基础 #### 5.3 吉他和弦伴奏 ```python # ===== 4.3 编写【吉他和弦伴奏】(4个小节,每小节1个和弦,氛围感拉满)===== guitar_volume = 95 # 和弦是多个音符同时发声,这里用 C大调 经典和弦走向:C → G → Am → F midi.addNote(0, CHANNEL_GUITAR, 48, 0, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 52, 0, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 55, 0, 2, guitar_volume) # C和弦 midi.addNote(0, CHANNEL_GUITAR, 43, 2, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 47, 2, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 50, 2, 2, guitar_volume) # G和弦 midi.addNote(0, CHANNEL_GUITAR, 45, 4, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 49, 4, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 52, 4, 2, guitar_volume) # Am和弦 midi.addNote(0, CHANNEL_GUITAR, 41, 6, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 45, 6, 2, guitar_volume) midi.addNote(0, CHANNEL_GUITAR, 48, 6, 2, guitar_volume) # F和弦 ``` **和弦分析**: - 采用 C 大调经典进行:C → G → Am → F - 每个和弦持续 2 拍(1小节) - 和弦构成: - C 和弦:C-E-G (48-52-55) - G 和弦:G-B-D (43-47-50) - Am 和弦:A-C-E (45-49-52) - F 和弦:F-A-C (41-45-48) #### 5.4 钢琴主旋律 ```python # ===== 4.4 编写【钢琴主旋律】(整首歌的核心旋律,C大调优美旋律,8小节)===== piano_volume = 100 piano_notes = [60,62,64,65,67,69,71,72, 71,69,67,65,64,62,60,59] piano_times = [0,0.5,1,1.5,2,2.5,3,3.5, 4,4.5,5,5.5,6,6.5,7,7.5] piano_durations = [0.5]*16 # 每个音符持续半拍,连贯流畅 for t, note, dur in zip(piano_times, piano_notes, piano_durations): midi.addNote(0, CHANNEL_PIANO, note, t, dur, piano_volume) ``` **旋律分析**: - 采用 C 大调音阶:C-D-E-F-G-A-B-C - 旋律走向:上行 → 下行,形成完整的音乐短语 - 每个音符持续半拍,保持流畅的节奏 - 高音区(60-72)突出主旋律 ### 6. 保存 MIDI 音乐文件 ```python # ===================== 5. 保存MIDI音乐文件 ===================== midi_filename = "我的Python创作歌曲.mid" with open(midi_filename, "wb") as midi_file: midi.writeFile(midi_file) print(f"✅ 歌曲创作完成!已保存为:{midi_filename}") ``` **保存说明**: - 使用二进制写入模式 (`wb`) - MIDI 文件扩展名为 `.mid` - 保存位置为当前工作目录 ### 7. 使用 Pygame 播放 MIDI 歌曲 ```python # ===================== 6. 使用Pygame播放生成的MIDI歌曲 ===================== def play_midi(midi_file_path): pygame.mixer.init() # 初始化pygame音频引擎 pygame.mixer.music.load(midi_file_path) # 加载MIDI文件 pygame.mixer.music.play() # 开始播放 print("▶️ 正在播放歌曲...") # 播放期间保持程序运行,直到播放结束 while pygame.mixer.music.get_busy(): time.sleep(0.1) pygame.mixer.quit() # 播放结束,关闭音频引擎 print("⏹️ 播放结束!") # 调用播放函数 play_midi(midi_filename) ``` **播放流程**: 1. 初始化 pygame 音频引擎 2. 加载 MIDI 文件 3. 开始播放 4. 等待播放完成 5. 关闭音频引擎 ## 🎯 核心 API 详解 ### MIDIFile 类 | 方法 | 参数 | 功能 | |------|------|------| | `__init__` | `numTracks, adjust_origin=False` | 创建 MIDI 文件对象 | | `addTempo` | `track, time, tempo` | 设置速度(BPM) | | `addProgramChange` | `track, channel, time, program` | 设置乐器音色 | | `addNote` | `track, channel, pitch, time, duration, volume` | 添加音符 | | `writeFile` | `file` | 写入 MIDI 文件 | ### addNote 参数详解 | 参数 | 类型 | 范围 | 说明 | |------|------|------|------| | `track` | int | 0+ | 音轨索引 | | `channel` | int | 0-15 | MIDI 通道(9号为鼓组) | | `pitch` | int | 0-127 | 音符编号(中央C=60) | | `time` | float | 0+ | 开始时间(以四分音符为单位) | | `duration` | float | >0 | 持续时间(以四分音符为单位) | | `volume` | int | 0-127 | 音量大小 | ## 🎨 音乐理论基础 ### MIDI 音符对照表 | 音符 | MIDI编号 | 说明 | |------|----------|------| | C3 | 48 | 低音C | | C4 | 60 | 中央C | | C5 | 72 | 高音C | | 底鼓 | 36 | 9号通道专用 | | 军鼓 | 38 | 9号通道专用 | | 闭合踩镲 | 42 | 9号通道专用 | ### 常用乐器编号 | 乐器 | 编号 | 通道建议 | |------|------|----------| | 钢琴 | 0 | 0 | | 原声贝斯 | 33 | 1 | | 原声吉他 | 24 | 2 | | 电吉他 | 25-31 | 2 | | 弦乐 | 48-51 | 3 | | 管乐 | 65-71 | 4 | ## 🔧 高级用法与扩展 ### 1. 修改歌曲速度 ```python # 修改第10行 tempo = 100 # 慢节奏(抒情歌) # 或 tempo = 140 # 快节奏(摇滚/电子) ``` ### 2. 更换乐器音色 ```python # 修改第22-24行的乐器编号 midi.addProgramChange(0, CHANNEL_PIANO, 0, 1) # 1=大钢琴 midi.addProgramChange(0, CHANNEL_BASS, 0, 34) # 34=电贝斯 midi.addProgramChange(0, CHANNEL_GUITAR, 0, 27) # 27=爵士吉他 ``` ### 3. 扩展歌曲长度 ```python # 方法1:重复现有段落 # 例如,将鼓点、贝斯等重复一次 for i in range(2): # 重复2次 for beat in [0,0.5,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5]: midi.addNote(0, CHANNEL_DRUM, DRUM_HIHAT_CLOSE, beat + i*8, 0.2, drum_volume-10) # 方法2:添加新的段落 # 例如,添加间奏或独奏部分 ``` ### 4. 更换和弦进行 ```python # 修改吉他和弦部分 # 例如,使用 G 大调进行:G → Em → Am → D # G和弦:43, 47, 50 # Em和弦:40, 44, 47 # Am和弦:45, 49, 52 # D和弦:42, 46, 49 ``` ### 5. 添加新乐器 ```python # 1. 定义新通道 CHANNEL_STRINGS = 3 # 弦乐通道 # 2. 设置乐器音色 midi.addProgramChange(0, CHANNEL_STRINGS, 0, 48) # 48=弦乐合奏1 # 3. 添加弦乐声部 strings_volume = 90 # 例如,添加弦乐长音 midi.addNote(0, CHANNEL_STRINGS, 60, 0, 8, strings_volume) midi.addNote(0, CHANNEL_STRINGS, 67, 0, 8, strings_volume) midi.addNote(0, CHANNEL_STRINGS, 72, 0, 8, strings_volume) ``` ## 🎵 音乐创作技巧 ### 1. 节奏编排技巧 - **鼓组**:4/4拍经典模式 = 底鼓(0,2) + 军鼓(1,3) + 踩镲(每半拍) - **贝斯**:与底鼓同步,强调根音 - **吉他**:每小节一个和弦,可添加扫弦节奏 - **钢琴**:旋律线与和弦进行匹配 ### 2. 和声进行选择 | 风格 | 推荐和弦进行 | |------|-------------| | 流行 | C → G → Am → F | | 摇滚 | I → IV → V → IV | | 抒情 | I → vi → IV → V | | 爵士 | ii → V → I | ### 3. 音量平衡建议 | 乐器 | 建议音量 (0-127) | 理由 | |------|------------------|------| | 鼓组 | 105-110 | 需要突出节奏感 | | 贝斯 | 100-105 | 作为低音基础 | | 吉他 | 90-95 | 作为伴奏,不应盖过主旋律 | | 钢琴 | 95-100 | 主旋律乐器,需要清晰突出 | | 弦乐 | 85-90 | 作为背景,增加层次感 | ## 🚀 运行与调试 ### 基本运行 ```bash # 在项目目录下运行 python test.py # 预期输出 ✅ 歌曲创作完成!已保存为:我的Python创作歌曲.mid ▶️ 正在播放歌曲... ⏹️ 播放结束! ``` ### 常见问题与解决方案 | 问题 | 原因 | 解决方案 | |------|------|----------| | `ModuleNotFoundError: No module named 'midiutil'` | 未安装 midiutil | `pip install midiutil` | | `ModuleNotFoundError: No module named 'pygame'` | 未安装 pygame | `pip install pygame` | | 播放无声音 | MIDI 设备问题 | 检查系统 MIDI 输出设置 | | 节奏不准确 | 时间参数设置错误 | 确保时间单位为四分音符 | | 音色不正确 | 乐器编号错误 | 参考 MIDI 乐器编号表 | ## 📁 项目文件结构 ``` Testfield/ ├── test.py # 主脚本文件 ├── 我的Python创作歌曲.mid # 生成的 MIDI 文件 └── README.md # 项目说明(可选) ``` ## 🎓 学习资源 ### MIDI 技术文档 - [MIDI 1.0 规范](https://www.midi.org/specifications/midi1-specifications/m1-v4-2-1-specifications-for-midi-1-0/) - [MIDI 乐器编号表](https://soundprogramming.net/file-formats/general-midi-instrument-list/) ### Python 音乐库 - [midiutil 文档](https://midiutil.readthedocs.io/) - [pygame 音频文档](https://www.pygame.org/docs/ref/mixer.html) ### 音乐理论资源 - [流行音乐和弦进行](https://www.hooktheory.com/theorytab/common-chord-progressions) - [MIDI 音乐制作入门](https://www.ableton.com/en/blog/midi-basics/) ## 🎉 总结 这个脚本是一个完美的 MIDI 音乐创作起点,它: 1. **理论与实践结合**:从音乐理论到实际代码的完整实现 2. **结构清晰**:模块化设计,易于理解和修改 3. **功能完整**:涵盖了从创作到播放的全流程 4. **扩展性强**:可以轻松添加新乐器、修改节奏和和声 5. **教育价值高**:适合学习 Python 音乐编程和 MIDI 技术 通过修改和扩展这个脚本,你可以创作出属于自己的完整音乐作品!🎵✨