From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / analogtv.c
index 1ff995d2ccef12ac4f2f52cb5abc860cce45418b..642037de54d6365f05055089efb0f9c26f694b8f 100644 (file)
   Trevor Blackwell <tlb@tlb.org>
 */
 
   Trevor Blackwell <tlb@tlb.org>
 */
 
-#ifdef HAVE_COCOA
+/*
+  2014-04-20, Dave Odell <dmo2118@gmail.com>:
+  API change: Folded analogtv_init_signal and *_add_signal into *_draw().
+  Added SMP support.
+  Replaced doubles with floats, including constants and transcendental functions.
+  Fixed a bug or two.
+*/
+
+/* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
+   - tint_control variable is used now
+   - removed unusable hashnoise code
+ */
+
+/*
+  2016-10-09, Dave Odell <dmo2118@gmail.com>:
+  Updated for new xshm.c.
+*/
+
+#ifdef HAVE_JWXYZ
 # include "jwxyz.h"
 # include "jwxyz.h"
-#else /* !HAVE_COCOA */
+#else /* !HAVE_JWXYZ */
 # include <X11/Xlib.h>
 # include <X11/Xutil.h>
 #endif
 # include <X11/Xlib.h>
 # include <X11/Xutil.h>
 #endif
+#include <limits.h>
 
 #include <assert.h>
 
 #include <assert.h>
+#include <errno.h>
 #include "utils.h"
 #include "resources.h"
 #include "analogtv.h"
 #include "yarandom.h"
 #include "grabscreen.h"
 #include "utils.h"
 #include "resources.h"
 #include "analogtv.h"
 #include "yarandom.h"
 #include "grabscreen.h"
+#include "visual.h"
 
 /* #define DEBUG 1 */
 
 
 /* #define DEBUG 1 */
 
-#ifdef DEBUG
+#if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
 /* only works on linux + freebsd */
 #include <machine/cpufunc.h>
 
 /* only works on linux + freebsd */
 #include <machine/cpufunc.h>
 
@@ -93,20 +114,22 @@ do { \
 #endif
 
 
 #endif
 
 
-#define FASTRND (fastrnd = fastrnd*1103515245+12345)
+#define FASTRND_A 1103515245
+#define FASTRND_C 12345
+#define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
 
 
-static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
-                                 int start, int end);
+static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
+                                 int start, int end, struct analogtv_yiq_s *it_yiq);
 
 
-static double puramp(analogtv *it, double tc, double start, double over)
+static float puramp(const analogtv *it, float tc, float start, float over)
 {
 {
-  double pt=it->powerup-start;
-  double ret;
-  if (pt<0.0) return 0.0;
-  if (pt>900.0 || pt/tc>8.0) return 1.0;
+  float pt=it->powerup-start;
+  float ret;
+  if (pt<0.0f) return 0.0f;
+  if (pt>900.0f || pt/tc>8.0f) return 1.0f;
 
 
-  ret=(1.0-exp(-pt/tc))*over;
-  if (ret>1.0) return 1.0;
+  ret=(1.0f-expf(-pt/tc))*over;
+  if (ret>1.0f) return 1.0f;
   return ret*ret;
 }
 
   return ret*ret;
 }
 
@@ -199,19 +222,19 @@ analogtv_set_defaults(analogtv *it, char *prefix)
 
 #ifdef DEBUG
   printf("analogtv: prefix=%s\n",prefix);
 
 #ifdef DEBUG
   printf("analogtv: prefix=%s\n",prefix);
-  printf("  use: shm=%d cmap=%d color=%d\n",
-         it->use_shm,it->use_cmap,it->use_color);
+  printf("  use: cmap=%d color=%d\n",
+         it->use_cmap,it->use_color);
   printf("  controls: tint=%g color=%g brightness=%g contrast=%g\n",
          it->tint_control, it->color_control, it->brightness_control,
          it->contrast_control);
   printf("  controls: tint=%g color=%g brightness=%g contrast=%g\n",
          it->tint_control, it->color_control, it->brightness_control,
          it->contrast_control);
-  printf("  freq_error %g: %g %d\n",
-         it->freq_error, it->freq_error_inc, it->flutter_tint);
+/*  printf("  freq_error %g: %g %d\n",
+         it->freq_error, it->freq_error_inc, it->flutter_tint); */
   printf("  desync: %g %d\n",
          it->horiz_desync, it->flutter_horiz_desync);
   printf("  hashnoise rpm: %g\n",
          it->hashnoise_rpm);
   printf("  desync: %g %d\n",
          it->horiz_desync, it->flutter_horiz_desync);
   printf("  hashnoise rpm: %g\n",
          it->hashnoise_rpm);
-  printf("  vis: %d %d %d\n",
-         it->visclass, it->visbits, it->visdepth);
+  printf("  vis: %d %d\n",
+         it->visclass, it->visdepth);
   printf("  shift: %d-%d %d-%d %d-%d\n",
          it->red_invprec,it->red_shift,
          it->green_invprec,it->green_shift,
   printf("  shift: %d-%d %d-%d %d-%d\n",
          it->red_invprec,it->red_shift,
          it->green_invprec,it->green_shift,
@@ -244,13 +267,7 @@ static void
 analogtv_free_image(analogtv *it)
 {
   if (it->image) {
 analogtv_free_image(analogtv *it)
 {
   if (it->image) {
-    if (it->use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-      destroy_xshm_image(it->dpy, it->image, &it->shm_info);
-#endif
-    } else {
-      XDestroyImage(it->image);
-    }
+    destroy_xshm_image(it->dpy, it->image, &it->shm_info);
     it->image=NULL;
   }
 }
     it->image=NULL;
   }
 }
@@ -258,19 +275,23 @@ analogtv_free_image(analogtv *it)
 static void
 analogtv_alloc_image(analogtv *it)
 {
 static void
 analogtv_alloc_image(analogtv *it)
 {
-  if (it->use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-    it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
-                                &it->shm_info, it->usewidth, it->useheight);
-#endif
-    if (!it->image) it->use_shm=0;
-  }
-  if (!it->image) {
-    it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
-                             it->usewidth, it->useheight, 8, 0);
-    it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
+  /* On failure, it->image is NULL. */
+
+  unsigned bits_per_pixel = visual_pixmap_depth(it->screen, it->xgwa.visual);
+  unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
+  /* Width is in bits. */
+  unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
+
+  it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth,
+                              ZPixmap, &it->shm_info,
+                              width / bits_per_pixel, it->useheight);
+
+  if (it->image) {
+    memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
+  } else {
+    /* Not enough memory. Maybe try a smaller window. */
+    fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
   }
   }
-  memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
 }
 
 
 }
 
 
@@ -284,19 +305,18 @@ analogtv_configure(analogtv *it)
   /* If the window is very small, don't let the image we draw get lower
      than the actual TV resolution (266x200.)
 
   /* If the window is very small, don't let the image we draw get lower
      than the actual TV resolution (266x200.)
 
-     If the aspect ratio of the window is within 15% of a 4:3 ratio,
+     If the aspect ratio of the window is close to a 4:3 or 16:9 ratio,
      then scale the image to exactly fill the window.
 
      Otherwise, center the image either horizontally or vertically,
      then scale the image to exactly fill the window.
 
      Otherwise, center the image either horizontally or vertically,
-     padding on the left+right, or top+bottom, but not both.
+     letterboxing or pillarboxing (but not both).
 
      If it's very close (2.5%) to a multiple of VISLINES, make it exact
      For example, it maps 1024 => 1000.
    */
 
      If it's very close (2.5%) to a multiple of VISLINES, make it exact
      For example, it maps 1024 => 1000.
    */
-  float percent = 0.15;  /* jwz: 20% caused severe top/bottom clipping
-                                 in Pong on 1680x1050 iMac screen. */
-  float min_ratio = 4.0 / 3.0 * (1 - percent);
-  float max_ratio = 4.0 / 3.0 * (1 + percent);
+  float percent = 0.15;
+  float min_ratio =  4.0 / 3.0 * (1 - percent);
+  float max_ratio = 16.0 / 9.0 * (1 + percent);
   float ratio;
   float height_snap=0.025;
 
   float ratio;
   float height_snap=0.025;
 
@@ -304,10 +324,10 @@ analogtv_configure(analogtv *it)
   wlim = it->xgwa.width;
   ratio = wlim / (float) hlim;
 
   wlim = it->xgwa.width;
   ratio = wlim / (float) hlim;
 
-#ifdef USE_IPHONE
+#ifdef HAVE_MOBILE
   /* Fill the whole iPhone screen, even though that distorts the image. */
   /* Fill the whole iPhone screen, even though that distorts the image. */
-  min_ratio = 320.0 / 480.0 * (1 - percent);
-  max_ratio = 480.0 / 320.0 * (1 + percent);
+  min_ratio = 0;
+  max_ratio = 10;
 #endif
 
   if (wlim < 266 || hlim < 200)
 #endif
 
   if (wlim < 266 || hlim < 200)
@@ -329,7 +349,7 @@ analogtv_configure(analogtv *it)
                wlim, hlim, min_ratio, ratio, max_ratio);
 # endif
     }
                wlim, hlim, min_ratio, ratio, max_ratio);
 # endif
     }
-  else if (ratio > max_ratio)
+  else if (ratio >= max_ratio)
     {
       wlim = hlim*max_ratio;
 # ifdef DEBUG
     {
       wlim = hlim*max_ratio;
 # ifdef DEBUG
@@ -339,7 +359,7 @@ analogtv_configure(analogtv *it)
                min_ratio, ratio, max_ratio);
 # endif
     }
                min_ratio, ratio, max_ratio);
 # endif
     }
-  else /* ratio < min_ratio */
+  else /* ratio <= min_ratio */
     {
       hlim = wlim/min_ratio;
 # ifdef DEBUG
     {
       hlim = wlim/min_ratio;
 # ifdef DEBUG
@@ -352,7 +372,7 @@ analogtv_configure(analogtv *it)
 
 
   height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
 
 
   height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
-  if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
+  if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
     {
       hlim -= height_diff;
     }
     {
       hlim -= height_diff;
     }
@@ -384,36 +404,95 @@ analogtv_reconfigure(analogtv *it)
   analogtv_configure(it);
 }
 
   analogtv_configure(it);
 }
 
+/* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
+#define ANALOGTV_SUBTOTAL_LEN 32
+
+typedef struct analogtv_thread_s
+{
+  analogtv *it;
+  unsigned thread_id;
+  size_t signal_start, signal_end;
+} analogtv_thread;
+
+#define SIGNAL_OFFSET(thread_id) \
+  ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
+
+static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
+                                  unsigned thread_id)
+{
+  analogtv_thread *thread = (analogtv_thread *)thread_raw;
+  unsigned align;
+
+  thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
+  thread->thread_id = thread_id;
+
+  align = thread_memory_alignment(thread->it->dpy) /
+            sizeof(thread->it->signal_subtotals[0]);
+  if (!align)
+    align = 1;
+  align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
+
+  thread->signal_start = SIGNAL_OFFSET(thread_id);
+  thread->signal_end = thread_id + 1 == threads->count ?
+                       ANALOGTV_SIGNAL_LEN :
+                       SIGNAL_OFFSET(thread_id + 1);
+
+  return 0;
+}
+
+static void analogtv_thread_destroy(void *thread_raw)
+{
+}
+
 analogtv *
 analogtv_allocate(Display *dpy, Window window)
 {
 analogtv *
 analogtv_allocate(Display *dpy, Window window)
 {
+  static const struct threadpool_class cls = {
+    sizeof(analogtv_thread),
+    analogtv_thread_create,
+    analogtv_thread_destroy
+  };
+
   XGCValues gcv;
   analogtv *it=NULL;
   int i;
   XGCValues gcv;
   analogtv *it=NULL;
   int i;
+  const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
 
   analogtv_init();
 
   it=(analogtv *)calloc(1,sizeof(analogtv));
   if (!it) return 0;
 
   analogtv_init();
 
   it=(analogtv *)calloc(1,sizeof(analogtv));
   if (!it) return 0;
+  it->threads.count=0;
+  it->rx_signal=NULL;
+  it->signal_subtotals=NULL;
+
   it->dpy=dpy;
   it->window=window;
 
   it->dpy=dpy;
   it->window=window;
 
+  if (thread_malloc((void **)&it->rx_signal, dpy,
+                    sizeof(it->rx_signal[0]) * rx_signal_len))
+    goto fail;
+
+  assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
+  if (thread_malloc((void **)&it->signal_subtotals, dpy,
+                    sizeof(it->signal_subtotals[0]) *
+                     (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
+    goto fail;
+
+  if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
+    goto fail;
+
+  assert(it->threads.count);
+
   it->shrinkpulse=-1;
 
   it->n_colors=0;
 
   it->shrinkpulse=-1;
 
   it->n_colors=0;
 
-#ifdef HAVE_XSHM_EXTENSION
-  it->use_shm=1;
-#else
-  it->use_shm=0;
-#endif
-
   XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
 
   it->screen=it->xgwa.screen;
   it->colormap=it->xgwa.colormap;
   XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
 
   it->screen=it->xgwa.screen;
   it->colormap=it->xgwa.colormap;
-  it->visclass=it->xgwa.visual->class;
-  it->visbits=it->xgwa.visual->bits_per_rgb;
+  it->visclass=visual_class(it->xgwa.screen, it->xgwa.visual);
   it->visdepth=it->xgwa.depth;
   if (it->visclass == TrueColor || it->visclass == DirectColor) {
     if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
   it->visdepth=it->xgwa.depth;
   if (it->visclass == TrueColor || it->visclass == DirectColor) {
     if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
@@ -432,9 +511,8 @@ analogtv_allocate(Display *dpy, Window window)
     it->use_color=0;
   }
 
     it->use_color=0;
   }
 
-  it->red_mask=it->xgwa.visual->red_mask;
-  it->green_mask=it->xgwa.visual->green_mask;
-  it->blue_mask=it->xgwa.visual->blue_mask;
+  visual_rgb_masks (it->xgwa.screen, it->xgwa.visual,
+                    &it->red_mask, &it->green_mask, &it->blue_mask);
   it->red_shift=it->red_invprec=-1;
   it->green_shift=it->green_invprec=-1;
   it->blue_shift=it->blue_invprec=-1;
   it->red_shift=it->red_invprec=-1;
   it->green_shift=it->green_invprec=-1;
   it->blue_shift=it->blue_invprec=-1;
@@ -479,7 +557,13 @@ analogtv_allocate(Display *dpy, Window window)
   return it;
 
  fail:
   return it;
 
  fail:
-  if (it) free(it);
+  if (it) {
+    if(it->threads.count)
+      threadpool_destroy(&it->threads);
+    thread_free(it->signal_subtotals);
+    thread_free(it->rx_signal);
+    free(it);
+  }
   return NULL;
 }
 
   return NULL;
 }
 
@@ -487,19 +571,16 @@ void
 analogtv_release(analogtv *it)
 {
   if (it->image) {
 analogtv_release(analogtv *it)
 {
   if (it->image) {
-    if (it->use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-      destroy_xshm_image(it->dpy, it->image, &it->shm_info);
-#endif
-    } else {
-      XDestroyImage(it->image);
-    }
+    destroy_xshm_image(it->dpy, it->image, &it->shm_info);
     it->image=NULL;
   }
   if (it->gc) XFreeGC(it->dpy, it->gc);
   it->gc=NULL;
   if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
   it->n_colors=0;
     it->image=NULL;
   }
   if (it->gc) XFreeGC(it->dpy, it->gc);
   it->gc=NULL;
   if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
   it->n_colors=0;
+  threadpool_destroy(&it->threads);
+  thread_free(it->rx_signal);
+  thread_free(it->signal_subtotals);
   free(it);
 }
 
   free(it);
 }
 
@@ -656,19 +737,19 @@ analogtv_line_signature(analogtv_input *input, int lineno)
 */
 
 static void
 */
 
 static void
-analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
-                     int start, int end)
+analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
+                     int start, int end, struct analogtv_yiq_s *it_yiq)
 {
   enum {MAXDELAY=32};
   int i;
 {
   enum {MAXDELAY=32};
   int i;
-  double *sp;
+  const float *sp;
   int phasecorr=(signal-it->rx_signal)&3;
   struct analogtv_yiq_s *yiq;
   int colormode;
   int phasecorr=(signal-it->rx_signal)&3;
   struct analogtv_yiq_s *yiq;
   int colormode;
-  double agclevel=it->agclevel;
-  double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
-  double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
-  double multiq2[4];
+  float agclevel=it->agclevel;
+  float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
+  float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
+  float multiq2[4];
 
   {
 
 
   {
 
@@ -680,11 +761,8 @@ analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
     colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
 
     if (colormode) {
     colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
 
     if (colormode) {
-      double tint_i = -cos((103 + it->color_control)*3.1415926/180);
-      double tint_q = sin((103 + it->color_control)*3.1415926/180);
-
-      multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
-      multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
+      multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
+      multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
       multiq2[2]=-multiq2[0];
       multiq2[3]=-multiq2[1];
     }
       multiq2[2]=-multiq2[0];
       multiq2[3]=-multiq2[1];
     }
@@ -703,14 +781,14 @@ analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
 #endif
 
   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
 #endif
 
   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
-  for (i=0; i<5; i++) dp[i]=0.0;
+  for (i=0; i<5; i++) dp[i]=0.0f;
 
   assert(start>=0);
   assert(end < ANALOGTV_PIC_LEN+10);
 
   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
   for (i=0; i<24; i++) dp[i]=0.0;
 
   assert(start>=0);
   assert(end < ANALOGTV_PIC_LEN+10);
 
   dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
   for (i=0; i<24; i++) dp[i]=0.0;
-  for (i=start, yiq=it->yiq+start, sp=signal+start;
+  for (i=start, yiq=it_yiq+start, sp=signal+start;
        i<end;
        i++, dp--, yiq++, sp++) {
 
        i<end;
        i++, dp--, yiq++, sp++) {
 
@@ -730,13 +808,13 @@ analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
        Delay about 2 */
 
        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
        Delay about 2 */
 
-    dp[0] = sp[0] * 0.0469904257251935 * agclevel;
-    dp[8] = (+1.0*(dp[6]+dp[0])
-             +4.0*(dp[5]+dp[1])
-             +7.0*(dp[4]+dp[2])
-             +8.0*(dp[3])
-             -0.0176648*dp[12]
-             -0.4860288*dp[10]);
+    dp[0] = sp[0] * 0.0469904257251935f * agclevel;
+    dp[8] = (+1.0f*(dp[6]+dp[0])
+             +4.0f*(dp[5]+dp[1])
+             +7.0f*(dp[4]+dp[2])
+             +8.0f*(dp[3])
+             -0.0176648f*dp[12]
+             -0.4860288f*dp[10]);
     yiq->y = dp[8] + brightadd;
   }
 
     yiq->y = dp[8] + brightadd;
   }
 
@@ -744,10 +822,10 @@ analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
     dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
     for (i=0; i<27; i++) dp[i]=0.0;
 
     dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
     for (i=0; i<27; i++) dp[i]=0.0;
 
-    for (i=start, yiq=it->yiq+start, sp=signal+start;
+    for (i=start, yiq=it_yiq+start, sp=signal+start;
          i<end;
          i++, dp--, yiq++, sp++) {
          i<end;
          i++, dp--, yiq++, sp++) {
-      double sig=*sp;
+      float sig=*sp;
 
       /* Filter I and Q with a 3-pole low-pass Butterworth filter at
          1.5 MHz with an extra zero at 3.5 MHz, from
 
       /* Filter I and Q with a 3-pole low-pass Butterworth filter at
          1.5 MHz with an extra zero at 3.5 MHz, from
@@ -755,21 +833,21 @@ analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
          Delay about 3.
       */
 
          Delay about 3.
       */
 
-      dp[0] = sig*multiq2[i&3] * 0.0833333333333;
+      dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
       yiq->i=dp[8] = (dp[5] + dp[0]
       yiq->i=dp[8] = (dp[5] + dp[0]
-                      +3.0*(dp[4] + dp[1])
-                      +4.0*(dp[3] + dp[2])
-                      -0.3333333333 * dp[10]);
+                      +3.0f*(dp[4] + dp[1])
+                      +4.0f*(dp[3] + dp[2])
+                      -0.3333333333f * dp[10]);
 
 
-      dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
+      dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
       yiq->q=dp[24] = (dp[16+5] + dp[16+0]
       yiq->q=dp[24] = (dp[16+5] + dp[16+0]
-                       +3.0*(dp[16+4] + dp[16+1])
-                       +4.0*(dp[16+3] + dp[16+2])
-                       -0.3333333333 * dp[24+2]);
+                       +3.0f*(dp[16+4] + dp[16+1])
+                       +4.0f*(dp[16+3] + dp[16+2])
+                       -0.3333333333f * dp[24+2]);
     }
   } else {
     }
   } else {
-    for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
-      yiq->i = yiq->q = 0.0;
+    for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
+      yiq->i = yiq->q = 0.0f;
     }
   }
 }
     }
   }
 }
@@ -796,7 +874,7 @@ analogtv_setup_teletext(analogtv_input *input)
 void
 analogtv_setup_frame(analogtv *it)
 {
 void
 analogtv_setup_frame(analogtv *it)
 {
-  int i,x,y;
+  /*  int i,x,y;*/
 
   it->redraw_all=0;
 
 
   it->redraw_all=0;
 
@@ -808,10 +886,13 @@ analogtv_setup_frame(analogtv *it)
       ((int)(random()&0xff)-0x80) * 0.000001;
   }
 
       ((int)(random()&0xff)-0x80) * 0.000001;
   }
 
+  /* it wasn't used
   for (i=0; i<ANALOGTV_V; i++) {
     it->hashnoise_times[i]=0;
   }
   for (i=0; i<ANALOGTV_V; i++) {
     it->hashnoise_times[i]=0;
   }
+  */
 
 
+  /* let's leave it to process shrinkpulse */
   if (it->hashnoise_enable && !it->hashnoise_on) {
     if (random()%10000==0) {
       it->hashnoise_on=1;
   if (it->hashnoise_enable && !it->hashnoise_on) {
     if (random()%10000==0) {
       it->hashnoise_on=1;
@@ -821,6 +902,8 @@ analogtv_setup_frame(analogtv *it)
   if (random()%1000==0) {
     it->hashnoise_on=0;
   }
   if (random()%1000==0) {
     it->hashnoise_on=0;
   }
+
+#if 0  /* never used */
   if (it->hashnoise_on) {
     it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
       ((int)(random()%2000)-1000)*0.1;
   if (it->hashnoise_on) {
     it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
       ((int)(random()%2000)-1000)*0.1;
@@ -830,11 +913,13 @@ analogtv_setup_frame(analogtv *it)
   }
   if (it->hashnoise_rpm > 0.0) {
     int hni;
   }
   if (it->hashnoise_rpm > 0.0) {
     int hni;
+    double hni_double;
     int hnc=it->hashnoise_counter; /* in 24.8 format */
 
     /* Convert rpm of a 16-pole motor into dots in 24.8 format */
     int hnc=it->hashnoise_counter; /* in 24.8 format */
 
     /* Convert rpm of a 16-pole motor into dots in 24.8 format */
-    hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
-                (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
+    hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
+                (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
+    hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
 
     while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
       y=(hnc>>8)/ANALOGTV_H;
 
     while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
       y=(hnc>>8)/ANALOGTV_H;
@@ -843,10 +928,18 @@ analogtv_setup_frame(analogtv *it)
       if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
         it->hashnoise_times[y]=x;
       }
       if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
         it->hashnoise_times[y]=x;
       }
-      hnc += hni + (int)(random()%65536)-32768;
+      /* hnc += hni + (int)(random()%65536)-32768; */
+      {
+        hnc += (int)(random()%65536)-32768;
+        if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
+        hnc += hni;
+      }
     }
     }
-/*    hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
   }
   }
+#endif /* 0 */
+
+/*    hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
+
 
   if (it->rx_signal_level != 0.0)
     it->agclevel = 1.0/it->rx_signal_level;
 
   if (it->rx_signal_level != 0.0)
     it->agclevel = 1.0/it->rx_signal_level;
@@ -902,37 +995,37 @@ analogtv_sync(analogtv *it)
   int cur_vsync=it->cur_vsync;
   int lineno = 0;
   int i,j;
   int cur_vsync=it->cur_vsync;
   int lineno = 0;
   int i,j;
-  double osc,filt;
-  double *sp;
-  double cbfc=1.0/128.0;
+  float osc,filt;
+  float *sp;
+  float cbfc=1.0f/128.0f;
 
 /*  sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
   for (i=-32; i<32; i++) {
     lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
     sp = it->rx_signal + lineno*ANALOGTV_H;
 
 /*  sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
   for (i=-32; i<32; i++) {
     lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
     sp = it->rx_signal + lineno*ANALOGTV_H;
-    filt=0.0;
+    filt=0.0f;
     for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
       filt += sp[j];
     }
     filt *= it->agclevel;
 
     for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
       filt += sp[j];
     }
     filt *= it->agclevel;
 
-    osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
+    osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
 
 
-    if (osc >= 1.05+0.0002 * filt) break;
+    if (osc >= 1.05f+0.0002f * filt) break;
   }
   cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
 
   for (lineno=0; lineno<ANALOGTV_V; lineno++) {
 
     if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
   }
   cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
 
   for (lineno=0; lineno<ANALOGTV_V; lineno++) {
 
     if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
-
-      sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
-                            )*ANALOGTV_H + cur_hsync;
+      unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
+      if (!lineno2) lineno2 = ANALOGTV_V;
+      sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
       for (i=-8; i<8; i++) {
       for (i=-8; i<8; i++) {
-        osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
+        osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
         filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
 
         filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
 
-        if (osc >= 1.005 + 0.0001*filt) break;
+        if (osc >= 1.005f + 0.0001f*filt) break;
       }
       cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
     }
       }
       cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
     }
@@ -949,19 +1042,19 @@ analogtv_sync(analogtv *it)
     if (lineno>15) {
       sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
       for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
     if (lineno>15) {
       sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
       for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
-        it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
+        it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
           sp[i]*it->agclevel*cbfc;
       }
     }
 
     {
           sp[i]*it->agclevel*cbfc;
       }
     }
 
     {
-      double tot=0.1;
-      double cbgain;
+      float tot=0.1f;
+      float cbgain;
 
       for (i=0; i<4; i++) {
         tot += it->cb_phase[i]*it->cb_phase[i];
       }
 
       for (i=0; i<4; i++) {
         tot += it->cb_phase[i]*it->cb_phase[i];
       }
-      cbgain = 32.0/sqrt(tot);
+      cbgain = 32.0f/sqrtf(tot);
 
       for (i=0; i<4; i++) {
         it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
 
       for (i=0; i<4; i++) {
         it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
@@ -983,14 +1076,14 @@ analogtv_sync(analogtv *it)
 }
 
 static double
 }
 
 static double
-analogtv_levelmult(analogtv *it, int level)
+analogtv_levelmult(const analogtv *it, int level)
 {
   static const double levelfac[3]={-7.5, 5.5, 24.5};
   return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
 }
 
 static int
 {
   static const double levelfac[3]={-7.5, 5.5, 24.5};
   return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
 }
 
 static int
-analogtv_level(analogtv *it, int y, int ytop, int ybot)
+analogtv_level(const analogtv *it, int y, int ytop, int ybot)
 {
   int level;
   if (ybot-ytop>=7) {
 {
   int level;
   if (ybot-ytop>=7) {
@@ -1052,8 +1145,213 @@ analogtv_setup_levels(analogtv *it, double avgheight)
   }
 }
 
   }
 }
 
+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;
+}
+
 static void
 static void
-analogtv_blast_imagerow(analogtv *it,
+analogtv_blast_imagerow(const analogtv *it,
                         float *rgbf, float *rgbf_end,
                         int ytop, int ybot)
 {
                         float *rgbf, float *rgbf_end,
                         int ytop, int ybot)
 {
@@ -1061,14 +1359,16 @@ analogtv_blast_imagerow(analogtv *it,
   float *rpf;
   char *level_copyfrom[3];
   int xrepl=it->xrepl;
   float *rpf;
   char *level_copyfrom[3];
   int xrepl=it->xrepl;
+  unsigned lineheight = ybot - ytop;
+  if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
   for (i=0; i<3; i++) level_copyfrom[i]=NULL;
 
   for (y=ytop; y<ybot; y++) {
   for (i=0; i<3; i++) level_copyfrom[i]=NULL;
 
   for (y=ytop; y<ybot; y++) {
-    int level=it->leveltable[ybot-ytop][y-ytop].index;
-    double levelmult=it->leveltable[ybot-ytop][y-ytop].value;
-    char *rowdata;
+    char *rowdata=it->image->data + y*it->image->bytes_per_line;
+    unsigned line = y-ytop;
 
 
-    rowdata=it->image->data + y*it->image->bytes_per_line;
+    int level=it->leveltable[lineheight][line].index;
+    float levelmult=it->leveltable[lineheight][line].value;
 
     /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
        why standard graphics sw has to be fast, or else people will have to
 
     /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
        why standard graphics sw has to be fast, or else people will have to
@@ -1118,7 +1418,7 @@ analogtv_blast_imagerow(analogtv *it,
                float_extraction_works &&
                it->image->byte_order==localbyteorder) {
         unsigned short *pixelptr=(unsigned short *)rowdata;
                float_extraction_works &&
                it->image->byte_order==localbyteorder) {
         unsigned short *pixelptr=(unsigned short *)rowdata;
-        double r2,g2,b2;
+        float r2,g2,b2;
         float_extract_t r1,g1,b1;
         unsigned short pix;
 
         float_extract_t r1,g1,b1;
         unsigned short pix;
 
@@ -1180,176 +1480,78 @@ analogtv_blast_imagerow(analogtv *it,
   }
 }
 
   }
 }
 
-void
-analogtv_draw(analogtv *it)
+static void analogtv_thread_draw_lines(void *thread_raw)
 {
 {
-  int i,j,x,y,lineno;
-  int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
-  float *rgb_start, *rgb_end;
-  double pixbright;
-  int pixmultinc;
-  int /*bigloadchange,*/drawcount;
-  double baseload;
-  double puheight;
-  int overall_top, overall_bot;
+  const analogtv_thread *thread = (analogtv_thread *)thread_raw;
+  const analogtv *it = thread->it;
 
 
-  float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
-  float *raw_rgb_end=raw_rgb_start+3*it->subwidth;
-  float *rrp;
+  int lineno;
 
 
-  if (! raw_rgb_start) return;
-  analogtv_setup_frame(it);
-  analogtv_set_demod(it);
+  float *raw_rgb_start;
+  float *raw_rgb_end;
+  raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
 
 
-  /* 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]));
-
-  analogtv_sync(it);
-
-  baseload=0.5;
-  /* if (it->hashnoise_on) baseload=0.5; */
-
-  /*bigloadchange=1;*/
-  drawcount=0;
-  it->crtload[ANALOGTV_TOP-1]=baseload;
-  puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
-    (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
+  if (! raw_rgb_start) return;
 
 
-  analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
+  raw_rgb_end=raw_rgb_start+3*it->subwidth;
 
 
-  overall_top=it->useheight;
-  overall_bot=0;
+  for (lineno=ANALOGTV_TOP + thread->thread_id;
+       lineno<ANALOGTV_BOT;
+       lineno += it->threads.count) {
+    int i,j,x,y;
 
 
-  for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
-    int slineno=lineno-ANALOGTV_TOP;
-    int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
-                    it->useheight/2)*puheight) + it->useheight/2;
-    int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
-                    it->useheight/2)*puheight) + it->useheight/2;
-#if 0
-    int linesig=analogtv_line_signature(input,lineno)
-      + it->hashnoise_times[lineno];
-#endif
-    double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
-                                      ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
-                    it->line_hsync[lineno]);
+    int slineno, ytop, ybot;
+    unsigned signal_offset;
 
 
-    if (ytop==ybot) continue;
-    if (ybot<0 || ytop>it->useheight) continue;
-    if (ytop<0) ytop=0;
-    if (ybot>it->useheight) ybot=it->useheight;
+    const float *signal;
 
 
-    if (ybot > ytop+ANALOGTV_MAX_LINEHEIGHT) ybot=ytop+ANALOGTV_MAX_LINEHEIGHT;
+    int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
+    float *rgb_start, *rgb_end;
+    float pixbright;
+    int pixmultinc;
 
 
-    if (ytop < overall_top) overall_top=ytop;
-    if (ybot > overall_bot) overall_bot=ybot;
+    float *rrp;
 
 
-    if (lineno==it->shrinkpulse) {
-      baseload += 0.4;
-      /*bigloadchange=1;*/
-      it->shrinkpulse=-1;
-    }
+    struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
 
 
-#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])) {
+    if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
+        &signal_offset))
       continue;
       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).
-    */
+    signal = it->rx_signal + signal_offset;
 
     {
 
     {
-      int totsignal=0;
-      double ncl/*,diff*/;
 
 
-      for (i=0; i<ANALOGTV_PIC_LEN; i++) {
-        totsignal += signal[i];
-      }
-      totsignal *= it->agclevel;
-      ncl = 0.95 * it->crtload[lineno-1] +
-        0.05*(baseload +
-              (totsignal-30000)/100000.0 +
-              (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
-               : 0.0));
-      /*diff=ncl - it->crtload[lineno];*/
-      /*bigloadchange = (diff>0.01 || diff<-0.01);*/
-      it->crtload[lineno]=ncl;
-    }
-
-    {
-      double bloomthisrow,shiftthisrow;
-      double viswidth,middle;
-      double scanwidth;
+      float bloomthisrow,shiftthisrow;
+      float viswidth,middle;
+      float scanwidth;
       int scw,scl,scr;
 
       int scw,scl,scr;
 
-      bloomthisrow = -10.0 * it->crtload[lineno];
-      if (bloomthisrow<-10.0) bloomthisrow=-10.0;
-      if (bloomthisrow>2.0) bloomthisrow=2.0;
+      bloomthisrow = -10.0f * it->crtload[lineno];
+      if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
+      if (bloomthisrow>2.0f) bloomthisrow=2.0f;
       if (slineno<16) {
       if (slineno<16) {
-        shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
-                                         (0.7+cos(slineno*0.6)));
+        shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
+                                         (0.7f+cosf(slineno*0.6f)));
       } else {
       } else {
-        shiftthisrow=0.0;
+        shiftthisrow=0.0f;
       }
 
       }
 
-      viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
+      viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
       middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
 
       middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
 
-      scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
+      scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
 
       scw=it->subwidth*scanwidth;
       if (scw>it->subwidth) scw=it->usewidth;
       scl=it->subwidth/2 - scw/2;
       scr=it->subwidth/2 + scw/2;
 
 
       scw=it->subwidth*scanwidth;
       if (scw>it->subwidth) scw=it->usewidth;
       scl=it->subwidth/2 - scw/2;
       scr=it->subwidth/2 + scw/2;
 
-      pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
-      scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
+      pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
+      scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
       scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
       scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
-      squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
-                                            - it->squish_control)) *65536.0);
+      squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
+                                            - it->squish_control)) *65536.0f);
       squishdiv=it->subwidth/15;
 
       rgb_start=raw_rgb_start+scl*3;
       squishdiv=it->subwidth/15;
 
       rgb_start=raw_rgb_start+scl*3;
@@ -1360,9 +1562,9 @@ analogtv_draw(analogtv *it)
 #ifdef DEBUG
       if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
                     lineno,
 #ifdef DEBUG
       if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
                     lineno,
-                    scanstart_i/65536.0,
-                    squishright_i/65536.0,
-                    scanend_i/65536.0,
+                    scanstart_i/65536.0f,
+                    squishright_i/65536.0f,
+                    scanend_i/65536.0f,
                     scl,scr,scw);
 #endif
     }
                     scl,scr,scw);
 #endif
     }
@@ -1370,14 +1572,13 @@ analogtv_draw(analogtv *it)
     if (it->use_cmap) {
       for (y=ytop; y<ybot; y++) {
         int level=analogtv_level(it, y, ytop, ybot);
     if (it->use_cmap) {
       for (y=ytop; y<ybot; y++) {
         int level=analogtv_level(it, y, ytop, ybot);
-        double levelmult=analogtv_levelmult(it, level);
-        double levelmult_y = levelmult * it->contrast_control
-          * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
-        double levelmult_iq = levelmult * 0.090;
+        float levelmult=analogtv_levelmult(it, level);
+        float levelmult_y = levelmult * it->contrast_control
+          * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
+        float levelmult_iq = levelmult * 0.090f;
 
 
-        struct analogtv_yiq_s *yiq=it->yiq;
         analogtv_ntsc_to_yiq(it, lineno, signal,
         analogtv_ntsc_to_yiq(it, lineno, signal,
-                             (scanstart_i>>16)-10, (scanend_i>>16)+10);
+                             (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
         pixmultinc=pixrate;
 
         x=0;
         pixmultinc=pixrate;
 
         x=0;
@@ -1389,21 +1590,21 @@ analogtv_draw(analogtv *it)
         }
 
         while (i<scanend_i && x<it->usewidth) {
         }
 
         while (i<scanend_i && x<it->usewidth) {
-          double pixfrac=(i&0xffff)/65536.0;
-          double invpixfrac=(1.0-pixfrac);
+          float pixfrac=(i&0xffff)/65536.0f;
+          float invpixfrac=(1.0f-pixfrac);
           int pati=i>>16;
           int yli,ili,qli,cmi;
 
           int pati=i>>16;
           int yli,ili,qli,cmi;
 
-          double interpy=(yiq[pati].y*invpixfrac
-                          + yiq[pati+1].y*pixfrac) * levelmult_y;
-          double interpi=(yiq[pati].i*invpixfrac
-                          + yiq[pati+1].i*pixfrac) * levelmult_iq;
-          double interpq=(yiq[pati].q*invpixfrac
-                          + yiq[pati+1].q*pixfrac) * levelmult_iq;
+          float interpy=(yiq[pati].y*invpixfrac
+                         + yiq[pati+1].y*pixfrac) * levelmult_y;
+          float interpi=(yiq[pati].i*invpixfrac
+                         + yiq[pati+1].i*pixfrac) * levelmult_iq;
+          float interpq=(yiq[pati].q*invpixfrac
+                         + yiq[pati+1].q*pixfrac) * levelmult_iq;
 
           yli = (int)(interpy * it->cmap_y_levels);
 
           yli = (int)(interpy * it->cmap_y_levels);
-          ili = (int)((interpi+0.5) * it->cmap_i_levels);
-          qli = (int)((interpq+0.5) * it->cmap_q_levels);
+          ili = (int)((interpi+0.5f) * it->cmap_i_levels);
+          qli = (int)((interpq+0.5f) * it->cmap_q_levels);
           if (yli<0) yli=0;
           if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
           if (ili<0) ili=0;
           if (yli<0) yli=0;
           if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
           if (ili<0) ili=0;
@@ -1439,12 +1640,11 @@ analogtv_draw(analogtv *it)
       }
     }
     else {
       }
     }
     else {
-      struct analogtv_yiq_s *yiq=it->yiq;
       analogtv_ntsc_to_yiq(it, lineno, signal,
       analogtv_ntsc_to_yiq(it, lineno, signal,
-                           (scanstart_i>>16)-10, (scanend_i>>16)+10);
+                           (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
 
 
-      pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
-        / (0.5+0.5*puheight) * 1024.0/100.0;
+      pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
+        / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
       pixmultinc=pixrate;
       i=scanstart_i; rrp=rgb_start;
       while (i<0 && rrp!=rgb_end) {
       pixmultinc=pixrate;
       i=scanstart_i; rrp=rgb_start;
       while (i<0 && rrp!=rgb_end) {
@@ -1453,14 +1653,14 @@ analogtv_draw(analogtv *it)
         rrp+=3;
       }
       while (i<scanend_i && rrp!=rgb_end) {
         rrp+=3;
       }
       while (i<scanend_i && rrp!=rgb_end) {
-        double pixfrac=(i&0xffff)/65536.0;
-        double invpixfrac=1.0-pixfrac;
+        float pixfrac=(i&0xffff)/65536.0f;
+        float invpixfrac=1.0f-pixfrac;
         int pati=i>>16;
         int pati=i>>16;
-        double r,g,b;
+        float r,g,b;
 
 
-        double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
-        double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
-        double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
+        float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
+        float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
+        float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
 
         /*
           According to the NTSC spec, Y,I,Q are generated as:
 
         /*
           According to the NTSC spec, Y,I,Q are generated as:
@@ -1478,12 +1678,12 @@ analogtv_draw(analogtv *it)
           b = y - 1.105 i + 1.729 q
         */
 
           b = y - 1.105 i + 1.729 q
         */
 
-        r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
-        g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
-        b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
-        if (r<0.0) r=0.0;
-        if (g<0.0) g=0.0;
-        if (b<0.0) b=0.0;
+        r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
+        g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
+        b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
+        if (r<0.0f) r=0.0f;
+        if (g<0.0f) g=0.0f;
+        if (b<0.0f) b=0.0f;
         rrp[0]=r;
         rrp[1]=g;
         rrp[2]=b;
         rrp[0]=r;
         rrp[1]=g;
         rrp[2]=b;
@@ -1496,7 +1696,7 @@ analogtv_draw(analogtv *it)
         rrp+=3;
       }
       while (rrp != rgb_end) {
         rrp+=3;
       }
       while (rrp != rgb_end) {
-        rrp[0]=rrp[1]=rrp[2]=0.0;
+        rrp[0]=rrp[1]=rrp[2]=0.0f;
         rrp+=3;
       }
 
         rrp+=3;
       }
 
@@ -1504,7 +1704,182 @@ analogtv_draw(analogtv *it)
                               ytop,ybot);
     }
   }
                               ytop,ybot);
     }
   }
+
   free(raw_rgb_start);
   free(raw_rgb_start);
+}
+
+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);
+
+  /* calculate tint once per frame */
+  it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
+  it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
+  
+  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);
 
 #if 0
   /* poor attempt at visible retrace */
 
 #if 0
   /* poor attempt at visible retrace */
@@ -1528,6 +1903,18 @@ analogtv_draw(analogtv *it)
     it->need_clear=0;
   }
 
     it->need_clear=0;
   }
 
+  /*
+    Subtle change: overall_bot was the bottom of the last scan line. Now it's
+    the top of the next-after-the-last scan line. This is the same until
+    the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
+  */
+
+  overall_top=(int)(it->useheight*(1-it->puheight)/2);
+  overall_bot=(int)(it->useheight*(1+it->puheight)/2);
+
+  if (overall_top<0) overall_top=0;
+  if (overall_bot>it->useheight) overall_bot=it->useheight;
+
   if (overall_top>0) {
     XClearArea(it->dpy, it->window,
                it->screen_xo, it->screen_yo,
   if (overall_top>0) {
     XClearArea(it->dpy, it->window,
                it->screen_xo, it->screen_yo,
@@ -1540,20 +1927,11 @@ analogtv_draw(analogtv *it)
   }
 
   if (overall_bot > overall_top) {
   }
 
   if (overall_bot > overall_top) {
-    if (it->use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-      XShmPutImage(it->dpy, it->window, it->gc, it->image,
+    put_xshm_image(it->dpy, it->window, it->gc, it->image,
                    0, overall_top,
                    it->screen_xo, it->screen_yo+overall_top,
                    it->usewidth, overall_bot - overall_top,
                    0, overall_top,
                    it->screen_xo, it->screen_yo+overall_top,
                    it->usewidth, overall_bot - overall_top,
-                   False);
-#endif
-    } else {
-      XPutImage(it->dpy, it->window, it->gc, it->image,
-                0, overall_top,
-                it->screen_xo, it->screen_yo+overall_top,
-                it->usewidth, overall_bot - overall_top);
-    }
+                   &it->shm_info);
   }
 
 #ifdef DEBUG
   }
 
 #ifdef DEBUG
@@ -1720,101 +2098,6 @@ void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
 #endif
 
 
 #endif
 
 
-void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
-{
-  analogtv_input *inp=rec->input;
-  double *ps=it->rx_signal;
-  double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
-  double *p=ps;
-  signed char *ss=&inp->signal[0][0];
-  signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
-  signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
-  int i;
-  int ec=it->channel_change_cycles;
-  double level=rec->level;
-  double hfloss=rec->hfloss;
-  unsigned int fastrnd=random();
-  double dp[8];
-
-  /* assert((se-ss)%4==0 && (se-s)%4==0); */
-
-  /* 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]));
-
-  for (i=0; i<8; i++) dp[i]=0.0;
-
-  if (ec) {
-    double noise_ampl;
-
-    /* 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
-    */
-
-    noise_ampl = 1.3;
-
-    while (p!=pe && ec>0) {
-
-      double sig0=(double)s[0];
-      double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
-      fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
-
-      p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
-
-      noise_ampl *= 0.99995;
-
-      p++;
-      s++;
-      if (s>=se) s=ss;
-      ec--;
-    }
-
-  }
-
-  while (p != pe) {
-    double sig0,sig1,sig2,sig3,sigr;
-
-    sig0=(double)s[0];
-    sig1=(double)s[1];
-    sig2=(double)s[2];
-    sig3=(double)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);
-  }
-
-  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]))));
-
-
-  it->channel_change_cycles=0;
-
-}
-
 #ifdef FIXME
 /* add hash */
   if (it->hashnoise_times[lineno]) {
 #ifdef FIXME
 /* add hash */
   if (it->hashnoise_times[lineno]) {
@@ -1840,25 +2123,6 @@ void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
 #endif
 
 
 #endif
 
 
-void analogtv_init_signal(analogtv *it, double noiselevel)
-{
-  double *ps=it->rx_signal;
-  double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
-  double *p=ps;
-  unsigned int fastrnd=random();
-  double nm1=0.0,nm2=0.0;
-  double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
-
-  while (p != pe) {
-    nm2=nm1;
-    nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
-    *p++ = nm1*nm2;
-    fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
-  }
-
-  it->rx_signal_level = noiselevel;
-}
-
 void
 analogtv_reception_update(analogtv_reception *rec)
 {
 void
 analogtv_reception_update(analogtv_reception *rec)
 {