1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
51 Trevor Blackwell <tlb@tlb.org>
56 #else /* !HAVE_COCOA */
57 # include <X11/Xlib.h>
58 # include <X11/Xutil.h>
63 #include "resources.h"
66 #include "grabscreen.h"
71 /* only works on linux + freebsd */
72 #include <machine/cpufunc.h>
74 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
75 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
76 #define DTIME dtimes[n_dtimes++]=rdtsc()
77 #define DTIME_SHOW(DIV) \
79 double _dtime_div=(DIV); \
80 printf("time/%.1f: ",_dtime_div); \
81 for (i=1; i<n_dtimes; i++) \
82 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
89 #define DTIME_START do { } while (0)
90 #define DTIME do { } while (0)
91 #define DTIME_SHOW(DIV) do { } while (0)
96 #define FASTRND (fastrnd = fastrnd*1103515245+12345)
98 static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
101 static double puramp(analogtv *it, double tc, double start, double over)
103 double pt=it->powerup-start;
105 if (pt<0.0) return 0.0;
106 if (pt>900.0 || pt/tc>8.0) return 1.0;
108 ret=(1.0-exp(-pt/tc))*over;
109 if (ret>1.0) return 1.0;
114 There are actual standards for TV signals: NTSC and RS-170A describe the
115 system used in the US and Japan. Europe has slightly different systems, but
116 not different enough to make substantially different screensaver displays.
117 Sadly, the standards bodies don't do anything so useful as publish the spec on
118 the web. Best bets are:
120 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
121 http://www.ntsc-tv.com/ntsc-index-02.htm
123 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
124 values it calculates across each scan line. In PseudoColor mode, it consider
125 each possible pattern of 5 preceding bit values in each possible position
126 modulo 4 and allocates a color for each. A few things, like the brightening on
127 the right side as the horizontal trace slows down, aren't done in PseudoColor.
129 I'd like to add a bit of visible retrace, but it conflicts with being able to
130 bitcopy the image when fast scrolling. After another couple of CPU
131 generations, we could probably regenerate the whole image from scratch every
132 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
136 /* localbyteorder is MSBFirst or LSBFirst */
137 static int localbyteorder;
138 static const double float_low8_ofs=8388608.0;
139 static int float_extraction_works;
151 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
152 localbyteorder=*(char *)&localbyteorder_loc;
159 float_extraction_works=1;
160 for (i=0; i<256*4; i++) {
161 fe.f=float_low8_ofs+(double)i;
165 printf("Float extraction failed for %d => %d\n",i,ans);
167 float_extraction_works=0;
176 analogtv_set_defaults(analogtv *it, char *prefix)
180 sprintf(buf,"%sTVTint",prefix);
181 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
182 sprintf(buf,"%sTVColor",prefix);
183 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
184 sprintf(buf,"%sTVBrightness",prefix);
185 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
186 sprintf(buf,"%sTVContrast",prefix);
187 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
188 it->height_control = 1.0;
189 it->width_control = 1.0;
190 it->squish_control = 0.0;
195 it->hashnoise_enable=1;
197 it->horiz_desync=frand(10.0)-5.0;
198 it->squeezebottom=frand(5.0)-1.0;
201 printf("analogtv: prefix=%s\n",prefix);
202 printf(" use: shm=%d cmap=%d color=%d\n",
203 it->use_shm,it->use_cmap,it->use_color);
204 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
205 it->tint_control, it->color_control, it->brightness_control,
206 it->contrast_control);
207 printf(" freq_error %g: %g %d\n",
208 it->freq_error, it->freq_error_inc, it->flutter_tint);
209 printf(" desync: %g %d\n",
210 it->horiz_desync, it->flutter_horiz_desync);
211 printf(" hashnoise rpm: %g\n",
213 printf(" vis: %d %d %d\n",
214 it->visclass, it->visbits, it->visdepth);
215 printf(" shift: %d-%d %d-%d %d-%d\n",
216 it->red_invprec,it->red_shift,
217 it->green_invprec,it->green_shift,
218 it->blue_invprec,it->blue_shift);
219 printf(" size: %d %d %d %d xrepl=%d\n",
220 it->usewidth, it->useheight,
221 it->screen_xo, it->screen_yo, it->xrepl);
223 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
224 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
225 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
226 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
227 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
228 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
229 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
230 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
231 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
232 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
233 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
234 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
235 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
241 extern Bool mono_p; /* shoot me */
244 analogtv_free_image(analogtv *it)
248 #ifdef HAVE_XSHM_EXTENSION
249 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
252 XDestroyImage(it->image);
259 analogtv_alloc_image(analogtv *it)
262 #ifdef HAVE_XSHM_EXTENSION
263 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
264 &it->shm_info, it->usewidth, it->useheight);
266 if (!it->image) it->use_shm=0;
269 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
270 it->usewidth, it->useheight, 8, 0);
271 it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
273 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
278 analogtv_configure(analogtv *it)
280 int oldwidth=it->usewidth;
281 int oldheight=it->useheight;
282 int wlim,hlim,height_diff;
284 /* If the window is very small, don't let the image we draw get lower
285 than the actual TV resolution (266x200.)
287 If the aspect ratio of the window is within 15% of a 4:3 ratio,
288 then scale the image to exactly fill the window.
290 Otherwise, center the image either horizontally or vertically,
291 padding on the left+right, or top+bottom, but not both.
293 If it's very close (2.5%) to a multiple of VISLINES, make it exact
294 For example, it maps 1024 => 1000.
296 float percent = 0.15; /* jwz: 20% caused severe top/bottom clipping
297 in Pong on 1680x1050 iMac screen. */
298 float min_ratio = 4.0 / 3.0 * (1 - percent);
299 float max_ratio = 4.0 / 3.0 * (1 + percent);
301 float height_snap=0.025;
303 hlim = it->xgwa.height;
304 wlim = it->xgwa.width;
305 ratio = wlim / (float) hlim;
307 if (wlim < 266 || hlim < 200)
313 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
314 wlim, hlim, it->xgwa.width, it->xgwa.height,
315 min_ratio, ratio, max_ratio);
318 else if (ratio > min_ratio && ratio < max_ratio)
322 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
323 wlim, hlim, min_ratio, ratio, max_ratio);
326 else if (ratio > max_ratio)
328 wlim = hlim*max_ratio;
331 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
332 wlim, hlim, it->xgwa.width, it->xgwa.height,
333 min_ratio, ratio, max_ratio);
336 else /* ratio < min_ratio */
338 hlim = wlim/min_ratio;
341 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
342 wlim, hlim, it->xgwa.width, it->xgwa.height,
343 min_ratio, ratio, max_ratio);
348 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
349 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
355 /* Most times this doesn't change */
356 if (wlim != oldwidth || hlim != oldheight) {
361 it->xrepl=1+it->usewidth/640;
362 if (it->xrepl>2) it->xrepl=2;
363 it->subwidth=it->usewidth/it->xrepl;
365 analogtv_free_image(it);
366 analogtv_alloc_image(it);
369 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
370 it->screen_yo = (it->xgwa.height-it->useheight)/2;
375 analogtv_reconfigure(analogtv *it)
377 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
378 analogtv_configure(it);
382 analogtv_allocate(Display *dpy, Window window)
390 it=(analogtv *)calloc(1,sizeof(analogtv));
398 #ifdef HAVE_XSHM_EXTENSION
404 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
406 it->screen=it->xgwa.screen;
407 it->colormap=it->xgwa.colormap;
408 it->visclass=it->xgwa.visual->class;
409 it->visbits=it->xgwa.visual->bits_per_rgb;
410 it->visdepth=it->xgwa.depth;
411 if (it->visclass == TrueColor || it->visclass == DirectColor) {
412 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
417 it->use_color=!mono_p;
419 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
421 it->use_color=!mono_p;
428 it->red_mask=it->xgwa.visual->red_mask;
429 it->green_mask=it->xgwa.visual->green_mask;
430 it->blue_mask=it->xgwa.visual->blue_mask;
431 it->red_shift=it->red_invprec=-1;
432 it->green_shift=it->green_invprec=-1;
433 it->blue_shift=it->blue_invprec=-1;
435 /* Is there a standard way to do this? Does this handle all cases? */
437 for (shift=0; shift<32; shift++) {
438 for (prec=1; prec<16 && prec<40-shift; prec++) {
439 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
440 if (it->red_shift<0 && mask==it->red_mask)
441 it->red_shift=shift, it->red_invprec=16-prec;
442 if (it->green_shift<0 && mask==it->green_mask)
443 it->green_shift=shift, it->green_invprec=16-prec;
444 if (it->blue_shift<0 && mask==it->blue_mask)
445 it->blue_shift=shift, it->blue_invprec=16-prec;
448 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
449 if (0) fprintf(stderr,"Can't figure out color space\n");
453 for (i=0; i<ANALOGTV_CV_MAX; i++) {
454 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
455 if (intensity>65535) intensity=65535;
456 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
457 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
458 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
463 gcv.background=get_pixel_resource(it->dpy, it->colormap,
464 "background", "Background");
466 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
467 XSetWindowBackground(it->dpy, it->window, gcv.background);
468 XClearWindow(dpy,window);
470 analogtv_configure(it);
480 analogtv_release(analogtv *it)
484 #ifdef HAVE_XSHM_EXTENSION
485 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
488 XDestroyImage(it->image);
492 if (it->gc) XFreeGC(it->dpy, it->gc);
494 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
500 First generate the I and Q reference signals, which we'll multiply
501 the input signal by to accomplish the demodulation. Normally they
502 are shifted 33 degrees from the colorburst. I think this was convenient
503 for inductor-capacitor-vacuum tube implementation.
505 The tint control, FWIW, just adds a phase shift to the chroma signal,
506 and the color control controls the amplitude.
508 In text modes (colormode==0) the system disabled the color burst, and no
509 color was detected by the monitor.
511 freq_error gives a mismatch between the built-in oscillator and the
512 TV's colorbust. Some II Plus machines seemed to occasionally get
513 instability problems -- the crystal oscillator was a single
514 transistor if I remember correctly -- and the frequency would vary
515 enough that the tint would change across the width of the screen.
516 The left side would be in correct tint because it had just gotten
517 resynchronized with the color burst.
519 If we're using a colormap, set it up.
522 analogtv_set_demod(analogtv *it)
524 int y_levels=10,i_levels=5,q_levels=5;
527 In principle, we might be able to figure out how to adjust the
528 color map frame-by-frame to get some nice color bummage. But I'm
529 terrified of changing the color map because we'll get flashing.
531 I can hardly believe we still have to deal with colormaps. They're
532 like having NEAR PTRs: an enormous hassle for the programmer just
533 to save on memory. They should have been deprecated by 1995 or
537 if (it->use_cmap && !it->n_colors) {
540 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
546 for (yli=0; yli<y_levels; yli++) {
547 for (ili=0; ili<i_levels; ili++) {
548 for (qli=0; qli<q_levels; qli++) {
549 double interpy,interpi,interpq;
550 double levelmult=700.0;
554 interpy=100.0 * ((double)yli/y_levels);
555 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
556 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
558 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
559 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
560 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
562 if (r>65535) r=65535;
564 if (g>65535) g=65535;
566 if (b>65535) b=65535;
569 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
570 interpy, interpi, interpq,
578 if (!XAllocColor(it->dpy, it->colormap, &col)) {
579 if (q_levels > y_levels*4/12)
581 else if (i_levels > y_levels*5/12)
590 it->colors[it->n_colors++]=col.pixel;
595 it->cmap_y_levels=y_levels;
596 it->cmap_i_levels=i_levels;
597 it->cmap_q_levels=q_levels;
606 analogtv_line_signature(analogtv_input *input, int lineno)
609 char *origsignal=&input->signal[(lineno+input->vsync)
610 %ANALOGTV_V][input->line_hsync[lineno]];
614 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
616 hash = hash + (hash<<17) + c;
619 hash += input->line_hsync[lineno];
622 hash += input->hashnoise_times[lineno];
631 /* Here we model the analog circuitry of an NTSC television.
632 Basically, it splits the signal into 3 signals: Y, I and Q. Y
633 corresponds to luminance, and you get it by low-pass filtering the
634 input signal to below 3.57 MHz.
636 I and Q are the in-phase and quadrature components of the 3.57 MHz
637 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
638 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
639 resolution in some colors than others, the I component gets
640 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
641 is approximately orange-blue, and Q is roughly purple-green. See
642 http://www.ntsc-tv.com for details.
644 We actually do an awful lot to the signal here. I suspect it would
645 make sense to wrap them all up together by calculating impulse
646 response and doing FFT convolutions.
651 analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
657 int phasecorr=(signal-it->rx_signal)&3;
658 struct analogtv_yiq_s *yiq;
660 double agclevel=it->agclevel;
661 double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
662 double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
667 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
668 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
669 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
670 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
672 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
675 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
676 double tint_q = sin((103 + it->color_control)*3.1415926/180);
678 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
679 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
680 multiq2[2]=-multiq2[0];
681 multiq2[3]=-multiq2[1];
687 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
688 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
689 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
690 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
691 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
692 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
693 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
697 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
698 for (i=0; i<5; i++) dp[i]=0.0;
701 assert(end < ANALOGTV_PIC_LEN+10);
703 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
704 for (i=0; i<24; i++) dp[i]=0.0;
705 for (i=start, yiq=it->yiq+start, sp=signal+start;
707 i++, dp--, yiq++, sp++) {
709 /* Now filter them. These are infinite impulse response filters
710 calculated by the script at
711 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
712 fixed-point integer DSP, son. No place for wimps. We do it in
713 integer because you can count on integer being faster on most
714 CPUs. We care about speed because we need to recalculate every
715 time we blink text, and when we spew random bytes into screen
716 memory. This is roughly 16.16 fixed point arithmetic, but we
717 scale some filter values up by a few bits to avoid some nasty
720 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
721 with an extra zero at 3.5 MHz, from
722 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
725 dp[0] = sp[0] * 0.0469904257251935 * agclevel;
726 dp[8] = (+1.0*(dp[6]+dp[0])
732 yiq->y = dp[8] + brightadd;
736 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
737 for (i=0; i<27; i++) dp[i]=0.0;
739 for (i=start, yiq=it->yiq+start, sp=signal+start;
741 i++, dp--, yiq++, sp++) {
744 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
745 1.5 MHz with an extra zero at 3.5 MHz, from
746 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
750 dp[0] = sig*multiq2[i&3] * 0.0833333333333;
751 yiq->i=dp[8] = (dp[5] + dp[0]
754 -0.3333333333 * dp[10]);
756 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
757 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
758 +3.0*(dp[16+4] + dp[16+1])
759 +4.0*(dp[16+3] + dp[16+2])
760 -0.3333333333 * dp[24+2]);
763 for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
764 yiq->i = yiq->q = 0.0;
770 analogtv_setup_teletext(analogtv_input *input)
773 int teletext=ANALOGTV_BLACK_LEVEL;
775 /* Teletext goes in line 21. But I suspect there are other things
776 in the vertical retrace interval */
778 for (y=19; y<22; y++) {
779 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
781 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
783 input->signal[y][x]=teletext;
789 analogtv_setup_frame(analogtv *it)
795 if (it->flutter_horiz_desync) {
796 /* Horizontal sync during vertical sync instability. */
797 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
798 ((int)(random()&0xff)-0x80) *
799 ((int)(random()&0xff)-0x80) *
800 ((int)(random()&0xff)-0x80) * 0.000001;
803 for (i=0; i<ANALOGTV_V; i++) {
804 it->hashnoise_times[i]=0;
807 if (it->hashnoise_enable && !it->hashnoise_on) {
808 if (random()%10000==0) {
810 it->shrinkpulse=random()%ANALOGTV_V;
813 if (random()%1000==0) {
816 if (it->hashnoise_on) {
817 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
818 ((int)(random()%2000)-1000)*0.1;
820 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
821 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
823 if (it->hashnoise_rpm > 0.0) {
825 int hnc=it->hashnoise_counter; /* in 24.8 format */
827 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
828 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
829 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
831 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
832 y=(hnc>>8)/ANALOGTV_H;
833 x=(hnc>>8)%ANALOGTV_H;
835 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
836 it->hashnoise_times[y]=x;
838 hnc += hni + (int)(random()%65536)-32768;
840 hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;
843 if (it->rx_signal_level != 0.0)
844 it->agclevel = 1.0/it->rx_signal_level;
849 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
850 printf(" %0.3f",it->ghostfir[i]);
852 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
857 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
862 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
864 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
865 vsync=lineno>=3 && lineno<7;
867 sig=input->signal[lineno];
869 i=ANALOGTV_SYNC_START;
871 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
872 while (i<ANALOGTV_H) sig[i++]=synclevel;
874 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
875 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
876 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
878 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
881 /* 9 cycles of colorburst */
882 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
883 sig[i+1] += ANALOGTV_CB_LEVEL;
884 sig[i+3] -= ANALOGTV_CB_LEVEL;
891 analogtv_sync(analogtv *it)
893 int cur_hsync=it->cur_hsync;
894 int cur_vsync=it->cur_vsync;
899 double cbfc=1.0/128.0;
901 sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;
902 for (i=-32; i<32; i++) {
903 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
904 sp = it->rx_signal + lineno*ANALOGTV_H;
906 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
909 filt *= it->agclevel;
911 osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
913 if (osc >= 1.05+0.0002 * filt) break;
915 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
917 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
919 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
921 sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
922 )*ANALOGTV_H + cur_hsync;
923 for (i=-8; i<8; i++) {
924 osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
925 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
927 if (osc >= 1.005 + 0.0001*filt) break;
929 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
932 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
933 ANALOGTV_H) % ANALOGTV_H;
935 /* Now look for the colorburst, which is a few cycles after the H
936 sync pulse, and store its phase.
937 The colorburst is 9 cycles long, and we look at the middle 5
942 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
943 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
944 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
945 sp[i]*it->agclevel*cbfc;
953 for (i=0; i<4; i++) {
954 tot += it->cb_phase[i]*it->cb_phase[i];
956 cbgain = 32.0/sqrt(tot);
958 for (i=0; i<4; i++) {
959 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
964 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
966 it->cb_phase[0], it->cb_phase[1],
967 it->cb_phase[2], it->cb_phase[3]);
970 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
973 it->cur_hsync = cur_hsync;
974 it->cur_vsync = cur_vsync;
978 analogtv_levelmult(analogtv *it, int level)
980 static const double levelfac[3]={-7.5, 5.5, 24.5};
981 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
985 analogtv_level(analogtv *it, int y, int ytop, int ybot)
989 if (y==ytop || y==ybot-1) level=0;
990 else if (y==ytop+1 || y==ybot-2) level=1;
993 else if (ybot-ytop>=5) {
994 if (y==ytop || y==ybot-1) level=0;
997 else if (ybot-ytop>=3) {
998 if (y==ytop) level=0;
1008 The point of this stuff is to ensure that when useheight is not a
1009 multiple of VISLINES so that TV scan lines map to different numbers
1010 of vertical screen pixels, the total brightness of each scan line
1012 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1013 it interpolates extra black lines.
1017 analogtv_setup_levels(analogtv *it, double avgheight)
1020 static const double levelfac[3]={-7.5, 5.5, 24.5};
1022 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1024 for (i=0; i<height; i++) {
1025 it->leveltable[height][i].index = 2;
1029 it->leveltable[height][0].index=0;
1032 it->leveltable[height][height-1].index=0;
1035 it->leveltable[height][1].index=1;
1036 it->leveltable[height][height-2].index=1;
1039 for (i=0; i<height; i++) {
1040 it->leveltable[height][i].value =
1041 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1048 analogtv_blast_imagerow(analogtv *it,
1049 float *rgbf, float *rgbf_end,
1054 char *level_copyfrom[3];
1055 int xrepl=it->xrepl;
1056 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1058 for (y=ytop; y<ybot; y++) {
1059 int level=it->leveltable[ybot-ytop][y-ytop].index;
1060 double levelmult=it->leveltable[ybot-ytop][y-ytop].value;
1063 rowdata=it->image->data + y*it->image->bytes_per_line;
1065 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1066 why standard graphics sw has to be fast, or else people will have to
1067 work around it and risk incompatibility. The quickdraw folks
1068 understood this. The other answer would be for X11 to have fewer
1069 formats for bitm.. oh, never mind. If neither of these cases work
1070 (they probably cover 99% of setups) it falls back on the Xlib
1073 if (level_copyfrom[level]) {
1074 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1077 level_copyfrom[level] = rowdata;
1081 else if (it->image->format==ZPixmap &&
1082 it->image->bits_per_pixel==32 &&
1083 sizeof(unsigned int)==4 &&
1084 it->image->byte_order==localbyteorder) {
1085 /* int is more likely to be 32 bits than long */
1086 unsigned int *pixelptr=(unsigned int *)rowdata;
1089 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1090 int ntscri=rpf[0]*levelmult;
1091 int ntscgi=rpf[1]*levelmult;
1092 int ntscbi=rpf[2]*levelmult;
1093 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1094 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1095 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1096 pix = (it->red_values[ntscri] |
1097 it->green_values[ntscgi] |
1098 it->blue_values[ntscbi]);
1102 if (xrepl>=3) pixelptr[2] = pix;
1107 else if (it->image->format==ZPixmap &&
1108 it->image->bits_per_pixel==16 &&
1109 sizeof(unsigned short)==2 &&
1110 float_extraction_works &&
1111 it->image->byte_order==localbyteorder) {
1112 unsigned short *pixelptr=(unsigned short *)rowdata;
1114 float_extract_t r1,g1,b1;
1117 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1118 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1119 r1.f=r2 * levelmult+float_low8_ofs;
1120 g1.f=g2 * levelmult+float_low8_ofs;
1121 b1.f=b2 * levelmult+float_low8_ofs;
1122 pix = (it->red_values[r1.i & 0x3ff] |
1123 it->green_values[g1.i & 0x3ff] |
1124 it->blue_values[b1.i & 0x3ff]);
1128 if (xrepl>=3) pixelptr[2] = pix;
1133 else if (it->image->format==ZPixmap &&
1134 it->image->bits_per_pixel==16 &&
1135 sizeof(unsigned short)==2 &&
1136 it->image->byte_order==localbyteorder) {
1137 unsigned short *pixelptr=(unsigned short *)rowdata;
1140 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1141 int r1=rpf[0] * levelmult;
1142 int g1=rpf[1] * levelmult;
1143 int b1=rpf[2] * levelmult;
1144 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1145 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1146 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1147 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1151 if (xrepl>=3) pixelptr[2] = pix;
1157 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1158 int ntscri=rpf[0]*levelmult;
1159 int ntscgi=rpf[1]*levelmult;
1160 int ntscbi=rpf[2]*levelmult;
1161 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1162 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1163 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1164 for (j=0; j<xrepl; j++) {
1165 XPutPixel(it->image, x*xrepl + j, y,
1166 it->red_values[ntscri] | it->green_values[ntscgi] |
1167 it->blue_values[ntscbi]);
1176 analogtv_draw(analogtv *it)
1179 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1180 float *rgb_start, *rgb_end;
1183 int bigloadchange,drawcount;
1186 int overall_top, overall_bot;
1188 float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1189 float *raw_rgb_end=raw_rgb_start+3*it->subwidth;
1192 analogtv_setup_frame(it);
1193 analogtv_set_demod(it);
1195 /* rx_signal has an extra 2 lines at the end, where we copy the
1196 first 2 lines so we can index into it while only worrying about
1197 wraparound on a per-line level */
1198 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1200 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1205 /* if (it->hashnoise_on) baseload=0.5; */
1209 it->crtload[ANALOGTV_TOP-1]=baseload;
1210 puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1211 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1213 analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1215 overall_top=it->useheight;
1218 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1219 int slineno=lineno-ANALOGTV_TOP;
1220 int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
1221 it->useheight/2)*puheight) + it->useheight/2;
1222 int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
1223 it->useheight/2)*puheight) + it->useheight/2;
1225 int linesig=analogtv_line_signature(input,lineno)
1226 + it->hashnoise_times[lineno];
1228 double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
1229 ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
1230 it->line_hsync[lineno]);
1232 if (ytop==ybot) continue;
1233 if (ybot<0 || ytop>it->useheight) continue;
1235 if (ybot>it->useheight) ybot=it->useheight;
1237 if (ybot > ytop+ANALOGTV_MAX_LINEHEIGHT) ybot=ytop+ANALOGTV_MAX_LINEHEIGHT;
1239 if (ytop < overall_top) overall_top=ytop;
1240 if (ybot > overall_bot) overall_bot=ybot;
1242 if (lineno==it->shrinkpulse) {
1249 if (it->hashnoise_rpm>0.0 &&
1252 (slineno<20 && it->flutter_horiz_desync) ||
1253 it->gaussiannoise_level>30 ||
1254 ((it->gaussiannoise_level>2.0 ||
1255 it->multipath) && random()%4) ||
1256 linesig != it->onscreen_signature[lineno])) {
1259 it->onscreen_signature[lineno] = linesig;
1264 Interpolate the 600-dotclock line into however many horizontal
1265 screen pixels we're using, and convert to RGB.
1267 We add some 'bloom', variations in the horizontal scan width with
1268 the amount of brightness, extremely common on period TV sets. They
1269 had a single oscillator which generated both the horizontal scan and
1270 (during the horizontal retrace interval) the high voltage for the
1271 electron beam. More brightness meant more load on the oscillator,
1272 which caused an decrease in horizontal deflection. Look for
1275 Also, the A2 did a bad job of generating horizontal sync pulses
1276 during the vertical blanking interval. This, and the fact that the
1277 horizontal frequency was a bit off meant that TVs usually went a bit
1278 out of sync during the vertical retrace, and the top of the screen
1279 would be bent a bit to the left or right. Look for (shiftthisrow).
1281 We also add a teeny bit of left overscan, just enough to be
1282 annoying, but you can still read the left column of text.
1284 We also simulate compression & brightening on the right side of the
1285 screen. Most TVs do this, but you don't notice because they overscan
1286 so it's off the right edge of the CRT. But the A2 video system used
1287 so much of the horizontal scan line that you had to crank the
1288 horizontal width down in order to not lose the right few characters,
1289 and you'd see the compression on the right edge. Associated with
1290 compression is brightening; since the electron beam was scanning
1291 slower, the same drive signal hit the phosphor harder. Look for
1292 (squishright_i) and (squishdiv).
1299 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
1300 totsignal += signal[i];
1302 totsignal *= it->agclevel;
1303 ncl = 0.95 * it->crtload[lineno-1] +
1305 (totsignal-30000)/100000.0 +
1306 (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
1308 diff=ncl - it->crtload[lineno];
1309 bigloadchange = (diff>0.01 || diff<-0.01);
1310 it->crtload[lineno]=ncl;
1314 double bloomthisrow,shiftthisrow;
1315 double viswidth,middle;
1319 bloomthisrow = -10.0 * it->crtload[lineno];
1320 if (bloomthisrow<-10.0) bloomthisrow=-10.0;
1321 if (bloomthisrow>2.0) bloomthisrow=2.0;
1323 shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
1324 (0.7+cos(slineno*0.6)));
1329 viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
1330 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1332 scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
1334 scw=it->subwidth*scanwidth;
1335 if (scw>it->subwidth) scw=it->usewidth;
1336 scl=it->subwidth/2 - scw/2;
1337 scr=it->subwidth/2 + scw/2;
1339 pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
1340 scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
1341 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1342 squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
1343 - it->squish_control)) *65536.0);
1344 squishdiv=it->subwidth/15;
1346 rgb_start=raw_rgb_start+scl*3;
1347 rgb_end=raw_rgb_start+scr*3;
1349 assert(scanstart_i>=0);
1352 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1354 scanstart_i/65536.0,
1355 squishright_i/65536.0,
1362 for (y=ytop; y<ybot; y++) {
1363 int level=analogtv_level(it, y, ytop, ybot);
1364 double levelmult=analogtv_levelmult(it, level);
1365 double levelmult_y = levelmult * it->contrast_control
1366 * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
1367 double levelmult_iq = levelmult * 0.090;
1369 struct analogtv_yiq_s *yiq=it->yiq;
1370 analogtv_ntsc_to_yiq(it, lineno, signal,
1371 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1376 while (i<0 && x<it->usewidth) {
1377 XPutPixel(it->image, x, y, it->colors[0]);
1382 while (i<scanend_i && x<it->usewidth) {
1383 double pixfrac=(i&0xffff)/65536.0;
1384 double invpixfrac=(1.0-pixfrac);
1386 int yli,ili,qli,cmi;
1388 double interpy=(yiq[pati].y*invpixfrac
1389 + yiq[pati+1].y*pixfrac) * levelmult_y;
1390 double interpi=(yiq[pati].i*invpixfrac
1391 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1392 double interpq=(yiq[pati].q*invpixfrac
1393 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1395 yli = (int)(interpy * it->cmap_y_levels);
1396 ili = (int)((interpi+0.5) * it->cmap_i_levels);
1397 qli = (int)((interpq+0.5) * it->cmap_q_levels);
1399 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1401 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1403 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1405 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1408 if ((random()%65536)==0) {
1409 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1410 interpy, interpi, interpq,
1416 for (j=0; j<it->xrepl; j++) {
1417 XPutPixel(it->image, x, y,
1421 if (i >= squishright_i) {
1422 pixmultinc += pixmultinc/squishdiv;
1426 while (x<it->usewidth) {
1427 XPutPixel(it->image, x, y, it->colors[0]);
1433 struct analogtv_yiq_s *yiq=it->yiq;
1434 analogtv_ntsc_to_yiq(it, lineno, signal,
1435 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1437 pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
1438 / (0.5+0.5*puheight) * 1024.0/100.0;
1440 i=scanstart_i; rrp=rgb_start;
1441 while (i<0 && rrp!=rgb_end) {
1442 rrp[0]=rrp[1]=rrp[2]=0;
1446 while (i<scanend_i && rrp!=rgb_end) {
1447 double pixfrac=(i&0xffff)/65536.0;
1448 double invpixfrac=1.0-pixfrac;
1452 double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1453 double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1454 double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1457 According to the NTSC spec, Y,I,Q are generated as:
1459 y=0.30 r + 0.59 g + 0.11 b
1460 i=0.60 r - 0.28 g - 0.32 b
1461 q=0.21 r - 0.52 g + 0.31 b
1463 So if you invert the implied 3x3 matrix you get what standard
1464 televisions implement with a bunch of resistors (or directly in the
1467 r = y + 0.948 i + 0.624 q
1468 g = y - 0.276 i - 0.639 q
1469 b = y - 1.105 i + 1.729 q
1472 r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
1473 g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
1474 b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
1482 if (i>=squishright_i) {
1483 pixmultinc += pixmultinc/squishdiv;
1484 pixbright += pixbright/squishdiv/2;
1489 while (rrp != rgb_end) {
1490 rrp[0]=rrp[1]=rrp[2]=0.0;
1494 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1498 free(raw_rgb_start);
1501 /* poor attempt at visible retrace */
1502 for (i=0; i<15; i++) {
1503 int ytop=(int)((i*it->useheight/15 -
1504 it->useheight/2)*puheight) + it->useheight/2;
1505 int ybot=(int)(((i+1)*it->useheight/15 -
1506 it->useheight/2)*puheight) + it->useheight/2;
1507 int div=it->usewidth*3/2;
1509 for (x=0; x<it->usewidth; x++) {
1510 y = ytop + (ybot-ytop)*x / div;
1511 if (y<0 || y>=it->useheight) continue;
1512 XPutPixel(it->image, x, y, 0xffffff);
1517 if (it->need_clear) {
1518 XClearWindow(it->dpy, it->window);
1522 if (overall_top>0) {
1523 XClearArea(it->dpy, it->window,
1524 it->screen_xo, it->screen_yo,
1525 it->usewidth, overall_top, 0);
1527 if (it->useheight > overall_bot) {
1528 XClearArea(it->dpy, it->window,
1529 it->screen_xo, it->screen_yo+overall_bot,
1530 it->usewidth, it->useheight-overall_bot, 0);
1533 if (overall_bot > overall_top) {
1535 #ifdef HAVE_XSHM_EXTENSION
1536 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1538 it->screen_xo, it->screen_yo+overall_top,
1539 it->usewidth, overall_bot - overall_top,
1543 XPutImage(it->dpy, it->window, it->gc, it->image,
1545 it->screen_xo, it->screen_yo+overall_top,
1546 it->usewidth, overall_bot - overall_top);
1555 gettimeofday(&tv,NULL);
1557 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1558 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1559 sprintf(buf, "FPS=%0.1f",fps);
1560 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1563 it->last_display_time=tv;
1569 analogtv_input_allocate()
1571 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1577 This takes a screen image and encodes it as a video camera would,
1578 including all the bandlimiting and YIQ modulation.
1579 This isn't especially tuned for speed.
1583 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1590 XColor col1[ANALOGTV_PIC_LEN];
1591 XColor col2[ANALOGTV_PIC_LEN];
1592 int multiq[ANALOGTV_PIC_LEN+4];
1593 int y_overscan=5; /* overscan this much top and bottom */
1594 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
1596 img_w=pic_im->width;
1597 img_h=pic_im->height;
1599 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
1600 double phase=90.0-90.0*i;
1602 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
1605 for (y=0; y<y_scanlength; y++) {
1606 int picy1=(y*img_h)/y_scanlength;
1607 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
1609 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1610 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
1611 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
1612 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
1614 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
1615 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
1617 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
1618 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
1620 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1622 int filty,filti,filtq;
1625 y=0.30 r + 0.59 g + 0.11 b
1626 i=0.60 r - 0.28 g - 0.32 b
1627 q=0.21 r - 0.52 g + 0.31 b
1628 The coefficients below are in .4 format */
1630 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
1631 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
1632 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
1633 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
1634 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
1635 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
1637 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
1638 with an extra zero at 3.5 MHz, from
1639 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
1641 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
1642 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
1643 fyx[6] = (rawy * 1897) >> 16;
1644 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
1645 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
1646 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
1647 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
1650 /* Filter I at 1.5 MHz. 3 pole Butterworth from
1651 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
1653 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
1654 fix[3] = (rawi * 1413) >> 16;
1655 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
1656 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
1657 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
1660 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
1661 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
1663 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
1664 fqx[3] = (rawq * 75) >> 16;
1665 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
1666 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
1667 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
1671 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
1672 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
1673 if (composite>125) composite=125;
1674 if (composite<0) composite=0;
1675 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
1683 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
1686 int change=random()%ANALOGTV_V;
1687 unsigned int fastrnd=random();
1688 double hso=(int)(random()%1000)-500;
1689 int yofs=random()%ANALOGTV_V;
1692 for (y=change; y<ANALOGTV_V; y++) {
1693 int s2y=(y+yofs)%ANALOGTV_V;
1695 int noiselevel=60000 / (y-change+100);
1697 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
1699 for (x=0; x<ANALOGTV_H; x++) {
1701 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
1702 noise=(filt*noiselevel)>>16;
1703 newsig=s2->signal[s2y][x] + noise;
1704 if (newsig>120) newsig=120;
1705 if (newsig<0) newsig=0;
1706 it->signal[y][x]=newsig;
1714 void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
1716 analogtv_input *inp=rec->input;
1717 double *ps=it->rx_signal;
1718 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1720 signed char *ss=&inp->signal[0][0];
1721 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1722 signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
1724 int ec=it->channel_change_cycles;
1725 double level=rec->level;
1726 double hfloss=rec->hfloss;
1727 unsigned int fastrnd=random();
1730 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1732 /* duplicate the first line into the Nth line to ease wraparound computation */
1733 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1734 ANALOGTV_H * sizeof(inp->signal[0][0]));
1736 for (i=0; i<8; i++) dp[i]=0.0;
1741 /* Do a big noisy transition. We can make the transition noise of
1742 high constant strength regardless of signal strength.
1744 There are two separate state machines. here, One is the noise
1745 process and the other is the
1747 We don't bother with the FIR filter here
1752 while (p!=pe && ec>0) {
1754 double sig0=(double)s[0];
1755 double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
1756 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1758 p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
1760 noise_ampl *= 0.99995;
1771 double sig0,sig1,sig2,sig3,sigr;
1778 dp[0]=sig0+sig1+sig2+sig3;
1780 /* Get the video out signal, and add some ghosting, typical of RF
1781 monitor cables. This corresponds to a pretty long cable, but
1785 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1786 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1787 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1789 p[0] += (sig0+sigr + sig2*hfloss) * level;
1790 p[1] += (sig1+sigr + sig3*hfloss) * level;
1791 p[2] += (sig2+sigr + sig0*hfloss) * level;
1792 p[3] += (sig3+sigr + sig1*hfloss) * level;
1796 if (s>=se) s = ss + (s-se);
1799 it->rx_signal_level =
1800 sqrt(it->rx_signal_level * it->rx_signal_level +
1801 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1802 rec->ghostfir[2] + rec->ghostfir[3]))));
1805 it->channel_change_cycles=0;
1811 if (it->hashnoise_times[lineno]) {
1812 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
1814 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
1816 double cur=frand(150.0)-20.0;
1817 int len=random()%15+3;
1818 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
1819 for (i=0; i<len; i++) {
1820 double sig=signal[hnt];
1823 cur += frand(5.0)-5.0;
1824 maxampl = maxampl*0.9;
1834 void analogtv_init_signal(analogtv *it, double noiselevel)
1836 double *ps=it->rx_signal;
1837 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1839 unsigned int fastrnd=random();
1840 double nm1=0.0,nm2=0.0;
1841 double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
1845 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1847 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1850 it->rx_signal_level = noiselevel;
1854 analogtv_reception_update(analogtv_reception *rec)
1858 if (rec->multipath > 0.0) {
1859 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1860 rec->ghostfir2[i] +=
1861 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
1863 if (random()%20==0) {
1864 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
1865 = rec->multipath * (frand(0.08)-0.04);
1867 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1868 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
1872 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
1873 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
1877 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1878 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
1885 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
1888 #include "images/6x10font.xbm"
1891 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
1892 int w, int h, char *fontname)
1899 XWindowAttributes xgwa;
1904 XGetWindowAttributes (dpy, window, &xgwa);
1906 if (fontname && !strcmp (fontname, "6x10")) {
1908 text_pm = XCreatePixmapFromBitmapData (dpy, window,
1909 (char *) font6x10_bits,
1913 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
1915 XFreePixmap(dpy, text_pm);
1917 } else if (fontname) {
1919 font = XLoadQueryFont (dpy, fontname);
1921 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
1925 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
1927 memset(&gcv, 0, sizeof(gcv));
1931 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
1933 XSetForeground(dpy, gc, 0);
1934 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
1935 XSetForeground(dpy, gc, 1);
1936 for (i=0; i<256; i++) {
1938 int x=f->char_w*i+1;
1939 int y=f->char_h*8/10;
1940 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
1942 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
1945 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
1946 256*f->char_w, f->char_h, -1, -1);
1949 XFreePixmap(dpy, text_pm);
1951 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
1952 256*f->char_w, f->char_h, 8, 0);
1953 f->text_im->data = (char *)calloc(f->text_im->height,
1954 f->text_im->bytes_per_line);
1962 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
1964 if (x<0 || x>=f->char_w) return 0;
1965 if (y<0 || y>=f->char_h) return 0;
1966 if (c<0 || c>=256) return 0;
1967 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
1971 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
1973 if (x<0 || x>=f->char_w) return;
1974 if (y<0 || y>=f->char_h) return;
1975 if (c<0 || c>=256) return;
1977 XPutPixel(f->text_im, c*f->char_w + x, y, value);
1981 analogtv_font_set_char(analogtv_font *f, int c, char *s)
1985 if (c<0 || c>=256) return;
1987 for (y=0; y<f->char_h; y++) {
1988 for (x=0; x<f->char_w; x++) {
1990 value=(*s==' ') ? 0 : 1;
1991 analogtv_font_set_pixel(f, c, x, y, value);
1998 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2001 for (i=0; i<4; i++) {
2002 double w=90.0*i + phase;
2003 double val=luma + chroma * (cos(3.1415926/180.0*w));
2004 if (val<0.0) val=0.0;
2005 if (val>127.0) val=127.0;
2011 analogtv_draw_solid(analogtv_input *input,
2012 int left, int right, int top, int bot,
2017 if (right-left<4) right=left+4;
2018 if (bot-top<1) bot=top+1;
2020 for (y=top; y<bot; y++) {
2021 for (x=left; x<right; x++) {
2022 input->signal[y][x] = ntsc[x&3];
2029 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2030 double left, double right, double top, double bot,
2031 double luma, double chroma, double phase)
2035 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2036 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2037 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2038 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2040 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2041 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2046 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2047 int c, int x, int y, int ntsc[4])
2049 int yc,xc,ys,xs,pix;
2051 for (yc=0; yc<f->char_h; yc++) {
2052 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2053 if (ys<0 || ys>=ANALOGTV_V) continue;
2055 for (xc=0; xc<f->char_w; xc++) {
2056 pix=analogtv_font_pixel(f, c, xc, yc);
2058 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2059 if (xs<0 || xs>=ANALOGTV_H) continue;
2061 input->signal[ys][xs] = ntsc[xs&3];
2070 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2071 char *s, int x, int y, int ntsc[4])
2074 analogtv_draw_char(input, f, *s, x, y, ntsc);
2081 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2082 char *s, int x, int y, int ntsc[4])
2084 int width=strlen(s) * f->char_w * 4;
2087 analogtv_draw_string(input, f, s, x, y, ntsc);
2091 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2092 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2093 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2094 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2095 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2096 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2097 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2098 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2101 Much of this function was adapted from logo.c
2104 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2105 const char * const *xpm, int left, int top)
2110 int ncolors, nbytes;
2113 int r; int g; int b;
2117 if (4 != sscanf ((const char *) *xpm,
2119 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2121 if (ncolors < 1 || ncolors > 255)
2123 if (nbytes != 1) /* a serious limitation */
2127 for (i = 0; i < ncolors; i++) {
2128 const char *line = *xpm;
2129 int colori = ((unsigned char)*line++)&0xff;
2134 while (*line == ' ' || *line == '\t')
2137 if (which != 'c' && which != 'm')
2139 while (*line == ' ' || *line == '\t')
2141 if (!strncasecmp(line, "None", 4))
2150 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2152 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2154 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2169 for (y=0; y<xpmh; y++) {
2170 const char *line = *xpm++;
2172 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2174 for (x=0; x<xpmw; x++) {
2175 int cbyte=((unsigned char)line[x])&0xff;
2178 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2180 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2181 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2182 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2189 for (i=0; i<4; i++) {
2190 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2191 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2194 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2195 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2196 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2197 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];