1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
51 Trevor Blackwell <tlb@tlb.org>
55 2014-04-20, Dave Odell <dmo2118@gmail.com>:
56 API change: Folded analogtv_init_signal and *_add_signal into *_draw().
58 Replaced doubles with floats, including constants and transcendental functions.
62 /* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
63 - tint_control variable is used now
64 - removed unusable hashnoise code
68 2016-10-09, Dave Odell <dmo2118@gmail.com>:
69 Updated for new xshm.c.
74 #else /* !HAVE_JWXYZ */
75 # include <X11/Xlib.h>
76 # include <X11/Xutil.h>
83 #include "resources.h"
86 #include "grabscreen.h"
91 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
92 /* only works on linux + freebsd */
93 #include <machine/cpufunc.h>
95 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
96 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
97 #define DTIME dtimes[n_dtimes++]=rdtsc()
98 #define DTIME_SHOW(DIV) \
100 double _dtime_div=(DIV); \
101 printf("time/%.1f: ",_dtime_div); \
102 for (i=1; i<n_dtimes; i++) \
103 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
110 #define DTIME_START do { } while (0)
111 #define DTIME do { } while (0)
112 #define DTIME_SHOW(DIV) do { } while (0)
117 #define FASTRND_A 1103515245
118 #define FASTRND_C 12345
119 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
121 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
122 int start, int end, struct analogtv_yiq_s *it_yiq);
124 static float puramp(const analogtv *it, float tc, float start, float over)
126 float pt=it->powerup-start;
128 if (pt<0.0f) return 0.0f;
129 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
131 ret=(1.0f-expf(-pt/tc))*over;
132 if (ret>1.0f) return 1.0f;
137 There are actual standards for TV signals: NTSC and RS-170A describe the
138 system used in the US and Japan. Europe has slightly different systems, but
139 not different enough to make substantially different screensaver displays.
140 Sadly, the standards bodies don't do anything so useful as publish the spec on
141 the web. Best bets are:
143 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
144 http://www.ntsc-tv.com/ntsc-index-02.htm
146 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
147 values it calculates across each scan line. In PseudoColor mode, it consider
148 each possible pattern of 5 preceding bit values in each possible position
149 modulo 4 and allocates a color for each. A few things, like the brightening on
150 the right side as the horizontal trace slows down, aren't done in PseudoColor.
152 I'd like to add a bit of visible retrace, but it conflicts with being able to
153 bitcopy the image when fast scrolling. After another couple of CPU
154 generations, we could probably regenerate the whole image from scratch every
155 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
159 /* localbyteorder is MSBFirst or LSBFirst */
160 static int localbyteorder;
161 static const double float_low8_ofs=8388608.0;
162 static int float_extraction_works;
174 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
175 localbyteorder=*(char *)&localbyteorder_loc;
182 float_extraction_works=1;
183 for (i=0; i<256*4; i++) {
184 fe.f=float_low8_ofs+(double)i;
188 printf("Float extraction failed for %d => %d\n",i,ans);
190 float_extraction_works=0;
199 analogtv_set_defaults(analogtv *it, char *prefix)
203 sprintf(buf,"%sTVTint",prefix);
204 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
205 sprintf(buf,"%sTVColor",prefix);
206 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
207 sprintf(buf,"%sTVBrightness",prefix);
208 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
209 sprintf(buf,"%sTVContrast",prefix);
210 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
211 it->height_control = 1.0;
212 it->width_control = 1.0;
213 it->squish_control = 0.0;
218 it->hashnoise_enable=1;
220 it->horiz_desync=frand(10.0)-5.0;
221 it->squeezebottom=frand(5.0)-1.0;
224 printf("analogtv: prefix=%s\n",prefix);
225 printf(" use: cmap=%d color=%d\n",
226 it->use_cmap,it->use_color);
227 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
228 it->tint_control, it->color_control, it->brightness_control,
229 it->contrast_control);
230 /* printf(" freq_error %g: %g %d\n",
231 it->freq_error, it->freq_error_inc, it->flutter_tint); */
232 printf(" desync: %g %d\n",
233 it->horiz_desync, it->flutter_horiz_desync);
234 printf(" hashnoise rpm: %g\n",
236 printf(" vis: %d %d\n",
237 it->visclass, it->visdepth);
238 printf(" shift: %d-%d %d-%d %d-%d\n",
239 it->red_invprec,it->red_shift,
240 it->green_invprec,it->green_shift,
241 it->blue_invprec,it->blue_shift);
242 printf(" size: %d %d %d %d xrepl=%d\n",
243 it->usewidth, it->useheight,
244 it->screen_xo, it->screen_yo, it->xrepl);
246 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
247 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
248 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
249 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
250 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
251 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
252 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
253 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
254 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
255 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
256 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
257 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
258 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
264 extern Bool mono_p; /* shoot me */
267 analogtv_free_image(analogtv *it)
270 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
276 analogtv_alloc_image(analogtv *it)
278 /* On failure, it->image is NULL. */
280 unsigned bits_per_pixel = visual_pixmap_depth(it->screen, it->xgwa.visual);
281 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
282 /* Width is in bits. */
283 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
285 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth,
286 ZPixmap, &it->shm_info,
287 width / bits_per_pixel, it->useheight);
290 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
292 /* Not enough memory. Maybe try a smaller window. */
293 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
299 analogtv_configure(analogtv *it)
301 int oldwidth=it->usewidth;
302 int oldheight=it->useheight;
303 int wlim,hlim,height_diff;
305 /* If the window is very small, don't let the image we draw get lower
306 than the actual TV resolution (266x200.)
308 If the aspect ratio of the window is close to a 4:3 or 16:9 ratio --
309 or if it is a completely weird aspect ratio --
310 then scale the image to exactly fill the window.
312 Otherwise, center the image either horizontally or vertically,
313 letterboxing or pillarboxing (but not both).
315 If it's very close (2.5%) to a multiple of VISLINES, make it exact
316 For example, it maps 1024 => 1000.
318 float percent = 0.15;
319 float min_ratio = 4.0 / 3.0 * (1 - percent);
320 float max_ratio = 16.0 / 9.0 * (1 + percent);
321 float crazy_min_ratio = 10;
322 float crazy_max_ratio = 1/crazy_min_ratio;
324 float height_snap=0.025;
326 hlim = it->xgwa.height;
327 wlim = it->xgwa.width;
328 ratio = wlim / (float) hlim;
331 /* Fill the whole iPhone screen, even though that distorts the image. */
336 if (wlim < 266 || hlim < 200)
342 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
343 wlim, hlim, it->xgwa.width, it->xgwa.height,
344 min_ratio, ratio, max_ratio);
347 else if (ratio > min_ratio && ratio < max_ratio)
351 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
352 wlim, hlim, min_ratio, ratio, max_ratio);
355 else if (ratio >= max_ratio)
357 wlim = hlim*max_ratio;
360 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
361 wlim, hlim, it->xgwa.width, it->xgwa.height,
362 min_ratio, ratio, max_ratio);
365 else /* ratio <= min_ratio */
367 hlim = wlim/min_ratio;
370 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
371 wlim, hlim, it->xgwa.width, it->xgwa.height,
372 min_ratio, ratio, max_ratio);
376 if (ratio < crazy_min_ratio || ratio > crazy_max_ratio)
378 if (ratio < crazy_min_ratio)
379 hlim = it->xgwa.height;
381 wlim = it->xgwa.width;
384 "size: aspect: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
385 wlim, hlim, it->xgwa.width, it->xgwa.height,
386 min_ratio, ratio, max_ratio);
391 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
392 if (height_diff != 0 && abs(height_diff) < hlim * height_snap)
398 /* Most times this doesn't change */
399 if (wlim != oldwidth || hlim != oldheight) {
404 it->xrepl=1+it->usewidth/640;
405 if (it->xrepl>2) it->xrepl=2;
406 it->subwidth=it->usewidth/it->xrepl;
408 analogtv_free_image(it);
409 analogtv_alloc_image(it);
412 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
413 it->screen_yo = (it->xgwa.height-it->useheight)/2;
418 analogtv_reconfigure(analogtv *it)
420 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
421 analogtv_configure(it);
424 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
425 #define ANALOGTV_SUBTOTAL_LEN 32
427 typedef struct analogtv_thread_s
431 size_t signal_start, signal_end;
434 #define SIGNAL_OFFSET(thread_id) \
435 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
437 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
440 analogtv_thread *thread = (analogtv_thread *)thread_raw;
443 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
444 thread->thread_id = thread_id;
446 align = thread_memory_alignment(thread->it->dpy) /
447 sizeof(thread->it->signal_subtotals[0]);
450 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
452 thread->signal_start = SIGNAL_OFFSET(thread_id);
453 thread->signal_end = thread_id + 1 == threads->count ?
454 ANALOGTV_SIGNAL_LEN :
455 SIGNAL_OFFSET(thread_id + 1);
460 static void analogtv_thread_destroy(void *thread_raw)
465 analogtv_allocate(Display *dpy, Window window)
467 static const struct threadpool_class cls = {
468 sizeof(analogtv_thread),
469 analogtv_thread_create,
470 analogtv_thread_destroy
476 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
480 it=(analogtv *)calloc(1,sizeof(analogtv));
484 it->signal_subtotals=NULL;
489 if (thread_malloc((void **)&it->rx_signal, dpy,
490 sizeof(it->rx_signal[0]) * rx_signal_len))
493 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
494 if (thread_malloc((void **)&it->signal_subtotals, dpy,
495 sizeof(it->signal_subtotals[0]) *
496 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
499 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
502 assert(it->threads.count);
508 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
510 it->screen=it->xgwa.screen;
511 it->colormap=it->xgwa.colormap;
512 it->visclass=visual_class(it->xgwa.screen, it->xgwa.visual);
513 it->visdepth=it->xgwa.depth;
514 if (it->visclass == TrueColor || it->visclass == DirectColor) {
515 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
520 it->use_color=!mono_p;
522 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
524 it->use_color=!mono_p;
531 visual_rgb_masks (it->xgwa.screen, it->xgwa.visual,
532 &it->red_mask, &it->green_mask, &it->blue_mask);
533 it->red_shift=it->red_invprec=-1;
534 it->green_shift=it->green_invprec=-1;
535 it->blue_shift=it->blue_invprec=-1;
537 /* Is there a standard way to do this? Does this handle all cases? */
539 for (shift=0; shift<32; shift++) {
540 for (prec=1; prec<16 && prec<40-shift; prec++) {
541 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
542 if (it->red_shift<0 && mask==it->red_mask)
543 it->red_shift=shift, it->red_invprec=16-prec;
544 if (it->green_shift<0 && mask==it->green_mask)
545 it->green_shift=shift, it->green_invprec=16-prec;
546 if (it->blue_shift<0 && mask==it->blue_mask)
547 it->blue_shift=shift, it->blue_invprec=16-prec;
550 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
551 if (0) fprintf(stderr,"Can't figure out color space\n");
555 for (i=0; i<ANALOGTV_CV_MAX; i++) {
556 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
557 if (intensity>65535) intensity=65535;
558 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
559 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
560 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
565 gcv.background=get_pixel_resource(it->dpy, it->colormap,
566 "background", "Background");
568 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
569 XSetWindowBackground(it->dpy, it->window, gcv.background);
570 XClearWindow(dpy,window);
572 analogtv_configure(it);
578 if(it->threads.count)
579 threadpool_destroy(&it->threads);
580 thread_free(it->signal_subtotals);
581 thread_free(it->rx_signal);
588 analogtv_release(analogtv *it)
591 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
594 if (it->gc) XFreeGC(it->dpy, it->gc);
596 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
598 threadpool_destroy(&it->threads);
599 thread_free(it->rx_signal);
600 thread_free(it->signal_subtotals);
606 First generate the I and Q reference signals, which we'll multiply
607 the input signal by to accomplish the demodulation. Normally they
608 are shifted 33 degrees from the colorburst. I think this was convenient
609 for inductor-capacitor-vacuum tube implementation.
611 The tint control, FWIW, just adds a phase shift to the chroma signal,
612 and the color control controls the amplitude.
614 In text modes (colormode==0) the system disabled the color burst, and no
615 color was detected by the monitor.
617 freq_error gives a mismatch between the built-in oscillator and the
618 TV's colorbust. Some II Plus machines seemed to occasionally get
619 instability problems -- the crystal oscillator was a single
620 transistor if I remember correctly -- and the frequency would vary
621 enough that the tint would change across the width of the screen.
622 The left side would be in correct tint because it had just gotten
623 resynchronized with the color burst.
625 If we're using a colormap, set it up.
628 analogtv_set_demod(analogtv *it)
630 int y_levels=10,i_levels=5,q_levels=5;
633 In principle, we might be able to figure out how to adjust the
634 color map frame-by-frame to get some nice color bummage. But I'm
635 terrified of changing the color map because we'll get flashing.
637 I can hardly believe we still have to deal with colormaps. They're
638 like having NEAR PTRs: an enormous hassle for the programmer just
639 to save on memory. They should have been deprecated by 1995 or
643 if (it->use_cmap && !it->n_colors) {
646 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
652 for (yli=0; yli<y_levels; yli++) {
653 for (ili=0; ili<i_levels; ili++) {
654 for (qli=0; qli<q_levels; qli++) {
655 double interpy,interpi,interpq;
656 double levelmult=700.0;
660 interpy=100.0 * ((double)yli/y_levels);
661 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
662 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
664 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
665 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
666 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
668 if (r>65535) r=65535;
670 if (g>65535) g=65535;
672 if (b>65535) b=65535;
675 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
676 interpy, interpi, interpq,
684 if (!XAllocColor(it->dpy, it->colormap, &col)) {
685 if (q_levels > y_levels*4/12)
687 else if (i_levels > y_levels*5/12)
696 it->colors[it->n_colors++]=col.pixel;
701 it->cmap_y_levels=y_levels;
702 it->cmap_i_levels=i_levels;
703 it->cmap_q_levels=q_levels;
712 analogtv_line_signature(analogtv_input *input, int lineno)
715 char *origsignal=&input->signal[(lineno+input->vsync)
716 %ANALOGTV_V][input->line_hsync[lineno]];
720 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
722 hash = hash + (hash<<17) + c;
725 hash += input->line_hsync[lineno];
728 hash += input->hashnoise_times[lineno];
737 /* Here we model the analog circuitry of an NTSC television.
738 Basically, it splits the signal into 3 signals: Y, I and Q. Y
739 corresponds to luminance, and you get it by low-pass filtering the
740 input signal to below 3.57 MHz.
742 I and Q are the in-phase and quadrature components of the 3.57 MHz
743 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
744 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
745 resolution in some colors than others, the I component gets
746 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
747 is approximately orange-blue, and Q is roughly purple-green. See
748 http://www.ntsc-tv.com for details.
750 We actually do an awful lot to the signal here. I suspect it would
751 make sense to wrap them all up together by calculating impulse
752 response and doing FFT convolutions.
757 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
758 int start, int end, struct analogtv_yiq_s *it_yiq)
763 int phasecorr=(signal-it->rx_signal)&3;
764 struct analogtv_yiq_s *yiq;
766 float agclevel=it->agclevel;
767 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
768 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
773 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
774 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
775 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
776 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
778 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
781 multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
782 multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
783 multiq2[2]=-multiq2[0];
784 multiq2[3]=-multiq2[1];
790 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
791 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
792 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
793 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
794 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
795 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
796 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
800 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
801 for (i=0; i<5; i++) dp[i]=0.0f;
804 assert(end < ANALOGTV_PIC_LEN+10);
806 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
807 for (i=0; i<24; i++) dp[i]=0.0;
808 for (i=start, yiq=it_yiq+start, sp=signal+start;
810 i++, dp--, yiq++, sp++) {
812 /* Now filter them. These are infinite impulse response filters
813 calculated by the script at
814 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
815 fixed-point integer DSP, son. No place for wimps. We do it in
816 integer because you can count on integer being faster on most
817 CPUs. We care about speed because we need to recalculate every
818 time we blink text, and when we spew random bytes into screen
819 memory. This is roughly 16.16 fixed point arithmetic, but we
820 scale some filter values up by a few bits to avoid some nasty
823 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
824 with an extra zero at 3.5 MHz, from
825 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
828 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
829 dp[8] = (+1.0f*(dp[6]+dp[0])
835 yiq->y = dp[8] + brightadd;
839 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
840 for (i=0; i<27; i++) dp[i]=0.0;
842 for (i=start, yiq=it_yiq+start, sp=signal+start;
844 i++, dp--, yiq++, sp++) {
847 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
848 1.5 MHz with an extra zero at 3.5 MHz, from
849 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
853 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
854 yiq->i=dp[8] = (dp[5] + dp[0]
855 +3.0f*(dp[4] + dp[1])
856 +4.0f*(dp[3] + dp[2])
857 -0.3333333333f * dp[10]);
859 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
860 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
861 +3.0f*(dp[16+4] + dp[16+1])
862 +4.0f*(dp[16+3] + dp[16+2])
863 -0.3333333333f * dp[24+2]);
866 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
867 yiq->i = yiq->q = 0.0f;
873 analogtv_setup_teletext(analogtv_input *input)
876 int teletext=ANALOGTV_BLACK_LEVEL;
878 /* Teletext goes in line 21. But I suspect there are other things
879 in the vertical retrace interval */
881 for (y=19; y<22; y++) {
882 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
884 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
886 input->signal[y][x]=teletext;
892 analogtv_setup_frame(analogtv *it)
898 if (it->flutter_horiz_desync) {
899 /* Horizontal sync during vertical sync instability. */
900 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
901 ((int)(random()&0xff)-0x80) *
902 ((int)(random()&0xff)-0x80) *
903 ((int)(random()&0xff)-0x80) * 0.000001;
907 for (i=0; i<ANALOGTV_V; i++) {
908 it->hashnoise_times[i]=0;
912 /* let's leave it to process shrinkpulse */
913 if (it->hashnoise_enable && !it->hashnoise_on) {
914 if (random()%10000==0) {
916 it->shrinkpulse=random()%ANALOGTV_V;
919 if (random()%1000==0) {
923 #if 0 /* never used */
924 if (it->hashnoise_on) {
925 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
926 ((int)(random()%2000)-1000)*0.1;
928 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
929 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
931 if (it->hashnoise_rpm > 0.0) {
934 int hnc=it->hashnoise_counter; /* in 24.8 format */
936 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
937 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
938 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
939 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
941 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
942 y=(hnc>>8)/ANALOGTV_H;
943 x=(hnc>>8)%ANALOGTV_H;
945 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
946 it->hashnoise_times[y]=x;
948 /* hnc += hni + (int)(random()%65536)-32768; */
950 hnc += (int)(random()%65536)-32768;
951 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
958 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
961 if (it->rx_signal_level != 0.0)
962 it->agclevel = 1.0/it->rx_signal_level;
967 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
968 printf(" %0.3f",it->ghostfir[i]);
970 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
975 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
980 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
982 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
983 vsync=lineno>=3 && lineno<7;
985 sig=input->signal[lineno];
987 i=ANALOGTV_SYNC_START;
989 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
990 while (i<ANALOGTV_H) sig[i++]=synclevel;
992 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
993 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
994 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
996 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
999 /* 9 cycles of colorburst */
1000 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1001 sig[i+1] += ANALOGTV_CB_LEVEL;
1002 sig[i+3] -= ANALOGTV_CB_LEVEL;
1009 analogtv_sync(analogtv *it)
1011 int cur_hsync=it->cur_hsync;
1012 int cur_vsync=it->cur_vsync;
1017 float cbfc=1.0f/128.0f;
1019 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1020 for (i=-32; i<32; i++) {
1021 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1022 sp = it->rx_signal + lineno*ANALOGTV_H;
1024 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1027 filt *= it->agclevel;
1029 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1031 if (osc >= 1.05f+0.0002f * filt) break;
1033 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1035 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1037 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1038 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1039 if (!lineno2) lineno2 = ANALOGTV_V;
1040 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1041 for (i=-8; i<8; i++) {
1042 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1043 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1045 if (osc >= 1.005f + 0.0001f*filt) break;
1047 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1050 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1051 ANALOGTV_H) % ANALOGTV_H;
1053 /* Now look for the colorburst, which is a few cycles after the H
1054 sync pulse, and store its phase.
1055 The colorburst is 9 cycles long, and we look at the middle 5
1060 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1061 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1062 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1063 sp[i]*it->agclevel*cbfc;
1071 for (i=0; i<4; i++) {
1072 tot += it->cb_phase[i]*it->cb_phase[i];
1074 cbgain = 32.0f/sqrtf(tot);
1076 for (i=0; i<4; i++) {
1077 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1082 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1084 it->cb_phase[0], it->cb_phase[1],
1085 it->cb_phase[2], it->cb_phase[3]);
1088 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1091 it->cur_hsync = cur_hsync;
1092 it->cur_vsync = cur_vsync;
1096 analogtv_levelmult(const analogtv *it, int level)
1098 static const double levelfac[3]={-7.5, 5.5, 24.5};
1099 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1103 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1107 if (y==ytop || y==ybot-1) level=0;
1108 else if (y==ytop+1 || y==ybot-2) level=1;
1111 else if (ybot-ytop>=5) {
1112 if (y==ytop || y==ybot-1) level=0;
1115 else if (ybot-ytop>=3) {
1116 if (y==ytop) level=0;
1126 The point of this stuff is to ensure that when useheight is not a
1127 multiple of VISLINES so that TV scan lines map to different numbers
1128 of vertical screen pixels, the total brightness of each scan line
1130 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1131 it interpolates extra black lines.
1135 analogtv_setup_levels(analogtv *it, double avgheight)
1138 static const double levelfac[3]={-7.5, 5.5, 24.5};
1140 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1142 for (i=0; i<height; i++) {
1143 it->leveltable[height][i].index = 2;
1147 it->leveltable[height][0].index=0;
1150 if (height >= 1) it->leveltable[height][height-1].index=0;
1153 it->leveltable[height][1].index=1;
1154 if (height >= 2) it->leveltable[height][height-2].index=1;
1157 for (i=0; i<height; i++) {
1158 it->leveltable[height][i].value =
1159 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1165 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1167 *a0 = (*a0 * a1) & 0xffffffffu;
1168 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1171 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1173 unsigned int a1 = *a, c1 = *c;
1179 rnd_combine(a, c, a1, c1);
1181 rnd_combine(&a1, &c1, a1, c1);
1185 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1187 rnd_seek_ac(&a, &c, dist);
1191 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1193 float *ps=it->rx_signal + start;
1194 float *pe=it->rx_signal + end;
1196 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1197 unsigned int fastrnd_offset;
1199 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1201 fastrnd_offset = fastrnd - 0x7fffffff;
1202 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1205 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1206 fastrnd_offset = fastrnd - 0x7fffffff;
1207 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1212 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1214 analogtv_input *inp=rec->input;
1215 float *ps=it->rx_signal + start;
1216 float *pe=it->rx_signal + end;
1218 signed char *ss=&inp->signal[0][0];
1219 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1220 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1223 float level=rec->level;
1224 float hfloss=rec->hfloss;
1225 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1228 const float noise_decay = 0.99995f;
1229 float noise_ampl = 1.3f * powf(noise_decay, start);
1234 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1236 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1238 /* Do a big noisy transition. We can make the transition noise of
1239 high constant strength regardless of signal strength.
1241 There are two separate state machines. here, One is the noise
1242 process and the other is the
1244 We don't bother with the FIR filter here
1247 float sig0=(float)s[0];
1248 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1249 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1250 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1252 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1254 noise_ampl *= noise_decay;
1263 for (i=1; i<5; i++) {
1266 s2 += ANALOGTV_SIGNAL_LEN;
1267 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1271 assert(!((pe - p) % 4));
1273 float sig0,sig1,sig2,sig3,sigr;
1280 dp[0]=sig0+sig1+sig2+sig3;
1282 /* Get the video out signal, and add some ghosting, typical of RF
1283 monitor cables. This corresponds to a pretty long cable, but
1287 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1288 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1289 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1291 p[0] += (sig0+sigr + sig2*hfloss) * level;
1292 p[1] += (sig1+sigr + sig3*hfloss) * level;
1293 p[2] += (sig2+sigr + sig0*hfloss) * level;
1294 p[3] += (sig3+sigr + sig1*hfloss) * level;
1298 if (s>=se) s = ss + (s-se);
1304 static void analogtv_thread_add_signals(void *thread_raw)
1306 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1307 const analogtv *it = thread->it;
1309 unsigned subtotal_end;
1311 unsigned start = thread->signal_start;
1312 while(start != thread->signal_end)
1316 /* Work on 8 KB blocks; these should fit in L1. */
1317 /* (Though it doesn't seem to help much on my system.) */
1318 unsigned end = start + 2048;
1319 if(end > thread->signal_end)
1320 end = thread->signal_end;
1322 analogtv_init_signal (it, it->noiselevel, start, end);
1324 for (i = 0; i != it->rec_count; ++i) {
1325 analogtv_add_signal (it, it->recs[i], start, end,
1326 !i ? it->channel_change_cycles : 0);
1329 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1330 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1332 p = it->rx_signal + start;
1333 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1334 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1336 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1338 it->signal_subtotals[i] = sum;
1339 p += ANALOGTV_SUBTOTAL_LEN;
1346 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1347 int *ytop, int *ybot, unsigned *signal_offset)
1349 *slineno=lineno-ANALOGTV_TOP;
1350 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1351 it->useheight/2)*it->puheight) + it->useheight/2;
1352 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1353 it->useheight/2)*it->puheight) + it->useheight/2;
1355 int linesig=analogtv_line_signature(input,lineno)
1356 + it->hashnoise_times[lineno];
1358 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1359 it->line_hsync[lineno];
1361 if (*ytop==*ybot) return 0;
1362 if (*ybot<0 || *ytop>it->useheight) return 0;
1363 if (*ytop<0) *ytop=0;
1364 if (*ybot>it->useheight) *ybot=it->useheight;
1366 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1371 analogtv_blast_imagerow(const analogtv *it,
1372 float *rgbf, float *rgbf_end,
1377 char *level_copyfrom[3];
1378 int xrepl=it->xrepl;
1379 unsigned lineheight = ybot - ytop;
1380 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1381 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1383 for (y=ytop; y<ybot; y++) {
1384 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1385 unsigned line = y-ytop;
1387 int level=it->leveltable[lineheight][line].index;
1388 float levelmult=it->leveltable[lineheight][line].value;
1390 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1391 why standard graphics sw has to be fast, or else people will have to
1392 work around it and risk incompatibility. The quickdraw folks
1393 understood this. The other answer would be for X11 to have fewer
1394 formats for bitm.. oh, never mind. If neither of these cases work
1395 (they probably cover 99% of setups) it falls back on the Xlib
1398 if (level_copyfrom[level]) {
1399 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1402 level_copyfrom[level] = rowdata;
1406 else if (it->image->format==ZPixmap &&
1407 it->image->bits_per_pixel==32 &&
1408 sizeof(unsigned int)==4 &&
1409 it->image->byte_order==localbyteorder) {
1410 /* int is more likely to be 32 bits than long */
1411 unsigned int *pixelptr=(unsigned int *)rowdata;
1414 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1415 int ntscri=rpf[0]*levelmult;
1416 int ntscgi=rpf[1]*levelmult;
1417 int ntscbi=rpf[2]*levelmult;
1418 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1419 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1420 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1421 pix = (it->red_values[ntscri] |
1422 it->green_values[ntscgi] |
1423 it->blue_values[ntscbi]);
1427 if (xrepl>=3) pixelptr[2] = pix;
1432 else if (it->image->format==ZPixmap &&
1433 it->image->bits_per_pixel==16 &&
1434 sizeof(unsigned short)==2 &&
1435 float_extraction_works &&
1436 it->image->byte_order==localbyteorder) {
1437 unsigned short *pixelptr=(unsigned short *)rowdata;
1439 float_extract_t r1,g1,b1;
1442 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1443 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1444 r1.f=r2 * levelmult+float_low8_ofs;
1445 g1.f=g2 * levelmult+float_low8_ofs;
1446 b1.f=b2 * levelmult+float_low8_ofs;
1447 pix = (it->red_values[r1.i & 0x3ff] |
1448 it->green_values[g1.i & 0x3ff] |
1449 it->blue_values[b1.i & 0x3ff]);
1453 if (xrepl>=3) pixelptr[2] = pix;
1458 else if (it->image->format==ZPixmap &&
1459 it->image->bits_per_pixel==16 &&
1460 sizeof(unsigned short)==2 &&
1461 it->image->byte_order==localbyteorder) {
1462 unsigned short *pixelptr=(unsigned short *)rowdata;
1465 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1466 int r1=rpf[0] * levelmult;
1467 int g1=rpf[1] * levelmult;
1468 int b1=rpf[2] * levelmult;
1469 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1470 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1471 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1472 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1476 if (xrepl>=3) pixelptr[2] = pix;
1482 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1483 int ntscri=rpf[0]*levelmult;
1484 int ntscgi=rpf[1]*levelmult;
1485 int ntscbi=rpf[2]*levelmult;
1486 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1487 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1488 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1489 for (j=0; j<xrepl; j++) {
1490 XPutPixel(it->image, x*xrepl + j, y,
1491 it->red_values[ntscri] | it->green_values[ntscgi] |
1492 it->blue_values[ntscbi]);
1500 static void analogtv_thread_draw_lines(void *thread_raw)
1502 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1503 const analogtv *it = thread->it;
1507 float *raw_rgb_start;
1509 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1511 if (! raw_rgb_start) return;
1513 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1515 for (lineno=ANALOGTV_TOP + thread->thread_id;
1516 lineno<ANALOGTV_BOT;
1517 lineno += it->threads.count) {
1520 int slineno, ytop, ybot;
1521 unsigned signal_offset;
1523 const float *signal;
1525 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1526 float *rgb_start, *rgb_end;
1532 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1534 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1538 signal = it->rx_signal + signal_offset;
1542 float bloomthisrow,shiftthisrow;
1543 float viswidth,middle;
1547 bloomthisrow = -10.0f * it->crtload[lineno];
1548 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1549 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1551 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1552 (0.7f+cosf(slineno*0.6f)));
1557 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1558 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1560 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1562 scw=it->subwidth*scanwidth;
1563 if (scw>it->subwidth) scw=it->usewidth;
1564 scl=it->subwidth/2 - scw/2;
1565 scr=it->subwidth/2 + scw/2;
1567 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1568 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1569 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1570 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1571 - it->squish_control)) *65536.0f);
1572 squishdiv=it->subwidth/15;
1574 rgb_start=raw_rgb_start+scl*3;
1575 rgb_end=raw_rgb_start+scr*3;
1577 assert(scanstart_i>=0);
1580 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1582 scanstart_i/65536.0f,
1583 squishright_i/65536.0f,
1590 for (y=ytop; y<ybot; y++) {
1591 int level=analogtv_level(it, y, ytop, ybot);
1592 float levelmult=analogtv_levelmult(it, level);
1593 float levelmult_y = levelmult * it->contrast_control
1594 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1595 float levelmult_iq = levelmult * 0.090f;
1597 analogtv_ntsc_to_yiq(it, lineno, signal,
1598 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1603 while (i<0 && x<it->usewidth) {
1604 XPutPixel(it->image, x, y, it->colors[0]);
1609 while (i<scanend_i && x<it->usewidth) {
1610 float pixfrac=(i&0xffff)/65536.0f;
1611 float invpixfrac=(1.0f-pixfrac);
1613 int yli,ili,qli,cmi;
1615 float interpy=(yiq[pati].y*invpixfrac
1616 + yiq[pati+1].y*pixfrac) * levelmult_y;
1617 float interpi=(yiq[pati].i*invpixfrac
1618 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1619 float interpq=(yiq[pati].q*invpixfrac
1620 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1622 yli = (int)(interpy * it->cmap_y_levels);
1623 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1624 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1626 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1628 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1630 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1632 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1635 if ((random()%65536)==0) {
1636 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1637 interpy, interpi, interpq,
1643 for (j=0; j<it->xrepl; j++) {
1644 XPutPixel(it->image, x, y,
1648 if (i >= squishright_i) {
1649 pixmultinc += pixmultinc/squishdiv;
1653 while (x<it->usewidth) {
1654 XPutPixel(it->image, x, y, it->colors[0]);
1660 analogtv_ntsc_to_yiq(it, lineno, signal,
1661 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1663 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1664 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1666 i=scanstart_i; rrp=rgb_start;
1667 while (i<0 && rrp!=rgb_end) {
1668 rrp[0]=rrp[1]=rrp[2]=0;
1672 while (i<scanend_i && rrp!=rgb_end) {
1673 float pixfrac=(i&0xffff)/65536.0f;
1674 float invpixfrac=1.0f-pixfrac;
1678 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1679 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1680 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1683 According to the NTSC spec, Y,I,Q are generated as:
1685 y=0.30 r + 0.59 g + 0.11 b
1686 i=0.60 r - 0.28 g - 0.32 b
1687 q=0.21 r - 0.52 g + 0.31 b
1689 So if you invert the implied 3x3 matrix you get what standard
1690 televisions implement with a bunch of resistors (or directly in the
1693 r = y + 0.948 i + 0.624 q
1694 g = y - 0.276 i - 0.639 q
1695 b = y - 1.105 i + 1.729 q
1698 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1699 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1700 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1708 if (i>=squishright_i) {
1709 pixmultinc += pixmultinc/squishdiv;
1710 pixbright += pixbright/squishdiv/2;
1715 while (rrp != rgb_end) {
1716 rrp[0]=rrp[1]=rrp[2]=0.0f;
1720 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1725 free(raw_rgb_start);
1729 analogtv_draw(analogtv *it, double noiselevel,
1730 const analogtv_reception *const *recs, unsigned rec_count)
1733 /* int bigloadchange,drawcount;*/
1735 int overall_top, overall_bot;
1737 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1741 it->rx_signal_level = noiselevel;
1742 for (i = 0; i != rec_count; ++i) {
1743 const analogtv_reception *rec = recs[i];
1744 double level = rec->level;
1745 analogtv_input *inp=rec->input;
1747 it->rx_signal_level =
1748 sqrt(it->rx_signal_level * it->rx_signal_level +
1749 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1750 rec->ghostfir[2] + rec->ghostfir[3]))));
1752 /* duplicate the first line into the Nth line to ease wraparound computation */
1753 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1754 ANALOGTV_H * sizeof(inp->signal[0][0]));
1757 analogtv_setup_frame(it);
1758 analogtv_set_demod(it);
1760 it->random0 = random();
1761 it->random1 = random();
1762 it->noiselevel = noiselevel;
1764 it->rec_count = rec_count;
1765 threadpool_run(&it->threads, analogtv_thread_add_signals);
1766 threadpool_wait(&it->threads);
1768 it->channel_change_cycles=0;
1770 /* rx_signal has an extra 2 lines at the end, where we copy the
1771 first 2 lines so we can index into it while only worrying about
1772 wraparound on a per-line level */
1773 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1775 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1777 /* Repeat for signal_subtotals. */
1779 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1780 &it->signal_subtotals[0],
1781 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1783 analogtv_sync(it); /* Requires the add_signals be complete. */
1786 /* if (it->hashnoise_on) baseload=0.5; */
1790 it->crtload[ANALOGTV_TOP-1]=baseload;
1791 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1792 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1794 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1796 /* calculate tint once per frame */
1797 it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1798 it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1800 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1801 int slineno, ytop, ybot;
1802 unsigned signal_offset;
1803 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1806 if (lineno==it->shrinkpulse) {
1808 /*bigloadchange=1;*/
1813 if (it->hashnoise_rpm>0.0 &&
1816 (slineno<20 && it->flutter_horiz_desync) ||
1817 it->gaussiannoise_level>30 ||
1818 ((it->gaussiannoise_level>2.0 ||
1819 it->multipath) && random()%4) ||
1820 linesig != it->onscreen_signature[lineno])) {
1823 it->onscreen_signature[lineno] = linesig;
1828 Interpolate the 600-dotclock line into however many horizontal
1829 screen pixels we're using, and convert to RGB.
1831 We add some 'bloom', variations in the horizontal scan width with
1832 the amount of brightness, extremely common on period TV sets. They
1833 had a single oscillator which generated both the horizontal scan and
1834 (during the horizontal retrace interval) the high voltage for the
1835 electron beam. More brightness meant more load on the oscillator,
1836 which caused an decrease in horizontal deflection. Look for
1839 Also, the A2 did a bad job of generating horizontal sync pulses
1840 during the vertical blanking interval. This, and the fact that the
1841 horizontal frequency was a bit off meant that TVs usually went a bit
1842 out of sync during the vertical retrace, and the top of the screen
1843 would be bent a bit to the left or right. Look for (shiftthisrow).
1845 We also add a teeny bit of left overscan, just enough to be
1846 annoying, but you can still read the left column of text.
1848 We also simulate compression & brightening on the right side of the
1849 screen. Most TVs do this, but you don't notice because they overscan
1850 so it's off the right edge of the CRT. But the A2 video system used
1851 so much of the horizontal scan line that you had to crank the
1852 horizontal width down in order to not lose the right few characters,
1853 and you'd see the compression on the right edge. Associated with
1854 compression is brightening; since the electron beam was scanning
1855 slower, the same drive signal hit the phosphor harder. Look for
1856 (squishright_i) and (squishdiv).
1860 /* This used to be an int, I suspect by mistake. - Dave */
1867 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1868 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1869 for (i=0; i != frac; i++) {
1873 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1875 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1876 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1877 totsignal += it->signal_subtotals[i];
1880 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1881 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1882 for (i=0; i != frac; i++) {
1886 totsignal *= it->agclevel;
1887 ncl = 0.95f * it->crtload[lineno-1] +
1889 (totsignal-30000)/100000.0f +
1890 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1892 /*diff=ncl - it->crtload[lineno];*/
1893 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1894 it->crtload[lineno]=ncl;
1898 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1899 threadpool_wait(&it->threads);
1902 /* poor attempt at visible retrace */
1903 for (i=0; i<15; i++) {
1904 int ytop=(int)((i*it->useheight/15 -
1905 it->useheight/2)*puheight) + it->useheight/2;
1906 int ybot=(int)(((i+1)*it->useheight/15 -
1907 it->useheight/2)*puheight) + it->useheight/2;
1908 int div=it->usewidth*3/2;
1910 for (x=0; x<it->usewidth; x++) {
1911 y = ytop + (ybot-ytop)*x / div;
1912 if (y<0 || y>=it->useheight) continue;
1913 XPutPixel(it->image, x, y, 0xffffff);
1918 if (it->need_clear) {
1919 XClearWindow(it->dpy, it->window);
1924 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1925 the top of the next-after-the-last scan line. This is the same until
1926 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1929 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1930 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1932 if (overall_top<0) overall_top=0;
1933 if (overall_bot>it->useheight) overall_bot=it->useheight;
1935 if (overall_top>0) {
1936 XClearArea(it->dpy, it->window,
1937 it->screen_xo, it->screen_yo,
1938 it->usewidth, overall_top, 0);
1940 if (it->useheight > overall_bot) {
1941 XClearArea(it->dpy, it->window,
1942 it->screen_xo, it->screen_yo+overall_bot,
1943 it->usewidth, it->useheight-overall_bot, 0);
1946 if (overall_bot > overall_top) {
1947 put_xshm_image(it->dpy, it->window, it->gc, it->image,
1949 it->screen_xo, it->screen_yo+overall_top,
1950 it->usewidth, overall_bot - overall_top,
1959 gettimeofday(&tv,NULL);
1961 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1962 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1963 sprintf(buf, "FPS=%0.1f",fps);
1964 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1967 it->last_display_time=tv;
1973 analogtv_input_allocate()
1975 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1981 This takes a screen image and encodes it as a video camera would,
1982 including all the bandlimiting and YIQ modulation.
1983 This isn't especially tuned for speed.
1987 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1994 XColor col1[ANALOGTV_PIC_LEN];
1995 XColor col2[ANALOGTV_PIC_LEN];
1996 int multiq[ANALOGTV_PIC_LEN+4];
1997 int y_overscan=5; /* overscan this much top and bottom */
1998 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2000 img_w=pic_im->width;
2001 img_h=pic_im->height;
2003 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2004 double phase=90.0-90.0*i;
2006 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2009 for (y=0; y<y_scanlength; y++) {
2010 int picy1=(y*img_h)/y_scanlength;
2011 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2013 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2014 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2015 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2016 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2018 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2019 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2021 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2022 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2024 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2026 int filty,filti,filtq;
2029 y=0.30 r + 0.59 g + 0.11 b
2030 i=0.60 r - 0.28 g - 0.32 b
2031 q=0.21 r - 0.52 g + 0.31 b
2032 The coefficients below are in .4 format */
2034 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2035 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2036 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2037 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2038 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2039 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2041 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2042 with an extra zero at 3.5 MHz, from
2043 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2045 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2046 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2047 fyx[6] = (rawy * 1897) >> 16;
2048 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2049 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2050 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2051 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2054 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2055 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2057 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2058 fix[3] = (rawi * 1413) >> 16;
2059 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2060 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2061 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2064 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2065 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2067 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2068 fqx[3] = (rawq * 75) >> 16;
2069 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2070 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2071 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2075 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2076 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2077 if (composite>125) composite=125;
2078 if (composite<0) composite=0;
2079 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2087 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2090 int change=random()%ANALOGTV_V;
2091 unsigned int fastrnd=random();
2092 double hso=(int)(random()%1000)-500;
2093 int yofs=random()%ANALOGTV_V;
2096 for (y=change; y<ANALOGTV_V; y++) {
2097 int s2y=(y+yofs)%ANALOGTV_V;
2099 int noiselevel=60000 / (y-change+100);
2101 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2103 for (x=0; x<ANALOGTV_H; x++) {
2105 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2106 noise=(filt*noiselevel)>>16;
2107 newsig=s2->signal[s2y][x] + noise;
2108 if (newsig>120) newsig=120;
2109 if (newsig<0) newsig=0;
2110 it->signal[y][x]=newsig;
2120 if (it->hashnoise_times[lineno]) {
2121 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2123 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2125 double cur=frand(150.0)-20.0;
2126 int len=random()%15+3;
2127 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2128 for (i=0; i<len; i++) {
2129 double sig=signal[hnt];
2132 cur += frand(5.0)-5.0;
2133 maxampl = maxampl*0.9;
2144 analogtv_reception_update(analogtv_reception *rec)
2148 if (rec->multipath > 0.0) {
2149 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2150 rec->ghostfir2[i] +=
2151 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2153 if (random()%20==0) {
2154 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2155 = rec->multipath * (frand(0.08)-0.04);
2157 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2158 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2162 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2163 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2167 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2168 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2175 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2178 #include "images/6x10font.xbm"
2181 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2182 int w, int h, char *fontname)
2189 XWindowAttributes xgwa;
2194 XGetWindowAttributes (dpy, window, &xgwa);
2196 if (fontname && !strcmp (fontname, "6x10")) {
2198 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2199 (char *) font6x10_bits,
2203 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2205 XFreePixmap(dpy, text_pm);
2207 } else if (fontname) {
2209 font = XLoadQueryFont (dpy, fontname);
2211 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2215 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2217 memset(&gcv, 0, sizeof(gcv));
2221 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2223 XSetForeground(dpy, gc, 0);
2224 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2225 XSetForeground(dpy, gc, 1);
2226 for (i=0; i<256; i++) {
2228 int x=f->char_w*i+1;
2229 int y=f->char_h*8/10;
2230 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2232 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2235 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2236 256*f->char_w, f->char_h, -1, -1);
2239 XFreePixmap(dpy, text_pm);
2241 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2242 256*f->char_w, f->char_h, 8, 0);
2243 f->text_im->data = (char *)calloc(f->text_im->height,
2244 f->text_im->bytes_per_line);
2252 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2254 if (x<0 || x>=f->char_w) return 0;
2255 if (y<0 || y>=f->char_h) return 0;
2256 if (c<0 || c>=256) return 0;
2257 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2261 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2263 if (x<0 || x>=f->char_w) return;
2264 if (y<0 || y>=f->char_h) return;
2265 if (c<0 || c>=256) return;
2267 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2271 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2275 if (c<0 || c>=256) return;
2277 for (y=0; y<f->char_h; y++) {
2278 for (x=0; x<f->char_w; x++) {
2280 value=(*s==' ') ? 0 : 1;
2281 analogtv_font_set_pixel(f, c, x, y, value);
2288 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2291 for (i=0; i<4; i++) {
2292 double w=90.0*i + phase;
2293 double val=luma + chroma * (cos(3.1415926/180.0*w));
2294 if (val<0.0) val=0.0;
2295 if (val>127.0) val=127.0;
2301 analogtv_draw_solid(analogtv_input *input,
2302 int left, int right, int top, int bot,
2307 if (right-left<4) right=left+4;
2308 if (bot-top<1) bot=top+1;
2310 for (y=top; y<bot; y++) {
2311 for (x=left; x<right; x++) {
2312 input->signal[y][x] = ntsc[x&3];
2319 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2320 double left, double right, double top, double bot,
2321 double luma, double chroma, double phase)
2325 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2326 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2327 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2328 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2330 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2331 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2336 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2337 int c, int x, int y, int ntsc[4])
2339 int yc,xc,ys,xs,pix;
2341 for (yc=0; yc<f->char_h; yc++) {
2342 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2343 if (ys<0 || ys>=ANALOGTV_V) continue;
2345 for (xc=0; xc<f->char_w; xc++) {
2346 pix=analogtv_font_pixel(f, c, xc, yc);
2348 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2349 if (xs<0 || xs>=ANALOGTV_H) continue;
2351 input->signal[ys][xs] = ntsc[xs&3];
2360 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2361 char *s, int x, int y, int ntsc[4])
2364 analogtv_draw_char(input, f, *s, x, y, ntsc);
2371 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2372 char *s, int x, int y, int ntsc[4])
2374 int width=strlen(s) * f->char_w * 4;
2377 analogtv_draw_string(input, f, s, x, y, ntsc);
2381 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2382 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2384 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2385 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2386 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2387 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2388 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2391 Much of this function was adapted from logo.c
2394 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2395 const char * const *xpm, int left, int top)
2400 int ncolors, nbytes;
2403 int r; int g; int b;
2407 if (4 != sscanf ((const char *) *xpm,
2409 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2411 if (ncolors < 1 || ncolors > 255)
2413 if (nbytes != 1) /* a serious limitation */
2417 for (i = 0; i < ncolors; i++) {
2418 const char *line = *xpm;
2419 int colori = ((unsigned char)*line++)&0xff;
2424 while (*line == ' ' || *line == '\t')
2427 if (which != 'c' && which != 'm')
2429 while (*line == ' ' || *line == '\t')
2431 if (!strncasecmp(line, "None", 4))
2440 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2442 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2444 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2459 for (y=0; y<xpmh; y++) {
2460 const char *line = *xpm++;
2462 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2464 for (x=0; x<xpmw; x++) {
2465 int cbyte=((unsigned char)line[x])&0xff;
2468 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2470 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2471 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2472 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2479 for (i=0; i<4; i++) {
2480 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2481 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2484 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2485 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2486 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2487 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];