news 2026/4/18 11:08:11

超酷!用Python写回文音乐,3步搞定你的MIDI创作处女作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
超酷!用Python写回文音乐,3步搞定你的MIDI创作处女作

音乐与代码的碰撞能产生什么火花?当数学中的回文结构遇上MIDI音乐创作,我们可以用Python生成一首正着听反着听都相同的奇妙旋律。无需乐理基础,无需专业设备,只需30分钟,你就能完成人生第一首AI辅助创作的音乐作品。

一、回文音乐:代码与艺术的完美对称
1.1 什么是回文音乐?
回文音乐(Palindrome Music)指正序和倒序播放完全相同的音乐片段。就像"上海自来水来自海上"这句话,无论从左读还是从右读都完全一致。在音乐中,这种结构会创造出独特的听觉体验——前半段逐渐展开,后半段自然收束,形成完美的闭环。

历史上许多作曲家都尝试过这种形式:巴赫的《螃蟹卡农》可以上下颠倒演奏;韦伯恩的《钢琴变奏曲》第二乐章是严格的回文结构。现在,我们用Python就能轻松实现这种高级音乐技巧。

1.2 技术实现原理
实现回文音乐的关键在于:

音符序列对称:第1个音符对应最后1个,第2个对应倒数第2个,以此类推
节奏对称:前半段的节奏型在后半段反向重现
MIDI协议支持:用数字信号精确控制每个音符的音高、时值和力度
Python的mido库能完美处理MIDI协议,配合列表操作就能轻松构建回文结构。

二、环境准备:3分钟搭建创作工坊
2.1 安装必要库
打开终端执行以下命令:

pip install mido pretty_midi numpy

mido:处理MIDI消息的核心库
pretty_midi:简化MIDI文件读写
numpy:处理数值计算
2.2 验证安装
运行这段测试代码:

import mido
print(f"检测到MIDI端口:{mido.get_output_names()}")

如果看到类似['Microsoft GS Wavetable Synth']的输出,说明系统已准备好创作。

2.3 备用方案:虚拟MIDI设备
若没有物理MIDI输出设备,可安装虚拟MIDI端口:

Windows:安装 loopMIDI
Mac:使用内置的IAC Driver
Linux:安装snd-virmidi模块
三、第一步:生成基础旋律线
3.1 定义音符参数
用Python列表存储音符信息,每个元素是(音高, 时值, 力度)的元组:

C大调音阶(音高对应MIDI编号:60=中央C)

scale = [60, 62, 64, 65, 67, 69, 71, 72]

生成8个音符的简单旋律

base_melody = [
(scale[0], 480, 80), # 音符1:C4,四分音符,力度80
(scale[2], 240, 70), # 音符2:E4,八分音符,力度70
(scale[4], 240, 75), # 音符3:G4,八分音符,力度75
(scale[5], 480, 90), # 音符4:A4,四分音符,力度90
(scale[3], 240, 65), # 音符5:F4,八分音符,力度65
(scale[1], 240, 70), # 音符6:D4,八分音符,力度70
(scale[0], 960, 100) # 音符7:C4,全音符,力度100
]

3.2 创建回文结构
将基础旋律反转并调整力度:

def create_palindrome(melody):

# 反转音符序列(保留第一个音符作为中心点) reversed_part = melody[-2::-1] # 从倒数第二个开始反转 # 调整后半部分力度(可选:减弱处理) palindrome = melody.copy() for i, note in enumerate(reversed_part): new_note = (note[0], note[1], note[2] * 0.8) palindrome.append(new_note) return palindrome

full_melody = create_palindrome(base_melody)

3.3 可视化验证
用matplotlib绘制音符分布:

import matplotlib.pyplot as plt

pitches = [note[0] for note in full_melody]
durations = [note[1]/100 for note in full_melody] # 转换为秒

plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.stem(range(len(pitches)), pitches)
plt.title("Pitch Sequence")

plt.subplot(1, 2, 2)
plt.bar(range(len(durations)), durations)
plt.title("Duration Sequence")
plt.show()

四、第二步:构建完整MIDI文件
4.1 创建MIDI轨道
使用pretty_midi构建完整结构:

import pretty_midi

def create_midi(melody, tempo=120, output_file="palindrome.mid"):

# 创建MIDI对象 pm = pretty_midi.PrettyMIDI(initial_tempo=tempo) # 创建钢琴轨道 piano_program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano') piano = pretty_midi.Instrument(program=piano_program) # 添加音符 current_time = 0 for pitch, duration, velocity in melody: # 将相对时值转换为绝对时间 note_length = duration / 1000 # 转换为秒 note = pretty_midi.Note( velocity=int(velocity), pitch=int(pitch), start=current_time, end=current_time + note_length ) piano.notes.append(note) current_time += note_length pm.instruments.append(piano) pm.write(output_file) print(f"MIDI文件已保存:{output_file}")

create_midi(full_melody)

4.2 添加和声层次
让音乐更丰富:

def add_harmony(melody, interval=3):
harmony = []
for pitch, duration, velocity in melody:

# 在指定音程上添加和声音符 harmony_pitch = pitch + interval harmony.append((harmony_pitch, duration, velocity * 0.7)) return harmony

harmony_track = add_harmony(full_melody)
create_midi(harmony_track, output_file="harmony.mid")

4.3 合并多轨道
创建包含主旋律和和声的完整MIDI:

def merge_tracks(*tracks):
pm = pretty_midi.PrettyMIDI(initial_tempo=120)

for i, track_data in enumerate(tracks): if i == 0: program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano') else: program = pretty_midi.instrument_name_to_program('String Ensemble 1') instrument = pretty_midi.Instrument(program=program) current_time = 0 for pitch, duration, velocity in track_data: note_length = duration / 1000 note = pretty_midi.Note( velocity=int(velocity), pitch=int(pitch), start=current_time, end=current_time + note_length ) instrument.notes.append(note) current_time += note_length pm.instruments.append(instrument) return pm

full_composition = merge_tracks(full_melody, add_harmony(full_melody))
full_composition.write("complete_palindrome.mid")

五、第三步:进阶创作技巧
5.1 动态节奏变化
让回文结构更自然:

import random

def dynamic_palindrome(melody, rhythm_variation=0.2):
palindrome = melody.copy()
reversed_part = melody[-2::-1]

for i, note in enumerate(reversed_part): # 随机调整时值(±20%) original_duration = note[1] variation = original_duration * rhythm_variation * (random.random() * 2 - 1) new_duration = max(10, original_duration + variation) # 最小时值10ms palindrome.append(( note[0], new_duration, note[2] * (0.7 + random.random() * 0.3) # 力度随机变化 )) return palindrome

dynamic_melody = dynamic_palindrome(base_melody, rhythm_variation=0.3)

5.2 多声部回文
创建四个声部的复杂结构:

def multi_voice_palindrome(base_melody, voices=4):
all_voices = [base_melody.copy()]

for _ in range(1, voices): # 每个声部音高偏移 offset = random.randint(-5, 5) transposed = [(p + offset, d, v) for p, d, v in base_melody] all_voices.append(transposed) # 为每个声部创建回文 full_composition = [] for voice in all_voices: palindrome = create_palindrome(voice) full_composition.extend(palindrome) return full_composition

complex_piece = multi_voice_palindrome(base_melody, voices=3)

5.3 算法生成基础旋律
完全用代码创作旋律:

def generate_random_melody(length=8, key_scale=None):
if key_scale is None:
key_scale = [60, 62, 64, 65, 67, 69, 71] # C大调

melody = [] current_pitch = key_scale[0] for _ in range(length): # 随机选择下一个音符(70%概率继续当前音阶方向) if len(melody) > 0 and random.random() < 0.7: last_pitch = melody[-1][0] diff = last_pitch - current_pitch current_pitch = last_pitch + (diff // abs(diff) if diff != 0 else 1) # 确保仍在音阶内 if current_pitch not in key_scale: current_pitch = max(min(current_pitch, key_scale[-1]), key_scale[0]) else: current_pitch = random.choice(key_scale) # 随机时值(四分音符为主) duration = random.choice([240, 240, 240, 480, 720]) # 偏重短音符 velocity = random.randint(60, 100) melody.append((current_pitch, duration, velocity)) return melody

algo_melody = generate_random_melody(length=12)

六、作品展示与优化
6.1 播放MIDI文件
使用mido播放创作成果:

def play_midi(file_path):
with open(file_path, 'rb') as f:
mid = mido.MidiFile(f)

port = mido.open_output() for msg in mid.play(): port.send(msg)

play_midi("complete_palindrome.mid")

6.2 转换为音频
推荐使用FluidSynth将MIDI转为WAV:

安装FluidSynth

sudo apt install fluidsynth # Linux
brew install fluidsynth # Mac

转换命令

fluidsynth -i soundfont.sf2 complete_palindrome.mid -F output.wav -r 44100

6.3 常见问题解决
Q1:生成的MIDI文件无法播放?
A:检查是否安装了MIDI合成器。Windows用户可安装 CoolSoft VirtualMIDISynth,Mac用户使用内置的DLS Music Device。

Q2:如何调整音乐速度?
A:修改PrettyMIDI的initial_tempo参数(默认120BPM),数值越大速度越快。

Q3:想用真实乐器音色?
A:下载高质量音源如 SFZ格式的免费音源,或使用商业音源如EastWest Quantum Leap。

七、创作灵感扩展
7.1 数学之美
回文结构本质是数学对称,尝试:

斐波那契数列生成节奏型
分形算法创建自相似结构
黄金分割比例安排乐句长度
7.2 跨学科融合
结合其他艺术形式:

用诗歌生成回文歌词
根据建筑结构创作对应音乐
用股票走势数据生成旋律
7.3 交互式创作
开发Web应用让用户实时生成回文音乐:

from flask import Flask, send_file
import io

app = Flask(name)

@app.route('/create')
def create_music():

# 这里调用之前的创作函数 melody = generate_random_melody() full = create_palindrome(melody) pm = pretty_midi.PrettyMIDI(initial_tempo=100) # ...构建MIDI对象... buffer = io.BytesIO() pm.write_to_fp(buffer) buffer.seek(0) return send_file(buffer, mimetype='audio/midi', as_attachment=True, download_name='dynamic_palindrome.mid')

ifname== 'main':
app.run(debug=True)

结语
从基础回文结构到复杂多声部创作,我们用Python解锁了音乐算法的新可能。这种创作方式不是要取代人类作曲家,而是提供新的灵感来源——就像照相机没有取代绘画,反而拓展了艺术表达的边界。现在打开你的IDE,让代码奏响数字时代的回文交响乐吧!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:57:15

springboot基于vue的大学生综合测评与奖学金评审系统_757bq110

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/4/18 7:33:57

折叠屏手机如何选?横向大折叠的便携与体验权衡

随着移动智能终端形态探索步入深水区&#xff0c;折叠屏手机从早期的尝鲜之物&#xff0c;渐渐发展成具备独特交互体以及成熟可靠性的高端之选。当下市场内的折叠屏产品主要分为横向内折、竖向内折以及外折等各异形态&#xff0c;它们分别在便携性、屏幕体验以及耐用性等方面有…

作者头像 李华
网站建设 2026/4/16 18:47:54

2025从土壤墒情到气象观测:数据驱动的农事决策链干货来袭

在现代农业管理中&#xff0c;土壤墒情监测站与农业气象观测站产生的数据并非孤立存在。在2025年&#xff0c;将它们串联起来分析&#xff0c;可以形成一条强大的数据驱动农事决策链&#xff0c;显著提升种植管理的预见性与科学性。这条决策链的运作示例&#xff1a;气象站预报…

作者头像 李华
网站建设 2026/4/18 8:28:31

使用PIL库将携带方向信息的图片转正

在深度学习训练当中&#xff0c;遇到有些图在不同平台上看到的方向不一样&#xff0c;会导致出错&#xff0c;这些图一般都是携带方向信息的。例如&#xff1a;我在训练yolo过程中遇到一些图&#xff0c;图片方向是在windows显示是正方向&#xff0c;opencv 读取的图片宽高也是…

作者头像 李华
网站建设 2026/4/18 9:04:38

JMeter 6.0性能测试实战:从瓶颈定位到优化方案全解析

JMeter 6.0性能测试实战&#xff1a;从瓶颈定位到优化方案全解析 【免费下载链接】jmeter Apache JMeter open-source load testing tool for analyzing and measuring the performance of a variety of services 项目地址: https://gitcode.com/gh_mirrors/jmeter1/jmeter …

作者头像 李华