From 66b36b336f1d8a5a40d93aec365ae8acfa2211df Mon Sep 17 00:00:00 2001 From: Lord-Nightmare Date: Fri, 28 Aug 2015 03:11:28 -0400 Subject: [PATCH] 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] --- src/emu/sound/tms5220.c | 575 ++++++++++++++++++++-------------------- 1 file changed, 286 insertions(+), 289 deletions(-) diff --git a/src/emu/sound/tms5220.c b/src/emu/sound/tms5220.c index db65f36d614..a812de44b38 100644 --- a/src/emu/sound/tms5220.c +++ b/src/emu/sound/tms5220.c @@ -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