107 lines
3.8 KiB
C#
107 lines
3.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|