You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python将Wav文件音色分配给MIDI文件?

用Python实现Wav吉他音色替换MIDI轨道的钢琴音色

核心思路是利用采样器逻辑:读取MIDI中的音符事件(音高、时长、时间点、力度),将吉他Wav样本转调到对应音符的音高,再按MIDI的时间轴拼接合成新音频——这和你之前用的audio-to-midi工具逻辑完全不同,后者是音频转MIDI,而我们需要的是用音频样本映射MIDI音符。

所需依赖库

先安装必要的Python库:

pip install mido librosa soundfile numpy sounddevice

实现步骤与代码示例

1. 读取MIDI文件,提取关键音符信息

用mido库读取MIDI的音符开/关事件,转换为统一的音符数据结构:

import mido
import numpy as np

def read_midi_notes(midi_path):
    mid = mido.MidiFile(midi_path)
    ticks_per_beat = mid.ticks_per_beat
    tempo = 500000  # 默认对应BPM=120,可从MIDI元事件读取实际值
    notes = []
    current_time = 0.0
    active_notes = {}

    for msg in mid:
        current_time += mido.tick2second(msg.time, ticks_per_beat, tempo)
        if msg.type == 'set_tempo':
            tempo = msg.tempo
        elif msg.type == 'note_on' and msg.velocity > 0:
            active_notes[msg.note] = (current_time, msg.velocity)
        elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0):
            if msg.note in active_notes:
                start_time, velocity = active_notes.pop(msg.note)
                duration = current_time - start_time
                notes.append({
                    'pitch': msg.note,
                    'start': start_time,
                    'duration': duration,
                    'velocity': velocity
                })
    return notes

2. 读取并预处理吉他Wav样本

用librosa读取Wav文件,转成单声道,并指定样本的基准音高(比如你的吉他样本是A4,对应MIDI音高编号69):

import librosa
import soundfile as sf

def load_guitar_sample(sample_path, base_pitch=69):
    audio, sr = librosa.load(sample_path, mono=True)
    return audio, sr, base_pitch

3. 音高转换与音频合成

对每个MIDI音符计算音高差,转调后按时间轴拼接所有音频片段:

def synthesize_audio(notes, guitar_audio, sr, base_pitch):
    # 计算总时长,创建空数组存储合成音频
    max_end_time = max(n['start'] + n['duration'] for n in notes)
    synthesized = np.zeros(int(max_end_time * sr), dtype=np.float32)

    for note in notes:
        # 计算音高差(半音数)
        pitch_diff = note['pitch'] - base_pitch
        # 转调处理
        shifted_audio = librosa.effects.pitch_shift(guitar_audio, sr=sr, n_steps=pitch_diff)
        # 根据MIDI力度调整音量(力度范围0-127)
        volume = note['velocity'] / 127.0
        shifted_audio *= volume

        # 计算音频在合成数组中的位置
        start_sample = int(note['start'] * sr)
        end_sample = start_sample + len(shifted_audio)
        # 处理超出总时长的边界情况
        if end_sample > len(synthesized):
            shifted_audio = shifted_audio[:len(synthesized)-start_sample]
            end_sample = len(synthesized)
        # 叠加音频片段
        synthesized[start_sample:end_sample] += shifted_audio

    # 归一化防止音量过载
    synthesized = librosa.util.normalize(synthesized)
    return synthesized, sr

4. 主函数调用

if __name__ == '__main__':
    # 替换为你的MIDI和Wav文件路径
    midi_file = "your_piano_midi.mid"
    guitar_sample = "your_guitar_sample.wav"

    # 读取MIDI音符
    notes = read_midi_notes(midi_file)
    # 读取吉他样本(注意调整base_pitch为你的样本实际音高编号)
    guitar_audio, sr, base_pitch = load_guitar_sample(guitar_sample, base_pitch=69)
    # 合成音频
    synthesized_audio, sr = synthesize_audio(notes, guitar_audio, sr, base_pitch)
    # 保存合成后的Wav文件
    sf.write("guitar_midi_output.wav", synthesized_audio, sr)
    # 可选:播放合成音频
    import sounddevice as sd
    sd.play(synthesized_audio, sr)
    sd.wait()

关键注意事项

  • 吉他样本选择:优先用干净的单音采样(无混响、失真等效果),转调后音质更自然;若用和弦采样,转调会导致和声混乱。
  • 基准音校准:必须确认吉他样本对应的MIDI音高编号(比如C4对应60,A4对应69),否则转调会出错。
  • MIDI tempo校准:示例默认用120BPM,实际可从MIDI的set_tempo元事件读取真实速度,保证时间轴准确。
  • 重叠音符处理:代码用叠加方式处理重叠音符,若要模拟吉他拨弦间隔,可添加触发间隔判断逻辑。

内容的提问来源于stack exchange,提问作者clacson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:22:36