VOCALOID3.exe | VOCALOID3主程序 |
DSE3.dll | VOCALOID3合成引擎 |
DSE3_DFT.dll | 字面理解是离散傅立叶变换算法,推测应该包括一系列DSE的频域算法 |
DSCL3.dll | 这个东西作用很杂,控制DSE3、控制编辑器、控制文件读写等等…… |
dbm3.dll | 用来读写歌手属性的 |
vedit3.dll | 歌手属性编辑器 |
xerces-c_3_1.dll | xml解析器 |
g2pa3_ENG.dll | 英语发音记号支持 |
g2pa3_KOR.dll | 韩语发音记号支持 |
g2pa3_ESP.dll | 西班牙语发音记号支持 |
g2pa3_CHS.dll | 汉语发音记号支持 |
g2pa3_JPN.dll | 日语发音记号支持 |
udm3_eng.dll | 英文用户词典编辑器 |
Vsq3.dll | 对VSQ、VSQX文件和数据结构的支持 |
VstHost3.dll | Vst插件管理器 |
每十进制40个字节会有一个新的罗马音的定义,这四十个字节中前32个字节用于存储罗马音(Lyric),后8个用于存储日文音源发音记号。若如下图替换成中文发音的日文记号:
保存。因为是字节对齐的,所以不用担心损坏文件。
打开V3,切到日文音源输入一个ka进去:
ka被自动转换成k h a了,而不是k a。
同理,你可以把其他拼音改掉。
不过汉语拼音的总数远大于日文罗马音,这个dll会塞不下。
g2pa3_JPN.dll搜索一个罗马音对应的发音记号的算法在偏移地址00001A20这个函数里:
上图中标出的那条指令将g2pa3_JPN.dll中存储的(发音词典的)地址载入寄存器。也就是说把这个地址改到我们自己注入的发音词典就可以实现自制发音词典。此地址对应在文件中为:
10 01 3C 30是一个偏移地址,指向dll载入内存后数据段中一个发音词典表的开始地址。
实现自定义g2pa3_JPN.dll的思路:
1. 给dll增加一个数据段。
2. dll文件大小扩展
3. 在扩展的范围内写入自定义词典数据
4. 将偏移地址更改,使其指向新的词典数据。
ddi开头先把所有发音符号罗列了一遍。
事实上如果你把其中一个发音符号改掉,比如把a改成abc,然后打开V3,输入一个abc的发音符号,她照样能把a唱出来。
你还可以看到几个呼吸声:Sil Asp br1 br2 br3 br4 br5
接下来是对各种发音记号的语音学上的分类:
从分类里整理出的表格:
Vowels | a i M e o |
Nasals | n J m N m' N' N\ |
VoicedPlosives | g g' d d' b b' |
VoicedFricatives | z Z h\ |
VoicedAffricates | dz dZ |
Liquids | 4 4' |
Semivowel | j w |
UnvoicedPlosives | k k' t t' p p' |
UnvoicedFricatives | s S h C p\ p\' |
UnvoicedAffricates | ts tS |
GlottalPlosive | 无 |
Bresathings | *in *out br5 br4 br3 br2 br1 |
Silence | Sil Asp |
SND这个标签我在miku.ddb里也看到了,这个标签代表一段时域信号的开始。
我在ddb里搜索SND,找到1664个,而ddi里找到1621个,两个数字非常接近!所以我觉得这个SND附近肯定以某种方法表明了ddb内相应时域信号的地址!但是我在ddi里找不到直接的对应ddb中SND的地址。。。期待有人能把这个坑填上。
5月30日自填坑:ddi里可以看到有很多如上图打头的部分重复,后面跟了一段这样的东西:
我怀疑这两排字存储的是ddb中的地址,于是我找了第一个:42 A7 86 05,倒序即0586A742,在ddb中这个地址对应的是:
于是我看到了一个FRM2的标签,我又把其他的数值当地址代进去,均指向不同位置的FRM2标识。因此可以推测这么一段ddi记录的是ddb中一个完整的Diphone的SND和一堆FRM2的地址。
若要反编DSE3查出这些标签究竟是何意思,这么大的工程恐怕非本人力所能及……求组队。。。
6月1日更新:
一般ddb中会连续记录一个发音记号在三个左右不同音调下的发音(FRM2 + ENV + ENV + SND)×3。ddi中由ARTp头开始的数据记录也大多是三个连续在一起,对应ddb中三个相同发音不同音高的数据记录。但是ddi和ddb中发音记号的记录顺序是不同的。下图是ddi中的一个发音记号在某个音高下的记录:
这是目前ddi中已经解析的部分
5. ddb文件
上面已经提到,ddi是ddb的索引,而且我找到了ddi和ddb的FRM2标识的对应关系。
我到目前一共在ddb中找到了三个标识:FRM2, SND, ENV。
我猜测FRM = Frame(帧),SND = Sound (声音),ENV = Envelope(包络)。仅是猜测。
(6月1日)现在猜测FRM或ENV可能记录了共振峰信息。
先抛开这些不管,来看看怎么直接打开ddb。
cxm菊苣很久以前对ddb做过一个拆包程序:
http://bbs.ivocaloid.com/thread-58004-1-1.html
这个程序只是对SND波形文件的拆包,但是他的研究提供了一些有价值的信息。
cxm的拆包程序头几行:
按照cxm的拆法的基础上纠正顺序问题,可以看出:
SIZE = 5E12 = 24082 (SND块大小)
FREQUENCY = AC44 = 44100 (SND块的采样率)
CHANNELS = 0001 = 1 (SND快为单声道)
如果那0593ECC4这个地址加上5E12,也就是size,你会落在05944AD6上,而这个地方正好是一个FRM2。
打开以后会变成这个样子:
如果你不停地放大,会看到:
在很多杂音间有时域波形!其实这些波形就是SND段。如果选中播放,你可以听见藤田咲的录音。
这些波形都是一个个diphone,即两个发音符号的过渡,也有少数是只有一个元音的。
SND总共的数量大约有1600个,一个发音一般有三个不同音高的SND。DSE通过频域算法综合多个音高进行变调。
那么分隔开这些时域波形的,就应该是FRM2和ENV,或者什么别的尚未发现的标识了。
为了了解SND FRM2 ENV的排列顺序,我用vb.net写了个程序专门搜索这些标识,为了提高速度尽量使用字节级的方式比较:
'
' Created by SharpDevelop.
' Sleepwalking
'
'
Imports System.IO
Module Program
Public DDBReader As BinaryReader = New BinaryReader(New FileStream("Z:\miku.ddb", FileMode.Open))
Public Output As StreamWriter = New StreamWriter("Z:\peek.txt")
Sub Main()
Dim pos As Integer
Dim len As Integer = CInt(DDBReader.BaseStream.Length) - 1
Dim b0, b1, b2, b3 As Byte
For pos = 0 To len
b0 = b1:b1 = b2:b2 = b3
b3 = DDBReader.ReadByte()
If b0 = 70 AndAlso b1 = 82 AndAlso b2 = 77 AndAlso b3 = 50 Then
'FRM2
Output.WriteLine("FRM2 " & pos - 3)
Else
If b0 = 83 AndAlso b1 = 78 AndAlso b2 = 68 AndAlso b3 = 32 Then
'SND
Output.WriteLine("SND " & pos - 3)
Else
If b0 = 69 AndAlso b1 = 78 AndAlso b2 = 86 AndAlso b3 = 32 Then
'ENV
Output.WriteLine("ENV " & pos - 3)
End If
End If
End If
If pos Mod 1000000 = 0 Then
Console.WriteLine(CInt(pos / len * 100) & "%")
End If
Next
Output.Close()
Console.Write("Press any key to continue . . . ")
Console.ReadKey(True)
End Sub
End Module
结果在Z盘(我的Ramdisk。。。)生成了一个peak.txt,差不多有3MB大:
由此可见每个FRM2后面跟两个ENV,然后每个SND后面有【好几十】个FRM2和【好几十×2】个ENV
ENV | 130632 |
FRM2 | 67394 |
JT2? | 65132 |
JT2; | 65131 |
JT2< | 65131 |
JT2= | 65131 |
JT2> | 65131 |
CENV | 13992 |
NV L | 5478 |
RM20 | 5474 |
NV D | 3542 |
RM2( | 3531 |
RM2 | 3394 |
NV | | 3204 |
RM2` | 3190 |
NV T | 2182 |
RM28 | 2181 |
EH?@ | 2108 |
QI?@ | 2046 |
AP&. | 1989 |
SQ?@ | 1967 |
AJT2 | 1914 |
KX?@ | 1865 |
NA?@ | 1802 |
HP?@ | 1768 |
YB?@ | 1746 |
NV < | 1699 |
VY?@ | 1692 |
SND | 1620 |
VY>@ | 1542 |
VY=@ | 1453 |
NV \ | 1262 |
RM2@ | 1252 |
NA? | 1092 |
YB? | 1082 |
NA?` | 1080 |
QI?` | 1059 |
KX? | 1053 |
VY? | 1048 |
VY?` | 1036 |
QI?U | 1033 |
KX?` | 1032 |
EH? | 1025 |
HP?` | 1024 |
EH?` | 1019 |
EH?U | 1015 |
YB?` | 1009 |
(双声道是幌子= =)
听上去是“啊!啊啊啊....”
合成出来的声音:
http://pan.baidu.com/share/link?shareid=549292&uk=3423845838
所以DSE2伸缩一个元音的方法是频域的循环过渡。
不过我倒更奇怪了,既然仅仅改SND就能把DSE骗过去,那么FRM2和ENV到底有什么用呢。。。?
DSCL3、DSE3合成时的函数调用分析
我本来怀着一丝希望能通过改DSE3解决V3的断元音功能(一丝。。。。一丝希望而已),于是去反编DSE3,结果第二天发现音源库的Diphone结构根本不可能实现断元音问题的解决,所以对DSE3的反编就停掉了。
VOCALOID.004EE550
DSCL3.CDaisyMidiBuffer::GetEndTimePosition
DSCL3.CRtSynthesis::GetDefaultSampleRate
DSCL3.CRtSynthesis::SetDseVVoiceTable
DSCL3.CDaisyMidiBuffer::objID
DSCL3.CRtSynthesis::DoSynthesis (1002F400)
DSCL3.CRtSynthesis::RTS_DeleteResidentDSE
Ret
DSCL3.CRtSynthesis::CreateDSE
DSE3.DSEGetVersion
DSCL3.CRtSynthesis::SendVVoiceTableToDse
DSE3.DSECreate
DSE3.DSESetStaticSetting
DSCL3.CRtSynthesis::DoSynthesis (1002A1E0)
DSCL3.1003B75D
1002A2A2 DSCL3.CRtSynthesis::PrepareWavFile
1002A2F5 DSCL3.CRtSynthesis::MsToSample
1002A322 DSCL3.CDaisyMidiBuffer::SeekToBegin
1002A32E DSCL3.CDaisyMidiBuffer::ReadNextEntry
1002A378 DSCL3.CRtSynthesis::RTS_GetFrameSize
1002A391 DSE3.DSEStart
1002A3EF DSCL3.CRtSynthesis::MsToSample
1002A404 DSCL3.CRtSynthesis::MsToSample
1002A4B0 DSCL3.CRtSynthesis::MsToSample
1002A4E9 DSCL3.CDaisyMidiBuffer::ReadEvent
1002A578 DSCL3.CDaisyMidiBuffer::ReadNextEntry
1002A4B0 DSCL3.CRtSynthesis::MsToSample
1002A5DB DSE3.DSEDoStepSynthesis
1002A4B0 DSCL3.CRtSynthesis::MsToSample
1002A5DB DSE3.DSEDoStepSynthesis
1002A610 DSCL3.CWaveFile::AppendSample
1002A4B0 DSCL3.CRtSynthesis::MsToSample
1002A5DB DSE3.DSEDoStepSynthesis
1002A610 DSCL3.CWaveFile::AppendSample (LOOP)
1002A4B0 DSCL3.CRtSynthesis::MsToSample
1002A5DB DSE3.DSEDoStepSynthesis
1002A610 DSCL3.CWaveFile::AppendSample
...
DSE3.DSEDelete
Vsq3.CVSVsqManagerIF::editCurrentCommitPoint
DSCL3.CSequenceObject::GetPitchBendBreakPointList
DSCL3.CSequenceObject::GetPitchBendBreakPointList
DSCL3.CRtSynthesis::MsToSample
DSCL3.CRtSynthesis::GetDefaultSampleRate
DSCL3.CRtSynthesis::GetDefaultSampleRate
DSCL3.CRtSynthesis::PrepareWavFile
Vsq3.CVSVsqManagerIF::vsqGetVsqObject
DSCL3.CRtSynthesis::ConcatenatePartWavFiles
这是DSE3每次执行DoStepSynthesis的内部调用:
DSE3.DSEDoStepSynthesis
01B617FF DSE3.01B798F0
01B799AF DSE3.01BBF010
01B799F5 DSE3.01BA9F30
01B79A1B DSE3.01B82080
01B79A36 DSE3.01B81DF0
01B79A50 DSE3.01B81E20
01B79A75 DSE3.01B79090
01B79A7D DSE3.01B70E60
01B79A94 DSE3.01B78B50
DSE3.01B787E0
DSE3.01B77170
DSE3.01BD4910
fsetpos
*DSE3.01BAC000
DSE3.01BABAC0
fread
fread
DSE3.01BC5710
DSE3.01BBF010
DSE3.01BB5BD0
DSE3.01BB5BD0
fread
fread
fread
DSE3.01BDA240
DSE3.01B6E1C0
ReadFile
DSE3.01BBF970
DSE3.01BD0290
DSE3_DFT.DftiComputeForward
DSE3.01B76D60
DSE3.01B6F2D0
DSE3.01B81E20
DSE3.01B827E0
*DSE3.01BABAB0
DSE3.01BD9F24
01B79ABC DSE3.01B784A0
01B79B19 DSE3.01B72800
01B79B2F DSE3.01B710B0
01B79B45 DSE3.01B71B70
01B79B67 DSE3.01B6E920
01B79B6F DSE3.01B70F80
01B79BCF DSE3.01B73590
DSE3.01BCFB90
DSE3_DFT.DftiComputeForward
DSE3.01BCFBE0
DSE3_DFT.DftiComputeBackward
01B79BF4 DSE3.01B827E0
01B7A044 DSE3.01BCB7D0
DSE3.01BCF670
DSE3_DFT.DftiComputeBackward
01B618A7 DSE3.01B6AAD0
01B6AB2A DSE3.01B6A160
01B618B7 DSE3.01B6AFB0
01B6AFC3 DSE3.01B6ADE0
01B6AFD8 DSE3.01B6ACF0
所以。。。累加后的地址。。。可能会让你比较伤脑筋抱歉。。。
如果有人原意填上这个巨坑就好了…………
另外在网上查了一下DftiComputeForward,发现是Intel MKL英特尔核心数学库的一个函数名。同样还有几个别的在DSE3_DFT里出现的导出名也和Intel MKL重合。Intel MKL极有可能被DSE3_DFT包括。
在某人博客上看到的摘抄作参考:http://blog.sina.com.cn/s/blog_57562d890100xy0j.html
欢迎光临 iVocaloid论坛 (http://bbs.ivocaloid.com/) | Powered by Discuz! X2 |