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

OpenAI生成的TXT转MIDI代码报错:mido无note_to_midi属性

修复Mido库AttributeError: 'module' has no attribute 'note_to_midi'问题

问题说明

运行Python代码将乐谱TXT转MIDI时触发AttributeError,提示module 'mido' has no attribute 'note_to_midi',重装openai库无效,在WSL2 Ubuntu 22.04 LTS和Windows 10 VS Code环境均复现该问题。

错误根源

  • note_to_midi并非mido主模块的方法,该函数属于mido.midifiles.meta子模块;
  • 重装openai库的操作无效,因为问题和openai无关,是mido库的使用方式错误。

修复方案

提供两种可行的修复方式:

方式一:使用mido内置的meta模块函数

修改导入语句引入note_to_midi,同时修复乐器设置的无效写法:

import mido
from mido.midifiles.meta import note_to_midi

# Function to convert a music chart string into a MIDI track
def chart_to_track(chart, instrument):
    # Split the chart into a list of measures
    measures = chart.strip().split("|")
    # Initialize an empty list to store the MIDI events
    events = []
    # Set the current time to zero
    time = 0
    # Iterate through the measures
    for measure in measures:
        # Split the measure into a list of notes
        notes = measure.strip().split()
        # Iterate through the notes
        for note in notes:
            # Convert the note name to a MIDI pitch
            pitch = note_to_midi(note)
            # Add a NOTE_ON event to the events list
            events.append(mido.Message('note_on', note=pitch, velocity=127, time=time))
            # Add a NOTE_OFF event to the events list after a quarter note duration
            events.append(mido.Message('note_off', note=pitch, velocity=127, time=480))
            # Increase the current time by a quarter note duration
            time += 480
    # Create a MIDI track with the events and the specified instrument
    track = mido.MidiTrack()
    # 修复:添加program_change消息设置乐器,原track.program写法无效
    track.append(mido.Message('program_change', program=instrument))
    track.extend(events)
    return track

# Function to prompt the user to choose an instrument
def choose_instrument():
    # Print a list of instrument options
    print("Choose an instrument:")
    print("1. Acoustic Grand Piano")
    print("2. Electric Guitar (clean)")
    print("3. Violin")
    print("4. Trumpet")
    print("5. Clarinet")
    # Prompt the user to enter a number
    choice = input("Enter the number of your choice: ")
    # Return the corresponding instrument number
    if choice == "1":
        return 0
    elif choice == "2":
        return 25
    elif choice == "3":
        return 40
    elif choice == "4":
        return 57
    elif choice == "5":
        return 71
    else:
        print("Invalid choice. Please try again.")
        return choose_instrument()

# Main function
def main():
    # Prompt the user to enter the name of the text file
    filename = input("Enter the name of the text file: ")
    # Open the file and read the contents into a string
    with open(filename, "r") as f:
        chart = f.read()
    # Choose an instrument
    instrument = choose_instrument()
    # Convert the chart to a MIDI track
    track = chart_to_track(chart, instrument)
    # Create a MIDI file and add the track
    midi = mido.MidiFile()
    midi.tracks.append(track)
    # Prompt the user to enter the name of the MIDI file
    output_filename = input("Enter the name of the MIDI file: ")
    # Save the MIDI file
    midi.save(output_filename)

# Run the main function
main()

方式二:自定义音符转换函数

如果不想依赖mido子模块,可自己实现音符到MIDI音高的映射:

添加以下自定义函数到代码中:

def note_to_midi(note):
    note_map = {
        'C0': 12, 'C#0':13, 'Db0':13, 'D0':14, 'D#0':15, 'Eb0':15, 'E0':16, 'F0':17,
        'F#0':18, 'Gb0':18, 'G0':19, 'G#0':20, 'Ab0':20, 'A0':21, 'A#0':22, 'Bb0':22, 'B0':23,
        'C1':24, 'C#1':25, 'Db1':25, 'D1':26, 'D#1':27, 'Eb1':27, 'E1':28, 'F1':29,
        'F#1':30, 'Gb1':30, 'G1':31, 'G#1':32, 'Ab1':32, 'A1':33, 'A#1':34, 'Bb1':34, 'B1':35,
        'C2':36, 'C#2':37, 'Db2':37, 'D2':38, 'D#2':39, 'Eb2':39, 'E2':40, 'F2':41,
        'F#2':42, 'Gb2':42, 'G2':43, 'G#2':44, 'Ab2':44, 'A2':45, 'A#2':46, 'Bb2':46, 'B2':47,
        'C3':48, 'C#3':49, 'Db3':49, 'D3':50, 'D#3':51, 'Eb3':51, 'E3':52, 'F3':53,
        'F#3':54, 'Gb3':54, 'G3':55, 'G#3':56, 'Ab3':56, 'A3':57, 'A#3':58, 'Bb3':58, 'B3':59,
        'C4':60, 'C#4':61, 'Db4':61, 'D4':62, 'D#4':63, 'Eb4':63, 'E4':64, 'F4':65,
        'F#4':66, 'Gb4':66, 'G4':67, 'G#4':68, 'Ab4':68, 'A4':69, 'A#4':70, 'Bb4':70, 'B4':71,
        'C5':72, 'C#5':73, 'Db5':73, 'D5':74, 'D#5':75, 'Eb5':75, 'E5':76, 'F5':77,
        'F#5':78, 'Gb5':78, 'G5':79, 'G#5':80, 'Ab5':80, 'A5':81, 'A#5':82, 'Bb5':82, 'B5':83,
        'C6':84, 'C#6':85, 'Db6':85, 'D6':86, 'D#6':87, 'Eb6':87, 'E6':88, 'F6':89,
        'F#6':90, 'Gb6':90, 'G6':91, 'G#6':92, 'Ab6':92, 'A6':93, 'A#6':94, 'Bb6':94, 'B6':95,
        'C7':96, 'C#7':97, 'Db7':97, 'D7':98, 'D#7':99, 'Eb7':99, 'E7':100, 'F7':101,
        'F#7':102, 'Gb7':102, 'G7':103, 'G#7':104, 'Ab7':104, 'A7':105, 'A#7':106, 'Bb7':106, 'B7':107,
        'C8':108, 'C#8':109, 'Db8':109, 'D8':110, 'D#8':111, 'Eb8':111, 'E8':112, 'F8':113,
        'F#8':114, 'Gb8':114, 'G8':115, 'G#8':116, 'Ab8':116, 'A8':117, 'A#8':118, 'Bb8':118, 'B8':119
    }
    return note_map.get(note, 60)  # 默认返回中央C(C4)

然后删除原代码中mido.note_to_midi相关的导入和调用,替换为这个自定义函数即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 10:30:52