Yamaha S-YXG50 逆向工程架构文档
以下是我烧掉 150 亿 token 的结果
因为是 AI 分析的,可能有不准确的地方
目录
- 概述
- 程序架构
- MIDI 到音频的完整数据流
- 效果器链运作机制
- 核心效果器算法
- 关键数据结构
- 音色表系统
- 附录
概述
Yamaha S-YXG50 是 Yamaha 公司于 2003 年发布的一款软件波表合成器,以 DLL 形式存在,主要用作 VST 插件。它的核心功能是接收 MIDI 音乐信号,实时合成出高质量的音频输出。
支持的 MIDI 标准:
- GM(通用 MIDI):最基础的 MIDI 标准,定义了 128 种乐器和一套鼓组
- GS(Roland 标准):GM 的扩展,增加了更多音色和鼓组
- XG(Yamaha 标准):Yamaha 自己的扩展标准,支持更丰富的音色编辑和效果器控制
基本能力:
- 同时处理 16 个 MIDI 通道,每个通道可以演奏不同的乐器
- 最大 128 复音,即同时可以有 128 个音符在发声
- 内置三组效果器:混响、合唱、变奏
- 44.1kHz 采样率、立体声浮点音频输出
- 基于波表的音色系统,音色数据存储在外部文件中
程序架构
整体结构
S-YXG50 采用分层架构设计,从上到下分为四个主要层次:
第一层:VST 插件外壳(CVstEffect)
这是与宿主程序(如 Cubase、FL Studio)对接的最外层。它负责接收宿主发来的各种消息,比如"打开插件"、“关闭插件”、“处理 MIDI 事件”、"设置采样率"等。这一层本身不做任何音频处理,只是一个翻译官,把宿主的消息翻译成内部引擎能理解的指令。
CVstEffect 内部持有一个 CSynthEngine 实例,所有实际工作都委托给它完成。
第二层:合成器核心引擎(CSynthEngine)
这是整个合成器的大脑。它协调各个子系统的工作:
- 持有 MIDI 处理器,负责解析 MIDI 消息
- 持有声音管理器,负责分配和回收声音资源
- 持有效果处理器,负责给音频加效果
- 持有音色表,存储所有乐器的音色数据
每当宿主请求一帧音频时,CSynthEngine 就会指挥各个子系统协同工作,最终输出立体声采样。
第三层:功能子系统
这一层包含四个独立的子系统,各司其职:
MIDI 处理器(CMidiHandler):接收原始 MIDI 字节流,解析出有意义的音乐事件。比如它能识别出"第 5 通道按下中央 C,力度 100"这样的指令,并通知声音管理器去触发对应的声音。
声音管理器(CVoiceManager):管理一个包含 128 个声音槽的池子。当收到 Note On 指令时,它会从池子里找一个空闲的声音槽,初始化它的参数,开始播放。当收到 Note Off 指令时,它会让对应的声音进入释放状态。每个声音槽都是一个独立的 DSP 处理单元,有自己的振荡器、包络、滤波器等。
效果处理器(CEffectsProcessor):对所有声音混合后的音频进行效果处理。它包含混响、合唱、变奏三组效果器,每组都可以独立设置类型和参数。
音色表管理器(CVoiceTable):负责加载和管理音色数据文件。它读取 sxgbin41.tbl 中的音色参数(比如用什么波形、包络怎么设置、滤波器怎么调)和 sxgwave4.tbl 中的实际波形采样数据。
第四层:DSP 处理模块
这是最底层的信号处理库,提供各种基础的音频处理功能:
- 振荡器:生成或读取波形数据
- 滤波器:改变音频的频率特性
- 包络发生器:控制声音的音量随时间变化
- 混响效果器:模拟空间反射,让声音有空间感
- 合唱效果器:通过延迟调制让声音更丰满
- 混音器:将多个声音混合成最终的立体声输出
源代码组织
源代码按照功能模块组织在不同的目录中:
include/ 目录存放所有头文件,定义了核心数据结构和接口。最重要的包括 types.h(基础类型)、engine.h(合成器引擎)、voice.h(声音对象)、effects.h(效果器接口)、voice_table.h(音色表)。
midi/ 目录存放 MIDI 消息处理相关的代码。核心文件 midi_processing.cpp 负责解析各种 MIDI 消息,包括 Note On/Off、控制变化、程序变化、系统专属消息等。
voice/ 目录存放声音引擎相关的代码,是整个项目中文件最多、代码量最大的部分。voice_engine.cpp 是声音引擎的核心,voice_rendering.cpp 处理声音的渲染,voice_param.cpp 管理声音参数,voice_state.cpp 管理声音的状态转换(激活、释放、静音等)。
effects/ 目录存放效果器相关的代码。effects_algorithms.cpp 实现了各种效果器算法,effects_params.cpp 处理效果器参数的设置和读取。
dsp/ 目录存放底层 DSP 处理代码。oscillator.cpp 实现波形生成,filters.cpp 实现各种滤波器,envelope.cpp 实现包络发生器,reverb.cpp 实现混响,chorus.cpp 实现合唱,mixer.cpp 实现混音。
channel/ 目录存放通道管理相关的代码。每个 MIDI 通道都有自己的参数状态(音量、声像、弯音等),这些代码负责管理它们。
audio/ 目录存放音频参数管理和命令分发的代码。audio_params.cpp 是最大的单个文件,包含了 DSP 处理器的大部分实现。
xg/ 目录存放 XG/GS 兼容层的代码。这些代码确保 S-YXG50 能正确响应各种 XG 和 GS 专属的 MIDI 消息和系统专属指令。
mfc/ 目录存放 MFC(Microsoft Foundation Classes)相关的工具函数。原始 DLL 使用了 MFC 框架,这些函数处理内存管理、字符串操作等基础功能。
core/ 目录存放 DLL 入口点、运行时工具等核心功能。
MIDI 到音频的完整数据流
总体流程
从 MIDI 信号进入 DLL 到最终输出音频,需要经过以下主要阶段:
- MIDI 事件接收
- MIDI 消息解析
- 通道参数更新
- 声音触发与分配
- 声音初始化
- 波形渲染
- 效果器处理
- 混音输出
阶段一:MIDI 事件接收
宿主程序通过 VST 接口将 MIDI 事件传递给插件。这些事件被打包在一个事件数组中,每个事件包含事件类型、时间戳和 MIDI 数据字节。
S-YXG50 在音频处理循环的开始处检查是否有待处理的 MIDI 事件。如果有,就逐个取出处理。
阶段二:MIDI 消息解析
对于每个 MIDI 事件,系统首先解析它的状态字节(第一个字节的高四位),确定这是什么类型的消息:
Note On(音符开启):表示某个键被按下。数据包含通道号、音符号(0-127,对应钢琴的 88 个键)和力度(按键的力度,0-127)。力度为 0 的 Note On 等同于 Note Off。
Note Off(音符关闭):表示某个键被释放。数据包含通道号和音符号。
Control Change(控制变化):表示某个控制器的值发生了变化。常见的控制器包括音量(CC#7)、声像(CC#10)、调制轮(CC#1)、延音踏板(CC#64)等。每个 CC 消息包含控制器编号和新的值。
Program Change(程序变化):表示切换乐器音色。数据包含新的音色编号(0-127)。
Pitch Bend(弯音):表示弯音轮的位置变化。弯音值是一个 14 位的数(0-16383),中间值 8192 表示没有弯音。
System Exclusive(系统专属):用于厂商特定的扩展功能。S-YXG50 通过系统专属消息接收 XG、GS、GM 的各种高级设置。
阶段三:通道参数更新
每个 MIDI 通道都有自己的参数状态,包括音量、声像、弯音、调制深度、滤波器截止频率、包络参数等。当收到控制变化消息时,系统会更新对应通道的对应参数。
这些参数存储在全局参数块中,供后续的声音渲染使用。比如音量参数会影响声音的响度,声像参数会影响声音在左右声道的分布,弯音参数会影响声音的音高。
阶段四:声音触发与分配
当收到 Note On 消息时,声音管理器需要为这个音符分配一个声音槽。声音池共有 128 个声音槽,每个 MIDI 通道最多可以同时使用 32 个声音。
分配过程如下:
- 首先在对应通道的声音槽范围内寻找空闲的槽位
- 如果找到空闲槽位,直接使用
- 如果没有空闲槽位,需要"偷取"一个正在发声的槽位(通常选择最早开始发声的)
分配成功后,声音槽被标记为"激活"状态。
阶段五:声音初始化
声音槽分配好后,需要初始化它的各种参数:
音色参数加载:从音色表中查找对应的音色数据。查找时需要考虑当前通道选择的乐器编号、Bank Select(音色库选择)的 MSB 和 LSB 值。如果是鼓组通道(第 10 通道),则从鼓组数据中查找。
振荡器初始化:设置波形数据的起始地址、循环点位置、采样率等。如果波形采样率与输出采样率不同,还需要设置采样率转换参数。
包络初始化:根据音色数据中的包络参数,计算 Attack、Decay、Sustain、Release 各阶段的速率和目标值。
滤波器初始化:根据音色数据中的滤波器参数,计算滤波器的截止频率和共振值。
增益初始化:根据力度、音量、声像等参数,计算初始的左右声道增益值。
阶段六:波形渲染
声音初始化完成后,进入波形渲染阶段。系统以 128 采样为一个处理块,逐块进行渲染。
波形读取:振荡器从波形数据中读取采样值。读取速率由音高决定——音高越高,读取速率越快,波形被"压缩",频率升高。由于读取速率通常不是整数,需要使用线性插值来计算中间值,保证声音平滑。
循环处理:大多数乐器音色都有循环区域。当读取位置到达循环结束点时,会跳回循环起始点继续读取,这样声音可以持续任意长时间。有些音色是单次播放(如钢琴),到达终点后就停止。
包络应用:包络发生器根据当前所处的阶段(Attack、Decay、Sustain、Release)计算一个音量因子,乘到波形数据上。Attack 阶段音量从 0 上升到最大值;Decay 阶段从最大值下降到 Sustain 电平;Sustain 阶段保持在 Sustain 电平;Release 阶段从当前电平下降到 0。
S-YXG50 为每个声音维护三组包络:主包络控制整体音量,合唱发送包络控制送往合唱效果器的信号量,混响发送包络控制送往混响效果器的信号量。
滤波器应用:双二阶滤波器(Biquad Filter)改变音频的频率特性。最常用的是低通滤波器,它让低频通过而衰减高频,使声音变得更暗、更柔和。滤波器的截止频率可以由 MIDI CC#74(Brightness)控制。
增益与声像:根据力度、通道音量、表情、声像等参数,计算最终的左右声道增益。声像控制声音在左右声道的分布——声像值为中间时左右声道音量相等,偏左时左声道更响,偏右时右声道更响。
输出到缓冲区:渲染好的采样数据被写入多个缓冲区——主输出缓冲区(左右声道)、合唱发送缓冲区、混响发送缓冲区。这些缓冲区将在后续的效果器处理阶段使用。
阶段七:效果器处理
当所有活跃的声音都渲染完成后,系统将它们的输出混合在一起,然后送入效果器链处理。效果器处理的详细机制在下一章描述。
阶段八:混音输出
效果器处理完成后,进入最终的混音输出阶段:
效果返回混合:将效果器处理后的信号(混响返回、合唱返回、变奏返回)与干信号混合在一起。
主音量应用:将混合后的信号乘以主音量系数。
限幅处理:防止信号超出有效范围。如果采样值超过最大值(131071)或低于最小值(-131072),就将其限制在范围内,避免削波失真。
输出到宿主:将最终的立体声音频数据写入宿主提供的输出缓冲区。
效果器链运作机制
效果器架构概述
S-YXG50 的效果器系统由三组独立的效果器组成:混响(Reverb)、合唱(Chorus)、变奏(Variation)。每组效果器可以独立设置类型和参数。
效果器的信号流如下:
每个声音渲染完成后,它的输出被分成多个部分:
- 主输出:直接送到最终混音器
- 混响发送:送到混响效果器
- 合唱发送:送到合唱效果器
- 变奏发送:送到变奏效果器
每组效果器处理完后,输出信号被送回最终混音器,与干信号混合。发送量由 MIDI CC 控制(CC#91 控制混响发送,CC#93 控制合唱发送,CC#94 控制变奏发送)。
混响效果器
混响模拟声音在空间中的反射效果,让声音听起来像是在某个房间里演奏。
支持的混响类型:
- Hall(大厅):模拟音乐厅的大空间,混响时间长,声音温暖丰满
- Room(房间):模拟普通房间,混响时间较短,声音紧凑
- Stage(舞台):模拟舞台环境,声音明亮清晰
- Plate(板式):模拟金属板混响器,声音明亮,衰减均匀
可调参数:
- 混响时间:声音衰减到听不见所需的时间
- 阻尼:高频衰减的速度,阻尼越大高频衰减越快,声音越暗
- 扩散:反射的密度,扩散越大声音越平滑
- 预延迟:直达声与第一次反射之间的时间差
合唱效果器
合唱效果通过将信号延迟并调制,产生多个略有差异的声音叠加的效果,让单个乐器听起来像是多个乐器在同时演奏。
支持的合唱类型:
- Chorus(合唱):标准合唱效果,延迟时间较长(10-30ms),没有或很少反馈
- Flanger(镶边):延迟时间很短(1-10ms),有较高的反馈,产生"喷气机"效果
- Symphonic(交响):多个延迟线叠加,产生更丰满的合唱效果
工作原理:
- 输入信号被写入一个延迟缓冲区
- 一个低频振荡器(LFO)不断调制读取位置,使得延迟时间在一定范围内周期性变化
- 读取出来的信号与原始信号混合,产生音高微调的效果
- 左右声道使用不同相位的 LFO,产生立体声扩展效果
变奏效果器
变奏效果器是一个通用的效果器插槽,可以加载各种不同类型的效果。支持的效果类型非常丰富:
调制类效果:
- Chorus/Flanger/Symphonic:与合唱效果器类似
- Phaser(相位器):使用全通滤波器链产生梳状滤波效果
- Tremolo(颤音):周期性调制音量
- Auto Pan(自动声像):周期性调制声像位置
空间类效果:
- Delay/Echo(延迟/回声):将信号延迟后与原始信号混合
- Early Reflections(早期反射):模拟房间的前几次反射
- Gate Reverb(门混响):混响突然截止的效果
失真类效果:
- Distortion(失真):硬削波,产生强烈的谐波失真
- Overdrive(过载):软削波,产生温暖的失真效果
- Amp Simulator(音箱模拟):模拟吉他音箱的音色
动态类效果:
- Compressor(压缩器):减小动态范围,让响的和轻的部分差距变小
- Limiter(限幅器):防止信号超过阈值
频率类效果:
- 3-Band EQ / 2-Band EQ:多段均衡器,可以调整不同频段的音量
- Auto Wah(自动哇音):周期性调制滤波器截止频率
其他效果:
- Rotary Speaker(旋转扬声器):模拟 Leslie 音箱的旋转效果
- Pitch Change(变调):改变音高
- Karaoke(卡拉 OK):消除人声
效果参数管理
效果器的参数通过一个专门的状态结构来管理。这个结构存储了所有效果器的当前设置,包括每种效果器的类型编号和各个参数值。
参数值通常是 0-127 范围的整数,对应 MIDI 控制器的值范围。有些参数使用 16 位编码,可以表示更精细的控制。
核心效果器算法
混响算法
S-YXG50 的混响采用经典的 Schroeder 混响结构,这是一种在数字音频领域广泛使用的混响算法。
信号处理流程:
-
预延迟:输入信号首先经过一个短延迟(5-20ms)。这模拟了从声源到墙壁的首次反射的时间差,增加了"空间感"。预延迟越长,听起来空间越大。
-
早期反射:接下来模拟房间的前几次反射。这些反射是离散的回声,每个回声有不同的延迟时间和音量。早期反射帮助听觉系统判断空间的大小和形状。
-
梳状滤波器组:这是混响的核心部分。8 个并联的梳状滤波器,每个都有不同的延迟长度(大约 30-45ms)。梳状滤波器的工作原理是将信号延迟后与原始信号混合,产生一系列共振峰。多个不同延迟长度的梳状滤波器并联,产生的共振峰相互补充,形成密集的混响尾音。每个梳状滤波器都有反馈(控制混响时间)和阻尼(控制高频衰减)。
-
全通滤波器组:4 个串联的全通滤波器。全通滤波器的特点是让所有频率都通过,但会改变相位。这一步的目的是增加混响的"扩散感",让声音更平滑,减少梳状滤波器带来的"金属感"。
-
阻尼处理:根据阻尼参数调整高频的衰减速度。阻尼越大,高频衰减越快,混响听起来越"温暖"、越"暗"。这模拟了真实房间中声波被墙壁材料吸收的特性。
不同混响类型的差异:
大厅混响有较长的混响时间(约 0.7 秒),较高的扩散和密度,预延迟约 20ms,适合营造宏大的空间感。
房间混响混响时间较短(约 0.3-0.5 秒),扩散和密度中等,预延迟约 5-10ms,适合营造紧凑的房间感。
板式混响扩散很高,声音明亮均匀,没有明显的早期反射,适合人声和乐器的混响处理。
合唱算法
信号处理流程:
-
延迟线:输入信号被写入一个循环缓冲区。缓冲区长度由最大延迟时间决定(10-30ms)。
-
LFO 调制读取位置:一个低频振荡器(通常是正弦波或三角波)不断改变读取位置。这使得读出的信号相对于写入的信号有周期性的时间偏移,从而产生音高微调效果。
-
反馈(可选):部分输出信号反馈回输入,增加效果的"厚度"。合唱模式通常没有或很少反馈,镶边模式有较高的反馈(0.7-0.9)。
-
立体声扩展:左右声道使用相位差 90 度的 LFO,使得左右声道的调制不同步,产生宽广的立体声效果。
-
混合:将调制后的信号与原始信号按一定比例混合。
合唱与镶边的区别:
合唱的延迟时间较长(10-30ms),LFO 速率较慢(0.1-5Hz),没有或很少反馈,产生的效果是声音变得更丰满、更宽广。
镶边的延迟时间很短(1-10ms),LFO 速率更慢(0.1-2Hz),有较高的反馈,产生的效果是那种典型的"喷气机"声音,带有明显的共振峰。
延迟算法
信号处理流程:
- 输入信号被写入延迟线
- 延迟线的输出按反馈系数送回输入
- 干信号和延迟信号按混合比例混合后输出
延迟时间可以从 1 毫秒到 1 秒以上。反馈系数控制重复次数——0% 时只有一个回声,接近 100% 时回声会重复很多次逐渐衰减。
相位器算法
信号处理流程:
- 输入信号通过 4-12 个串联的全通滤波器
- LFO 调制全通滤波器的中心频率
- 部分输出信号反馈回输入
- 调制后的信号与原始信号混合
全通滤波器会改变信号的相位但不改变幅度。当多个全通滤波器串联时,某些频率的相位变化会相互抵消,而另一些频率会增强或减弱,形成梳状滤波效果。LFO 不断改变这个梳状滤波的频率位置,产生动态的"嗖嗖"声。
颤音与自动声像算法
颤音:LFO 直接调制信号的音量。当 LFO 值大时音量大,LFO 值小时音量小,产生周期性的音量波动。
自动声像:LFO 调制信号在左右声道的分配。当 LFO 值大时声音偏右,LFO 值小时声音偏左,产生声音在左右之间移动的效果。
失真与过载算法
信号处理流程:
- 输入信号先经过增益放大
- 放大后的信号通过非线性函数进行削波
- 削波后的信号通过低通滤波器去除高频谐波
- 处理后的信号与原始信号按混合比例混合
过载使用软削波函数(如双曲正切函数),信号在接近阈值时逐渐被压缩,产生的失真比较温暖、平滑。
失真使用硬削波,信号超过阈值后直接被截断,产生的失真比较强烈、粗糙。
削波会产生大量谐波,听起来很刺耳,所以后面通常会接一个低通滤波器来柔化音色。
均衡器算法
S-YXG50 的均衡器采用三段设计:
低频搁架:可以提升或衰减某个频率以下的所有频率。典型频率范围是 20-500Hz。就像一个可以整体调高或调低的低音旋钮。
中频峰值:可以提升或衰减某个中心频率附近的一个频带。中心频率和带宽(Q 值)都可以调整。这是最灵活的 EQ 段,可以针对特定频率进行精确调整。
高频搁架:可以提升或衰减某个频率以上的所有频率。典型频率范围是 2kHz-20kHz。就像一个可以整体调高或调低的高音旋钮。
压缩器算法
信号处理流程:
- 检测输入信号的电平(可以是 RMS 平均值或峰值)
- 将检测到的电平与阈值比较
- 如果电平低于阈值,增益为 1(不压缩)
- 如果电平高于阈值,按照压缩比降低增益
- 用攻击时间和释放时间平滑增益变化
- 将计算出的增益应用到信号上
压缩比决定了压缩的强度。比如 4:1 的压缩比意味着输入信号每增加 4dB,输出只增加 1dB。无限大:1 的压缩比就是限幅器,输出永远不会超过阈值。
攻击时间决定了增益下降的速度——攻击时间短,压缩反应快,能抓住瞬态峰值;攻击时间长,压缩反应慢,保留更多瞬态。
释放时间决定了增益恢复的速度——释放时间短,压缩恢复快,可能产生"抽吸"效果;释放时间长,压缩恢复慢,动作更平滑。
旋转扬声器算法
这个效果模拟 Leslie 音箱的旋转扬声器,常见于风琴音色。
工作原理:
Leslie 音箱有两组扬声器——高频号角和低频低音箱——它们会旋转。旋转产生两个效果:
多普勒效应:当扬声器朝向听者运动时,接收到的频率会升高;当远离时频率会降低。这产生了音高调制效果。
幅度调制:旋转时,扬声器有时朝向听者(音量大),有时背向听者(音量小)。这产生了音量调制效果。
S-YXG50 分别模拟高频和低频的旋转效果,然后将它们混合。左右声道分别模拟不同的旋转位置,产生环绕感。LFO 速率可以切换快速和慢速。
早期反射算法
早期反射模拟房间中直达声之后的前几次墙壁反射。与混响不同,早期反射是离散的回声,而不是密集的混响尾音。
工作原理:
使用多抽头延迟线,每个抽头代表一次反射。每个抽头有不同的延迟时间和增益,模拟不同距离和角度的反射。
小房间的反射间隔短、衰减快;大房间的反射间隔长、衰减慢。走廊会产生强侧向反射。
关键数据结构
声音 DSP 对象(VoiceDSPObject)
这是每个活跃声音的核心数据结构,大小为 780 字节(0x30C)。每个正在发声的音符都有一个这样的对象实例。
主要字段说明:
状态标志:
- active 字段表示这个声音槽是否在使用中
- flags 字段用位标志表示声音的各种状态——是否正在发声、是否正在释放、是否静音等
音频参数:
- sampleRate 存储当前的采样率
- rateScale 存储速率缩放因子,由力度决定
- coeffTable 指向系数表,系数表存储了各种 DSP 处理需要的参数
振荡器状态:
- 包含波形数据指针、循环点位置、当前采样位置等
- samplePos 和 sampleFrac 记录精确的读取位置(整数部分和小数部分)
滤波器状态:
- 22 个浮点数存储滤波器的内部状态
- 双二阶滤波器需要存储前两个输入和输出的值
噪声发生器:
- 4 组 23 位线性反馈移位寄存器(LFSR)
- 用于生成白噪声,模拟某些乐器的噪声成分
包络电平:
- 四个浮点数分别存储主包络、合唱包络、混响包络和备用主包络的当前电平
增益值:
- 四组当前增益和目标增益(分别对应主输出 L/R 和发送 L/R)
- 增益平滑算法逐步将当前增益移向目标增益,避免突变产生咔嗒声
DSP 函数指针表:
- 29 个函数指针,构成一个处理链
- 每个函数负责一个特定的 DSP 任务
- 系统按顺序调用这些函数,完成整个声音的渲染
引擎布局结构(EngineLayout)
这个结构描述了 DLL 内部引擎对象的内存布局。它是连接各个子系统的桥梁。
关键字段:
- voiceManager 指针:指向声音管理器
- midiHandler 指针:指向 MIDI 处理器
- effectsProcessor 指针:指向效果处理器
- voiceTable 指针:指向音色表管理器
- midiResetMode:MIDI 重置模式(0=GM,1=XG,2=GS)
- isXGLite:XG Lite 模式标志
效果器布局结构(EffectsLayout)
这个结构描述了效果处理器内部的参数存储布局。所有效果器参数都以字节为单位存储在连续的内存区域中。
主要参数组:
- 混响参数:时间、阻尼、扩散、预延迟
- 合唱参数:速率、深度、反馈、延迟
- 延迟参数:时间、反馈、电平
- 变奏参数:类型和最多 4 个通用参数
- 均衡器参数:低频增益、中频增益、高频增益、中频频率
- 插入效果参数:20 个通用参数
- 主效果参数:4 个通用参数
MIDI 参数块(MidiParamBlock)
存储当前活跃通道的 MIDI CC 值。这是一个全局数据块,所有通道共享,当切换通道时需要更新。
存储的参数包括:
- 声像(CC#10)
- 音量(CC#7)
- 弯音值(0-16383)
- 滤波器截止频率(CC#74)
- 调制轮(CC#1)
- 滤波器共振(CC#71)
- 包络起音(CC#73)
- 包络衰减(CC#75)
- 表情(CC#11)
- 包络释放(CC#72)
- 合唱发送(CC#93)
- 混响发送(CC#91)
- 变奏发送(CC#94)
- 弯音灵敏度(RPN 0)
- 调音(RPN 1/2)
- 当前力度
音色表系统
音色表文件结构
S-YXG50 使用两个音色数据文件:
sxgbin41.tbl:音色参数表,约 4MB。包含各种乐器的参数定义、鼓组配置、音色索引等。文件内部被分成 17 个数据段,每个段存储不同类型的数据。
sxgwave4.tbl:波形数据表,约 4MB。包含所有乐器的实际音频采样数据。这些采样是单声道的,以 22050Hz 或 44100Hz 采样率录制。文件经过简单的编码保护,加载时需要解码。
音色参数表的数据段
鼓组表(dataSeg00-03):四张鼓组表,分别对应 GS、XG、XG SFX、GM2 标准。每张表 128 个字节,每个字节对应一个音符,值是鼓组编号(用于索引鼓组数据)。
鼓组映射(dataSeg04):XG 鼓组的映射表,31 个鼓组 × 128 个音符。每个条目是 16 位,指向鼓组参数数据的位置。
鼓组默认参数(dataSeg05):每个鼓键的默认参数,包括音高偏移、电平、声像、效果发送量、滤波器设置、包络设置等。每个鼓键 30 字节。
SFX 索引表(dataSeg06):特殊音效的索引表,用于快速查找 SFX 音色。
Bank Select 表(dataSeg07-10):存储 GS 和 XG 标准的 Bank Select MSB/LSB 映射关系。当收到 Bank Select 消息时,系统通过这些表确定实际要加载的音色。
音色索引表(dataSeg11-12):GS/GM 索引表和 XG 索引表。这些表将程序编号(和可能的 Bank Select 值)映射到具体的音色参数位置。
基础音色参数表(dataSeg13):存储所有基础音色的参数,每个音色的参数大小可变。这些参数包括波形选择、音高设置、包络参数、滤波器参数等。
扩展音色参数表(dataSeg14):存储扩展音色的参数,结构与基础参数表类似。
采样参数索引(dataSeg15):将音色映射到具体的采样描述符。
采样描述符(dataSeg16):每个描述符 16 字节,描述一个采样片段的属性。
采样描述符详解
每个采样描述符包含以下信息:
力度分层:同一个音符可能有多个采样,按力度分层。比如轻按和重按会触发不同的采样,以模拟真实乐器的力度变化。
基准音高:这个采样原本录制时的音高。播放时需要根据请求的音高进行音高转换。
采样起始地址:波形数据在文件中的起始位置。使用 3 字节大端序编码。
循环结束点:循环播放的结束位置。到达这个位置后会跳回循环起始点继续播放。
采样率指示:标明这个采样是以 22050Hz 还是 44100Hz 录制的。如果采样率与输出采样率不同,播放时需要进行采样率转换。
音域范围:这个采样适用的音高范围。超出这个范围的音符会使用相邻的采样。
波形数据解码
sxgwave4.tbl 文件经过简单的编码保护。解码过程如下:
对文件中的每个字节,依次进行以下操作:
- 与当前密钥和字节索引进行异或运算
- 交换高四位和低四位
- 与固定值 0x5C 异或
- 密钥在 0x5D 和 0xA2 之间交替切换
这个解码算法在 DLL 中实现,加载波形文件时自动执行。
音色查找流程
当需要为某个音符查找音色时,系统执行以下步骤:
- 确定当前通道是旋律通道还是鼓组通道
- 如果是旋律通道,根据 Bank Select MSB、LSB 和程序编号,在索引表中查找对应的音色参数
- 如果是鼓组通道,根据鼓组编号和音符号,在鼓组映射表中查找对应的鼓键参数
- 从音色参数中获取波形索引
- 根据波形索引找到采样描述符
- 从采样描述符中获取采样起始地址、循环点等信息
- 使用这些信息初始化声音的振荡器
附录
MIDI CC 编号与功能对照
CC#0 Bank Select MSB:选择音色库的高 7 位
CC#1 Modulation:调制轮,通常用于控制颤音深度
CC#7 Volume:通道音量
CC#10 Pan:声像位置,控制声音在左右声道的分布
CC#11 Expression:表情,通常与音量配合使用
CC#64 Sustain:延音踏板,按下后音符不会在释放键时停止
CC#71 Resonance:滤波器共振,增强截止频率附近的频率
CC#72 Release:包络释放时间
CC#73 Attack:包络起音时间
CC#74 Cutoff:滤波器截止频率
CC#75 Decay:包络衰减时间
CC#91 Reverb Send:送往混响效果器的信号量
CC#93 Chorus Send:送往合唱效果器的信号量
CC#94 Variation Send:送往变奏效果器的信号量
CC#120 All Sound Off:立即停止所有声音
CC#121 Reset All:重置所有控制器到默认值
CC#123 All Notes Off:让所有正在发声的音符进入释放状态
XG 效果类型代码
混响类型(0x0100-0x0500):
- Hall 1/2:大厅混响
- Room 1/2/3:房间混响
- Stage 1/2:舞台混响
- Plate 1/2:板式混响
- GM Reverb:通用 MIDI 默认混响
合唱类型(0x4100-0x4300):
- Chorus 1/2/3/4:不同风格的合唱
- Flanger 1/2:镶边效果
- GM Chorus:通用 MIDI 默认合唱
变奏类型(0x4100-0x5D02):
- 合唱/镶边/交响:调制类效果
- 相位器:梳状滤波效果
- 旋转扬声器:Leslie 音箱模拟
- 颤音/自动声像:周期性调制
- 失真/过载:谐波失真
- 均衡器:频率调整
- 延迟/回声:时间延迟效果
- 早期反射/门混响:空间效果
- 卡拉 OK:人声消除
采样率转换原理
当波形采样率(如 22050Hz)与输出采样率(44100Hz)不同时,需要进行采样率转换。
转换使用线性插值方法:假设输入采样率是输出采样率的一半,那么每产生一个输出采样,读取位置只前进半个输入采样。当读取位置落在两个输入采样之间时,根据距离两个采样的比例计算出一个中间值。
比如读取位置在第 100 个和第 101 个输入采样之间的 0.3 位置,那么输出值 = 第100个采样 × 0.7 + 第101个采样 × 0.3。
文档版本:1.0
生成日期:2026-06-07
基于 S-YXG50.dll 逆向工程分析