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;
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 char *ss=&inp->signal[0][0];
1724 char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1725 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
1889 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
1890 int w, int h, char *fontname)
1897 XWindowAttributes xgwa;
1902 XGetWindowAttributes (dpy, window, &xgwa);
1906 font = XLoadQueryFont (dpy, fontname);
1908 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
1912 text_pm=XCreatePixmap(dpy, window, 128*f->char_w, f->char_h, xgwa.depth);
1914 memset(&gcv, 0, sizeof(gcv));
1918 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
1920 XSetForeground(dpy, gc, 0);
1921 XFillRectangle(dpy, text_pm, gc, 0, 0, 128*f->char_w, f->char_h);
1922 XSetForeground(dpy, gc, 1);
1924 for (i=0; i<128; i++) {
1926 int x=f->char_w*i+1;
1927 int y=f->char_h*8/10;
1928 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
1930 f->text_im = XGetImage(dpy, text_pm, 0, 0, 128*f->char_w, f->char_h,
1933 XFreePixmap(dpy, text_pm);
1935 f->text_im = XCreateImage(dpy, xgwa.visual, xgwa.depth,
1937 128*f->char_w, f->char_h, 8, 0);
1938 f->text_im->data = (char *)calloc(f->text_im->height,
1939 f->text_im->bytes_per_line);
1947 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
1949 if (x<0 || x>=f->char_w) return 0;
1950 if (y<0 || y>=f->char_h) return 0;
1951 if (c<0 || c>=128) return 0;
1952 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
1956 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
1958 if (x<0 || x>=f->char_w) return;
1959 if (y<0 || y>=f->char_h) return;
1960 if (c<0 || c>=128) return;
1962 XPutPixel(f->text_im, c*f->char_w + x, y, value);
1966 analogtv_font_set_char(analogtv_font *f, int c, char *s)
1970 if (c<0 || c>=128) return;
1972 for (y=0; y<f->char_h; y++) {
1973 for (x=0; x<f->char_w; x++) {
1975 value=(*s==' ') ? 0 : 1;
1976 analogtv_font_set_pixel(f, c, x, y, value);
1983 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
1986 for (i=0; i<4; i++) {
1987 double w=90.0*i + phase;
1988 double val=luma + chroma * (cos(3.1415926/180.0*w));
1989 if (val<0.0) val=0.0;
1990 if (val>127.0) val=127.0;
1996 analogtv_draw_solid(analogtv_input *input,
1997 int left, int right, int top, int bot,
2002 if (right-left<4) right=left+4;
2003 if (bot-top<1) bot=top+1;
2005 for (y=top; y<bot; y++) {
2006 for (x=left; x<right; x++) {
2007 input->signal[y][x] = ntsc[x&3];
2014 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2015 double left, double right, double top, double bot,
2016 double luma, double chroma, double phase)
2020 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2021 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2022 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2023 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2025 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2026 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2031 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2032 int c, int x, int y, int ntsc[4])
2034 int yc,xc,ys,xs,pix;
2036 for (yc=0; yc<f->char_h; yc++) {
2037 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2038 if (ys<0 || ys>=ANALOGTV_V) continue;
2040 for (xc=0; xc<f->char_w; xc++) {
2041 pix=analogtv_font_pixel(f, c, xc, yc);
2043 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2044 if (xs<0 || xs>=ANALOGTV_H) continue;
2046 input->signal[ys][xs] = ntsc[xs&3];
2055 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2056 char *s, int x, int y, int ntsc[4])
2059 analogtv_draw_char(input, f, *s, x, y, ntsc);
2066 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2067 char *s, int x, int y, int ntsc[4])
2069 int width=strlen(s) * f->char_w * 4;
2072 analogtv_draw_string(input, f, s, x, y, ntsc);
2076 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2077 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2078 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2079 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2080 0, 10,11,12,13,14,15,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, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2083 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2086 Much of this function was adapted from logo.c
2089 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2090 const char * const *xpm, int left, int top)
2095 int ncolors, nbytes;
2098 int r; int g; int b;
2102 if (4 != sscanf ((const char *) *xpm,
2104 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2106 if (ncolors < 1 || ncolors > 255)
2108 if (nbytes != 1) /* a serious limitation */
2112 for (i = 0; i < ncolors; i++) {
2113 const char *line = *xpm;
2114 int colori = ((unsigned char)*line++)&0xff;
2119 while (*line == ' ' || *line == '\t')
2122 if (which != 'c' && which != 'm')
2124 while (*line == ' ' || *line == '\t')
2126 if (!strncasecmp(line, "None", 4))
2135 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2137 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2139 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2154 for (y=0; y<xpmh; y++) {
2155 const char *line = *xpm++;
2157 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2159 for (x=0; x<xpmw; x++) {
2160 int cbyte=((unsigned char)line[x])&0xff;
2163 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2165 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2166 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2167 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2174 for (i=0; i<4; i++) {
2175 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2176 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2179 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2180 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2181 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2182 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];
2187 extern XtAppContext app;
2190 analogtv_handle_events (analogtv *it)
2192 XSync(it->dpy, False);
2193 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
2194 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
2196 while (XPending (it->dpy))
2199 XNextEvent (it->dpy, &event);
2200 switch (event.xany.type)
2210 if (it->key_handler) {
2211 if (it->key_handler (it->dpy, &event, it->key_data))
2214 XLookupString (&event.xkey, &c, 1, &keysym, 0);
2215 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
2221 /* I don't seem to get an event when clicking the "full
2222 screen" window manager icon, at least when using
2223 metacity. Thus, it doesn't change the video size. Is this
2224 some separate WM_* message I have to deal with?
2226 case ConfigureNotify:
2227 if (event.xconfigure.width != it->xgwa.width ||
2228 event.xconfigure.height != it->xgwa.height)
2229 analogtv_reconfigure(it);
2233 case GraphicsExpose:
2240 if (it->event_handler) {
2241 (*it->event_handler) (it->dpy, &event);