十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于音频特征与机器学习的歌曲力量感量化实战

基于音频特征与机器学习的歌曲力量感量化实战 最近在开发音乐推荐系统时经常遇到一个需求如何从海量歌曲中快速、准确地筛选出那些听起来“有劲儿”、“带感”的歌曲也就是用户常说的“这歌有力气”。无论是为健身应用生成动感歌单还是为短视频平台匹配高能卡点BGM这个需求都非常普遍。然而单纯依赖歌曲的“能量值Energy”或“响度Loudness”等音频特征有时并不能完全捕捉到人耳感知的那种“力量感”和“冲击力”。本文将深入探讨如何从技术层面定义和量化一首歌的“力气”并提供一个完整的实战方案。我们将从音频特征分析入手结合音乐心理学和信号处理知识构建一个多维度的“歌曲力量感”评估模型。文章会包含完整的Python代码示例、Librosa库的使用、特征工程流程以及一个简单的机器学习分类器实现。无论你是音乐信息检索MIR的初学者还是希望优化推荐算法的开发者都能从中获得可直接复用的思路和代码。1. 背景与核心概念什么是歌曲的“力气”在音乐信息检索和音频分析领域“力气”或“力量感”是一个复合的主观感知属性它并非一个标准的学术术语但可以通过一系列可量化的音频特征来近似描述。通俗理解当我们说“这歌有力气”时通常指的是歌曲能带来强烈的节奏冲击、饱满的音色、激昂的情绪或高涨的能量。这种感觉可能来源于强劲且稳定的节拍如摇滚乐中的鼓点、电子音乐中的Drop部分。丰富的频谱与高音量乐器密集中低频如贝斯、底鼓饱满整体响度大。快速的音乐进行高 tempo速度音符密集。和声的紧张度与释放使用强力和弦、不和谐音程的解决等。人声的力度嘶吼、有力的唱腔。技术定义我们可以将“力气”建模为多个底层音频特征的加权组合。这些特征通常包括节奏特征节拍强度Onset Strength、节奏Tempo、节拍位置Beat。能量特征均方根能量RMS Energy、频谱通量Spectral Flux。频谱特征频谱质心Spectral Centroid、频谱带宽Spectral Bandwidth、频谱滚降点Spectral Rolloff。音色特征梅尔频率倒谱系数MFCCs的统计量。动态特征响度Loudness通常使用 ITU-R BS.1770 标准估算。与相关概念的区别能量Energy vs 力气Perceived Force能量是物理量信号振幅的平方而“力气”是感知量包含节奏型、音色等因素。一首歌可能平均能量不高但通过切分节奏和音色对比依然让人觉得“有劲”。响度Loudness vs 力气响度是人耳对声音强度的主观感受。虽然高响度常伴随力量感但经过压缩处理的“大声”歌曲可能缺乏动态冲击力反而显得“平”。真正的“力气”往往需要动态对比如弱起突然变强。本文的目标就是教会你如何提取这些特征并通过一个数据驱动的模型将主观的“力气”评价转化为客观的、可计算的分数。2. 环境准备与版本说明为了完成本教程的实战部分你需要准备以下开发环境。本文示例代码主要基于 Python 生态中强大的音频处理库librosa。操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu 20.04) 均可。本文命令以 Linux/macOS 的 bash 为例Windows 用户可在 PowerShell 或 WSL 中运行。Python 环境建议使用 Python 3.8 或以上版本。我们将使用venv或conda创建独立的虚拟环境。核心库及版本librosa0.10.1核心音频分析库。numpy1.24.3数值计算基础。scikit-learn1.3.0用于特征标准化和简单建模。pandas2.0.3用于数据处理和特征表格管理。matplotlib3.7.2用于可视化可选。soundfile0.12.1或pydub0.25.1用于音频文件读取librosa 背后依赖。安装命令 在你的项目目录下创建并激活虚拟环境后使用 pip 安装# 创建虚拟环境 (可选) python -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows # 安装核心库 pip install librosa0.10.1 numpy1.24.3 scikit-learn1.3.0 pandas2.0.3 matplotlib3.7.2 soundfile0.12.1IDE 或编辑器任何你熟悉的即可如 VS Code, PyCharm, Jupyter Notebook。本文代码将以.py脚本形式呈现也兼容 Notebook。示例项目结构 建议按以下结构组织你的项目便于管理代码、数据和模型。song_power_analysis/ ├── audio_samples/ # 存放用于分析的音频文件 (如 .mp3, .wav) │ ├── powerful_song_1.mp3 │ └── calm_song_1.wav ├── features/ # 存放提取的特征文件 (如 .csv, .pkl) ├── models/ # 存放训练好的模型 ├── utils/ # 工具函数 │ └── feature_extractor.py ├── config.yaml # 配置文件 (可选) ├── train_model.py # 训练脚本 ├── predict_power.py # 预测脚本 └── requirements.txt # 依赖列表版本兼容性说明librosa的 API 在不同小版本间可能略有变化但核心功能稳定。本文代码基于0.10.1编写。如果你使用其他版本遇到函数参数错误请查阅对应版本的官方文档。scikit-learn的StandardScaler和SVM等接口非常稳定一般不会有问题。3. 核心原理与特征工程拆解本节将详细拆解用于评估“歌曲力气”的核心音频特征并解释其物理意义和感知关联。3.1 节奏与节拍特征节奏是力量感最直接的来源。一个明确、强劲的节拍能立刻抓住听众。节拍强度Onset Strength检测音乐中音符或事件开始的瞬间强度。计算方法是频谱通量相邻帧频谱差值的范数。librosa.onset.onset_strength函数可以计算整个音频的onset强度包络。高且密集的onset峰值通常对应有力的打击乐或断奏。节奏TempoBPM每分钟节拍数。通常较快的 tempo如 120-140 BPM 的电子舞曲比慢速歌曲如 60 BPM 的民谣更容易让人感到有活力。librosa.beat.tempo可以估算全局 tempo。节拍帧Beat Frames检测到的每个节拍点的具体时间位置。节拍的规律性和强度稳定性也是力量感的体现。librosa.beat.beat_track可以同时获取 tempo 和 beat frames。import librosa import numpy as np # 加载音频文件 audio_path audio_samples/rock_song.mp3 y, sr librosa.load(audio_path, sr22050) # sr为采样率22050是常用值 # 1. 计算节拍强度包络 onset_env librosa.onset.onset_strength(yy, srsr) # onset_env 是一个一维数组表示每个时间帧的onset强度 # 2. 估算全局节奏 (BPM) 和节拍位置 tempo, beat_frames librosa.beat.beat_track(yy, srsr, onset_envelopeonset_env) print(f估算节奏: {tempo[0]:.2f} BPM) print(f检测到节拍帧数: {len(beat_frames)}) # 3. 将节拍帧转换为时间点秒 beat_times librosa.frames_to_time(beat_frames, srsr)3.2 能量与动态特征能量直接反映了声音信号的振幅大小。均方根能量RMS Energy信号振幅的平方的均值的平方根是衡量音频片段平均能量的经典指标。librosa.feature.rms可以计算每一帧的 RMS。频谱通量Spectral Flux相邻短时傅里叶变换STFT频谱之间的差异的范数。它反映了频谱变化的剧烈程度在鼓点、和弦变化时会出现峰值是“冲击力”的重要指标。我们可以通过计算 onset strength 前的差分来近似或直接计算。响度Loudness符合人耳感知的能量。librosa.feature.rms计算的是物理能量而响度需要考虑心理声学模型如 ITU-R BS.1770。pyloudnorm库可以更专业地计算响度LUFS但librosa的 RMS 在大多数情况下是一个有效的代理特征。# 计算帧级的RMS能量 rms librosa.feature.rms(yy) # rms 是一个二维数组 (1, n_frames)我们取第一行 rms_energy rms[0] # 计算RMS能量的统计特征作为歌曲级别的描述 mean_rms np.mean(rms_energy) max_rms np.max(rms_energy) std_rms np.std(rms_energy) # 动态范围标准差大可能意味着对比强烈 print(f平均RMS能量: {mean_rms:.4f}) print(f最大RMS能量: {max_rms:.4f}) print(fRMS能量标准差: {std_rms:.4f})3.3 频谱与音色特征频谱形状决定了音色而某些音色如失真的电吉他、厚重的合成器贝斯本身就与力量感强相关。频谱质心Spectral Centroid频谱的“重心”频率单位通常是 Hz。较高的频谱质心通常与明亮、尖锐、有穿透力的声音相关这可能是高音吉他solo或镲片的声音但不一定是“力气”的核心。有时力量感来源于中低频。频谱带宽Spectral Bandwidth频谱围绕质心的分散程度。带宽大可能意味着音色复杂、饱满。频谱滚降点Spectral Rolloff低于该频率的频谱能量占总能量特定比例如85%的频率点。可以粗略区分“低沉”和“明亮”。梅尔频率倒谱系数MFCCs这是描述音色最核心的特征集之一。它模拟了人耳的听觉特性前几个系数代表了频谱包络音色后面的系数代表了细节。我们通常取前13个MFCCs并计算它们的均值、方差等统计量作为特征。# 计算频谱质心 spectral_centroids librosa.feature.spectral_centroid(yy, srsr)[0] mean_centroid np.mean(spectral_centroids) # 计算频谱滚降点 (85%) rolloff librosa.feature.spectral_rolloff(yy, srsr, roll_percent0.85)[0] mean_rolloff np.mean(rolloff) # 计算MFCCs (取前13个) mfccs librosa.feature.mfcc(yy, srsr, n_mfcc13) # 计算每个MFCC系数的均值和标准差作为歌曲级特征 mfccs_mean np.mean(mfccs, axis1) mfccs_std np.std(mfccs, axis1) print(f平均频谱质心: {mean_centroid:.2f} Hz) print(f平均频谱滚降点(85%): {mean_rolloff:.2f} Hz) print(f前5个MFCC系数均值: {mfccs_mean[:5]})3.4 特征聚合与向量构建上面提取的都是帧级特征时间序列。为了用一首歌的一个特征向量来代表它我们需要进行聚合统计。常用的统计量包括均值平均强度、标准差变化程度、偏度分布不对称性、峰度分布尖锐度、最大值、最小值、以及不同分位数如25%75%。我们将为每个底层特征如RMS、频谱质心、每个MFCC系数计算一组统计量然后拼接成一个长向量这就是我们用于机器学习模型的特征向量。4. 完整实战构建“歌曲力气”评估模型现在我们将把上述理论付诸实践构建一个完整的流程从音频文件读取到特征提取、数据标注、模型训练最后对新歌曲进行预测。4.1 项目结构与工具函数首先我们创建一个工具函数模块utils/feature_extractor.py封装特征提取逻辑。# utils/feature_extractor.py import librosa import numpy as np import pandas as pd def extract_features(file_path, sr22050, hop_length512): 从单个音频文件中提取聚合特征。 返回一个字典键为特征名值为标量。 y, sr librosa.load(file_path, srsr) features {} # 1. 基本特征 duration librosa.get_duration(yy, srsr) features[duration] duration # 2. 节奏特征 onset_env librosa.onset.onset_strength(yy, srsr) tempo, _ librosa.beat.beat_track(onset_envelopeonset_env, srsr) features[tempo] tempo[0] if len(tempo) 0 else 0 # 3. 能量特征 rms librosa.feature.rms(yy)[0] features[rms_mean] np.mean(rms) features[rms_std] np.std(rms) features[rms_max] np.max(rms) # 4. 频谱特征 spectral_centroids librosa.feature.spectral_centroid(yy, srsr)[0] features[centroid_mean] np.mean(spectral_centroids) features[centroid_std] np.std(spectral_centroids) spectral_bandwidth librosa.feature.spectral_bandwidth(yy, srsr)[0] features[bandwidth_mean] np.mean(spectral_bandwidth) spectral_rolloff librosa.feature.spectral_rolloff(yy, srsr)[0] features[rolloff_mean] np.mean(spectral_rolloff) # 5. MFCC特征 (取前13个系数计算均值和标准差) mfccs librosa.feature.mfcc(yy, srsr, n_mfcc13) for i in range(13): features[fmfcc{i1}_mean] np.mean(mfccs[i]) features[fmfcc{i1}_std] np.std(mfccs[i]) # 6. 色度特征 (可选表示和声信息) chroma librosa.feature.chroma_stft(yy, srsr) chroma_mean np.mean(chroma, axis1) for i in range(12): features[fchroma_c{i}_mean] chroma_mean[i] # 7. 零交叉率 (粗略表征音调) zcr librosa.feature.zero_crossing_rate(y)[0] features[zcr_mean] np.mean(zcr) features[zcr_std] np.std(zcr) return features def create_feature_dataset(audio_dir, label_csvNone): 遍历音频目录为每个文件提取特征并可选地合并标签。 audio_dir: 存放音频文件的文件夹路径 label_csv: 可选的CSV文件路径包含filename和label列 import os audio_files [f for f in os.listdir(audio_dir) if f.endswith((.mp3, .wav, .flac))] all_features [] filenames [] for audio_file in audio_files: file_path os.path.join(audio_dir, audio_file) print(fProcessing: {audio_file}) try: feat_dict extract_features(file_path) all_features.append(feat_dict) filenames.append(audio_file) except Exception as e: print(fError processing {audio_file}: {e}) # 创建特征DataFrame df_features pd.DataFrame(all_features) df_features.insert(0, filename, filenames) # 合并标签 (如果有) if label_csv and os.path.exists(label_csv): df_labels pd.read_csv(label_csv) # 假设label_csv有filename和power_label列 (1表示有力气0表示无力气) df_merged pd.merge(df_features, df_labels, onfilename, howleft) return df_merged else: return df_features4.2 数据准备与标注机器学习需要带标签的数据。由于“力气”是主观的我们可以通过以下方式获取标签人工标注邀请多人听一批歌曲对“力量感”打分如1-5分取平均分或中位数。这是最可靠但最耗时的方法。利用现有标签某些音乐数据集可能有“能量energy”、“舞蹈性danceability”等标签可以近似使用。启发式规则用我们之前讨论的特征高 tempo、高 RMS、强 onset设定阈值自动生成伪标签用于初步模型验证。假设我们有一个labels.csv文件内容如下filename,power_label powerful_song_1.mp3,1 powerful_song_2.mp3,1 calm_song_1.wav,0 calm_song_2.wav,0 ...4.3 模型训练脚本接下来我们创建训练脚本train_model.py。我们将使用一个简单的逻辑回归Logistic Regression模型作为起点因为它易于解释且能告诉我们哪些特征对“力气”的贡献大。# train_model.py import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, confusion_matrix, accuracy_score import joblib # 用于保存模型和Scaler import os from utils.feature_extractor import create_feature_dataset def main(): # 配置路径 AUDIO_DIR audio_samples/ LABEL_CSV labels.csv MODEL_SAVE_PATH models/power_classifier.pkl SCALER_SAVE_PATH models/scaler.pkl # 1. 提取特征并合并标签 print(正在提取音频特征...) df create_feature_dataset(AUDIO_DIR, LABEL_CSV) # 检查是否有缺失标签 if power_label not in df.columns: print(错误未找到 power_label 列。请确保 labels.csv 格式正确。) return df df.dropna(subset[power_label]) # 2. 准备特征矩阵X和标签y # 移除非特征列 non_feature_cols [filename, power_label] feature_cols [col for col in df.columns if col not in non_feature_cols] X df[feature_cols].values y df[power_label].values print(f特征数量: {len(feature_cols)}) print(f样本数量: {len(X)}) # 3. 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 4. 特征标准化 (非常重要) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 5. 训练逻辑回归模型 print(训练逻辑回归模型...) model LogisticRegression(random_state42, max_iter1000) model.fit(X_train_scaled, y_train) # 6. 在测试集上评估 y_pred model.predict(X_test_scaled) accuracy accuracy_score(y_test, y_pred) print(f\n测试集准确率: {accuracy:.4f}) print(\n分类报告:) print(classification_report(y_test, y_pred, target_names[无力气, 有力气])) print(\n混淆矩阵:) print(confusion_matrix(y_test, y_pred)) # 7. 查看特征重要性 (逻辑回归的系数绝对值) feature_importance pd.DataFrame({ feature: feature_cols, coefficient: model.coef_[0] }) feature_importance[abs_coef] np.abs(feature_importance[coefficient]) feature_importance feature_importance.sort_values(abs_coef, ascendingFalse) print(\n特征重要性 (按系数绝对值排序):) print(feature_importance.head(15)) # 8. 保存模型和标准化器 os.makedirs(models, exist_okTrue) joblib.dump(model, MODEL_SAVE_PATH) joblib.dump(scaler, SCALER_SAVE_PATH) print(f\n模型已保存至: {MODEL_SAVE_PATH}) print(f标准化器已保存至: {SCALER_SAVE_PATH}) # 9. (可选) 保存处理后的特征数据集供后续分析 df.to_csv(features/audio_features_with_labels.csv, indexFalse) print(特征数据集已保存。) if __name__ __main__: main()4.4 运行与验证准备数据在audio_samples/文件夹中放入一批.mp3或.wav歌曲文件。同时根据你的标注创建对应的labels.csv文件。运行训练在项目根目录下执行命令。python train_model.py查看输出脚本会输出特征数量、样本数量、测试集准确率、分类报告精确率、召回率、F1-score、混淆矩阵以及最重要的特征列表。例如你可能会发现rms_mean、tempo、mfcc1_std、onset_strength_mean等特征的系数绝对值很大这验证了我们的理论。4.5 对新歌曲进行预测训练好模型后我们可以编写一个预测脚本predict_power.py对新的、未标注的歌曲进行“力气”评分。# predict_power.py import librosa import numpy as np import joblib from utils.feature_extractor import extract_features def predict_song_power(audio_path, model_pathmodels/power_classifier.pkl, scaler_pathmodels/scaler.pkl): 预测单首歌曲的‘力气’标签和概率。 # 1. 加载模型和标准化器 model joblib.load(model_path) scaler joblib.load(scaler_path) # 2. 提取特征 (使用与训练时相同的函数) feat_dict extract_features(audio_path) # 注意extract_features返回字典我们需要按训练时的顺序转换为向量 # 我们需要知道训练时用了哪些特征。这里假设我们保存了特征列名。 # 更稳健的做法是保存一个特征列名的列表。 # 为了简化我们这里重新加载训练时的特征DataFrame的列不包括filename和label try: import pandas as pd df_train pd.read_csv(features/audio_features_with_labels.csv) non_feature_cols [filename, power_label] feature_cols [col for col in df_train.columns if col not in non_feature_cols] except FileNotFoundError: # 如果文件不存在则使用一个默认的特征顺序必须与训练时完全一致 # 这是一个潜在的风险点。最佳实践是训练时保存 feature_cols 列表。 print(警告未找到特征列文件使用默认特征顺序。可能不匹配) # 这里需要你根据实际训练的特征手动列出例如 feature_cols [duration, tempo, rms_mean, rms_std, ...] # 必须完整 # 3. 根据特征列名从字典中构建特征向量 X_new np.array([feat_dict[col] for col in feature_cols]).reshape(1, -1) # 4. 标准化 X_new_scaled scaler.transform(X_new) # 5. 预测 prediction model.predict(X_new_scaled)[0] # 0 或 1 prediction_proba model.predict_proba(X_new_scaled)[0] # 属于各类的概率 # 6. 输出结果 power_label 有力气 if prediction 1 else 无力气 confidence prediction_proba[1] if prediction 1 else prediction_proba[0] print(f歌曲: {audio_path}) print(f预测结果: {power_label}) print(f预测概率: [无力气: {prediction_proba[0]:.3f}, 有力气: {prediction_proba[1]:.3f}]) print(f置信度: {confidence:.3f}) # 返回预测标签和概率便于集成到其他系统 return prediction, prediction_proba if __name__ __main__: # 示例预测一首新歌 new_song_path new_song_to_analyze.mp3 predict_song_power(new_song_path)运行预测脚本python predict_power.py5. 常见问题与排查思路在实际操作中你可能会遇到以下问题问题现象常见原因解决思路librosa.load报错NoBackendError缺少音频解码后端如 ffmpeg。安装ffmpeg。Ubuntu:sudo apt install ffmpegmacOS:brew install ffmpegWindows: 从官网下载并添加至 PATH。或安装audioread库pip install audioread。特征提取非常慢音频文件太长采样率太高。1. 考虑截取歌曲片段如前30秒进行分析。2. 降低librosa.load的sr参数如 22050 Hz 已足够。3. 使用librosa.effects.trim先静音修剪。模型准确率很低60%1. 数据量太少。2. 标签质量差主观性强。3. 特征不足以区分。4. 正负样本不均衡。1. 增加标注数据量至少每类50-100首。2. 多人标注取平均或使用更客观的代理标签如从音乐平台API获取“能量”分数。3. 尝试更多特征如librosa.feature.spectral_contrast,tonnetz。4. 对样本进行过采样/欠采样或使用class_weightbalanced参数。预测时特征维度不匹配训练和预测时提取的特征列顺序或数量不一致。这是关键训练时务必保存feature_cols列表joblib.dump预测时加载该列表来构建特征向量。修改predict_power.py从训练脚本保存的feature_cols.pkl中加载列名。tempo检测为0或异常歌曲节奏不明确如环境音乐、自由节奏。1. 检查音频是否静音或损坏。2. 使用librosa.beat.plp替代beat_track可能更鲁棒。3. 考虑不使用 tempo或用一个默认值填充并增加一个has_clear_beat的布尔特征。内存不足一次性加载太多音频文件或特征矩阵太大。1. 使用生成器或分批处理。2. 使用librosa.load时设置duration参数只加载一部分。3. 使用numpy.memmap或scipy.sparse矩阵处理超大特征集。6. 最佳实践与工程建议将“歌曲力气”分析投入生产环境或严肃项目时需要考虑以下工程化问题特征标准化与版本控制固定特征管道确保训练和推理时特征提取的每一步采样率、帧长、hop_length、特征列表、统计量完全一致。将特征提取代码封装成类并保存其配置。保存Scaler和特征列如之前所述必须保存用于标准化的StandardScaler对象和特征列名列表。这是模型可复现性的关键。数据质量与标注黄金标准数据集如果项目重要投入资源创建高质量的人工标注数据集。可以采用多轮标注、剔除分歧大的样本等方式提高标签一致性。数据增强对于音频数据可以对原始音频进行轻微的音调变换、时间拉伸、添加背景噪声等来扩充数据集提高模型鲁棒性。模型选择与优化从简单开始逻辑回归或线性SVM作为基线模型因其可解释性强。尝试复杂模型如果数据量足够可以尝试随机森林、梯度提升树如XGBoost、LightGBM或简单的神经网络。这些模型可能捕捉更复杂的特征交互。模型解释使用SHAP或LIME等工具解释模型预测这不仅能验证特征是否符合认知如高RMS导致“有力气”还能发现意想不到的关联。性能与部署预处理优化对于需要实时或批量处理大量歌曲的场景特征提取是瓶颈。考虑使用更高效的库如essentia或将提取好的特征存入数据库或特征仓库。API化将预测功能封装成REST API使用FastAPI或Flask方便其他服务如推荐系统调用。监控与更新监控模型在生产环境的表现。如果音乐风格流行趋势变化模型可能退化需要定期用新数据重新训练或微调。超越二分类回归问题如果标注是1-5的分数可以将其视为回归问题预测分数使用如SVR、随机森林回归等模型。多维度评分“力气”可能包含多个子维度节奏力量、音色力量、动态力量。可以训练多个模型分别预测或使用多输出模型。合法与伦理版权确保你用于分析和训练的音频数据拥有相应的使用权尤其是在商业项目中。偏见注意训练数据可能存在的风格偏见如金属摇滚都被标为“有力气”而古典乐被标为“无力气”。这可能导致模型对特定音乐类型产生不公平的预测。尽量使用风格均衡的数据集。通过本教程你不仅学会了如何用技术手段量化一首歌的“力气”更掌握了一套完整的音频特征提取、机器学习建模和工程部署的流程。这个框架可以轻松扩展到其他音乐属性分析上如“悲伤度”、“舞蹈性”、“乐器复杂度”等。音乐与AI的结合充满乐趣希望你能用这些工具创造出更智能的音乐应用。如果在实践中遇到任何问题欢迎在评论区交流探讨。
返回列表