Python音频处理与MIDI制作:从五声调式到TTS语音合成(12实验全实战)

服务器环境:华为云 FlexusX x2e.8u.16g | Ubuntu 24.04 | Python 3.12.3
核心依赖:mido 1.3.3 | numpy 2.5.0 | scipy 1.18.0 | edge-tts 7.2.8
实验服务器:ecs-d2c2-0001 (113.44.212.38) | 8vCPU 16GiB
上机时间:2026-07-03


目录

章节 模块 实验数 核心技术
第1章 节奏基础 8 五声调式/作曲/模进/宋词
第2章 MIDI制作 2 mido/MIDI消息/打击乐
第3章 音频处理 2 edge-tts/WAV/FFT/方言

环境架构

┌─────────────────────────────────────────────────────┐
│              ecs-d2c2-0001 (113.44.212.38)           │
│              Ubuntu 24.04 | 8vCPU | 16GiB            │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ┌──────────┐  ┌──────────┐  ┌───────────────────┐ │
│  │  Python   │  │  NumPy   │  │    SciPy (FFT)    │ │
│  │  3.12.3   │  │  2.5.0   │  │     1.18.0        │ │
│  └────┬─────┘  └────┬─────┘  └────────┬──────────┘ │
│       │              │                  │            │
│  ┌────▼─────────────▼──────────────────▼──────────┐ │
│  │              音频处理核心层                      │ │
│  ├────────────────┬───────────────────────────────┤ │
│  │  mido 1.3.3    │  edge-tts 7.2.8              │ │
│  │  (MIDI制作)    │  (微软TTS引擎)                │ │
│  │  → .mid文件    │  → .mp3文件                   │ │
│  └────────────────┴───────────────────────────────┘ │
│  ┌─────────────────────────────────────────────────┐ │
│  │              wave + struct                      │ │
│  │  (WAV波形生成/解析)  → .wav文件                 │ │
│  └─────────────────────────────────────────────────┘ │
│                                                     │
│  产出文件: 8 WAV + 4 MIDI + 9 MP3 = 21个音频文件    │
└─────────────────────────────────────────────────────┘

安装依赖

pip3 install --break-system-packages \
    --index-url https://mirrors.aliyun.com/pypi/simple/ \
    mido numpy scipy edge-tts

验证安装:

$ python3 -c "import mido; print('mido OK')"
$ python3 -c "import edge_tts; print('edge-tts', edge_tts.__version__)"
edge-tts 7.2.8
$ python3 -c "import numpy; print('numpy', numpy.__version__)"
numpy 2.5.0

第1章 节奏基础

实验1:断竹·弹歌 — 竹文化与音乐之始

文化背景

《弹歌》是中国最古老歌谣之一,载于《吴越春秋》:

断竹,续竹;飞土,逐肉。

释义:砍竹 → 接竹为弓 → 射泥丸 → 追猎物。四个动词勾勒出原始狩猎场景。

竹制乐器音高

音名 频率(Hz) MIDI编号 对应字
D4 293.66 62 断竹
E4 329.63 64 续竹
G4 392.00 67 飞土
A4 440.00 69 逐肉

竹林七贤与音乐

嵇康: 《广陵散》—— 临刑抚琴,千古绝响
阮籍: 《酒狂》—— 醉酒佯狂,古琴名曲
山涛: 好老庄,通音律
向秀: 《思旧赋》—— 悼嵇康
刘伶: 《酒德颂》—— 以酒为乐
阮咸: 善弹琵琶(乐器"阮咸"即以其命名)
王戎: 聪颖善鉴

苏轼《定风波·莫听穿林打叶声》:

莫听穿林打叶声,何妨吟啸且徐行。
竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。

生成弹歌旋律WAV

SAMPLE_RATE = 44100

def save_wav(filename, freqs, durations, volume=0.5, sample_rate=SAMPLE_RATE):
    """将频率列表和时长列表合成为WAV文件"""
    samples = np.array([], dtype=np.float32)
    for freq, dur in zip(freqs, durations):
        if freq <= 0:
            n = int(dur * sample_rate)
            seg = np.zeros(n, dtype=np.float32)
        else:
            n = int(dur * sample_rate)
            t = np.linspace(0, dur, n, endpoint=False)
            # 基波 + 2次/3次谐波,模拟竹笛音色
            seg = (volume * np.sin(2 * np.pi * freq * t)
                   + 0.3 * volume * np.sin(2 * np.pi * freq * 2 * t)
                   + 0.1 * volume * np.sin(2 * np.pi * freq * 3 * t))
            # ADSR包络
            attack = min(int(0.01 * sample_rate), n // 4)
            release = min(int(0.05 * sample_rate), n // 4)
            if attack > 0:
                seg[:attack] *= np.linspace(0, 1, attack)
            if release > 0:
                seg[-release:] *= np.linspace(1, 0, release)
        samples = np.concatenate([samples, seg.astype(np.float32)])
    # 归一化
    max_val = np.max(np.abs(samples))
    if max_val > 0:
        samples = samples / max_val * 0.8
    pcm = (samples * 32767).astype(np.int16)
    with wave.open(filename, "w") as wf:
        wf.setnchannels(1)
        wf.setsampwidth(2)
        wf.setframerate(sample_rate)
        wf.writeframes(pcm.tobytes())

服务器输出:

生成WAV: /tmp/exp01_tange.wav (326384 bytes)

王羲之《兰亭集序》:茂林修竹,清流激湍,映带左右。

郑板桥《竹石》:咬定青山不放松,立根原在破岩中。


实验2:中国传统五声调式 — 宫商角徵羽

五声调式音阶对照表

音名 唱名 频率(Hz) MIDI 与前音音程 特性
宫© do 261.63 60 起点 中央C,五声之首
商(D) re 293.66 62 大二度 商音清亮
角(E) mi 329.63 64 大二度 角音柔和
徵(G) sol 392.00 67 小三度 徵音高亢
羽(A) la 440.00 69 大二度 羽音悠远

音程关系

宫 → 商: 2半音 (大二度/全音)
商 → 角: 2半音 (大二度/全音)
角 → 徵: 3半音 (小三度)    ← 跳过F(清角)
徵 → 羽: 2半音 (大二度)
羽 → 宫: 3半音 (小三度)    ← 跳过B(变宫)

关键:五声调式无半音关系(没有fa和si),因此听起来和谐、东方韵味浓厚。西方七声自然大调含半音关系(E-F, B-C),产生不协和感。

五种调式

调式 音阶 唱名 色彩
宫调式 C-D-E-G-A-C do-re-mi-sol-la-do 大调色彩,明亮
商调式 D-E-G-A-C-D re-mi-sol-la-do-re 微暗淡
角调式 E-G-A-C-D-E mi-sol-la-do-re-mi 清丽
徵调式 G-A-C-D-E-G sol-la-do-re-mi-sol 明亮
羽调式 A-C-D-E-G-A la-do-re-mi-sol-la 小调色彩

生成WAV:

生成WAV: /tmp/exp02_pentatonic.wav (158804 bytes)

实验3:五声调式歌曲创作 — 民族风旋律动机与节奏型

旋律创作三要素

1. 音高序列 — 使用五声调式音阶(C D E G A)
2. 节奏型   — 音的长短组合(全音符/二分/四分/八分/十六分)
3. 力度     — 音的强弱变化

音符时值对照表

名称 时值(拍) 符号
全音符 4.00 𝅝
二分音符 2.00 𝅗𝅥
四分音符 1.00
八分音符 0.50
十六分音符 0.25 𝅘𝅥𝅯
附点四分 1.50 ♩.
附点八分 0.75 ♪.

古风旋律《竹林晚风》

调式: C宫五声调式 | 拍号: 4/4 | 速度: ♩=80

起承转合结构(宫商角徵羽记谱):

阶段 宫商角徵羽 音名
起(4小节) 角 徵 羽 宫 羽 徵 角 商 E4 G4 A4 C5 A4 G4 E4 D4
承(4小节) 宫 商 角 徵 角 商 宫 〇 C4 D4 E4 G4 E4 D4 C4 rest
转(4小节) 徵 羽 宫 羽 徵 角 商 〇 G4 A4 C5 A4 G4 E4 D4 rest
合(4小节) 宫 角 商 宫 商 宫 〇 〇 C4 E4 D4 C4 D4 C4 rest rest
生成WAV: /tmp/exp03_gufeng.wav (1190716 bytes)

常见节奏型

节奏型 拍值 力度模式
进行曲 1+1+1+1=4拍 强 弱 强 弱
华尔兹 0.75+0.25+1=2拍 强 弱弱
摇滚 0.5+0.5+1+0.5+0.5=3拍 强 弱 次强 弱 弱
切分音 0.75+0.25+0.5+0.5=2拍 强→弱→弱→强
散板 0.5+1+0.25+0.25+1=3拍 自由节奏

实验4:短歌行 — 韵脚·力度·二维数组·五声调式作曲

曹操《短歌行》:

对酒当歌,人生几何!譬如朝露,去日苦多。
慨当以慷,忧思难忘。何以解忧?唯有杜康。

韵脚分析

诗句 韵脚 韵类
对酒当歌,人生几何 歌/何 e韵
譬如朝露,去日苦多 露/多 o韵
慨当以慷,忧思难忘 慷/忘 ang韵
何以解忧,唯有杜康 忧/康 ou/ang韵

二维数组编码

每个字编码为 [字, 音名, 时值(拍), 力度(0-1)]

composition = [
    ["对", "D4", 0.5, 0.7], ["酒", "E4", 0.5, 0.6],
    ["当", "G4", 1.0, 0.8], ["歌", "A4", 1.0, 0.9],
    ["人", "G4", 0.5, 0.6], ["生", "E4", 0.5, 0.5],
    ["几", "D4", 0.5, 0.7], ["何", "C4", 1.5, 0.9],
    # ... 后续各句
]

服务器输出(节选):

字    音名     频率(Hz)       时值(拍)      力度     MIDI
-------------------------------------------------------
对    D4     293.66       0.5        ███████░░░ 62
酒    E4     329.63       0.5        ██████░░░░ 64
当    G4     392.00       1.0        ████████░░ 67
歌    A4     440.00       1.0        █████████░ 69
人    G4     392.00       0.5        ██████░░░░ 67
生    E4     329.63       0.5        █████░░░░░ 64
几    D4     293.66       0.5        ███████░░░ 62
何    C4     261.63       1.5        █████████░ 60
...
康    C4     261.63       2.0        ██████████ 60

生成WAV: /tmp/exp04_duange.wav (1296578 bytes)

实验5:四言绝句·古诗AI作曲 — 起承转合·关雎

《诗经·周南·关雎》:

关关雎鸠,在河之洲。窈窕淑女,君子好逑。

起承转合作曲法

阶段 作用 音区 节奏
呈示主题(关关雎鸠) 中低,平稳 中速
重复/延伸(在河之洲) 升高 加快上行
对比/转折(窈窕淑女) 引入新音调 切分变化
回归主题(君子好逑) 收束 圆满

AI声调映射算法

将汉字四声映射到五声调式:

声调 调名 映射音名 频率(Hz)
阴平(1声) C4 (宫) 261.63
阳平(2声) E4 (角) 329.63
上声(3声) 降升 G4 (徵) 392.00
去声(4声) A4 (羽) 440.00

服务器输出:

字    声调     调名       映射音名     频率(Hz)       阶段
------------------------------------------------------------
关    1      阴平       C4       261.63       起
关    1      阴平       C4       261.63       起
雎    1      阴平       C4       261.63       起
鸠    1      阴平       C4       261.63       起
在    4      去声       A4       440.00       承
河    2      阳平       E4       329.63       承
之    1      阴平       C4       261.63       承
洲    1      阴平       C4       261.63       承
窈    3      上声       G4       392.00       转
窕    3      上声       G4       392.00       转
淑    1      阴平       C4       261.63       转
女    3      上声       G4       392.00       转
君    1      阴平       C4       261.63       合
子    3      上声       G4       392.00       合
好    4      去声       A4       440.00       合
逑    2      阳平       E4       329.63       合

生成WAV: /tmp/exp05_guanju.wav (529244 bytes)

起承转合节奏特征

阶段 节奏型 总拍 特征
0.5+0.5+1.0+1.0 3.0 平稳,中速
0.5+0.5+0.5+0.5+1.0 3.0 加快,上行
0.25+0.25+0.5+1.0 2.0 切分,变化
0.5+0.5+0.5+0.5+1.0 3.0 收束,圆满

实验6:节奏型·变调 — rhythm_pattern·时长·强弱

节奏型大全

名称 拍值 图示 应用
均匀型 1+1+1+1 ●●●● 进行曲、儿歌
长短型 1.5+0.5+1+1 ●.●●● 附点节奏
短长型 0.5+1.5+1+1 ●●.●● 切分效果
三连音 0.67+0.67+0.66+1 ◐◐◐● 爵士摇摆
前八后十六 0.5+0.25+0.25+1 ♪𝅘𝅥𝅯● 进行曲
前十六后八 0.25+0.25+0.5+1 𝅘𝅥𝅯♪● 舞曲
休止前奏 0+1+0.5+1.5 ○●◐●. 弱起拍

变调(Transposition)

变调 = 将旋律中所有音升高或降低相同的半音数。

原始旋律(C宫): C4 D4 E4 G4 A4

变调 半音数 结果 频率比率
升高大二度(D宫) +2 D4 E4 F#4 A4 B4 1.1225
升高纯四度(F宫) +5 F4 G4 A4 C5 D5 1.3348
升高纯五度(G宫) +7 G4 A4 B4 D5 E5 1.4983
降低大二度(B宫) -2 A#3 C4 D4 F4 G4 0.8909

数学原理:频率比率 = 2^(半音数/12)。每升高一个八度(12半音),频率翻倍。

拍号强弱规律

拍号 拍数 强弱模式 应用
2/4 2拍/小节 强 弱 进行曲
3/4 3拍/小节 强 弱 弱 华尔兹
4/4 4拍/小节 强 弱 次强 弱 流行音乐
6/8 6拍/小节 强 弱弱 次强弱弱 船歌
生成WAV: /tmp/exp06_transpose.wav (432224 bytes)

实验7:酒干倘卖无 — 模进上行·模进下行

模进(Sequence)原理

模进 = 将一个旋律动机在不同音高上重复出现
  模进上行: 每次重复时升高音高(情绪递进、推向高潮)
  模进下行: 每次重复时降低音高(情绪回落、收束)

《酒干倘卖无》— 苏芮演唱,侯德健作曲。(酒干=空酒瓶,倘卖无=有没有卖)

模进上行示例

动机: C-E-G (三音上行),每次升高大二度(+2半音)

第几次 半音偏移 音名 频率(Hz)
原型 0 C4 E4 G4 261.6 / 329.6 / 392.0
+2 2 D4 F#4 A4 293.7 / 370.0 / 440.0
+4 4 E4 G#4 B4 329.6 / 415.3 / 493.9
+6 6 F#4 A#4 C#5 370.0 / 466.2 / 554.4
+8 8 G#4 C5 D#5 415.3 / 523.3 / 622.3

模进下行示例

动机: A-G-E (三音下行),每次降低大二度(-2半音)

第几次 半音偏移 音名 频率(Hz)
原型 0 A4 G4 E4 440.0 / 392.0 / 329.6
-2 -2 G4 F4 D4 392.0 / 349.2 / 293.7
-4 -4 F4 D#4 C4 349.2 / 311.1 / 261.6
-6 -6 D#4 C#4 A#3 311.1 / 277.2 / 233.1
-8 -8 C#4 B3 G#3 277.2 / 246.9 / 207.7

模进的情感效果

类型 情感效果 应用场景
模进上行 情绪逐渐高涨,推向高潮 副歌前奏、情绪积累
模进下行 情绪逐渐舒缓,归于平静 尾声、收束段落
自由模进 保持音程关系但允许微调 爵士即兴
严格模进 完全保持音程关系 古典赋格
生成WAV: /tmp/exp07_sequence.wav (882044 bytes)

实验8:宋词 — 词牌旋律创作

宋词与音乐

宋词本为配乐演唱的歌词,每个词牌对应固定曲调。词牌名 = 曲调名。

苏轼《水调歌头·明月几时有》:

明月几时有?把酒问青天。不知天上宫阙,今夕是何年。
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。

水调歌头词牌格律

上阕: 5-5-6-5-6-5-6 (字数)
下阕: 2-4-4-5-6-5-6 (字数)
押韵: 平韵一韵到底 (天/年/寒/间)

为《水调歌头》作曲

调式: D羽调式 (A-C-D-E-G-A) | 拍号: 散板→4/4

上阕38字逐字配音(节选):

字    音名     频率(Hz)     时值(拍)    力度     阶段
--------------------------------------------------
明    A4     440.00     1.0      ███████░░░ 起
月    G4     392.00     0.5      ██████░░░░ 起
几    E4     329.63     0.5      █████░░░░░ 起
时    G4     392.00     0.5      ██████░░░░ 起
有    A4     440.00     1.5      ████████░░ 起
把    D4     293.66     0.5      ██████░░░░ 承
酒    E4     329.63     0.5      █████░░░░░ 承
问    G4     392.00     1.0      ███████░░░ 承
青    A4     440.00     0.5      ███████░░░ 承
天    C5     523.25     2.0      █████████░ 承
...
寒    D4     293.66     2.0      █████████░ 合

生成WAV: /tmp/exp08_shuidiao.wav (1256894 bytes)

常见词牌音乐特点

词牌名 字数 风格 代表作
水调歌头 95字 雄浑豪放 苏轼《明月几时有》
念奴娇 100字 激越豪壮 苏轼《赤壁怀古》
蝶恋花 60字 缠绵悱恻 柳永《伫倚危楼风细细》
满江红 93字 慷慨激昂 岳飞《怒发冲冠》
雨霖铃 103字 凄婉哀怨 柳永《寒蝉凄切》
声声慢 97字 凄凉悲苦 李清照《寻寻觅觅》

第2章 MIDI制作

实验9:搭建基础环境 — MIDI制作

MIDI基础概念

MIDI (Musical Instrument Digital Interface)
- 数字音乐的国际标准协议
- 传输的是音乐事件(音符开/关/力度/控制器),不是音频
- 文件极小: 1分钟MIDI约2-10KB,WAV约5-10MB

MIDI消息类型

消息类型 说明 参数 状态字节
note_on 音符按下 channel, note, velocity 0x90-0x9F
note_off 音符释放 channel, note, velocity 0x80-0x8F
control_change 控制器变化 channel, control, value 0xB0-0xBF
program_change 音色切换 channel, program 0xC0-0xCF
pitchwheel 弯音轮 channel, pitch 0xE0-0xEF
meta 元信息 type, value 0xFF

General MIDI 标准音色表

编号 英文名 中文名
0 Acoustic Grand Piano 大钢琴
1 Bright Acoustic Piano 明亮钢琴
4 Electric Piano 1 电钢琴
24 Acoustic Guitar (nylon) 尼龙弦吉他
30 Distortion Guitar 失真吉他
40 Violin 小提琴
42 Cello 大提琴
56 Trumpet 小号
73 Flute 长笛
78 Shakuhachi 尺八(日本竹笛)

GM标准打击乐器 (Channel 10)

音符 英文 中文
35 Acoustic Bass Drum 底鼓
36 Bass Drum 1 大鼓
38 Acoustic Snare 军鼓/小鼓
42 Closed Hi-Hat 闭合踩镲
46 Open Hi-Hat 开放踩镲
49 Crash Cymbal 1 吊镲
51 Ride Cymbal 1 叮叮镲
52 Chinese Cymbal 中国镲

用mido创建MIDI文件

from mido import Message, MidiTrack, MidiFile, MetaMessage, bpm2tempo

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

# 设置速度 BPM=120
track.append(MetaMessage("set_tempo", tempo=bpm2tempo(120), time=0))
# 设置音色: 0 = 大钢琴
track.append(Message("program_change", program=0, channel=0, time=0))

# C大调音阶
scale_notes = [60, 62, 64, 65, 67, 69, 71, 72]  # C4 D4 E4 F4 G4 A4 B4 C5
for note in scale_notes:
    velocity = 80 if note % 2 == 0 else 60
    track.append(Message("note_on", note=note, velocity=velocity, time=0))
    track.append(Message("note_off", note=note, velocity=0, time=480))  # 四分音符

mid.save("/tmp/exp09_scale.mid")

服务器输出:

保存MIDI: /tmp/exp09_scale.mid (123 bytes)

读取并分析MIDI文件:
  MIDI类型: 1
  ticks_per_beat: 480
  音轨数: 1
  音轨 0: Demo Melody
    消息数: 20
    [ 0] MetaMessage('set_tempo', tempo=500000, time=0)
    [ 1] program_change channel=0 program=0 time=0
    [ 3] note_on channel=0 note=60 velocity=80 time=0
    [ 4] note_off channel=0 note=60 velocity=0 time=480
    ...

五声调式MIDI旋律

# BPM=90, 音色=长笛(73)
track3.append(Message("program_change", program=73, channel=0, time=0))

pentatonic_melody = [
    (60, 480, 80),  # C4 四分音符 强
    (62, 480, 60),  # D4 四分音符 弱
    (64, 480, 80),  # E4
    (67, 960, 70),  # G4 二分音符
    (69, 480, 80),  # A4
    (72, 960, 90),  # C5 二分音符 高潮
    (69, 480, 70),  # A4
    (67, 480, 60),  # G4
    (64, 480, 70),  # E4
    (62, 480, 60),  # D4
    (60, 960, 90),  # C4 二分音符 收束
]

MIDI文件结构分析:

  总tick数: 6720
  ticks_per_beat: 480
  BPM: 90
  总时长: 9.33秒
  音符数: 11
  文件大小: 156 bytes

对比:9.33秒的MIDI仅156 bytes,同等时长的WAV约823KB——MIDI比WAV小5280倍!


实验10:打击乐器的变化

基本鼓点模式 (4/4拍)

GM标准: Channel 10 (0-indexed: channel=9) 专用于打击乐。

时间(tick)  乐器              音符  力度         拍位
-------------------------------------------------------
0           Bass Drum         36    ██████████  1
0           Hi-Hat(closed)    42    ██████░░░░  1
240         Hi-Hat(closed)    42    █████░░░░░  1&
480         Snare             38    █████████░  2
480         Hi-Hat(closed)    42    ██████░░░░  2
720         Hi-Hat(closed)    42    █████░░░░░  2&
960         Bass Drum         36    ██████████  3
960         Hi-Hat(closed)    42    ██████░░░░  3
1200        Hi-Hat(closed)    42    █████░░░░░  3&
1440        Snare             38    █████████░  4
1440        Hi-Hat(closed)    42    ██████░░░░  4
1680        Hi-Hat(closed)    42    █████░░░░░  4&

保存: /tmp/exp10_drum_basic.mid (133 bytes)

MIDI事件列表构建(关键技巧)

# 构建事件列表: (tick, is_note_on, note, velocity)
events = []
for note, tick, vel in drum_pattern:
    events.append((tick, 1, note, vel))       # note_on
    events.append((tick + 120, 0, note, 0))   # note_off
events.sort(key=lambda x: (x[0], x[1]))  # 按tick排序

# 写入MIDI轨道
prev_tick = 0
for tick, is_on, note, vel in events:
    delta = max(0, tick - prev_tick)
    if is_on:
        track.append(Message("note_on", note=note, velocity=vel, 
                            channel=9, time=delta))
    else:
        track.append(Message("note_off", note=note, velocity=0, 
                            channel=9, time=delta))
    prev_tick = tick

踩坑:同时刻多个音符必须用事件列表排序后逐个写入,直接交替note_on/note_off会导致delta time为负值。

三种鼓点变化

变化1: 摇滚节拍 (Rock Beat)

踩镲在每个八分音符位置
底鼓在拍1,3,5,7 (强拍)
军鼓在拍2,4,6,8 (弱拍反拍)

变化2: 华尔兹节拍 (3/4, 3拍/小节)

小节1 拍1: Bass Drum + Hi-Hat (强)
小节1 拍2: Snare + Hi-Hat (弱)
小节1 拍3: Snare + Hi-Hat (弱)

变化3: 切分节奏 (Syncopation)

底鼓: 拍1, 拍2.5(切分), 拍4
军鼓: 拍2, 拍4.5(切分)
踩镲: 每个八分 + 反拍加重

Ghost Notes(幽灵音)

Ghost Note = 极低力度的装饰音,增加律动感。

位置 乐器 力度 效果
强拍底鼓 Bass Drum 120 ████████████
弱拍军鼓 Snare 40 ████
幽灵音 Snare 20 ██
强拍军鼓 Snare 110 ███████████
踩镲强 Hi-Hat 80 ████████
踩镲弱 Hi-Hat 35 ███
幽灵音 Snare 18
保存: /tmp/exp10_drum_variations.mid (491 bytes)

第3章 音频处理

实验11:TTS — edge-tts 文字转语音

edge-tts 概述

edge-tts: 微软Edge浏览器TTS引擎的Python接口
- 免费、无需API Key
- 支持300+语音(含中文普通话、粤语、方言)
- 输出MP3格式
- 支持SSML标记(语速、音调调整)

可用中文语音

语音名称 地区 性别 特点
zh-CN-XiaoxiaoNeural zh-CN 温柔亲切
zh-CN-YunxiNeural zh-CN 沉稳阳刚
zh-CN-YunyangNeural zh-CN 新闻播报
zh-CN-XiaochenNeural zh-CN 活泼明快
zh-CN-XiaohanNeural zh-CN 温暖舒缓
zh-CN-XiaomengNeural zh-CN 柔美梦幻
zh-CN-XiaoqiuNeural zh-CN 成熟知性
zh-CN-XiaoruiNeural zh-CN 干练利落
zh-CN-XiaoshuangNeural zh-CN 少女音
zh-CN-XiaoyanNeural zh-CN 标准播音
zh-CN-liaoning-XiaobeiNeural zh-CN-liaoning 东北话
zh-CN-shaanxi-XiaoniNeural zh-CN-shaanxi 陕西话
zh-HK-HiuMaanNeural zh-HK 粤语
zh-TW-HsiaoChenNeural zh-TW 台湾国语

TTS合成代码

import edge_tts, asyncio

async def tts_synthesize(text, voice, output_file, rate="+0%", pitch="+0Hz"):
    communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
    await communicate.save(output_file)

# 普通话朗读《短歌行》
text = "对酒当歌,人生几何。譬如朝露,去日苦多。"
asyncio.run(tts_synthesize(text, "zh-CN-XiaoxiaoNeural", "/tmp/tts_poem.mp3"))

服务器输出

【TTS合成示例1: 普通话朗读《短歌行》】
  语音: zh-CN-XiaoxiaoNeural (女声)
  保存: /tmp/exp11_tts_poem.mp3 (61200 bytes)

【TTS合成示例2: 英语朗读】
  语音: en-US-AriaNeural (女声)
  保存: /tmp/exp11_tts_english.mp3 (37584 bytes)

【粤语TTS合成】
  语音: zh-HK-HiuMaanNeural (粤语女声)
  保存: /tmp/exp11_tts_cantonese.mp3 (39600 bytes)

语速与音调控制

测试文本: “宫商角徵羽,五声调式之美。” 语音: zh-CN-YunxiNeural (男声)

参数 rate pitch 文件大小 效果
正常语速 +0% +0Hz 22464 bytes 基准
慢速朗读 -30% +0Hz 31824 bytes 语速放慢,文件变大
快速朗读 +50% +0Hz 15120 bytes 语速加快,文件变小
低沉声音 +0% -50Hz 22320 bytes 音调降低
高亢声音 +0% +50Hz 22464 bytes 音调升高

实验12:WAV波形文件分析 — 方言

WAV文件格式

WAV (Waveform Audio File Format)
- RIFF (Resource Interchange File Format) 容器
- 无损PCM编码
- 结构: RIFF头(12B) + fmt块(24B) + data块(8B头+音频数据)

WAV二进制结构分析

Offset 0-3:   b'RIFF'     (RIFF标识)
Offset 4-7:        88236  (文件大小-8)
Offset 8-11:  b'WAVE'     (WAVE标识)
Offset 12-15: b'fmt '     (fmt 标识)
Offset 16-19:         16  (fmt块大小)
Offset 20-21:          1  (音频格式=1 PCM)
Offset 22-23:          1  (声道数)
Offset 24-27:      44100  (采样率)
Offset 28-31:      88200  (字节率 = 采样率×声道×位深/8)
Offset 32-33:          2  (块对齐 = 声道×位深/8)
Offset 34-35:         16  (位深度 = 16bit)
Offset 36-39: b'data'     (data标识)
Offset 40-43:      88200  (音频数据大小)

Python wave模块分析

import wave, numpy as np

with wave.open("/tmp/exp12_test.wav", "rb") as wf:
    print(f"声道数: {wf.getnchannels()}")         # 1
    print(f"采样位深: {wf.getsampwidth()*8} bit")  # 16
    print(f"采样率: {wf.getframerate()} Hz")       # 44100
    print(f"总帧数: {wf.getnframes()}")            # 44100
    print(f"总时长: {wf.getnframes()/wf.getframerate():.3f}秒")  # 1.000

    frames = wf.readframes(wf.getnframes())
    samples = np.frombuffer(frames, dtype=np.int16)
    print(f"RMS: {np.sqrt(np.mean(samples.astype(np.float64)**2)):.2f}")
    print(f"峰值因子: {np.max(np.abs(samples)) / np.sqrt(np.mean(samples.astype(np.float64)**2)):.2f}")

输出:

  声道数:       1
  采样位深度:   16 bit
  采样率:       44100 Hz
  总帧数:       44100
  总时长:       1.000 秒
  最大振幅:     18486
  RMS:          11904.37
  峰值因子:     1.55

FFT频谱分析

from scipy.fft import fft, fftfreq

N = len(samples)
yf = fft(samples.astype(np.float64))
xf = fftfreq(N, 1 / SAMPLE_RATE)

magnitude = np.abs(yf[:N//2])
freqs = xf[:N//2]
peak_indices = np.argsort(magnitude)[-5:][::-1]

440Hz(A4)信号的FFT检测结果:

频率(Hz) 幅度 说明
440.00 350,402,948 基频 (A4)
880.00 105,116,882 2次谐波
1320.00 35,033,800 3次谐波
441.00 10,802,444 频率分辨率噪声
439.00 10,802,354 频率分辨率噪声

基频440Hz + 2次谐波880Hz + 3次谐波1320Hz,正是代码中三个正弦波叠加的结果。

不同采样率对比

采样率 文件大小 音质 应用场景
8000 16KB 电话音质 电话/对讲机
16000 32KB 语音音质 VoIP语音
22050 44KB FM音质 网络音频
44100 87KB CD音质 音乐CD
48000 94KB DVD音质 专业音频/DVD

方言TTS合成

方言 语音引擎 文件大小 文本
普通话 zh-CN-XiaoxiaoNeural 26KB 今天天气真好,我们一起去公园散步吧。
粤语 zh-HK-HiuMaanNeural 29KB 今日天气好好,我哋一齐去公园散步啦。
台湾国语 zh-TW-HsiaoChenNeural 29KB 今天天气真好,我们一起去公园散步吧。
四川话模拟 zh-CN-XiaoyiNeural 29KB 今天天气好得很嘛,我们一起去公园走一下嘛。

WAV波形ASCII可视化

440Hz正弦波(A4)前200个采样点的ASCII波形图:

  ------------------------------------------------------------
                                                             *                 
                                    *                        ***               
           ******                *******                   *****               
  *************            *****                          **********           
                                                      **********************  
  ------------------------------------------------------------
  采样率: 44100Hz | 位深: 16bit | 采样点: 200

音频格式对比

格式 编码方式 码率 音质 文件大小
WAV 无压缩PCM 1411kbps CD音质 10MB/分钟
MP3 有损压缩 128-320kbps 接近CD 1-3MB/分钟
FLAC 无损压缩 700-1000kbps 无损 5-7MB/分钟
AAC 有损压缩 128-256kbps 优于MP3 1-2MB/分钟
OGG 有损压缩 96-500kbps 接近MP3 1-2MB/分钟
MIDI 事件序列 事件数据 依赖音源 2-10KB/分钟

服务器产出文件清单

实验 文件 格式 大小
1 exp01_tange.wav WAV 319KB
2 exp02_pentatonic.wav WAV 156KB
3 exp03_gufeng.wav WAV 1.2MB
4 exp04_duange.wav WAV 1.3MB
5 exp05_guanju.wav WAV 517KB
6 exp06_transpose.wav WAV 423KB
7 exp07_sequence.wav WAV 862KB
8 exp08_shuidiao.wav WAV 1.2MB
9 exp09_scale.mid MIDI 123B
9 exp09_pentatonic.mid MIDI 156B
10 exp10_drum_basic.mid MIDI 133B
10 exp10_drum_variations.mid MIDI 491B
11 exp11_tts_poem.mp3 MP3 60KB
11 exp11_tts_english.mp3 MP3 37KB
11 exp11_tts_cantonese.mp3 MP3 39KB
11 exp11_tts_*.mp3 (5个) MP3 115KB
12 exp12_test.wav WAV 87KB
12 exp12_wav_*.wav (5个) WAV 273KB
12 exp12_tts_*.mp3 (4个) MP3 113KB
合计 29个文件 ~6.9MB

踩坑记录

# 问题 原因 解决
1 FluidSynth安装失败 华为云镜像源404 使用mido纯Python方案,不依赖FluidSynth
2 MIDI保存报time must be non-negative 同时刻多个音符交替note_on/off导致delta为负 构建事件列表排序后逐个写入
3 WAV ASCII可视化OverflowError int16溢出(threshold>32767) 转为int32后比较
4 PyPI超时 华为云香港节点网络限制 使用阿里云镜像 mirrors.aliyun.com

技术要点总结

音名 ↔ 频率 ↔ MIDI编号 换算

# 音名 → 频率 (A4=440Hz为基准)
freq = 440.0 * (2.0 ** ((midi_note - 69) / 12.0))

# 频率 → MIDI编号
midi_note = int(round(12 * math.log2(freq / 440.0) + 69))

# 音名 → MIDI编号
# C4=60, D4=62, E4=64, F4=65, G4=67, A4=69, B4=71, C5=72

五声调式核心

宫(do) 商(re) 角(mi) 徵(sol) 羽(la)
  C      D      E      G      A
无半音关系 → 和谐东方韵味

MIDI vs WAV 文件大小

属性 MIDI WAV (CD音质)
编码方式 事件序列 PCM采样
1分钟大小 2-10KB ~10MB
压缩比 ~1000:1 1:1
音质 依赖音源 固定不变
编辑性 易编辑 难编辑

edge-tts 语速/音调参数

# rate: 语速调整 (-100% 到 +100%)
# pitch: 音调调整 (-50Hz 到 +50Hz)
edge_tts.Communicate(text, voice, rate="-30%", pitch="+50Hz")
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐