+static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
+{
+ *a0 = (*a0 * a1) & 0xffffffffu;
+ *c0 = (c1 + a1 * *c0) & 0xffffffffu;
+}
+
+static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
+{
+ unsigned int a1 = *a, c1 = *c;
+ *a = 1, *c = 0;
+
+ while(dist)
+ {
+ if(dist & 1)
+ rnd_combine(a, c, a1, c1);
+ dist >>= 1;
+ rnd_combine(&a1, &c1, a1, c1);
+ }
+}
+
+static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
+{
+ rnd_seek_ac(&a, &c, dist);
+ return a * rnd + c;
+}
+
+static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
+{
+ float *ps=it->rx_signal + start;
+ float *pe=it->rx_signal + end;
+ float *p=ps;
+ unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
+ unsigned int fastrnd_offset;
+ float nm1,nm2;
+ float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
+
+ fastrnd_offset = fastrnd - 0x7fffffff;
+ nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
+ while (p != pe) {
+ nm2=nm1;
+ fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
+ fastrnd_offset = fastrnd - 0x7fffffff;
+ nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
+ *p++ = nm1*nm2;
+ }
+}
+
+static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
+{
+ analogtv_input *inp=rec->input;
+ float *ps=it->rx_signal + start;
+ float *pe=it->rx_signal + end;
+ float *p=ps;
+ signed char *ss=&inp->signal[0][0];
+ signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
+ signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
+ signed char *s2;
+ int i;
+ float level=rec->level;
+ float hfloss=rec->hfloss;
+ unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
+ float dp[5];
+
+ const float noise_decay = 0.99995f;
+ float noise_ampl = 1.3f * powf(noise_decay, start);
+
+ if (ec > end)
+ ec = end;
+
+ /* assert((se-ss)%4==0 && (se-s)%4==0); */
+
+ for (i = start; i < ec; i++) { /* Sometimes start > ec. */
+
+ /* Do a big noisy transition. We can make the transition noise of
+ high constant strength regardless of signal strength.
+
+ There are two separate state machines. here, One is the noise
+ process and the other is the
+
+ We don't bother with the FIR filter here
+ */
+
+ float sig0=(float)s[0];
+ unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
+ float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
+ fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
+
+ p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
+
+ noise_ampl *= noise_decay;
+
+ p++;
+ s++;
+ if (s>=se) s=ss;
+ }
+
+ dp[0]=0.0;
+ s2 = s;
+ for (i=1; i<5; i++) {
+ s2 -= 4;
+ if (s2 < ss)
+ s2 += ANALOGTV_SIGNAL_LEN;
+ dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
+ }
+
+ assert(p <= pe);
+ assert(!((pe - p) % 4));
+ while (p != pe) {
+ float sig0,sig1,sig2,sig3,sigr;
+
+ sig0=(float)s[0];
+ sig1=(float)s[1];
+ sig2=(float)s[2];
+ sig3=(float)s[3];
+
+ dp[0]=sig0+sig1+sig2+sig3;
+
+ /* Get the video out signal, and add some ghosting, typical of RF
+ monitor cables. This corresponds to a pretty long cable, but
+ looks right to me.
+ */
+
+ sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
+ dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
+ dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
+
+ p[0] += (sig0+sigr + sig2*hfloss) * level;
+ p[1] += (sig1+sigr + sig3*hfloss) * level;
+ p[2] += (sig2+sigr + sig0*hfloss) * level;
+ p[3] += (sig3+sigr + sig1*hfloss) * level;
+
+ p += 4;
+ s += 4;
+ if (s>=se) s = ss + (s-se);
+ }
+
+ assert(p == pe);
+}
+
+static void analogtv_thread_add_signals(void *thread_raw)
+{
+ const analogtv_thread *thread = (analogtv_thread *)thread_raw;
+ const analogtv *it = thread->it;
+ unsigned i, j;
+ unsigned subtotal_end;
+
+ unsigned start = thread->signal_start;
+ while(start != thread->signal_end)
+ {
+ float *p;
+
+ /* Work on 8 KB blocks; these should fit in L1. */
+ /* (Though it doesn't seem to help much on my system.) */
+ unsigned end = start + 2048;
+ if(end > thread->signal_end)
+ end = thread->signal_end;
+
+ analogtv_init_signal (it, it->noiselevel, start, end);
+
+ for (i = 0; i != it->rec_count; ++i) {
+ analogtv_add_signal (it, it->recs[i], start, end,
+ !i ? it->channel_change_cycles : 0);
+ }
+
+ assert (!(start % ANALOGTV_SUBTOTAL_LEN));
+ assert (!(end % ANALOGTV_SUBTOTAL_LEN));
+
+ p = it->rx_signal + start;
+ subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
+ for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
+ float sum = p[0];
+ for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
+ sum += p[j];
+ it->signal_subtotals[i] = sum;
+ p += ANALOGTV_SUBTOTAL_LEN;
+ }
+
+ start = end;
+ }
+}
+
+static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
+ int *ytop, int *ybot, unsigned *signal_offset)
+{
+ *slineno=lineno-ANALOGTV_TOP;
+ *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
+ it->useheight/2)*it->puheight) + it->useheight/2;
+ *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
+ it->useheight/2)*it->puheight) + it->useheight/2;
+#if 0
+ int linesig=analogtv_line_signature(input,lineno)
+ + it->hashnoise_times[lineno];
+#endif
+ *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
+ it->line_hsync[lineno];
+
+ if (*ytop==*ybot) return 0;
+ if (*ybot<0 || *ytop>it->useheight) return 0;
+ if (*ytop<0) *ytop=0;
+ if (*ybot>it->useheight) *ybot=it->useheight;
+
+ if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
+ return 1;
+}
+