stumblemodel 发表于 2026-7-3 23:05:29

分析乐音

python实现:输入一段音频文件,音频内容是一首钢琴曲的录音,输出这个曲子的乐谱

FishC 发表于 2026-7-3 23:05:30

FishC 分析乐音方案

要实现从钢琴曲录音提取乐谱,可以使用 Python 的 librosa、crepe 和 pydub 等库。基本流程如下:


[*] 读取音频文件,转换为单声道(mono)。
[*] 进行短时傅里叶变换(STFT),得到频谱图。
[*] 使用常数Q变换(CQT)或简单音高估计算法(如自相关、YIN算法)提取每个时间帧的音高。
[*] 对音高序列进行平滑、去除倍频错误。
[*] 检测音符 onset(起始点),通过能量变化或频谱差异分割音符。
[*] 将每个音符的音高映射到 MIDI 编号,时值量化(四分音符、八分音符等)。
[*] 生成 MIDI 文件或文本形式的简谱。


示例代码(核心部分)


import librosa
import numpy as np

# 1. 读取音频
y, sr = librosa.load('piano.wav', sr=44100, mono=True)

# 2. 提取 onset 和音高
onset_frames = librosa.onset.onset_detect(y=y, sr=sr)
pitches, magnitudes = librosa.piptrack(y=y, sr=sr)

# 3. 将上一步的 onset_frames 转化为时间
onset_times = librosa.frames_to_time(onset_frames, sr=sr)

# 4. 在每个 onset 区间内取平均音高
notes = []
for i in range(len(onset_times)-1):
    start, end = onset_times, onset_times
    # 找到对应时间范围
    start_frame = librosa.time_to_frames(start, sr=sr)
    end_frame = librosa.time_to_frames(end, sr=sr)
    # 取该区间内 energy 最大的音高
    idx = np.argmax(np.mean(magnitudes[:, start_frame:end_frame], axis=1))
    freq = pitches
    midi = librosa.hz_to_midi(freq)
    duration = end - start
    notes.append((midi, duration))

# 5. 输出简谱(简化:只显示音高和时值)
for midi, dur in notes:
    note_name = librosa.midi_to_note(midi)
    print(f'音符: {note_name}, 时值: {dur:.2f}秒')


注意事项

- 上述代码仅为教学演示,实际音乐中可能有和弦(多个同时音)、踏板延音、泛音干扰,需要更高级算法(如神经网络音高检测、和弦识别)。
- 推荐使用 basic-pitch(Google Magenta)或 pypianoroll 进行多音高提取。
- 时值量化需结合节拍追踪(librosa.beat.tempo)来假设节拍速度。

最终输出乐谱形式

- MIDI 文件:使用 mido 库写入,可以在 DAW 中打开。
- 文本乐谱:如 ABC notation、MusicXML(需专用库)。
- 简谱(数字谱):将 MIDI 编号映射为简谱数字和八度。

建议学习路线

1. 先掌握 librosa 基础操作(加载、STFT、onset detect)。
2. 深入学习音高估计(pitch tracking)。
3. 研究音乐理论(时值、节拍、调式)以优化量化算法。
4. 尝试使用现成的音符转写模型(如 Onsets and Frames 演示模型)。

有任何细节问题,可以继续追问,我会尽力帮你拆解。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 分析乐音