1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
51 Trevor Blackwell <tlb@tlb.org>
56 #else /* !HAVE_COCOA */
57 # include <X11/Xlib.h>
58 # include <X11/Xutil.h>
63 #include "resources.h"
66 #include "grabscreen.h"
71 /* only works on linux + freebsd */
72 #include <machine/cpufunc.h>
74 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
75 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
76 #define DTIME dtimes[n_dtimes++]=rdtsc()
77 #define DTIME_SHOW(DIV) \
79 double _dtime_div=(DIV); \
80 printf("time/%.1f: ",_dtime_div); \
81 for (i=1; i<n_dtimes; i++) \
82 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
89 #define DTIME_START do { } while (0)
90 #define DTIME do { } while (0)
91 #define DTIME_SHOW(DIV) do { } while (0)
96 #define FASTRND (fastrnd = fastrnd*1103515245+12345)
98 static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
101 static double puramp(analogtv *it, double tc, double start, double over)
103 double pt=it->powerup-start;
105 if (pt<0.0) return 0.0;
106 if (pt>900.0 || pt/tc>8.0) return 1.0;
108 ret=(1.0-exp(-pt/tc))*over;
109 if (ret>1.0) return 1.0;
114 There are actual standards for TV signals: NTSC and RS-170A describe the
115 system used in the US and Japan. Europe has slightly different systems, but
116 not different enough to make substantially different screensaver displays.
117 Sadly, the standards bodies don't do anything so useful as publish the spec on
118 the web. Best bets are:
120 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
121 http://www.ntsc-tv.com/ntsc-index-02.htm
123 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
124 values it calculates across each scan line. In PseudoColor mode, it consider
125 each possible pattern of 5 preceding bit values in each possible position
126 modulo 4 and allocates a color for each. A few things, like the brightening on
127 the right side as the horizontal trace slows down, aren't done in PseudoColor.
129 I'd like to add a bit of visible retrace, but it conflicts with being able to
130 bitcopy the image when fast scrolling. After another couple of CPU
131 generations, we could probably regenerate the whole image from scratch every
132 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
136 /* localbyteorder is MSBFirst or LSBFirst */
137 static int localbyteorder;
138 static const double float_low8_ofs=8388608.0;
139 static int float_extraction_works;
151 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
152 localbyteorder=*(char *)&localbyteorder_loc;
159 float_extraction_works=1;
160 for (i=0; i<256*4; i++) {
161 fe.f=float_low8_ofs+(double)i;
165 printf("Float extraction failed for %d => %d\n",i,ans);
167 float_extraction_works=0;
176 analogtv_set_defaults(analogtv *it, char *prefix)
180 sprintf(buf,"%sTVTint",prefix);
181 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
182 sprintf(buf,"%sTVColor",prefix);
183 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
184 sprintf(buf,"%sTVBrightness",prefix);
185 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
186 sprintf(buf,"%sTVContrast",prefix);
187 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
188 it->height_control = 1.0;
189 it->width_control = 1.0;
190 it->squish_control = 0.0;
195 it->hashnoise_enable=1;
197 it->horiz_desync=frand(10.0)-5.0;
198 it->squeezebottom=frand(5.0)-1.0;
201 printf("analogtv: prefix=%s\n",prefix);
202 printf(" use: shm=%d cmap=%d color=%d\n",
203 it->use_shm,it->use_cmap,it->use_color);
204 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
205 it->tint_control, it->color_control, it->brightness_control,
206 it->contrast_control);
207 printf(" freq_error %g: %g %d\n",
208 it->freq_error, it->freq_error_inc, it->flutter_tint);
209 printf(" desync: %g %d\n",
210 it->horiz_desync, it->flutter_horiz_desync);
211 printf(" hashnoise rpm: %g\n",
213 printf(" vis: %d %d %d\n",
214 it->visclass, it->visbits, it->visdepth);
215 printf(" shift: %d-%d %d-%d %d-%d\n",
216 it->red_invprec,it->red_shift,
217 it->green_invprec,it->green_shift,
218 it->blue_invprec,it->blue_shift);
219 printf(" size: %d %d %d %d xrepl=%d\n",
220 it->usewidth, it->useheight,
221 it->screen_xo, it->screen_yo, it->xrepl);
223 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
224 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
225 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
226 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
227 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
228 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
229 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
230 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
231 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
232 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
233 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
234 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
235 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
241 extern Bool mono_p; /* shoot me */
244 analogtv_free_image(analogtv *it)
248 #ifdef HAVE_XSHM_EXTENSION
249 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
252 XDestroyImage(it->image);
259 analogtv_alloc_image(analogtv *it)
262 #ifdef HAVE_XSHM_EXTENSION
263 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
264 &it->shm_info, it->usewidth, it->useheight);
266 if (!it->image) it->use_shm=0;
269 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
270 it->usewidth, it->useheight, 8, 0);
271 it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
273 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
278 analogtv_configure(analogtv *it)
280 int oldwidth=it->usewidth;
281 int oldheight=it->useheight;
282 int wlim,hlim,height_diff;
284 /* If the window is very small, don't let the image we draw get lower
285 than the actual TV resolution (266x200.)
287 If the aspect ratio of the window is within 15% of a 4:3 ratio,
288 then scale the image to exactly fill the window.
290 Otherwise, center the image either horizontally or vertically,
291 padding on the left+right, or top+bottom, but not both.
293 If it's very close (2.5%) to a multiple of VISLINES, make it exact
294 For example, it maps 1024 => 1000.
296 float percent = 0.15; /* jwz: 20% caused severe top/bottom clipping
297 in Pong on 1680x1050 iMac screen. */
298 float min_ratio = 4.0 / 3.0 * (1 - percent);
299 float max_ratio = 4.0 / 3.0 * (1 + percent);
301 float height_snap=0.025;
303 hlim = it->xgwa.height;
304 wlim = it->xgwa.width;
305 ratio = wlim / (float) hlim;
307 if (wlim < 266 || hlim < 200)
313 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
314 wlim, hlim, it->xgwa.width, it->xgwa.height,
315 min_ratio, ratio, max_ratio);
318 else if (ratio > min_ratio && ratio < max_ratio)
322 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
323 wlim, hlim, min_ratio, ratio, max_ratio);
326 else if (ratio > max_ratio)
328 wlim = hlim*max_ratio;
331 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
332 wlim, hlim, it->xgwa.width, it->xgwa.height,
333 min_ratio, ratio, max_ratio);
336 else /* ratio < min_ratio */
338 hlim = wlim/min_ratio;
341 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
342 wlim, hlim, it->xgwa.width, it->xgwa.height,
343 min_ratio, ratio, max_ratio);
348 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
349 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
355 /* Most times this doesn't change */
356 if (wlim != oldwidth || hlim != oldheight) {
361 it->xrepl=1+it->usewidth/640;
362 if (it->xrepl>2) it->xrepl=2;
363 it->subwidth=it->usewidth/it->xrepl;
365 analogtv_free_image(it);
366 analogtv_alloc_image(it);
369 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
370 it->screen_yo = (it->xgwa.height-it->useheight)/2;
375 analogtv_reconfigure(analogtv *it)
377 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
378 analogtv_configure(it);
382 analogtv_allocate(Display *dpy, Window window)
390 it=(analogtv *)calloc(1,sizeof(analogtv));
399 #ifdef HAVE_XSHM_EXTENSION
405 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
407 it->screen=it->xgwa.screen;
408 it->colormap=it->xgwa.colormap;
409 it->visclass=it->xgwa.visual->class;
410 it->visbits=it->xgwa.visual->bits_per_rgb;
411 it->visdepth=it->xgwa.depth;
412 if (it->visclass == TrueColor || it->visclass == DirectColor) {
413 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
418 it->use_color=!mono_p;
420 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
422 it->use_color=!mono_p;
429 it->red_mask=it->xgwa.visual->red_mask;
430 it->green_mask=it->xgwa.visual->green_mask;
431 it->blue_mask=it->xgwa.visual->blue_mask;
432 it->red_shift=it->red_invprec=-1;
433 it->green_shift=it->green_invprec=-1;
434 it->blue_shift=it->blue_invprec=-1;
436 /* Is there a standard way to do this? Does this handle all cases? */
438 for (shift=0; shift<32; shift++) {
439 for (prec=1; prec<16 && prec<40-shift; prec++) {
440 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
441 if (it->red_shift<0 && mask==it->red_mask)
442 it->red_shift=shift, it->red_invprec=16-prec;
443 if (it->green_shift<0 && mask==it->green_mask)
444 it->green_shift=shift, it->green_invprec=16-prec;
445 if (it->blue_shift<0 && mask==it->blue_mask)
446 it->blue_shift=shift, it->blue_invprec=16-prec;
449 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
450 if (0) fprintf(stderr,"Can't figure out color space\n");
454 for (i=0; i<ANALOGTV_CV_MAX; i++) {
455 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
456 if (intensity>65535) intensity=65535;
457 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
458 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
459 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
464 gcv.background=get_pixel_resource(it->dpy, it->colormap,
465 "background", "Background");
467 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
468 XSetWindowBackground(it->dpy, it->window, gcv.background);
469 XClearWindow(dpy,window);
471 analogtv_configure(it);
481 analogtv_release(analogtv *it)
485 #ifdef HAVE_XSHM_EXTENSION
486 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
489 XDestroyImage(it->image);
493 if (it->gc) XFreeGC(it->dpy, it->gc);
495 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
501 First generate the I and Q reference signals, which we'll multiply
502 the input signal by to accomplish the demodulation. Normally they
503 are shifted 33 degrees from the colorburst. I think this was convenient
504 for inductor-capacitor-vacuum tube implementation.
506 The tint control, FWIW, just adds a phase shift to the chroma signal,
507 and the color control controls the amplitude.
509 In text modes (colormode==0) the system disabled the color burst, and no
510 color was detected by the monitor.
512 freq_error gives a mismatch between the built-in oscillator and the
513 TV's colorbust. Some II Plus machines seemed to occasionally get
514 instability problems -- the crystal oscillator was a single
515 transistor if I remember correctly -- and the frequency would vary
516 enough that the tint would change across the width of the screen.
517 The left side would be in correct tint because it had just gotten
518 resynchronized with the color burst.
520 If we're using a colormap, set it up.
523 analogtv_set_demod(analogtv *it)
525 int y_levels=10,i_levels=5,q_levels=5;
528 In principle, we might be able to figure out how to adjust the
529 color map frame-by-frame to get some nice color bummage. But I'm
530 terrified of changing the color map because we'll get flashing.
532 I can hardly believe we still have to deal with colormaps. They're
533 like having NEAR PTRs: an enormous hassle for the programmer just
534 to save on memory. They should have been deprecated by 1995 or
538 if (it->use_cmap && !it->n_colors) {
541 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
547 for (yli=0; yli<y_levels; yli++) {
548 for (ili=0; ili<i_levels; ili++) {
549 for (qli=0; qli<q_levels; qli++) {
550 double interpy,interpi,interpq;
551 double levelmult=700.0;
555 interpy=100.0 * ((double)yli/y_levels);
556 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
557 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
559 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
560 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
561 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
563 if (r>65535) r=65535;
565 if (g>65535) g=65535;
567 if (b>65535) b=65535;
570 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
571 interpy, interpi, interpq,
579 if (!XAllocColor(it->dpy, it->colormap, &col)) {
580 if (q_levels > y_levels*4/12)
582 else if (i_levels > y_levels*5/12)
591 it->colors[it->n_colors++]=col.pixel;
596 it->cmap_y_levels=y_levels;
597 it->cmap_i_levels=i_levels;
598 it->cmap_q_levels=q_levels;
607 analogtv_line_signature(analogtv_input *input, int lineno)
610 char *origsignal=&input->signal[(lineno+input->vsync)
611 %ANALOGTV_V][input->line_hsync[lineno]];
615 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
617 hash = hash + (hash<<17) + c;
620 hash += input->line_hsync[lineno];
623 hash += input->hashnoise_times[lineno];
632 /* Here we model the analog circuitry of an NTSC television.
633 Basically, it splits the signal into 3 signals: Y, I and Q. Y
634 corresponds to luminance, and you get it by low-pass filtering the
635 input signal to below 3.57 MHz.
637 I and Q are the in-phase and quadrature components of the 3.57 MHz
638 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
639 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
640 resolution in some colors than others, the I component gets
641 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
642 is approximately orange-blue, and Q is roughly purple-green. See
643 http://www.ntsc-tv.com for details.
645 We actually do an awful lot to the signal here. I suspect it would
646 make sense to wrap them all up together by calculating impulse
647 response and doing FFT convolutions.
652 analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
658 int phasecorr=(signal-it->rx_signal)&3;
659 struct analogtv_yiq_s *yiq;
661 double agclevel=it->agclevel;
662 double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
663 double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
668 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
669 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
670 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
671 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
673 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
676 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
677 double tint_q = sin((103 + it->color_control)*3.1415926/180);
679 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
680 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
681 multiq2[2]=-multiq2[0];
682 multiq2[3]=-multiq2[1];
688 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
689 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
690 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
691 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
692 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
693 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
694 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
698 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
699 for (i=0; i<5; i++) dp[i]=0.0;
702 assert(end < ANALOGTV_PIC_LEN+10);
704 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
705 for (i=0; i<24; i++) dp[i]=0.0;
706 for (i=start, yiq=it->yiq+start, sp=signal+start;
708 i++, dp--, yiq++, sp++) {
710 /* Now filter them. These are infinite impulse response filters
711 calculated by the script at
712 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
713 fixed-point integer DSP, son. No place for wimps. We do it in
714 integer because you can count on integer being faster on most
715 CPUs. We care about speed because we need to recalculate every
716 time we blink text, and when we spew random bytes into screen
717 memory. This is roughly 16.16 fixed point arithmetic, but we
718 scale some filter values up by a few bits to avoid some nasty
721 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
722 with an extra zero at 3.5 MHz, from
723 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
726 dp[0] = sp[0] * 0.0469904257251935 * agclevel;
727 dp[8] = (+1.0*(dp[6]+dp[0])
733 yiq->y = dp[8] + brightadd;
737 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
738 for (i=0; i<27; i++) dp[i]=0.0;
740 for (i=start, yiq=it->yiq+start, sp=signal+start;
742 i++, dp--, yiq++, sp++) {
745 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
746 1.5 MHz with an extra zero at 3.5 MHz, from
747 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
751 dp[0] = sig*multiq2[i&3] * 0.0833333333333;
752 yiq->i=dp[8] = (dp[5] + dp[0]
755 -0.3333333333 * dp[10]);
757 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
758 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
759 +3.0*(dp[16+4] + dp[16+1])
760 +4.0*(dp[16+3] + dp[16+2])
761 -0.3333333333 * dp[24+2]);
764 for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
765 yiq->i = yiq->q = 0.0;
771 analogtv_setup_teletext(analogtv_input *input)
774 int teletext=ANALOGTV_BLACK_LEVEL;
776 /* Teletext goes in line 21. But I suspect there are other things
777 in the vertical retrace interval */
779 for (y=19; y<22; y++) {
780 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
782 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
784 input->signal[y][x]=teletext;
790 analogtv_setup_frame(analogtv *it)
796 if (it->flutter_horiz_desync) {
797 /* Horizontal sync during vertical sync instability. */
798 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
799 ((int)(random()&0xff)-0x80) *
800 ((int)(random()&0xff)-0x80) *
801 ((int)(random()&0xff)-0x80) * 0.000001;
804 for (i=0; i<ANALOGTV_V; i++) {
805 it->hashnoise_times[i]=0;
808 if (it->hashnoise_enable && !it->hashnoise_on) {
809 if (random()%10000==0) {
811 it->shrinkpulse=random()%ANALOGTV_V;
814 if (random()%1000==0) {
817 if (it->hashnoise_on) {
818 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
819 ((int)(random()%2000)-1000)*0.1;
821 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
822 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
824 if (it->hashnoise_rpm > 0.0) {
826 int hnc=it->hashnoise_counter; /* in 24.8 format */
828 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
829 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
830 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
832 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
833 y=(hnc>>8)/ANALOGTV_H;
834 x=(hnc>>8)%ANALOGTV_H;
836 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
837 it->hashnoise_times[y]=x;
839 hnc += hni + (int)(random()%65536)-32768;
841 hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;
844 if (it->rx_signal_level != 0.0)
845 it->agclevel = 1.0/it->rx_signal_level;
850 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
851 printf(" %0.3f",it->ghostfir[i]);
853 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
858 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
863 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
865 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
866 vsync=lineno>=3 && lineno<7;
868 sig=input->signal[lineno];
870 i=ANALOGTV_SYNC_START;
872 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
873 while (i<ANALOGTV_H) sig[i++]=synclevel;
875 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
876 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
877 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
879 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
882 /* 9 cycles of colorburst */
883 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
884 sig[i+1] += ANALOGTV_CB_LEVEL;
885 sig[i+3] -= ANALOGTV_CB_LEVEL;
892 analogtv_sync(analogtv *it)
894 int cur_hsync=it->cur_hsync;
895 int cur_vsync=it->cur_vsync;
900 double cbfc=1.0/128.0;
902 sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;
903 for (i=-32; i<32; i++) {
904 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
905 sp = it->rx_signal + lineno*ANALOGTV_H;
907 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
910 filt *= it->agclevel;
912 osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
914 if (osc >= 1.05+0.0002 * filt) break;
916 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
918 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
920 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
922 sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
923 )*ANALOGTV_H + cur_hsync;
924 for (i=-8; i<8; i++) {
925 osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
926 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
928 if (osc >= 1.005 + 0.0001*filt) break;
930 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
933 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
934 ANALOGTV_H) % ANALOGTV_H;
936 /* Now look for the colorburst, which is a few cycles after the H
937 sync pulse, and store its phase.
938 The colorburst is 9 cycles long, and we look at the middle 5
943 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
944 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
945 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
946 sp[i]*it->agclevel*cbfc;
954 for (i=0; i<4; i++) {
955 tot += it->cb_phase[i]*it->cb_phase[i];
957 cbgain = 32.0/sqrt(tot);
959 for (i=0; i<4; i++) {
960 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
965 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
967 it->cb_phase[0], it->cb_phase[1],
968 it->cb_phase[2], it->cb_phase[3]);
971 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
974 it->cur_hsync = cur_hsync;
975 it->cur_vsync = cur_vsync;
979 analogtv_levelmult(analogtv *it, int level)
981 static const double levelfac[3]={-7.5, 5.5, 24.5};
982 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
986 analogtv_level(analogtv *it, int y, int ytop, int ybot)
990 if (y==ytop || y==ybot-1) level=0;
991 else if (y==ytop+1 || y==ybot-2) level=1;
994 else if (ybot-ytop>=5) {
995 if (y==ytop || y==ybot-1) level=0;
998 else if (ybot-ytop>=3) {
999 if (y==ytop) level=0;
1009 The point of this stuff is to ensure that when useheight is not a
1010 multiple of VISLINES so that TV scan lines map to different numbers
1011 of vertical screen pixels, the total brightness of each scan line
1013 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1014 it interpolates extra black lines.
1018 analogtv_setup_levels(analogtv *it, double avgheight)
1021 static const double levelfac[3]={-7.5, 5.5, 24.5};
1023 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1025 for (i=0; i<height; i++) {
1026 it->leveltable[height][i].index = 2;
1030 it->leveltable[height][0].index=0;
1033 it->leveltable[height][height-1].index=0;
1036 it->leveltable[height][1].index=1;
1037 it->leveltable[height][height-2].index=1;
1040 for (i=0; i<height; i++) {
1041 it->leveltable[height][i].value =
1042 (40.0 + levelfac[it->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=it->leveltable[ybot-ytop][y-ytop].index;
1061 double levelmult=it->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 if (! raw_rgb_start) return;
1194 analogtv_setup_frame(it);
1195 analogtv_set_demod(it);
1197 /* rx_signal has an extra 2 lines at the end, where we copy the
1198 first 2 lines so we can index into it while only worrying about
1199 wraparound on a per-line level */
1200 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1202 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1207 /* if (it->hashnoise_on) baseload=0.5; */
1211 it->crtload[ANALOGTV_TOP-1]=baseload;
1212 puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1213 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1215 analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1217 overall_top=it->useheight;
1220 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1221 int slineno=lineno-ANALOGTV_TOP;
1222 int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
1223 it->useheight/2)*puheight) + it->useheight/2;
1224 int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
1225 it->useheight/2)*puheight) + it->useheight/2;
1227 int linesig=analogtv_line_signature(input,lineno)
1228 + it->hashnoise_times[lineno];
1230 double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
1231 ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
1232 it->line_hsync[lineno]);
1234 if (ytop==ybot) continue;
1235 if (ybot<0 || ytop>it->useheight) continue;
1237 if (ybot>it->useheight) ybot=it->useheight;
1239 if (ybot > ytop+ANALOGTV_MAX_LINEHEIGHT) ybot=ytop+ANALOGTV_MAX_LINEHEIGHT;
1241 if (ytop < overall_top) overall_top=ytop;
1242 if (ybot > overall_bot) overall_bot=ybot;
1244 if (lineno==it->shrinkpulse) {
1251 if (it->hashnoise_rpm>0.0 &&
1254 (slineno<20 && it->flutter_horiz_desync) ||
1255 it->gaussiannoise_level>30 ||
1256 ((it->gaussiannoise_level>2.0 ||
1257 it->multipath) && random()%4) ||
1258 linesig != it->onscreen_signature[lineno])) {
1261 it->onscreen_signature[lineno] = linesig;
1266 Interpolate the 600-dotclock line into however many horizontal
1267 screen pixels we're using, and convert to RGB.
1269 We add some 'bloom', variations in the horizontal scan width with
1270 the amount of brightness, extremely common on period TV sets. They
1271 had a single oscillator which generated both the horizontal scan and
1272 (during the horizontal retrace interval) the high voltage for the
1273 electron beam. More brightness meant more load on the oscillator,
1274 which caused an decrease in horizontal deflection. Look for
1277 Also, the A2 did a bad job of generating horizontal sync pulses
1278 during the vertical blanking interval. This, and the fact that the
1279 horizontal frequency was a bit off meant that TVs usually went a bit
1280 out of sync during the vertical retrace, and the top of the screen
1281 would be bent a bit to the left or right. Look for (shiftthisrow).
1283 We also add a teeny bit of left overscan, just enough to be
1284 annoying, but you can still read the left column of text.
1286 We also simulate compression & brightening on the right side of the
1287 screen. Most TVs do this, but you don't notice because they overscan
1288 so it's off the right edge of the CRT. But the A2 video system used
1289 so much of the horizontal scan line that you had to crank the
1290 horizontal width down in order to not lose the right few characters,
1291 and you'd see the compression on the right edge. Associated with
1292 compression is brightening; since the electron beam was scanning
1293 slower, the same drive signal hit the phosphor harder. Look for
1294 (squishright_i) and (squishdiv).
1301 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
1302 totsignal += signal[i];
1304 totsignal *= it->agclevel;
1305 ncl = 0.95 * it->crtload[lineno-1] +
1307 (totsignal-30000)/100000.0 +
1308 (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
1310 diff=ncl - it->crtload[lineno];
1311 bigloadchange = (diff>0.01 || diff<-0.01);
1312 it->crtload[lineno]=ncl;
1316 double bloomthisrow,shiftthisrow;
1317 double viswidth,middle;
1321 bloomthisrow = -10.0 * it->crtload[lineno];
1322 if (bloomthisrow<-10.0) bloomthisrow=-10.0;
1323 if (bloomthisrow>2.0) bloomthisrow=2.0;
1325 shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
1326 (0.7+cos(slineno*0.6)));
1331 viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
1332 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1334 scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
1336 scw=it->subwidth*scanwidth;
1337 if (scw>it->subwidth) scw=it->usewidth;
1338 scl=it->subwidth/2 - scw/2;
1339 scr=it->subwidth/2 + scw/2;
1341 pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
1342 scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
1343 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1344 squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
1345 - it->squish_control)) *65536.0);
1346 squishdiv=it->subwidth/15;
1348 rgb_start=raw_rgb_start+scl*3;
1349 rgb_end=raw_rgb_start+scr*3;
1351 assert(scanstart_i>=0);
1354 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1356 scanstart_i/65536.0,
1357 squishright_i/65536.0,
1364 for (y=ytop; y<ybot; y++) {
1365 int level=analogtv_level(it, y, ytop, ybot);
1366 double levelmult=analogtv_levelmult(it, level);
1367 double levelmult_y = levelmult * it->contrast_control
1368 * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
1369 double levelmult_iq = levelmult * 0.090;
1371 struct analogtv_yiq_s *yiq=it->yiq;
1372 analogtv_ntsc_to_yiq(it, lineno, signal,
1373 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1378 while (i<0 && x<it->usewidth) {
1379 XPutPixel(it->image, x, y, it->colors[0]);
1384 while (i<scanend_i && x<it->usewidth) {
1385 double pixfrac=(i&0xffff)/65536.0;
1386 double invpixfrac=(1.0-pixfrac);
1388 int yli,ili,qli,cmi;
1390 double interpy=(yiq[pati].y*invpixfrac
1391 + yiq[pati+1].y*pixfrac) * levelmult_y;
1392 double interpi=(yiq[pati].i*invpixfrac
1393 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1394 double interpq=(yiq[pati].q*invpixfrac
1395 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1397 yli = (int)(interpy * it->cmap_y_levels);
1398 ili = (int)((interpi+0.5) * it->cmap_i_levels);
1399 qli = (int)((interpq+0.5) * it->cmap_q_levels);
1401 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1403 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1405 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1407 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1410 if ((random()%65536)==0) {
1411 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1412 interpy, interpi, interpq,
1418 for (j=0; j<it->xrepl; j++) {
1419 XPutPixel(it->image, x, y,
1423 if (i >= squishright_i) {
1424 pixmultinc += pixmultinc/squishdiv;
1428 while (x<it->usewidth) {
1429 XPutPixel(it->image, x, y, it->colors[0]);
1435 struct analogtv_yiq_s *yiq=it->yiq;
1436 analogtv_ntsc_to_yiq(it, lineno, signal,
1437 (scanstart_i>>16)-10, (scanend_i>>16)+10);
1439 pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
1440 / (0.5+0.5*puheight) * 1024.0/100.0;
1442 i=scanstart_i; rrp=rgb_start;
1443 while (i<0 && rrp!=rgb_end) {
1444 rrp[0]=rrp[1]=rrp[2]=0;
1448 while (i<scanend_i && rrp!=rgb_end) {
1449 double pixfrac=(i&0xffff)/65536.0;
1450 double invpixfrac=1.0-pixfrac;
1454 double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1455 double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1456 double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1459 According to the NTSC spec, Y,I,Q are generated as:
1461 y=0.30 r + 0.59 g + 0.11 b
1462 i=0.60 r - 0.28 g - 0.32 b
1463 q=0.21 r - 0.52 g + 0.31 b
1465 So if you invert the implied 3x3 matrix you get what standard
1466 televisions implement with a bunch of resistors (or directly in the
1469 r = y + 0.948 i + 0.624 q
1470 g = y - 0.276 i - 0.639 q
1471 b = y - 1.105 i + 1.729 q
1474 r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
1475 g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
1476 b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
1484 if (i>=squishright_i) {
1485 pixmultinc += pixmultinc/squishdiv;
1486 pixbright += pixbright/squishdiv/2;
1491 while (rrp != rgb_end) {
1492 rrp[0]=rrp[1]=rrp[2]=0.0;
1496 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1500 free(raw_rgb_start);
1503 /* poor attempt at visible retrace */
1504 for (i=0; i<15; i++) {
1505 int ytop=(int)((i*it->useheight/15 -
1506 it->useheight/2)*puheight) + it->useheight/2;
1507 int ybot=(int)(((i+1)*it->useheight/15 -
1508 it->useheight/2)*puheight) + it->useheight/2;
1509 int div=it->usewidth*3/2;
1511 for (x=0; x<it->usewidth; x++) {
1512 y = ytop + (ybot-ytop)*x / div;
1513 if (y<0 || y>=it->useheight) continue;
1514 XPutPixel(it->image, x, y, 0xffffff);
1519 if (it->need_clear) {
1520 XClearWindow(it->dpy, it->window);
1524 if (overall_top>0) {
1525 XClearArea(it->dpy, it->window,
1526 it->screen_xo, it->screen_yo,
1527 it->usewidth, overall_top, 0);
1529 if (it->useheight > overall_bot) {
1530 XClearArea(it->dpy, it->window,
1531 it->screen_xo, it->screen_yo+overall_bot,
1532 it->usewidth, it->useheight-overall_bot, 0);
1535 if (overall_bot > overall_top) {
1537 #ifdef HAVE_XSHM_EXTENSION
1538 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1540 it->screen_xo, it->screen_yo+overall_top,
1541 it->usewidth, overall_bot - overall_top,
1545 XPutImage(it->dpy, it->window, it->gc, it->image,
1547 it->screen_xo, it->screen_yo+overall_top,
1548 it->usewidth, overall_bot - overall_top);
1557 gettimeofday(&tv,NULL);
1559 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1560 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1561 sprintf(buf, "FPS=%0.1f",fps);
1562 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1565 it->last_display_time=tv;
1571 analogtv_input_allocate()
1573 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1579 This takes a screen image and encodes it as a video camera would,
1580 including all the bandlimiting and YIQ modulation.
1581 This isn't especially tuned for speed.
1585 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1592 XColor col1[ANALOGTV_PIC_LEN];
1593 XColor col2[ANALOGTV_PIC_LEN];
1594 int multiq[ANALOGTV_PIC_LEN+4];
1595 int y_overscan=5; /* overscan this much top and bottom */
1596 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
1598 img_w=pic_im->width;
1599 img_h=pic_im->height;
1601 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
1602 double phase=90.0-90.0*i;
1604 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
1607 for (y=0; y<y_scanlength; y++) {
1608 int picy1=(y*img_h)/y_scanlength;
1609 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
1611 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1612 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
1613 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
1614 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
1616 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
1617 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
1619 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
1620 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
1622 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
1624 int filty,filti,filtq;
1627 y=0.30 r + 0.59 g + 0.11 b
1628 i=0.60 r - 0.28 g - 0.32 b
1629 q=0.21 r - 0.52 g + 0.31 b
1630 The coefficients below are in .4 format */
1632 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
1633 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
1634 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
1635 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
1636 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
1637 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
1639 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
1640 with an extra zero at 3.5 MHz, from
1641 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
1643 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
1644 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
1645 fyx[6] = (rawy * 1897) >> 16;
1646 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
1647 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
1648 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
1649 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
1652 /* Filter I at 1.5 MHz. 3 pole Butterworth from
1653 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
1655 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
1656 fix[3] = (rawi * 1413) >> 16;
1657 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
1658 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
1659 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
1662 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
1663 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
1665 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
1666 fqx[3] = (rawq * 75) >> 16;
1667 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
1668 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
1669 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
1673 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
1674 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
1675 if (composite>125) composite=125;
1676 if (composite<0) composite=0;
1677 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
1685 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
1688 int change=random()%ANALOGTV_V;
1689 unsigned int fastrnd=random();
1690 double hso=(int)(random()%1000)-500;
1691 int yofs=random()%ANALOGTV_V;
1694 for (y=change; y<ANALOGTV_V; y++) {
1695 int s2y=(y+yofs)%ANALOGTV_V;
1697 int noiselevel=60000 / (y-change+100);
1699 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
1701 for (x=0; x<ANALOGTV_H; x++) {
1703 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
1704 noise=(filt*noiselevel)>>16;
1705 newsig=s2->signal[s2y][x] + noise;
1706 if (newsig>120) newsig=120;
1707 if (newsig<0) newsig=0;
1708 it->signal[y][x]=newsig;
1716 void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
1718 analogtv_input *inp=rec->input;
1719 double *ps=it->rx_signal;
1720 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1722 signed char *ss=&inp->signal[0][0];
1723 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1724 signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
1726 int ec=it->channel_change_cycles;
1727 double level=rec->level;
1728 double hfloss=rec->hfloss;
1729 unsigned int fastrnd=random();
1732 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1734 /* duplicate the first line into the Nth line to ease wraparound computation */
1735 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1736 ANALOGTV_H * sizeof(inp->signal[0][0]));
1738 for (i=0; i<8; i++) dp[i]=0.0;
1743 /* Do a big noisy transition. We can make the transition noise of
1744 high constant strength regardless of signal strength.
1746 There are two separate state machines. here, One is the noise
1747 process and the other is the
1749 We don't bother with the FIR filter here
1754 while (p!=pe && ec>0) {
1756 double sig0=(double)s[0];
1757 double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
1758 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1760 p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
1762 noise_ampl *= 0.99995;
1773 double sig0,sig1,sig2,sig3,sigr;
1780 dp[0]=sig0+sig1+sig2+sig3;
1782 /* Get the video out signal, and add some ghosting, typical of RF
1783 monitor cables. This corresponds to a pretty long cable, but
1787 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1788 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1789 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1791 p[0] += (sig0+sigr + sig2*hfloss) * level;
1792 p[1] += (sig1+sigr + sig3*hfloss) * level;
1793 p[2] += (sig2+sigr + sig0*hfloss) * level;
1794 p[3] += (sig3+sigr + sig1*hfloss) * level;
1798 if (s>=se) s = ss + (s-se);
1801 it->rx_signal_level =
1802 sqrt(it->rx_signal_level * it->rx_signal_level +
1803 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1804 rec->ghostfir[2] + rec->ghostfir[3]))));
1807 it->channel_change_cycles=0;
1813 if (it->hashnoise_times[lineno]) {
1814 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
1816 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
1818 double cur=frand(150.0)-20.0;
1819 int len=random()%15+3;
1820 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
1821 for (i=0; i<len; i++) {
1822 double sig=signal[hnt];
1825 cur += frand(5.0)-5.0;
1826 maxampl = maxampl*0.9;
1836 void analogtv_init_signal(analogtv *it, double noiselevel)
1838 double *ps=it->rx_signal;
1839 double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
1841 unsigned int fastrnd=random();
1842 double nm1=0.0,nm2=0.0;
1843 double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
1847 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1849 fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
1852 it->rx_signal_level = noiselevel;
1856 analogtv_reception_update(analogtv_reception *rec)
1860 if (rec->multipath > 0.0) {
1861 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1862 rec->ghostfir2[i] +=
1863 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
1865 if (random()%20==0) {
1866 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
1867 = rec->multipath * (frand(0.08)-0.04);
1869 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1870 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
1874 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
1875 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
1879 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
1880 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
1887 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
1890 #include "images/6x10font.xbm"
1893 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
1894 int w, int h, char *fontname)
1901 XWindowAttributes xgwa;
1906 XGetWindowAttributes (dpy, window, &xgwa);
1908 if (fontname && !strcmp (fontname, "6x10")) {
1910 text_pm = XCreatePixmapFromBitmapData (dpy, window,
1911 (char *) font6x10_bits,
1915 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
1917 XFreePixmap(dpy, text_pm);
1919 } else if (fontname) {
1921 font = XLoadQueryFont (dpy, fontname);
1923 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
1927 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
1929 memset(&gcv, 0, sizeof(gcv));
1933 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
1935 XSetForeground(dpy, gc, 0);
1936 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
1937 XSetForeground(dpy, gc, 1);
1938 for (i=0; i<256; i++) {
1940 int x=f->char_w*i+1;
1941 int y=f->char_h*8/10;
1942 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
1944 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
1947 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
1948 256*f->char_w, f->char_h, -1, -1);
1951 XFreePixmap(dpy, text_pm);
1953 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
1954 256*f->char_w, f->char_h, 8, 0);
1955 f->text_im->data = (char *)calloc(f->text_im->height,
1956 f->text_im->bytes_per_line);
1964 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
1966 if (x<0 || x>=f->char_w) return 0;
1967 if (y<0 || y>=f->char_h) return 0;
1968 if (c<0 || c>=256) return 0;
1969 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
1973 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
1975 if (x<0 || x>=f->char_w) return;
1976 if (y<0 || y>=f->char_h) return;
1977 if (c<0 || c>=256) return;
1979 XPutPixel(f->text_im, c*f->char_w + x, y, value);
1983 analogtv_font_set_char(analogtv_font *f, int c, char *s)
1987 if (c<0 || c>=256) return;
1989 for (y=0; y<f->char_h; y++) {
1990 for (x=0; x<f->char_w; x++) {
1992 value=(*s==' ') ? 0 : 1;
1993 analogtv_font_set_pixel(f, c, x, y, value);
2000 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2003 for (i=0; i<4; i++) {
2004 double w=90.0*i + phase;
2005 double val=luma + chroma * (cos(3.1415926/180.0*w));
2006 if (val<0.0) val=0.0;
2007 if (val>127.0) val=127.0;
2013 analogtv_draw_solid(analogtv_input *input,
2014 int left, int right, int top, int bot,
2019 if (right-left<4) right=left+4;
2020 if (bot-top<1) bot=top+1;
2022 for (y=top; y<bot; y++) {
2023 for (x=left; x<right; x++) {
2024 input->signal[y][x] = ntsc[x&3];
2031 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2032 double left, double right, double top, double bot,
2033 double luma, double chroma, double phase)
2037 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2038 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2039 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2040 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2042 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2043 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2048 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2049 int c, int x, int y, int ntsc[4])
2051 int yc,xc,ys,xs,pix;
2053 for (yc=0; yc<f->char_h; yc++) {
2054 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2055 if (ys<0 || ys>=ANALOGTV_V) continue;
2057 for (xc=0; xc<f->char_w; xc++) {
2058 pix=analogtv_font_pixel(f, c, xc, yc);
2060 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2061 if (xs<0 || xs>=ANALOGTV_H) continue;
2063 input->signal[ys][xs] = ntsc[xs&3];
2072 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2073 char *s, int x, int y, int ntsc[4])
2076 analogtv_draw_char(input, f, *s, x, y, ntsc);
2083 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2084 char *s, int x, int y, int ntsc[4])
2086 int width=strlen(s) * f->char_w * 4;
2089 analogtv_draw_string(input, f, s, x, y, ntsc);
2093 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2094 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2095 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2096 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 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,
2099 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2103 Much of this function was adapted from logo.c
2106 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2107 const char * const *xpm, int left, int top)
2112 int ncolors, nbytes;
2115 int r; int g; int b;
2119 if (4 != sscanf ((const char *) *xpm,
2121 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2123 if (ncolors < 1 || ncolors > 255)
2125 if (nbytes != 1) /* a serious limitation */
2129 for (i = 0; i < ncolors; i++) {
2130 const char *line = *xpm;
2131 int colori = ((unsigned char)*line++)&0xff;
2136 while (*line == ' ' || *line == '\t')
2139 if (which != 'c' && which != 'm')
2141 while (*line == ' ' || *line == '\t')
2143 if (!strncasecmp(line, "None", 4))
2152 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2154 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2156 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2171 for (y=0; y<xpmh; y++) {
2172 const char *line = *xpm++;
2174 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2176 for (x=0; x<xpmw; x++) {
2177 int cbyte=((unsigned char)line[x])&0xff;
2180 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2182 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2183 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2184 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2191 for (i=0; i<4; i++) {
2192 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2193 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2196 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2197 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2198 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2199 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];