+}
+
+void
+analogtv_draw(analogtv *it, double noiselevel,
+ const analogtv_reception *const *recs, unsigned rec_count)
+{
+ int i,lineno;
+ int /*bigloadchange,*/drawcount;
+ double baseload;
+ int overall_top, overall_bot;
+
+ /* AnalogTV isn't very interesting if there isn't enough RAM. */
+ if (!it->image)
+ return;
+
+ it->rx_signal_level = noiselevel;
+ for (i = 0; i != rec_count; ++i) {
+ const analogtv_reception *rec = recs[i];
+ double level = rec->level;
+ analogtv_input *inp=rec->input;
+
+ it->rx_signal_level =
+ sqrt(it->rx_signal_level * it->rx_signal_level +
+ (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
+ rec->ghostfir[2] + rec->ghostfir[3]))));
+
+ /* duplicate the first line into the Nth line to ease wraparound computation */
+ memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
+ ANALOGTV_H * sizeof(inp->signal[0][0]));
+ }
+
+ analogtv_setup_frame(it);
+ analogtv_set_demod(it);
+
+ it->random0 = random();
+ it->random1 = random();
+ it->noiselevel = noiselevel;
+ it->recs = recs;
+ it->rec_count = rec_count;
+ threadpool_run(&it->threads, analogtv_thread_add_signals);
+ threadpool_wait(&it->threads);
+
+ it->channel_change_cycles=0;
+
+ /* rx_signal has an extra 2 lines at the end, where we copy the
+ first 2 lines so we can index into it while only worrying about
+ wraparound on a per-line level */
+ memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
+ &it->rx_signal[0],
+ 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
+
+ /* Repeat for signal_subtotals. */
+
+ memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
+ &it->signal_subtotals[0],
+ (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
+
+ analogtv_sync(it); /* Requires the add_signals be complete. */
+
+ baseload=0.5;
+ /* if (it->hashnoise_on) baseload=0.5; */
+
+ /*bigloadchange=1;*/
+ drawcount=0;
+ it->crtload[ANALOGTV_TOP-1]=baseload;
+ it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
+ (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
+
+ analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
+
+ for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
+ int slineno, ytop, ybot;
+ unsigned signal_offset;
+ if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
+ continue;
+
+ if (lineno==it->shrinkpulse) {
+ baseload += 0.4;
+ /*bigloadchange=1;*/
+ it->shrinkpulse=-1;
+ }
+
+#if 0
+ if (it->hashnoise_rpm>0.0 &&
+ !(bigloadchange ||
+ it->redraw_all ||
+ (slineno<20 && it->flutter_horiz_desync) ||
+ it->gaussiannoise_level>30 ||
+ ((it->gaussiannoise_level>2.0 ||
+ it->multipath) && random()%4) ||
+ linesig != it->onscreen_signature[lineno])) {
+ continue;
+ }
+ it->onscreen_signature[lineno] = linesig;
+#endif
+ drawcount++;
+
+ /*
+ Interpolate the 600-dotclock line into however many horizontal
+ screen pixels we're using, and convert to RGB.
+
+ We add some 'bloom', variations in the horizontal scan width with
+ the amount of brightness, extremely common on period TV sets. They
+ had a single oscillator which generated both the horizontal scan and
+ (during the horizontal retrace interval) the high voltage for the
+ electron beam. More brightness meant more load on the oscillator,
+ which caused an decrease in horizontal deflection. Look for
+ (bloomthisrow).
+
+ Also, the A2 did a bad job of generating horizontal sync pulses
+ during the vertical blanking interval. This, and the fact that the
+ horizontal frequency was a bit off meant that TVs usually went a bit
+ out of sync during the vertical retrace, and the top of the screen
+ would be bent a bit to the left or right. Look for (shiftthisrow).
+
+ We also add a teeny bit of left overscan, just enough to be
+ annoying, but you can still read the left column of text.
+
+ We also simulate compression & brightening on the right side of the
+ screen. Most TVs do this, but you don't notice because they overscan
+ so it's off the right edge of the CRT. But the A2 video system used
+ so much of the horizontal scan line that you had to crank the
+ horizontal width down in order to not lose the right few characters,
+ and you'd see the compression on the right edge. Associated with
+ compression is brightening; since the electron beam was scanning
+ slower, the same drive signal hit the phosphor harder. Look for
+ (squishright_i) and (squishdiv).
+ */
+
+ {
+ /* This used to be an int, I suspect by mistake. - Dave */
+ float totsignal=0;
+ float ncl/*,diff*/;
+ unsigned frac;
+ size_t end0, end1;
+ const float *p;
+
+ frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
+ p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
+ for (i=0; i != frac; i++) {
+ totsignal -= p[i];
+ }
+
+ end0 = (signal_offset + ANALOGTV_PIC_LEN);
+
+ end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
+ for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
+ totsignal += it->signal_subtotals[i];
+ }
+
+ frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
+ p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
+ for (i=0; i != frac; i++) {
+ totsignal += p[i];
+ }
+
+ totsignal *= it->agclevel;
+ ncl = 0.95f * it->crtload[lineno-1] +
+ 0.05f*(baseload +
+ (totsignal-30000)/100000.0f +
+ (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
+ : 0.0f));
+ /*diff=ncl - it->crtload[lineno];*/
+ /*bigloadchange = (diff>0.01 || diff<-0.01);*/
+ it->crtload[lineno]=ncl;
+ }
+ }
+
+ threadpool_run(&it->threads, analogtv_thread_draw_lines);
+ threadpool_wait(&it->threads);