1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
51 Trevor Blackwell <tlb@tlb.org>
56 #else /* !HAVE_COCOA */
57 # include <X11/Xlib.h>
58 # include <X11/Xutil.h>
63 #include "resources.h"
66 #include "grabscreen.h"
71 /* only works on linux + freebsd */
72 #include <machine/cpufunc.h>
74 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
75 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
76 #define DTIME dtimes[n_dtimes++]=rdtsc()
77 #define DTIME_SHOW(DIV) \
79 double _dtime_div=(DIV); \
80 printf("time/%.1f: ",_dtime_div); \
81 for (i=1; i<n_dtimes; i++) \
82 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
89 #define DTIME_START do { } while (0)
90 #define DTIME do { } while (0)
91 #define DTIME_SHOW(DIV) do { } while (0)
96 #define FASTRND (fastrnd = fastrnd*1103515245+12345)
98 static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
101 static double puramp(analogtv *it, double tc, double start, double over)
103 double pt=it->powerup-start;
105 if (pt<0.0) return 0.0;
106 if (pt>900.0 || pt/tc>8.0) return 1.0;
108 ret=(1.0-exp(-pt/tc))*over;
109 if (ret>1.0) return 1.0;
114 There are actual standards for TV signals: NTSC and RS-170A describe the
115 system used in the US and Japan. Europe has slightly different systems, but
116 not different enough to make substantially different screensaver displays.
117 Sadly, the standards bodies don't do anything so useful as publish the spec on
118 the web. Best bets are:
120 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
121 http://www.ntsc-tv.com/ntsc-index-02.htm
123 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
124 values it calculates across each scan line. In PseudoColor mode, it consider
125 each possible pattern of 5 preceding bit values in each possible position
126 modulo 4 and allocates a color for each. A few things, like the brightening on
127 the right side as the horizontal trace slows down, aren't done in PseudoColor.
129 I'd like to add a bit of visible retrace, but it conflicts with being able to
130 bitcopy the image when fast scrolling. After another couple of CPU
131 generations, we could probably regenerate the whole image from scratch every
132 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
136 /* localbyteorder is MSBFirst or LSBFirst */
137 static int localbyteorder;
138 static const double float_low8_ofs=8388608.0;
139 static int float_extraction_works;
151 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
152 localbyteorder=*(char *)&localbyteorder_loc;
159 float_extraction_works=1;
160 for (i=0; i<256*4; i++) {
161 fe.f=float_low8_ofs+(double)i;
165 printf("Float extraction failed for %d => %d\n",i,ans);
167 float_extraction_works=0;
176 analogtv_set_defaults(analogtv *it, char *prefix)
180 sprintf(buf,"%sTVTint",prefix);
181 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
182 sprintf(buf,"%sTVColor",prefix);
183 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
184 sprintf(buf,"%sTVBrightness",prefix);
185 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
186 sprintf(buf,"%sTVContrast",prefix);
187 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
188 it->height_control = 1.0;
189 it->width_control = 1.0;
190 it->squish_control = 0.0;
195 it->hashnoise_enable=1;
197 it->horiz_desync=frand(10.0)-5.0;
198 it->squeezebottom=frand(5.0)-1.0;
201 printf("analogtv: prefix=%s\n",prefix);
202 printf(" use: shm=%d cmap=%d color=%d\n",
203 it->use_shm,it->use_cmap,it->use_color);
204 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
205 it->tint_control, it->color_control, it->brightness_control,
206 it->contrast_control);
207 printf(" freq_error %g: %g %d\n",
208 it->freq_error, it->freq_error_inc, it->flutter_tint);
209 printf(" desync: %g %d\n",
210 it->horiz_desync, it->flutter_horiz_desync);
211 printf(" hashnoise rpm: %g\n",
213 printf(" vis: %d %d %d\n",
214 it->visclass, it->visbits, it->visdepth);
215 printf(" shift: %d-%d %d-%d %d-%d\n",
216 it->red_invprec,it->red_shift,
217 it->green_invprec,it->green_shift,
218 it->blue_invprec,it->blue_shift);
219 printf(" size: %d %d %d %d xrepl=%d\n",
220 it->usewidth, it->useheight,
221 it->screen_xo, it->screen_yo, it->xrepl);
223 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
224 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
225 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
226 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
227 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
228 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
229 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
230 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
231 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
232 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
233 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
234 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
235 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
241 extern Bool mono_p; /* shoot me */
244 analogtv_free_image(analogtv *it)
248 #ifdef HAVE_XSHM_EXTENSION
249 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
252 XDestroyImage(it->image);
259 analogtv_alloc_image(analogtv *it)
262 #ifdef HAVE_XSHM_EXTENSION
263 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
264 &it->shm_info, it->usewidth, it->useheight);
266 if (!it->image) it->use_shm=0;
269 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
270 it->usewidth, it->useheight, 8, 0);
271 it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
273 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
278 analogtv_configure(analogtv *it)
280 int oldwidth=it->usewidth;
281 int oldheight=it->useheight;
282 int wlim,hlim,height_diff;
284 /* If the window is very small, don't let the image we draw get lower
285 than the actual TV resolution (266x200.)
287 If the aspect ratio of the window is within 15% of a 4:3 ratio,
288 then scale the image to exactly fill the window.
290 Otherwise, center the image either horizontally or vertically,
291 padding on the left+right, or top+bottom, but not both.
293 If it's very close (2.5%) to a multiple of VISLINES, make it exact
294 For example, it maps 1024 => 1000.
296 float percent = 0.15; /* jwz: 20% caused severe top/bottom clipping
297 in Pong on 1680x1050 iMac screen. */
298 float min_ratio = 4.0 / 3.0 * (1 - percent);
299 float max_ratio = 4.0 / 3.0 * (1 + percent);
301 float height_snap=0.025;
303 hlim = it->xgwa.height;
304 wlim = it->xgwa.width;
305 ratio = wlim / (float) hlim;
307 if (wlim < 266 || hlim < 200)
313 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
314 wlim, hlim, it->xgwa.width, it->xgwa.height,
315 min_ratio, ratio, max_ratio);
318 else if (ratio > min_ratio && ratio < max_ratio)
322 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
323 wlim, hlim, min_ratio, ratio, max_ratio);
326 else if (ratio > max_ratio)
328 wlim = hlim*max_ratio;
331 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
332 wlim, hlim, it->xgwa.width, it->xgwa.height,
333 min_ratio, ratio, max_ratio);
336 else /* ratio < min_ratio */
338 hlim = wlim/min_ratio;
341 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
342 wlim, hlim, it->xgwa.width, it->xgwa.height,
343 min_ratio, ratio, max_ratio);
348 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
349 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
355 /* Most times this doesn't change */
356 if (wlim != oldwidth || hlim != oldheight) {
361 it->xrepl=1+it->usewidth/640;
362 if (it->xrepl>2) it->xrepl=2;
363 it->subwidth=it->usewidth/it->xrepl;
365 analogtv_free_image(it);
366 analogtv_alloc_image(it);
369 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
370 it->screen_yo = (it->xgwa.height-it->useheight)/2;
375 analogtv_reconfigure(analogtv *it)
377 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
378 analogtv_configure(it);
382 analogtv_allocate(Display *dpy, Window window)
390 it=(analogtv *)calloc(1,sizeof(analogtv));
399 #ifdef HAVE_XSHM_EXTENSION
405 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
407 it->screen=it->xgwa.screen;
408 it->colormap=it->xgwa.colormap;
409 it->visclass=it->xgwa.visual->class;
410 it->visbits=it->xgwa.visual->bits_per_rgb;
411 it->visdepth=it->xgwa.depth;
412 if (it->visclass == TrueColor || it->visclass == DirectColor) {
413 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
418 it->use_color=!mono_p;
420 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
422 it->use_color=!mono_p;
429 it->red_mask=it->xgwa.visual->red_mask;
430 it->green_mask=it->xgwa.visual->green_mask;
431 it->blue_mask=it->xgwa.visual->blue_mask;
432 it->red_shift=it->red_invprec=-1;
433 it->green_shift=it->green_invprec=-1;
434 it->blue_shift=it->blue_invprec=-1;
436 /* Is there a standard way to do this? Does this handle all cases? */
438 for (shift=0; shift<32; shift++) {
439 for (prec=1; prec<16 && prec<40-shift; prec++) {
440 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
441 if (it->red_shift<0 && mask==it->red_mask)
442 it->red_shift=shift, it->red_invprec=16-prec;
443 if (it->green_shift<0 && mask==it->green_mask)
444 it->green_shift=shift, it->green_invprec=16-prec;
445 if (it->blue_shift<0 && mask==it->blue_mask)
446 it->blue_shift=shift, it->blue_invprec=16-prec;
449 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
450 if (0) fprintf(stderr,"Can't figure out color space\n");
454 for (i=0; i<ANALOGTV_CV_MAX; i++) {
455 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
456 if (intensity>65535) intensity=65535;
457 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
458 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
459 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
464 gcv.background=get_pixel_resource(it->dpy, it->colormap,
465 "background", "Background");
467 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
468 XSetWindowBackground(it->dpy, it->window, gcv.background);
469 XClearWindow(dpy,window);
471 analogtv_configure(it);
481 analogtv_release(analogtv *it)
485 #ifdef HAVE_XSHM_EXTENSION
486 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
489 XDestroyImage(it->image);
493 if (it->gc) XFreeGC(it->dpy, it->gc);
495 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
502 First generate the I and Q reference signals, which we'll multiply
503 the input signal by to accomplish the demodulation. Normally they
504 are shifted 33 degrees from the colorburst. I think this was convenient
505 for inductor-capacitor-vacuum tube implementation.
507 The tint control, FWIW, just adds a phase shift to the chroma signal,
508 and the color control controls the amplitude.
510 In text modes (colormode==0) the system disabled the color burst, and no
511 color was detected by the monitor.
513 freq_error gives a mismatch between the built-in oscillator and the
514 TV's colorbust. Some II Plus machines seemed to occasionally get
515 instability problems -- the crystal oscillator was a single
516 transistor if I remember correctly -- and the frequency would vary
517 enough that the tint would change across the width of the screen.
518 The left side would be in correct tint because it had just gotten
519 resynchronized with the color burst.
521 If we're using a colormap, set it up.
524 analogtv_set_demod(analogtv *it)
526 int y_levels=10,i_levels=5,q_levels=5;
529 In principle, we might be able to figure out how to adjust the
530 color map frame-by-frame to get some nice color bummage. But I'm
531 terrified of changing the color map because we'll get flashing.
533 I can hardly believe we still have to deal with colormaps. They're
534 like having NEAR PTRs: an enormous hassle for the programmer just
535 to save on memory. They should have been deprecated by 1995 or
539 if (it->use_cmap && !it->n_colors) {
542 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
548 for (yli=0; yli<y_levels; yli++) {
549 for (ili=0; ili<i_levels; ili++) {
550 for (qli=0; qli<q_levels; qli++) {
551 double interpy,interpi,interpq;
552 double levelmult=700.0;
556 interpy=100.0 * ((double)yli/y_levels);
557 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
558 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
560 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
561 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
562 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
564 if (r>65535) r=65535;
566 if (g>65535) g=65535;
568 if (b>65535) b=65535;
571 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
572 interpy, interpi, interpq,
580 if (!XAllocColor(it->dpy, it->colormap, &col)) {
581 if (q_levels > y_levels*4/12)
583 else if (i_levels > y_levels*5/12)
592 it->colors[it->n_colors++]=col.pixel;
597 it->cmap_y_levels=y_levels;
598 it->cmap_i_levels=i_levels;
599 it->cmap_q_levels=q_levels;
608 analogtv_line_signature(analogtv_input *input, int lineno)
611 char *origsignal=&input->signal[(lineno+input->vsync)
612 %ANALOGTV_V][input->line_hsync[lineno]];
616 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
618 hash = hash + (hash<<17) + c;
621 hash += input->line_hsync[lineno];
624 hash += input->hashnoise_times[lineno];
633 /* Here we model the analog circuitry of an NTSC television.
634 Basically, it splits the signal into 3 signals: Y, I and Q. Y
635 corresponds to luminance, and you get it by low-pass filtering the
636 input signal to below 3.57 MHz.
638 I and Q are the in-phase and quadrature components of the 3.57 MHz
639 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
640 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
641 resolution in some colors than others, the I component gets
642 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
643 is approximately orange-blue, and Q is roughly purple-green. See
644 http://www.ntsc-tv.com for details.
646 We actually do an awful lot to the signal here. I suspect it would
647 make sense to wrap them all up together by calculating impulse
648 response and doing FFT convolutions.
653 analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
659 int phasecorr=(signal-it->rx_signal)&3;
660 struct analogtv_yiq_s *yiq;
662 double agclevel=it->agclevel;
663 double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
664 double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
669 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
670 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
671 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
672 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
674 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
677 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
678 double tint_q = sin((103 + it->color_control)*3.1415926/180);
680 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
681 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
682 multiq2[2]=-multiq2[0];
683 multiq2[3]=-multiq2[1];
689 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
690 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
691 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
692 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
693 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
694 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
695 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
699 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
700 for (i=0; i<5; i++) dp[i]=0.0;
703 assert(end < ANALOGTV_PIC_LEN+10);
705 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
706 for (i=0; i<24; i++) dp[i]=0.0;
707 for (i=start, yiq=it->yiq+start, sp=signal+start;
709 i++, dp--, yiq++, sp++) {
711 /* Now filter them. These are infinite impulse response filters
712 calculated by the script at
713 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
714 fixed-point integer DSP, son. No place for wimps. We do it in
715 integer because you can count on integer being faster on most
716 CPUs. We care about speed because we need to recalculate every
717 time we blink text, and when we spew random bytes into screen
718 memory. This is roughly 16.16 fixed point arithmetic, but we
719 scale some filter values up by a few bits to avoid some nasty
722 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
723 with an extra zero at 3.5 MHz, from
724 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
727 dp[0] = sp[0] * 0.0469904257251935 * agclevel;
728 dp[8] = (+1.0*(dp[6]+dp[0])
734 yiq->y = dp[8] + brightadd;
738 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
739 for (i=0; i<27; i++) dp[i]=0.0;
741 for (i=start, yiq=it->yiq+start, sp=signal+start;
743 i++, dp--, yiq++, sp++) {
746 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
747 1.5 MHz with an extra zero at 3.5 MHz, from
748 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
752 dp[0] = sig*multiq2[i&3] * 0.0833333333333;
753 yiq->i=dp[8] = (dp[5] + dp[0]
756 -0.3333333333 * dp[10]);
758 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
759 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
760 +3.0*(dp[16+4] + dp[16+1])
761 +4.0*(dp[16+3] + dp[16+2])
762 -0.3333333333 * dp[24+2]);
765 for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
766 yiq->i = yiq->q = 0.0;
772 analogtv_setup_teletext(analogtv_input *input)
775 int teletext=ANALOGTV_BLACK_LEVEL;
777 /* Teletext goes in line 21. But I suspect there are other things
778 in the vertical retrace interval */
780 for (y=19; y<22; y++) {
781 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
783 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
785 input->signal[y][x]=teletext;
791 analogtv_setup_frame(analogtv *it)
797 if (it->flutter_horiz_desync) {
798 /* Horizontal sync during vertical sync instability. */
799 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
800 ((int)(random()&0xff)-0x80) *
801 ((int)(random()&0xff)-0x80) *
802 ((int)(random()&0xff)-0x80) * 0.000001;
805 for (i=0; i<ANALOGTV_V; i++) {
806 it->hashnoise_times[i]=0;
809 if (it->hashnoise_enable && !it->hashnoise_on) {
810 if (random()%10000==0) {
812 it->shrinkpulse=random()%ANALOGTV_V;
815 if (random()%1000==0) {
818 if (it->hashnoise_on) {
819 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
820 ((int)(random()%2000)-1000)*0.1;
822 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
823 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
825 if (it->hashnoise_rpm > 0.0) {
827 int hnc=it->hashnoise_counter; /* in 24.8 format */
829 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
830 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
831 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
833 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
834 y=(hnc>>8)/ANALOGTV_H;
835 x=(hnc>>8)%ANALOGTV_H;
837 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
838 it->hashnoise_times[y]=x;
840 hnc += hni + (int)(random()%65536)-32768;
842 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
845 if (it->rx_signal_level != 0.0)
846 it->agclevel = 1.0/it->rx_signal_level;
851 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
852 printf(" %0.3f",it->ghostfir[i]);
854 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
859 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
864 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
866 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
867 vsync=lineno>=3 && lineno<7;
869 sig=input->signal[lineno];
871 i=ANALOGTV_SYNC_START;
873 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
874 while (i<ANALOGTV_H) sig[i++]=synclevel;
876 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
877 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
878 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
880 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
883 /* 9 cycles of colorburst */
884 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
885 sig[i+1] += ANALOGTV_CB_LEVEL;
886 sig[i+3] -= ANALOGTV_CB_LEVEL;
893 analogtv_sync(analogtv *it)
895 int cur_hsync=it->cur_hsync;
896 int cur_vsync=it->cur_vsync;
901 double cbfc=1.0/128.0;
903 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
904 for (i=-32; i<32; i++) {
905 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
906 sp = it->rx_signal + lineno*ANALOGTV_H;
908 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
911 filt *= it->agclevel;
913 osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
915 if (osc >= 1.05+0.0002 * filt) break;
917 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
919 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
921 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
923 sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
924 )*ANALOGTV_H + cur_hsync;
925 for (i=-8; i<8; i++) {
926 osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
927 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
929 if (osc >= 1.005 + 0.0001*filt) break;
931 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
934 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
935 ANALOGTV_H) % ANALOGTV_H;
937 /* Now look for the colorburst, which is a few cycles after the H
938 sync pulse, and store its phase.
939 The colorburst is 9 cycles long, and we look at the middle 5
944 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
945 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
946 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
947 sp[i]*it->agclevel*cbfc;
955 for (i=0; i<4; i++) {
956 tot += it->cb_phase[i]*it->cb_phase[i];
958 cbgain = 32.0/sqrt(tot);
960 for (i=0; i<4; i++) {
961 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
966 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
968 it->cb_phase[0], it->cb_phase[1],
969 it->cb_phase[2], it->cb_phase[3]);
972 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
975 it->cur_hsync = cur_hsync;
976 it->cur_vsync = cur_vsync;
980 analogtv_levelmult(analogtv *it, int level)
982 static const double levelfac[3]={-7.5, 5.5, 24.5};
983 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
987 analogtv_level(analogtv *it, int y, int ytop, int ybot)
991 if (y==ytop || y==ybot-1) level=0;
992 else if (y==ytop+1 || y==ybot-2) level=1;
995 else if (ybot-ytop>=5) {
996 if (y==ytop || y==ybot-1) level=0;
999 else if (ybot-ytop>=3) {
1000 if (y==ytop) level=0;
1010 The point of this stuff is to ensure that when useheight is not a
1011 multiple of VISLINES so that TV scan lines map to different numbers
1012 of vertical screen pixels, the total brightness of each scan line
1014 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1015 it interpolates extra black lines.
1019 analogtv_setup_levels(analogtv *it, double avgheight)
1022 static const double levelfac[3]={-7.5, 5.5, 24.5};
1024 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1026 for (i=0; i<height; i++) {
1027 it->leveltable[height][i].index = 2;
1031 it->leveltable[height][0].index=0;
1034 if (height >= 1) it->leveltable[height][height-1].index=0;
1037 it->leveltable[height][1].index=1;
1038 if (height >= 2) it->leveltable[height][height-2].index=1;
1041 for (i=0; i<height; i++) {
1042 it->leveltable[height][i].value =
1043 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1050 analogtv_blast_imagerow(analogtv *it,
1051 float *rgbf, float *rgbf_end,
1056 char *level_copyfrom[3];
1057 int xrepl=it->xrepl;
1058 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1060 for (y=ytop; y<ybot; y++) {
1061 int level=it->leveltable[ybot-ytop][y-ytop].index;
1062 double levelmult=it->leveltable[ybot-ytop][y-ytop].value;
1065 rowdata=it->image->data + y*it->image->bytes_per_line;
1067 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1068 why standard graphics sw has to be fast, or else people will have to
1069 work around it and risk incompatibility. The quickdraw folks
1070 understood this. The other answer would be for X11 to have fewer
1071 formats for bitm.. oh, never mind. If neither of these cases work
1072 (they probably cover 99% of setups) it falls back on the Xlib
1075 if (level_copyfrom[level]) {
1076 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1079 level_copyfrom[level] = rowdata;
1083 else if (it->image->format==ZPixmap &&
1084 it->image->bits_per_pixel==32 &&
1085 sizeof(unsigned int)==4 &&
1086 it->image->byte_order==localbyteorder) {
1087 /* int is more likely to be 32 bits than long */
1088 unsigned int *pixelptr=(unsigned int *)rowdata;
1091 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1092 int ntscri=rpf[0]*levelmult;
1093 int ntscgi=rpf[1]*levelmult;
1094 int ntscbi=rpf[2]*levelmult;
1095 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1096 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1097 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1098 pix = (it->red_values[ntscri] |
1099 it->green_values[ntscgi] |
1100 it->blue_values[ntscbi]);
1104 if (xrepl>=3) pixelptr[2] = pix;
1109 else if (it->image->format==ZPixmap &&
1110 it->image->bits_per_pixel==16 &&
1111 sizeof(unsigned short)==2 &&
1112 float_extraction_works &&
1113 it->image->byte_order==localbyteorder) {
1114 unsigned short *pixelptr=(unsigned short *)rowdata;
1116 float_extract_t r1,g1,b1;
1119 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1120 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1121 r1.f=r2 * levelmult+float_low8_ofs;
1122 g1.f=g2 * levelmult+float_low8_ofs;
1123 b1.f=b2 * levelmult+float_low8_ofs;
1124 pix = (it->red_values[r1.i & 0x3ff] |
1125 it->green_values[g1.i & 0x3ff] |
1126 it->blue_values[b1.i & 0x3ff]);
1130 if (xrepl>=3) pixelptr[2] = pix;
1135 else if (it->image->format==ZPixmap &&
1136 it->image->bits_per_pixel==16 &&
1137 sizeof(unsigned short)==2 &&
1138 it->image->byte_order==localbyteorder) {
1139 unsigned short *pixelptr=(unsigned short *)rowdata;
1142 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1143 int r1=rpf[0] * levelmult;
1144 int g1=rpf[1] * levelmult;
1145 int b1=rpf[2] * levelmult;
1146 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1147 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1148 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1149 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1153 if (xrepl>=3) pixelptr[2] = pix;
1159 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1160 int ntscri=rpf[0]*levelmult;
1161 int ntscgi=rpf[1]*levelmult;
1162 int ntscbi=rpf[2]*levelmult;
1163 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1164 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1165 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1166 for (j=0; j<xrepl; j++) {
1167 XPutPixel(it->image, x*xrepl + j, y,
1168 it->red_values[ntscri] | it->green_values[ntscgi] |
1169 it->blue_values[ntscbi]);
1178 analogtv_draw(analogtv *it)
1181 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1182 float *rgb_start, *rgb_end;
1185 int /*bigloadchange,*/drawcount;
1188 int overall_top, overall_bot;
1190 float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1191 float *raw_rgb_end=raw_rgb_start+3*it->subwidth;
1194 if (! raw_rgb_start) return;
1195 analogtv_setup_frame(it);
1196 analogtv_set_demod(it);
1198 /* rx_signal has an extra 2 lines at the end, where we copy the
1199 first 2 lines so we can index into it while only worrying about
1200 wraparound on a per-line level */
1201 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1203 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1208 /* if (it->hashnoise_on) baseload=0.5; */
1210 /*bigloadchange=1;*/
1212 it->crtload[ANALOGTV_TOP-1]=baseload;
1213 puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1214 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1216 analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1218 overall_top=it->useheight;
1221 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1222 int slineno=lineno-ANALOGTV_TOP;
1223 int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
1224 it->useheight/2)*puheight) + it->useheight/2;
1225 int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
1226 it->useheight/2)*puheight) + it->useheight/2;
1228 int linesig=analogtv_line_signature(input,lineno)
1229 + it->hashnoise_times[lineno];
1231 double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
1232 ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
1233 it->line_hsync[lineno]);
1235 if (ytop==ybot) continue;
1236 if (ybot<0 || ytop>it->useheight) continue;
1238 if (ybot>it->useheight) ybot=it->useheight;
1240 if (ybot > ytop+ANALOGTV_MAX_LINEHEIGHT) ybot=ytop+ANALOGTV_MAX_LINEHEIGHT;
1242 if (ytop < overall_top) overall_top=ytop;
1243 if (ybot > overall_bot) overall_bot=ybot;
1245 if (lineno==it->shrinkpulse) {
1247 /*bigloadchange=1;*/
1252 if (it->hashnoise_rpm>0.0 &&
1255 (slineno<20 && it->flutter_horiz_desync) ||
1256 it->gaussiannoise_level>30 ||
1257 ((it->gaussiannoise_level>2.0 ||
1258 it->multipath) && random()%4) ||
1259 linesig != it->onscreen_signature[lineno])) {
1262 it->onscreen_signature[lineno] = linesig;
1267 Interpolate the 600-dotclock line into however many horizontal
1268 screen pixels we're using, and convert to RGB.
1270 We add some 'bloom', variations in the horizontal scan width with
1271 the amount of brightness, extremely common on period TV sets. They
1272 had a single oscillator which generated both the horizontal scan and
1273 (during the horizontal retrace interval) the high voltage for the
1274 electron beam. More brightness meant more load on the oscillator,
1275 which caused an decrease in horizontal deflection. Look for
1278 Also, the A2 did a bad job of generating horizontal sync pulses
1279 during the vertical blanking interval. This, and the fact that the
1280 horizontal frequency was a bit off meant that TVs usually went a bit
1281 out of sync during the vertical retrace, and the top of the screen
1282 would be bent a bit to the left or right. Look for (shiftthisrow).
1284 We also add a teeny bit of left overscan, just enough to be
1285 annoying, but you can still read the left column of text.
1287 We also simulate compression & brightening on the right side of the
1288 screen. Most TVs do this, but you don't notice because they overscan
1289 so it's off the right edge of the CRT. But the A2 video system used
1290 so much of the horizontal scan line that you had to crank the
1291 horizontal width down in order to not lose the right few characters,
1292 and you'd see the compression on the right edge. Associated with
1293 compression is brightening; since the electron beam was scanning
1294 slower, the same drive signal hit the phosphor harder. Look for
1295 (squishright_i) and (squishdiv).
1300 double ncl/*,diff*/;
1302 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
1303 totsignal += signal[i];
1305 totsignal *= it->agclevel;
1306 ncl = 0.95 * it->crtload[lineno-1] +
1308 (totsignal-30000)/100000.0 +
1309 (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
1311 /*diff=ncl - it->crtload[lineno];*/
1312 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1313 it->crtload[lineno]=ncl;
1317 double bloomthisrow,shiftthisrow;
1318 double viswidth,middle;
1322 bloomthisrow = -10.0 * it->crtload[lineno];
1323 if (bloomthisrow<-10.0) bloomthisrow=-10.0;
1324 if (bloomthisrow>2.0) bloomthisrow=2.0;
1326 shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
1327 (0.7+cos(slineno*0.6)));
1332 viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
1333 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1335 scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
1337 scw=it->subwidth*scanwidth;
1338 if (scw>it->subwidth) scw=it->usewidth;
1339 scl=it->subwidth/2 - scw/2;
1340 scr=it->subwidth/2 + scw/2;
1342 pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
1343 scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
1344 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1345 squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
1346 - it->squish_control)) *65536.0);
1347 squishdiv=it->subwidth/15;
1349 rgb_start=raw_rgb_start+scl*3;
1350 rgb_end=raw_rgb_start+scr*3;
1352 assert(scanstart_i>=0);
1355 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1357 scanstart_i/65536.0,
1358 squishright_i/65536.0,
1365 for (y=ytop; y<ybot; y++) {
1366 int level=analogtv_level(it, y, ytop, ybot);
1367 double levelmult=analogtv_levelmult(it, level);
1368 double levelmult_y = levelmult * it->contrast_control
1369 * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
1370 double levelmult_iq = levelmult * 0.090;
1372 struct analogtv_yiq_s *yiq=it->yiq;
1373 analogtv_ntsc_to_yiq(it, lineno, signal,
1374 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1379 while (i<0 && x<it->usewidth) {
1380 XPutPixel(it->image, x, y, it->colors[0]);
1385 while (i<scanend_i && x<it->usewidth) {
1386 double pixfrac=(i&0xffff)/65536.0;
1387 double invpixfrac=(1.0-pixfrac);
1389 int yli,ili,qli,cmi;
1391 double interpy=(yiq[pati].y*invpixfrac
1392 + yiq[pati+1].y*pixfrac) * levelmult_y;
1393 double interpi=(yiq[pati].i*invpixfrac
1394 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1395 double interpq=(yiq[pati].q*invpixfrac
1396 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1398 yli = (int)(interpy * it->cmap_y_levels);
1399 ili = (int)((interpi+0.5) * it->cmap_i_levels);
1400 qli = (int)((interpq+0.5) * it->cmap_q_levels);
1402 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1404 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1406 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1408 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1411 if ((random()%65536)==0) {
1412 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1413 interpy, interpi, interpq,
1419 for (j=0; j<it->xrepl; j++) {
1420 XPutPixel(it->image, x, y,
1424 if (i >= squishright_i) {
1425 pixmultinc += pixmultinc/squishdiv;
1429 while (x<it->usewidth) {
1430 XPutPixel(it->image, x, y, it->colors[0]);
1436 struct analogtv_yiq_s *yiq=it->yiq;
1437 analogtv_ntsc_to_yiq(it, lineno, signal,
1438 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1440 pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
1441 / (0.5+0.5*puheight) * 1024.0/100.0;
1443 i=scanstart_i; rrp=rgb_start;
1444 while (i<0 && rrp!=rgb_end) {
1445 rrp[0]=rrp[1]=rrp[2]=0;
1449 while (i<scanend_i && rrp!=rgb_end) {
1450 double pixfrac=(i&0xffff)/65536.0;
1451 double invpixfrac=1.0-pixfrac;
1455 double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1456 double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1457 double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1460 According to the NTSC spec, Y,I,Q are generated as:
1462 y=0.30 r + 0.59 g + 0.11 b
1463 i=0.60 r - 0.28 g - 0.32 b
1464 q=0.21 r - 0.52 g + 0.31 b
1466 So if you invert the implied 3x3 matrix you get what standard
1467 televisions implement with a bunch of resistors (or directly in the
1470 r = y + 0.948 i + 0.624 q
1471 g = y - 0.276 i - 0.639 q
1472 b = y - 1.105 i + 1.729 q
1475 r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
1476 g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
1477 b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
1485 if (i>=squishright_i) {
1486 pixmultinc += pixmultinc/squishdiv;
1487 pixbright += pixbright/squishdiv/2;
1492 while (rrp != rgb_end) {
1493 rrp[0]=rrp[1]=rrp[2]=0.0;
1497 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1501 free(raw_rgb_start);
1504 /* poor attempt at visible retrace */
1505 for (i=0; i<15; i++) {
1506 int ytop=(int)((i*it->useheight/15 -
1507 it->useheight/2)*puheight) + it->useheight/2;
1508 int ybot=(int)(((i+1)*it->useheight/15 -
1509 it->useheight/2)*puheight) + it->useheight/2;
1510 int div=it->usewidth*3/2;
1512 for (x=0; x<it->usewidth; x++) {
1513 y = ytop + (ybot-ytop)*x / div;
1514 if (y<0 || y>=it->useheight) continue;
1515 XPutPixel(it->image, x, y, 0xffffff);
1520 if (it->need_clear) {
1521 XClearWindow(it->dpy, it->window);
1525 if (overall_top>0) {
1526 XClearArea(it->dpy, it->window,
1527 it->screen_xo, it->screen_yo,
1528 it->usewidth, overall_top, 0);
1530 if (it->useheight > overall_bot) {
1531 XClearArea(it->dpy, it->window,
1532 it->screen_xo, it->screen_yo+overall_bot,
1533 it->usewidth, it->useheight-overall_bot, 0);
1536 if (overall_bot > overall_top) {
1538 #ifdef HAVE_XSHM_EXTENSION
1539 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1541 it->screen_xo, it->screen_yo+overall_top,
1542 it->usewidth, overall_bot - overall_top,
1546 XPutImage(it->dpy, it->window, it->gc, it->image,
1548 it->screen_xo, it->screen_yo+overall_top,
1549 it->usewidth, overall_bot - overall_top);
1558 gettimeofday(&tv,NULL);
1560 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1561 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1562 sprintf(buf, "FPS=%0.1f",fps);
1563 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1566 it->last_display_time=tv;
1572 analogtv_input_allocate()
1574 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1580 This takes a screen image and encodes it as a video camera would,
1581 including all the bandlimiting and YIQ modulation.
1582 This isn't especially tuned for speed.
1586 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1593 XColor col1[ANALOGTV_PIC_LEN];
1594 XColor col2[ANALOGTV_PIC_LEN];
1595 int multiq[ANALOGTV_PIC_LEN+4];
1596 int y_overscan=5; /* overscan this much top and bottom */
1597 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
1599 img_w=pic_im->width;
1600 img_h=pic_im->height;
1602 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
1603 double phase=90.0-90.0*i;
1605 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
1608 for (y=0; y<y_scanlength; y++) {
1609 int picy1=(y*img_h)/y_scanlength;
1610 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
1612 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1613 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
1614 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
1615 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
1617 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
1618 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
1620 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
1621 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
1623 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1625 int filty,filti,filtq;
1628 y=0.30 r + 0.59 g + 0.11 b
1629 i=0.60 r - 0.28 g - 0.32 b
1630 q=0.21 r - 0.52 g + 0.31 b
1631 The coefficients below are in .4 format */
1633 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
1634 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
1635 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
1636 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
1637 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
1638 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
1640 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
1641 with an extra zero at 3.5 MHz, from
1642 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
1644 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
1645 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
1646 fyx[6] = (rawy * 1897) >> 16;
1647 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
1648 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
1649 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
1650 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
1653 /* Filter I at 1.5 MHz. 3 pole Butterworth from
1654 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
1656 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
1657 fix[3] = (rawi * 1413) >> 16;
1658 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
1659 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
1660 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
1663 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
1664 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
1666 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
1667 fqx[3] = (rawq * 75) >> 16;
1668 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
1669 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
1670 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
1674 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
1675 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
1676 if (composite>125) composite=125;
1677 if (composite<0) composite=0;
1678 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
1686 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
1689 int change=random()%ANALOGTV_V;
1690 unsigned int fastrnd=random();
1691 double hso=(int)(random()%1000)-500;
1692 int yofs=random()%ANALOGTV_V;
1695 for (y=change; y<ANALOGTV_V; y++) {
1696 int s2y=(y+yofs)%ANALOGTV_V;
1698 int noiselevel=60000 / (y-change+100);
1700 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
1702 for (x=0; x<ANALOGTV_H; x++) {
1704 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
1705 noise=(filt*noiselevel)>>16;
1706 newsig=s2->signal[s2y][x] + noise;
1707 if (newsig>120) newsig=120;
1708 if (newsig<0) newsig=0;
1709 it->signal[y][x]=newsig;
1717 void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
1719 analogtv_input *inp=rec->input;
1720 double *ps=it->rx_signal;
1721 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1723 signed char *ss=&inp->signal[0][0];
1724 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1725 signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
1727 int ec=it->channel_change_cycles;
1728 double level=rec->level;
1729 double hfloss=rec->hfloss;
1730 unsigned int fastrnd=random();
1733 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1735 /* duplicate the first line into the Nth line to ease wraparound computation */
1736 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1737 ANALOGTV_H * sizeof(inp->signal[0][0]));
1739 for (i=0; i<8; i++) dp[i]=0.0;
1744 /* Do a big noisy transition. We can make the transition noise of
1745 high constant strength regardless of signal strength.
1747 There are two separate state machines. here, One is the noise
1748 process and the other is the
1750 We don't bother with the FIR filter here
1755 while (p!=pe && ec>0) {
1757 double sig0=(double)s[0];
1758 double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
1759 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1761 p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
1763 noise_ampl *= 0.99995;
1774 double sig0,sig1,sig2,sig3,sigr;
1781 dp[0]=sig0+sig1+sig2+sig3;
1783 /* Get the video out signal, and add some ghosting, typical of RF
1784 monitor cables. This corresponds to a pretty long cable, but
1788 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1789 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1790 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1792 p[0] += (sig0+sigr + sig2*hfloss) * level;
1793 p[1] += (sig1+sigr + sig3*hfloss) * level;
1794 p[2] += (sig2+sigr + sig0*hfloss) * level;
1795 p[3] += (sig3+sigr + sig1*hfloss) * level;
1799 if (s>=se) s = ss + (s-se);
1802 it->rx_signal_level =
1803 sqrt(it->rx_signal_level * it->rx_signal_level +
1804 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1805 rec->ghostfir[2] + rec->ghostfir[3]))));
1808 it->channel_change_cycles=0;
1814 if (it->hashnoise_times[lineno]) {
1815 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
1817 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
1819 double cur=frand(150.0)-20.0;
1820 int len=random()%15+3;
1821 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
1822 for (i=0; i<len; i++) {
1823 double sig=signal[hnt];
1826 cur += frand(5.0)-5.0;
1827 maxampl = maxampl*0.9;
1837 void analogtv_init_signal(analogtv *it, double noiselevel)
1839 double *ps=it->rx_signal;
1840 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1842 unsigned int fastrnd=random();
1843 double nm1=0.0,nm2=0.0;
1844 double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
1848 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1850 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1853 it->rx_signal_level = noiselevel;
1857 analogtv_reception_update(analogtv_reception *rec)
1861 if (rec->multipath > 0.0) {
1862 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1863 rec->ghostfir2[i] +=
1864 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
1866 if (random()%20==0) {
1867 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
1868 = rec->multipath * (frand(0.08)-0.04);
1870 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1871 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
1875 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
1876 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
1880 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1881 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
1888 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
1891 #include "images/6x10font.xbm"
1894 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
1895 int w, int h, char *fontname)
1902 XWindowAttributes xgwa;
1907 XGetWindowAttributes (dpy, window, &xgwa);
1909 if (fontname && !strcmp (fontname, "6x10")) {
1911 text_pm = XCreatePixmapFromBitmapData (dpy, window,
1912 (char *) font6x10_bits,
1916 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
1918 XFreePixmap(dpy, text_pm);
1920 } else if (fontname) {
1922 font = XLoadQueryFont (dpy, fontname);
1924 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
1928 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
1930 memset(&gcv, 0, sizeof(gcv));
1934 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
1936 XSetForeground(dpy, gc, 0);
1937 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
1938 XSetForeground(dpy, gc, 1);
1939 for (i=0; i<256; i++) {
1941 int x=f->char_w*i+1;
1942 int y=f->char_h*8/10;
1943 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
1945 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
1948 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
1949 256*f->char_w, f->char_h, -1, -1);
1952 XFreePixmap(dpy, text_pm);
1954 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
1955 256*f->char_w, f->char_h, 8, 0);
1956 f->text_im->data = (char *)calloc(f->text_im->height,
1957 f->text_im->bytes_per_line);
1965 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
1967 if (x<0 || x>=f->char_w) return 0;
1968 if (y<0 || y>=f->char_h) return 0;
1969 if (c<0 || c>=256) return 0;
1970 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
1974 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
1976 if (x<0 || x>=f->char_w) return;
1977 if (y<0 || y>=f->char_h) return;
1978 if (c<0 || c>=256) return;
1980 XPutPixel(f->text_im, c*f->char_w + x, y, value);
1984 analogtv_font_set_char(analogtv_font *f, int c, char *s)
1988 if (c<0 || c>=256) return;
1990 for (y=0; y<f->char_h; y++) {
1991 for (x=0; x<f->char_w; x++) {
1993 value=(*s==' ') ? 0 : 1;
1994 analogtv_font_set_pixel(f, c, x, y, value);
2001 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2004 for (i=0; i<4; i++) {
2005 double w=90.0*i + phase;
2006 double val=luma + chroma * (cos(3.1415926/180.0*w));
2007 if (val<0.0) val=0.0;
2008 if (val>127.0) val=127.0;
2014 analogtv_draw_solid(analogtv_input *input,
2015 int left, int right, int top, int bot,
2020 if (right-left<4) right=left+4;
2021 if (bot-top<1) bot=top+1;
2023 for (y=top; y<bot; y++) {
2024 for (x=left; x<right; x++) {
2025 input->signal[y][x] = ntsc[x&3];
2032 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2033 double left, double right, double top, double bot,
2034 double luma, double chroma, double phase)
2038 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2039 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2040 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2041 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2043 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2044 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2049 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2050 int c, int x, int y, int ntsc[4])
2052 int yc,xc,ys,xs,pix;
2054 for (yc=0; yc<f->char_h; yc++) {
2055 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2056 if (ys<0 || ys>=ANALOGTV_V) continue;
2058 for (xc=0; xc<f->char_w; xc++) {
2059 pix=analogtv_font_pixel(f, c, xc, yc);
2061 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2062 if (xs<0 || xs>=ANALOGTV_H) continue;
2064 input->signal[ys][xs] = ntsc[xs&3];
2073 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2074 char *s, int x, int y, int ntsc[4])
2077 analogtv_draw_char(input, f, *s, x, y, ntsc);
2084 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2085 char *s, int x, int y, int ntsc[4])
2087 int width=strlen(s) * f->char_w * 4;
2090 analogtv_draw_string(input, f, s, x, y, ntsc);
2094 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2095 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2096 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2097 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2098 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2099 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2100 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2104 Much of this function was adapted from logo.c
2107 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2108 const char * const *xpm, int left, int top)
2113 int ncolors, nbytes;
2116 int r; int g; int b;
2120 if (4 != sscanf ((const char *) *xpm,
2122 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2124 if (ncolors < 1 || ncolors > 255)
2126 if (nbytes != 1) /* a serious limitation */
2130 for (i = 0; i < ncolors; i++) {
2131 const char *line = *xpm;
2132 int colori = ((unsigned char)*line++)&0xff;
2137 while (*line == ' ' || *line == '\t')
2140 if (which != 'c' && which != 'm')
2142 while (*line == ' ' || *line == '\t')
2144 if (!strncasecmp(line, "None", 4))
2153 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2155 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2157 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2172 for (y=0; y<xpmh; y++) {
2173 const char *line = *xpm++;
2175 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2177 for (x=0; x<xpmw; x++) {
2178 int cbyte=((unsigned char)line[x])&0xff;
2181 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2183 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2184 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2185 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2192 for (i=0; i<4; i++) {
2193 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2194 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2197 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2198 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2199 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2200 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];