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>
55 #include <X11/Xutil.h>
56 #include <X11/Intrinsic.h>
59 #include "resources.h"
62 #include "grabscreen.h"
67 /* only works on linux + freebsd */
68 #include <machine/cpufunc.h>
70 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
71 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
72 #define DTIME dtimes[n_dtimes++]=rdtsc()
73 #define DTIME_SHOW(DIV) \
75 double _dtime_div=(DIV); \
76 printf("time/%.1f: ",_dtime_div); \
77 for (i=1; i<n_dtimes; i++) \
78 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
85 #define DTIME_START do { } while (0)
86 #define DTIME do { } while (0)
87 #define DTIME_SHOW(DIV) do { } while (0)
92 #define FASTRND (fastrnd = fastrnd*1103515245+12345)
94 static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
97 static double puramp(analogtv *it, double tc, double start, double over)
99 double pt=it->powerup-start;
101 if (pt<0.0) return 0.0;
102 if (pt>900.0 || pt/tc>8.0) return 1.0;
104 ret=(1.0-exp(-pt/tc))*over;
105 if (ret>1.0) return 1.0;
110 There are actual standards for TV signals: NTSC and RS-170A describe the
111 system used in the US and Japan. Europe has slightly different systems, but
112 not different enough to make substantially different screensaver displays.
113 Sadly, the standards bodies don't do anything so useful as publish the spec on
114 the web. Best bets are:
116 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
117 http://www.ntsc-tv.com/ntsc-index-02.htm
119 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
120 values it calculates across each scan line. In PseudoColor mode, it consider
121 each possible pattern of 5 preceding bit values in each possible position
122 modulo 4 and allocates a color for each. A few things, like the brightening on
123 the right side as the horizontal trace slows down, aren't done in PseudoColor.
125 I'd like to add a bit of visible retrace, but it conflicts with being able to
126 bitcopy the image when fast scrolling. After another couple of CPU
127 generations, we could probably regenerate the whole image from scratch every
128 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
132 /* localbyteorder is MSBFirst or LSBFirst */
133 static int localbyteorder;
134 static const double float_low8_ofs=8388608.0;
135 static int float_extraction_works;
147 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
148 localbyteorder=*(char *)&localbyteorder_loc;
155 float_extraction_works=1;
156 for (i=0; i<256*4; i++) {
157 fe.f=float_low8_ofs+(double)i;
161 printf("Float extraction failed for %d => %d\n",i,ans);
163 float_extraction_works=0;
172 analogtv_set_defaults(analogtv *it, char *prefix)
176 sprintf(buf,"%sTVTint",prefix);
177 it->tint_control = get_float_resource(buf,"TVTint");
178 sprintf(buf,"%sTVColor",prefix);
179 it->color_control = get_float_resource(buf,"TVColor")/100.0;
180 sprintf(buf,"%sTVBrightness",prefix);
181 it->brightness_control = get_float_resource(buf,"TVBrightness") / 100.0;
182 sprintf(buf,"%sTVContrast",prefix);
183 it->contrast_control = get_float_resource(buf,"TVContrast") / 100.0;
184 it->height_control = 1.0;
185 it->width_control = 1.0;
186 it->squish_control = 0.0;
191 it->hashnoise_enable=1;
193 it->horiz_desync=frand(10.0)-5.0;
194 it->squeezebottom=frand(5.0)-1.0;
197 printf("analogtv: prefix=%s\n",prefix);
198 printf(" use: shm=%d cmap=%d color=%d\n",
199 it->use_shm,it->use_cmap,it->use_color);
200 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
201 it->tint_control, it->color_control, it->brightness_control,
202 it->contrast_control);
203 printf(" freq_error %g: %g %d\n",
204 it->freq_error, it->freq_error_inc, it->flutter_tint);
205 printf(" desync: %g %d\n",
206 it->horiz_desync, it->flutter_horiz_desync);
207 printf(" hashnoise rpm: %g\n",
209 printf(" vis: %d %d %d\n",
210 it->visclass, it->visbits, it->visdepth);
211 printf(" shift: %d-%d %d-%d %d-%d\n",
212 it->red_invprec,it->red_shift,
213 it->green_invprec,it->green_shift,
214 it->blue_invprec,it->blue_shift);
215 printf(" size: %d %d %d %d xrepl=%d\n",
216 it->usewidth, it->useheight,
217 it->screen_xo, it->screen_yo, it->xrepl);
219 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
220 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
221 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
222 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
223 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
224 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
225 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
226 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
227 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
228 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
229 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
230 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
231 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
237 extern Bool mono_p; /* shoot me */
240 analogtv_free_image(analogtv *it)
244 #ifdef HAVE_XSHM_EXTENSION
245 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
248 XDestroyImage(it->image);
255 analogtv_alloc_image(analogtv *it)
258 #ifdef HAVE_XSHM_EXTENSION
259 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
260 &it->shm_info, it->usewidth, it->useheight);
262 if (!it->image) it->use_shm=0;
265 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
266 it->usewidth, it->useheight, 8, 0);
267 it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
269 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
274 analogtv_configure(analogtv *it)
276 int oldwidth=it->usewidth;
277 int oldheight=it->useheight;
278 int wlim,hlim,height_diff;
280 /* If the window is very small, don't let the image we draw get lower
281 than the actual TV resolution (266x200.)
283 If the aspect ratio of the window is within 20% of a 4:3 ratio,
284 then scale the image to exactly fill the window.
286 Otherwise, center the image either horizontally or vertically,
287 padding on the left+right, or top+bottom, but not both.
289 If it's very close (2.5%) to a multiple of VISLINES, make it exact
290 For example, it maps 1024 => 1000.
292 float percent = 0.20;
293 float min_ratio = 4.0 / 3.0 * (1 - percent);
294 float max_ratio = 4.0 / 3.0 * (1 + percent);
296 float height_snap=0.025;
298 hlim = it->xgwa.height;
299 wlim = it->xgwa.width;
300 ratio = wlim / (float) hlim;
302 if (wlim < 266 || hlim < 200)
308 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
309 wlim, hlim, it->xgwa.width, it->xgwa.height,
310 min_ratio, ratio, max_ratio);
313 else if (ratio > min_ratio && ratio < max_ratio)
317 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
318 wlim, hlim, min_ratio, ratio, max_ratio);
321 else if (ratio > max_ratio)
323 wlim = hlim*max_ratio;
326 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
327 wlim, hlim, it->xgwa.width, it->xgwa.height,
328 min_ratio, ratio, max_ratio);
331 else /* ratio < min_ratio */
333 hlim = wlim/min_ratio;
336 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
337 wlim, hlim, it->xgwa.width, it->xgwa.height,
338 min_ratio, ratio, max_ratio);
343 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
344 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
350 /* Most times this doesn't change */
351 if (wlim != oldwidth || hlim != oldheight) {
356 it->xrepl=1+it->usewidth/640;
357 if (it->xrepl>2) it->xrepl=2;
358 it->subwidth=it->usewidth/it->xrepl;
360 analogtv_free_image(it);
361 analogtv_alloc_image(it);
364 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
365 it->screen_yo = (it->xgwa.height-it->useheight)/2;
370 analogtv_reconfigure(analogtv *it)
372 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
373 analogtv_configure(it);
377 analogtv_allocate(Display *dpy, Window window)
385 it=(analogtv *)calloc(1,sizeof(analogtv));
393 #ifdef HAVE_XSHM_EXTENSION
399 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
401 it->screen=it->xgwa.screen;
402 it->colormap=it->xgwa.colormap;
403 it->visclass=it->xgwa.visual->class;
404 it->visbits=it->xgwa.visual->bits_per_rgb;
405 it->visdepth=it->xgwa.depth;
406 if (it->visclass == TrueColor || it->visclass == DirectColor) {
407 if (get_integer_resource ("use_cmap", "Integer")) {
412 it->use_color=!mono_p;
414 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
416 it->use_color=!mono_p;
423 it->red_mask=it->xgwa.visual->red_mask;
424 it->green_mask=it->xgwa.visual->green_mask;
425 it->blue_mask=it->xgwa.visual->blue_mask;
426 it->red_shift=it->red_invprec=-1;
427 it->green_shift=it->green_invprec=-1;
428 it->blue_shift=it->blue_invprec=-1;
430 /* Is there a standard way to do this? Does this handle all cases? */
432 for (shift=0; shift<32; shift++) {
433 for (prec=1; prec<16 && prec<32-shift; prec++) {
434 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
435 if (it->red_shift<0 && mask==it->red_mask)
436 it->red_shift=shift, it->red_invprec=16-prec;
437 if (it->green_shift<0 && mask==it->green_mask)
438 it->green_shift=shift, it->green_invprec=16-prec;
439 if (it->blue_shift<0 && mask==it->blue_mask)
440 it->blue_shift=shift, it->blue_invprec=16-prec;
443 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
444 if (0) fprintf(stderr,"Can't figure out color space\n");
448 for (i=0; i<ANALOGTV_CV_MAX; i++) {
449 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
450 if (intensity>65535) intensity=65535;
451 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
452 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
453 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
458 gcv.background=get_pixel_resource("background", "Background",
459 it->dpy, it->colormap);
461 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
462 XSetWindowBackground(it->dpy, it->window, gcv.background);
463 XClearWindow(dpy,window);
465 analogtv_configure(it);
475 analogtv_release(analogtv *it)
479 #ifdef HAVE_XSHM_EXTENSION
480 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
483 XDestroyImage(it->image);
487 if (it->gc) XFreeGC(it->dpy, it->gc);
489 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
495 First generate the I and Q reference signals, which we'll multiply
496 the input signal by to accomplish the demodulation. Normally they
497 are shifted 33 degrees from the colorburst. I think this was convenient
498 for inductor-capacitor-vacuum tube implementation.
500 The tint control, FWIW, just adds a phase shift to the chroma signal,
501 and the color control controls the amplitude.
503 In text modes (colormode==0) the system disabled the color burst, and no
504 color was detected by the monitor.
506 freq_error gives a mismatch between the built-in oscillator and the
507 TV's colorbust. Some II Plus machines seemed to occasionally get
508 instability problems -- the crystal oscillator was a single
509 transistor if I remember correctly -- and the frequency would vary
510 enough that the tint would change across the width of the screen.
511 The left side would be in correct tint because it had just gotten
512 resynchronized with the color burst.
514 If we're using a colormap, set it up.
517 analogtv_set_demod(analogtv *it)
519 int y_levels=10,i_levels=5,q_levels=5;
522 In principle, we might be able to figure out how to adjust the
523 color map frame-by-frame to get some nice color bummage. But I'm
524 terrified of changing the color map because we'll get flashing.
526 I can hardly believe we still have to deal with colormaps. They're
527 like having NEAR PTRs: an enormous hassle for the programmer just
528 to save on memory. They should have been deprecated by 1995 or
532 if (it->use_cmap && !it->n_colors) {
535 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
541 for (yli=0; yli<y_levels; yli++) {
542 for (ili=0; ili<i_levels; ili++) {
543 for (qli=0; qli<q_levels; qli++) {
544 double interpy,interpi,interpq;
545 double levelmult=700.0;
549 interpy=100.0 * ((double)yli/y_levels);
550 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
551 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
553 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
554 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
555 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
557 if (r>65535) r=65535;
559 if (g>65535) g=65535;
561 if (b>65535) b=65535;
564 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
565 interpy, interpi, interpq,
573 if (!XAllocColor(it->dpy, it->colormap, &col)) {
574 if (q_levels > y_levels*4/12)
576 else if (i_levels > y_levels*5/12)
585 it->colors[it->n_colors++]=col.pixel;
590 it->cmap_y_levels=y_levels;
591 it->cmap_i_levels=i_levels;
592 it->cmap_q_levels=q_levels;
601 analogtv_line_signature(analogtv_input *input, int lineno)
604 char *origsignal=&input->signal[(lineno+input->vsync)
605 %ANALOGTV_V][input->line_hsync[lineno]];
609 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
611 hash = hash + (hash<<17) + c;
614 hash += input->line_hsync[lineno];
617 hash += input->hashnoise_times[lineno];
626 /* Here we model the analog circuitry of an NTSC television.
627 Basically, it splits the signal into 3 signals: Y, I and Q. Y
628 corresponds to luminance, and you get it by low-pass filtering the
629 input signal to below 3.57 MHz.
631 I and Q are the in-phase and quadrature components of the 3.57 MHz
632 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
633 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
634 resolution in some colors than others, the I component gets
635 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
636 is approximately orange-blue, and Q is roughly purple-green. See
637 http://www.ntsc-tv.com for details.
639 We actually do an awful lot to the signal here. I suspect it would
640 make sense to wrap them all up together by calculating impulse
641 response and doing FFT convolutions.
646 analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
652 int phasecorr=(signal-it->rx_signal)&3;
653 struct analogtv_yiq_s *yiq;
655 double agclevel=it->agclevel;
656 double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
657 double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
662 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
663 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
664 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
665 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
667 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
670 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
671 double tint_q = sin((103 + it->color_control)*3.1415926/180);
673 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
674 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
675 multiq2[2]=-multiq2[0];
676 multiq2[3]=-multiq2[1];
682 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
683 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
684 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
685 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
686 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
687 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
688 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
692 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
693 for (i=0; i<5; i++) dp[i]=0.0;
696 assert(end < ANALOGTV_PIC_LEN+10);
698 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
699 for (i=0; i<24; i++) dp[i]=0.0;
700 for (i=start, yiq=it->yiq+start, sp=signal+start;
702 i++, dp--, yiq++, sp++) {
704 /* Now filter them. These are infinite impulse response filters
705 calculated by the script at
706 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
707 fixed-point integer DSP, son. No place for wimps. We do it in
708 integer because you can count on integer being faster on most
709 CPUs. We care about speed because we need to recalculate every
710 time we blink text, and when we spew random bytes into screen
711 memory. This is roughly 16.16 fixed point arithmetic, but we
712 scale some filter values up by a few bits to avoid some nasty
715 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
716 with an extra zero at 3.5 MHz, from
717 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
720 dp[0] = sp[0] * 0.0469904257251935 * agclevel;
721 dp[8] = (+1.0*(dp[6]+dp[0])
727 yiq->y = dp[8] + brightadd;
731 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
732 for (i=0; i<27; i++) dp[i]=0.0;
734 for (i=start, yiq=it->yiq+start, sp=signal+start;
736 i++, dp--, yiq++, sp++) {
739 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
740 1.5 MHz with an extra zero at 3.5 MHz, from
741 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
745 dp[0] = sig*multiq2[i&3] * 0.0833333333333;
746 yiq->i=dp[8] = (dp[5] + dp[0]
749 -0.3333333333 * dp[10]);
751 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
752 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
753 +3.0*(dp[16+4] + dp[16+1])
754 +4.0*(dp[16+3] + dp[16+2])
755 -0.3333333333 * dp[24+2]);
758 for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
759 yiq->i = yiq->q = 0.0;
765 analogtv_setup_teletext(analogtv_input *input)
768 int teletext=ANALOGTV_BLACK_LEVEL;
770 /* Teletext goes in line 21. But I suspect there are other things
771 in the vertical retrace interval */
773 for (y=19; y<22; y++) {
774 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
776 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
778 input->signal[y][x]=teletext;
784 analogtv_setup_frame(analogtv *it)
790 if (it->flutter_horiz_desync) {
791 /* Horizontal sync during vertical sync instability. */
792 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
793 ((int)(random()&0xff)-0x80) *
794 ((int)(random()&0xff)-0x80) *
795 ((int)(random()&0xff)-0x80) * 0.000001;
798 for (i=0; i<ANALOGTV_V; i++) {
799 it->hashnoise_times[i]=0;
802 if (it->hashnoise_enable && !it->hashnoise_on) {
803 if (random()%10000==0) {
805 it->shrinkpulse=random()%ANALOGTV_V;
808 if (random()%1000==0) {
811 if (it->hashnoise_on) {
812 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
813 ((int)(random()%2000)-1000)*0.1;
815 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
816 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
818 if (it->hashnoise_rpm > 0.0) {
820 int hnc=it->hashnoise_counter; /* in 24.8 format */
822 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
823 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
824 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
826 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
827 y=(hnc>>8)/ANALOGTV_H;
828 x=(hnc>>8)%ANALOGTV_H;
830 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
831 it->hashnoise_times[y]=x;
833 hnc += hni + (int)(random()%65536)-32768;
835 hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;
838 if (it->rx_signal_level != 0.0)
839 it->agclevel = 1.0/it->rx_signal_level;
844 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
845 printf(" %0.3f",it->ghostfir[i]);
847 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
852 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
857 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
859 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
860 vsync=lineno>=3 && lineno<7;
862 sig=input->signal[lineno];
864 i=ANALOGTV_SYNC_START;
866 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
867 while (i<ANALOGTV_H) sig[i++]=synclevel;
869 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
870 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
871 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
873 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
876 /* 9 cycles of colorburst */
877 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
878 sig[i+1] += ANALOGTV_CB_LEVEL;
879 sig[i+3] -= ANALOGTV_CB_LEVEL;
886 analogtv_sync(analogtv *it)
888 int cur_hsync=it->cur_hsync;
889 int cur_vsync=it->cur_vsync;
894 double cbfc=1.0/128.0;
896 sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;
897 for (i=-32; i<32; i++) {
898 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
899 sp = it->rx_signal + lineno*ANALOGTV_H;
901 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
904 filt *= it->agclevel;
906 osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
908 if (osc >= 1.05+0.0002 * filt) break;
910 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
912 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
914 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
916 sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
917 )*ANALOGTV_H + cur_hsync;
918 for (i=-8; i<8; i++) {
919 osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
920 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
922 if (osc >= 1.005 + 0.0001*filt) break;
924 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
927 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
928 ANALOGTV_H) % ANALOGTV_H;
930 /* Now look for the colorburst, which is a few cycles after the H
931 sync pulse, and store its phase.
932 The colorburst is 9 cycles long, and we look at the middle 5
937 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
938 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
939 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
940 sp[i]*it->agclevel*cbfc;
948 for (i=0; i<4; i++) {
949 tot += it->cb_phase[i]*it->cb_phase[i];
951 cbgain = 32.0/sqrt(tot);
953 for (i=0; i<4; i++) {
954 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
959 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
961 it->cb_phase[0], it->cb_phase[1],
962 it->cb_phase[2], it->cb_phase[3]);
965 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
968 it->cur_hsync = cur_hsync;
969 it->cur_vsync = cur_vsync;
973 analogtv_levelmult(analogtv *it, int level)
975 static double levelfac[3]={-7.5, 5.5, 24.5};
976 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
980 analogtv_level(analogtv *it, int y, int ytop, int ybot)
984 if (y==ytop || y==ybot-1) level=0;
985 else if (y==ytop+1 || y==ybot-2) level=1;
988 else if (ybot-ytop>=5) {
989 if (y==ytop || y==ybot-1) level=0;
992 else if (ybot-ytop>=3) {
993 if (y==ytop) level=0;
1004 The point of this stuff is to ensure that when useheight is not a
1005 multiple of VISLINES so that TV scan lines map to different numbers
1006 of vertical screen pixels, the total brightness of each scan line
1008 MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1009 it interpolates extra black lines.
1011 enum {MAX_LINEHEIGHT=12};
1015 } leveltable[MAX_LINEHEIGHT+1][MAX_LINEHEIGHT+1];
1018 analogtv_setup_levels(analogtv *it, double avgheight)
1021 static double levelfac[3]={-7.5, 5.5, 24.5};
1023 for (height=0; height<avgheight+2.0 && height<=MAX_LINEHEIGHT; height++) {
1025 for (i=0; i<height; i++) {
1026 leveltable[height][i].index = 2;
1030 leveltable[height][0].index=0;
1033 leveltable[height][height-1].index=0;
1036 leveltable[height][1].index=1;
1037 leveltable[height][height-2].index=1;
1040 for (i=0; i<height; i++) {
1041 leveltable[height][i].value =
1042 (40.0 + levelfac[leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1049 analogtv_blast_imagerow(analogtv *it,
1050 float *rgbf, float *rgbf_end,
1055 char *level_copyfrom[3];
1056 int xrepl=it->xrepl;
1057 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1059 for (y=ytop; y<ybot; y++) {
1060 int level=leveltable[ybot-ytop][y-ytop].index;
1061 double levelmult=leveltable[ybot-ytop][y-ytop].value;
1064 rowdata=it->image->data + y*it->image->bytes_per_line;
1066 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1067 why standard graphics sw has to be fast, or else people will have to
1068 work around it and risk incompatibility. The quickdraw folks
1069 understood this. The other answer would be for X11 to have fewer
1070 formats for bitm.. oh, never mind. If neither of these cases work
1071 (they probably cover 99% of setups) it falls back on the Xlib
1074 if (level_copyfrom[level]) {
1075 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1078 level_copyfrom[level] = rowdata;
1082 else if (it->image->format==ZPixmap &&
1083 it->image->bits_per_pixel==32 &&
1084 sizeof(unsigned int)==4 &&
1085 it->image->byte_order==localbyteorder) {
1086 /* int is more likely to be 32 bits than long */
1087 unsigned int *pixelptr=(unsigned int *)rowdata;
1090 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1091 int ntscri=rpf[0]*levelmult;
1092 int ntscgi=rpf[1]*levelmult;
1093 int ntscbi=rpf[2]*levelmult;
1094 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1095 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1096 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1097 pix = (it->red_values[ntscri] |
1098 it->green_values[ntscgi] |
1099 it->blue_values[ntscbi]);
1103 if (xrepl>=3) pixelptr[2] = pix;
1108 else if (it->image->format==ZPixmap &&
1109 it->image->bits_per_pixel==16 &&
1110 sizeof(unsigned short)==2 &&
1111 float_extraction_works &&
1112 it->image->byte_order==localbyteorder) {
1113 unsigned short *pixelptr=(unsigned short *)rowdata;
1115 float_extract_t r1,g1,b1;
1118 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1119 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1120 r1.f=r2 * levelmult+float_low8_ofs;
1121 g1.f=g2 * levelmult+float_low8_ofs;
1122 b1.f=b2 * levelmult+float_low8_ofs;
1123 pix = (it->red_values[r1.i & 0x3ff] |
1124 it->green_values[g1.i & 0x3ff] |
1125 it->blue_values[b1.i & 0x3ff]);
1129 if (xrepl>=3) pixelptr[2] = pix;
1134 else if (it->image->format==ZPixmap &&
1135 it->image->bits_per_pixel==16 &&
1136 sizeof(unsigned short)==2 &&
1137 it->image->byte_order==localbyteorder) {
1138 unsigned short *pixelptr=(unsigned short *)rowdata;
1141 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1142 int r1=rpf[0] * levelmult;
1143 int g1=rpf[1] * levelmult;
1144 int b1=rpf[2] * levelmult;
1145 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1146 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1147 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1148 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1152 if (xrepl>=3) pixelptr[2] = pix;
1158 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1159 int ntscri=rpf[0]*levelmult;
1160 int ntscgi=rpf[1]*levelmult;
1161 int ntscbi=rpf[2]*levelmult;
1162 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1163 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1164 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1165 for (j=0; j<xrepl; j++) {
1166 XPutPixel(it->image, x*xrepl + j, y,
1167 it->red_values[ntscri] | it->green_values[ntscgi] |
1168 it->blue_values[ntscbi]);
1177 analogtv_draw(analogtv *it)
1180 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1181 float *rgb_start, *rgb_end;
1184 int bigloadchange,drawcount;
1187 int overall_top, overall_bot;
1189 float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1190 float *raw_rgb_end=raw_rgb_start+3*it->subwidth;
1193 analogtv_setup_frame(it);
1194 analogtv_set_demod(it);
1196 /* rx_signal has an extra 2 lines at the end, where we copy the
1197 first 2 lines so we can index into it while only worrying about
1198 wraparound on a per-line level */
1199 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1201 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1206 /* if (it->hashnoise_on) baseload=0.5; */
1210 it->crtload[ANALOGTV_TOP-1]=baseload;
1211 puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1212 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1214 analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1216 overall_top=it->useheight;
1219 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1220 int slineno=lineno-ANALOGTV_TOP;
1221 int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
1222 it->useheight/2)*puheight) + it->useheight/2;
1223 int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
1224 it->useheight/2)*puheight) + it->useheight/2;
1226 int linesig=analogtv_line_signature(input,lineno)
1227 + it->hashnoise_times[lineno];
1229 double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
1230 ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
1231 it->line_hsync[lineno]);
1233 if (ytop==ybot) continue;
1234 if (ybot<0 || ytop>it->useheight) continue;
1236 if (ybot>it->useheight) ybot=it->useheight;
1238 if (ybot > ytop+MAX_LINEHEIGHT) ybot=ytop+MAX_LINEHEIGHT;
1240 if (ytop < overall_top) overall_top=ytop;
1241 if (ybot > overall_bot) overall_bot=ybot;
1243 if (lineno==it->shrinkpulse) {
1250 if (it->hashnoise_rpm>0.0 &&
1253 (slineno<20 && it->flutter_horiz_desync) ||
1254 it->gaussiannoise_level>30 ||
1255 ((it->gaussiannoise_level>2.0 ||
1256 it->multipath) && random()%4) ||
1257 linesig != it->onscreen_signature[lineno])) {
1260 it->onscreen_signature[lineno] = linesig;
1265 Interpolate the 600-dotclock line into however many horizontal
1266 screen pixels we're using, and convert to RGB.
1268 We add some 'bloom', variations in the horizontal scan width with
1269 the amount of brightness, extremely common on period TV sets. They
1270 had a single oscillator which generated both the horizontal scan and
1271 (during the horizontal retrace interval) the high voltage for the
1272 electron beam. More brightness meant more load on the oscillator,
1273 which caused an decrease in horizontal deflection. Look for
1276 Also, the A2 did a bad job of generating horizontal sync pulses
1277 during the vertical blanking interval. This, and the fact that the
1278 horizontal frequency was a bit off meant that TVs usually went a bit
1279 out of sync during the vertical retrace, and the top of the screen
1280 would be bent a bit to the left or right. Look for (shiftthisrow).
1282 We also add a teeny bit of left overscan, just enough to be
1283 annoying, but you can still read the left column of text.
1285 We also simulate compression & brightening on the right side of the
1286 screen. Most TVs do this, but you don't notice because they overscan
1287 so it's off the right edge of the CRT. But the A2 video system used
1288 so much of the horizontal scan line that you had to crank the
1289 horizontal width down in order to not lose the right few characters,
1290 and you'd see the compression on the right edge. Associated with
1291 compression is brightening; since the electron beam was scanning
1292 slower, the same drive signal hit the phosphor harder. Look for
1293 (squishright_i) and (squishdiv).
1300 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
1301 totsignal += signal[i];
1303 totsignal *= it->agclevel;
1304 ncl = 0.95 * it->crtload[lineno-1] +
1306 (totsignal-30000)/100000.0 +
1307 (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
1309 diff=ncl - it->crtload[lineno];
1310 bigloadchange = (diff>0.01 || diff<-0.01);
1311 it->crtload[lineno]=ncl;
1315 double bloomthisrow,shiftthisrow;
1316 double viswidth,middle;
1320 bloomthisrow = -10.0 * it->crtload[lineno];
1321 if (bloomthisrow<-10.0) bloomthisrow=-10.0;
1322 if (bloomthisrow>2.0) bloomthisrow=2.0;
1324 shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
1325 (0.7+cos(slineno*0.6)));
1330 viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
1331 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1333 scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
1335 scw=it->subwidth*scanwidth;
1336 if (scw>it->subwidth) scw=it->usewidth;
1337 scl=it->subwidth/2 - scw/2;
1338 scr=it->subwidth/2 + scw/2;
1340 pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
1341 scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
1342 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1343 squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
1344 - it->squish_control)) *65536.0);
1345 squishdiv=it->subwidth/15;
1347 rgb_start=raw_rgb_start+scl*3;
1348 rgb_end=raw_rgb_start+scr*3;
1350 assert(scanstart_i>=0);
1353 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1355 scanstart_i/65536.0,
1356 squishright_i/65536.0,
1363 for (y=ytop; y<ybot; y++) {
1364 int level=analogtv_level(it, y, ytop, ybot);
1365 double levelmult=analogtv_levelmult(it, level);
1366 double levelmult_y = levelmult * it->contrast_control
1367 * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
1368 double levelmult_iq = levelmult * 0.090;
1370 struct analogtv_yiq_s *yiq=it->yiq;
1371 analogtv_ntsc_to_yiq(it, lineno, signal,
1372 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1377 while (i<0 && x<it->usewidth) {
1378 XPutPixel(it->image, x, y, it->colors[0]);
1383 while (i<scanend_i && x<it->usewidth) {
1384 double pixfrac=(i&0xffff)/65536.0;
1385 double invpixfrac=(1.0-pixfrac);
1387 int yli,ili,qli,cmi;
1389 double interpy=(yiq[pati].y*invpixfrac
1390 + yiq[pati+1].y*pixfrac) * levelmult_y;
1391 double interpi=(yiq[pati].i*invpixfrac
1392 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1393 double interpq=(yiq[pati].q*invpixfrac
1394 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1396 yli = (int)(interpy * it->cmap_y_levels);
1397 ili = (int)((interpi+0.5) * it->cmap_i_levels);
1398 qli = (int)((interpq+0.5) * it->cmap_q_levels);
1400 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1402 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1404 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1406 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1409 if ((random()%65536)==0) {
1410 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1411 interpy, interpi, interpq,
1417 for (j=0; j<it->xrepl; j++) {
1418 XPutPixel(it->image, x, y,
1422 if (i >= squishright_i) {
1423 pixmultinc += pixmultinc/squishdiv;
1427 while (x<it->usewidth) {
1428 XPutPixel(it->image, x, y, it->colors[0]);
1434 struct analogtv_yiq_s *yiq=it->yiq;
1435 analogtv_ntsc_to_yiq(it, lineno, signal,
1436 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1438 pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
1439 / (0.5+0.5*puheight) * 1024.0/100.0;
1441 i=scanstart_i; rrp=rgb_start;
1442 while (i<0 && rrp!=rgb_end) {
1443 rrp[0]=rrp[1]=rrp[2]=0;
1447 while (i<scanend_i && rrp!=rgb_end) {
1448 double pixfrac=(i&0xffff)/65536.0;
1449 double invpixfrac=1.0-pixfrac;
1453 double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1454 double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1455 double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1458 According to the NTSC spec, Y,I,Q are generated as:
1460 y=0.30 r + 0.59 g + 0.11 b
1461 i=0.60 r - 0.28 g - 0.32 b
1462 q=0.21 r - 0.52 g + 0.31 b
1464 So if you invert the implied 3x3 matrix you get what standard
1465 televisions implement with a bunch of resistors (or directly in the
1468 r = y + 0.948 i + 0.624 q
1469 g = y - 0.276 i - 0.639 q
1470 b = y - 1.105 i + 1.729 q
1473 r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
1474 g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
1475 b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
1483 if (i>=squishright_i) {
1484 pixmultinc += pixmultinc/squishdiv;
1485 pixbright += pixbright/squishdiv/2;
1490 while (rrp != rgb_end) {
1491 rrp[0]=rrp[1]=rrp[2]=0.0;
1495 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1499 free(raw_rgb_start);
1502 /* poor attempt at visible retrace */
1503 for (i=0; i<15; i++) {
1504 int ytop=(int)((i*it->useheight/15 -
1505 it->useheight/2)*puheight) + it->useheight/2;
1506 int ybot=(int)(((i+1)*it->useheight/15 -
1507 it->useheight/2)*puheight) + it->useheight/2;
1508 int div=it->usewidth*3/2;
1510 for (x=0; x<it->usewidth; x++) {
1511 y = ytop + (ybot-ytop)*x / div;
1512 if (y<0 || y>=it->useheight) continue;
1513 XPutPixel(it->image, x, y, 0xffffff);
1518 if (it->need_clear) {
1519 XClearWindow(it->dpy, it->window);
1523 if (overall_top>0) {
1524 XClearArea(it->dpy, it->window,
1525 it->screen_xo, it->screen_yo,
1526 it->usewidth, overall_top, 0);
1528 if (it->useheight > overall_bot) {
1529 XClearArea(it->dpy, it->window,
1530 it->screen_xo, it->screen_yo+overall_bot,
1531 it->usewidth, it->useheight-overall_bot, 0);
1534 if (overall_bot > overall_top) {
1536 #ifdef HAVE_XSHM_EXTENSION
1537 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1539 it->screen_xo, it->screen_yo+overall_top,
1540 it->usewidth, overall_bot - overall_top,
1544 XPutImage(it->dpy, it->window, it->gc, it->image,
1546 it->screen_xo, it->screen_yo+overall_top,
1547 it->usewidth, overall_bot - overall_top);
1556 gettimeofday(&tv,NULL);
1558 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1559 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1560 sprintf(buf, "FPS=%0.1f",fps);
1561 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1564 it->last_display_time=tv;
1570 /* Small delay to avoid hogging the CPU. */
1575 analogtv_input_allocate()
1577 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1583 This takes a screen image and encodes it as a video camera would,
1584 including all the bandlimiting and YIQ modulation.
1585 This isn't especially tuned for speed.
1589 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1596 XColor col1[ANALOGTV_PIC_LEN];
1597 XColor col2[ANALOGTV_PIC_LEN];
1598 int multiq[ANALOGTV_PIC_LEN+4];
1599 int y_overscan=5; /* overscan this much top and bottom */
1600 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
1602 img_w=pic_im->width;
1603 img_h=pic_im->height;
1605 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
1606 double phase=90.0-90.0*i;
1608 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
1611 for (y=0; y<y_scanlength; y++) {
1612 int picy1=(y*img_h)/y_scanlength;
1613 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
1615 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1616 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
1617 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
1618 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
1620 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
1621 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
1623 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
1624 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
1626 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1628 int filty,filti,filtq;
1631 y=0.30 r + 0.59 g + 0.11 b
1632 i=0.60 r - 0.28 g - 0.32 b
1633 q=0.21 r - 0.52 g + 0.31 b
1634 The coefficients below are in .4 format */
1636 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
1637 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
1638 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
1639 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
1640 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
1641 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
1643 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
1644 with an extra zero at 3.5 MHz, from
1645 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
1647 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
1648 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
1649 fyx[6] = (rawy * 1897) >> 16;
1650 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
1651 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
1652 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
1653 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
1656 /* Filter I at 1.5 MHz. 3 pole Butterworth from
1657 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
1659 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
1660 fix[3] = (rawi * 1413) >> 16;
1661 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
1662 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
1663 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
1666 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
1667 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
1669 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
1670 fqx[3] = (rawq * 75) >> 16;
1671 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
1672 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
1673 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
1677 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
1678 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
1679 if (composite>125) composite=125;
1680 if (composite<0) composite=0;
1681 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
1689 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
1692 int change=random()%ANALOGTV_V;
1693 unsigned int fastrnd=random();
1694 double hso=(int)(random()%1000)-500;
1695 int yofs=random()%ANALOGTV_V;
1698 for (y=change; y<ANALOGTV_V; y++) {
1699 int s2y=(y+yofs)%ANALOGTV_V;
1701 int noiselevel=60000 / (y-change+100);
1703 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
1705 for (x=0; x<ANALOGTV_H; x++) {
1707 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
1708 noise=(filt*noiselevel)>>16;
1709 newsig=s2->signal[s2y][x] + noise;
1710 if (newsig>120) newsig=120;
1711 if (newsig<0) newsig=0;
1712 it->signal[y][x]=newsig;
1720 void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
1722 analogtv_input *inp=rec->input;
1723 double *ps=it->rx_signal;
1724 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1726 signed char *ss=&inp->signal[0][0];
1727 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1728 signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
1730 int ec=it->channel_change_cycles;
1731 double level=rec->level;
1732 double hfloss=rec->hfloss;
1733 unsigned int fastrnd=random();
1736 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1738 /* duplicate the first line into the Nth line to ease wraparound computation */
1739 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1740 ANALOGTV_H * sizeof(inp->signal[0][0]));
1742 for (i=0; i<8; i++) dp[i]=0.0;
1747 /* Do a big noisy transition. We can make the transition noise of
1748 high constant strength regardless of signal strength.
1750 There are two separate state machines. here, One is the noise
1751 process and the other is the
1753 We don't bother with the FIR filter here
1758 while (p!=pe && ec>0) {
1760 double sig0=(double)s[0];
1761 double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
1762 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1764 p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
1766 noise_ampl *= 0.99995;
1777 double sig0,sig1,sig2,sig3,sigr;
1784 dp[0]=sig0+sig1+sig2+sig3;
1786 /* Get the video out signal, and add some ghosting, typical of RF
1787 monitor cables. This corresponds to a pretty long cable, but
1791 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1792 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1793 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1795 p[0] += (sig0+sigr + sig2*hfloss) * level;
1796 p[1] += (sig1+sigr + sig3*hfloss) * level;
1797 p[2] += (sig2+sigr + sig0*hfloss) * level;
1798 p[3] += (sig3+sigr + sig1*hfloss) * level;
1802 if (s>=se) s = ss + (s-se);
1805 it->rx_signal_level =
1806 sqrt(it->rx_signal_level * it->rx_signal_level +
1807 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1808 rec->ghostfir[2] + rec->ghostfir[3]))));
1811 it->channel_change_cycles=0;
1817 if (it->hashnoise_times[lineno]) {
1818 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
1820 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
1822 double cur=frand(150.0)-20.0;
1823 int len=random()%15+3;
1824 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
1825 for (i=0; i<len; i++) {
1826 double sig=signal[hnt];
1829 cur += frand(5.0)-5.0;
1830 maxampl = maxampl*0.9;
1840 void analogtv_init_signal(analogtv *it, double noiselevel)
1842 double *ps=it->rx_signal;
1843 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1845 unsigned int fastrnd=random();
1846 double nm1=0.0,nm2=0.0;
1847 double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
1851 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1853 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1856 it->rx_signal_level = noiselevel;
1860 analogtv_reception_update(analogtv_reception *rec)
1864 if (rec->multipath > 0.0) {
1865 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1866 rec->ghostfir2[i] +=
1867 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
1869 if (random()%20==0) {
1870 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
1871 = rec->multipath * (frand(0.08)-0.04);
1873 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1874 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
1878 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
1879 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
1883 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1884 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
1892 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
1893 int w, int h, char *fontname)
1900 XWindowAttributes xgwa;
1905 XGetWindowAttributes (dpy, window, &xgwa);
1909 font = XLoadQueryFont (dpy, fontname);
1911 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
1915 text_pm=XCreatePixmap(dpy, window, 128*f->char_w, f->char_h, xgwa.depth);
1917 memset(&gcv, 0, sizeof(gcv));
1921 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
1923 XSetForeground(dpy, gc, 0);
1924 XFillRectangle(dpy, text_pm, gc, 0, 0, 128*f->char_w, f->char_h);
1925 XSetForeground(dpy, gc, 1);
1927 for (i=0; i<128; i++) {
1929 int x=f->char_w*i+1;
1930 int y=f->char_h*8/10;
1931 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
1933 f->text_im = XGetImage(dpy, text_pm, 0, 0, 128*f->char_w, f->char_h,
1936 XFreePixmap(dpy, text_pm);
1938 f->text_im = XCreateImage(dpy, xgwa.visual, xgwa.depth,
1940 128*f->char_w, f->char_h, 8, 0);
1941 f->text_im->data = (char *)calloc(f->text_im->height,
1942 f->text_im->bytes_per_line);
1950 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
1952 if (x<0 || x>=f->char_w) return 0;
1953 if (y<0 || y>=f->char_h) return 0;
1954 if (c<0 || c>=128) return 0;
1955 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
1959 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
1961 if (x<0 || x>=f->char_w) return;
1962 if (y<0 || y>=f->char_h) return;
1963 if (c<0 || c>=128) return;
1965 XPutPixel(f->text_im, c*f->char_w + x, y, value);
1969 analogtv_font_set_char(analogtv_font *f, int c, char *s)
1973 if (c<0 || c>=128) return;
1975 for (y=0; y<f->char_h; y++) {
1976 for (x=0; x<f->char_w; x++) {
1978 value=(*s==' ') ? 0 : 1;
1979 analogtv_font_set_pixel(f, c, x, y, value);
1986 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
1989 for (i=0; i<4; i++) {
1990 double w=90.0*i + phase;
1991 double val=luma + chroma * (cos(3.1415926/180.0*w));
1992 if (val<0.0) val=0.0;
1993 if (val>127.0) val=127.0;
1999 analogtv_draw_solid(analogtv_input *input,
2000 int left, int right, int top, int bot,
2005 if (right-left<4) right=left+4;
2006 if (bot-top<1) bot=top+1;
2008 for (y=top; y<bot; y++) {
2009 for (x=left; x<right; x++) {
2010 input->signal[y][x] = ntsc[x&3];
2017 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2018 double left, double right, double top, double bot,
2019 double luma, double chroma, double phase)
2023 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2024 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2025 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2026 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2028 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2029 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2034 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2035 int c, int x, int y, int ntsc[4])
2037 int yc,xc,ys,xs,pix;
2039 for (yc=0; yc<f->char_h; yc++) {
2040 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2041 if (ys<0 || ys>=ANALOGTV_V) continue;
2043 for (xc=0; xc<f->char_w; xc++) {
2044 pix=analogtv_font_pixel(f, c, xc, yc);
2046 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2047 if (xs<0 || xs>=ANALOGTV_H) continue;
2049 input->signal[ys][xs] = ntsc[xs&3];
2058 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2059 char *s, int x, int y, int ntsc[4])
2062 analogtv_draw_char(input, f, *s, x, y, ntsc);
2069 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2070 char *s, int x, int y, int ntsc[4])
2072 int width=strlen(s) * f->char_w * 4;
2075 analogtv_draw_string(input, f, s, x, y, ntsc);
2079 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2080 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2081 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2082 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2083 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2084 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2085 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2086 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2089 Much of this function was adapted from logo.c
2092 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2093 const char * const *xpm, int left, int top)
2098 int ncolors, nbytes;
2101 int r; int g; int b;
2105 if (4 != sscanf ((const char *) *xpm,
2107 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2109 if (ncolors < 1 || ncolors > 255)
2111 if (nbytes != 1) /* a serious limitation */
2115 for (i = 0; i < ncolors; i++) {
2116 const char *line = *xpm;
2117 int colori = ((unsigned char)*line++)&0xff;
2122 while (*line == ' ' || *line == '\t')
2125 if (which != 'c' && which != 'm')
2127 while (*line == ' ' || *line == '\t')
2129 if (!strncasecmp(line, "None", 4))
2138 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2140 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2142 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2157 for (y=0; y<xpmh; y++) {
2158 const char *line = *xpm++;
2160 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2162 for (x=0; x<xpmw; x++) {
2163 int cbyte=((unsigned char)line[x])&0xff;
2166 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2168 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2169 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2170 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2177 for (i=0; i<4; i++) {
2178 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2179 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2182 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2183 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2184 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2185 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];
2190 extern XtAppContext app;
2193 analogtv_handle_events (analogtv *it)
2195 XSync(it->dpy, False);
2196 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
2197 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
2199 while (XPending (it->dpy))
2202 XNextEvent (it->dpy, &event);
2203 switch (event.xany.type)
2213 if (it->key_handler) {
2214 if (it->key_handler (it->dpy, &event, it->key_data))
2217 XLookupString (&event.xkey, &c, 1, &keysym, 0);
2218 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
2224 /* I don't seem to get an event when clicking the "full
2225 screen" window manager icon, at least when using
2226 metacity. Thus, it doesn't change the video size. Is this
2227 some separate WM_* message I have to deal with?
2229 case ConfigureNotify:
2230 if (event.xconfigure.width != it->xgwa.width ||
2231 event.xconfigure.height != it->xgwa.height)
2232 analogtv_reconfigure(it);
2236 case GraphicsExpose:
2243 if (it->event_handler) {
2244 (*it->event_handler) (it->dpy, &event);