简介中文歌声数据集提供一份已标注好的完整数据包面向中文语音处理、歌声合成与音乐信息检索等场景包含20段中文歌曲的wav录音、对应的TextGrid歌词时间对齐标注以及MIDI乐谱数据另附readme说明文档。wav文件保留原始声学波形可直接用于音高追踪、情感识别或歌声合成模型训练TextGrid精确标注了每个汉字在音频中的起止时间方便歌词同步、语音转写等任务MIDI提供音符、节奏与调性信息适合旋律分析和自动伴奏生成。资源共68个文件除三组20对文件外还包含XML工程配置等整体压缩包约463MB目录结构清晰数据格式规范便于直接开展实验。目前已有2164人学习下载对于需要高质量标注歌声数据的研究者和开发者来说能显著降低数据准备成本快速用于模型验证与应用落地。 做歌声合成和歌声转换的人应该都有同感模型架构、损失函数、推理加速这些问题网上教程一抓一大把真正卡住大多数人的反而是最底层的数据。尤其是中文歌声你要找一份带标注的资源难度完全不亚于自己录一堆干声慢慢抠。最近我一直在折腾一套中文歌声数据集已经标注好文件形式就是标题里这三种——wav、textgrid、midis。这套组合看起来简单但它恰恰把歌声合成最需要的三样东西全照顾到了干净的音频、字级别的发音对齐、音符级别的旋律信息。这篇就把它的结构、标注逻辑和实际使用经验都拆开讲讲适合正在做singing voice synthesis、SVS、歌声转换或者音高预测模型的读者。1. 三件套wav、textgrid、midi 到底各自承担什么角色1.1 为什么是 wav 而不是 mp3 或 m4a很多刚接触音频数据集的人会问wav 体积那么大一首歌动不动几十MB换成 mp3 或 m4a 能省不少空间为什么数据集非要抱着 wav 不放核心原因很简单歌声合成任务里任何一次有损压缩都会在频域上留下痕迹。mp3 在 16kHz 以上会直接砍掉一部分高频m4aAAC虽然比 mp3 聪明一点但它的编码噪声在安静段和尾音衰减段依然听得出。训练声学模型时这些噪声会被当成“真实演唱特征”学进去最后合成出来的声音高频发闷、气声发糊怎么调后处理都救不回来。格式压缩方式是否适合做训练数据典型场景wav无损PCM非常适合保留完整频谱数据集存储、模型训练flac无损压缩适合但部分工具链要转码备份、存档mp3有损不适合试听、分享m4a/aac有损不适合苹果生态、流媒体另外采样率也要留意。这套数据集的 wav 如果是 44.1kHz 或 48kHz 的 16bit PCM算是标准配置。44.1kHz 是音乐行业的老规矩48kHz 更贴合视频和部分 vocoder 的默认设置。拿到手先别急着改看清楚再决定是否重采样。1.2 textgrid把“唱了什么字”变成机器能读的时间轴wav 只是一段波形机器不知道人在什么时候唱了哪个字。textgrid 就是干这个的——它是 Praat 的标注文件格式本质是个纯文本用层级tier把时间轴切成一段一段每一段标上对应的内容。这套数据集里textgrid 的价值在于它不只是一个词级或句级的粗对齐而是做到了音素级。对中文来说音素粒度一般会拆到声母、韵母比如“花”这个字会分成hh ua两个音素。为什么必须细到这个程度因为歌声合成模型要学的不是“某个字对应一段音频”而是“某个声母/韵母在某个音高下的过渡形态”。音素边界标得准不准直接决定模型能不能学到字与字之间的自然衔接尤其是中文里非常典型的辅音爆破、韵母拖腔。textgrid 里通常还会有多层。常见的是四层词层、音节层、音素层、备注层。词层管语义音节层管中文的“一字一音”单位音素层管最细的发音单元备注层记录语气、换气、滑音这些特殊情况。用的时候按需取层就行不一定每层都用。1.3 midi把“怎么唱”变成音符和节拍textgrid 解决了“唱了什么字”midi 解决的则是“音有多高、时值多长”。歌声和说话最大的区别就在于旋律同一个“啊”字你唱 C3 和唱 C5声带振动方式完全不同模型需要知道这一句里每个音对应的音高轨迹。这套数据集里的 midi 文件每条 note 基本对应一个汉字或一个长音。midi 的好处是跟 textgrid 天然互补textgrid 记录的是秒级时间轴midi 记录的是小节、拍、音符号和力度。把两者放在一起你可以精确地对上“第 42 个字是从第 2 秒第 300 毫秒开始音高是 F4持续了 0.8 秒”。这里要特别提一下中文演唱的特殊性中文讲究字正腔圆一字多音装饰音、滑音非常常见。midi 里如果只标了主音高忽略了滑音模型训练出来会非常呆板。所以拿到 midi 后先看看是不是只保留了主干音符如果有多轨或额外控制信息别急着丢。2. “已经标注好”这几个字省下的是按周计算的时间2.1 训练歌声模型真正的门槛不是模型而是数据现在开源的 SVS 模型不少很多人的做法是把模型跑通然后卡在“没有自己的数据”这个环节。自己录干声要处理房间混响和麦克风底噪找人买数据又常常买到只有 wav 和歌词文本的半成品。这时你才发现模型训练需要的是一个文件三件套音频、发音时间对齐、音符信息。没有 textgrid你没法做帧级标签没有 midi你没法做音高条件输入。两个缺一个模型要么学不到字音和时间的关系要么学不到旋律和字音的关系最后合成出来的东西不是“唱得不准”而是“根本不像在唱”。所以“已经标注好”这套数据真正值钱的是它的对齐结果。你拿到手可以直接用它跑基线模型不用先花几周做数据预处理。2.2 亲自标过一次音素对齐才知道这项工作有多折腾我在早期自己做过一个几十首的小数据集用的方法是在 Praat 里建 textgrid然后对着频谱图和听力手动拖边界。一首三分钟的歌包含几百个音素纯手动标注大概要三到五个小时这还是建立在语速正常、发音清晰的前提下。遇到歌手吞字、换气声、尾音转音一个边界可能要反复听十几遍才能定下来。后来我也试过用自动对齐工具先跑一遍再手动修正速度快了不少但自动对齐对歌声的处理明显不如语音成熟。背景音乐一旦和人身混在一起对齐工具经常把音乐里的打击乐也当成语音段该拆分的地方不拆不该拆的地方乱拆修正起来比全手动还累。这个数据集既然标注好了等于帮你把这几百个小时的重复劳动省掉了。2.3 textgrid 和 midi 的三角关系歌词、音素、音符怎么对上最需要注意的一点是textgrid 和 midi 不是两份独立文件它们之间存在对应关系。大多数情况下midi 里的一个 note 对应 textgrid 里的一个音节而不是一个音素。也就是说拿到数据后要先按时间轴做一次关联检查确认 midi 的起始时间和 textgrid 的音节层起始时间是否落在合理范围内。我自己常用的做法是以 textgrid 音节层为基准把 midi note 的起止时间转成秒和音节层做最近邻匹配。如果两个时间差超过 150 毫秒就要人工看一眼可能是标注不一致也可能是这首歌本身有前奏音符或装饰音。这个检查听起来费时间但它能避免训练时出现“音频是唱这个字条件却是另一个音节”的错位问题。3. 从零产出一份中文歌声数据集的基本路径常见实践的补充如果你手头没有这套现成数据集或者想自己扩充数据下面这条路径是圈里比较通用的做法可以作为参考。3.1 选曲与录音音域、咬字和底噪都要权衡选曲的时候不要只挑自己喜欢的歌优先考虑音域不要太宽最好集中在歌手舒适区、咬字清楚、每句之间有自然换气、没有大量说唱段落。歌手录音时保持固定距离尽量在安静房间录后期再统一做轻量降噪和响度归一化。比较理想的情况下每首歌形成一个 wav 一个 textgrid 一个 midi 的三件套。文件名最好用统一编号比如song_001.wav、song_001.TextGrid、song_001.mid这样后面做数据管线时省很多事。3.2 标注流程Praat 里逐字逐音素对齐的真实操作标注的核心流程是先建音节层把整首歌按歌词分成一个字一个字的时间段再在音节层基础上切音素层把每个音节拆成声母和韵母最后用备注层记录换气、破音、滑音等特殊事件。过程中最实用的技巧是开宽带语谱图看频谱能量突变的位置。声母和韵母之间通常有声纹断裂韵母段有明显的共振峰横杠换气段在语谱图上会显示为一段较宽的静默区。手动拖边界时把边界放在波形过零点的位置听起来会更自然。3.3 midi 生成自动提取只是起点人工校订才算标好很多人以为 midi 可以用算法自动提取提取完直接就能用。实际上自动提取在清唱数据上效果尚可但在带伴奏数据上经常出现八度错误和音符断裂。拿到自动提取结果后至少要做两件事第一把 midi note 和实际演唱的音高逐句比对听到有出入就手动调整第二检查音符时值歌手唱的装饰音、滑音和换气前后的尾音算法经常处理不好。这个环节的工作量不比标注 textgrid 少但它是 midi 文件“可用”和“好用”的分界线。4. 拿到手之后的第一套工具链读取、转换、喂给模型4.1 用 Praat/Parselmouth 读取 textgrid拿到 textgrid 后最常见的操作是提取音素层的时间边界和标签。如果你习惯用命令行和 Python推荐用 Parselmouth它是 Praat 的 Python 接口代码很简单import parselmouth from parselmouth.praat import call tg parselmouth.Data.read(song_001.TextGrid) num_tiers call(tg, Get number of tiers) for tier_idx in range(1, num_tiers 1): name call(tg, Get tier name, tier_idx) print(tier:, tier_idx, name)如果想拿到某个 tier 里所有音素的时间段和标签tier_idx 1 # 按需调整 num_intervals call(tg, Get number of intervals, tier_idx) for i in range(1, num_intervals 1): start call(tg, Get start point, tier_idx, i) end call(tg, Get end point, tier_idx, i) label call(tg, Get label of interval, tier_idx, i) if label: print(start, end, label)4.2 把 midi 转换成训练所需的帧级音高midi 是事件序列训练模型时通常需要逐帧的 F0 标签。用 Python 的 mido 或 pretty_midi 都能读取import pretty_midi pm pretty_midi.PrettyMIDI(song_001.mid) note_list [] for inst in pm.instruments: for note in inst.notes: note_list.append((note.start, note.end, note.pitch)) # 转成 10ms 步长的帧级音高序列 def midi_pitch_to_f0(midi_pitch): return 440.0 * (2 ** ((midi_pitch - 69) / 12)) note_list.sort() frame_len 0.01 f0_seq [] for t in range(int(pm.get_end_time() / frame_len)): tt t * frame_len hit [n[2] for n in note_list if n[0] tt n[1]] f0_seq.append(midi_pitch_to_f0(hit[0]) if hit else 0)这样得到的 F0 序列后面可以直接叠加上说话人的基频抖动作为歌声模型的辅助条件输入。midi 的每个音符其实对应的是平滑的阶跃音高真实演唱会有微小的频率波动这个差异建模时需要额外处理。4.3 数据管线里的格式转换与批量校验训练管线里最容易被忽略的是格式一致性。我习惯先写一个批量脚本检查三件事所有 wav 采样率是否一致、声道数是否为单声道、textgrid 里是否所有 interval 都有非空 label。采样率不一致会导致模型把不同音频当成不同时长来对齐声道不一致会让后续特征提取出错空 label 会让序列模型在时序对齐时出现空洞。另外注意有的 textgrid 文件保存的是“间隔序列”有的直接存“点序列”用脚本读取时要去查 tier 类型。两者的时间点含义不同混着用会出现边界错位这是很隐蔽的坑。import wave, glob for wav_path in glob.glob(wavs/*.wav): with wave.open(wav_path, rb) as wf: sr wf.getframerate() ch wf.getnchannels() print(wav_path, sr, ch)5. 实测中遇到的坑以及我的处理方式5.1 标注风格不统一是数据清洗里最头疼的问题这套数据集整体标注质量不错但不同文件之间还是能看到标注者风格的细微差异。有人喜欢把“zhi”标成完整音节有人拆成zh i两个音素有人把换气单独分成一段有人把换气塞进前一个字。这些差异不致命但如果你直接拿全部数据训练模型会在换气和声母边界上学到互相矛盾的模式。我的处理方式是做一次“标注风格归一化”。先扫一遍所有 textgrid统计音素集合把明显属于同一发音但写法不同的标签统一映射到同一套符号表。比如zh、Z、zhh统一成zh换气统一标为SP。这样做之后模型稳定性和收敛速度都有明显提升。5.2 midi 音高和实际演唱差半音或差八度自动生成或人工录入的 midi偶尔会出现整体低八度或个别音符差半音的情况。如果直接拿去做训练标签模型会学到“听起来是 C 实际却标了个 B”的错误映射。这类问题光靠听很难逐条排查我一般先统计 midi 音高范围和实际演唱 F0 范围做一次整体偏移分析再用简单的分布比对找出明显异常值。不过要注意流行歌里人声和 midi 相差八度在编曲上是合理的得先确认 midi 是记实际人声音高还是记伴奏旋律。用的时候先抽几首歌人工核对确认整体记谱逻辑再决定要不要全局修正。5.3 wav 文件损坏和采样率混用批量校验不能省我从不同渠道收集过不少 wav最常遇到的问题是文件头正常但数据部分被截断或者部分文件是 48kHz、部分文件是 44.1kHz。前者在训练时会突然报错中断后者会让整个数据集的时长统计失真。用 Python 的wave模块逐文件读取并检查时长和采样率是最快的预防手段发现问题文件直接删掉或用 ffmpeg 修复不要留在集合里。5.4 数据增广三件套也能做出新花样数据集不是只能原样使用。做过歌声合成的人都知道单一数据集训练出来的模型声线固定泛化性一般。可以把 midi 做整体移调比如升 2 个半音再用变调器把 wav 同步移调textgrid 不变这样就能在不重新标注的情况下扩展出一批新的音区数据。移调后歌手声线会有一点“变小”或“变大”的感觉但对模型学音高和发音的泛化有好处。我自己的做法是正常音高数据占 70%升 3 个半音和降 3 个半音各占 15%合成后模型在高音区的稳定性明显更好。这个增广技巧不需要额外标注完全是已经标注好的 triple 格式带来的便利。6. 我最后想分享的一个小习惯无论是用现成的中文歌声数据集还是自己攒数据我强烈建议在项目一开始就做一个“数据体检脚本”。它的职责很简单扫描所有 wav 的采样率、时长、声道数检查 textgrid 是否存在空标签或越界时间点解析 midi 并统计音高分布最后输出一份整体报告。别小看这一步它能帮你省下后面排查训练崩溃的大把时间。我到现在都留着这个脚本每拿到一批新数据就跑一遍。它不解决所有问题但能保证至少不会因为数据本身的问题浪费一个星期的训练时间。数据集的标注格式越标准脚本能帮你做的自动检查就越多这套 wav、textgrid、midi 三件套的组合属实是省心的类型。本文还有配套的精品资源点击获取