osu自动做谱程序, 对音频文件进行分析后, 自动生成铺面, 由于谱面的数据可以分为时间维度和轨道维度, 我们可以用时频变换得到的频谱图与之对应, 广义来说, 一个频谱图就可以对应一个osu谱面, 只不过想要得到具有可玩性的谱面需要额外一些处理. 整个流程大致分为4步
- 对MP3音频文件先转为WAV格式
- 进行时频变换
- 对变换结果进行处理
- 根据官网给的osu谱面格式制作.osu谱面文件
比较简单, 对应代码在 mp3_to_wav.py
可选的方法如下:
- 直接进行STFT得到spectrogram, 保留最全的信息
- 转换为DB的spectrogram, 仍然没有信息损失,或许会方便DB筛选
- mel power spectrogram
- Constant-Q Transform(CQT)
- Chroma_CQT 损失部分信息, 但可以看到转换成八度后的频谱, 类似钢琴谱
说实话我没有完全搞明白它们之间的关系, 也没有想好用哪个, 但是目前看来chroma稍微方便点
由步骤2得到的数据通常是很复杂的频谱图, 还不能直接用, 我选择先对chroma频谱做二值化
接下来还没有完成, 我的想法是想办法对二值化后的数据降维, 这个降维应该是两个维度的:
- 对于轨道维度, 应该降维成想玩的k数, 比如想得到6k谱就从chroma的八度谱共12个音符降到6个轨道
- 对于时间维度, 由于音频成分复杂, 得到的谱面密度很高, 其中一些是无效的音, 可以尝试用能量密度来判断这个音是否有效, 具体方法我还没想好
处理步骤在 osu_file_make.py
中, 具体规则可以参考osu官网
x,y,time,type,hitSound,addition
- x: note的横向位置,范围为0~512,其中0表示最左侧,512表示最右侧。 - y: note的纵向位置,范围为0~192,其中0表示最上方,192表示最下方。 - time: note的出现时间,以毫秒为单位,表示自谱面开始时间点的偏移量。 - type: note的类型,表示该note需要击打的方式。该参数是一个二进制数,其中1表示需要击打,0表示不需要击打。四种类型分别对应二进制数的四个位数,分别为:1、2、4、8。例如,如果type值为6,则表示该note需要使用键盘上的2和3键击打。 - hitSound: note的击打声音,表示播放的音效文件。该参数是一个二进制数,其中1表示需要播放,0表示不需要播放。四种声音分别对应二进制数的四个位数,分别为:1、2、4、8。例如,如果hitSound值为3,则表示播放第一个和第二个声音文件。 - addition: 额外参数,表示note的其他属性,如颜色、表现等。该参数的具体含义取决于谱面文件的作者和编辑器。
bpm_calculate.py
这个文件主要用于计算bpm, 第一个和最后一个note的时间
为什么要计算这些? 当然是为了给对音做个参考, 不过如果能够完全从频谱图中抽象出这些对音的时间戳且精度不错的话, 这个步骤就不用了
对音频做完时频变换后得到的时间戳单位unit是ms
一拍的间隔=bpm/60*1000, 则x分音的公式如下:
cent4_interval=bpm/60*1000/4
cent8_interval=bpm/60*1000/8
cent16_interval=bpm/60*1000/16
cent32_interval=bpm/60*1000/16
我测试了一下, 龙少女BPM=164的情况下, 16分音的间隔是88ms, 根据以上公式验证结果, 差不多正确
在高难的音游曲目中,每分钟的BPM可以超过180,有些高难曲目的BPM甚至在200到300以上
以BPM为300为例,计算16分音符的时间间隔:
时间间隔 = (1 / (300 / 60)) * 0.25 * 1000 ≈ 50 毫秒
由于cv2目前不支持python11, 所以用python10比较稳
现在好像没用到cv2, 应该没有啥限制