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.
64 #else /* !HAVE_COCOA */
65 # include <X11/Xlib.h>
66 # include <X11/Xutil.h>
72 #include "resources.h"
75 #include "grabscreen.h"
81 /* only works on linux + freebsd */
82 #include <machine/cpufunc.h>
84 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
85 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
86 #define DTIME dtimes[n_dtimes++]=rdtsc()
87 #define DTIME_SHOW(DIV) \
89 double _dtime_div=(DIV); \
90 printf("time/%.1f: ",_dtime_div); \
91 for (i=1; i<n_dtimes; i++) \
92 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
99 #define DTIME_START do { } while (0)
100 #define DTIME do { } while (0)
101 #define DTIME_SHOW(DIV) do { } while (0)
106 #define FASTRND_A 1103515245
107 #define FASTRND_C 12345
108 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
110 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
111 int start, int end, struct analogtv_yiq_s *it_yiq);
113 static float puramp(const analogtv *it, float tc, float start, float over)
115 float pt=it->powerup-start;
117 if (pt<0.0f) return 0.0f;
118 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
120 ret=(1.0f-expf(-pt/tc))*over;
121 if (ret>1.0f) return 1.0f;
126 There are actual standards for TV signals: NTSC and RS-170A describe the
127 system used in the US and Japan. Europe has slightly different systems, but
128 not different enough to make substantially different screensaver displays.
129 Sadly, the standards bodies don't do anything so useful as publish the spec on
130 the web. Best bets are:
132 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
133 http://www.ntsc-tv.com/ntsc-index-02.htm
135 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
136 values it calculates across each scan line. In PseudoColor mode, it consider
137 each possible pattern of 5 preceding bit values in each possible position
138 modulo 4 and allocates a color for each. A few things, like the brightening on
139 the right side as the horizontal trace slows down, aren't done in PseudoColor.
141 I'd like to add a bit of visible retrace, but it conflicts with being able to
142 bitcopy the image when fast scrolling. After another couple of CPU
143 generations, we could probably regenerate the whole image from scratch every
144 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
148 /* localbyteorder is MSBFirst or LSBFirst */
149 static int localbyteorder;
150 static const double float_low8_ofs=8388608.0;
151 static int float_extraction_works;
163 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
164 localbyteorder=*(char *)&localbyteorder_loc;
171 float_extraction_works=1;
172 for (i=0; i<256*4; i++) {
173 fe.f=float_low8_ofs+(double)i;
177 printf("Float extraction failed for %d => %d\n",i,ans);
179 float_extraction_works=0;
188 analogtv_set_defaults(analogtv *it, char *prefix)
192 sprintf(buf,"%sTVTint",prefix);
193 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
194 sprintf(buf,"%sTVColor",prefix);
195 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
196 sprintf(buf,"%sTVBrightness",prefix);
197 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
198 sprintf(buf,"%sTVContrast",prefix);
199 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
200 it->height_control = 1.0;
201 it->width_control = 1.0;
202 it->squish_control = 0.0;
207 it->hashnoise_enable=1;
209 it->horiz_desync=frand(10.0)-5.0;
210 it->squeezebottom=frand(5.0)-1.0;
213 printf("analogtv: prefix=%s\n",prefix);
214 printf(" use: shm=%d cmap=%d color=%d\n",
215 it->use_shm,it->use_cmap,it->use_color);
216 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
217 it->tint_control, it->color_control, it->brightness_control,
218 it->contrast_control);
219 printf(" freq_error %g: %g %d\n",
220 it->freq_error, it->freq_error_inc, it->flutter_tint);
221 printf(" desync: %g %d\n",
222 it->horiz_desync, it->flutter_horiz_desync);
223 printf(" hashnoise rpm: %g\n",
225 printf(" vis: %d %d %d\n",
226 it->visclass, it->visbits, it->visdepth);
227 printf(" shift: %d-%d %d-%d %d-%d\n",
228 it->red_invprec,it->red_shift,
229 it->green_invprec,it->green_shift,
230 it->blue_invprec,it->blue_shift);
231 printf(" size: %d %d %d %d xrepl=%d\n",
232 it->usewidth, it->useheight,
233 it->screen_xo, it->screen_yo, it->xrepl);
235 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
236 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
237 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
238 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
239 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
240 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
241 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
242 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
243 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
244 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
245 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
246 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
247 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
253 extern Bool mono_p; /* shoot me */
256 analogtv_free_image(analogtv *it)
260 #ifdef HAVE_XSHM_EXTENSION
261 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
264 thread_free(it->image->data);
265 it->image->data = NULL;
266 XDestroyImage(it->image);
273 analogtv_alloc_image(analogtv *it)
275 /* On failure, it->image is NULL. */
277 unsigned bits_per_pixel = get_bits_per_pixel(it->dpy, it->xgwa.depth);
278 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
279 /* Width is in bits. */
280 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
283 #ifdef HAVE_XSHM_EXTENSION
284 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
286 width / bits_per_pixel, it->useheight);
288 if (!it->image) it->use_shm=0;
291 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
292 it->usewidth, it->useheight, 8, width / 8);
294 if(thread_malloc((void **)&it->image->data, it->dpy,
295 it->image->height * it->image->bytes_per_line)) {
296 it->image->data = NULL;
297 XDestroyImage(it->image);
304 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
306 /* Not enough memory. Maybe try a smaller window. */
307 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
313 analogtv_configure(analogtv *it)
315 int oldwidth=it->usewidth;
316 int oldheight=it->useheight;
317 int wlim,hlim,height_diff;
319 /* If the window is very small, don't let the image we draw get lower
320 than the actual TV resolution (266x200.)
322 If the aspect ratio of the window is within 15% of a 4:3 ratio,
323 then scale the image to exactly fill the window.
325 Otherwise, center the image either horizontally or vertically,
326 padding on the left+right, or top+bottom, but not both.
328 If it's very close (2.5%) to a multiple of VISLINES, make it exact
329 For example, it maps 1024 => 1000.
331 float percent = 0.15; /* jwz: 20% caused severe top/bottom clipping
332 in Pong on 1680x1050 iMac screen. */
333 float min_ratio = 4.0 / 3.0 * (1 - percent);
334 float max_ratio = 4.0 / 3.0 * (1 + percent);
336 float height_snap=0.025;
338 hlim = it->xgwa.height;
339 wlim = it->xgwa.width;
340 ratio = wlim / (float) hlim;
343 /* Fill the whole iPhone screen, even though that distorts the image. */
344 min_ratio = 640.0 / 1136.0 * (1 - percent);
345 max_ratio = 1136.0 / 640.0 * (1 + percent);
348 if (wlim < 266 || hlim < 200)
354 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
355 wlim, hlim, it->xgwa.width, it->xgwa.height,
356 min_ratio, ratio, max_ratio);
359 else if (ratio > min_ratio && ratio < max_ratio)
363 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
364 wlim, hlim, min_ratio, ratio, max_ratio);
367 else if (ratio > max_ratio)
369 wlim = hlim*max_ratio;
372 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
373 wlim, hlim, it->xgwa.width, it->xgwa.height,
374 min_ratio, ratio, max_ratio);
377 else /* ratio < min_ratio */
379 hlim = wlim/min_ratio;
382 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
383 wlim, hlim, it->xgwa.width, it->xgwa.height,
384 min_ratio, ratio, max_ratio);
389 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
390 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
396 /* Most times this doesn't change */
397 if (wlim != oldwidth || hlim != oldheight) {
402 it->xrepl=1+it->usewidth/640;
403 if (it->xrepl>2) it->xrepl=2;
404 it->subwidth=it->usewidth/it->xrepl;
406 analogtv_free_image(it);
407 analogtv_alloc_image(it);
410 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
411 it->screen_yo = (it->xgwa.height-it->useheight)/2;
416 analogtv_reconfigure(analogtv *it)
418 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
419 analogtv_configure(it);
422 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
423 #define ANALOGTV_SUBTOTAL_LEN 32
425 typedef struct analogtv_thread_s
429 size_t signal_start, signal_end;
432 #define SIGNAL_OFFSET(thread_id) \
433 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
435 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
438 analogtv_thread *thread = (analogtv_thread *)thread_raw;
441 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
442 thread->thread_id = thread_id;
444 align = thread_memory_alignment(thread->it->dpy) /
445 sizeof(thread->it->signal_subtotals[0]);
448 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
450 thread->signal_start = SIGNAL_OFFSET(thread_id);
451 thread->signal_end = thread_id + 1 == threads->count ?
452 ANALOGTV_SIGNAL_LEN :
453 SIGNAL_OFFSET(thread_id + 1);
458 static void analogtv_thread_destroy(void *thread_raw)
463 analogtv_allocate(Display *dpy, Window window)
465 static const struct threadpool_class cls = {
466 sizeof(analogtv_thread),
467 analogtv_thread_create,
468 analogtv_thread_destroy
474 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
478 it=(analogtv *)calloc(1,sizeof(analogtv));
482 it->signal_subtotals=NULL;
487 if (thread_malloc((void **)&it->rx_signal, dpy,
488 sizeof(it->rx_signal[0]) * rx_signal_len))
491 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
492 if (thread_malloc((void **)&it->signal_subtotals, dpy,
493 sizeof(it->signal_subtotals[0]) *
494 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
497 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
500 assert(it->threads.count);
506 #ifdef HAVE_XSHM_EXTENSION
512 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
514 it->screen=it->xgwa.screen;
515 it->colormap=it->xgwa.colormap;
516 it->visclass=it->xgwa.visual->class;
517 it->visbits=it->xgwa.visual->bits_per_rgb;
518 it->visdepth=it->xgwa.depth;
519 if (it->visclass == TrueColor || it->visclass == DirectColor) {
520 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
525 it->use_color=!mono_p;
527 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
529 it->use_color=!mono_p;
536 it->red_mask=it->xgwa.visual->red_mask;
537 it->green_mask=it->xgwa.visual->green_mask;
538 it->blue_mask=it->xgwa.visual->blue_mask;
539 it->red_shift=it->red_invprec=-1;
540 it->green_shift=it->green_invprec=-1;
541 it->blue_shift=it->blue_invprec=-1;
543 /* Is there a standard way to do this? Does this handle all cases? */
545 for (shift=0; shift<32; shift++) {
546 for (prec=1; prec<16 && prec<40-shift; prec++) {
547 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
548 if (it->red_shift<0 && mask==it->red_mask)
549 it->red_shift=shift, it->red_invprec=16-prec;
550 if (it->green_shift<0 && mask==it->green_mask)
551 it->green_shift=shift, it->green_invprec=16-prec;
552 if (it->blue_shift<0 && mask==it->blue_mask)
553 it->blue_shift=shift, it->blue_invprec=16-prec;
556 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
557 if (0) fprintf(stderr,"Can't figure out color space\n");
561 for (i=0; i<ANALOGTV_CV_MAX; i++) {
562 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
563 if (intensity>65535) intensity=65535;
564 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
565 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
566 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
571 gcv.background=get_pixel_resource(it->dpy, it->colormap,
572 "background", "Background");
574 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
575 XSetWindowBackground(it->dpy, it->window, gcv.background);
576 XClearWindow(dpy,window);
578 analogtv_configure(it);
584 if(it->threads.count)
585 threadpool_destroy(&it->threads);
586 thread_free(it->signal_subtotals);
587 thread_free(it->rx_signal);
594 analogtv_release(analogtv *it)
598 #ifdef HAVE_XSHM_EXTENSION
599 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
602 thread_free(it->image->data);
603 it->image->data = NULL;
604 XDestroyImage(it->image);
608 if (it->gc) XFreeGC(it->dpy, it->gc);
610 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
612 threadpool_destroy(&it->threads);
613 thread_free(it->rx_signal);
614 thread_free(it->signal_subtotals);
620 First generate the I and Q reference signals, which we'll multiply
621 the input signal by to accomplish the demodulation. Normally they
622 are shifted 33 degrees from the colorburst. I think this was convenient
623 for inductor-capacitor-vacuum tube implementation.
625 The tint control, FWIW, just adds a phase shift to the chroma signal,
626 and the color control controls the amplitude.
628 In text modes (colormode==0) the system disabled the color burst, and no
629 color was detected by the monitor.
631 freq_error gives a mismatch between the built-in oscillator and the
632 TV's colorbust. Some II Plus machines seemed to occasionally get
633 instability problems -- the crystal oscillator was a single
634 transistor if I remember correctly -- and the frequency would vary
635 enough that the tint would change across the width of the screen.
636 The left side would be in correct tint because it had just gotten
637 resynchronized with the color burst.
639 If we're using a colormap, set it up.
642 analogtv_set_demod(analogtv *it)
644 int y_levels=10,i_levels=5,q_levels=5;
647 In principle, we might be able to figure out how to adjust the
648 color map frame-by-frame to get some nice color bummage. But I'm
649 terrified of changing the color map because we'll get flashing.
651 I can hardly believe we still have to deal with colormaps. They're
652 like having NEAR PTRs: an enormous hassle for the programmer just
653 to save on memory. They should have been deprecated by 1995 or
657 if (it->use_cmap && !it->n_colors) {
660 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
666 for (yli=0; yli<y_levels; yli++) {
667 for (ili=0; ili<i_levels; ili++) {
668 for (qli=0; qli<q_levels; qli++) {
669 double interpy,interpi,interpq;
670 double levelmult=700.0;
674 interpy=100.0 * ((double)yli/y_levels);
675 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
676 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
678 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
679 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
680 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
682 if (r>65535) r=65535;
684 if (g>65535) g=65535;
686 if (b>65535) b=65535;
689 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
690 interpy, interpi, interpq,
698 if (!XAllocColor(it->dpy, it->colormap, &col)) {
699 if (q_levels > y_levels*4/12)
701 else if (i_levels > y_levels*5/12)
710 it->colors[it->n_colors++]=col.pixel;
715 it->cmap_y_levels=y_levels;
716 it->cmap_i_levels=i_levels;
717 it->cmap_q_levels=q_levels;
726 analogtv_line_signature(analogtv_input *input, int lineno)
729 char *origsignal=&input->signal[(lineno+input->vsync)
730 %ANALOGTV_V][input->line_hsync[lineno]];
734 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
736 hash = hash + (hash<<17) + c;
739 hash += input->line_hsync[lineno];
742 hash += input->hashnoise_times[lineno];
751 /* Here we model the analog circuitry of an NTSC television.
752 Basically, it splits the signal into 3 signals: Y, I and Q. Y
753 corresponds to luminance, and you get it by low-pass filtering the
754 input signal to below 3.57 MHz.
756 I and Q are the in-phase and quadrature components of the 3.57 MHz
757 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
758 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
759 resolution in some colors than others, the I component gets
760 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
761 is approximately orange-blue, and Q is roughly purple-green. See
762 http://www.ntsc-tv.com for details.
764 We actually do an awful lot to the signal here. I suspect it would
765 make sense to wrap them all up together by calculating impulse
766 response and doing FFT convolutions.
771 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
772 int start, int end, struct analogtv_yiq_s *it_yiq)
777 int phasecorr=(signal-it->rx_signal)&3;
778 struct analogtv_yiq_s *yiq;
780 float agclevel=it->agclevel;
781 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
782 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
787 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
788 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
789 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
790 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
792 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
795 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
796 double tint_q = sin((103 + it->color_control)*3.1415926/180);
798 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
799 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
800 multiq2[2]=-multiq2[0];
801 multiq2[3]=-multiq2[1];
807 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
808 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
809 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
810 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
811 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
812 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
813 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
817 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
818 for (i=0; i<5; i++) dp[i]=0.0f;
821 assert(end < ANALOGTV_PIC_LEN+10);
823 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
824 for (i=0; i<24; i++) dp[i]=0.0;
825 for (i=start, yiq=it_yiq+start, sp=signal+start;
827 i++, dp--, yiq++, sp++) {
829 /* Now filter them. These are infinite impulse response filters
830 calculated by the script at
831 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
832 fixed-point integer DSP, son. No place for wimps. We do it in
833 integer because you can count on integer being faster on most
834 CPUs. We care about speed because we need to recalculate every
835 time we blink text, and when we spew random bytes into screen
836 memory. This is roughly 16.16 fixed point arithmetic, but we
837 scale some filter values up by a few bits to avoid some nasty
840 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
841 with an extra zero at 3.5 MHz, from
842 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
845 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
846 dp[8] = (+1.0f*(dp[6]+dp[0])
852 yiq->y = dp[8] + brightadd;
856 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
857 for (i=0; i<27; i++) dp[i]=0.0;
859 for (i=start, yiq=it_yiq+start, sp=signal+start;
861 i++, dp--, yiq++, sp++) {
864 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
865 1.5 MHz with an extra zero at 3.5 MHz, from
866 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
870 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
871 yiq->i=dp[8] = (dp[5] + dp[0]
872 +3.0f*(dp[4] + dp[1])
873 +4.0f*(dp[3] + dp[2])
874 -0.3333333333f * dp[10]);
876 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
877 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
878 +3.0f*(dp[16+4] + dp[16+1])
879 +4.0f*(dp[16+3] + dp[16+2])
880 -0.3333333333f * dp[24+2]);
883 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
884 yiq->i = yiq->q = 0.0f;
890 analogtv_setup_teletext(analogtv_input *input)
893 int teletext=ANALOGTV_BLACK_LEVEL;
895 /* Teletext goes in line 21. But I suspect there are other things
896 in the vertical retrace interval */
898 for (y=19; y<22; y++) {
899 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
901 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
903 input->signal[y][x]=teletext;
909 analogtv_setup_frame(analogtv *it)
915 if (it->flutter_horiz_desync) {
916 /* Horizontal sync during vertical sync instability. */
917 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
918 ((int)(random()&0xff)-0x80) *
919 ((int)(random()&0xff)-0x80) *
920 ((int)(random()&0xff)-0x80) * 0.000001;
923 for (i=0; i<ANALOGTV_V; i++) {
924 it->hashnoise_times[i]=0;
927 if (it->hashnoise_enable && !it->hashnoise_on) {
928 if (random()%10000==0) {
930 it->shrinkpulse=random()%ANALOGTV_V;
933 if (random()%1000==0) {
936 if (it->hashnoise_on) {
937 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
938 ((int)(random()%2000)-1000)*0.1;
940 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
941 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
943 if (it->hashnoise_rpm > 0.0) {
945 int hnc=it->hashnoise_counter; /* in 24.8 format */
947 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
948 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
949 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
951 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
952 y=(hnc>>8)/ANALOGTV_H;
953 x=(hnc>>8)%ANALOGTV_H;
955 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
956 it->hashnoise_times[y]=x;
958 hnc += hni + (int)(random()%65536)-32768;
960 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
963 if (it->rx_signal_level != 0.0)
964 it->agclevel = 1.0/it->rx_signal_level;
969 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
970 printf(" %0.3f",it->ghostfir[i]);
972 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
977 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
982 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
984 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
985 vsync=lineno>=3 && lineno<7;
987 sig=input->signal[lineno];
989 i=ANALOGTV_SYNC_START;
991 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
992 while (i<ANALOGTV_H) sig[i++]=synclevel;
994 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
995 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
996 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
998 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1001 /* 9 cycles of colorburst */
1002 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1003 sig[i+1] += ANALOGTV_CB_LEVEL;
1004 sig[i+3] -= ANALOGTV_CB_LEVEL;
1011 analogtv_sync(analogtv *it)
1013 int cur_hsync=it->cur_hsync;
1014 int cur_vsync=it->cur_vsync;
1019 float cbfc=1.0f/128.0f;
1021 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1022 for (i=-32; i<32; i++) {
1023 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1024 sp = it->rx_signal + lineno*ANALOGTV_H;
1026 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1029 filt *= it->agclevel;
1031 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1033 if (osc >= 1.05f+0.0002f * filt) break;
1035 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1037 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1039 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1040 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1041 if (!lineno2) lineno2 = ANALOGTV_V;
1042 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1043 for (i=-8; i<8; i++) {
1044 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1045 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1047 if (osc >= 1.005f + 0.0001f*filt) break;
1049 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1052 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1053 ANALOGTV_H) % ANALOGTV_H;
1055 /* Now look for the colorburst, which is a few cycles after the H
1056 sync pulse, and store its phase.
1057 The colorburst is 9 cycles long, and we look at the middle 5
1062 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1063 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1064 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1065 sp[i]*it->agclevel*cbfc;
1073 for (i=0; i<4; i++) {
1074 tot += it->cb_phase[i]*it->cb_phase[i];
1076 cbgain = 32.0f/sqrtf(tot);
1078 for (i=0; i<4; i++) {
1079 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1084 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1086 it->cb_phase[0], it->cb_phase[1],
1087 it->cb_phase[2], it->cb_phase[3]);
1090 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1093 it->cur_hsync = cur_hsync;
1094 it->cur_vsync = cur_vsync;
1098 analogtv_levelmult(const analogtv *it, int level)
1100 static const double levelfac[3]={-7.5, 5.5, 24.5};
1101 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1105 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1109 if (y==ytop || y==ybot-1) level=0;
1110 else if (y==ytop+1 || y==ybot-2) level=1;
1113 else if (ybot-ytop>=5) {
1114 if (y==ytop || y==ybot-1) level=0;
1117 else if (ybot-ytop>=3) {
1118 if (y==ytop) level=0;
1128 The point of this stuff is to ensure that when useheight is not a
1129 multiple of VISLINES so that TV scan lines map to different numbers
1130 of vertical screen pixels, the total brightness of each scan line
1132 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1133 it interpolates extra black lines.
1137 analogtv_setup_levels(analogtv *it, double avgheight)
1140 static const double levelfac[3]={-7.5, 5.5, 24.5};
1142 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1144 for (i=0; i<height; i++) {
1145 it->leveltable[height][i].index = 2;
1149 it->leveltable[height][0].index=0;
1152 if (height >= 1) it->leveltable[height][height-1].index=0;
1155 it->leveltable[height][1].index=1;
1156 if (height >= 2) it->leveltable[height][height-2].index=1;
1159 for (i=0; i<height; i++) {
1160 it->leveltable[height][i].value =
1161 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1167 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1169 *a0 = (*a0 * a1) & 0xffffffffu;
1170 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1173 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1175 unsigned int a1 = *a, c1 = *c;
1181 rnd_combine(a, c, a1, c1);
1183 rnd_combine(&a1, &c1, a1, c1);
1187 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1189 rnd_seek_ac(&a, &c, dist);
1193 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1195 float *ps=it->rx_signal + start;
1196 float *pe=it->rx_signal + end;
1198 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1200 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1202 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1205 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1206 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1211 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1213 analogtv_input *inp=rec->input;
1214 float *ps=it->rx_signal + start;
1215 float *pe=it->rx_signal + end;
1217 signed char *ss=&inp->signal[0][0];
1218 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1219 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1222 float level=rec->level;
1223 float hfloss=rec->hfloss;
1224 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1227 const float noise_decay = 0.99995f;
1228 float noise_ampl = 1.3f * powf(noise_decay, start);
1233 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1235 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1237 /* Do a big noisy transition. We can make the transition noise of
1238 high constant strength regardless of signal strength.
1240 There are two separate state machines. here, One is the noise
1241 process and the other is the
1243 We don't bother with the FIR filter here
1246 float sig0=(float)s[0];
1247 float noise = ((int)fastrnd-(int)0x7fffffff) * (50.0f/(float)0x7fffffff);
1248 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1250 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1252 noise_ampl *= noise_decay;
1261 for (i=1; i<5; i++) {
1264 s2 += ANALOGTV_SIGNAL_LEN;
1265 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1269 assert(!((pe - p) % 4));
1271 float sig0,sig1,sig2,sig3,sigr;
1278 dp[0]=sig0+sig1+sig2+sig3;
1280 /* Get the video out signal, and add some ghosting, typical of RF
1281 monitor cables. This corresponds to a pretty long cable, but
1285 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1286 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1287 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1289 p[0] += (sig0+sigr + sig2*hfloss) * level;
1290 p[1] += (sig1+sigr + sig3*hfloss) * level;
1291 p[2] += (sig2+sigr + sig0*hfloss) * level;
1292 p[3] += (sig3+sigr + sig1*hfloss) * level;
1296 if (s>=se) s = ss + (s-se);
1302 static void analogtv_thread_add_signals(void *thread_raw)
1304 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1305 const analogtv *it = thread->it;
1307 unsigned subtotal_end;
1309 unsigned start = thread->signal_start;
1310 while(start != thread->signal_end)
1314 /* Work on 8 KB blocks; these should fit in L1. */
1315 /* (Though it doesn't seem to help much on my system.) */
1316 unsigned end = start + 2048;
1317 if(end > thread->signal_end)
1318 end = thread->signal_end;
1320 analogtv_init_signal (it, it->noiselevel, start, end);
1322 for (i = 0; i != it->rec_count; ++i) {
1323 analogtv_add_signal (it, it->recs[i], start, end,
1324 !i ? it->channel_change_cycles : 0);
1327 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1328 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1330 p = it->rx_signal + start;
1331 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1332 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1334 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1336 it->signal_subtotals[i] = sum;
1337 p += ANALOGTV_SUBTOTAL_LEN;
1344 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1345 int *ytop, int *ybot, unsigned *signal_offset)
1347 *slineno=lineno-ANALOGTV_TOP;
1348 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1349 it->useheight/2)*it->puheight) + it->useheight/2;
1350 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1351 it->useheight/2)*it->puheight) + it->useheight/2;
1353 int linesig=analogtv_line_signature(input,lineno)
1354 + it->hashnoise_times[lineno];
1356 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1357 it->line_hsync[lineno];
1359 if (*ytop==*ybot) return 0;
1360 if (*ybot<0 || *ytop>it->useheight) return 0;
1361 if (*ytop<0) *ytop=0;
1362 if (*ybot>it->useheight) *ybot=it->useheight;
1364 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1369 analogtv_blast_imagerow(const analogtv *it,
1370 float *rgbf, float *rgbf_end,
1375 char *level_copyfrom[3];
1376 int xrepl=it->xrepl;
1377 unsigned lineheight = ybot - ytop;
1378 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1379 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1381 for (y=ytop; y<ybot; y++) {
1382 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1383 unsigned line = y-ytop;
1385 int level=it->leveltable[lineheight][line].index;
1386 float levelmult=it->leveltable[lineheight][line].value;
1388 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1389 why standard graphics sw has to be fast, or else people will have to
1390 work around it and risk incompatibility. The quickdraw folks
1391 understood this. The other answer would be for X11 to have fewer
1392 formats for bitm.. oh, never mind. If neither of these cases work
1393 (they probably cover 99% of setups) it falls back on the Xlib
1396 if (level_copyfrom[level]) {
1397 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1400 level_copyfrom[level] = rowdata;
1404 else if (it->image->format==ZPixmap &&
1405 it->image->bits_per_pixel==32 &&
1406 sizeof(unsigned int)==4 &&
1407 it->image->byte_order==localbyteorder) {
1408 /* int is more likely to be 32 bits than long */
1409 unsigned int *pixelptr=(unsigned int *)rowdata;
1412 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1413 int ntscri=rpf[0]*levelmult;
1414 int ntscgi=rpf[1]*levelmult;
1415 int ntscbi=rpf[2]*levelmult;
1416 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1417 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1418 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1419 pix = (it->red_values[ntscri] |
1420 it->green_values[ntscgi] |
1421 it->blue_values[ntscbi]);
1425 if (xrepl>=3) pixelptr[2] = pix;
1430 else if (it->image->format==ZPixmap &&
1431 it->image->bits_per_pixel==16 &&
1432 sizeof(unsigned short)==2 &&
1433 float_extraction_works &&
1434 it->image->byte_order==localbyteorder) {
1435 unsigned short *pixelptr=(unsigned short *)rowdata;
1437 float_extract_t r1,g1,b1;
1440 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1441 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1442 r1.f=r2 * levelmult+float_low8_ofs;
1443 g1.f=g2 * levelmult+float_low8_ofs;
1444 b1.f=b2 * levelmult+float_low8_ofs;
1445 pix = (it->red_values[r1.i & 0x3ff] |
1446 it->green_values[g1.i & 0x3ff] |
1447 it->blue_values[b1.i & 0x3ff]);
1451 if (xrepl>=3) pixelptr[2] = pix;
1456 else if (it->image->format==ZPixmap &&
1457 it->image->bits_per_pixel==16 &&
1458 sizeof(unsigned short)==2 &&
1459 it->image->byte_order==localbyteorder) {
1460 unsigned short *pixelptr=(unsigned short *)rowdata;
1463 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1464 int r1=rpf[0] * levelmult;
1465 int g1=rpf[1] * levelmult;
1466 int b1=rpf[2] * levelmult;
1467 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1468 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1469 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1470 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1474 if (xrepl>=3) pixelptr[2] = pix;
1480 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1481 int ntscri=rpf[0]*levelmult;
1482 int ntscgi=rpf[1]*levelmult;
1483 int ntscbi=rpf[2]*levelmult;
1484 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1485 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1486 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1487 for (j=0; j<xrepl; j++) {
1488 XPutPixel(it->image, x*xrepl + j, y,
1489 it->red_values[ntscri] | it->green_values[ntscgi] |
1490 it->blue_values[ntscbi]);
1498 static void analogtv_thread_draw_lines(void *thread_raw)
1500 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1501 const analogtv *it = thread->it;
1505 float *raw_rgb_start;
1507 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1509 if (! raw_rgb_start) return;
1511 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1513 for (lineno=ANALOGTV_TOP + thread->thread_id;
1514 lineno<ANALOGTV_BOT;
1515 lineno += it->threads.count) {
1518 int slineno, ytop, ybot;
1519 unsigned signal_offset;
1521 const float *signal;
1523 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1524 float *rgb_start, *rgb_end;
1530 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1532 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1536 signal = it->rx_signal + signal_offset;
1540 float bloomthisrow,shiftthisrow;
1541 float viswidth,middle;
1545 bloomthisrow = -10.0f * it->crtload[lineno];
1546 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1547 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1549 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1550 (0.7f+cosf(slineno*0.6f)));
1555 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1556 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1558 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1560 scw=it->subwidth*scanwidth;
1561 if (scw>it->subwidth) scw=it->usewidth;
1562 scl=it->subwidth/2 - scw/2;
1563 scr=it->subwidth/2 + scw/2;
1565 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1566 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1567 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1568 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1569 - it->squish_control)) *65536.0f);
1570 squishdiv=it->subwidth/15;
1572 rgb_start=raw_rgb_start+scl*3;
1573 rgb_end=raw_rgb_start+scr*3;
1575 assert(scanstart_i>=0);
1578 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1580 scanstart_i/65536.0f,
1581 squishright_i/65536.0f,
1588 for (y=ytop; y<ybot; y++) {
1589 int level=analogtv_level(it, y, ytop, ybot);
1590 float levelmult=analogtv_levelmult(it, level);
1591 float levelmult_y = levelmult * it->contrast_control
1592 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1593 float levelmult_iq = levelmult * 0.090f;
1595 analogtv_ntsc_to_yiq(it, lineno, signal,
1596 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1601 while (i<0 && x<it->usewidth) {
1602 XPutPixel(it->image, x, y, it->colors[0]);
1607 while (i<scanend_i && x<it->usewidth) {
1608 float pixfrac=(i&0xffff)/65536.0f;
1609 float invpixfrac=(1.0f-pixfrac);
1611 int yli,ili,qli,cmi;
1613 float interpy=(yiq[pati].y*invpixfrac
1614 + yiq[pati+1].y*pixfrac) * levelmult_y;
1615 float interpi=(yiq[pati].i*invpixfrac
1616 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1617 float interpq=(yiq[pati].q*invpixfrac
1618 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1620 yli = (int)(interpy * it->cmap_y_levels);
1621 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1622 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1624 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1626 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1628 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1630 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1633 if ((random()%65536)==0) {
1634 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1635 interpy, interpi, interpq,
1641 for (j=0; j<it->xrepl; j++) {
1642 XPutPixel(it->image, x, y,
1646 if (i >= squishright_i) {
1647 pixmultinc += pixmultinc/squishdiv;
1651 while (x<it->usewidth) {
1652 XPutPixel(it->image, x, y, it->colors[0]);
1658 analogtv_ntsc_to_yiq(it, lineno, signal,
1659 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1661 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1662 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1664 i=scanstart_i; rrp=rgb_start;
1665 while (i<0 && rrp!=rgb_end) {
1666 rrp[0]=rrp[1]=rrp[2]=0;
1670 while (i<scanend_i && rrp!=rgb_end) {
1671 float pixfrac=(i&0xffff)/65536.0f;
1672 float invpixfrac=1.0f-pixfrac;
1676 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1677 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1678 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1681 According to the NTSC spec, Y,I,Q are generated as:
1683 y=0.30 r + 0.59 g + 0.11 b
1684 i=0.60 r - 0.28 g - 0.32 b
1685 q=0.21 r - 0.52 g + 0.31 b
1687 So if you invert the implied 3x3 matrix you get what standard
1688 televisions implement with a bunch of resistors (or directly in the
1691 r = y + 0.948 i + 0.624 q
1692 g = y - 0.276 i - 0.639 q
1693 b = y - 1.105 i + 1.729 q
1696 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1697 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1698 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1706 if (i>=squishright_i) {
1707 pixmultinc += pixmultinc/squishdiv;
1708 pixbright += pixbright/squishdiv/2;
1713 while (rrp != rgb_end) {
1714 rrp[0]=rrp[1]=rrp[2]=0.0f;
1718 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1723 free(raw_rgb_start);
1727 analogtv_draw(analogtv *it, double noiselevel,
1728 const analogtv_reception *const *recs, unsigned rec_count)
1731 int /*bigloadchange,*/drawcount;
1733 int overall_top, overall_bot;
1735 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1739 it->rx_signal_level = noiselevel;
1740 for (i = 0; i != rec_count; ++i) {
1741 const analogtv_reception *rec = recs[i];
1742 double level = rec->level;
1743 analogtv_input *inp=rec->input;
1745 it->rx_signal_level =
1746 sqrt(it->rx_signal_level * it->rx_signal_level +
1747 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1748 rec->ghostfir[2] + rec->ghostfir[3]))));
1750 /* duplicate the first line into the Nth line to ease wraparound computation */
1751 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1752 ANALOGTV_H * sizeof(inp->signal[0][0]));
1755 analogtv_setup_frame(it);
1756 analogtv_set_demod(it);
1758 it->random0 = random();
1759 it->random1 = random();
1760 it->noiselevel = noiselevel;
1762 it->rec_count = rec_count;
1763 threadpool_run(&it->threads, analogtv_thread_add_signals);
1764 threadpool_wait(&it->threads);
1766 it->channel_change_cycles=0;
1768 /* rx_signal has an extra 2 lines at the end, where we copy the
1769 first 2 lines so we can index into it while only worrying about
1770 wraparound on a per-line level */
1771 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1773 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1775 /* Repeat for signal_subtotals. */
1777 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1778 &it->signal_subtotals[0],
1779 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1781 analogtv_sync(it); /* Requires the add_signals be complete. */
1784 /* if (it->hashnoise_on) baseload=0.5; */
1786 /*bigloadchange=1;*/
1788 it->crtload[ANALOGTV_TOP-1]=baseload;
1789 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1790 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1792 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1794 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1795 int slineno, ytop, ybot;
1796 unsigned signal_offset;
1797 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1800 if (lineno==it->shrinkpulse) {
1802 /*bigloadchange=1;*/
1807 if (it->hashnoise_rpm>0.0 &&
1810 (slineno<20 && it->flutter_horiz_desync) ||
1811 it->gaussiannoise_level>30 ||
1812 ((it->gaussiannoise_level>2.0 ||
1813 it->multipath) && random()%4) ||
1814 linesig != it->onscreen_signature[lineno])) {
1817 it->onscreen_signature[lineno] = linesig;
1822 Interpolate the 600-dotclock line into however many horizontal
1823 screen pixels we're using, and convert to RGB.
1825 We add some 'bloom', variations in the horizontal scan width with
1826 the amount of brightness, extremely common on period TV sets. They
1827 had a single oscillator which generated both the horizontal scan and
1828 (during the horizontal retrace interval) the high voltage for the
1829 electron beam. More brightness meant more load on the oscillator,
1830 which caused an decrease in horizontal deflection. Look for
1833 Also, the A2 did a bad job of generating horizontal sync pulses
1834 during the vertical blanking interval. This, and the fact that the
1835 horizontal frequency was a bit off meant that TVs usually went a bit
1836 out of sync during the vertical retrace, and the top of the screen
1837 would be bent a bit to the left or right. Look for (shiftthisrow).
1839 We also add a teeny bit of left overscan, just enough to be
1840 annoying, but you can still read the left column of text.
1842 We also simulate compression & brightening on the right side of the
1843 screen. Most TVs do this, but you don't notice because they overscan
1844 so it's off the right edge of the CRT. But the A2 video system used
1845 so much of the horizontal scan line that you had to crank the
1846 horizontal width down in order to not lose the right few characters,
1847 and you'd see the compression on the right edge. Associated with
1848 compression is brightening; since the electron beam was scanning
1849 slower, the same drive signal hit the phosphor harder. Look for
1850 (squishright_i) and (squishdiv).
1854 /* This used to be an int, I suspect by mistake. - Dave */
1861 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1862 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1863 for (i=0; i != frac; i++) {
1867 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1869 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1870 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1871 totsignal += it->signal_subtotals[i];
1874 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1875 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1876 for (i=0; i != frac; i++) {
1880 totsignal *= it->agclevel;
1881 ncl = 0.95f * it->crtload[lineno-1] +
1883 (totsignal-30000)/100000.0f +
1884 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1886 /*diff=ncl - it->crtload[lineno];*/
1887 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1888 it->crtload[lineno]=ncl;
1892 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1893 threadpool_wait(&it->threads);
1896 /* poor attempt at visible retrace */
1897 for (i=0; i<15; i++) {
1898 int ytop=(int)((i*it->useheight/15 -
1899 it->useheight/2)*puheight) + it->useheight/2;
1900 int ybot=(int)(((i+1)*it->useheight/15 -
1901 it->useheight/2)*puheight) + it->useheight/2;
1902 int div=it->usewidth*3/2;
1904 for (x=0; x<it->usewidth; x++) {
1905 y = ytop + (ybot-ytop)*x / div;
1906 if (y<0 || y>=it->useheight) continue;
1907 XPutPixel(it->image, x, y, 0xffffff);
1912 if (it->need_clear) {
1913 XClearWindow(it->dpy, it->window);
1918 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1919 the top of the next-after-the-last scan line. This is the same until
1920 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1923 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1924 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1926 if (overall_top<0) overall_top=0;
1927 if (overall_bot>it->useheight) overall_bot=it->useheight;
1929 if (overall_top>0) {
1930 XClearArea(it->dpy, it->window,
1931 it->screen_xo, it->screen_yo,
1932 it->usewidth, overall_top, 0);
1934 if (it->useheight > overall_bot) {
1935 XClearArea(it->dpy, it->window,
1936 it->screen_xo, it->screen_yo+overall_bot,
1937 it->usewidth, it->useheight-overall_bot, 0);
1940 if (overall_bot > overall_top) {
1942 #ifdef HAVE_XSHM_EXTENSION
1943 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1945 it->screen_xo, it->screen_yo+overall_top,
1946 it->usewidth, overall_bot - overall_top,
1950 XPutImage(it->dpy, it->window, it->gc, it->image,
1952 it->screen_xo, it->screen_yo+overall_top,
1953 it->usewidth, overall_bot - overall_top);
1962 gettimeofday(&tv,NULL);
1964 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1965 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1966 sprintf(buf, "FPS=%0.1f",fps);
1967 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1970 it->last_display_time=tv;
1976 analogtv_input_allocate()
1978 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1984 This takes a screen image and encodes it as a video camera would,
1985 including all the bandlimiting and YIQ modulation.
1986 This isn't especially tuned for speed.
1990 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1997 XColor col1[ANALOGTV_PIC_LEN];
1998 XColor col2[ANALOGTV_PIC_LEN];
1999 int multiq[ANALOGTV_PIC_LEN+4];
2000 int y_overscan=5; /* overscan this much top and bottom */
2001 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2003 img_w=pic_im->width;
2004 img_h=pic_im->height;
2006 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2007 double phase=90.0-90.0*i;
2009 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2012 for (y=0; y<y_scanlength; y++) {
2013 int picy1=(y*img_h)/y_scanlength;
2014 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2016 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2017 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2018 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2019 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2021 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2022 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2024 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2025 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2027 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2029 int filty,filti,filtq;
2032 y=0.30 r + 0.59 g + 0.11 b
2033 i=0.60 r - 0.28 g - 0.32 b
2034 q=0.21 r - 0.52 g + 0.31 b
2035 The coefficients below are in .4 format */
2037 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2038 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2039 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2040 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2041 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2042 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2044 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2045 with an extra zero at 3.5 MHz, from
2046 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2048 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2049 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2050 fyx[6] = (rawy * 1897) >> 16;
2051 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2052 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2053 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2054 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2057 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2058 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2060 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2061 fix[3] = (rawi * 1413) >> 16;
2062 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2063 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2064 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2067 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2068 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2070 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2071 fqx[3] = (rawq * 75) >> 16;
2072 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2073 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2074 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2078 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2079 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2080 if (composite>125) composite=125;
2081 if (composite<0) composite=0;
2082 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2090 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2093 int change=random()%ANALOGTV_V;
2094 unsigned int fastrnd=random();
2095 double hso=(int)(random()%1000)-500;
2096 int yofs=random()%ANALOGTV_V;
2099 for (y=change; y<ANALOGTV_V; y++) {
2100 int s2y=(y+yofs)%ANALOGTV_V;
2102 int noiselevel=60000 / (y-change+100);
2104 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2106 for (x=0; x<ANALOGTV_H; x++) {
2108 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2109 noise=(filt*noiselevel)>>16;
2110 newsig=s2->signal[s2y][x] + noise;
2111 if (newsig>120) newsig=120;
2112 if (newsig<0) newsig=0;
2113 it->signal[y][x]=newsig;
2123 if (it->hashnoise_times[lineno]) {
2124 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2126 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2128 double cur=frand(150.0)-20.0;
2129 int len=random()%15+3;
2130 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2131 for (i=0; i<len; i++) {
2132 double sig=signal[hnt];
2135 cur += frand(5.0)-5.0;
2136 maxampl = maxampl*0.9;
2147 analogtv_reception_update(analogtv_reception *rec)
2151 if (rec->multipath > 0.0) {
2152 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2153 rec->ghostfir2[i] +=
2154 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2156 if (random()%20==0) {
2157 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2158 = rec->multipath * (frand(0.08)-0.04);
2160 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2161 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2165 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2166 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2170 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2171 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2178 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2181 #include "images/6x10font.xbm"
2184 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2185 int w, int h, char *fontname)
2192 XWindowAttributes xgwa;
2197 XGetWindowAttributes (dpy, window, &xgwa);
2199 if (fontname && !strcmp (fontname, "6x10")) {
2201 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2202 (char *) font6x10_bits,
2206 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2208 XFreePixmap(dpy, text_pm);
2210 } else if (fontname) {
2212 font = XLoadQueryFont (dpy, fontname);
2214 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2218 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2220 memset(&gcv, 0, sizeof(gcv));
2224 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2226 XSetForeground(dpy, gc, 0);
2227 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2228 XSetForeground(dpy, gc, 1);
2229 for (i=0; i<256; i++) {
2231 int x=f->char_w*i+1;
2232 int y=f->char_h*8/10;
2233 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2235 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2238 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2239 256*f->char_w, f->char_h, -1, -1);
2242 XFreePixmap(dpy, text_pm);
2244 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2245 256*f->char_w, f->char_h, 8, 0);
2246 f->text_im->data = (char *)calloc(f->text_im->height,
2247 f->text_im->bytes_per_line);
2255 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2257 if (x<0 || x>=f->char_w) return 0;
2258 if (y<0 || y>=f->char_h) return 0;
2259 if (c<0 || c>=256) return 0;
2260 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2264 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2266 if (x<0 || x>=f->char_w) return;
2267 if (y<0 || y>=f->char_h) return;
2268 if (c<0 || c>=256) return;
2270 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2274 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2278 if (c<0 || c>=256) return;
2280 for (y=0; y<f->char_h; y++) {
2281 for (x=0; x<f->char_w; x++) {
2283 value=(*s==' ') ? 0 : 1;
2284 analogtv_font_set_pixel(f, c, x, y, value);
2291 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2294 for (i=0; i<4; i++) {
2295 double w=90.0*i + phase;
2296 double val=luma + chroma * (cos(3.1415926/180.0*w));
2297 if (val<0.0) val=0.0;
2298 if (val>127.0) val=127.0;
2304 analogtv_draw_solid(analogtv_input *input,
2305 int left, int right, int top, int bot,
2310 if (right-left<4) right=left+4;
2311 if (bot-top<1) bot=top+1;
2313 for (y=top; y<bot; y++) {
2314 for (x=left; x<right; x++) {
2315 input->signal[y][x] = ntsc[x&3];
2322 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2323 double left, double right, double top, double bot,
2324 double luma, double chroma, double phase)
2328 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2329 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2330 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2331 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2333 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2334 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2339 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2340 int c, int x, int y, int ntsc[4])
2342 int yc,xc,ys,xs,pix;
2344 for (yc=0; yc<f->char_h; yc++) {
2345 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2346 if (ys<0 || ys>=ANALOGTV_V) continue;
2348 for (xc=0; xc<f->char_w; xc++) {
2349 pix=analogtv_font_pixel(f, c, xc, yc);
2351 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2352 if (xs<0 || xs>=ANALOGTV_H) continue;
2354 input->signal[ys][xs] = ntsc[xs&3];
2363 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2364 char *s, int x, int y, int ntsc[4])
2367 analogtv_draw_char(input, f, *s, x, y, ntsc);
2374 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2375 char *s, int x, int y, int ntsc[4])
2377 int width=strlen(s) * f->char_w * 4;
2380 analogtv_draw_string(input, f, s, x, y, ntsc);
2384 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2385 0, 0, 0, 0, 0, 0, 0, 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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2388 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2389 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2390 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2394 Much of this function was adapted from logo.c
2397 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2398 const char * const *xpm, int left, int top)
2403 int ncolors, nbytes;
2406 int r; int g; int b;
2410 if (4 != sscanf ((const char *) *xpm,
2412 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2414 if (ncolors < 1 || ncolors > 255)
2416 if (nbytes != 1) /* a serious limitation */
2420 for (i = 0; i < ncolors; i++) {
2421 const char *line = *xpm;
2422 int colori = ((unsigned char)*line++)&0xff;
2427 while (*line == ' ' || *line == '\t')
2430 if (which != 'c' && which != 'm')
2432 while (*line == ' ' || *line == '\t')
2434 if (!strncasecmp(line, "None", 4))
2443 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2445 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2447 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2462 for (y=0; y<xpmh; y++) {
2463 const char *line = *xpm++;
2465 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2467 for (x=0; x<xpmw; x++) {
2468 int cbyte=((unsigned char)line[x])&0xff;
2471 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2473 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2474 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2475 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2482 for (i=0; i<4; i++) {
2483 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2484 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2487 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2488 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2489 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2490 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];