mirror of
https://github.com/holub/mame
synced 2025-04-23 00:39:36 +03:00
TMS5220: Purge process() of gotos to hopefully make the compiler's optimizer happier. Add a hack so Victory's "Shields Up" sample and the sample afterward aren't corrupted. [Lord Nightmare]
This commit is contained in:
parent
3b72af8a26
commit
66b36b336f
@ -271,6 +271,10 @@ static INT16 clip_analog(INT16 cliptemp);
|
||||
* or clip logic, even though the real hardware doesn't do this, partially verified by decap */
|
||||
#undef ALLOW_4_LSB
|
||||
|
||||
/* forces m_TALK active instantly whenever m_SPEN would be activated, causing speech delay to be reduced by up to one frame time */
|
||||
/* for some reason, this hack makes victory behave better, though it does not match the patent */
|
||||
#define FAST_START_HACK 1
|
||||
|
||||
|
||||
/* *****configuration of chip connection stuff***** */
|
||||
/* must be defined; if 0, output the waveform as if it was tapped on the speaker pin as usual, if 1, output the waveform as if it was tapped on the i/o pin (volume is much lower in the latter case) */
|
||||
@ -499,6 +503,9 @@ void tms5220_device::data_write(int data)
|
||||
m_old_uv_zpar = 1; // zero old k4-k10 as well
|
||||
#endif
|
||||
m_SPEN = 1;
|
||||
#ifdef FAST_START_HACK
|
||||
m_TALK = 1;
|
||||
#endif
|
||||
m_new_frame_energy_idx = 0;
|
||||
m_new_frame_pitch_idx = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
@ -744,307 +751,294 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
|
||||
int i, bitout;
|
||||
INT32 this_sample;
|
||||
|
||||
/* the following gotos are probably safe to remove */
|
||||
/* if we're empty and still not speaking, fill with nothingness */
|
||||
if (!m_TALKD)
|
||||
goto empty;
|
||||
|
||||
#ifdef VERBOSE
|
||||
fprintf(stderr,"not empty called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
fprintf(stderr,"process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
|
||||
render:
|
||||
/* loop until the buffer is full or we've stopped speaking */
|
||||
while ((size > 0) && m_TALKD)
|
||||
{
|
||||
|
||||
/* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1
|
||||
* (In reality, the frame was really loaded incrementally during the entire IP=0
|
||||
* PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens)
|
||||
*/
|
||||
if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1))
|
||||
{
|
||||
// HACK for regression testing, be sure to comment out before release!
|
||||
//m_RNG = 0x1234;
|
||||
// end HACK
|
||||
|
||||
/* appropriately override the interp count if needed; this will be incremented after the frame parse! */
|
||||
m_IP = reload_table[m_c_variant_rate&0x3];
|
||||
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
/* remember previous frame energy, pitch, and coefficients */
|
||||
m_old_frame_energy_idx = m_new_frame_energy_idx;
|
||||
m_old_frame_pitch_idx = m_new_frame_pitch_idx;
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_old_frame_k_idx[i] = m_new_frame_k_idx[i];
|
||||
#endif
|
||||
|
||||
/* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */
|
||||
parse_frame();
|
||||
|
||||
// if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters
|
||||
// NOTE: this is probably the bug the tms5100/tmc0280 has, pre-rev D, I think.
|
||||
// GUESS: Pre-rev D versions start zeroing k5-k10 immediately upon new frame load regardless of interpolation inhibit
|
||||
// I.e. ZPAR = /TALKD || (PC>5&&P=0)
|
||||
// GUESS: D and later versions only start or stop zeroing k5-k10 at the IP7->IP0 transition AFTER the frame
|
||||
// I.e. ZPAR = /TALKD || (PC>5&&OLDP)
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
m_old_uv_zpar = m_uv_zpar;
|
||||
m_old_zpar = m_zpar; // unset old zpar on new frame
|
||||
#endif
|
||||
m_zpar = 0;
|
||||
//m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110
|
||||
m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100
|
||||
|
||||
/* if the new frame is a stop frame, unset both TALK and SPEN (via TCON). TALKD remains active while the energy is ramping to 0. */
|
||||
if (NEW_FRAME_STOP_FLAG == 1)
|
||||
{
|
||||
m_TALK = m_SPEN = 0;
|
||||
}
|
||||
|
||||
/* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it.
|
||||
Interpolation inhibit cases:
|
||||
* Old frame was voiced, new is unvoiced
|
||||
* Old frame was silence/zero energy, new has nonzero energy
|
||||
* Old frame was unvoiced, new is voiced
|
||||
* Old frame was unvoiced, new frame is silence/zero energy (unique to tms52xx)
|
||||
*/
|
||||
if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1))
|
||||
|| ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0))
|
||||
|| ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0))
|
||||
|| ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 1)) )
|
||||
m_inhibit = 1;
|
||||
else // normal frame, normal interpolation
|
||||
m_inhibit = 0;
|
||||
|
||||
#ifdef DEBUG_GENERATION
|
||||
/* Debug info for current parsed frame */
|
||||
fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP);
|
||||
fprintf(stderr,"Processing new frame: ");
|
||||
if (m_inhibit == 0)
|
||||
fprintf(stderr, "Normal Frame\n");
|
||||
else
|
||||
fprintf(stderr,"Interpolation Inhibited\n");
|
||||
fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]);
|
||||
fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",
|
||||
(m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)),
|
||||
m_new_frame_energy_idx,
|
||||
(m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)),
|
||||
(m_coeff->ktable[0][m_new_frame_k_idx[0]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[1][m_new_frame_k_idx[1]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[2][m_new_frame_k_idx[2]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[3][m_new_frame_k_idx[3]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[4][m_new_frame_k_idx[4]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[5][m_new_frame_k_idx[5]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[6][m_new_frame_k_idx[6]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[7][m_new_frame_k_idx[7]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[8][m_new_frame_k_idx[8]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[9][m_new_frame_k_idx[9]] * (1-m_uv_zpar)) );
|
||||
#endif
|
||||
|
||||
}
|
||||
else // Not a new frame, just interpolate the existing frame.
|
||||
{
|
||||
int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW
|
||||
//int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
|
||||
int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7));
|
||||
//fprintf(stderr, "CS: %03d", current_sample);
|
||||
// reset the current energy, pitch, etc to what it was at frame start
|
||||
m_current_energy = (m_coeff->energytable[m_old_frame_energy_idx] * (1-m_old_zpar));
|
||||
m_current_pitch = (m_coeff->pitchtable[m_old_frame_pitch_idx] * (1-m_old_zpar));
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-((i<4)?m_old_zpar:m_old_uv_zpar)));
|
||||
// now adjust each value to be exactly correct for each of the samples per frame
|
||||
if (m_IP != 0) // if we're still interpolating...
|
||||
{
|
||||
m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
|
||||
m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_current_k[i] + (((m_coeff->ktable[i][m_new_frame_k_idx[i]] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-((i<4)?m_zpar:m_uv_zpar));
|
||||
}
|
||||
else // we're done, play this frame for 1/8 frame.
|
||||
{
|
||||
m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar));
|
||||
m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar));
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-((i<4)?m_zpar:m_uv_zpar)));
|
||||
}
|
||||
#else
|
||||
//Updates to parameters only happen on subcycle '2' (B cycle) of PCs.
|
||||
if (m_subcycle == 2)
|
||||
{
|
||||
switch(m_PC)
|
||||
{
|
||||
case 0: /* PC = 0, B cycle, write updated energy */
|
||||
m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
|
||||
break;
|
||||
case 1: /* PC = 1, B cycle, write updated pitch */
|
||||
m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
|
||||
break;
|
||||
case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
|
||||
/* PC = 2 through 11, B cycle, write updated K1 through K10 */
|
||||
m_current_k[m_PC-2] = (m_current_k[m_PC-2] + (((m_coeff->ktable[m_PC-2][m_new_frame_k_idx[m_PC-2]] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT))*(((m_PC-2)>4)?(1-m_uv_zpar):(1-m_zpar));
|
||||
break;
|
||||
case 12: /* PC = 12 */
|
||||
/* we should NEVER reach this point, PC=12 doesn't have a subcycle 2 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// calculate the output
|
||||
if (OLD_FRAME_UNVOICED_FLAG == 1)
|
||||
{
|
||||
// generate unvoiced samples here
|
||||
if (m_RNG & 1)
|
||||
m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/
|
||||
else
|
||||
m_excitation_data = 0x40;
|
||||
}
|
||||
else /* (OLD_FRAME_UNVOICED_FLAG == 0) */
|
||||
{
|
||||
// generate voiced samples here
|
||||
/* US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp
|
||||
* function has a chirp/peak and then a long chain of zeroes.
|
||||
* The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample,
|
||||
* and if the address reaches that point the ADDRESS incrementer is
|
||||
* disabled, forcing all samples beyond 51d to be == 51d
|
||||
*/
|
||||
if (m_pitch_count >= 51)
|
||||
m_excitation_data = (INT8)m_coeff->chirptable[51];
|
||||
else /*m_pitch_count < 51*/
|
||||
m_excitation_data = (INT8)m_coeff->chirptable[m_pitch_count];
|
||||
}
|
||||
|
||||
// Update LFSR *20* times every sample (once per T cycle), like patent shows
|
||||
for (i=0; i<20; i++)
|
||||
{
|
||||
bitout = ((m_RNG >> 12) & 1) ^
|
||||
((m_RNG >> 3) & 1) ^
|
||||
((m_RNG >> 2) & 1) ^
|
||||
((m_RNG >> 0) & 1);
|
||||
m_RNG <<= 1;
|
||||
m_RNG |= bitout;
|
||||
}
|
||||
this_sample = lattice_filter(); /* execute lattice filter */
|
||||
#ifdef DEBUG_GENERATION_VERBOSE
|
||||
//fprintf(stderr,"C:%01d; ",m_subcycle);
|
||||
fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
|
||||
//fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
|
||||
for (i=0; i<10; i++)
|
||||
fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]);
|
||||
fprintf(stderr,"Out:%06d ", this_sample);
|
||||
//#ifdef PERFECT_INTERPOLATION_HACK
|
||||
// fprintf(stderr,"%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar);
|
||||
//#else
|
||||
// fprintf(stderr,"x%dx%d",m_zpar,m_uv_zpar);
|
||||
//#endif
|
||||
fprintf(stderr,"\n");
|
||||
#endif
|
||||
/* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */
|
||||
while (this_sample > 16383) this_sample -= 32768;
|
||||
while (this_sample < -16384) this_sample += 32768;
|
||||
if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping
|
||||
buffer[buf_count] = clip_analog(this_sample);
|
||||
else // digital I/O pin output is 12 bits
|
||||
{
|
||||
#ifdef ALLOW_4_LSB
|
||||
// input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn
|
||||
// N taps: ^ = 0x2000;
|
||||
// output: ssss ssss ssss ssss snnn nnnn nnnn nnnN
|
||||
buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13);
|
||||
#else
|
||||
this_sample &= ~0xF;
|
||||
// input: ssss ssss ssss ssss ssnn nnnn nnnn 0000
|
||||
// N taps: ^^ ^^^ = 0x3E00;
|
||||
// output: ssss ssss ssss ssss snnn nnnn nnnN NNNN
|
||||
buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9);
|
||||
#endif
|
||||
}
|
||||
// Update all counts
|
||||
|
||||
m_subcycle++;
|
||||
if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
|
||||
{
|
||||
/* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0
|
||||
* if INHIBIT was true during the most recent frame transition.
|
||||
* The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12
|
||||
* and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles,
|
||||
* which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17
|
||||
* (and hence INHIBIT itself 2 t-cycles later). We do it here because it is
|
||||
* convenient and should make no difference in output.
|
||||
*/
|
||||
if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_zero = 1;
|
||||
if ((m_IP == 0)&&(m_pitch_zero==1)) m_pitch_zero = 0;
|
||||
if (m_IP == 7) // RESETL4
|
||||
{
|
||||
// Latch OLDE and OLDP
|
||||
OLD_FRAME_SILENCE_FLAG = NEW_FRAME_SILENCE_FLAG; // m_OLDE
|
||||
OLD_FRAME_UNVOICED_FLAG = NEW_FRAME_UNVOICED_FLAG; // m_OLDP
|
||||
/* if TALK was clear last frame, halt speech now, since TALKD (latched from TALK on new frame) just went inactive. */
|
||||
#ifdef DEBUG_GENERATION
|
||||
fprintf(stderr,"RESETL4, about to update status: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
#ifdef DEBUG_GENERATION
|
||||
if (m_TALK == 0)
|
||||
fprintf(stderr,"tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n");
|
||||
#endif
|
||||
m_TALKD = m_TALK; // TALKD is latched from TALK
|
||||
update_fifo_status_and_ints(); // to trigger an interrupt if TALK_STATUS is now inactive
|
||||
m_TALK = m_SPEN; // TALK is latched from SPEN
|
||||
#ifdef DEBUG_GENERATION
|
||||
fprintf(stderr,"RESETL4, status updated: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
}
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC = 0;
|
||||
m_IP++;
|
||||
m_IP&=0x7;
|
||||
}
|
||||
else if (m_subcycle == 3)
|
||||
{
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC++;
|
||||
}
|
||||
m_pitch_count++;
|
||||
if ((m_pitch_count >= m_current_pitch)||(m_pitch_zero == 1)) m_pitch_count = 0;
|
||||
m_pitch_count &= 0x1FF;
|
||||
buf_count++;
|
||||
size--;
|
||||
}
|
||||
|
||||
empty:
|
||||
|
||||
#ifdef VERBOSE
|
||||
fprintf(stderr,"empty called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
while (size > 0)
|
||||
{
|
||||
m_subcycle++;
|
||||
if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
|
||||
if(m_TALKD) // speaking
|
||||
{
|
||||
if (m_IP == 7) // RESETL4
|
||||
/* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1
|
||||
* (In reality, the frame was really loaded incrementally during the entire IP=0
|
||||
* PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens)
|
||||
*/
|
||||
if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1))
|
||||
{
|
||||
m_TALKD = m_TALK; // TALKD is latched from TALK
|
||||
m_TALK = m_SPEN; // TALK is latched from SPEN
|
||||
// HACK for regression testing, be sure to comment out before release!
|
||||
//m_RNG = 0x1234;
|
||||
// end HACK
|
||||
|
||||
/* appropriately override the interp count if needed; this will be incremented after the frame parse! */
|
||||
m_IP = reload_table[m_c_variant_rate&0x3];
|
||||
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
/* remember previous frame energy, pitch, and coefficients */
|
||||
m_old_frame_energy_idx = m_new_frame_energy_idx;
|
||||
m_old_frame_pitch_idx = m_new_frame_pitch_idx;
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_old_frame_k_idx[i] = m_new_frame_k_idx[i];
|
||||
#endif
|
||||
|
||||
/* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */
|
||||
parse_frame();
|
||||
|
||||
// if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters
|
||||
// NOTE: this is probably the bug the tms5100/tmc0280 has, pre-rev D, I think.
|
||||
// GUESS: Pre-rev D versions start zeroing k5-k10 immediately upon new frame load regardless of interpolation inhibit
|
||||
// I.e. ZPAR = /TALKD || (PC>5&&P=0)
|
||||
// GUESS: D and later versions only start or stop zeroing k5-k10 at the IP7->IP0 transition AFTER the frame
|
||||
// I.e. ZPAR = /TALKD || (PC>5&&OLDP)
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
m_old_uv_zpar = m_uv_zpar;
|
||||
m_old_zpar = m_zpar; // unset old zpar on new frame
|
||||
#endif
|
||||
m_zpar = 0;
|
||||
//m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110
|
||||
m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100
|
||||
|
||||
/* if the new frame is a stop frame, unset both TALK and SPEN (via TCON). TALKD remains active while the energy is ramping to 0. */
|
||||
if (NEW_FRAME_STOP_FLAG == 1)
|
||||
{
|
||||
m_TALK = m_SPEN = 0;
|
||||
}
|
||||
|
||||
/* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it.
|
||||
Interpolation inhibit cases:
|
||||
* Old frame was voiced, new is unvoiced
|
||||
* Old frame was silence/zero energy, new has nonzero energy
|
||||
* Old frame was unvoiced, new is voiced
|
||||
* Old frame was unvoiced, new frame is silence/zero energy (unique to tms52xx)
|
||||
*/
|
||||
if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1))
|
||||
|| ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0))
|
||||
|| ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0))
|
||||
|| ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 1)) )
|
||||
m_inhibit = 1;
|
||||
else // normal frame, normal interpolation
|
||||
m_inhibit = 0;
|
||||
|
||||
#ifdef DEBUG_GENERATION
|
||||
/* Debug info for current parsed frame */
|
||||
fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP);
|
||||
fprintf(stderr,"Processing new frame: ");
|
||||
if (m_inhibit == 0)
|
||||
fprintf(stderr, "Normal Frame\n");
|
||||
else
|
||||
fprintf(stderr,"Interpolation Inhibited\n");
|
||||
fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]);
|
||||
fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",
|
||||
(m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)),
|
||||
m_new_frame_energy_idx,
|
||||
(m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)),
|
||||
(m_coeff->ktable[0][m_new_frame_k_idx[0]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[1][m_new_frame_k_idx[1]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[2][m_new_frame_k_idx[2]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[3][m_new_frame_k_idx[3]] * (1-m_zpar)),
|
||||
(m_coeff->ktable[4][m_new_frame_k_idx[4]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[5][m_new_frame_k_idx[5]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[6][m_new_frame_k_idx[6]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[7][m_new_frame_k_idx[7]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[8][m_new_frame_k_idx[8]] * (1-m_uv_zpar)),
|
||||
(m_coeff->ktable[9][m_new_frame_k_idx[9]] * (1-m_uv_zpar)) );
|
||||
#endif
|
||||
|
||||
}
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC = 0;
|
||||
m_IP++;
|
||||
m_IP&=0x7;
|
||||
if (m_TALKD) goto render;
|
||||
else // Not a new frame, just interpolate the existing frame.
|
||||
{
|
||||
int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value
|
||||
#ifdef PERFECT_INTERPOLATION_HACK
|
||||
int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW
|
||||
//int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
|
||||
int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7));
|
||||
//fprintf(stderr, "CS: %03d", current_sample);
|
||||
// reset the current energy, pitch, etc to what it was at frame start
|
||||
m_current_energy = (m_coeff->energytable[m_old_frame_energy_idx] * (1-m_old_zpar));
|
||||
m_current_pitch = (m_coeff->pitchtable[m_old_frame_pitch_idx] * (1-m_old_zpar));
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-((i<4)?m_old_zpar:m_old_uv_zpar)));
|
||||
// now adjust each value to be exactly correct for each of the samples per frame
|
||||
if (m_IP != 0) // if we're still interpolating...
|
||||
{
|
||||
m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
|
||||
m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_current_k[i] + (((m_coeff->ktable[i][m_new_frame_k_idx[i]] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-((i<4)?m_zpar:m_uv_zpar));
|
||||
}
|
||||
else // we're done, play this frame for 1/8 frame.
|
||||
{
|
||||
m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar));
|
||||
m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar));
|
||||
for (i = 0; i < m_coeff->num_k; i++)
|
||||
m_current_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-((i<4)?m_zpar:m_uv_zpar)));
|
||||
}
|
||||
#else
|
||||
//Updates to parameters only happen on subcycle '2' (B cycle) of PCs.
|
||||
if (m_subcycle == 2)
|
||||
{
|
||||
switch(m_PC)
|
||||
{
|
||||
case 0: /* PC = 0, B cycle, write updated energy */
|
||||
m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
|
||||
break;
|
||||
case 1: /* PC = 1, B cycle, write updated pitch */
|
||||
m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
|
||||
break;
|
||||
case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
|
||||
/* PC = 2 through 11, B cycle, write updated K1 through K10 */
|
||||
m_current_k[m_PC-2] = (m_current_k[m_PC-2] + (((m_coeff->ktable[m_PC-2][m_new_frame_k_idx[m_PC-2]] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT))*(((m_PC-2)>4)?(1-m_uv_zpar):(1-m_zpar));
|
||||
break;
|
||||
case 12: /* PC = 12 */
|
||||
/* we should NEVER reach this point, PC=12 doesn't have a subcycle 2 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// calculate the output
|
||||
if (OLD_FRAME_UNVOICED_FLAG == 1)
|
||||
{
|
||||
// generate unvoiced samples here
|
||||
if (m_RNG & 1)
|
||||
m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/
|
||||
else
|
||||
m_excitation_data = 0x40;
|
||||
}
|
||||
else /* (OLD_FRAME_UNVOICED_FLAG == 0) */
|
||||
{
|
||||
// generate voiced samples here
|
||||
/* US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp
|
||||
* function has a chirp/peak and then a long chain of zeroes.
|
||||
* The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample,
|
||||
* and if the address reaches that point the ADDRESS incrementer is
|
||||
* disabled, forcing all samples beyond 51d to be == 51d
|
||||
*/
|
||||
if (m_pitch_count >= 51)
|
||||
m_excitation_data = (INT8)m_coeff->chirptable[51];
|
||||
else /*m_pitch_count < 51*/
|
||||
m_excitation_data = (INT8)m_coeff->chirptable[m_pitch_count];
|
||||
}
|
||||
|
||||
// Update LFSR *20* times every sample (once per T cycle), like patent shows
|
||||
for (i=0; i<20; i++)
|
||||
{
|
||||
bitout = ((m_RNG >> 12) & 1) ^
|
||||
((m_RNG >> 3) & 1) ^
|
||||
((m_RNG >> 2) & 1) ^
|
||||
((m_RNG >> 0) & 1);
|
||||
m_RNG <<= 1;
|
||||
m_RNG |= bitout;
|
||||
}
|
||||
this_sample = lattice_filter(); /* execute lattice filter */
|
||||
#ifdef DEBUG_GENERATION_VERBOSE
|
||||
//fprintf(stderr,"C:%01d; ",m_subcycle);
|
||||
fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
|
||||
//fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
|
||||
for (i=0; i<10; i++)
|
||||
fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]);
|
||||
fprintf(stderr,"Out:%06d ", this_sample);
|
||||
//#ifdef PERFECT_INTERPOLATION_HACK
|
||||
// fprintf(stderr,"%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar);
|
||||
//#else
|
||||
// fprintf(stderr,"x%dx%d",m_zpar,m_uv_zpar);
|
||||
//#endif
|
||||
fprintf(stderr,"\n");
|
||||
#endif
|
||||
/* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */
|
||||
while (this_sample > 16383) this_sample -= 32768;
|
||||
while (this_sample < -16384) this_sample += 32768;
|
||||
if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping
|
||||
buffer[buf_count] = clip_analog(this_sample);
|
||||
else // digital I/O pin output is 12 bits
|
||||
{
|
||||
#ifdef ALLOW_4_LSB
|
||||
// input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn
|
||||
// N taps: ^ = 0x2000;
|
||||
// output: ssss ssss ssss ssss snnn nnnn nnnn nnnN
|
||||
buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13);
|
||||
#else
|
||||
this_sample &= ~0xF;
|
||||
// input: ssss ssss ssss ssss ssnn nnnn nnnn 0000
|
||||
// N taps: ^^ ^^^ = 0x3E00;
|
||||
// output: ssss ssss ssss ssss snnn nnnn nnnN NNNN
|
||||
buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9);
|
||||
#endif
|
||||
}
|
||||
// Update all counts
|
||||
|
||||
m_subcycle++;
|
||||
if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
|
||||
{
|
||||
/* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0
|
||||
* if INHIBIT was true during the most recent frame transition.
|
||||
* The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12
|
||||
* and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles,
|
||||
* which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17
|
||||
* (and hence INHIBIT itself 2 t-cycles later). We do it here because it is
|
||||
* convenient and should make no difference in output.
|
||||
*/
|
||||
if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_zero = 1;
|
||||
if ((m_IP == 0)&&(m_pitch_zero==1)) m_pitch_zero = 0;
|
||||
if (m_IP == 7) // RESETL4
|
||||
{
|
||||
// Latch OLDE and OLDP
|
||||
OLD_FRAME_SILENCE_FLAG = NEW_FRAME_SILENCE_FLAG; // m_OLDE
|
||||
OLD_FRAME_UNVOICED_FLAG = NEW_FRAME_UNVOICED_FLAG; // m_OLDP
|
||||
/* if TALK was clear last frame, halt speech now, since TALKD (latched from TALK on new frame) just went inactive. */
|
||||
#ifdef DEBUG_GENERATION
|
||||
fprintf(stderr,"RESETL4, about to update status: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
#ifdef DEBUG_GENERATION
|
||||
if (m_TALK == 0)
|
||||
fprintf(stderr,"tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n");
|
||||
#endif
|
||||
m_TALKD = m_TALK; // TALKD is latched from TALK
|
||||
update_fifo_status_and_ints(); // to trigger an interrupt if TALK_STATUS is now inactive
|
||||
m_TALK = m_SPEN; // TALK is latched from SPEN
|
||||
#ifdef DEBUG_GENERATION
|
||||
fprintf(stderr,"RESETL4, status updated: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
|
||||
#endif
|
||||
}
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC = 0;
|
||||
m_IP++;
|
||||
m_IP&=0x7;
|
||||
}
|
||||
else if (m_subcycle == 3)
|
||||
{
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC++;
|
||||
}
|
||||
m_pitch_count++;
|
||||
if ((m_pitch_count >= m_current_pitch)||(m_pitch_zero == 1)) m_pitch_count = 0;
|
||||
m_pitch_count &= 0x1FF;
|
||||
}
|
||||
else if (m_subcycle == 3)
|
||||
else // m_TALKD == 0
|
||||
{
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC++;
|
||||
m_subcycle++;
|
||||
if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
|
||||
{
|
||||
if (m_IP == 7) // RESETL4
|
||||
{
|
||||
m_TALKD = m_TALK; // TALKD is latched from TALK
|
||||
m_TALK = m_SPEN; // TALK is latched from SPEN
|
||||
}
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC = 0;
|
||||
m_IP++;
|
||||
m_IP&=0x7;
|
||||
}
|
||||
else if (m_subcycle == 3)
|
||||
{
|
||||
m_subcycle = m_subc_reload;
|
||||
m_PC++;
|
||||
}
|
||||
buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */
|
||||
}
|
||||
buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */
|
||||
buf_count++;
|
||||
size--;
|
||||
buf_count++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1246,6 +1240,9 @@ void tms5220_device::process_command(unsigned char cmd)
|
||||
m_speechrom->read(1);
|
||||
}
|
||||
m_SPEN = 1;
|
||||
#ifdef FAST_START_HACK
|
||||
m_TALK = 1;
|
||||
#endif
|
||||
m_DDIS = 0;
|
||||
m_zpar = 1; // zero all the parameters
|
||||
m_uv_zpar = 1; // zero k4-k10 as well
|
||||
|
Loading…
Reference in New Issue
Block a user