运行音乐生成Python脚本遇TypeError,寻求修复方案
音乐生成器TypeError错误修复方案
报错信息
1 frames <ipython-input-7-3be77148eaa3> in create_training_data(data) 39 # 遍历数据并提取相关信息 40 for i in range(len(data['energy'])): ---> 41 X[i][0] = data['energy'][i] 42 X[i][1] = data['chroma_stft'][i] 43 X[i][2] = data['spectral_contrast'][i] TypeError: 'numpy.float64' object does not support item assignment
错误原因分析
- 数组维度不匹配:
create_training_data中X = np.zeros(data_shape[0])创建的是一维数组,但代码中使用了X[i][0]这种二维索引赋值,直接触发类型错误。 - Librosa特征维度处理错误:Librosa提取的音频特征(如
energy、chroma_stft)均为二维数组(形状为(特征维度, 时间步数量)),直接按索引取值会导致维度混乱。 - 循环范围错误:
len(data['energy'])获取的是特征维度数(比如energy的形状是(1, n),长度为1),而非实际需要遍历的时间步数量。 - 隐藏问题:原代码中
file_retreival函数未返回sample_rate,但main函数尝试调用data['sample_rate'];create_music中拍号处理逻辑错误,无法直接将"4/4"格式传入time_signature消息。
修正方案
1. 修复file_retreival函数
添加采样率返回,并统一特征维度为时间步优先:
def file_retreival(file): audio, sample_rate = librosa.load(file) results = { "sample_rate": sample_rate, # 新增返回采样率 "tempo": librosa.beat.tempo(audio, sample_rate)[0], # 提取tempo标量值 "energy": librosa.feature.rms(audio)[0], # 转换为一维数组(时间步维度) "chroma_stft": librosa.feature.chroma_stft(audio).T, # 转置为(时间步, 12)的二维数组 "spectral_contrast": librosa.feature.spectral_contrast(audio).T, # 转置为(时间步, 7)的二维数组 "tonnetz": librosa.feature.tonnetz(y=librosa.effects.harmonic(audio)).T # 转置为(时间步, 6)的二维数组 } return results
2. 重构create_training_data函数
创建正确维度的训练数组,按时间步拼接所有特征:
def create_training_data(data): # 获取时间步数量(所有特征的时间步一致) time_steps = len(data['energy']) # 计算总特征数:1(energy) +12(chroma)+7(contrast)+6(tonnetz)+1(tempo) =27 feature_num = 1 + data['chroma_stft'].shape[1] + data['spectral_contrast'].shape[1] + data['tonnetz'].shape[1] +1 # 创建二维训练数组:(时间步数量, 总特征数) X = np.zeros((time_steps, feature_num)) for i in range(time_steps): X[i][0] = data['energy'][i] X[i][1:13] = data['chroma_stft'][i] # 填充12维chroma特征 X[i][13:20] = data['spectral_contrast'][i] # 填充7维频谱对比度特征 X[i][20:26] = data['tonnetz'][i] # 填充6维tonnetz特征 X[i][26] = data['tempo'] # 填充tempo标量 return X
3. 修复create_music中的拍号处理
拆分用户输入的"4/4"格式,适配mido的参数要求:
def create_music(pred, sample_rate, tempo, rhythm, tone): from mido import Message, MidiFile from mido.midifiles import MidiTrack mid = MidiFile() track = MidiTrack() mid.tracks.append(track) # 将BPM转换为mido所需的微秒每拍 mido_tempo = mido.bpm2tempo(tempo) track.append(Message("set_tempo", tempo=mido_tempo)) # 拆分拍号输入(如"4/4"转为分子4、分母4) numerator, denominator = map(int, rhythm.split('/')) track.append(Message("time_signature", numerator=numerator, denominator=denominator)) # 遍历预测结果生成音符 for note_val, duration in pred: track.append(Message("note_on", note=int(note_val), velocity=127, time=0)) track.append(Message("note_off", note=int(note_val), velocity=127, time=int(duration*1000))) # 用控制消息设置基调示例 track.append(Message("control_change", control=0, value=2)) mid.save("generated_music.mid")
4. 修正main函数中的标签维度
确保标签数组与训练数据行数匹配:
def main(): file = "audio.mp3" data = file_retreival(file) X_train = create_training_data(data) # 标签维度与训练数据行数一致 y_train = np.zeros(X_train.shape[0]) model = build_model(X_train, y_train) music_pred = generate_music(model, X_train) create_music(music_pred, data['sample_rate'], tempo, rhythm, tone)
完整修正代码
# 导入必要的库 import pandas as pd import numpy as np import matplotlib.pyplot as plt import sklearn import librosa import mido # 获取用户输入 tempo = int(input("你希望歌曲的速度是多少?(BPM)")) rhythm = input("你希望歌曲的节奏是怎样的?(例如4/4)") tone = input("歌曲的‘感觉’或基调是什么?(例如欢快、忧郁)") # 文件读取函数 def file_retreival(file): audio, sample_rate = librosa.load(file) results = { "sample_rate": sample_rate, "tempo": librosa.beat.tempo(audio, sample_rate)[0], "energy": librosa.feature.rms(audio)[0], "chroma_stft": librosa.feature.chroma_stft(audio).T, "spectral_contrast": librosa.feature.spectral_contrast(audio).T, "tonnetz": librosa.feature.tonnetz(y=librosa.effects.harmonic(audio)).T } return results # 创建训练数据函数 def create_training_data(data): time_steps = len(data['energy']) feature_num = 1 + data['chroma_stft'].shape[1] + data['spectral_contrast'].shape[1] + data['tonnetz'].shape[1] +1 X = np.zeros((time_steps, feature_num)) for i in range(time_steps): X[i][0] = data['energy'][i] X[i][1:13] = data['chroma_stft'][i] X[i][13:20] = data['spectral_contrast'][i] X[i][20:26] = data['tonnetz'][i] X[i][26] = data['tempo'] return X # 构建模型函数 def build_model(X_train, y_train): from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(n_estimators=100) model.fit(X_train, y_train) return model # 生成音乐函数(示例:输出音符和时长) def generate_music(model, X): # 示例:生成60-80范围的音符,0.5-2秒的时长 pred_notes = np.random.randint(60,80, size=X.shape[0]) pred_durations = np.random.uniform(0.5,2, size=X.shape[0]) return np.column_stack((pred_notes, pred_durations)) # 创建音乐文件函数 def create_music(pred, sample_rate, tempo, rhythm, tone): mid = MidiFile() track = MidiTrack() mid.tracks.append(track) mido_tempo = mido.bpm2tempo(tempo) track.append(Message("set_tempo", tempo=mido_tempo)) numerator, denominator = map(int, rhythm.split('/')) track.append(Message("time_signature", numerator=numerator, denominator=denominator)) for note_val, duration in pred: track.append(Message("note_on", note=int(note_val), velocity=127, time=0)) track.append(Message("note_off", note=int(note_val), velocity=127, time=int(duration*1000))) track.append(Message("control_change", control=0, value=2)) mid.save("generated_music.mid") # 主函数 def main(): file = "audio.mp3" data = file_retreival(file) X_train = create_training_data(data) y_train = np.zeros(X_train.shape[0]) model = build_model(X_train, y_train) music_pred = generate_music(model, X_train) create_music(music_pred, data['sample_rate'], tempo, rhythm, tone) main()
内容的提问来源于stack exchange,提问作者OdinX
相关产品推荐
相关产品推荐

