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"
80 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
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 close to a 4:3 or 16:9 ratio,
323 then scale the image to exactly fill the window.
325 Otherwise, center the image either horizontally or vertically,
326 letterboxing or pillarboxing (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;
332 float min_ratio = 4.0 / 3.0 * (1 - percent);
333 float max_ratio = 16.0 / 9.0 * (1 + percent);
335 float height_snap=0.025;
337 hlim = it->xgwa.height;
338 wlim = it->xgwa.width;
339 ratio = wlim / (float) hlim;
342 /* Fill the whole iPhone screen, even though that distorts the image. */
347 if (wlim < 266 || hlim < 200)
353 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
354 wlim, hlim, it->xgwa.width, it->xgwa.height,
355 min_ratio, ratio, max_ratio);
358 else if (ratio > min_ratio && ratio < max_ratio)
362 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
363 wlim, hlim, min_ratio, ratio, max_ratio);
366 else if (ratio >= max_ratio)
368 wlim = hlim*max_ratio;
371 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
372 wlim, hlim, it->xgwa.width, it->xgwa.height,
373 min_ratio, ratio, max_ratio);
376 else /* ratio <= min_ratio */
378 hlim = wlim/min_ratio;
381 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
382 wlim, hlim, it->xgwa.width, it->xgwa.height,
383 min_ratio, ratio, max_ratio);
388 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
389 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
395 /* Most times this doesn't change */
396 if (wlim != oldwidth || hlim != oldheight) {
401 it->xrepl=1+it->usewidth/640;
402 if (it->xrepl>2) it->xrepl=2;
403 it->subwidth=it->usewidth/it->xrepl;
405 analogtv_free_image(it);
406 analogtv_alloc_image(it);
409 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
410 it->screen_yo = (it->xgwa.height-it->useheight)/2;
415 analogtv_reconfigure(analogtv *it)
417 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
418 analogtv_configure(it);
421 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
422 #define ANALOGTV_SUBTOTAL_LEN 32
424 typedef struct analogtv_thread_s
428 size_t signal_start, signal_end;
431 #define SIGNAL_OFFSET(thread_id) \
432 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
434 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
437 analogtv_thread *thread = (analogtv_thread *)thread_raw;
440 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
441 thread->thread_id = thread_id;
443 align = thread_memory_alignment(thread->it->dpy) /
444 sizeof(thread->it->signal_subtotals[0]);
447 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
449 thread->signal_start = SIGNAL_OFFSET(thread_id);
450 thread->signal_end = thread_id + 1 == threads->count ?
451 ANALOGTV_SIGNAL_LEN :
452 SIGNAL_OFFSET(thread_id + 1);
457 static void analogtv_thread_destroy(void *thread_raw)
462 analogtv_allocate(Display *dpy, Window window)
464 static const struct threadpool_class cls = {
465 sizeof(analogtv_thread),
466 analogtv_thread_create,
467 analogtv_thread_destroy
473 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
477 it=(analogtv *)calloc(1,sizeof(analogtv));
481 it->signal_subtotals=NULL;
486 if (thread_malloc((void **)&it->rx_signal, dpy,
487 sizeof(it->rx_signal[0]) * rx_signal_len))
490 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
491 if (thread_malloc((void **)&it->signal_subtotals, dpy,
492 sizeof(it->signal_subtotals[0]) *
493 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
496 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
499 assert(it->threads.count);
505 #ifdef HAVE_XSHM_EXTENSION
511 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
513 it->screen=it->xgwa.screen;
514 it->colormap=it->xgwa.colormap;
515 it->visclass=it->xgwa.visual->class;
516 it->visbits=it->xgwa.visual->bits_per_rgb;
517 it->visdepth=it->xgwa.depth;
518 if (it->visclass == TrueColor || it->visclass == DirectColor) {
519 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
524 it->use_color=!mono_p;
526 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
528 it->use_color=!mono_p;
535 it->red_mask=it->xgwa.visual->red_mask;
536 it->green_mask=it->xgwa.visual->green_mask;
537 it->blue_mask=it->xgwa.visual->blue_mask;
538 it->red_shift=it->red_invprec=-1;
539 it->green_shift=it->green_invprec=-1;
540 it->blue_shift=it->blue_invprec=-1;
542 /* Is there a standard way to do this? Does this handle all cases? */
544 for (shift=0; shift<32; shift++) {
545 for (prec=1; prec<16 && prec<40-shift; prec++) {
546 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
547 if (it->red_shift<0 && mask==it->red_mask)
548 it->red_shift=shift, it->red_invprec=16-prec;
549 if (it->green_shift<0 && mask==it->green_mask)
550 it->green_shift=shift, it->green_invprec=16-prec;
551 if (it->blue_shift<0 && mask==it->blue_mask)
552 it->blue_shift=shift, it->blue_invprec=16-prec;
555 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
556 if (0) fprintf(stderr,"Can't figure out color space\n");
560 for (i=0; i<ANALOGTV_CV_MAX; i++) {
561 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
562 if (intensity>65535) intensity=65535;
563 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
564 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
565 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
570 gcv.background=get_pixel_resource(it->dpy, it->colormap,
571 "background", "Background");
573 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
574 XSetWindowBackground(it->dpy, it->window, gcv.background);
575 XClearWindow(dpy,window);
577 analogtv_configure(it);
583 if(it->threads.count)
584 threadpool_destroy(&it->threads);
585 thread_free(it->signal_subtotals);
586 thread_free(it->rx_signal);
593 analogtv_release(analogtv *it)
597 #ifdef HAVE_XSHM_EXTENSION
598 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
601 thread_free(it->image->data);
602 it->image->data = NULL;
603 XDestroyImage(it->image);
607 if (it->gc) XFreeGC(it->dpy, it->gc);
609 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
611 threadpool_destroy(&it->threads);
612 thread_free(it->rx_signal);
613 thread_free(it->signal_subtotals);
619 First generate the I and Q reference signals, which we'll multiply
620 the input signal by to accomplish the demodulation. Normally they
621 are shifted 33 degrees from the colorburst. I think this was convenient
622 for inductor-capacitor-vacuum tube implementation.
624 The tint control, FWIW, just adds a phase shift to the chroma signal,
625 and the color control controls the amplitude.
627 In text modes (colormode==0) the system disabled the color burst, and no
628 color was detected by the monitor.
630 freq_error gives a mismatch between the built-in oscillator and the
631 TV's colorbust. Some II Plus machines seemed to occasionally get
632 instability problems -- the crystal oscillator was a single
633 transistor if I remember correctly -- and the frequency would vary
634 enough that the tint would change across the width of the screen.
635 The left side would be in correct tint because it had just gotten
636 resynchronized with the color burst.
638 If we're using a colormap, set it up.
641 analogtv_set_demod(analogtv *it)
643 int y_levels=10,i_levels=5,q_levels=5;
646 In principle, we might be able to figure out how to adjust the
647 color map frame-by-frame to get some nice color bummage. But I'm
648 terrified of changing the color map because we'll get flashing.
650 I can hardly believe we still have to deal with colormaps. They're
651 like having NEAR PTRs: an enormous hassle for the programmer just
652 to save on memory. They should have been deprecated by 1995 or
656 if (it->use_cmap && !it->n_colors) {
659 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
665 for (yli=0; yli<y_levels; yli++) {
666 for (ili=0; ili<i_levels; ili++) {
667 for (qli=0; qli<q_levels; qli++) {
668 double interpy,interpi,interpq;
669 double levelmult=700.0;
673 interpy=100.0 * ((double)yli/y_levels);
674 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
675 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
677 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
678 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
679 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
681 if (r>65535) r=65535;
683 if (g>65535) g=65535;
685 if (b>65535) b=65535;
688 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
689 interpy, interpi, interpq,
697 if (!XAllocColor(it->dpy, it->colormap, &col)) {
698 if (q_levels > y_levels*4/12)
700 else if (i_levels > y_levels*5/12)
709 it->colors[it->n_colors++]=col.pixel;
714 it->cmap_y_levels=y_levels;
715 it->cmap_i_levels=i_levels;
716 it->cmap_q_levels=q_levels;
725 analogtv_line_signature(analogtv_input *input, int lineno)
728 char *origsignal=&input->signal[(lineno+input->vsync)
729 %ANALOGTV_V][input->line_hsync[lineno]];
733 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
735 hash = hash + (hash<<17) + c;
738 hash += input->line_hsync[lineno];
741 hash += input->hashnoise_times[lineno];
750 /* Here we model the analog circuitry of an NTSC television.
751 Basically, it splits the signal into 3 signals: Y, I and Q. Y
752 corresponds to luminance, and you get it by low-pass filtering the
753 input signal to below 3.57 MHz.
755 I and Q are the in-phase and quadrature components of the 3.57 MHz
756 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
757 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
758 resolution in some colors than others, the I component gets
759 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
760 is approximately orange-blue, and Q is roughly purple-green. See
761 http://www.ntsc-tv.com for details.
763 We actually do an awful lot to the signal here. I suspect it would
764 make sense to wrap them all up together by calculating impulse
765 response and doing FFT convolutions.
770 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
771 int start, int end, struct analogtv_yiq_s *it_yiq)
776 int phasecorr=(signal-it->rx_signal)&3;
777 struct analogtv_yiq_s *yiq;
779 float agclevel=it->agclevel;
780 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
781 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
786 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
787 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
788 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
789 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
791 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
794 double tint_i = -cos((103 + it->color_control)*3.1415926/180);
795 double tint_q = sin((103 + it->color_control)*3.1415926/180);
797 multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
798 multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
799 multiq2[2]=-multiq2[0];
800 multiq2[3]=-multiq2[1];
806 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
807 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
808 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
809 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
810 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
811 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
812 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
816 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
817 for (i=0; i<5; i++) dp[i]=0.0f;
820 assert(end < ANALOGTV_PIC_LEN+10);
822 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
823 for (i=0; i<24; i++) dp[i]=0.0;
824 for (i=start, yiq=it_yiq+start, sp=signal+start;
826 i++, dp--, yiq++, sp++) {
828 /* Now filter them. These are infinite impulse response filters
829 calculated by the script at
830 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
831 fixed-point integer DSP, son. No place for wimps. We do it in
832 integer because you can count on integer being faster on most
833 CPUs. We care about speed because we need to recalculate every
834 time we blink text, and when we spew random bytes into screen
835 memory. This is roughly 16.16 fixed point arithmetic, but we
836 scale some filter values up by a few bits to avoid some nasty
839 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
840 with an extra zero at 3.5 MHz, from
841 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
844 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
845 dp[8] = (+1.0f*(dp[6]+dp[0])
851 yiq->y = dp[8] + brightadd;
855 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
856 for (i=0; i<27; i++) dp[i]=0.0;
858 for (i=start, yiq=it_yiq+start, sp=signal+start;
860 i++, dp--, yiq++, sp++) {
863 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
864 1.5 MHz with an extra zero at 3.5 MHz, from
865 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
869 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
870 yiq->i=dp[8] = (dp[5] + dp[0]
871 +3.0f*(dp[4] + dp[1])
872 +4.0f*(dp[3] + dp[2])
873 -0.3333333333f * dp[10]);
875 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
876 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
877 +3.0f*(dp[16+4] + dp[16+1])
878 +4.0f*(dp[16+3] + dp[16+2])
879 -0.3333333333f * dp[24+2]);
882 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
883 yiq->i = yiq->q = 0.0f;
889 analogtv_setup_teletext(analogtv_input *input)
892 int teletext=ANALOGTV_BLACK_LEVEL;
894 /* Teletext goes in line 21. But I suspect there are other things
895 in the vertical retrace interval */
897 for (y=19; y<22; y++) {
898 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
900 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
902 input->signal[y][x]=teletext;
908 analogtv_setup_frame(analogtv *it)
914 if (it->flutter_horiz_desync) {
915 /* Horizontal sync during vertical sync instability. */
916 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
917 ((int)(random()&0xff)-0x80) *
918 ((int)(random()&0xff)-0x80) *
919 ((int)(random()&0xff)-0x80) * 0.000001;
922 for (i=0; i<ANALOGTV_V; i++) {
923 it->hashnoise_times[i]=0;
926 if (it->hashnoise_enable && !it->hashnoise_on) {
927 if (random()%10000==0) {
929 it->shrinkpulse=random()%ANALOGTV_V;
932 if (random()%1000==0) {
935 if (it->hashnoise_on) {
936 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
937 ((int)(random()%2000)-1000)*0.1;
939 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
940 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
942 if (it->hashnoise_rpm > 0.0) {
944 int hnc=it->hashnoise_counter; /* in 24.8 format */
946 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
947 hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
948 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
950 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
951 y=(hnc>>8)/ANALOGTV_H;
952 x=(hnc>>8)%ANALOGTV_H;
954 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
955 it->hashnoise_times[y]=x;
957 hnc += hni + (int)(random()%65536)-32768;
959 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
962 if (it->rx_signal_level != 0.0)
963 it->agclevel = 1.0/it->rx_signal_level;
968 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
969 printf(" %0.3f",it->ghostfir[i]);
971 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
976 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
981 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
983 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
984 vsync=lineno>=3 && lineno<7;
986 sig=input->signal[lineno];
988 i=ANALOGTV_SYNC_START;
990 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
991 while (i<ANALOGTV_H) sig[i++]=synclevel;
993 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
994 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
995 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
997 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1000 /* 9 cycles of colorburst */
1001 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1002 sig[i+1] += ANALOGTV_CB_LEVEL;
1003 sig[i+3] -= ANALOGTV_CB_LEVEL;
1010 analogtv_sync(analogtv *it)
1012 int cur_hsync=it->cur_hsync;
1013 int cur_vsync=it->cur_vsync;
1018 float cbfc=1.0f/128.0f;
1020 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1021 for (i=-32; i<32; i++) {
1022 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1023 sp = it->rx_signal + lineno*ANALOGTV_H;
1025 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1028 filt *= it->agclevel;
1030 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1032 if (osc >= 1.05f+0.0002f * filt) break;
1034 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1036 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1038 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1039 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1040 if (!lineno2) lineno2 = ANALOGTV_V;
1041 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1042 for (i=-8; i<8; i++) {
1043 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1044 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1046 if (osc >= 1.005f + 0.0001f*filt) break;
1048 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1051 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1052 ANALOGTV_H) % ANALOGTV_H;
1054 /* Now look for the colorburst, which is a few cycles after the H
1055 sync pulse, and store its phase.
1056 The colorburst is 9 cycles long, and we look at the middle 5
1061 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1062 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1063 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1064 sp[i]*it->agclevel*cbfc;
1072 for (i=0; i<4; i++) {
1073 tot += it->cb_phase[i]*it->cb_phase[i];
1075 cbgain = 32.0f/sqrtf(tot);
1077 for (i=0; i<4; i++) {
1078 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1083 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1085 it->cb_phase[0], it->cb_phase[1],
1086 it->cb_phase[2], it->cb_phase[3]);
1089 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1092 it->cur_hsync = cur_hsync;
1093 it->cur_vsync = cur_vsync;
1097 analogtv_levelmult(const analogtv *it, int level)
1099 static const double levelfac[3]={-7.5, 5.5, 24.5};
1100 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1104 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1108 if (y==ytop || y==ybot-1) level=0;
1109 else if (y==ytop+1 || y==ybot-2) level=1;
1112 else if (ybot-ytop>=5) {
1113 if (y==ytop || y==ybot-1) level=0;
1116 else if (ybot-ytop>=3) {
1117 if (y==ytop) level=0;
1127 The point of this stuff is to ensure that when useheight is not a
1128 multiple of VISLINES so that TV scan lines map to different numbers
1129 of vertical screen pixels, the total brightness of each scan line
1131 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1132 it interpolates extra black lines.
1136 analogtv_setup_levels(analogtv *it, double avgheight)
1139 static const double levelfac[3]={-7.5, 5.5, 24.5};
1141 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1143 for (i=0; i<height; i++) {
1144 it->leveltable[height][i].index = 2;
1148 it->leveltable[height][0].index=0;
1151 if (height >= 1) it->leveltable[height][height-1].index=0;
1154 it->leveltable[height][1].index=1;
1155 if (height >= 2) it->leveltable[height][height-2].index=1;
1158 for (i=0; i<height; i++) {
1159 it->leveltable[height][i].value =
1160 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1166 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1168 *a0 = (*a0 * a1) & 0xffffffffu;
1169 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1172 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1174 unsigned int a1 = *a, c1 = *c;
1180 rnd_combine(a, c, a1, c1);
1182 rnd_combine(&a1, &c1, a1, c1);
1186 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1188 rnd_seek_ac(&a, &c, dist);
1192 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1194 float *ps=it->rx_signal + start;
1195 float *pe=it->rx_signal + end;
1197 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1199 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1201 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1204 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1205 nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
1210 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1212 analogtv_input *inp=rec->input;
1213 float *ps=it->rx_signal + start;
1214 float *pe=it->rx_signal + end;
1216 signed char *ss=&inp->signal[0][0];
1217 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1218 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1221 float level=rec->level;
1222 float hfloss=rec->hfloss;
1223 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1226 const float noise_decay = 0.99995f;
1227 float noise_ampl = 1.3f * powf(noise_decay, start);
1232 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1234 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1236 /* Do a big noisy transition. We can make the transition noise of
1237 high constant strength regardless of signal strength.
1239 There are two separate state machines. here, One is the noise
1240 process and the other is the
1242 We don't bother with the FIR filter here
1245 float sig0=(float)s[0];
1246 float noise = ((int)fastrnd-(int)0x7fffffff) * (50.0f/(float)0x7fffffff);
1247 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1249 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1251 noise_ampl *= noise_decay;
1260 for (i=1; i<5; i++) {
1263 s2 += ANALOGTV_SIGNAL_LEN;
1264 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1268 assert(!((pe - p) % 4));
1270 float sig0,sig1,sig2,sig3,sigr;
1277 dp[0]=sig0+sig1+sig2+sig3;
1279 /* Get the video out signal, and add some ghosting, typical of RF
1280 monitor cables. This corresponds to a pretty long cable, but
1284 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1285 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1286 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1288 p[0] += (sig0+sigr + sig2*hfloss) * level;
1289 p[1] += (sig1+sigr + sig3*hfloss) * level;
1290 p[2] += (sig2+sigr + sig0*hfloss) * level;
1291 p[3] += (sig3+sigr + sig1*hfloss) * level;
1295 if (s>=se) s = ss + (s-se);
1301 static void analogtv_thread_add_signals(void *thread_raw)
1303 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1304 const analogtv *it = thread->it;
1306 unsigned subtotal_end;
1308 unsigned start = thread->signal_start;
1309 while(start != thread->signal_end)
1313 /* Work on 8 KB blocks; these should fit in L1. */
1314 /* (Though it doesn't seem to help much on my system.) */
1315 unsigned end = start + 2048;
1316 if(end > thread->signal_end)
1317 end = thread->signal_end;
1319 analogtv_init_signal (it, it->noiselevel, start, end);
1321 for (i = 0; i != it->rec_count; ++i) {
1322 analogtv_add_signal (it, it->recs[i], start, end,
1323 !i ? it->channel_change_cycles : 0);
1326 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1327 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1329 p = it->rx_signal + start;
1330 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1331 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1333 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1335 it->signal_subtotals[i] = sum;
1336 p += ANALOGTV_SUBTOTAL_LEN;
1343 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1344 int *ytop, int *ybot, unsigned *signal_offset)
1346 *slineno=lineno-ANALOGTV_TOP;
1347 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1348 it->useheight/2)*it->puheight) + it->useheight/2;
1349 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1350 it->useheight/2)*it->puheight) + it->useheight/2;
1352 int linesig=analogtv_line_signature(input,lineno)
1353 + it->hashnoise_times[lineno];
1355 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1356 it->line_hsync[lineno];
1358 if (*ytop==*ybot) return 0;
1359 if (*ybot<0 || *ytop>it->useheight) return 0;
1360 if (*ytop<0) *ytop=0;
1361 if (*ybot>it->useheight) *ybot=it->useheight;
1363 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1368 analogtv_blast_imagerow(const analogtv *it,
1369 float *rgbf, float *rgbf_end,
1374 char *level_copyfrom[3];
1375 int xrepl=it->xrepl;
1376 unsigned lineheight = ybot - ytop;
1377 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1378 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1380 for (y=ytop; y<ybot; y++) {
1381 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1382 unsigned line = y-ytop;
1384 int level=it->leveltable[lineheight][line].index;
1385 float levelmult=it->leveltable[lineheight][line].value;
1387 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1388 why standard graphics sw has to be fast, or else people will have to
1389 work around it and risk incompatibility. The quickdraw folks
1390 understood this. The other answer would be for X11 to have fewer
1391 formats for bitm.. oh, never mind. If neither of these cases work
1392 (they probably cover 99% of setups) it falls back on the Xlib
1395 if (level_copyfrom[level]) {
1396 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1399 level_copyfrom[level] = rowdata;
1403 else if (it->image->format==ZPixmap &&
1404 it->image->bits_per_pixel==32 &&
1405 sizeof(unsigned int)==4 &&
1406 it->image->byte_order==localbyteorder) {
1407 /* int is more likely to be 32 bits than long */
1408 unsigned int *pixelptr=(unsigned int *)rowdata;
1411 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1412 int ntscri=rpf[0]*levelmult;
1413 int ntscgi=rpf[1]*levelmult;
1414 int ntscbi=rpf[2]*levelmult;
1415 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1416 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1417 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1418 pix = (it->red_values[ntscri] |
1419 it->green_values[ntscgi] |
1420 it->blue_values[ntscbi]);
1424 if (xrepl>=3) pixelptr[2] = pix;
1429 else if (it->image->format==ZPixmap &&
1430 it->image->bits_per_pixel==16 &&
1431 sizeof(unsigned short)==2 &&
1432 float_extraction_works &&
1433 it->image->byte_order==localbyteorder) {
1434 unsigned short *pixelptr=(unsigned short *)rowdata;
1436 float_extract_t r1,g1,b1;
1439 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1440 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1441 r1.f=r2 * levelmult+float_low8_ofs;
1442 g1.f=g2 * levelmult+float_low8_ofs;
1443 b1.f=b2 * levelmult+float_low8_ofs;
1444 pix = (it->red_values[r1.i & 0x3ff] |
1445 it->green_values[g1.i & 0x3ff] |
1446 it->blue_values[b1.i & 0x3ff]);
1450 if (xrepl>=3) pixelptr[2] = pix;
1455 else if (it->image->format==ZPixmap &&
1456 it->image->bits_per_pixel==16 &&
1457 sizeof(unsigned short)==2 &&
1458 it->image->byte_order==localbyteorder) {
1459 unsigned short *pixelptr=(unsigned short *)rowdata;
1462 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1463 int r1=rpf[0] * levelmult;
1464 int g1=rpf[1] * levelmult;
1465 int b1=rpf[2] * levelmult;
1466 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1467 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1468 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1469 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1473 if (xrepl>=3) pixelptr[2] = pix;
1479 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1480 int ntscri=rpf[0]*levelmult;
1481 int ntscgi=rpf[1]*levelmult;
1482 int ntscbi=rpf[2]*levelmult;
1483 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1484 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1485 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1486 for (j=0; j<xrepl; j++) {
1487 XPutPixel(it->image, x*xrepl + j, y,
1488 it->red_values[ntscri] | it->green_values[ntscgi] |
1489 it->blue_values[ntscbi]);
1497 static void analogtv_thread_draw_lines(void *thread_raw)
1499 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1500 const analogtv *it = thread->it;
1504 float *raw_rgb_start;
1506 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1508 if (! raw_rgb_start) return;
1510 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1512 for (lineno=ANALOGTV_TOP + thread->thread_id;
1513 lineno<ANALOGTV_BOT;
1514 lineno += it->threads.count) {
1517 int slineno, ytop, ybot;
1518 unsigned signal_offset;
1520 const float *signal;
1522 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1523 float *rgb_start, *rgb_end;
1529 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1531 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1535 signal = it->rx_signal + signal_offset;
1539 float bloomthisrow,shiftthisrow;
1540 float viswidth,middle;
1544 bloomthisrow = -10.0f * it->crtload[lineno];
1545 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1546 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1548 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1549 (0.7f+cosf(slineno*0.6f)));
1554 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1555 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1557 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1559 scw=it->subwidth*scanwidth;
1560 if (scw>it->subwidth) scw=it->usewidth;
1561 scl=it->subwidth/2 - scw/2;
1562 scr=it->subwidth/2 + scw/2;
1564 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1565 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1566 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1567 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1568 - it->squish_control)) *65536.0f);
1569 squishdiv=it->subwidth/15;
1571 rgb_start=raw_rgb_start+scl*3;
1572 rgb_end=raw_rgb_start+scr*3;
1574 assert(scanstart_i>=0);
1577 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1579 scanstart_i/65536.0f,
1580 squishright_i/65536.0f,
1587 for (y=ytop; y<ybot; y++) {
1588 int level=analogtv_level(it, y, ytop, ybot);
1589 float levelmult=analogtv_levelmult(it, level);
1590 float levelmult_y = levelmult * it->contrast_control
1591 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1592 float levelmult_iq = levelmult * 0.090f;
1594 analogtv_ntsc_to_yiq(it, lineno, signal,
1595 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1600 while (i<0 && x<it->usewidth) {
1601 XPutPixel(it->image, x, y, it->colors[0]);
1606 while (i<scanend_i && x<it->usewidth) {
1607 float pixfrac=(i&0xffff)/65536.0f;
1608 float invpixfrac=(1.0f-pixfrac);
1610 int yli,ili,qli,cmi;
1612 float interpy=(yiq[pati].y*invpixfrac
1613 + yiq[pati+1].y*pixfrac) * levelmult_y;
1614 float interpi=(yiq[pati].i*invpixfrac
1615 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1616 float interpq=(yiq[pati].q*invpixfrac
1617 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1619 yli = (int)(interpy * it->cmap_y_levels);
1620 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1621 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1623 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1625 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1627 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1629 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1632 if ((random()%65536)==0) {
1633 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1634 interpy, interpi, interpq,
1640 for (j=0; j<it->xrepl; j++) {
1641 XPutPixel(it->image, x, y,
1645 if (i >= squishright_i) {
1646 pixmultinc += pixmultinc/squishdiv;
1650 while (x<it->usewidth) {
1651 XPutPixel(it->image, x, y, it->colors[0]);
1657 analogtv_ntsc_to_yiq(it, lineno, signal,
1658 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1660 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1661 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1663 i=scanstart_i; rrp=rgb_start;
1664 while (i<0 && rrp!=rgb_end) {
1665 rrp[0]=rrp[1]=rrp[2]=0;
1669 while (i<scanend_i && rrp!=rgb_end) {
1670 float pixfrac=(i&0xffff)/65536.0f;
1671 float invpixfrac=1.0f-pixfrac;
1675 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1676 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1677 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1680 According to the NTSC spec, Y,I,Q are generated as:
1682 y=0.30 r + 0.59 g + 0.11 b
1683 i=0.60 r - 0.28 g - 0.32 b
1684 q=0.21 r - 0.52 g + 0.31 b
1686 So if you invert the implied 3x3 matrix you get what standard
1687 televisions implement with a bunch of resistors (or directly in the
1690 r = y + 0.948 i + 0.624 q
1691 g = y - 0.276 i - 0.639 q
1692 b = y - 1.105 i + 1.729 q
1695 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1696 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1697 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1705 if (i>=squishright_i) {
1706 pixmultinc += pixmultinc/squishdiv;
1707 pixbright += pixbright/squishdiv/2;
1712 while (rrp != rgb_end) {
1713 rrp[0]=rrp[1]=rrp[2]=0.0f;
1717 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1722 free(raw_rgb_start);
1726 analogtv_draw(analogtv *it, double noiselevel,
1727 const analogtv_reception *const *recs, unsigned rec_count)
1730 int /*bigloadchange,*/drawcount;
1732 int overall_top, overall_bot;
1734 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1738 it->rx_signal_level = noiselevel;
1739 for (i = 0; i != rec_count; ++i) {
1740 const analogtv_reception *rec = recs[i];
1741 double level = rec->level;
1742 analogtv_input *inp=rec->input;
1744 it->rx_signal_level =
1745 sqrt(it->rx_signal_level * it->rx_signal_level +
1746 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1747 rec->ghostfir[2] + rec->ghostfir[3]))));
1749 /* duplicate the first line into the Nth line to ease wraparound computation */
1750 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1751 ANALOGTV_H * sizeof(inp->signal[0][0]));
1754 analogtv_setup_frame(it);
1755 analogtv_set_demod(it);
1757 it->random0 = random();
1758 it->random1 = random();
1759 it->noiselevel = noiselevel;
1761 it->rec_count = rec_count;
1762 threadpool_run(&it->threads, analogtv_thread_add_signals);
1763 threadpool_wait(&it->threads);
1765 it->channel_change_cycles=0;
1767 /* rx_signal has an extra 2 lines at the end, where we copy the
1768 first 2 lines so we can index into it while only worrying about
1769 wraparound on a per-line level */
1770 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1772 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1774 /* Repeat for signal_subtotals. */
1776 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1777 &it->signal_subtotals[0],
1778 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1780 analogtv_sync(it); /* Requires the add_signals be complete. */
1783 /* if (it->hashnoise_on) baseload=0.5; */
1785 /*bigloadchange=1;*/
1787 it->crtload[ANALOGTV_TOP-1]=baseload;
1788 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1789 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1791 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1793 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1794 int slineno, ytop, ybot;
1795 unsigned signal_offset;
1796 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1799 if (lineno==it->shrinkpulse) {
1801 /*bigloadchange=1;*/
1806 if (it->hashnoise_rpm>0.0 &&
1809 (slineno<20 && it->flutter_horiz_desync) ||
1810 it->gaussiannoise_level>30 ||
1811 ((it->gaussiannoise_level>2.0 ||
1812 it->multipath) && random()%4) ||
1813 linesig != it->onscreen_signature[lineno])) {
1816 it->onscreen_signature[lineno] = linesig;
1821 Interpolate the 600-dotclock line into however many horizontal
1822 screen pixels we're using, and convert to RGB.
1824 We add some 'bloom', variations in the horizontal scan width with
1825 the amount of brightness, extremely common on period TV sets. They
1826 had a single oscillator which generated both the horizontal scan and
1827 (during the horizontal retrace interval) the high voltage for the
1828 electron beam. More brightness meant more load on the oscillator,
1829 which caused an decrease in horizontal deflection. Look for
1832 Also, the A2 did a bad job of generating horizontal sync pulses
1833 during the vertical blanking interval. This, and the fact that the
1834 horizontal frequency was a bit off meant that TVs usually went a bit
1835 out of sync during the vertical retrace, and the top of the screen
1836 would be bent a bit to the left or right. Look for (shiftthisrow).
1838 We also add a teeny bit of left overscan, just enough to be
1839 annoying, but you can still read the left column of text.
1841 We also simulate compression & brightening on the right side of the
1842 screen. Most TVs do this, but you don't notice because they overscan
1843 so it's off the right edge of the CRT. But the A2 video system used
1844 so much of the horizontal scan line that you had to crank the
1845 horizontal width down in order to not lose the right few characters,
1846 and you'd see the compression on the right edge. Associated with
1847 compression is brightening; since the electron beam was scanning
1848 slower, the same drive signal hit the phosphor harder. Look for
1849 (squishright_i) and (squishdiv).
1853 /* This used to be an int, I suspect by mistake. - Dave */
1860 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1861 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1862 for (i=0; i != frac; i++) {
1866 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1868 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1869 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1870 totsignal += it->signal_subtotals[i];
1873 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1874 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1875 for (i=0; i != frac; i++) {
1879 totsignal *= it->agclevel;
1880 ncl = 0.95f * it->crtload[lineno-1] +
1882 (totsignal-30000)/100000.0f +
1883 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1885 /*diff=ncl - it->crtload[lineno];*/
1886 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1887 it->crtload[lineno]=ncl;
1891 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1892 threadpool_wait(&it->threads);
1895 /* poor attempt at visible retrace */
1896 for (i=0; i<15; i++) {
1897 int ytop=(int)((i*it->useheight/15 -
1898 it->useheight/2)*puheight) + it->useheight/2;
1899 int ybot=(int)(((i+1)*it->useheight/15 -
1900 it->useheight/2)*puheight) + it->useheight/2;
1901 int div=it->usewidth*3/2;
1903 for (x=0; x<it->usewidth; x++) {
1904 y = ytop + (ybot-ytop)*x / div;
1905 if (y<0 || y>=it->useheight) continue;
1906 XPutPixel(it->image, x, y, 0xffffff);
1911 if (it->need_clear) {
1912 XClearWindow(it->dpy, it->window);
1917 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1918 the top of the next-after-the-last scan line. This is the same until
1919 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1922 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1923 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1925 if (overall_top<0) overall_top=0;
1926 if (overall_bot>it->useheight) overall_bot=it->useheight;
1928 if (overall_top>0) {
1929 XClearArea(it->dpy, it->window,
1930 it->screen_xo, it->screen_yo,
1931 it->usewidth, overall_top, 0);
1933 if (it->useheight > overall_bot) {
1934 XClearArea(it->dpy, it->window,
1935 it->screen_xo, it->screen_yo+overall_bot,
1936 it->usewidth, it->useheight-overall_bot, 0);
1939 if (overall_bot > overall_top) {
1941 #ifdef HAVE_XSHM_EXTENSION
1942 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1944 it->screen_xo, it->screen_yo+overall_top,
1945 it->usewidth, overall_bot - overall_top,
1949 XPutImage(it->dpy, it->window, it->gc, it->image,
1951 it->screen_xo, it->screen_yo+overall_top,
1952 it->usewidth, overall_bot - overall_top);
1961 gettimeofday(&tv,NULL);
1963 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1964 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1965 sprintf(buf, "FPS=%0.1f",fps);
1966 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1969 it->last_display_time=tv;
1975 analogtv_input_allocate()
1977 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
1983 This takes a screen image and encodes it as a video camera would,
1984 including all the bandlimiting and YIQ modulation.
1985 This isn't especially tuned for speed.
1989 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
1996 XColor col1[ANALOGTV_PIC_LEN];
1997 XColor col2[ANALOGTV_PIC_LEN];
1998 int multiq[ANALOGTV_PIC_LEN+4];
1999 int y_overscan=5; /* overscan this much top and bottom */
2000 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2002 img_w=pic_im->width;
2003 img_h=pic_im->height;
2005 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2006 double phase=90.0-90.0*i;
2008 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2011 for (y=0; y<y_scanlength; y++) {
2012 int picy1=(y*img_h)/y_scanlength;
2013 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2015 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2016 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2017 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2018 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2020 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2021 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2023 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2024 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2026 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2028 int filty,filti,filtq;
2031 y=0.30 r + 0.59 g + 0.11 b
2032 i=0.60 r - 0.28 g - 0.32 b
2033 q=0.21 r - 0.52 g + 0.31 b
2034 The coefficients below are in .4 format */
2036 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2037 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2038 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2039 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2040 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2041 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2043 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2044 with an extra zero at 3.5 MHz, from
2045 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2047 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2048 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2049 fyx[6] = (rawy * 1897) >> 16;
2050 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2051 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2052 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2053 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2056 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2057 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2059 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2060 fix[3] = (rawi * 1413) >> 16;
2061 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2062 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2063 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2066 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2067 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2069 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2070 fqx[3] = (rawq * 75) >> 16;
2071 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2072 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2073 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2077 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2078 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2079 if (composite>125) composite=125;
2080 if (composite<0) composite=0;
2081 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2089 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2092 int change=random()%ANALOGTV_V;
2093 unsigned int fastrnd=random();
2094 double hso=(int)(random()%1000)-500;
2095 int yofs=random()%ANALOGTV_V;
2098 for (y=change; y<ANALOGTV_V; y++) {
2099 int s2y=(y+yofs)%ANALOGTV_V;
2101 int noiselevel=60000 / (y-change+100);
2103 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2105 for (x=0; x<ANALOGTV_H; x++) {
2107 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2108 noise=(filt*noiselevel)>>16;
2109 newsig=s2->signal[s2y][x] + noise;
2110 if (newsig>120) newsig=120;
2111 if (newsig<0) newsig=0;
2112 it->signal[y][x]=newsig;
2122 if (it->hashnoise_times[lineno]) {
2123 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2125 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2127 double cur=frand(150.0)-20.0;
2128 int len=random()%15+3;
2129 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2130 for (i=0; i<len; i++) {
2131 double sig=signal[hnt];
2134 cur += frand(5.0)-5.0;
2135 maxampl = maxampl*0.9;
2146 analogtv_reception_update(analogtv_reception *rec)
2150 if (rec->multipath > 0.0) {
2151 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2152 rec->ghostfir2[i] +=
2153 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2155 if (random()%20==0) {
2156 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2157 = rec->multipath * (frand(0.08)-0.04);
2159 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2160 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2164 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2165 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2169 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2170 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2177 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2180 #include "images/6x10font.xbm"
2183 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2184 int w, int h, char *fontname)
2191 XWindowAttributes xgwa;
2196 XGetWindowAttributes (dpy, window, &xgwa);
2198 if (fontname && !strcmp (fontname, "6x10")) {
2200 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2201 (char *) font6x10_bits,
2205 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2207 XFreePixmap(dpy, text_pm);
2209 } else if (fontname) {
2211 font = XLoadQueryFont (dpy, fontname);
2213 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2217 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2219 memset(&gcv, 0, sizeof(gcv));
2223 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2225 XSetForeground(dpy, gc, 0);
2226 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2227 XSetForeground(dpy, gc, 1);
2228 for (i=0; i<256; i++) {
2230 int x=f->char_w*i+1;
2231 int y=f->char_h*8/10;
2232 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2234 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2237 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2238 256*f->char_w, f->char_h, -1, -1);
2241 XFreePixmap(dpy, text_pm);
2243 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2244 256*f->char_w, f->char_h, 8, 0);
2245 f->text_im->data = (char *)calloc(f->text_im->height,
2246 f->text_im->bytes_per_line);
2254 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2256 if (x<0 || x>=f->char_w) return 0;
2257 if (y<0 || y>=f->char_h) return 0;
2258 if (c<0 || c>=256) return 0;
2259 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2263 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2265 if (x<0 || x>=f->char_w) return;
2266 if (y<0 || y>=f->char_h) return;
2267 if (c<0 || c>=256) return;
2269 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2273 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2277 if (c<0 || c>=256) return;
2279 for (y=0; y<f->char_h; y++) {
2280 for (x=0; x<f->char_w; x++) {
2282 value=(*s==' ') ? 0 : 1;
2283 analogtv_font_set_pixel(f, c, x, y, value);
2290 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2293 for (i=0; i<4; i++) {
2294 double w=90.0*i + phase;
2295 double val=luma + chroma * (cos(3.1415926/180.0*w));
2296 if (val<0.0) val=0.0;
2297 if (val>127.0) val=127.0;
2303 analogtv_draw_solid(analogtv_input *input,
2304 int left, int right, int top, int bot,
2309 if (right-left<4) right=left+4;
2310 if (bot-top<1) bot=top+1;
2312 for (y=top; y<bot; y++) {
2313 for (x=left; x<right; x++) {
2314 input->signal[y][x] = ntsc[x&3];
2321 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2322 double left, double right, double top, double bot,
2323 double luma, double chroma, double phase)
2327 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2328 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2329 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2330 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2332 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2333 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2338 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2339 int c, int x, int y, int ntsc[4])
2341 int yc,xc,ys,xs,pix;
2343 for (yc=0; yc<f->char_h; yc++) {
2344 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2345 if (ys<0 || ys>=ANALOGTV_V) continue;
2347 for (xc=0; xc<f->char_w; xc++) {
2348 pix=analogtv_font_pixel(f, c, xc, yc);
2350 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2351 if (xs<0 || xs>=ANALOGTV_H) continue;
2353 input->signal[ys][xs] = ntsc[xs&3];
2362 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2363 char *s, int x, int y, int ntsc[4])
2366 analogtv_draw_char(input, f, *s, x, y, ntsc);
2373 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2374 char *s, int x, int y, int ntsc[4])
2376 int width=strlen(s) * f->char_w * 4;
2379 analogtv_draw_string(input, f, s, x, y, ntsc);
2383 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2384 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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 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,
2389 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2390 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2393 Much of this function was adapted from logo.c
2396 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2397 const char * const *xpm, int left, int top)
2402 int ncolors, nbytes;
2405 int r; int g; int b;
2409 if (4 != sscanf ((const char *) *xpm,
2411 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2413 if (ncolors < 1 || ncolors > 255)
2415 if (nbytes != 1) /* a serious limitation */
2419 for (i = 0; i < ncolors; i++) {
2420 const char *line = *xpm;
2421 int colori = ((unsigned char)*line++)&0xff;
2426 while (*line == ' ' || *line == '\t')
2429 if (which != 'c' && which != 'm')
2431 while (*line == ' ' || *line == '\t')
2433 if (!strncasecmp(line, "None", 4))
2442 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2444 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2446 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2461 for (y=0; y<xpmh; y++) {
2462 const char *line = *xpm++;
2464 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2466 for (x=0; x<xpmw; x++) {
2467 int cbyte=((unsigned char)line[x])&0xff;
2470 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2472 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2473 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2474 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2481 for (i=0; i<4; i++) {
2482 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2483 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2486 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2487 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2488 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2489 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];