Add code
This commit is contained in:
parent
a9ad0525e4
commit
caf33d2dc8
5 changed files with 2078 additions and 0 deletions
107
Process.cs
Normal file
107
Process.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using static SAMSharp.Sam;
|
||||
using static SAMSharp.Renderer;
|
||||
|
||||
namespace SAMSharp
|
||||
{
|
||||
class Process
|
||||
{
|
||||
static void CombineGlottalAndFormants(byte phase1, byte phase2, byte phase3, byte Y)
|
||||
{
|
||||
byte tmp;
|
||||
|
||||
tmp = multtable[sinus[phase1] | amplitude1[Y]];
|
||||
tmp += multtable[sinus[phase2] | amplitude2[Y]];
|
||||
tmp += (byte)(tmp > 255 ? 1 : 0); // if addition above overflows, we for some reason add one;
|
||||
tmp += multtable[rectangle[phase3] | amplitude3[Y]];
|
||||
tmp += 136;
|
||||
tmp >>= 4; // Scale down to 0..15 range of C64 audio.
|
||||
|
||||
Output(0, (byte)(tmp & 0xf));
|
||||
}
|
||||
|
||||
// PROCESS THE FRAMES
|
||||
//
|
||||
// In traditional vocal synthesis, the glottal pulse drives filters, which
|
||||
// are attenuated to the frequencies of the formants.
|
||||
//
|
||||
// SAM generates these formants directly with sin and rectangular waves.
|
||||
// To simulate them being driven by the glottal pulse, the waveforms are
|
||||
// reset at the beginning of each glottal pulse.
|
||||
//
|
||||
public static void ProcessFrames(byte mem48)
|
||||
{
|
||||
byte speedcounter = 72;
|
||||
byte phase1 = 0;
|
||||
byte phase2 = 0;
|
||||
byte phase3 = 0;
|
||||
byte mem66 = 0; //!! was not initialized
|
||||
|
||||
byte Y = 0;
|
||||
|
||||
byte glottal_pulse = pitches[0];
|
||||
byte mem38 = (byte)(glottal_pulse - (glottal_pulse >> 2)); // mem44 * 0.75
|
||||
|
||||
while (mem48 != 0)
|
||||
{
|
||||
byte flags = sampledConsonantFlag[Y];
|
||||
|
||||
// unvoiced sampled phoneme?
|
||||
if ((flags & 248) != 0)
|
||||
{
|
||||
RenderSample(mem66, flags, Y);
|
||||
// skip ahead two in the phoneme buffer
|
||||
Y += 2;
|
||||
mem48 -= 2;
|
||||
speedcounter = speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
CombineGlottalAndFormants(phase1, phase2, phase3, Y);
|
||||
|
||||
speedcounter--;
|
||||
if (speedcounter == 0)
|
||||
{
|
||||
Y++; //go to next amplitude
|
||||
// decrement the frame count
|
||||
mem48--;
|
||||
if (mem48 == 0) return;
|
||||
speedcounter = speed;
|
||||
}
|
||||
|
||||
--glottal_pulse;
|
||||
|
||||
if (glottal_pulse != 0)
|
||||
{
|
||||
// not finished with a glottal pulse
|
||||
|
||||
--mem38;
|
||||
// within the first 75% of the glottal pulse?
|
||||
// is the count non-zero and the sampled flag is zero?
|
||||
if ((mem38 != 0) || (flags == 0))
|
||||
{
|
||||
// reset the phase of the formants to match the pulse
|
||||
phase1 += frequency1[Y];
|
||||
phase2 += frequency2[Y];
|
||||
phase3 += frequency3[Y];
|
||||
continue;
|
||||
}
|
||||
|
||||
// voiced sampled phonemes interleave the sample with the
|
||||
// glottal pulse. The sample flag is non-zero, so render
|
||||
// the sample for the phoneme.
|
||||
RenderSample(mem66, flags, Y);
|
||||
}
|
||||
}
|
||||
|
||||
glottal_pulse = pitches[Y];
|
||||
mem38 = (byte)(glottal_pulse - (glottal_pulse >> 2)); // mem44 * 0.75
|
||||
|
||||
// reset the formant wave generators to keep them in
|
||||
// sync with the glottal pulse
|
||||
phase1 = 0;
|
||||
phase2 = 0;
|
||||
phase3 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
863
Renderer.cs
Normal file
863
Renderer.cs
Normal file
|
@ -0,0 +1,863 @@
|
|||
using static SAMSharp.Process;
|
||||
using static SAMSharp.Sam;
|
||||
using static SAMSharp.Transitions;
|
||||
|
||||
namespace SAMSharp
|
||||
{
|
||||
class Renderer
|
||||
{
|
||||
const int PHONEME_PERIOD = 1;
|
||||
const int PHONEME_QUESTION = 2;
|
||||
const int RISING_INFLECTION = 1;
|
||||
const int FALLING_INFLECTION = 255;
|
||||
|
||||
static byte[] tab48426 = { 0x18, 0x1A, 0x17, 0x17, 0x17 };
|
||||
|
||||
static byte[] tab47492 =
|
||||
{
|
||||
0 , 0 , 0xE0 , 0xE6 , 0xEC , 0xF3 , 0xF9 , 0 ,
|
||||
6 , 0xC , 6
|
||||
};
|
||||
|
||||
static byte[] amplitudeRescale =
|
||||
{
|
||||
0 , 1 , 2 , 2 , 2 , 3 , 3 , 4 ,
|
||||
4 , 5 , 6 , 8 , 9 ,0xB ,0xD ,0xF, 0 //17 elements?
|
||||
};
|
||||
|
||||
// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected.
|
||||
// tab45856
|
||||
public static byte[] blendRank =
|
||||
{
|
||||
0 , 0x1F , 0x1F , 0x1F , 0x1F , 2 , 2 , 2 ,
|
||||
2 , 2 , 2 , 2 , 2 , 2 , 5 , 5 ,
|
||||
2 ,0xA , 2 , 8 , 5 , 5 ,0xB ,0xA ,
|
||||
9 , 8 , 8 , 0xA0 , 8 , 8 , 0x17 , 0x1F ,
|
||||
0x12 , 0x12 , 0x12 , 0x12 , 0x1E , 0x1E , 0x14 , 0x14 ,
|
||||
0x14 , 0x14 , 0x17 , 0x17 , 0x1A , 0x1A , 0x1D , 0x1D ,
|
||||
2 , 2 , 2 , 2 , 2 , 2 , 0x1A , 0x1D ,
|
||||
0x1B , 0x1A , 0x1D , 0x1B , 0x1A , 0x1D , 0x1B , 0x1A ,
|
||||
0x1D , 0x1B , 0x17 , 0x1D , 0x17 , 0x17 , 0x1D , 0x17 ,
|
||||
0x17 , 0x1D , 0x17 , 0x17 , 0x1D , 0x17 , 0x17 , 0x17
|
||||
};
|
||||
|
||||
// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value
|
||||
//tab45696
|
||||
public static byte[] outBlendLength =
|
||||
{
|
||||
0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 ,
|
||||
4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,
|
||||
4 , 4 , 3 , 2 , 4 , 4 , 2 , 2 ,
|
||||
2 , 2 , 2 , 1 , 1 , 1 , 1 , 1 ,
|
||||
1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 ,
|
||||
2 , 1 , 0 , 1 , 0 , 1 , 0 , 5 ,
|
||||
5 , 5 , 5 , 5 , 4 , 4 , 2 , 0 ,
|
||||
1 , 2 , 0 , 1 , 2 , 0 , 1 , 2 ,
|
||||
0 , 1 , 2 , 0 , 2 , 2 , 0 , 1 ,
|
||||
3 , 0 , 2 , 3 , 0 , 2 , 0xA0 , 0xA0
|
||||
};
|
||||
|
||||
// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value
|
||||
// tab45776
|
||||
public static byte[] inBlendLength =
|
||||
{
|
||||
0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 ,
|
||||
4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,
|
||||
4 , 4 , 3 , 3 , 4 , 4 , 3 , 3 ,
|
||||
3 , 3 , 3 , 1 , 2 , 3 , 2 , 1 ,
|
||||
3 , 3 , 3 , 3 , 1 , 1 , 3 , 3 ,
|
||||
3 , 2 , 2 , 3 , 2 , 3 , 0 , 0 ,
|
||||
5 , 5 , 5 , 5 , 4 , 4 , 2 , 0 ,
|
||||
2 , 2 , 0 , 3 , 2 , 0 , 4 , 2 ,
|
||||
0 , 3 , 2 , 0 , 2 , 2 , 0 , 2 ,
|
||||
3 , 0 , 3 , 3 , 0 , 3 , 0xB0 , 0xA0
|
||||
};
|
||||
|
||||
// Looks like it's used as bit flags
|
||||
// High bits masked by 248 (11111000)
|
||||
//
|
||||
// 32: S* 241 11110001
|
||||
// 33: SH 226 11100010
|
||||
// 34: F* 211 11010011
|
||||
// 35: TH 187 10111011
|
||||
// 36: /H 124 01111100
|
||||
// 37: /X 149 10010101
|
||||
// 38: Z* 1 00000001
|
||||
// 39: ZH 2 00000010
|
||||
// 40: V* 3 00000011
|
||||
// 41: DH 3 00000011
|
||||
// 43: ** 114 01110010
|
||||
// 45: ** 2 00000010
|
||||
// 67: ** 27 00011011
|
||||
// 70: ** 25 00011001
|
||||
// tab45936
|
||||
public static byte[] sampledConsonantFlags =
|
||||
{
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0xF1 , 0xE2 , 0xD3 , 0xBB , 0x7C , 0x95 , 1 , 2 ,
|
||||
3 , 3 , 0 , 0x72 , 0 , 2 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0x1B , 0 , 0 , 0x19 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
|
||||
};
|
||||
|
||||
//tab45056
|
||||
static byte[] freq1data =
|
||||
{
|
||||
0x00 ,0x13 ,0x13 ,0x13 ,0x13 , 0xA , 0xE ,0x12
|
||||
, 0x18 ,0x1A ,0x16 ,0x14 ,0x10 ,0x14 , 0xE ,0x12
|
||||
, 0xE ,0x12 ,0x12 ,0x10 , 0xC , 0xE , 0xA ,0x12
|
||||
, 0xE ,0xA , 8 , 6 , 6 , 6 , 6 ,0x11
|
||||
, 6 , 6 , 6 , 6 ,0xE , 0x10 , 9 ,0xA
|
||||
, 8 ,0xA , 6 , 6 , 6 , 5 , 6 , 0
|
||||
, 0x12 , 0x1A , 0x14 , 0x1A , 0x12 ,0xC , 6 , 6
|
||||
, 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6
|
||||
, 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6
|
||||
, 6 ,0xA ,0xA , 6 , 6 , 6 , 0x2C , 0x13
|
||||
};
|
||||
|
||||
//tab451356
|
||||
static byte[] freq2data =
|
||||
{
|
||||
0x00 , 0x43 , 0x43 , 0x43 , 0x43 , 0x54 , 0x48 , 0x42 ,
|
||||
0x3E , 0x28 , 0x2C , 0x1E , 0x24 , 0x2C , 0x48 , 0x30 ,
|
||||
0x24 , 0x1E , 0x32 , 0x24 , 0x1C , 0x44 , 0x18 , 0x32 ,
|
||||
0x1E , 0x18 , 0x52 , 0x2E , 0x36 , 0x56 , 0x36 , 0x43 ,
|
||||
0x49 , 0x4F , 0x1A , 0x42 , 0x49 , 0x25 , 0x33 , 0x42 ,
|
||||
0x28 , 0x2F , 0x4F , 0x4F , 0x42 , 0x4F , 0x6E , 0x00 ,
|
||||
0x48 , 0x26 , 0x1E , 0x2A , 0x1E , 0x22 , 0x1A , 0x1A ,
|
||||
0x1A , 0x42 , 0x42 , 0x42 , 0x6E , 0x6E , 0x6E , 0x54 ,
|
||||
0x54 , 0x54 , 0x1A , 0x1A , 0x1A , 0x42 , 0x42 , 0x42 ,
|
||||
0x6D , 0x56 , 0x6D , 0x54 , 0x54 , 0x54 , 0x7F , 0x7F
|
||||
};
|
||||
|
||||
//tab45216
|
||||
static byte[] freq3data =
|
||||
{
|
||||
0x00 , 0x5B , 0x5B , 0x5B , 0x5B , 0x6E , 0x5D , 0x5B ,
|
||||
0x58 , 0x59 , 0x57 , 0x58 , 0x52 , 0x59 , 0x5D , 0x3E ,
|
||||
0x52 , 0x58 , 0x3E , 0x6E , 0x50 , 0x5D , 0x5A , 0x3C ,
|
||||
0x6E , 0x5A , 0x6E , 0x51 , 0x79 , 0x65 , 0x79 , 0x5B ,
|
||||
0x63 , 0x6A , 0x51 , 0x79 , 0x5D , 0x52 , 0x5D , 0x67 ,
|
||||
0x4C , 0x5D , 0x65 , 0x65 , 0x79 , 0x65 , 0x79 , 0x00 ,
|
||||
0x5A , 0x58 , 0x58 , 0x58 , 0x58 , 0x52 , 0x51 , 0x51 ,
|
||||
0x51 , 0x79 , 0x79 , 0x79 , 0x70 , 0x6E , 0x6E , 0x5E ,
|
||||
0x5E , 0x5E , 0x51 , 0x51 , 0x51 , 0x79 , 0x79 , 0x79 ,
|
||||
0x65 , 0x65 , 0x70 , 0x5E , 0x5E , 0x5E , 0x08 , 0x01
|
||||
};
|
||||
|
||||
static byte[] ampl1data =
|
||||
{
|
||||
0 , 0 , 0 , 0 , 0 ,0xD ,0xD ,0xE ,
|
||||
0xF ,0xF ,0xF ,0xF ,0xF ,0xC ,0xD ,0xC ,
|
||||
0xF ,0xF ,0xD ,0xD ,0xD ,0xE ,0xD ,0xC ,
|
||||
0xD ,0xD ,0xD ,0xC , 9 , 9 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 ,0xB ,0xB ,
|
||||
0xB ,0xB , 0 , 0 , 1 ,0xB , 0 , 2 ,
|
||||
0xE ,0xF ,0xF ,0xF ,0xF ,0xD , 2 , 4 ,
|
||||
0 , 2 , 4 , 0 , 1 , 4 , 0 , 1 ,
|
||||
4 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 ,0xC , 0 , 0 , 0 , 0 ,0xF ,0xF
|
||||
};
|
||||
|
||||
static byte[] ampl2data =
|
||||
{
|
||||
0 , 0 , 0 , 0 , 0 ,0xA ,0xB ,0xD ,
|
||||
0xE ,0xD ,0xC ,0xC ,0xB , 9 ,0xB ,0xB ,
|
||||
0xC ,0xC ,0xC , 8 , 8 ,0xC , 8 ,0xA ,
|
||||
8 , 8 ,0xA , 3 , 9 , 6 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 3 , 5 ,
|
||||
3 , 4 , 0 , 0 , 0 , 5 ,0xA , 2 ,
|
||||
0xE ,0xD ,0xC ,0xD ,0xC , 8 , 0 , 1 ,
|
||||
0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 ,
|
||||
1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 ,0xA , 0 , 0 ,0xA , 0 , 0 , 0
|
||||
};
|
||||
|
||||
static byte[] ampl3data =
|
||||
{
|
||||
0 , 0 , 0 , 0 , 0 , 8 , 7 , 8 ,
|
||||
8 , 1 , 1 , 0 , 1 , 0 , 7 , 5 ,
|
||||
1 , 0 , 6 , 1 , 0 , 7 , 0 , 5 ,
|
||||
1 , 0 , 8 , 0 , 0 , 3 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ,
|
||||
0 , 0 , 0 , 0 , 0 , 1 ,0xE , 1 ,
|
||||
9 , 1 , 0 , 1 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
0 , 7 , 0 , 0 , 5 , 0 , 0x13 , 0x10
|
||||
};
|
||||
|
||||
//tab42240
|
||||
public static byte[] sinus =
|
||||
{
|
||||
0x00 , 0x00 , 0x00 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 ,
|
||||
0x10 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x30 ,
|
||||
0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x40 , 0x40 ,
|
||||
0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x50 , 0x50 , 0x50 ,
|
||||
0x50 , 0x50 , 0x50 , 0x50 , 0x50 , 0x60 , 0x60 , 0x60 ,
|
||||
0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 ,
|
||||
0x60 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 ,
|
||||
0x60 , 0x60 , 0x60 , 0x60 , 0x50 , 0x50 , 0x50 , 0x50 ,
|
||||
0x50 , 0x50 , 0x50 , 0x50 , 0x40 , 0x40 , 0x40 , 0x40 ,
|
||||
0x40 , 0x40 , 0x40 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 ,
|
||||
0x30 , 0x30 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 ,
|
||||
0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x00 , 0x00 ,
|
||||
0x00 , 0x00 , 0x00 , 0xF0 , 0xF0 , 0xF0 , 0xF0 , 0xF0 ,
|
||||
0xF0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xD0 ,
|
||||
0xD0 , 0xD0 , 0xD0 , 0xD0 , 0xD0 , 0xD0 , 0xC0 , 0xC0 ,
|
||||
0xC0 , 0xC0 , 0xC0 , 0xC0 , 0xC0 , 0xB0 , 0xB0 , 0xB0 ,
|
||||
0xB0 , 0xB0 , 0xB0 , 0xB0 , 0xB0 , 0xA0 , 0xA0 , 0xA0 ,
|
||||
0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 ,
|
||||
0xA0 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xA0 ,
|
||||
0xA0 , 0xA0 , 0xA0 , 0xA0 , 0xB0 , 0xB0 , 0xB0 , 0xB0 ,
|
||||
0xB0 , 0xB0 , 0xB0 , 0xB0 , 0xC0 , 0xC0 , 0xC0 , 0xC0 ,
|
||||
0xC0 , 0xC0 , 0xC0 , 0xD0 , 0xD0 , 0xD0 , 0xD0 , 0xD0 ,
|
||||
0xD0 , 0xD0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 ,
|
||||
0xF0 , 0xF0 , 0xF0 , 0xF0 , 0xF0 , 0xF0 , 0x00 , 0x00
|
||||
};
|
||||
|
||||
//tab42496
|
||||
public static byte[] rectangle =
|
||||
{
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 ,
|
||||
0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70
|
||||
};
|
||||
|
||||
|
||||
//tab42752
|
||||
public static byte[] multtable =
|
||||
{
|
||||
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
|
||||
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
|
||||
0x00 , 0x00 , 0x01 , 0x01 , 0x02 , 0x02 , 0x03 , 0x03 ,
|
||||
0x04 , 0x04 , 0x05 , 0x05 , 0x06 , 0x06 , 0x07 , 0x07 ,
|
||||
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
|
||||
0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
|
||||
0x00 , 0x01 , 0x03 , 0x04 , 0x06 , 0x07 , 0x09 , 0x0A ,
|
||||
0x0C , 0x0D , 0x0F , 0x10 , 0x12 , 0x13 , 0x15 , 0x16 ,
|
||||
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0E ,
|
||||
0x10 , 0x12 , 0x14 , 0x16 , 0x18 , 0x1A , 0x1C , 0x1E ,
|
||||
0x00 , 0x02 , 0x05 , 0x07 , 0x0A , 0x0C , 0x0F , 0x11 ,
|
||||
0x14 , 0x16 , 0x19 , 0x1B , 0x1E , 0x20 , 0x23 , 0x25 ,
|
||||
0x00 , 0x03 , 0x06 , 0x09 , 0x0C , 0x0F , 0x12 , 0x15 ,
|
||||
0x18 , 0x1B , 0x1E , 0x21 , 0x24 , 0x27 , 0x2A , 0x2D ,
|
||||
0x00 , 0x03 , 0x07 , 0x0A , 0x0E , 0x11 , 0x15 , 0x18 ,
|
||||
0x1C , 0x1F , 0x23 , 0x26 , 0x2A , 0x2D , 0x31 , 0x34 ,
|
||||
0x00 , 0xFC , 0xF8 , 0xF4 , 0xF0 , 0xEC , 0xE8 , 0xE4 ,
|
||||
0xE0 , 0xDC , 0xD8 , 0xD4 , 0xD0 , 0xCC , 0xC8 , 0xC4 ,
|
||||
0x00 , 0xFC , 0xF9 , 0xF5 , 0xF2 , 0xEE , 0xEB , 0xE7 ,
|
||||
0xE4 , 0xE0 , 0xDD , 0xD9 , 0xD6 , 0xD2 , 0xCF , 0xCB ,
|
||||
0x00 , 0xFD , 0xFA , 0xF7 , 0xF4 , 0xF1 , 0xEE , 0xEB ,
|
||||
0xE8 , 0xE5 , 0xE2 , 0xDF , 0xDC , 0xD9 , 0xD6 , 0xD3 ,
|
||||
0x00 , 0xFD , 0xFB , 0xF8 , 0xF6 , 0xF3 , 0xF1 , 0xEE ,
|
||||
0xEC , 0xE9 , 0xE7 , 0xE4 , 0xE2 , 0xDF , 0xDD , 0xDA ,
|
||||
0x00 , 0xFE , 0xFC , 0xFA , 0xF8 , 0xF6 , 0xF4 , 0xF2 ,
|
||||
0xF0 , 0xEE , 0xEC , 0xEA , 0xE8 , 0xE6 , 0xE4 , 0xE2 ,
|
||||
0x00 , 0xFE , 0xFD , 0xFB , 0xFA , 0xF8 , 0xF7 , 0xF5 ,
|
||||
0xF4 , 0xF2 , 0xF1 , 0xEF , 0xEE , 0xEC , 0xEB , 0xE9 ,
|
||||
0x00 , 0xFF , 0xFE , 0xFD , 0xFC , 0xFB , 0xFA , 0xF9 ,
|
||||
0xF8 , 0xF7 , 0xF6 , 0xF5 , 0xF4 , 0xF3 , 0xF2 , 0xF1 ,
|
||||
0x00 , 0xFF , 0xFF , 0xFE , 0xFE , 0xFD , 0xFD , 0xFC ,
|
||||
0xFC , 0xFB , 0xFB , 0xFA , 0xFA , 0xF9 , 0xF9 , 0xF8
|
||||
};
|
||||
|
||||
//random data ?
|
||||
static byte[] sampleTable =
|
||||
{
|
||||
//00
|
||||
|
||||
0x38 , 0x84 , 0x6B , 0x19 , 0xC6 , 0x63 , 0x18 , 0x86
|
||||
, 0x73 , 0x98 , 0xC6 , 0xB1 , 0x1C , 0xCA , 0x31 , 0x8C
|
||||
, 0xC7 , 0x31 , 0x88 , 0xC2 , 0x30 , 0x98 , 0x46 , 0x31
|
||||
, 0x18 , 0xC6 , 0x35 ,0xC , 0xCA , 0x31 ,0xC , 0xC6
|
||||
//20
|
||||
, 0x21 , 0x10 , 0x24 , 0x69 , 0x12 , 0xC2 , 0x31 , 0x14
|
||||
, 0xC4 , 0x71 , 8 , 0x4A , 0x22 , 0x49 , 0xAB , 0x6A
|
||||
, 0xA8 , 0xAC , 0x49 , 0x51 , 0x32 , 0xD5 , 0x52 , 0x88
|
||||
, 0x93 , 0x6C , 0x94 , 0x22 , 0x15 , 0x54 , 0xD2 , 0x25
|
||||
//40
|
||||
, 0x96 , 0xD4 , 0x50 , 0xA5 , 0x46 , 0x21 , 8 , 0x85
|
||||
, 0x6B , 0x18 , 0xC4 , 0x63 , 0x10 , 0xCE , 0x6B , 0x18
|
||||
, 0x8C , 0x71 , 0x19 , 0x8C , 0x63 , 0x35 ,0xC , 0xC6
|
||||
, 0x33 , 0x99 , 0xCC , 0x6C , 0xB5 , 0x4E , 0xA2 , 0x99
|
||||
//60
|
||||
, 0x46 , 0x21 , 0x28 , 0x82 , 0x95 , 0x2E , 0xE3 , 0x30
|
||||
, 0x9C , 0xC5 , 0x30 , 0x9C , 0xA2 , 0xB1 , 0x9C , 0x67
|
||||
, 0x31 , 0x88 , 0x66 , 0x59 , 0x2C , 0x53 , 0x18 , 0x84
|
||||
, 0x67 , 0x50 , 0xCA , 0xE3 ,0xA , 0xAC , 0xAB , 0x30
|
||||
//80
|
||||
, 0xAC , 0x62 , 0x30 , 0x8C , 0x63 , 0x10 , 0x94 , 0x62
|
||||
, 0xB1 , 0x8C , 0x82 , 0x28 , 0x96 , 0x33 , 0x98 , 0xD6
|
||||
, 0xB5 , 0x4C , 0x62 , 0x29 , 0xA5 , 0x4A , 0xB5 , 0x9C
|
||||
, 0xC6 , 0x31 , 0x14 , 0xD6 , 0x38 , 0x9C , 0x4B , 0xB4
|
||||
//A0
|
||||
, 0x86 , 0x65 , 0x18 , 0xAE , 0x67 , 0x1C , 0xA6 , 0x63
|
||||
, 0x19 , 0x96 , 0x23 , 0x19 , 0x84 , 0x13 , 8 , 0xA6
|
||||
, 0x52 , 0xAC , 0xCA , 0x22 , 0x89 , 0x6E , 0xAB , 0x19
|
||||
, 0x8C , 0x62 , 0x34 , 0xC4 , 0x62 , 0x19 , 0x86 , 0x63
|
||||
//C0
|
||||
, 0x18 , 0xC4 , 0x23 , 0x58 , 0xD6 , 0xA3 , 0x50 , 0x42
|
||||
, 0x54 , 0x4A , 0xAD , 0x4A , 0x25 , 0x11 , 0x6B , 0x64
|
||||
, 0x89 , 0x4A , 0x63 , 0x39 , 0x8A , 0x23 , 0x31 , 0x2A
|
||||
, 0xEA , 0xA2 , 0xA9 , 0x44 , 0xC5 , 0x12 , 0xCD , 0x42
|
||||
//E0
|
||||
, 0x34 , 0x8C , 0x62 , 0x18 , 0x8C , 0x63 , 0x11 , 0x48
|
||||
, 0x66 , 0x31 , 0x9D , 0x44 , 0x33 , 0x1D , 0x46 , 0x31
|
||||
, 0x9C , 0xC6 , 0xB1 ,0xC , 0xCD , 0x32 , 0x88 , 0xC4
|
||||
, 0x73 , 0x18 , 0x86 , 0x73 , 8 , 0xD6 , 0x63 , 0x58
|
||||
//100
|
||||
, 7 , 0x81 , 0xE0 , 0xF0 , 0x3C , 7 , 0x87 , 0x90
|
||||
, 0x3C , 0x7C ,0xF , 0xC7 , 0xC0 , 0xC0 , 0xF0 , 0x7C
|
||||
, 0x1E , 7 , 0x80 , 0x80 , 0 , 0x1C , 0x78 , 0x70
|
||||
, 0xF1 , 0xC7 , 0x1F , 0xC0 ,0xC , 0xFE , 0x1C , 0x1F
|
||||
//120
|
||||
, 0x1F ,0xE ,0xA , 0x7A , 0xC0 , 0x71 , 0xF2 , 0x83
|
||||
, 0x8F , 3 ,0xF ,0xF ,0xC , 0 , 0x79 , 0xF8
|
||||
, 0x61 , 0xE0 , 0x43 ,0xF , 0x83 , 0xE7 , 0x18 , 0xF9
|
||||
, 0xC1 , 0x13 , 0xDA , 0xE9 , 0x63 , 0x8F ,0xF , 0x83
|
||||
//140
|
||||
, 0x83 , 0x87 , 0xC3 , 0x1F , 0x3C , 0x70 , 0xF0 , 0xE1
|
||||
, 0xE1 , 0xE3 , 0x87 , 0xB8 , 0x71 ,0xE , 0x20 , 0xE3
|
||||
, 0x8D , 0x48 , 0x78 , 0x1C , 0x93 , 0x87 , 0x30 , 0xE1
|
||||
, 0xC1 , 0xC1 , 0xE4 , 0x78 , 0x21 , 0x83 , 0x83 , 0xC3
|
||||
//160
|
||||
, 0x87 , 6 , 0x39 , 0xE5 , 0xC3 , 0x87 , 7 ,0xE
|
||||
, 0x1C , 0x1C , 0x70 , 0xF4 , 0x71 , 0x9C , 0x60 , 0x36
|
||||
, 0x32 , 0xC3 , 0x1E , 0x3C , 0xF3 , 0x8F ,0xE , 0x3C
|
||||
, 0x70 , 0xE3 , 0xC7 , 0x8F ,0xF ,0xF ,0xE , 0x3C
|
||||
//180
|
||||
, 0x78 , 0xF0 , 0xE3 , 0x87 , 6 , 0xF0 , 0xE3 , 7
|
||||
, 0xC1 , 0x99 , 0x87 ,0xF , 0x18 , 0x78 , 0x70 , 0x70
|
||||
, 0xFC , 0xF3 , 0x10 , 0xB1 , 0x8C , 0x8C , 0x31 , 0x7C
|
||||
, 0x70 , 0xE1 , 0x86 , 0x3C , 0x64 , 0x6C , 0xB0 , 0xE1
|
||||
//1A0
|
||||
, 0xE3 ,0xF , 0x23 , 0x8F ,0xF , 0x1E , 0x3E , 0x38
|
||||
, 0x3C , 0x38 , 0x7B , 0x8F , 7 ,0xE , 0x3C , 0xF4
|
||||
, 0x17 , 0x1E , 0x3C , 0x78 , 0xF2 , 0x9E , 0x72 , 0x49
|
||||
, 0xE3 , 0x25 , 0x36 , 0x38 , 0x58 , 0x39 , 0xE2 , 0xDE
|
||||
//1C0
|
||||
, 0x3C , 0x78 , 0x78 , 0xE1 , 0xC7 , 0x61 , 0xE1 , 0xE1
|
||||
, 0xB0 , 0xF0 , 0xF0 , 0xC3 , 0xC7 ,0xE , 0x38 , 0xC0
|
||||
, 0xF0 , 0xCE , 0x73 , 0x73 , 0x18 , 0x34 , 0xB0 , 0xE1
|
||||
, 0xC7 , 0x8E , 0x1C , 0x3C , 0xF8 , 0x38 , 0xF0 , 0xE1
|
||||
//1E0
|
||||
, 0xC1 , 0x8B , 0x86 , 0x8F , 0x1C , 0x78 , 0x70 , 0xF0
|
||||
, 0x78 , 0xAC , 0xB1 , 0x8F , 0x39 , 0x31 , 0xDB , 0x38
|
||||
, 0x61 , 0xC3 ,0xE ,0xE , 0x38 , 0x78 , 0x73 , 0x17
|
||||
, 0x1E , 0x39 , 0x1E , 0x38 , 0x64 , 0xE1 , 0xF1 , 0xC1
|
||||
//200
|
||||
, 0x4E ,0xF , 0x40 , 0xA2 , 2 , 0xC5 , 0x8F , 0x81
|
||||
, 0xA1 , 0xFC , 0x12 , 8 , 0x64 , 0xE0 , 0x3C , 0x22
|
||||
, 0xE0 , 0x45 , 7 , 0x8E ,0xC , 0x32 , 0x90 , 0xF0
|
||||
, 0x1F , 0x20 , 0x49 , 0xE0 , 0xF8 ,0xC , 0x60 , 0xF0
|
||||
//220
|
||||
, 0x17 , 0x1A , 0x41 , 0xAA , 0xA4 , 0xD0 , 0x8D , 0x12
|
||||
, 0x82 , 0x1E , 0x1E , 3 , 0xF8 , 0x3E , 3 ,0xC
|
||||
, 0x73 , 0x80 , 0x70 , 0x44 , 0x26 , 3 , 0x24 , 0xE1
|
||||
, 0x3E , 4 , 0x4E , 4 , 0x1C , 0xC1 , 9 , 0xCC
|
||||
//240
|
||||
, 0x9E , 0x90 , 0x21 , 7 , 0x90 , 0x43 , 0x64 , 0xC0
|
||||
, 0xF , 0xC6 , 0x90 , 0x9C , 0xC1 , 0x5B , 3 , 0xE2
|
||||
, 0x1D , 0x81 , 0xE0 , 0x5E , 0x1D , 3 , 0x84 , 0xB8
|
||||
, 0x2C ,0xF , 0x80 , 0xB1 , 0x83 , 0xE0 , 0x30 , 0x41
|
||||
//260
|
||||
, 0x1E , 0x43 , 0x89 , 0x83 , 0x50 , 0xFC , 0x24 , 0x2E
|
||||
, 0x13 , 0x83 , 0xF1 , 0x7C , 0x4C , 0x2C , 0xC9 ,0xD
|
||||
, 0x83 , 0xB0 , 0xB5 , 0x82 , 0xE4 , 0xE8 , 6 , 0x9C
|
||||
, 7 , 0xA0 , 0x99 , 0x1D , 7 , 0x3E , 0x82 , 0x8F
|
||||
//280
|
||||
, 0x70 , 0x30 , 0x74 , 0x40 , 0xCA , 0x10 , 0xE4 , 0xE8
|
||||
, 0xF , 0x92 , 0x14 , 0x3F , 6 , 0xF8 , 0x84 , 0x88
|
||||
, 0x43 , 0x81 ,0xA , 0x34 , 0x39 , 0x41 , 0xC6 , 0xE3
|
||||
, 0x1C , 0x47 , 3 , 0xB0 , 0xB8 , 0x13 ,0xA , 0xC2
|
||||
//2A0
|
||||
, 0x64 , 0xF8 , 0x18 , 0xF9 , 0x60 , 0xB3 , 0xC0 , 0x65
|
||||
, 0x20 , 0x60 , 0xA6 , 0x8C , 0xC3 , 0x81 , 0x20 , 0x30
|
||||
, 0x26 , 0x1E , 0x1C , 0x38 , 0xD3 , 1 , 0xB0 , 0x26
|
||||
, 0x40 , 0xF4 ,0xB , 0xC3 , 0x42 , 0x1F , 0x85 , 0x32
|
||||
//2C0
|
||||
, 0x26 , 0x60 , 0x40 , 0xC9 , 0xCB , 1 , 0xEC , 0x11
|
||||
, 0x28 , 0x40 , 0xFA , 4 , 0x34 , 0xE0 , 0x70 , 0x4C
|
||||
, 0x8C , 0x1D , 7 , 0x69 , 3 , 0x16 , 0xC8 , 4
|
||||
, 0x23 , 0xE8 , 0xC6 , 0x9A ,0xB , 0x1A , 3 , 0xE0
|
||||
//2E0
|
||||
, 0x76 , 6 , 5 , 0xCF , 0x1E , 0xBC , 0x58 , 0x31
|
||||
, 0x71 , 0x66 , 0 , 0xF8 , 0x3F , 4 , 0xFC ,0xC
|
||||
, 0x74 , 0x27 , 0x8A , 0x80 , 0x71 , 0xC2 , 0x3A , 0x26
|
||||
, 6 , 0xC0 , 0x1F , 5 ,0xF , 0x98 , 0x40 , 0xAE
|
||||
//300
|
||||
, 1 , 0x7F , 0xC0 , 7 , 0xFF , 0 ,0xE , 0xFE
|
||||
, 0 , 3 , 0xDF , 0x80 , 3 , 0xEF , 0x80 , 0x1B
|
||||
, 0xF1 , 0xC2 , 0 , 0xE7 , 0xE0 , 0x18 , 0xFC , 0xE0
|
||||
, 0x21 , 0xFC , 0x80 , 0x3C , 0xFC , 0x40 ,0xE , 0x7E
|
||||
//320
|
||||
, 0 , 0x3F , 0x3E , 0 ,0xF , 0xFE , 0 , 0x1F
|
||||
, 0xFF , 0 , 0x3E , 0xF0 , 7 , 0xFC , 0 , 0x7E
|
||||
, 0x10 , 0x3F , 0xFF , 0 , 0x3F , 0x38 ,0xE , 0x7C
|
||||
, 1 , 0x87 ,0xC , 0xFC , 0xC7 , 0 , 0x3E , 4
|
||||
//340
|
||||
, 0xF , 0x3E , 0x1F ,0xF ,0xF , 0x1F ,0xF , 2
|
||||
, 0x83 , 0x87 , 0xCF , 3 , 0x87 ,0xF , 0x3F , 0xC0
|
||||
, 7 , 0x9E , 0x60 , 0x3F , 0xC0 , 3 , 0xFE , 0
|
||||
, 0x3F , 0xE0 , 0x77 , 0xE1 , 0xC0 , 0xFE , 0xE0 , 0xC3
|
||||
//360
|
||||
, 0xE0 , 1 , 0xDF , 0xF8 , 3 , 7 , 0 , 0x7E
|
||||
, 0x70 , 0 , 0x7C , 0x38 , 0x18 , 0xFE ,0xC , 0x1E
|
||||
, 0x78 , 0x1C , 0x7C , 0x3E ,0xE , 0x1F , 0x1E , 0x1E
|
||||
, 0x3E , 0 , 0x7F , 0x83 , 7 , 0xDB , 0x87 , 0x83
|
||||
//380
|
||||
, 7 , 0xC7 , 7 , 0x10 , 0x71 , 0xFF , 0 , 0x3F
|
||||
, 0xE2 , 1 , 0xE0 , 0xC1 , 0xC3 , 0xE1 , 0 , 0x7F
|
||||
, 0xC0 , 5 , 0xF0 , 0x20 , 0xF8 , 0xF0 , 0x70 , 0xFE
|
||||
, 0x78 , 0x79 , 0xF8 , 2 , 0x3F ,0xC , 0x8F , 3
|
||||
//3a0
|
||||
, 0xF , 0x9F , 0xE0 , 0xC1 , 0xC7 , 0x87 , 3 , 0xC3
|
||||
, 0xC3 , 0xB0 , 0xE1 , 0xE1 , 0xC1 , 0xE3 , 0xE0 , 0x71
|
||||
, 0xF0 , 0 , 0xFC , 0x70 , 0x7C ,0xC , 0x3E , 0x38
|
||||
, 0xE , 0x1C , 0x70 , 0xC3 , 0xC7 , 3 , 0x81 , 0xC1
|
||||
//3c0
|
||||
, 0xC7 , 0xE7 , 0 ,0xF , 0xC7 , 0x87 , 0x19 , 9
|
||||
, 0xEF , 0xC4 , 0x33 , 0xE0 , 0xC1 , 0xFC , 0xF8 , 0x70
|
||||
, 0xF0 , 0x78 , 0xF8 , 0xF0 , 0x61 , 0xC7 , 0 , 0x1F
|
||||
, 0xF8 , 1 , 0x7C , 0xF8 , 0xF0 , 0x78 , 0x70 , 0x3C
|
||||
//3e0
|
||||
, 0x7C , 0xCE ,0xE , 0x21 , 0x83 , 0xCF , 8 , 7
|
||||
, 0x8F , 8 , 0xC1 , 0x87 , 0x8F , 0x80 , 0xC7 , 0xE3
|
||||
, 0 , 7 , 0xF8 , 0xE0 , 0xEF , 0 , 0x39 , 0xF7
|
||||
, 0x80 ,0xE , 0xF8 , 0xE1 , 0xE3 , 0xF8 , 0x21 , 0x9F
|
||||
//400
|
||||
, 0xC0 , 0xFF , 3 , 0xF8 , 7 , 0xC0 , 0x1F , 0xF8
|
||||
, 0xC4 , 4 , 0xFC , 0xC4 , 0xC1 , 0xBC , 0x87 , 0xF0
|
||||
, 0xF , 0xC0 , 0x7F , 5 , 0xE0 , 0x25 , 0xEC , 0xC0
|
||||
, 0x3E , 0x84 , 0x47 , 0xF0 , 0x8E , 3 , 0xF8 , 3
|
||||
//420
|
||||
, 0xFB , 0xC0 , 0x19 , 0xF8 , 7 , 0x9C ,0xC , 0x17
|
||||
, 0xF8 , 7 , 0xE0 , 0x1F , 0xA1 , 0xFC ,0xF , 0xFC
|
||||
, 1 , 0xF0 , 0x3F , 0 , 0xFE , 3 , 0xF0 , 0x1F
|
||||
, 0 , 0xFD , 0 , 0xFF , 0x88 ,0xD , 0xF9 , 1
|
||||
//440
|
||||
, 0xFF , 0 , 0x70 , 7 , 0xC0 , 0x3E , 0x42 , 0xF3
|
||||
, 0xD , 0xC4 , 0x7F , 0x80 , 0xFC , 7 , 0xF0 , 0x5E
|
||||
, 0xC0 , 0x3F , 0 , 0x78 , 0x3F , 0x81 , 0xFF , 1
|
||||
, 0xF8 , 1 , 0xC3 , 0xE8 ,0xC , 0xE4 , 0x64 , 0x8F
|
||||
////460
|
||||
, 0xE4 ,0xF , 0xF0 , 7 , 0xF0 , 0xC2 , 0x1F , 0
|
||||
, 0x7F , 0xC0 , 0x6F , 0x80 , 0x7E , 3 , 0xF8 , 7
|
||||
, 0xF0 , 0x3F , 0xC0 , 0x78 ,0xF , 0x82 , 7 , 0xFE
|
||||
, 0x22 , 0x77 , 0x70 , 2 , 0x76 , 3 , 0xFE , 0
|
||||
//480
|
||||
, 0xFE , 0x67 , 0 , 0x7C , 0xC7 , 0xF1 , 0x8E , 0xC6
|
||||
, 0x3B , 0xE0 , 0x3F , 0x84 , 0xF3 , 0x19 , 0xD8 , 3
|
||||
, 0x99 , 0xFC , 9 , 0xB8 ,0xF , 0xF8 , 0 , 0x9D
|
||||
, 0x24 , 0x61 , 0xF9 ,0xD , 0 , 0xFD , 3 , 0xF0
|
||||
//4a0
|
||||
, 0x1F , 0x90 , 0x3F , 1 , 0xF8 , 0x1F , 0xD0 ,0xF
|
||||
, 0xF8 , 0x37 , 1 , 0xF8 , 7 , 0xF0 ,0xF , 0xC0
|
||||
, 0x3F , 0 , 0xFE , 3 , 0xF8 ,0xF , 0xC0 , 0x3F
|
||||
, 0 , 0xFA , 3 , 0xF0 ,0xF , 0x80 , 0xFF , 1
|
||||
//4c0
|
||||
, 0xB8 , 7 , 0xF0 , 1 , 0xFC , 1 , 0xBC , 0x80
|
||||
, 0x13 , 0x1E , 0 , 0x7F , 0xE1 , 0x40 , 0x7F , 0xA0
|
||||
, 0x7F , 0xB0 , 0 , 0x3F , 0xC0 , 0x1F , 0xC0 , 0x38
|
||||
, 0xF , 0xF0 , 0x1F , 0x80 , 0xFF , 1 , 0xFC , 3
|
||||
//4e0
|
||||
, 0xF1 , 0x7E , 1 , 0xFE , 1 , 0xF0 , 0xFF , 0
|
||||
, 0x7F , 0xC0 , 0x1D , 7 , 0xF0 ,0xF , 0xC0 , 0x7E
|
||||
, 6 , 0xE0 , 7 , 0xE0 ,0xF , 0xF8 , 6 , 0xC1
|
||||
, 0xFE , 1 , 0xFC , 3 , 0xE0 ,0xF , 0 , 0xFC
|
||||
};
|
||||
|
||||
public static byte[] pitches = new byte[256]; // tab43008
|
||||
|
||||
public static byte[] frequency1 = new byte[256];
|
||||
public static byte[] frequency2 = new byte[256];
|
||||
public static byte[] frequency3 = new byte[256];
|
||||
|
||||
public static byte[] amplitude1 = new byte[256];
|
||||
public static byte[] amplitude2 = new byte[256];
|
||||
public static byte[] amplitude3 = new byte[256];
|
||||
|
||||
public static byte[] sampledConsonantFlag = new byte[256]; // tab44800
|
||||
|
||||
//return = hibyte(mem39212*mem39213) << 1
|
||||
static byte trans(byte a, byte b) => (byte)((((uint)a * b) >> 8) << 1);
|
||||
|
||||
//timetable for more accurate c64 simulation
|
||||
static readonly int[,] timetable =
|
||||
{
|
||||
{162, 167, 167, 127, 128},
|
||||
{226, 60, 60, 0, 0},
|
||||
{225, 60, 59, 0, 0},
|
||||
{200, 0, 0, 54, 55},
|
||||
{199, 0, 0, 54, 54}
|
||||
};
|
||||
|
||||
public static void Output(int index, byte A)
|
||||
{
|
||||
uint oldtimetableindex = 0;
|
||||
int k;
|
||||
bufferpos += timetable[oldtimetableindex,index];
|
||||
oldtimetableindex = (uint)index;
|
||||
// write a little bit in advance
|
||||
for (k = 0; k < 5; k++)
|
||||
buffer[bufferpos / 50 + k] = (byte)((A & 15) * 16);
|
||||
}
|
||||
|
||||
static byte RenderVoicedSample(ushort hi, byte off, byte phase1)
|
||||
{
|
||||
do
|
||||
{
|
||||
byte bit = 8;
|
||||
byte sample = sampleTable[hi + off];
|
||||
do
|
||||
{
|
||||
if ((sample & 128) != 0) Output(3, 26);
|
||||
else Output(4, 6);
|
||||
sample <<= 1;
|
||||
} while (--bit != 0);
|
||||
off++;
|
||||
} while (++phase1 != 0);
|
||||
return off;
|
||||
}
|
||||
|
||||
static void RenderUnvoicedSample(ushort hi, byte off, byte mem53)
|
||||
{
|
||||
do
|
||||
{
|
||||
byte bit = 8;
|
||||
byte sample = sampleTable[hi + off];
|
||||
do
|
||||
{
|
||||
if ((sample & 128) != 0) Output(2, 5);
|
||||
else Output(1, mem53);
|
||||
sample <<= 1;
|
||||
} while (--bit != 0);
|
||||
} while (++off != 0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
//Code48227
|
||||
// Render a sampled sound from the sampleTable.
|
||||
//
|
||||
// Phoneme Sample Start Sample End
|
||||
// 32: S* 15 255
|
||||
// 33: SH 257 511
|
||||
// 34: F* 559 767
|
||||
// 35: TH 583 767
|
||||
// 36: /H 903 1023
|
||||
// 37: /X 1135 1279
|
||||
// 38: Z* 84 119
|
||||
// 39: ZH 340 375
|
||||
// 40: V* 596 639
|
||||
// 41: DH 596 631
|
||||
//
|
||||
// 42: CH
|
||||
// 43: ** 399 511
|
||||
//
|
||||
// 44: J*
|
||||
// 45: ** 257 276
|
||||
// 46: **
|
||||
//
|
||||
// 66: P*
|
||||
// 67: ** 743 767
|
||||
// 68: **
|
||||
//
|
||||
// 69: T*
|
||||
// 70: ** 231 255
|
||||
// 71: **
|
||||
//
|
||||
// The SampledPhonemesTable[] holds flags indicating if a phoneme is
|
||||
// voiced or not. If the upper 5 bits are zero, the sample is voiced.
|
||||
//
|
||||
// Samples in the sampleTable are compressed, with bits being converted to
|
||||
// bytes from high bit to low, as follows:
|
||||
//
|
||||
// unvoiced 0 bit -> X
|
||||
// unvoiced 1 bit -> 5
|
||||
//
|
||||
// voiced 0 bit -> 6
|
||||
// voiced 1 bit -> 24
|
||||
//
|
||||
// Where X is a value from the table:
|
||||
//
|
||||
// { 0x18, 0x1A, 0x17, 0x17, 0x17 };
|
||||
//
|
||||
// The index into this table is determined by masking off the lower
|
||||
// 3 bits from the SampledPhonemesTable:
|
||||
//
|
||||
// index = (SampledPhonemesTable[i] & 7) - 1;
|
||||
//
|
||||
// For voices samples, samples are interleaved between voiced output.
|
||||
|
||||
|
||||
public static void RenderSample(byte mem66, byte consonantFlag, byte mem49)
|
||||
{
|
||||
// mem49 == current phoneme's index
|
||||
|
||||
// mask low three bits and subtract 1 get value to
|
||||
// convert 0 bits on unvoiced samples.
|
||||
byte hibyte = (byte)((consonantFlag & 7) - 1);
|
||||
|
||||
// determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 }
|
||||
// T, S, Z 0 0x18
|
||||
// CH, J, SH, ZH 1 0x1A
|
||||
// P, F*, V, TH, DH 2 0x17
|
||||
// /H 3 0x17
|
||||
// /X 4 0x17
|
||||
|
||||
ushort hi = (ushort)(hibyte * 256);
|
||||
// voiced sample?
|
||||
byte pitchl = (byte)(consonantFlag & 248);
|
||||
if (pitchl == 0)
|
||||
{
|
||||
// voiced phoneme: Z*, ZH, V*, DH
|
||||
pitchl = (byte)(pitches[mem49] >> 4);
|
||||
mem66 = RenderVoicedSample(hi, mem66, (byte)(pitchl ^ 255));
|
||||
}
|
||||
else
|
||||
RenderUnvoicedSample(hi, (byte)(pitchl ^ 255), tab48426[hibyte]);
|
||||
}
|
||||
|
||||
// CREATE FRAMES
|
||||
//
|
||||
// The length parameter in the list corresponds to the number of frames
|
||||
// to expand the phoneme to. Each frame represents 10 milliseconds of time.
|
||||
// So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration.
|
||||
//
|
||||
// The parameters are copied from the phoneme to the frame verbatim.
|
||||
//
|
||||
static void CreateFrames()
|
||||
{
|
||||
byte X = 0;
|
||||
uint i = 0;
|
||||
while (i < 256)
|
||||
{
|
||||
// get the phoneme at the index
|
||||
byte phoneme = phonemeIndexOutput[i];
|
||||
byte phase1;
|
||||
uint phase2;
|
||||
|
||||
// if terminal phoneme, exit the loop
|
||||
if (phoneme == 255) break;
|
||||
|
||||
if (phoneme == PHONEME_PERIOD) AddInflection(RISING_INFLECTION, X);
|
||||
else if (phoneme == PHONEME_QUESTION) AddInflection(FALLING_INFLECTION, X);
|
||||
|
||||
// get the stress amount (more stress = higher pitch)
|
||||
phase1 = tab47492[stressOutput[i] + 1];
|
||||
|
||||
// get number of frames to write
|
||||
phase2 = phonemeLengthOutput[i];
|
||||
|
||||
// copy from the source to the frames list
|
||||
do
|
||||
{
|
||||
frequency1[X] = freq1data[phoneme]; // F1 frequency
|
||||
frequency2[X] = freq2data[phoneme]; // F2 frequency
|
||||
frequency3[X] = freq3data[phoneme]; // F3 frequency
|
||||
amplitude1[X] = ampl1data[phoneme]; // F1 amplitude
|
||||
amplitude2[X] = ampl2data[phoneme]; // F2 amplitude
|
||||
amplitude3[X] = ampl3data[phoneme]; // F3 amplitude
|
||||
sampledConsonantFlag[X] = sampledConsonantFlags[phoneme]; // phoneme data for sampled consonants
|
||||
pitches[X] = (byte)(pitch + phase1); // pitch
|
||||
++X;
|
||||
} while (--phase2 != 0);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// RESCALE AMPLITUDE
|
||||
//
|
||||
// Rescale volume from a linear scale to decibels.
|
||||
//
|
||||
static void RescaleAmplitude()
|
||||
{
|
||||
for (int i = 255; i >= 0; i--)
|
||||
{
|
||||
try { amplitude1[i] = amplitudeRescale[amplitude1[i]]; } catch { }
|
||||
try { amplitude2[i] = amplitudeRescale[amplitude2[i]]; } catch { }
|
||||
try { amplitude3[i] = amplitudeRescale[amplitude3[i]]; } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// ASSIGN PITCH CONTOUR
|
||||
//
|
||||
// This subtracts the F1 frequency from the pitch to create a
|
||||
// pitch contour. Without this, the output would be at a single
|
||||
// pitch level (monotone).
|
||||
|
||||
static void AssignPitchContour()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
// subtract half the frequency of the formant 1.
|
||||
// this adds variety to the voice
|
||||
pitches[i] -= (byte)(frequency1[i] >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// RENDER THE PHONEMES IN THE LIST
|
||||
//
|
||||
// The phoneme list is converted into sound through the steps:
|
||||
//
|
||||
// 1. Copy each phoneme <length> number of times into the frames list,
|
||||
// where each frame represents 10 milliseconds of sound.
|
||||
//
|
||||
// 2. Determine the transitions lengths between phonemes, and linearly
|
||||
// interpolate the values across the frames.
|
||||
//
|
||||
// 3. Offset the pitches by the fundamental frequency.
|
||||
//
|
||||
// 4. Render the each frame.
|
||||
public static void Render()
|
||||
{
|
||||
byte t;
|
||||
|
||||
if (phonemeIndexOutput[0] == 255) return; //exit if no data
|
||||
|
||||
CreateFrames();
|
||||
t = CreateTransitions();
|
||||
|
||||
if (singmode == 0) AssignPitchContour();
|
||||
RescaleAmplitude();
|
||||
|
||||
ProcessFrames(t);
|
||||
}
|
||||
|
||||
|
||||
// Create a rising or falling inflection 30 frames prior to
|
||||
// index X. A rising inflection is used for questions, and
|
||||
// a falling inflection is used for statements.
|
||||
|
||||
static void AddInflection(byte inflection, byte pos)
|
||||
{
|
||||
byte A;
|
||||
// store the location of the punctuation
|
||||
byte end = pos;
|
||||
|
||||
if (pos < 30) pos = 0;
|
||||
else pos -= 30;
|
||||
|
||||
// FIXME: Explain this fix better, it's not obvious
|
||||
// ML : A =, fixes a problem with invalid pitch with '.'
|
||||
while ((A = pitches[pos]) == 127) ++pos;
|
||||
|
||||
while (pos != end)
|
||||
{
|
||||
// add the inflection direction
|
||||
A += inflection;
|
||||
|
||||
// set the inflection
|
||||
pitches[pos] = A;
|
||||
|
||||
while ((++pos != end) && pitches[pos] == 255) ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SAM's voice can be altered by changing the frequencies of the
|
||||
mouth formant (F1) and the throat formant (F2). Only the voiced
|
||||
phonemes (5-29 and 48-53) are altered.
|
||||
*/
|
||||
public static void SetMouthThroat(byte mouth, byte throat)
|
||||
{
|
||||
// mouth formants (F1) 5..29
|
||||
byte[] mouthFormants5_29 = {
|
||||
0, 0, 0, 0, 0, 10,
|
||||
14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18,
|
||||
16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6};
|
||||
|
||||
// throat formants (F2) 5..29
|
||||
byte[] throatFormants5_29 = {
|
||||
255, 255,
|
||||
255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49,
|
||||
36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86,
|
||||
};
|
||||
|
||||
// there must be no zeros in this 2 tables
|
||||
// formant 1 frequencies (mouth) 48..53
|
||||
byte[] mouthFormants48_53 = { 19, 27, 21, 27, 18, 13 };
|
||||
|
||||
// formant 2 frequencies (throat) 48..53
|
||||
byte[] throatFormants48_53 = { 72, 39, 31, 43, 30, 34 };
|
||||
|
||||
byte newFrequency = 0;
|
||||
byte pos = 5;
|
||||
|
||||
// recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2)
|
||||
while (pos < 30)
|
||||
{
|
||||
// recalculate mouth frequency
|
||||
byte initialFrequency = mouthFormants5_29[pos];
|
||||
if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency);
|
||||
freq1data[pos] = newFrequency;
|
||||
|
||||
// recalculate throat frequency
|
||||
initialFrequency = throatFormants5_29[pos];
|
||||
if (initialFrequency != 0) newFrequency = trans(throat, initialFrequency);
|
||||
freq2data[pos] = newFrequency;
|
||||
pos++;
|
||||
}
|
||||
|
||||
// recalculate formant frequencies 48..53
|
||||
pos = 0;
|
||||
while (pos < 6)
|
||||
{
|
||||
// recalculate F1 (mouth formant)
|
||||
byte initialFrequency = mouthFormants48_53[pos];
|
||||
freq1data[pos + 48] = trans(mouth, initialFrequency);
|
||||
|
||||
// recalculate F2 (throat formant)
|
||||
initialFrequency = throatFormants48_53[pos];
|
||||
freq2data[pos + 48] = trans(throat, initialFrequency);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
SAMSharp.csproj
Normal file
58
SAMSharp.csproj
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{B666563F-F769-4BB0-BB7E-49A5E8C6A2ED}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>SAMSharp</RootNamespace>
|
||||
<AssemblyName>SAMSharp</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Process.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Reciter.cs" />
|
||||
<Compile Include="Renderer.cs" />
|
||||
<Compile Include="Sam.cs" />
|
||||
<Compile Include="Transitions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
846
Sam.cs
Normal file
846
Sam.cs
Normal file
|
@ -0,0 +1,846 @@
|
|||
using static SAMSharp.Renderer;
|
||||
|
||||
namespace SAMSharp
|
||||
{
|
||||
class Sam
|
||||
{
|
||||
//tab40672
|
||||
static byte[] stressInputTable =
|
||||
{
|
||||
(byte)'*',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',(byte)'8'
|
||||
};
|
||||
|
||||
//tab40682
|
||||
static byte[] signInputTable1 =
|
||||
{
|
||||
(byte)' ',(byte)'.',(byte)'?',(byte)',',(byte)'-',(byte)'I',(byte)'I',(byte)'E',
|
||||
(byte)'A',(byte)'A',(byte)'A',(byte)'A',(byte)'U',(byte)'A',(byte)'I',(byte)'E',
|
||||
(byte)'U',(byte)'O',(byte)'R',(byte)'L',(byte)'W',(byte)'Y',(byte)'W',(byte)'R',
|
||||
(byte)'L',(byte)'W',(byte)'Y',(byte)'M',(byte)'N',(byte)'N',(byte)'D',(byte)'Q',
|
||||
(byte)'S',(byte)'S',(byte)'F',(byte)'T',(byte)'/',(byte)'/',(byte)'Z',(byte)'Z',
|
||||
(byte)'V',(byte)'D',(byte)'C',(byte)'*',(byte)'J',(byte)'*',(byte)'*',(byte)'*',
|
||||
(byte)'E',(byte)'A',(byte)'O',(byte)'A',(byte)'O',(byte)'U',(byte)'B',(byte)'*',
|
||||
(byte)'*',(byte)'D',(byte)'*',(byte)'*',(byte)'G',(byte)'*',(byte)'*',(byte)'G',
|
||||
(byte)'*',(byte)'*',(byte)'P',(byte)'*',(byte)'*',(byte)'T',(byte)'*',(byte)'*',
|
||||
(byte)'K',(byte)'*',(byte)'*',(byte)'K',(byte)'*',(byte)'*',(byte)'U',(byte)'U',
|
||||
(byte)'U'
|
||||
};
|
||||
|
||||
//tab40763
|
||||
static byte[] signInputTable2 =
|
||||
{
|
||||
(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'Y',(byte)'H',(byte)'H',
|
||||
(byte)'E',(byte)'A',(byte)'H',(byte)'O',(byte)'H',(byte)'X',(byte)'X',(byte)'R',
|
||||
(byte)'X',(byte)'H',(byte)'X',(byte)'X',(byte)'X',(byte)'X',(byte)'H',(byte)'*',
|
||||
(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'X',(byte)'X',(byte)'*',
|
||||
(byte)'*',(byte)'H',(byte)'*',(byte)'H',(byte)'H',(byte)'X',(byte)'*',(byte)'H',
|
||||
(byte)'*',(byte)'H',(byte)'H',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',
|
||||
(byte)'Y',(byte)'Y',(byte)'Y',(byte)'W',(byte)'W',(byte)'W',(byte)'*',(byte)'*',
|
||||
(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'X',
|
||||
(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',
|
||||
(byte)'*',(byte)'*',(byte)'*',(byte)'X',(byte)'*',(byte)'*',(byte)'L',(byte)'M',
|
||||
(byte)'N'
|
||||
};
|
||||
|
||||
//loc_9F8C
|
||||
enum Flags
|
||||
{
|
||||
FLAG_PLOSIVE = 0x0001,
|
||||
FLAG_STOPCONS = 0x0002, /* stop consonant */
|
||||
FLAG_VOICED = 0x0004,
|
||||
/* 0x08 */
|
||||
FLAG_DIPTHONG = 0x0010,
|
||||
FLAG_DIP_YX = 0x0020, /* dipthong ending with YX */
|
||||
FLAG_CONSONANT = 0x0040,
|
||||
FLAG_VOWEL = 0x0080,
|
||||
FLAG_PUNCT = 0x0100,
|
||||
/* 0x200 */
|
||||
FLAG_ALVEOLAR = 0x0400,
|
||||
FLAG_NASAL = 0x0800,
|
||||
FLAG_LIQUIC = 0x1000, /* liquic consonant */
|
||||
FLAG_FRICATIVE = 0x2000
|
||||
};
|
||||
|
||||
static ushort[] flags = {
|
||||
0x8000 , 0xC100 , 0xC100 , 0xC100 , 0xC100 , 0x00A4 , 0x00A4 , 0x00A4 ,
|
||||
0x00A4 , 0x00A4 , 0x00A4 , 0x0084 , 0x0084 , 0x00A4 , 0x00A4 , 0x0084 ,
|
||||
0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0044 , 0x1044 ,
|
||||
0x1044 , 0x1044 , 0x1044 , 0x084C , 0x0C4C , 0x084C , 0x0448 , 0x404C ,
|
||||
|
||||
0x2440 , 0x2040 , 0x2040 , 0x2440 , 0x0040 , 0x0040 , 0x2444 , 0x2044 ,
|
||||
0x2044 , 0x2444 , 0x2048 , 0x2040 , 0x004C , 0x2044 , 0x0000 , 0x0000 ,
|
||||
0x00B4 , 0x00B4 , 0x00B4 , 0x0094 , 0x0094 , 0x0094 , 0x004E , 0x004E ,
|
||||
|
||||
0x004E , 0x044E , 0x044E , 0x044E , 0x004E , 0x004E , 0x004E , 0x004E ,
|
||||
0x004E , 0x004E , 0x004B , 0x004B , 0x004B , 0x044B , 0x044B , 0x044B ,
|
||||
0x004B , 0x004B , 0x004B , 0x004B , 0x004B , 0x004B , 0x0080 , 0x00C1 ,
|
||||
0x00C1
|
||||
};
|
||||
|
||||
|
||||
//tab45616???
|
||||
static byte[] phonemeStressedLengthTable =
|
||||
{
|
||||
0x00 , 0x12 , 0x12 , 0x12 , 8 ,0xB , 9 ,0xB ,
|
||||
0xE ,0xF ,0xB , 0x10 ,0xC , 6 , 6 ,0xE ,
|
||||
0xC ,0xE ,0xC ,0xB , 8 , 8 ,0xB ,0xA ,
|
||||
9 , 8 , 8 , 8 , 8 , 8 , 3 , 5 ,
|
||||
2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 ,
|
||||
8 , 6 , 6 , 2 , 9 , 4 , 2 , 1 ,
|
||||
0xE ,0xF ,0xF ,0xF ,0xE ,0xE , 8 , 2 ,
|
||||
2 , 7 , 2 , 1 , 7 , 2 , 2 , 7 ,
|
||||
2 , 2 , 8 , 2 , 2 , 6 , 2 , 2 ,
|
||||
7 , 2 , 4 , 7 , 1 , 4 , 5 , 5
|
||||
};
|
||||
|
||||
//tab45536???
|
||||
static byte[] phonemeLengthTable =
|
||||
{
|
||||
0 , 0x12 , 0x12 , 0x12 , 8 , 8 , 8 , 8 ,
|
||||
8 ,0xB , 6 ,0xC ,0xA , 5 , 5 ,0xB ,
|
||||
0xA ,0xA ,0xA , 9 , 8 , 7 , 9 , 7 ,
|
||||
6 , 8 , 6 , 7 , 7 , 7 , 2 , 5 ,
|
||||
2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 ,
|
||||
7 , 6 , 6 , 2 , 8 , 3 , 1 , 0x1E ,
|
||||
0xD ,0xC ,0xC ,0xC ,0xE , 9 , 6 , 1 ,
|
||||
2 , 5 , 1 , 1 , 6 , 1 , 2 , 6 ,
|
||||
1 , 2 , 8 , 2 , 2 , 4 , 2 , 2 ,
|
||||
6 , 1 , 4 , 6 , 1 , 4 , 0xC7 , 0xFF
|
||||
};
|
||||
|
||||
enum Handler : byte
|
||||
{
|
||||
pR = 23,
|
||||
pD = 57,
|
||||
pT = 69,
|
||||
BREAK = 254,
|
||||
END = 255
|
||||
};
|
||||
|
||||
static byte[] input = new byte[256]; //tab39445
|
||||
//standard sam sound
|
||||
public static byte speed = 72;
|
||||
public static byte pitch = 64;
|
||||
public static byte mouth = 128;
|
||||
public static byte throat = 128;
|
||||
public static int singmode = 0;
|
||||
|
||||
public static byte[] stress = new byte[256]; //numbers from 0 to 8
|
||||
public static byte[] phonemeLength = new byte[256]; //tab40160
|
||||
public static byte[] phonemeindex = new byte[256];
|
||||
|
||||
public static byte[] phonemeIndexOutput = new byte[60]; //tab47296
|
||||
public static byte[] stressOutput = new byte[60]; //tab47365
|
||||
public static byte[] phonemeLengthOutput = new byte[60]; //tab47416
|
||||
|
||||
// contains the final soundbuffer
|
||||
public static int bufferpos = 0;
|
||||
public static byte[] buffer = null;
|
||||
|
||||
public static void SetInput(byte[] _input)
|
||||
{
|
||||
int i, l;
|
||||
l = input.Length;
|
||||
if (l > 254) l = 254;
|
||||
for (i = 0; i < l; i++)
|
||||
input[i] = _input[i];
|
||||
input[l] = 0;
|
||||
}
|
||||
|
||||
void SetSpeed(byte _speed) => speed = _speed;
|
||||
void SetPitch(byte _pitch) => pitch = _pitch;
|
||||
void SetMouth(byte _mouth) => mouth = _mouth;
|
||||
void SetThroat(byte _throat) => throat = _throat;
|
||||
void EnableSingmode() => singmode = 1;
|
||||
public static byte[] GetBuffer() => buffer;
|
||||
public static int GetBufferLength() => bufferpos;
|
||||
|
||||
static void Init()
|
||||
{
|
||||
int i;
|
||||
SetMouthThroat(mouth, throat);
|
||||
|
||||
bufferpos = 0;
|
||||
// TODO, check for free the memory, 10 seconds of output should be more than enough
|
||||
buffer = new byte[22050 * 10];
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
stress[i] = 0;
|
||||
phonemeLength[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 60; i++)
|
||||
{
|
||||
phonemeIndexOutput[i] = 0;
|
||||
stressOutput[i] = 0;
|
||||
phonemeLengthOutput[i] = 0;
|
||||
}
|
||||
phonemeindex[255] = (byte)Handler.END; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs
|
||||
}
|
||||
|
||||
public static int SAMMain()
|
||||
{
|
||||
byte X = 0; //!! is this intended like this?
|
||||
Init();
|
||||
/* FIXME: At odds with assignment in Init() */
|
||||
phonemeindex[255] = 32; //to prevent buffer overflow
|
||||
|
||||
if (Parser1() == 0) return 0;
|
||||
Parser2();
|
||||
CopyStress();
|
||||
SetPhonemeLength();
|
||||
AdjustLengths();
|
||||
Code41240();
|
||||
do
|
||||
{
|
||||
if (phonemeindex[X] > 80)
|
||||
{
|
||||
phonemeindex[X] = (byte)Handler.END;
|
||||
break; // error: delete all behind it
|
||||
}
|
||||
} while (++X != 0);
|
||||
InsertBreath();
|
||||
|
||||
PrepareOutput();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void PrepareOutput()
|
||||
{
|
||||
byte srcpos = 0; // Position in source
|
||||
byte destpos = 0; // Position in output
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte A = phonemeindex[srcpos];
|
||||
phonemeIndexOutput[destpos] = A;
|
||||
switch (A)
|
||||
{
|
||||
case (byte)Handler.END:
|
||||
Render();
|
||||
return;
|
||||
case (byte)Handler.BREAK:
|
||||
phonemeIndexOutput[destpos] = (byte)Handler.END;
|
||||
Render();
|
||||
destpos = 0;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
phonemeLengthOutput[destpos] = phonemeLength[srcpos];
|
||||
stressOutput[destpos] = stress[srcpos];
|
||||
++destpos;
|
||||
break;
|
||||
}
|
||||
++srcpos;
|
||||
}
|
||||
}
|
||||
|
||||
static void InsertBreath()
|
||||
{
|
||||
byte mem54 = 255;
|
||||
byte len = 0;
|
||||
byte index; //variable Y
|
||||
|
||||
byte pos = 0;
|
||||
|
||||
while ((index = phonemeindex[pos]) != (byte)Handler.END)
|
||||
{
|
||||
len += phonemeLength[pos];
|
||||
if (len < 232)
|
||||
{
|
||||
if (index == (byte)Handler.BREAK)
|
||||
{
|
||||
}
|
||||
else if ((flags[index] & (ushort)Flags.FLAG_PUNCT) == 0)
|
||||
{
|
||||
if (index == 0) mem54 = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = 0;
|
||||
Insert(++pos, (byte)Handler.BREAK, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = mem54;
|
||||
phonemeindex[pos] = 31; // 'Q*' glottal stop
|
||||
phonemeLength[pos] = 4;
|
||||
stress[pos] = 0;
|
||||
|
||||
len = 0;
|
||||
Insert(++pos, (byte)Handler.BREAK, 0, 0);
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Iterates through the phoneme buffer, copying the stress value from
|
||||
// the following phoneme under the following circumstance:
|
||||
|
||||
// 1. The current phoneme is voiced, excluding plosives and fricatives
|
||||
// 2. The following phoneme is voiced, excluding plosives and fricatives, and
|
||||
// 3. The following phoneme is stressed
|
||||
//
|
||||
// In those cases, the stress value+1 from the following phoneme is copied.
|
||||
//
|
||||
// For example, the word LOITER is represented as LOY5TER, with as stress
|
||||
// of 5 on the dipthong OY. This routine will copy the stress value of 6 (5+1)
|
||||
// to the L that precedes it.
|
||||
|
||||
static void CopyStress()
|
||||
{
|
||||
// loop thought all the phonemes to be output
|
||||
byte pos = 0; //mem66
|
||||
byte Y;
|
||||
|
||||
while ((Y = phonemeindex[pos]) != (byte)Handler.END)
|
||||
{
|
||||
// if CONSONANT_FLAG set, skip - only vowels get stress
|
||||
if ((flags[Y] & 64) != 0)
|
||||
{
|
||||
Y = phonemeindex[pos + 1];
|
||||
|
||||
// if the following phoneme is the end, or a vowel, skip
|
||||
if (Y != (byte)Handler.END && (flags[Y] & 128) != 0)
|
||||
{
|
||||
// get the stress value at the next position
|
||||
Y = stress[pos + 1];
|
||||
if (Y != 0 && (Y & 128) == 0)
|
||||
{
|
||||
// if next phoneme is stressed, and a VOWEL OR ER
|
||||
// copy stress from next phoneme to this one
|
||||
stress[pos] = (byte)(Y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
static void Insert(byte position/*var57*/, byte mem60, byte mem59, byte mem58)
|
||||
{
|
||||
int i;
|
||||
for (i = 253; i >= position; i--) // ML : always keep last safe-guarding 255
|
||||
{
|
||||
phonemeindex[i + 1] = phonemeindex[i];
|
||||
phonemeLength[i + 1] = phonemeLength[i];
|
||||
stress[i + 1] = stress[i];
|
||||
}
|
||||
|
||||
phonemeindex[position] = mem60;
|
||||
phonemeLength[position] = mem59;
|
||||
stress[position] = mem58;
|
||||
}
|
||||
|
||||
|
||||
static int full_match(byte sign1, byte sign2)
|
||||
{
|
||||
byte Y = 0;
|
||||
do
|
||||
{
|
||||
// GET FIRST CHARACTER AT POSITION Y IN signInputTable
|
||||
// --> should change name to PhonemeNameTable1
|
||||
byte A = signInputTable1[Y];
|
||||
|
||||
if (A == sign1)
|
||||
{
|
||||
A = signInputTable2[Y];
|
||||
// NOT A SPECIAL AND MATCHES SECOND CHARACTER?
|
||||
if ((A != '*') && (A == sign2)) return Y;
|
||||
}
|
||||
} while (++Y != 81);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int wild_match(byte sign1)
|
||||
{
|
||||
int Y = 0;
|
||||
do
|
||||
{
|
||||
if (signInputTable2[Y] == '*')
|
||||
{
|
||||
if (signInputTable1[Y] == sign1) return Y;
|
||||
}
|
||||
} while (++Y != 81);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The input[] buffer contains a string of phonemes and stress markers along
|
||||
// the lines of:
|
||||
//
|
||||
// DHAX KAET IHZ AH5GLIY. <0x9B>
|
||||
//
|
||||
// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes
|
||||
// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z".
|
||||
// There are also stress markers, such as "5" and ".".
|
||||
//
|
||||
// The first character of the phonemes are stored in the table signInputTable1[].
|
||||
// The second character of the phonemes are stored in the table signInputTable2[].
|
||||
// The stress characters are arranged in low to high stress order in stressInputTable[].
|
||||
//
|
||||
// The following process is used to parse the input[] buffer:
|
||||
//
|
||||
// Repeat until the <0x9B> character is reached:
|
||||
//
|
||||
// First, a search is made for a 2 character match for phonemes that do not
|
||||
// end with the '*' (wildcard) character. On a match, the index of the phoneme
|
||||
// is added to phonemeIndex[] and the buffer position is advanced 2 bytes.
|
||||
//
|
||||
// If this fails, a search is made for a 1 character match against all
|
||||
// phoneme names ending with a '*' (wildcard). If this succeeds, the
|
||||
// phoneme is added to phonemeIndex[] and the buffer position is advanced
|
||||
// 1 byte.
|
||||
//
|
||||
// If this fails, search for a 1 character match in the stressInputTable[].
|
||||
// If this succeeds, the stress value is placed in the last stress[] table
|
||||
// at the same index of the last added phoneme, and the buffer position is
|
||||
// advanced by 1 byte.
|
||||
//
|
||||
// If this fails, return a 0.
|
||||
//
|
||||
// On success:
|
||||
//
|
||||
// 1. phonemeIndex[] will contain the index of all the phonemes.
|
||||
// 2. The last index in phonemeIndex[] will be 255.
|
||||
// 3. stress[] will contain the stress value for each phoneme
|
||||
|
||||
// input[] holds the string of phonemes, each two bytes wide
|
||||
// signInputTable1[] holds the first character of each phoneme
|
||||
// signInputTable2[] holds te second character of each phoneme
|
||||
// phonemeIndex[] holds the indexes of the phonemes after parsing input[]
|
||||
//
|
||||
// The parser scans through the input[], finding the names of the phonemes
|
||||
// by searching signInputTable1[] and signInputTable2[]. On a match, it
|
||||
// copies the index of the phoneme into the phonemeIndexTable[].
|
||||
//
|
||||
// The character <0x9B> marks the end of text in input[]. When it is reached,
|
||||
// the index 255 is placed at the end of the phonemeIndexTable[], and the
|
||||
// function returns with a 1 indicating success.
|
||||
static int Parser1()
|
||||
{
|
||||
byte sign1;
|
||||
byte position = 0;
|
||||
byte srcpos = 0;
|
||||
|
||||
stress = new byte[256]; // Clear the stress table.
|
||||
|
||||
while ((sign1 = input[srcpos]) != 155)
|
||||
{ // 155 (\233) is end of line marker
|
||||
int match;
|
||||
byte sign2 = input[++srcpos];
|
||||
if ((match = full_match(sign1, sign2)) != -1)
|
||||
{
|
||||
// Matched both characters (no wildcards)
|
||||
phonemeindex[position++] = (byte)match;
|
||||
++srcpos; // Skip the second character of the input as we've matched it
|
||||
}
|
||||
else if ((match = wild_match(sign1)) != -1)
|
||||
{
|
||||
// Matched just the first character (with second character matching '*'
|
||||
phonemeindex[position++] = (byte)match;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should be a stress character. Search through the
|
||||
// stress table backwards.
|
||||
match = 8; // End of stress table. FIXME: Don't hardcode.
|
||||
while ((sign1 != stressInputTable[match]) && (match > 0)) --match;
|
||||
|
||||
if (match == 0) return 0; // failure
|
||||
|
||||
stress[position - 1] = (byte)match; // Set stress for prior phoneme
|
||||
}
|
||||
} //while
|
||||
|
||||
phonemeindex[position] = (byte)Handler.END;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
//change phonemelength depedendent on stress
|
||||
static void SetPhonemeLength()
|
||||
{
|
||||
int position = 0;
|
||||
while (phonemeindex[position] != 255)
|
||||
{
|
||||
byte A = stress[position];
|
||||
if ((A == 0) || ((A & 128) != 0))
|
||||
{
|
||||
phonemeLength[position] = phonemeLengthTable[phonemeindex[position]];
|
||||
}
|
||||
else
|
||||
{
|
||||
phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]];
|
||||
}
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
||||
static void Code41240()
|
||||
{
|
||||
byte pos = 0;
|
||||
|
||||
while (phonemeindex[pos] != (byte)Handler.END)
|
||||
{
|
||||
byte index = phonemeindex[pos];
|
||||
|
||||
if ((flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0)
|
||||
{
|
||||
if ((flags[index] & (ushort)Flags.FLAG_PLOSIVE) != 0)
|
||||
{
|
||||
byte A;
|
||||
byte X = pos;
|
||||
while (phonemeindex[++X] == 0) ; /* Skip pause */
|
||||
A = phonemeindex[X];
|
||||
if (A != (byte)Handler.END)
|
||||
{
|
||||
if ((flags[A] & 8) != 0 || (A == 36) || (A == 37)) { ++pos; continue; } // '/H' '/X'
|
||||
}
|
||||
|
||||
}
|
||||
Insert((byte)(pos + 1), (byte)(index + 1), phonemeLengthTable[index + 1], stress[pos]);
|
||||
Insert((byte)(pos + 2), (byte)(index + 2), phonemeLengthTable[index + 2], stress[pos]);
|
||||
pos += 2;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ChangeRule(byte position, byte mem60)
|
||||
{
|
||||
phonemeindex[position] = 13; //rule;
|
||||
Insert((byte)(position + 1), mem60, 0, stress[position]);
|
||||
}
|
||||
|
||||
// Rewrites the phonemes using the following rules:
|
||||
//
|
||||
// <DIPTHONG ENDING WITH WX> -> <DIPTHONG ENDING WITH WX> WX
|
||||
// <DIPTHONG NOT ENDING WITH WX> -> <DIPTHONG NOT ENDING WITH WX> YX
|
||||
// UL -> AX L
|
||||
// UM -> AX M
|
||||
// <STRESSED VOWEL> <SILENCE> <STRESSED VOWEL> -> <STRESSED VOWEL> <SILENCE> Q <VOWEL>
|
||||
// T R -> CH R
|
||||
// D R -> J R
|
||||
// <VOWEL> R -> <VOWEL> RX
|
||||
// <VOWEL> L -> <VOWEL> LX
|
||||
// G S -> G Z
|
||||
// K <VOWEL OR DIPTHONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPTHONG NOT ENDING WITH IY>
|
||||
// G <VOWEL OR DIPTHONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPTHONG NOT ENDING WITH IY>
|
||||
// S P -> S B
|
||||
// S T -> S D
|
||||
// S K -> S G
|
||||
// S KX -> S GX
|
||||
// <ALVEOLAR> UW -> <ALVEOLAR> UX
|
||||
// CH -> CH CH' (CH requires two phonemes to represent it)
|
||||
// J -> J J' (J requires two phonemes to represent it)
|
||||
// <UNSTRESSED VOWEL> T <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
|
||||
// <UNSTRESSED VOWEL> D <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
|
||||
|
||||
|
||||
static void rule_alveolar_uw(byte X)
|
||||
{
|
||||
// ALVEOLAR flag set?
|
||||
if ((flags[phonemeindex[X - 1]] & (ushort)Flags.FLAG_ALVEOLAR) != 0)
|
||||
phonemeindex[X] = 16;
|
||||
}
|
||||
|
||||
static void rule_ch(byte X)
|
||||
{
|
||||
Insert((byte)(X + 1), 43, 0, stress[X]);
|
||||
}
|
||||
|
||||
static void rule_j(byte X)
|
||||
{
|
||||
Insert((byte)(X + 1), 45, 0, stress[X]);
|
||||
}
|
||||
|
||||
static void rule_g(byte pos)
|
||||
{
|
||||
// G <VOWEL OR DIPTHONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPTHONG NOT ENDING WITH IY>
|
||||
// Example: GO
|
||||
|
||||
byte index = phonemeindex[pos + 1];
|
||||
|
||||
// If dipthong ending with YX, move continue processing next phoneme
|
||||
if ((index != 255) && ((flags[index] & (ushort)Flags.FLAG_DIP_YX) == 0))
|
||||
// replace G with GX and continue processing next phoneme
|
||||
phonemeindex[pos] = 63; // 'GX'
|
||||
}
|
||||
|
||||
static void change(byte pos, byte val)
|
||||
{
|
||||
phonemeindex[pos] = val;
|
||||
}
|
||||
|
||||
static void rule_dipthong(byte p, ushort pf, byte pos)
|
||||
{
|
||||
// <DIPTHONG ENDING WITH WX> -> <DIPTHONG ENDING WITH WX> WX
|
||||
// <DIPTHONG NOT ENDING WITH WX> -> <DIPTHONG NOT ENDING WITH WX> YX
|
||||
// Example: OIL, COW
|
||||
|
||||
// If ends with IY, use YX, else use WX
|
||||
byte A = ((pf & (ushort)Flags.FLAG_DIP_YX) != 0) ? (byte)21 : (byte)20; // 'WX' = 20 'YX' = 21
|
||||
|
||||
// Insert at WX or YX following, copying the stress
|
||||
Insert((byte)(pos + 1), A, 0, stress[pos]);
|
||||
|
||||
if (p == 53) rule_alveolar_uw(pos); // Example: NEW, DEW, SUE, ZOO, THOO, TOO
|
||||
else if (p == 42) rule_ch(pos); // Example: CHEW
|
||||
else if (p == 44) rule_j(pos); // Example: JAY
|
||||
}
|
||||
|
||||
static void Parser2()
|
||||
{
|
||||
byte pos = 0; //mem66;
|
||||
byte p;
|
||||
|
||||
while ((p = phonemeindex[pos]) != (byte)Handler.END)
|
||||
{
|
||||
ushort pf;
|
||||
byte prior;
|
||||
|
||||
if (p == 0)
|
||||
{ // Is phoneme pause?
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
pf = flags[p];
|
||||
prior = phonemeindex[pos - 1];
|
||||
|
||||
if ((pf & (ushort)Flags.FLAG_DIPTHONG) != 0) rule_dipthong(p, pf, pos);
|
||||
else if (p == 78) ChangeRule(pos, 24); // Example: MEDDLE
|
||||
else if (p == 79) ChangeRule(pos, 27); // Example: ASTRONOMY
|
||||
else if (p == 80) ChangeRule(pos, 28); // Example: FUNCTION
|
||||
else if ((pf & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos] != 0)
|
||||
{
|
||||
// RULE:
|
||||
// <STRESSED VOWEL> <SILENCE> <STRESSED VOWEL> -> <STRESSED VOWEL> <SILENCE> Q <VOWEL>
|
||||
// EXAMPLE: AWAY EIGHT
|
||||
if (phonemeindex[pos + 1] == 0)
|
||||
{ // If following phoneme is a pause, get next
|
||||
p = phonemeindex[pos + 2];
|
||||
if (p != (byte)Handler.END && (flags[p] & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos + 2] != 0)
|
||||
Insert((byte)(pos + 2), 31, 0, 0); // 31 = 'Q'
|
||||
}
|
||||
}
|
||||
else if (p == (byte)Handler.pR)
|
||||
{ // RULES FOR PHONEMES BEFORE R
|
||||
if (prior == (byte)Handler.pT) change((byte)(pos - 1), 42); // Example: TRACK
|
||||
else if (prior == (byte)Handler.pD) change((byte)(pos - 1), 44); // Example: DRY
|
||||
else if ((flags[prior] & (ushort)Flags.FLAG_VOWEL) != 0) change(pos, 18); // Example: ART
|
||||
}
|
||||
else if (p == 24 && (flags[prior] & (ushort)Flags.FLAG_VOWEL) != 0) change(pos, 19); // Example: ALL
|
||||
else if (prior == 60 && p == 32)
|
||||
{ // 'G' 'S'
|
||||
// Can't get to fire -
|
||||
// 1. The G -> GX rule intervenes
|
||||
// 2. Reciter already replaces GS -> GZ
|
||||
change(pos, 38);
|
||||
}
|
||||
else if (p == 60) rule_g(pos);
|
||||
else
|
||||
{
|
||||
if (p == 72)
|
||||
{ // 'K'
|
||||
// K <VOWEL OR DIPTHONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPTHONG NOT ENDING WITH IY>
|
||||
// Example: COW
|
||||
byte Y = phonemeindex[pos + 1];
|
||||
// If at end, replace current phoneme with KX
|
||||
if ((flags[Y] & (ushort)Flags.FLAG_DIP_YX) == 0 || Y == (byte)Handler.END)
|
||||
{ // VOWELS AND DIPTHONGS ENDING WITH IY SOUND flag set?
|
||||
change(pos, 75);
|
||||
p = 75;
|
||||
pf = flags[p];
|
||||
}
|
||||
}
|
||||
|
||||
// Replace with softer version?
|
||||
if ((flags[p] & (ushort)Flags.FLAG_PLOSIVE) != 0 && (prior == 32))
|
||||
{ // 'S'
|
||||
// RULE:
|
||||
// S P -> S B
|
||||
// S T -> S D
|
||||
// S K -> S G
|
||||
// S KX -> S GX
|
||||
// Examples: SPY, STY, SKY, SCOWL
|
||||
|
||||
phonemeindex[pos] = (byte)(p - 12);
|
||||
}
|
||||
else if ((pf & (ushort)Flags.FLAG_PLOSIVE) == 0)
|
||||
{
|
||||
p = phonemeindex[pos];
|
||||
if (p == 53) rule_alveolar_uw(pos); // Example: NEW, DEW, SUE, ZOO, THOO, TOO
|
||||
else if (p == 42) rule_ch(pos); // Example: CHEW
|
||||
else if (p == 44) rule_j(pos); // Example: JAY
|
||||
}
|
||||
|
||||
if (p == 69 || p == 57)
|
||||
{ // 'T', 'D'
|
||||
// RULE: Soften T following vowel
|
||||
// NOTE: This rule fails for cases such as "ODD"
|
||||
// <UNSTRESSED VOWEL> T <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
|
||||
// <UNSTRESSED VOWEL> D <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
|
||||
// Example: PARTY, TARDY
|
||||
if (flags[phonemeindex[pos - 1]] != 0 & (ushort)Flags.FLAG_VOWEL != 0)
|
||||
{
|
||||
p = phonemeindex[pos + 1];
|
||||
if (p == 0) p = phonemeindex[pos + 2];
|
||||
if ((flags[p] & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos + 1] == 0) change(pos, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
} // while
|
||||
}
|
||||
|
||||
// Applies various rules that adjust the lengths of phonemes
|
||||
//
|
||||
// Lengthen <FRICATIVE> or <VOICED> between <VOWEL> and <PUNCTUATION> by 1.5
|
||||
// <VOWEL> <RX | LX> <CONSONANT> - decrease <VOWEL> length by 1
|
||||
// <VOWEL> <UNVOICED PLOSIVE> - decrease vowel by 1/8th
|
||||
// <VOWEL> <UNVOICED CONSONANT> - increase vowel by 1/2 + 1
|
||||
// <NASAL> <STOP CONSONANT> - set nasal = 5, consonant = 6
|
||||
// <VOICED STOP CONSONANT> {optional silence} <STOP CONSONANT> - shorten both to 1/2 + 1
|
||||
// <LIQUID CONSONANT> <DIPTHONG> - decrease by 2
|
||||
//
|
||||
static void AdjustLengths()
|
||||
{
|
||||
// LENGTHEN VOWELS PRECEDING PUNCTUATION
|
||||
//
|
||||
// Search for punctuation. If found, back up to the first vowel, then
|
||||
// process all phonemes between there and up to (but not including) the punctuation.
|
||||
// If any phoneme is found that is a either a fricative or voiced, the duration is
|
||||
// increased by (length * 1.5) + 1
|
||||
|
||||
// loop index
|
||||
{
|
||||
byte X = 0;
|
||||
byte index1;
|
||||
|
||||
while ((index1 = phonemeindex[X]) != (byte)Handler.END)
|
||||
{
|
||||
byte loopIndex1;
|
||||
|
||||
// not punctuation?
|
||||
if ((flags[index1] & (ushort)Flags.FLAG_PUNCT) == 0)
|
||||
{
|
||||
++X;
|
||||
continue;
|
||||
}
|
||||
|
||||
loopIndex1 = X;
|
||||
|
||||
while (--X != 0 && (flags[phonemeindex[X]] & (ushort)Flags.FLAG_VOWEL) == 0) ; // back up while not a vowel
|
||||
if (X == 0) break;
|
||||
|
||||
do
|
||||
{
|
||||
// test for vowel
|
||||
index1 = phonemeindex[X];
|
||||
|
||||
// test for fricative/unvoiced or not voiced
|
||||
if ((flags[index1] & (ushort)Flags.FLAG_FRICATIVE) == 0 || (flags[index1] & (ushort)Flags.FLAG_VOICED) != 0)
|
||||
{ //nochmal überprüfen
|
||||
byte A = phonemeLength[X];
|
||||
// change phoneme length to (length * 1.5) + 1
|
||||
phonemeLength[X] = (byte)((A >> 1) + A + 1);
|
||||
}
|
||||
} while (++X != loopIndex1);
|
||||
X++;
|
||||
} // while
|
||||
}
|
||||
|
||||
// Similar to the above routine, but shorten vowels under some circumstances
|
||||
|
||||
// Loop through all phonemes
|
||||
byte loopIndex = 0;
|
||||
byte index;
|
||||
|
||||
while ((index = phonemeindex[loopIndex]) != (byte)Handler.END)
|
||||
{
|
||||
byte X = loopIndex;
|
||||
|
||||
if ((flags[index] & (ushort)Flags.FLAG_VOWEL) != 0)
|
||||
{
|
||||
index = phonemeindex[loopIndex + 1];
|
||||
if ((flags[index] & (ushort)Flags.FLAG_CONSONANT) == 0)
|
||||
{
|
||||
if ((index == 18) || (index == 19))
|
||||
{ // 'RX', 'LX'
|
||||
index = phonemeindex[loopIndex + 2];
|
||||
if ((flags[index] & (ushort)Flags.FLAG_CONSONANT) != 0)
|
||||
phonemeLength[loopIndex]--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Got here if not <VOWEL>
|
||||
ushort flag = (index == (byte)Handler.END) ? (ushort)65 : (ushort)flags[index]; // 65 if end marker
|
||||
|
||||
if ((flag & (ushort)Flags.FLAG_VOICED) == 0)
|
||||
{ // Unvoiced
|
||||
// *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX
|
||||
if ((flag & (ushort)Flags.FLAG_PLOSIVE) != 0)
|
||||
{ // unvoiced plosive
|
||||
// RULE: <VOWEL> <UNVOICED PLOSIVE>
|
||||
// <VOWEL> <P*, T*, K*, KX>
|
||||
phonemeLength[loopIndex] -= (byte)(phonemeLength[loopIndex] >> 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte A;
|
||||
// decrease length
|
||||
A = phonemeLength[loopIndex];
|
||||
phonemeLength[loopIndex] = (byte)((A >> 2) + A + 1); // 5/4*A + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((flags[index] & (ushort)Flags.FLAG_NASAL) != 0)
|
||||
{ // nasal?
|
||||
// RULE: <NASAL> <STOP CONSONANT>
|
||||
// Set punctuation length to 6
|
||||
// Set stop consonant length to 5
|
||||
index = phonemeindex[++X];
|
||||
if (index != (byte)Handler.END && (flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0)
|
||||
{
|
||||
phonemeLength[X] = 6; // set stop consonant length to 6
|
||||
phonemeLength[X - 1] = 5; // set nasal length to 5
|
||||
}
|
||||
}
|
||||
else if ((flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0)
|
||||
{ // (voiced) stop consonant?
|
||||
// RULE: <VOICED STOP CONSONANT> {optional silence} <STOP CONSONANT>
|
||||
// Shorten both to (length/2 + 1)
|
||||
|
||||
// move past silence
|
||||
while ((index = phonemeindex[++X]) == 0) ;
|
||||
|
||||
if (index != (byte)Handler.END && (flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0)
|
||||
{
|
||||
// FIXME, this looks wrong?
|
||||
// RULE: <UNVOICED STOP CONSONANT> {optional silence} <STOP CONSONANT>
|
||||
phonemeLength[X] = (byte)((phonemeLength[X] >> 1) + 1);
|
||||
phonemeLength[loopIndex] = (byte)((phonemeLength[loopIndex] >> 1) + 1);
|
||||
X = loopIndex;
|
||||
}
|
||||
}
|
||||
else if ((flags[index] & (ushort)Flags.FLAG_LIQUIC) != 0)
|
||||
{ // liquic consonant?
|
||||
// RULE: <VOICED NON-VOWEL> <DIPTHONG>
|
||||
// Decrease <DIPTHONG> by 2
|
||||
index = phonemeindex[X - 1]; // prior phoneme;
|
||||
phonemeLength[X] -= 2; // 20ms
|
||||
}
|
||||
|
||||
++loopIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
204
Transitions.cs
Normal file
204
Transitions.cs
Normal file
|
@ -0,0 +1,204 @@
|
|||
using System;
|
||||
|
||||
using static SAMSharp.Sam;
|
||||
using static SAMSharp.Renderer;
|
||||
|
||||
namespace SAMSharp
|
||||
{
|
||||
class Transitions
|
||||
{
|
||||
// CREATE TRANSITIONS
|
||||
//
|
||||
// Linear transitions are now created to smoothly connect each
|
||||
// phoeneme. This transition is spread between the ending frames
|
||||
// of the old phoneme (outBlendLength), and the beginning frames
|
||||
// of the new phoneme (inBlendLength).
|
||||
//
|
||||
// To determine how many frames to use, the two phonemes are
|
||||
// compared using the blendRank[] table. The phoneme with the
|
||||
// smaller score is used. In case of a tie, a blend of each is used:
|
||||
//
|
||||
// if blendRank[phoneme1] == blendRank[phomneme2]
|
||||
// // use lengths from each phoneme
|
||||
// outBlendFrames = outBlend[phoneme1]
|
||||
// inBlendFrames = outBlend[phoneme2]
|
||||
// else if blendRank[phoneme1] < blendRank[phoneme2]
|
||||
// // use lengths from first phoneme
|
||||
// outBlendFrames = outBlendLength[phoneme1]
|
||||
// inBlendFrames = inBlendLength[phoneme1]
|
||||
// else
|
||||
// // use lengths from the second phoneme
|
||||
// // note that in and out are swapped around!
|
||||
// outBlendFrames = inBlendLength[phoneme2]
|
||||
// inBlendFrames = outBlendLength[phoneme2]
|
||||
//
|
||||
// Blend lengths can't be less than zero.
|
||||
//
|
||||
// For most of the parameters, SAM interpolates over the range of the last
|
||||
// outBlendFrames-1 and the first inBlendFrames.
|
||||
//
|
||||
// The exception to this is the Pitch[] parameter, which is interpolates the
|
||||
// pitch from the center of the current phoneme to the center of the next
|
||||
// phoneme.
|
||||
|
||||
//written by me because of different table positions.
|
||||
// mem[47] = ...
|
||||
// 168=pitches
|
||||
// 169=frequency1
|
||||
// 170=frequency2
|
||||
// 171=frequency3
|
||||
// 172=amplitude1
|
||||
// 173=amplitude2
|
||||
// 174=amplitude3
|
||||
static byte Read(byte p, byte Y)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case 168: return pitches[Y];
|
||||
case 169: return frequency1[Y];
|
||||
case 170: return frequency2[Y];
|
||||
case 171: return frequency3[Y];
|
||||
case 172: return amplitude1[Y];
|
||||
case 173: return amplitude2[Y];
|
||||
case 174: return amplitude3[Y];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void Write(byte p, byte Y, byte value)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case 168: pitches[Y] = value; return;
|
||||
case 169: frequency1[Y] = value; return;
|
||||
case 170: frequency2[Y] = value; return;
|
||||
case 171: frequency3[Y] = value; return;
|
||||
case 172: amplitude1[Y] = value; return;
|
||||
case 173: amplitude2[Y] = value; return;
|
||||
case 174: amplitude3[Y] = value; return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// linearly interpolate values
|
||||
static void interpolate(byte width, byte table, byte frame, char mem53)
|
||||
{
|
||||
bool sign = (mem53 < 0);
|
||||
byte remainder = (byte)(Math.Abs(mem53) % width);
|
||||
byte div = (byte)(mem53 / width);
|
||||
|
||||
byte error = 0;
|
||||
byte pos = width;
|
||||
byte val = (byte)(Read(table, frame) + div);
|
||||
|
||||
while (--pos != 0)
|
||||
{
|
||||
error += remainder;
|
||||
if (error >= width)
|
||||
{ // accumulated a whole integer error, so adjust output
|
||||
error -= width;
|
||||
if (sign) val--;
|
||||
else if (val != 0) val++; // if input is 0, we always leave it alone
|
||||
}
|
||||
Write(table, ++frame, val); // Write updated value back to next frame.
|
||||
val += div;
|
||||
}
|
||||
}
|
||||
|
||||
static void interpolate_pitch(byte pos, byte mem49, byte phase3)
|
||||
{
|
||||
// unlike the other values, the pitches[] interpolates from
|
||||
// the middle of the current phoneme to the middle of the
|
||||
// next phoneme
|
||||
|
||||
// half the width of the current and next phoneme
|
||||
byte cur_width = (byte)(phonemeLengthOutput[pos] / 2);
|
||||
byte next_width = (byte)(phonemeLengthOutput[pos + 1] / 2);
|
||||
// sum the values
|
||||
byte width = (byte)(cur_width + next_width);
|
||||
char pitch = (char)(pitches[next_width + mem49] - pitches[mem49 - cur_width]);
|
||||
interpolate(width, 168, phase3, pitch);
|
||||
}
|
||||
|
||||
|
||||
public static byte CreateTransitions()
|
||||
{
|
||||
byte mem49 = 0;
|
||||
byte pos = 0;
|
||||
while (true)
|
||||
{
|
||||
byte next_rank;
|
||||
byte rank;
|
||||
byte speedcounter;
|
||||
byte phase1;
|
||||
byte phase2;
|
||||
byte phase3;
|
||||
byte transition;
|
||||
|
||||
byte phoneme = phonemeIndexOutput[pos];
|
||||
byte next_phoneme = phonemeIndexOutput[pos + 1];
|
||||
|
||||
if (next_phoneme == 255) break; // 255 == end_token
|
||||
|
||||
// get the ranking of each phoneme
|
||||
next_rank = blendRank[next_phoneme];
|
||||
rank = blendRank[phoneme];
|
||||
|
||||
// compare the rank - lower rank value is stronger
|
||||
if (rank == next_rank)
|
||||
{
|
||||
// same rank, so use out blend lengths from each phoneme
|
||||
phase1 = outBlendLength[phoneme];
|
||||
phase2 = outBlendLength[next_phoneme];
|
||||
}
|
||||
else if (rank < next_rank)
|
||||
{
|
||||
// next phoneme is stronger, so us its blend lengths
|
||||
phase1 = inBlendLength[next_phoneme];
|
||||
phase2 = outBlendLength[next_phoneme];
|
||||
}
|
||||
else
|
||||
{
|
||||
// current phoneme is stronger, so use its blend lengths
|
||||
// note the out/in are swapped
|
||||
phase1 = outBlendLength[phoneme];
|
||||
phase2 = inBlendLength[phoneme];
|
||||
}
|
||||
|
||||
mem49 += phonemeLengthOutput[pos];
|
||||
|
||||
speedcounter = (byte)(mem49 + phase2);
|
||||
phase3 = (byte)(mem49 - phase1);
|
||||
transition = (byte)(phase1 + phase2); // total transition?
|
||||
|
||||
if (((transition - 2) & 128) == 0)
|
||||
{
|
||||
byte table = 169;
|
||||
interpolate_pitch(pos, mem49, phase3);
|
||||
while (table < 175)
|
||||
{
|
||||
// tables:
|
||||
// 168 pitches[]
|
||||
// 169 frequency1
|
||||
// 170 frequency2
|
||||
// 171 frequency3
|
||||
// 172 amplitude1
|
||||
// 173 amplitude2
|
||||
// 174 amplitude3
|
||||
|
||||
char value = (char)(Read(table, speedcounter) - Read(table, phase3));
|
||||
interpolate(transition, table, phase3, value);
|
||||
table++;
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
|
||||
// add the length of this phoneme
|
||||
return (byte)(mem49 + phonemeLengthOutput[pos]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue