[{"content":"引入 如图所示的，是你使用 FL Studio 过程中用得最多界面之一：「钢琴卷帘」。\n在 Draw 模式下，你可以在这个窗口中写下绿色的音符。\n有一天，你发现在左上角的色块处可以改变音符的颜色——\n左键单击可以在 16 个颜色中选择一个，右键单击则可以自定义当前选中的颜色。\n你觉得默认的绿色看厌了，于是左键换了一个颜色。\n再写新的音符，音符的颜色就改变了\u0026hellip;\u0026hellip;\n然后，你就解锁了这个功能的错误用法！ (￣▽￣|||)\n事实上，这是用来在一个乐器上写不同 MIDI 通道1的音符的。\n即标题所说的 「MIDI 多通道编辑」。\n正确的用法 左键点击弹出的菜单中，编号为 1 ~ 16 的 16 个颜色对应了 16 个 MIDI 通道。\n用不同的颜色写音符，播放时就会把音符发送到当前乐器对应的通道上。就是这样。\n颜色不同只是为了标识不同 MIDI 通道的音符。(‾◡◝)\n有什么用？ 多通道的 VSTi 如果你经常和 VST 乐器打交道，你应该用过像 Kontakt、HALion 这样的采样器。\n那么你应该已经想到了，在这样的采样器里是可以同时挂载多个音色实例2的：\n而每一个音色分别由对应编号的 MIDI 通道的信号控制。\n所以，如果需要挂载多个音色到这样的采样器上，但是出于某种原因，觉得使用 MIDI Out 太碍事的话，就可以改变音符的「颜色」后直接写在采样器的 Channel3 里。\n对于其他支持多通道输入的 VST 乐器，也可以这么干。比如需要在一些鼓机上混用两套鼓的时候。\n但是由于音符可能会重叠，很多情况下使用 MIDI Out 会更方便4。（你要挂两个 VST 也不是不行\n某些插件的特殊音符 有的插件利用了空闲的 MIDI 通道来实现一些特殊用法，比如 FL 自带的 Harmor。\n使用 Harmor 时，在选择颜色的菜单里，后 5 个通道会有对应功能的说明文字。\n差不多是 Keyswitch 那样的东西。\n具体用法请自行查阅 Harmor 的官方文档 中的「Special note colors」部分。\n小技巧——快速修改音符的通道 在套 MIDI 的时候，如果直接在主界面的 File -\u0026gt; Import -\u0026gt; MIDI File... 处导入 MIDI，会发现得到的 Pattern 里的音符都是带对应 MIDI 通道的颜色的。\n如果直接复制到多通道 VST 乐器里，可能就不能正常播放了。\n而选中多个音符时，双击音符又不能批量更改音符的颜色。\n怎么办？套一层 MIDI Out4 吗？我之前还真是这么干的。\n不需要这么做，稍微想一下就知道既然有这个需求怎么可能没做这个功能。\n先左键单击左上角的色块选好想要的颜色（MIDI 通道），再选中要修改颜色的音符，打开左上角的小三角菜单，选择 Edit -\u0026gt; Change color 即可批量修改颜色。\n可以看到 Windows 下的快捷键是 Alt + C，按下快捷键可以达到同样的效果。\n全文完。\n关于 MIDI 的「通道」概念，请自行查阅有关手册或说明。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n此处表述或不严谨。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n此指 FL Studio「通道机架」界面中的「通道」概念，为了区分称作「Channel」。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于 MIDI Out 插件的使用，请自行查阅相关的教程或官方文档。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-06-08T00:00:00Z","image":"https://blog.strmnl.eu.org/p/20240608/fl-tips-1/00_cover_hu_ca310a7c8db9e7e6.png","permalink":"https://blog.strmnl.eu.org/p/20240608/fl-tips-1/","title":"不是装饰吗？——钢琴卷帘中的 MIDI 多通道编辑"},{"content":"封面Pixiv插画ID：94070537，画师：Akyuun。如有侵权请联系我以删除之。\n（本文主要讨论的是弹幕作。格斗作不讨论。）\n众所周知，原作的游戏大小多在 400~500MB（除了体验版）。而游戏文件里最大的都是一个名为 thbgm.dat（体验版游戏中为 thbgm_tr.dat）的文件（th07+）或一个名为 bgm 的文件夹（th06）。显然这就是游戏 BGM 所在了。果然游戏只是音乐的载体而已。 游戏 BGM 为什么这么大？它是如何储存的？本文将讨论原作 BGM 的相关文件格式。\n目录 为什么 BGM 这么大：BGM 音频部分的文件格式\n还有一些信息去哪了：循环点与 wav 文件头的储存\n红魔乡\n妖妖梦及妖妖梦以后\n名与实的联系：Music Room 中的曲名和文字\n红妖永\n花映冢\n文花帖\n妖精大战争与神灵庙\n为什么 BGM 这么大：BGM 的音频数据 不难发现东方红魔乡 （搶曽峠杺嫿） 的 bgm 文件夹里都是以 th06_xx 的格式命名的 wav 文件。嗯\u0026hellip;\u0026hellip;这么原始的不压缩的音频格式能不大吗\u0026hellip;\u0026hellip;\n而从东方妖妖梦起，bgm 文件夹为 thbgm.dat 文件所取代。文件格式变了吗？东方妖妖梦~东方花映冢的游戏里仍然把音频版的BGM音源叫做「WAV」，但 thbgm.dat 改个后缀名直接扔进音频播放器会发现打不开。\n由这篇文章可知，把 thbgm.dat 当成原始音频数据扔进 GoldWave、Audition 等软件里，便可以播放了。所以这个文件应该仍然包含wav格式的音频数据部分。\n以星莲船的 thbgm.dat 为例，扔进 HxD Hex Editor 里，可以看到这样的内容：\n1 2 5a 57 41 56 01 00 00 00 00 12 00 00 00 00 00 00 ... 通过观察可以猜测，文件的结构如下图所示。\n本质上确实还是 wav 格式。\n那么这就是「为什么 BGM 这么大」和「BGM 如何储存」的全部答案。\n全文终\u0026hellip;\u0026hellip;ん？\n你说游戏怎么从连续的音频数据里找到每一首曲子的位置？\n曲子在游戏里不是循环播放的吗，游戏怎么知道这首曲子从哪到哪是循环部分？\n原来的wav格式的文件头里还有那么多正常播放需要的信息（比如采样率、声道数、位深度），怎么在 thbgm.dat 里没有？\n难道这些是硬编码在游戏代码里了吗？\n嗯\u0026hellip;\u0026hellip;这些可能会改变的东西理论上不应该硬编码，尤其是循环点，肯定是每次都要改的。如果不在 thbgm.dat 里，那么这一些应该放在了另外的二进制文件或者文本文件里。这样便于修改，还可以写个脚本一键生成，减少修改时的重复性工作。酒鬼正是设计了另外的文件来存放这些内容。\n还有一些信息去哪了：循环点与 wav 文件头的储存 红魔乡 BGM仍然使用最原始的wav格式的红魔乡不存在文件头的问题，只需要解决循环点的问题。\n用 thtk 中的 thdat 解包游戏目录下的 紅魔郷MD.DAT ，得到的除了 midi 格式的 BGM 以外，发现还有一些和 BGM 同名的 th06_xx.pos 文件和一个 musiccmt.txt。后者留到后面再做讨论，前者应该就是我们要找的循环点了。\n以 th06_01.pos 为例，同样用 HxD 打开：\n1 a7 09 04 00 a5 d8 27 00 可以猜测，前一半是循坏开始点，后一半是循环结束点（顺便一提，数值的采用的字节顺序是小端序 1 ）。Touhou Wiki 上的有关信息表明确实是这样：\nloops in 紅魔郷MD.dat/*.pos\nFormat: \u0026lt;start sample\u0026gt;.4b \u0026lt;end sample\u0026gt;.4b\n这个 sample 是指什么？由这个帖子可以归纳出其数值的计算公式：\n设曲目播放到第t秒时开始/结束循环.\n则循坏开始点/结束点的 sample = 44100 * t（结果取整数）\n显然 44100 即红魔乡所有曲目的采样率 44.1kHz。\n那么结合关于数字音频的常识 2 ，我们可以知道 sample 的取值 n 即表示「文件中的第 (n+1) 个采样点」。\n所以我们现在知道了，红魔乡的 BGM 循环点用采样点表示，储存在 紅魔郷MD.DAT 里的 th06_xx.pos 中。\n妖妖梦及妖妖梦以后 妖妖梦起，游戏目录下的 thxx.dat（xx 为游戏编号，体验版游戏为 thxx_tr.dat）用 thtk 解包后会得到一个 thbgm.fmt 文件（体验版游戏可能是 thbgm_tr.fmt）。\n嗯\u0026hellip;fmt？不难想到 wav 文件头里的 FormatChunk 3 。而我们刚才所说的 wav 文件「正常播放需要的信息」，其实就是 wav 文件头里的 FormatChunk。\n以 th19 的 thbgm_tr.fmt 为例，这个也用 HxD 打开看看：\n1 2 3 4 5 6 74 68 31 39 5f 30 31 2e 77 61 76 00 00 00 00 00 10 00 00 00 c0 43 94 01 00 20 03 00 c0 43 94 01 01 00 02 00 44 ac 00 00 10 b1 02 00 04 00 10 00 00 00 00 00 ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 发现第三行确实就是完完整整的 FormatChunk 的数据部分，一点不差：\n1 01 00 02 00 44 ac 00 00 10 b1 02 00 04 00 10 00 那其他内容呢？在 HxD 里可以看见，第一行对应的字符便是我们在红魔乡里见过的文件名格式：th19_01.wav\n第二行则是循环点相关。具体见下面整个文件的结构。\nTHBWiki 上的脚本对照表 用了 C 语言的结构体来表示整个文件的结构，我觉得挺清晰的。\n改了一下copy过来：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 typedef struct //单首曲目 { char fileName[16]; //文件名。（大端序，下面的数值皆为小端序） unsigned int songStartOffset; //曲目开头在thbgm.dat中的偏移量。单位是bytes，下面三项同。 unsigned int unknown; //th13起与loopEndOffsetInSong恒等，此前取值意义不明。 unsigned int introLength; //循环开始时相对于songStartOffset的偏移量，或理解为曲子的前奏的长度。 unsigned int totalLength; //循环结束时相对于songStartOffset的偏移量，或理解为曲子的总长。 /*fmt chunk*/ unsigned short formatTag; //恒为1，音频数据为PCM编码 unsigned short channels; //声道数，常为2 unsigned int samplePerSec; //采样率，常为44100，神灵庙的灵界版BGM是22050 unsigned int bytesPerSec; //blockAlign*samplePerSec 即每秒的采样数据的大小，单位是bytes unsigned short blockAlign; //bitsPerSample*channels/8 即每次采样的大小，常为4（单位是bytes） unsigned short bitsPerSample; //位深度，常为16（单位是bit） int fmtEnd; //结尾4个字节都是0 } thfmt_t; typedef struct //整个文件 { thfmt_t fmtList[18]; //18只是举个例子，看实际曲目数 char fileEnd[17]; //结尾17个字节都是\u0026#39;\\0\u0026#39; } thfmt_body_t; Touhou Wiki 上的说明也贴上来，这个比较简洁：\nFormat: {\u0026lt;wav name\u0026gt;.16b \u0026lt;start offset\u0026gt;.4b \u0026lt;???\u0026gt;.4b \u0026lt;intro length\u0026gt;.4b \u0026lt;total length\u0026gt;.4b\n\u0026lt;RIFF WAVEfmt header (WAVEFORMATEX structure)\u0026gt;.18b 0000 }.52b*\n设前奏或全曲时长为 t 秒，则对应 \u0026lt;intro length\u0026gt; 或 \u0026lt;total length\u0026gt; 的计算公式：\nlength = bytesPerSec * t = blockAlign * samplePerSec * t = (samplePerSec * t) * bitsPerSample * channels / 8 至于 \u0026lt;start offset\u0026gt;，所谓的偏移量其实就是前面全部内容的 length 嘛。\n因此，手动获取的话，可以采取前面提到的把 thbgm.dat 当成原始音频数据扔进音频编辑软件里的做法。\n在软件中查看曲目开始的时间点的时间 t，即为前面所有内容的时长 t。\n但是注意代入公式的参数应与你打开文件时的设置一致，而不是 thbgm.fmt 里的参数。\n软件会把 thbgm.dat 的文件头也当做音频数据，所以算出来的结果已经包含了文件头的长度。\n但是，感觉不如写个脚本，生成 thbgm.dat 的同时生成配套的 thbgm.fmt。（\n名与实的联系：Music Room 中的曲名和文字 现在来看看刚才提到的 musiccmt.txt。在 thxx.dat 解包后的文件里也发现了这个。\n试着用记事本打开红魔乡的 musiccmt.txt。打开后可以看到\u0026hellip;\u0026hellip;\n1 2 侽侾俀俁係俆俇俈俉俋侽侾俀俁係俆侽侾俀俁係俆俇俈俉俋侽侾俀俁係俆 ... ？\n嗯\u0026hellip;看不到\u0026hellip;因为 Windows 自带的记事本在中文环境下打开 Shift-JIS 编码的文件会乱码。\n换个编辑器打开就好了。\n打开之后，可以发现主要是出现在 Music Room 里的文字，还有曲目对应的文件名。\n1 2 3 4 5 6 7 8 9 10 11 ０１２３４５６７８９０１２３４５０１２３４５６７８９０１２３４５ @bgm/th06_01.mid 赤より紅い夢 No.1 赤より紅い夢 タイトル画面テーマです。 東方なんで、和風にしてみました。いやほんと。 ゲームはまるで和風じゃ無いくせに(^^; ＳＴＧとは思えないような曲です。 そもそも、タイトル画面に曲は必要なのでしょうか（笑）？ ... 推测应该是 Music Room 的曲目列表。\n红妖永 对照着 Music Room 来看，在红魔乡中（也适用于妖和永）：\n那一行全角数字不知道有什么用。\n格式大体上是：\n1 2 3 4 5 6 @bgm/\u0026lt;文件名\u0026gt;.mid \u0026lt;Music Room 列表里显示的标题\u0026gt; \u0026lt;乐评里的标题\u0026gt; \u0026lt;乐评正文\u0026gt; ... 文件名虽然写上了后缀 .mid，但使用WAV音源时游戏应该会匹配同名的 .wav 文件。\n尝试将 mid 改成任意的 3 个字母，仍然能播放；但是改成非 3 个字母，在红魔乡里就不能正常播放，而在妖妖梦和永夜抄里可以正常播放。如果尝试删掉后缀名，红妖永都会发生崩溃。\n另外，上面的无法正常播放的情况，仅限于在 Music Room 里无法播放。可以推测游戏进程中，以及标题画面中，BGM 的读取并不受这个文件影响。\n乐评正文的缩进是每行前敲一个全角空格实现的。\n另外，在永夜抄里添加了「Now Playing」的提示。内容和 \u0026lt;乐评里的标题\u0026gt; 是一样的。\n花映冢中的小改动 1 2 3 4 5 6 7 8 9 10 ０１２３４５６７８９０１２３４５０１２３４５６７８９０１２３４５ @bgm/th09_00.mid No.1 花映塚　～ Higan Retour タイトル画面テーマです。 いつもの曲ですね。ええ。 ゲーム自体はここ暫くの間の中では一番バカになっているのに、 この曲だけはいつものままです。 ちょっとバカ度が足りないかもしれない。ここだけは幻想郷に。 ... 可以发现相比红妖永少了一行 \u0026lt;Music Room 列表里显示的标题\u0026gt;。因为 Music Room 里的样式改了。原来的 \u0026lt;乐评里的标题\u0026gt; 不再显示在乐评中，只显示上方在“再生中”的文字旁，列表里的标题直接使用了“再生中”旁的文字。\n缩进变为了每一句的开头用两个全角空格，句中换行用一个全角空格。\n没有 midi 的曲目的后缀名仍然是 .mid。后缀名相关特性同妖永。\n（题外话：为什么花映冢 Music Room 里新加的暂停和淡出到后面又没了）\n文花帖中的小改动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # # 曲のコメント # #0１２３４５６７８９０１２３４５６７８９０１２３４５６７８９０ @bgm/th10_02.wav No.1 封印されし神々 ♪封印されし神々 タイトル画面のテーマです。 いつもの曲……ですね。 和風な神様がテーマと言う事で、少し大げさに作ってみました。 割と奇妙奇天烈なリズムの曲ですが、ずっと聞いているとしっくり 来ます。特にデバッグ中とか。 ... （注：这一部分的变化最早出现在东方文花帖而非风神录。）\n前面添加了三行。\n1 2 3 # # 曲のコメント # 「#」是注释吗？似乎不是。全角数字那一行的内容变了，前面也加了个「#」。\n（数字里第 1 个 0 就是半角的，并不是我复制时候出错了。而且一直到兽王园这个半角的 0 都没改过来\u0026hellip;\u0026hellip;）\n随着 MIDI 音源的彻底移除，文件名的后缀改成了 .wav。但后缀名相关特性仍同妖永花。\n其余部分又回到了之前的三行的格式。但是原本的 \u0026lt;乐评里的标题\u0026gt; 的格式继续用在列表中，现在的乐评中的标题前面加了个「♪」（于是在游戏里和正文对齐了）。\n另外乐评中的空行上其实都有一个全角空格。没有全角空格的行读取时会被忽略。\n妖精大战争与神灵庙中的小改动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # # 曲のコメント # #0１２３４５６７８９０１２３４５６７８９０１２３４５６７８９０ @bgm/th13_00 No. 1 欲深き霊魂 ♪欲深き霊魂 タイトル画面のテーマです。 いつものメロディです。今回はダーク色多めでお送りします。 ＳＴＧはポップな世界観よりダークな方が好まれる傾向にある 気がします。 このゲームは全く持ってダークじゃないんですけどね。 ... 妖精大战争中，列表中的标题的「No.」与数字之间加了一个半角空格。\n神灵庙中，文件名的后缀去掉了。尝试加上后缀名无法正常播放，但不会崩溃，而是会播放标题曲。\n此外，尝试将疮痍曲加进来时，可以播放，但总是会显示为没有在游戏进程中播放过的曲目，如下图所示。\n暂时不知道游戏是怎么记录有没有播放过的。\n可以看见，最后一行虽然加上了井号，但是没有被屏蔽，所以我刚才说「# 似乎不是注释」的原因。\n将一个多位数的低位放在较小的地址处，高位放在较大的地址处，则称小端序。\n例如在这一处，值 0x000409a7 被记为了 a7 09 04 00。\n后文提到的大端序则会反过来记为 00 04 09 a7，和我们从左往右的阅读顺序一致。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n在音频数字化的三个步骤「采样——量化——编码」中，“采样”时每秒采样的次数称为采样率。故 采样率 * 秒数 即表示使用这一采样率时，时长为这一秒数的音频包含的采样点个数。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n该 chunk 开头三个字节便是字符 fmt。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-07-01T00:00:00Z","image":"https://blog.strmnl.eu.org/p/20230701001/cover_hu_ad66ba4bb89562ab.jpg","permalink":"https://blog.strmnl.eu.org/p/20230701001/","title":"东方原作 BGM 文件格式探究"}]