Sleepwalking / Rocaloid-old

OBSOLETED! Moved to http://github.com/Rocaloid
GNU General Public License v3.0
63 stars 16 forks source link

What features & bug fixes should be included in the rewritten CVE? #3

Closed Sleepwalking closed 11 years ago

Sleepwalking commented 11 years ago

We've decided to write the entire project in C++. The basic algorism won't change, still TDPSM. But currently CVE has a few bugs and we'd better fix that in the written version. Some functions are uncompleted, such as GEN factor and the factor conversion from vsqx to rsc.

There are two bugs in CVE 1.6:


The bug of period prediction.

When a transition takes place, for example, a_C3 -> o_C3, the transition is done by stretching and mixing hundreds of periods of a_C3 & o_C3 by different ratio. Let's suppose the instantaneous transition ratio is 0.5, so it should be half a_C3 and half o_C3 if it is perfect. Then there comes a problem: the transition ratio in the beginning of a period is not the same with the transition ratio in the end. Why? If don't do so, the end of the period cannot match with the start of the next period. In a mathematical way to prove it, let's suppose the instantaneous transition ratio is given by:

TR(t) = t * 0.5 (When 0 <= t <= 2)

So this is a transition of 2 seconds. Suppose there is a period begins at 1 sec, which lengths 0.01 sec. So the TR at its beginning should be exactly 0.5 and at its end, TR should be (1 + 0.01) * 0.5 = 0.505. Period Prediction is a method used in CVE 1.6 to solve the problem of different TRs at the beginning and end of periods. In fact I didn't realize this problem when I designed CVE 1.6. And Period Prediction was added after I finished the first version of CVE 1.6...

Here is the code (in PitchPreSynthesizer):

Dim TR1 As Double, TR2 As Double TR1 = PCalc.TransitionRatio SetStartMixRatio(TR1) PCalc.PitchCalc(Time + 1 / PCalc.GetFreqAt(Time)) TR2 = PCalc.TransitionRatio If TR2 > 1 Then TR2 = 1 If TR2 < TR1 Then TR2 = 1

As you can see, the EndRatio(TR2) comes from PCalc.PitchCalc(Time + 1 / PCalc.GetFreqAt(Time)), plainly add the current time with the length of the current period, but we don't know the exact length of the current period! So there would be an error of aroud 5 samples.

(I guess this is the slightest bug... 5 samples... May result in almost no change in the outputed wave...)


The bug of Pitch Calculator.

Here's an example that shows how Pitch Calculator works: When CVE shifts the pitch from C3 to E3 and back to C3, the Pitch Calculator provides these transition instructions as time increases:

Sounds perfect, but what would happen if we shrink the total time of pitch change to 0.2s? There are 8 transitions in all, so each transition can only take 0.025s, which is the length of 3 periods under C3... Such short transitions would cause a sharp decrease in quality. So I set up a limit in PCalc:

TimeResolution = 0.03

Then PCalc skips some of the transitions like this:

Then the bug comes... What would happen if you suddenly change the pitch from D3 -> a bit lower than E3 -> A2?

Pay attention to the second and third transition above. There should be a moment, when the second transition is finished, the output is at a state between D#3 and E3, and in the next moment it becomes a state between E3 and D3... You know D#3 and D3 are from different files... So a boom may occur at the intersection of two neighboring periods...

My idea is to rewrite the PCalc. When a segment is loaded, send its FreqList to the PCalc. The PCalc should calculate all pitch transitions and store them in an array before being called by the synthesizers. The pitch transitions should fit in two rules:

  1. The [start of the current transition] should be same with the [end of the last transition].
  2. The [end of the current transition] should have a small difference between the lower pitch and the higher pitch. The difference in pitch decreases the quality of synthesis.