1 /* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 This is the code for implementing something that looks like a conventional
15 analog TV set. It simulates the following characteristics of standard
18 - Realistic rendering of a composite video signal
19 - Compression & brightening on the right, as the scan gets truncated
20 because of saturation in the flyback transformer
21 - Blooming of the picture dependent on brightness
22 - Overscan, cutting off a few pixels on the left side.
23 - Colored text in mixed graphics/text modes
25 It's amazing how much it makes your high-end monitor look like at large
26 late-70s TV. All you need is to put a big "Solid State" logo in curly script
27 on it and you'd be set.
29 In DirectColor or TrueColor modes, it generates pixel values
30 directly from RGB values it calculates across each scan line. In
31 PseudoColor mode, it consider each possible pattern of 5 preceding
32 bit values in each possible position modulo 4 and allocates a color
33 for each. A few things, like the brightening on the right side as
34 the horizontal trace slows down, aren't done in PseudoColor.
36 I originally wrote it for the Apple ][ emulator, and generalized it
37 here for use with a rewrite of xteevee and possibly others.
39 A maxim of technology is that failures reveal underlying mechanism.
40 A good way to learn how something works is to push it to failure.
41 The way it fails will usually tell you a lot about how it works. The
42 corollary for this piece of software is that in order to emulate
43 realistic failures of a TV set, it has to work just like a TV set.
44 So there is lots of DSP-style emulation of analog circuitry for
45 things like color decoding, H and V sync following, and more. In
46 2003, computers are just fast enough to do this at television signal
47 rates. We use a 14 MHz sample rate here, so we can do on the order
48 of a couple hundred instructions per sample and keep a good frame
51 Trevor Blackwell <tlb@tlb.org>
55 2014-04-20, Dave Odell <dmo2118@gmail.com>:
56 API change: Folded analogtv_init_signal and *_add_signal into *_draw().
58 Replaced doubles with floats, including constants and transcendental functions.
62 /* 2015-02-27, Tomasz Sulej <tomeksul@gmail.com>:
63 - tint_control variable is used now
64 - removed unusable hashnoise code
69 #else /* !HAVE_COCOA */
70 # include <X11/Xlib.h>
71 # include <X11/Xutil.h>
78 #include "resources.h"
81 #include "grabscreen.h"
86 #if defined(DEBUG) && (defined(__linux) || defined(__FreeBSD__))
87 /* only works on linux + freebsd */
88 #include <machine/cpufunc.h>
90 #define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
91 #define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
92 #define DTIME dtimes[n_dtimes++]=rdtsc()
93 #define DTIME_SHOW(DIV) \
95 double _dtime_div=(DIV); \
96 printf("time/%.1f: ",_dtime_div); \
97 for (i=1; i<n_dtimes; i++) \
98 printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
105 #define DTIME_START do { } while (0)
106 #define DTIME do { } while (0)
107 #define DTIME_SHOW(DIV) do { } while (0)
112 #define FASTRND_A 1103515245
113 #define FASTRND_C 12345
114 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
116 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
117 int start, int end, struct analogtv_yiq_s *it_yiq);
119 static float puramp(const analogtv *it, float tc, float start, float over)
121 float pt=it->powerup-start;
123 if (pt<0.0f) return 0.0f;
124 if (pt>900.0f || pt/tc>8.0f) return 1.0f;
126 ret=(1.0f-expf(-pt/tc))*over;
127 if (ret>1.0f) return 1.0f;
132 There are actual standards for TV signals: NTSC and RS-170A describe the
133 system used in the US and Japan. Europe has slightly different systems, but
134 not different enough to make substantially different screensaver displays.
135 Sadly, the standards bodies don't do anything so useful as publish the spec on
136 the web. Best bets are:
138 http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
139 http://www.ntsc-tv.com/ntsc-index-02.htm
141 In DirectColor or TrueColor modes, it generates pixel values directly from RGB
142 values it calculates across each scan line. In PseudoColor mode, it consider
143 each possible pattern of 5 preceding bit values in each possible position
144 modulo 4 and allocates a color for each. A few things, like the brightening on
145 the right side as the horizontal trace slows down, aren't done in PseudoColor.
147 I'd like to add a bit of visible retrace, but it conflicts with being able to
148 bitcopy the image when fast scrolling. After another couple of CPU
149 generations, we could probably regenerate the whole image from scratch every
150 time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
154 /* localbyteorder is MSBFirst or LSBFirst */
155 static int localbyteorder;
156 static const double float_low8_ofs=8388608.0;
157 static int float_extraction_works;
169 unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
170 localbyteorder=*(char *)&localbyteorder_loc;
177 float_extraction_works=1;
178 for (i=0; i<256*4; i++) {
179 fe.f=float_low8_ofs+(double)i;
183 printf("Float extraction failed for %d => %d\n",i,ans);
185 float_extraction_works=0;
194 analogtv_set_defaults(analogtv *it, char *prefix)
198 sprintf(buf,"%sTVTint",prefix);
199 it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
200 sprintf(buf,"%sTVColor",prefix);
201 it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
202 sprintf(buf,"%sTVBrightness",prefix);
203 it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
204 sprintf(buf,"%sTVContrast",prefix);
205 it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
206 it->height_control = 1.0;
207 it->width_control = 1.0;
208 it->squish_control = 0.0;
213 it->hashnoise_enable=1;
215 it->horiz_desync=frand(10.0)-5.0;
216 it->squeezebottom=frand(5.0)-1.0;
219 printf("analogtv: prefix=%s\n",prefix);
220 printf(" use: shm=%d cmap=%d color=%d\n",
221 it->use_shm,it->use_cmap,it->use_color);
222 printf(" controls: tint=%g color=%g brightness=%g contrast=%g\n",
223 it->tint_control, it->color_control, it->brightness_control,
224 it->contrast_control);
225 /* printf(" freq_error %g: %g %d\n",
226 it->freq_error, it->freq_error_inc, it->flutter_tint); */
227 printf(" desync: %g %d\n",
228 it->horiz_desync, it->flutter_horiz_desync);
229 printf(" hashnoise rpm: %g\n",
231 printf(" vis: %d %d %d\n",
232 it->visclass, it->visbits, it->visdepth);
233 printf(" shift: %d-%d %d-%d %d-%d\n",
234 it->red_invprec,it->red_shift,
235 it->green_invprec,it->green_shift,
236 it->blue_invprec,it->blue_shift);
237 printf(" size: %d %d %d %d xrepl=%d\n",
238 it->usewidth, it->useheight,
239 it->screen_xo, it->screen_yo, it->xrepl);
241 printf(" ANALOGTV_V=%d\n",ANALOGTV_V);
242 printf(" ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
243 printf(" ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
244 printf(" ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
245 printf(" ANALOGTV_H=%d\n",ANALOGTV_H);
246 printf(" ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
247 printf(" ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
248 printf(" ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
249 printf(" ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
250 printf(" ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
251 printf(" ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
252 printf(" ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
253 printf(" ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
259 extern Bool mono_p; /* shoot me */
262 analogtv_free_image(analogtv *it)
266 #ifdef HAVE_XSHM_EXTENSION
267 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
270 thread_free(it->image->data);
271 it->image->data = NULL;
272 XDestroyImage(it->image);
279 analogtv_alloc_image(analogtv *it)
281 /* On failure, it->image is NULL. */
283 unsigned bits_per_pixel = get_bits_per_pixel(it->dpy, it->xgwa.depth);
284 unsigned align = thread_memory_alignment(it->dpy) * 8 - 1;
285 /* Width is in bits. */
286 unsigned width = (it->usewidth * bits_per_pixel + align) & ~align;
289 #ifdef HAVE_XSHM_EXTENSION
290 it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
292 width / bits_per_pixel, it->useheight);
294 if (!it->image) it->use_shm=0;
297 it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
298 it->usewidth, it->useheight, 8, width / 8);
300 if(thread_malloc((void **)&it->image->data, it->dpy,
301 it->image->height * it->image->bytes_per_line)) {
302 it->image->data = NULL;
303 XDestroyImage(it->image);
310 memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
312 /* Not enough memory. Maybe try a smaller window. */
313 fprintf(stderr, "analogtv: %s\n", strerror(ENOMEM));
319 analogtv_configure(analogtv *it)
321 int oldwidth=it->usewidth;
322 int oldheight=it->useheight;
323 int wlim,hlim,height_diff;
325 /* If the window is very small, don't let the image we draw get lower
326 than the actual TV resolution (266x200.)
328 If the aspect ratio of the window is close to a 4:3 or 16:9 ratio,
329 then scale the image to exactly fill the window.
331 Otherwise, center the image either horizontally or vertically,
332 letterboxing or pillarboxing (but not both).
334 If it's very close (2.5%) to a multiple of VISLINES, make it exact
335 For example, it maps 1024 => 1000.
337 float percent = 0.15;
338 float min_ratio = 4.0 / 3.0 * (1 - percent);
339 float max_ratio = 16.0 / 9.0 * (1 + percent);
341 float height_snap=0.025;
343 hlim = it->xgwa.height;
344 wlim = it->xgwa.width;
345 ratio = wlim / (float) hlim;
348 /* Fill the whole iPhone screen, even though that distorts the image. */
353 if (wlim < 266 || hlim < 200)
359 "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
360 wlim, hlim, it->xgwa.width, it->xgwa.height,
361 min_ratio, ratio, max_ratio);
364 else if (ratio > min_ratio && ratio < max_ratio)
368 "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
369 wlim, hlim, min_ratio, ratio, max_ratio);
372 else if (ratio >= max_ratio)
374 wlim = hlim*max_ratio;
377 "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
378 wlim, hlim, it->xgwa.width, it->xgwa.height,
379 min_ratio, ratio, max_ratio);
382 else /* ratio <= min_ratio */
384 hlim = wlim/min_ratio;
387 "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
388 wlim, hlim, it->xgwa.width, it->xgwa.height,
389 min_ratio, ratio, max_ratio);
394 height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
395 if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
401 /* Most times this doesn't change */
402 if (wlim != oldwidth || hlim != oldheight) {
407 it->xrepl=1+it->usewidth/640;
408 if (it->xrepl>2) it->xrepl=2;
409 it->subwidth=it->usewidth/it->xrepl;
411 analogtv_free_image(it);
412 analogtv_alloc_image(it);
415 it->screen_xo = (it->xgwa.width-it->usewidth)/2;
416 it->screen_yo = (it->xgwa.height-it->useheight)/2;
421 analogtv_reconfigure(analogtv *it)
423 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
424 analogtv_configure(it);
427 /* Can be any power-of-two <= 32. 16 a slightly better choice for 2-3 threads. */
428 #define ANALOGTV_SUBTOTAL_LEN 32
430 typedef struct analogtv_thread_s
434 size_t signal_start, signal_end;
437 #define SIGNAL_OFFSET(thread_id) \
438 ((ANALOGTV_SIGNAL_LEN * (thread_id) / threads->count) & align)
440 static int analogtv_thread_create(void *thread_raw, struct threadpool *threads,
443 analogtv_thread *thread = (analogtv_thread *)thread_raw;
446 thread->it = GET_PARENT_OBJ(analogtv, threads, threads);
447 thread->thread_id = thread_id;
449 align = thread_memory_alignment(thread->it->dpy) /
450 sizeof(thread->it->signal_subtotals[0]);
453 align = ~(align * ANALOGTV_SUBTOTAL_LEN - 1);
455 thread->signal_start = SIGNAL_OFFSET(thread_id);
456 thread->signal_end = thread_id + 1 == threads->count ?
457 ANALOGTV_SIGNAL_LEN :
458 SIGNAL_OFFSET(thread_id + 1);
463 static void analogtv_thread_destroy(void *thread_raw)
468 analogtv_allocate(Display *dpy, Window window)
470 static const struct threadpool_class cls = {
471 sizeof(analogtv_thread),
472 analogtv_thread_create,
473 analogtv_thread_destroy
479 const size_t rx_signal_len = ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H;
483 it=(analogtv *)calloc(1,sizeof(analogtv));
487 it->signal_subtotals=NULL;
492 if (thread_malloc((void **)&it->rx_signal, dpy,
493 sizeof(it->rx_signal[0]) * rx_signal_len))
496 assert(!(ANALOGTV_SIGNAL_LEN % ANALOGTV_SUBTOTAL_LEN));
497 if (thread_malloc((void **)&it->signal_subtotals, dpy,
498 sizeof(it->signal_subtotals[0]) *
499 (rx_signal_len / ANALOGTV_SUBTOTAL_LEN)))
502 if (threadpool_create(&it->threads, &cls, dpy, hardware_concurrency(dpy)))
505 assert(it->threads.count);
511 #ifdef HAVE_XSHM_EXTENSION
517 XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
519 it->screen=it->xgwa.screen;
520 it->colormap=it->xgwa.colormap;
521 it->visclass=it->xgwa.visual->class;
522 it->visbits=it->xgwa.visual->bits_per_rgb;
523 it->visdepth=it->xgwa.depth;
524 if (it->visclass == TrueColor || it->visclass == DirectColor) {
525 if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
530 it->use_color=!mono_p;
532 else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
534 it->use_color=!mono_p;
541 it->red_mask=it->xgwa.visual->red_mask;
542 it->green_mask=it->xgwa.visual->green_mask;
543 it->blue_mask=it->xgwa.visual->blue_mask;
544 it->red_shift=it->red_invprec=-1;
545 it->green_shift=it->green_invprec=-1;
546 it->blue_shift=it->blue_invprec=-1;
548 /* Is there a standard way to do this? Does this handle all cases? */
550 for (shift=0; shift<32; shift++) {
551 for (prec=1; prec<16 && prec<40-shift; prec++) {
552 unsigned long mask=(0xffffUL>>(16-prec)) << shift;
553 if (it->red_shift<0 && mask==it->red_mask)
554 it->red_shift=shift, it->red_invprec=16-prec;
555 if (it->green_shift<0 && mask==it->green_mask)
556 it->green_shift=shift, it->green_invprec=16-prec;
557 if (it->blue_shift<0 && mask==it->blue_mask)
558 it->blue_shift=shift, it->blue_invprec=16-prec;
561 if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
562 if (0) fprintf(stderr,"Can't figure out color space\n");
566 for (i=0; i<ANALOGTV_CV_MAX; i++) {
567 int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
568 if (intensity>65535) intensity=65535;
569 it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
570 it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
571 it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
576 gcv.background=get_pixel_resource(it->dpy, it->colormap,
577 "background", "Background");
579 it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
580 XSetWindowBackground(it->dpy, it->window, gcv.background);
581 XClearWindow(dpy,window);
583 analogtv_configure(it);
589 if(it->threads.count)
590 threadpool_destroy(&it->threads);
591 thread_free(it->signal_subtotals);
592 thread_free(it->rx_signal);
599 analogtv_release(analogtv *it)
603 #ifdef HAVE_XSHM_EXTENSION
604 destroy_xshm_image(it->dpy, it->image, &it->shm_info);
607 thread_free(it->image->data);
608 it->image->data = NULL;
609 XDestroyImage(it->image);
613 if (it->gc) XFreeGC(it->dpy, it->gc);
615 if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
617 threadpool_destroy(&it->threads);
618 thread_free(it->rx_signal);
619 thread_free(it->signal_subtotals);
625 First generate the I and Q reference signals, which we'll multiply
626 the input signal by to accomplish the demodulation. Normally they
627 are shifted 33 degrees from the colorburst. I think this was convenient
628 for inductor-capacitor-vacuum tube implementation.
630 The tint control, FWIW, just adds a phase shift to the chroma signal,
631 and the color control controls the amplitude.
633 In text modes (colormode==0) the system disabled the color burst, and no
634 color was detected by the monitor.
636 freq_error gives a mismatch between the built-in oscillator and the
637 TV's colorbust. Some II Plus machines seemed to occasionally get
638 instability problems -- the crystal oscillator was a single
639 transistor if I remember correctly -- and the frequency would vary
640 enough that the tint would change across the width of the screen.
641 The left side would be in correct tint because it had just gotten
642 resynchronized with the color burst.
644 If we're using a colormap, set it up.
647 analogtv_set_demod(analogtv *it)
649 int y_levels=10,i_levels=5,q_levels=5;
652 In principle, we might be able to figure out how to adjust the
653 color map frame-by-frame to get some nice color bummage. But I'm
654 terrified of changing the color map because we'll get flashing.
656 I can hardly believe we still have to deal with colormaps. They're
657 like having NEAR PTRs: an enormous hassle for the programmer just
658 to save on memory. They should have been deprecated by 1995 or
662 if (it->use_cmap && !it->n_colors) {
665 XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
671 for (yli=0; yli<y_levels; yli++) {
672 for (ili=0; ili<i_levels; ili++) {
673 for (qli=0; qli<q_levels; qli++) {
674 double interpy,interpi,interpq;
675 double levelmult=700.0;
679 interpy=100.0 * ((double)yli/y_levels);
680 interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
681 interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
683 r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
684 g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
685 b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
687 if (r>65535) r=65535;
689 if (g>65535) g=65535;
691 if (b>65535) b=65535;
694 printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
695 interpy, interpi, interpq,
703 if (!XAllocColor(it->dpy, it->colormap, &col)) {
704 if (q_levels > y_levels*4/12)
706 else if (i_levels > y_levels*5/12)
715 it->colors[it->n_colors++]=col.pixel;
720 it->cmap_y_levels=y_levels;
721 it->cmap_i_levels=i_levels;
722 it->cmap_q_levels=q_levels;
731 analogtv_line_signature(analogtv_input *input, int lineno)
734 char *origsignal=&input->signal[(lineno+input->vsync)
735 %ANALOGTV_V][input->line_hsync[lineno]];
739 for (i=0; i<ANALOGTV_PIC_LEN; i++) {
741 hash = hash + (hash<<17) + c;
744 hash += input->line_hsync[lineno];
747 hash += input->hashnoise_times[lineno];
756 /* Here we model the analog circuitry of an NTSC television.
757 Basically, it splits the signal into 3 signals: Y, I and Q. Y
758 corresponds to luminance, and you get it by low-pass filtering the
759 input signal to below 3.57 MHz.
761 I and Q are the in-phase and quadrature components of the 3.57 MHz
762 subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
763 sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
764 resolution in some colors than others, the I component gets
765 low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
766 is approximately orange-blue, and Q is roughly purple-green. See
767 http://www.ntsc-tv.com for details.
769 We actually do an awful lot to the signal here. I suspect it would
770 make sense to wrap them all up together by calculating impulse
771 response and doing FFT convolutions.
776 analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
777 int start, int end, struct analogtv_yiq_s *it_yiq)
782 int phasecorr=(signal-it->rx_signal)&3;
783 struct analogtv_yiq_s *yiq;
785 float agclevel=it->agclevel;
786 float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
787 float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
792 double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
793 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
794 double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
795 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
797 colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
800 multiq2[0] = (cb_i*it->tint_i - cb_q*it->tint_q) * it->color_control;
801 multiq2[1] = (cb_q*it->tint_i + cb_i*it->tint_q) * it->color_control;
802 multiq2[2]=-multiq2[0];
803 multiq2[3]=-multiq2[1];
809 printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
810 it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
811 printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
812 it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
813 it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
814 printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
815 multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
819 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
820 for (i=0; i<5; i++) dp[i]=0.0f;
823 assert(end < ANALOGTV_PIC_LEN+10);
825 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
826 for (i=0; i<24; i++) dp[i]=0.0;
827 for (i=start, yiq=it_yiq+start, sp=signal+start;
829 i++, dp--, yiq++, sp++) {
831 /* Now filter them. These are infinite impulse response filters
832 calculated by the script at
833 http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
834 fixed-point integer DSP, son. No place for wimps. We do it in
835 integer because you can count on integer being faster on most
836 CPUs. We care about speed because we need to recalculate every
837 time we blink text, and when we spew random bytes into screen
838 memory. This is roughly 16.16 fixed point arithmetic, but we
839 scale some filter values up by a few bits to avoid some nasty
842 /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
843 with an extra zero at 3.5 MHz, from
844 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
847 dp[0] = sp[0] * 0.0469904257251935f * agclevel;
848 dp[8] = (+1.0f*(dp[6]+dp[0])
854 yiq->y = dp[8] + brightadd;
858 dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
859 for (i=0; i<27; i++) dp[i]=0.0;
861 for (i=start, yiq=it_yiq+start, sp=signal+start;
863 i++, dp--, yiq++, sp++) {
866 /* Filter I and Q with a 3-pole low-pass Butterworth filter at
867 1.5 MHz with an extra zero at 3.5 MHz, from
868 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
872 dp[0] = sig*multiq2[i&3] * 0.0833333333333f;
873 yiq->i=dp[8] = (dp[5] + dp[0]
874 +3.0f*(dp[4] + dp[1])
875 +4.0f*(dp[3] + dp[2])
876 -0.3333333333f * dp[10]);
878 dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333f;
879 yiq->q=dp[24] = (dp[16+5] + dp[16+0]
880 +3.0f*(dp[16+4] + dp[16+1])
881 +4.0f*(dp[16+3] + dp[16+2])
882 -0.3333333333f * dp[24+2]);
885 for (i=start, yiq=it_yiq+start; i<end; i++, yiq++) {
886 yiq->i = yiq->q = 0.0f;
892 analogtv_setup_teletext(analogtv_input *input)
895 int teletext=ANALOGTV_BLACK_LEVEL;
897 /* Teletext goes in line 21. But I suspect there are other things
898 in the vertical retrace interval */
900 for (y=19; y<22; y++) {
901 for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
903 teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
905 input->signal[y][x]=teletext;
911 analogtv_setup_frame(analogtv *it)
917 if (it->flutter_horiz_desync) {
918 /* Horizontal sync during vertical sync instability. */
919 it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
920 ((int)(random()&0xff)-0x80) *
921 ((int)(random()&0xff)-0x80) *
922 ((int)(random()&0xff)-0x80) * 0.000001;
926 for (i=0; i<ANALOGTV_V; i++) {
927 it->hashnoise_times[i]=0;
931 /* let's leave it to process shrinkpulse */
932 if (it->hashnoise_enable && !it->hashnoise_on) {
933 if (random()%10000==0) {
935 it->shrinkpulse=random()%ANALOGTV_V;
938 if (random()%1000==0) {
942 #if 0 /* never used */
943 if (it->hashnoise_on) {
944 it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
945 ((int)(random()%2000)-1000)*0.1;
947 it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
948 if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
950 if (it->hashnoise_rpm > 0.0) {
953 int hnc=it->hashnoise_counter; /* in 24.8 format */
955 /* Convert rpm of a 16-pole motor into dots in 24.8 format */
956 hni_double = ANALOGTV_V * ANALOGTV_H * 256.0 /
957 (it->hashnoise_rpm * 16.0 / 60.0 / 60.0);
958 hni = (hni_double <= INT_MAX) ? (int)hni_double : INT_MAX;
960 while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
961 y=(hnc>>8)/ANALOGTV_H;
962 x=(hnc>>8)%ANALOGTV_H;
964 if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
965 it->hashnoise_times[y]=x;
967 /* hnc += hni + (int)(random()%65536)-32768; */
969 hnc += (int)(random()%65536)-32768;
970 if ((hnc >= 0) && (INT_MAX - hnc < hni)) break;
977 /* hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;*/
980 if (it->rx_signal_level != 0.0)
981 it->agclevel = 1.0/it->rx_signal_level;
986 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
987 printf(" %0.3f",it->ghostfir[i]);
989 printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
994 analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
999 int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
1001 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1002 vsync=lineno>=3 && lineno<7;
1004 sig=input->signal[lineno];
1006 i=ANALOGTV_SYNC_START;
1008 while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
1009 while (i<ANALOGTV_H) sig[i++]=synclevel;
1011 while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
1012 while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
1013 while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
1015 while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
1018 /* 9 cycles of colorburst */
1019 for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
1020 sig[i+1] += ANALOGTV_CB_LEVEL;
1021 sig[i+3] -= ANALOGTV_CB_LEVEL;
1028 analogtv_sync(analogtv *it)
1030 int cur_hsync=it->cur_hsync;
1031 int cur_vsync=it->cur_vsync;
1036 float cbfc=1.0f/128.0f;
1038 /* sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;*/
1039 for (i=-32; i<32; i++) {
1040 lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1041 sp = it->rx_signal + lineno*ANALOGTV_H;
1043 for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
1046 filt *= it->agclevel;
1048 osc = (float)(ANALOGTV_V+i)/(float)ANALOGTV_V;
1050 if (osc >= 1.05f+0.0002f * filt) break;
1052 cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
1054 for (lineno=0; lineno<ANALOGTV_V; lineno++) {
1056 if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
1057 unsigned lineno2 = (lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V;
1058 if (!lineno2) lineno2 = ANALOGTV_V;
1059 sp = it->rx_signal + lineno2*ANALOGTV_H + cur_hsync;
1060 for (i=-8; i<8; i++) {
1061 osc = (float)(ANALOGTV_H+i)/(float)ANALOGTV_H;
1062 filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
1064 if (osc >= 1.005f + 0.0001f*filt) break;
1066 cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
1069 it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
1070 ANALOGTV_H) % ANALOGTV_H;
1072 /* Now look for the colorburst, which is a few cycles after the H
1073 sync pulse, and store its phase.
1074 The colorburst is 9 cycles long, and we look at the middle 5
1079 sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
1080 for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
1081 it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0f-cbfc) +
1082 sp[i]*it->agclevel*cbfc;
1090 for (i=0; i<4; i++) {
1091 tot += it->cb_phase[i]*it->cb_phase[i];
1093 cbgain = 32.0f/sqrtf(tot);
1095 for (i=0; i<4; i++) {
1096 it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
1101 if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
1103 it->cb_phase[0], it->cb_phase[1],
1104 it->cb_phase[2], it->cb_phase[3]);
1107 /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
1110 it->cur_hsync = cur_hsync;
1111 it->cur_vsync = cur_vsync;
1115 analogtv_levelmult(const analogtv *it, int level)
1117 static const double levelfac[3]={-7.5, 5.5, 24.5};
1118 return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
1122 analogtv_level(const analogtv *it, int y, int ytop, int ybot)
1126 if (y==ytop || y==ybot-1) level=0;
1127 else if (y==ytop+1 || y==ybot-2) level=1;
1130 else if (ybot-ytop>=5) {
1131 if (y==ytop || y==ybot-1) level=0;
1134 else if (ybot-ytop>=3) {
1135 if (y==ytop) level=0;
1145 The point of this stuff is to ensure that when useheight is not a
1146 multiple of VISLINES so that TV scan lines map to different numbers
1147 of vertical screen pixels, the total brightness of each scan line
1149 ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
1150 it interpolates extra black lines.
1154 analogtv_setup_levels(analogtv *it, double avgheight)
1157 static const double levelfac[3]={-7.5, 5.5, 24.5};
1159 for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
1161 for (i=0; i<height; i++) {
1162 it->leveltable[height][i].index = 2;
1166 it->leveltable[height][0].index=0;
1169 if (height >= 1) it->leveltable[height][height-1].index=0;
1172 it->leveltable[height][1].index=1;
1173 if (height >= 2) it->leveltable[height][height-2].index=1;
1176 for (i=0; i<height; i++) {
1177 it->leveltable[height][i].value =
1178 (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
1184 static void rnd_combine(unsigned *a0, unsigned *c0, unsigned a1, unsigned c1)
1186 *a0 = (*a0 * a1) & 0xffffffffu;
1187 *c0 = (c1 + a1 * *c0) & 0xffffffffu;
1190 static void rnd_seek_ac(unsigned *a, unsigned *c, unsigned dist)
1192 unsigned int a1 = *a, c1 = *c;
1198 rnd_combine(a, c, a1, c1);
1200 rnd_combine(&a1, &c1, a1, c1);
1204 static unsigned int rnd_seek(unsigned a, unsigned c, unsigned rnd, unsigned dist)
1206 rnd_seek_ac(&a, &c, dist);
1210 static void analogtv_init_signal(const analogtv *it, double noiselevel, unsigned start, unsigned end)
1212 float *ps=it->rx_signal + start;
1213 float *pe=it->rx_signal + end;
1215 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random0, start);
1216 unsigned int fastrnd_offset;
1218 float noisemul = sqrt(noiselevel*150)/(float)0x7fffffff;
1220 fastrnd_offset = fastrnd - 0x7fffffff;
1221 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1224 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1225 fastrnd_offset = fastrnd - 0x7fffffff;
1226 nm1 = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * noisemul;
1231 static void analogtv_add_signal(const analogtv *it, const analogtv_reception *rec, unsigned start, unsigned end, int ec)
1233 analogtv_input *inp=rec->input;
1234 float *ps=it->rx_signal + start;
1235 float *pe=it->rx_signal + end;
1237 signed char *ss=&inp->signal[0][0];
1238 signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
1239 signed char *s=ss + ((start + (unsigned)rec->ofs) % ANALOGTV_SIGNAL_LEN);
1242 float level=rec->level;
1243 float hfloss=rec->hfloss;
1244 unsigned int fastrnd=rnd_seek(FASTRND_A, FASTRND_C, it->random1, start);
1247 const float noise_decay = 0.99995f;
1248 float noise_ampl = 1.3f * powf(noise_decay, start);
1253 /* assert((se-ss)%4==0 && (se-s)%4==0); */
1255 for (i = start; i < ec; i++) { /* Sometimes start > ec. */
1257 /* Do a big noisy transition. We can make the transition noise of
1258 high constant strength regardless of signal strength.
1260 There are two separate state machines. here, One is the noise
1261 process and the other is the
1263 We don't bother with the FIR filter here
1266 float sig0=(float)s[0];
1267 unsigned int fastrnd_offset = fastrnd - 0x7fffffff;
1268 float noise = (fastrnd_offset <= INT_MAX ? (int)fastrnd_offset : -1 - (int)(UINT_MAX - fastrnd_offset)) * (50.0f/(float)0x7fffffff);
1269 fastrnd = (fastrnd*FASTRND_A+FASTRND_C) & 0xffffffffu;
1271 p[0] += sig0 * level * (1.0f - noise_ampl) + noise * noise_ampl;
1273 noise_ampl *= noise_decay;
1282 for (i=1; i<5; i++) {
1285 s2 += ANALOGTV_SIGNAL_LEN;
1286 dp[i] = (float)((int)s2[0]+(int)s2[1]+(int)s2[2]+(int)s2[3]);
1290 assert(!((pe - p) % 4));
1292 float sig0,sig1,sig2,sig3,sigr;
1299 dp[0]=sig0+sig1+sig2+sig3;
1301 /* Get the video out signal, and add some ghosting, typical of RF
1302 monitor cables. This corresponds to a pretty long cable, but
1306 sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
1307 dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
1308 dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
1310 p[0] += (sig0+sigr + sig2*hfloss) * level;
1311 p[1] += (sig1+sigr + sig3*hfloss) * level;
1312 p[2] += (sig2+sigr + sig0*hfloss) * level;
1313 p[3] += (sig3+sigr + sig1*hfloss) * level;
1317 if (s>=se) s = ss + (s-se);
1323 static void analogtv_thread_add_signals(void *thread_raw)
1325 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1326 const analogtv *it = thread->it;
1328 unsigned subtotal_end;
1330 unsigned start = thread->signal_start;
1331 while(start != thread->signal_end)
1335 /* Work on 8 KB blocks; these should fit in L1. */
1336 /* (Though it doesn't seem to help much on my system.) */
1337 unsigned end = start + 2048;
1338 if(end > thread->signal_end)
1339 end = thread->signal_end;
1341 analogtv_init_signal (it, it->noiselevel, start, end);
1343 for (i = 0; i != it->rec_count; ++i) {
1344 analogtv_add_signal (it, it->recs[i], start, end,
1345 !i ? it->channel_change_cycles : 0);
1348 assert (!(start % ANALOGTV_SUBTOTAL_LEN));
1349 assert (!(end % ANALOGTV_SUBTOTAL_LEN));
1351 p = it->rx_signal + start;
1352 subtotal_end = end / ANALOGTV_SUBTOTAL_LEN;
1353 for (i = start / ANALOGTV_SUBTOTAL_LEN; i != subtotal_end; ++i) {
1355 for (j = 1; j != ANALOGTV_SUBTOTAL_LEN; ++j)
1357 it->signal_subtotals[i] = sum;
1358 p += ANALOGTV_SUBTOTAL_LEN;
1365 static int analogtv_get_line(const analogtv *it, int lineno, int *slineno,
1366 int *ytop, int *ybot, unsigned *signal_offset)
1368 *slineno=lineno-ANALOGTV_TOP;
1369 *ytop=(int)((*slineno*it->useheight/ANALOGTV_VISLINES -
1370 it->useheight/2)*it->puheight) + it->useheight/2;
1371 *ybot=(int)(((*slineno+1)*it->useheight/ANALOGTV_VISLINES -
1372 it->useheight/2)*it->puheight) + it->useheight/2;
1374 int linesig=analogtv_line_signature(input,lineno)
1375 + it->hashnoise_times[lineno];
1377 *signal_offset = ((lineno+it->cur_vsync+ANALOGTV_V) % ANALOGTV_V) * ANALOGTV_H +
1378 it->line_hsync[lineno];
1380 if (*ytop==*ybot) return 0;
1381 if (*ybot<0 || *ytop>it->useheight) return 0;
1382 if (*ytop<0) *ytop=0;
1383 if (*ybot>it->useheight) *ybot=it->useheight;
1385 if (*ybot > *ytop+ANALOGTV_MAX_LINEHEIGHT) *ybot=*ytop+ANALOGTV_MAX_LINEHEIGHT;
1390 analogtv_blast_imagerow(const analogtv *it,
1391 float *rgbf, float *rgbf_end,
1396 char *level_copyfrom[3];
1397 int xrepl=it->xrepl;
1398 unsigned lineheight = ybot - ytop;
1399 if (lineheight > ANALOGTV_MAX_LINEHEIGHT) lineheight = ANALOGTV_MAX_LINEHEIGHT;
1400 for (i=0; i<3; i++) level_copyfrom[i]=NULL;
1402 for (y=ytop; y<ybot; y++) {
1403 char *rowdata=it->image->data + y*it->image->bytes_per_line;
1404 unsigned line = y-ytop;
1406 int level=it->leveltable[lineheight][line].index;
1407 float levelmult=it->leveltable[lineheight][line].value;
1409 /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
1410 why standard graphics sw has to be fast, or else people will have to
1411 work around it and risk incompatibility. The quickdraw folks
1412 understood this. The other answer would be for X11 to have fewer
1413 formats for bitm.. oh, never mind. If neither of these cases work
1414 (they probably cover 99% of setups) it falls back on the Xlib
1417 if (level_copyfrom[level]) {
1418 memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
1421 level_copyfrom[level] = rowdata;
1425 else if (it->image->format==ZPixmap &&
1426 it->image->bits_per_pixel==32 &&
1427 sizeof(unsigned int)==4 &&
1428 it->image->byte_order==localbyteorder) {
1429 /* int is more likely to be 32 bits than long */
1430 unsigned int *pixelptr=(unsigned int *)rowdata;
1433 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1434 int ntscri=rpf[0]*levelmult;
1435 int ntscgi=rpf[1]*levelmult;
1436 int ntscbi=rpf[2]*levelmult;
1437 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1438 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1439 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1440 pix = (it->red_values[ntscri] |
1441 it->green_values[ntscgi] |
1442 it->blue_values[ntscbi]);
1446 if (xrepl>=3) pixelptr[2] = pix;
1451 else if (it->image->format==ZPixmap &&
1452 it->image->bits_per_pixel==16 &&
1453 sizeof(unsigned short)==2 &&
1454 float_extraction_works &&
1455 it->image->byte_order==localbyteorder) {
1456 unsigned short *pixelptr=(unsigned short *)rowdata;
1458 float_extract_t r1,g1,b1;
1461 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1462 r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
1463 r1.f=r2 * levelmult+float_low8_ofs;
1464 g1.f=g2 * levelmult+float_low8_ofs;
1465 b1.f=b2 * levelmult+float_low8_ofs;
1466 pix = (it->red_values[r1.i & 0x3ff] |
1467 it->green_values[g1.i & 0x3ff] |
1468 it->blue_values[b1.i & 0x3ff]);
1472 if (xrepl>=3) pixelptr[2] = pix;
1477 else if (it->image->format==ZPixmap &&
1478 it->image->bits_per_pixel==16 &&
1479 sizeof(unsigned short)==2 &&
1480 it->image->byte_order==localbyteorder) {
1481 unsigned short *pixelptr=(unsigned short *)rowdata;
1484 for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
1485 int r1=rpf[0] * levelmult;
1486 int g1=rpf[1] * levelmult;
1487 int b1=rpf[2] * levelmult;
1488 if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
1489 if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
1490 if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
1491 pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
1495 if (xrepl>=3) pixelptr[2] = pix;
1501 for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
1502 int ntscri=rpf[0]*levelmult;
1503 int ntscgi=rpf[1]*levelmult;
1504 int ntscbi=rpf[2]*levelmult;
1505 if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
1506 if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
1507 if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
1508 for (j=0; j<xrepl; j++) {
1509 XPutPixel(it->image, x*xrepl + j, y,
1510 it->red_values[ntscri] | it->green_values[ntscgi] |
1511 it->blue_values[ntscbi]);
1519 static void analogtv_thread_draw_lines(void *thread_raw)
1521 const analogtv_thread *thread = (analogtv_thread *)thread_raw;
1522 const analogtv *it = thread->it;
1526 float *raw_rgb_start;
1528 raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
1530 if (! raw_rgb_start) return;
1532 raw_rgb_end=raw_rgb_start+3*it->subwidth;
1534 for (lineno=ANALOGTV_TOP + thread->thread_id;
1535 lineno<ANALOGTV_BOT;
1536 lineno += it->threads.count) {
1539 int slineno, ytop, ybot;
1540 unsigned signal_offset;
1542 const float *signal;
1544 int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
1545 float *rgb_start, *rgb_end;
1551 struct analogtv_yiq_s yiq[ANALOGTV_PIC_LEN+10];
1553 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot,
1557 signal = it->rx_signal + signal_offset;
1561 float bloomthisrow,shiftthisrow;
1562 float viswidth,middle;
1566 bloomthisrow = -10.0f * it->crtload[lineno];
1567 if (bloomthisrow<-10.0f) bloomthisrow=-10.0f;
1568 if (bloomthisrow>2.0f) bloomthisrow=2.0f;
1570 shiftthisrow=it->horiz_desync * (expf(-0.17f*slineno) *
1571 (0.7f+cosf(slineno*0.6f)));
1576 viswidth=ANALOGTV_PIC_LEN * 0.79f - 5.0f*bloomthisrow;
1577 middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
1579 scanwidth=it->width_control * puramp(it, 0.5f, 0.3f, 1.0f);
1581 scw=it->subwidth*scanwidth;
1582 if (scw>it->subwidth) scw=it->usewidth;
1583 scl=it->subwidth/2 - scw/2;
1584 scr=it->subwidth/2 + scw/2;
1586 pixrate=(int)((viswidth*65536.0f*1.0f)/it->subwidth)/scanwidth;
1587 scanstart_i=(int)((middle-viswidth*0.5f)*65536.0f);
1588 scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
1589 squishright_i=(int)((middle+viswidth*(0.25f + 0.25f*puramp(it, 2.0f, 0.0f, 1.1f)
1590 - it->squish_control)) *65536.0f);
1591 squishdiv=it->subwidth/15;
1593 rgb_start=raw_rgb_start+scl*3;
1594 rgb_end=raw_rgb_start+scr*3;
1596 assert(scanstart_i>=0);
1599 if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
1601 scanstart_i/65536.0f,
1602 squishright_i/65536.0f,
1609 for (y=ytop; y<ybot; y++) {
1610 int level=analogtv_level(it, y, ytop, ybot);
1611 float levelmult=analogtv_levelmult(it, level);
1612 float levelmult_y = levelmult * it->contrast_control
1613 * puramp(it, 1.0f, 0.0f, 1.0f) / (0.5f+0.5f*it->puheight) * 0.070f;
1614 float levelmult_iq = levelmult * 0.090f;
1616 analogtv_ntsc_to_yiq(it, lineno, signal,
1617 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1622 while (i<0 && x<it->usewidth) {
1623 XPutPixel(it->image, x, y, it->colors[0]);
1628 while (i<scanend_i && x<it->usewidth) {
1629 float pixfrac=(i&0xffff)/65536.0f;
1630 float invpixfrac=(1.0f-pixfrac);
1632 int yli,ili,qli,cmi;
1634 float interpy=(yiq[pati].y*invpixfrac
1635 + yiq[pati+1].y*pixfrac) * levelmult_y;
1636 float interpi=(yiq[pati].i*invpixfrac
1637 + yiq[pati+1].i*pixfrac) * levelmult_iq;
1638 float interpq=(yiq[pati].q*invpixfrac
1639 + yiq[pati+1].q*pixfrac) * levelmult_iq;
1641 yli = (int)(interpy * it->cmap_y_levels);
1642 ili = (int)((interpi+0.5f) * it->cmap_i_levels);
1643 qli = (int)((interpq+0.5f) * it->cmap_q_levels);
1645 if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
1647 if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
1649 if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
1651 cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
1654 if ((random()%65536)==0) {
1655 printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
1656 interpy, interpi, interpq,
1662 for (j=0; j<it->xrepl; j++) {
1663 XPutPixel(it->image, x, y,
1667 if (i >= squishright_i) {
1668 pixmultinc += pixmultinc/squishdiv;
1672 while (x<it->usewidth) {
1673 XPutPixel(it->image, x, y, it->colors[0]);
1679 analogtv_ntsc_to_yiq(it, lineno, signal,
1680 (scanstart_i>>16)-10, (scanend_i>>16)+10, yiq);
1682 pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
1683 / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
1685 i=scanstart_i; rrp=rgb_start;
1686 while (i<0 && rrp!=rgb_end) {
1687 rrp[0]=rrp[1]=rrp[2]=0;
1691 while (i<scanend_i && rrp!=rgb_end) {
1692 float pixfrac=(i&0xffff)/65536.0f;
1693 float invpixfrac=1.0f-pixfrac;
1697 float interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
1698 float interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
1699 float interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
1702 According to the NTSC spec, Y,I,Q are generated as:
1704 y=0.30 r + 0.59 g + 0.11 b
1705 i=0.60 r - 0.28 g - 0.32 b
1706 q=0.21 r - 0.52 g + 0.31 b
1708 So if you invert the implied 3x3 matrix you get what standard
1709 televisions implement with a bunch of resistors (or directly in the
1712 r = y + 0.948 i + 0.624 q
1713 g = y - 0.276 i - 0.639 q
1714 b = y - 1.105 i + 1.729 q
1717 r=(interpy + 0.948f*interpi + 0.624f*interpq) * pixbright;
1718 g=(interpy - 0.276f*interpi - 0.639f*interpq) * pixbright;
1719 b=(interpy - 1.105f*interpi + 1.729f*interpq) * pixbright;
1727 if (i>=squishright_i) {
1728 pixmultinc += pixmultinc/squishdiv;
1729 pixbright += pixbright/squishdiv/2;
1734 while (rrp != rgb_end) {
1735 rrp[0]=rrp[1]=rrp[2]=0.0f;
1739 analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
1744 free(raw_rgb_start);
1748 analogtv_draw(analogtv *it, double noiselevel,
1749 const analogtv_reception *const *recs, unsigned rec_count)
1752 /* int bigloadchange,drawcount;*/
1754 int overall_top, overall_bot;
1756 /* AnalogTV isn't very interesting if there isn't enough RAM. */
1760 it->rx_signal_level = noiselevel;
1761 for (i = 0; i != rec_count; ++i) {
1762 const analogtv_reception *rec = recs[i];
1763 double level = rec->level;
1764 analogtv_input *inp=rec->input;
1766 it->rx_signal_level =
1767 sqrt(it->rx_signal_level * it->rx_signal_level +
1768 (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
1769 rec->ghostfir[2] + rec->ghostfir[3]))));
1771 /* duplicate the first line into the Nth line to ease wraparound computation */
1772 memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
1773 ANALOGTV_H * sizeof(inp->signal[0][0]));
1776 analogtv_setup_frame(it);
1777 analogtv_set_demod(it);
1779 it->random0 = random();
1780 it->random1 = random();
1781 it->noiselevel = noiselevel;
1783 it->rec_count = rec_count;
1784 threadpool_run(&it->threads, analogtv_thread_add_signals);
1785 threadpool_wait(&it->threads);
1787 it->channel_change_cycles=0;
1789 /* rx_signal has an extra 2 lines at the end, where we copy the
1790 first 2 lines so we can index into it while only worrying about
1791 wraparound on a per-line level */
1792 memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
1794 2*ANALOGTV_H*sizeof(it->rx_signal[0]));
1796 /* Repeat for signal_subtotals. */
1798 memcpy(&it->signal_subtotals[ANALOGTV_SIGNAL_LEN / ANALOGTV_SUBTOTAL_LEN],
1799 &it->signal_subtotals[0],
1800 (2*ANALOGTV_H/ANALOGTV_SUBTOTAL_LEN)*sizeof(it->signal_subtotals[0]));
1802 analogtv_sync(it); /* Requires the add_signals be complete. */
1805 /* if (it->hashnoise_on) baseload=0.5; */
1809 it->crtload[ANALOGTV_TOP-1]=baseload;
1810 it->puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
1811 (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
1813 analogtv_setup_levels(it, it->puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
1815 /* calculate tint once per frame */
1816 it->tint_i = -cos((103 + it->tint_control)*3.1415926/180);
1817 it->tint_q = sin((103 + it->tint_control)*3.1415926/180);
1819 for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
1820 int slineno, ytop, ybot;
1821 unsigned signal_offset;
1822 if (! analogtv_get_line(it, lineno, &slineno, &ytop, &ybot, &signal_offset))
1825 if (lineno==it->shrinkpulse) {
1827 /*bigloadchange=1;*/
1832 if (it->hashnoise_rpm>0.0 &&
1835 (slineno<20 && it->flutter_horiz_desync) ||
1836 it->gaussiannoise_level>30 ||
1837 ((it->gaussiannoise_level>2.0 ||
1838 it->multipath) && random()%4) ||
1839 linesig != it->onscreen_signature[lineno])) {
1842 it->onscreen_signature[lineno] = linesig;
1847 Interpolate the 600-dotclock line into however many horizontal
1848 screen pixels we're using, and convert to RGB.
1850 We add some 'bloom', variations in the horizontal scan width with
1851 the amount of brightness, extremely common on period TV sets. They
1852 had a single oscillator which generated both the horizontal scan and
1853 (during the horizontal retrace interval) the high voltage for the
1854 electron beam. More brightness meant more load on the oscillator,
1855 which caused an decrease in horizontal deflection. Look for
1858 Also, the A2 did a bad job of generating horizontal sync pulses
1859 during the vertical blanking interval. This, and the fact that the
1860 horizontal frequency was a bit off meant that TVs usually went a bit
1861 out of sync during the vertical retrace, and the top of the screen
1862 would be bent a bit to the left or right. Look for (shiftthisrow).
1864 We also add a teeny bit of left overscan, just enough to be
1865 annoying, but you can still read the left column of text.
1867 We also simulate compression & brightening on the right side of the
1868 screen. Most TVs do this, but you don't notice because they overscan
1869 so it's off the right edge of the CRT. But the A2 video system used
1870 so much of the horizontal scan line that you had to crank the
1871 horizontal width down in order to not lose the right few characters,
1872 and you'd see the compression on the right edge. Associated with
1873 compression is brightening; since the electron beam was scanning
1874 slower, the same drive signal hit the phosphor harder. Look for
1875 (squishright_i) and (squishdiv).
1879 /* This used to be an int, I suspect by mistake. - Dave */
1886 frac = signal_offset & (ANALOGTV_SUBTOTAL_LEN - 1);
1887 p = it->rx_signal + (signal_offset & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1888 for (i=0; i != frac; i++) {
1892 end0 = (signal_offset + ANALOGTV_PIC_LEN);
1894 end1 = end0 / ANALOGTV_SUBTOTAL_LEN;
1895 for (i=signal_offset / ANALOGTV_SUBTOTAL_LEN; i<end1; i++) {
1896 totsignal += it->signal_subtotals[i];
1899 frac = end0 & (ANALOGTV_SUBTOTAL_LEN - 1);
1900 p = it->rx_signal + (end0 & ~(ANALOGTV_SUBTOTAL_LEN - 1));
1901 for (i=0; i != frac; i++) {
1905 totsignal *= it->agclevel;
1906 ncl = 0.95f * it->crtload[lineno-1] +
1908 (totsignal-30000)/100000.0f +
1909 (slineno>184 ? (slineno-184)*(lineno-184)*0.001f * it->squeezebottom
1911 /*diff=ncl - it->crtload[lineno];*/
1912 /*bigloadchange = (diff>0.01 || diff<-0.01);*/
1913 it->crtload[lineno]=ncl;
1917 threadpool_run(&it->threads, analogtv_thread_draw_lines);
1918 threadpool_wait(&it->threads);
1921 /* poor attempt at visible retrace */
1922 for (i=0; i<15; i++) {
1923 int ytop=(int)((i*it->useheight/15 -
1924 it->useheight/2)*puheight) + it->useheight/2;
1925 int ybot=(int)(((i+1)*it->useheight/15 -
1926 it->useheight/2)*puheight) + it->useheight/2;
1927 int div=it->usewidth*3/2;
1929 for (x=0; x<it->usewidth; x++) {
1930 y = ytop + (ybot-ytop)*x / div;
1931 if (y<0 || y>=it->useheight) continue;
1932 XPutPixel(it->image, x, y, 0xffffff);
1937 if (it->need_clear) {
1938 XClearWindow(it->dpy, it->window);
1943 Subtle change: overall_bot was the bottom of the last scan line. Now it's
1944 the top of the next-after-the-last scan line. This is the same until
1945 the y-dimension is > 2400, note ANALOGTV_MAX_LINEHEIGHT.
1948 overall_top=(int)(it->useheight*(1-it->puheight)/2);
1949 overall_bot=(int)(it->useheight*(1+it->puheight)/2);
1951 if (overall_top<0) overall_top=0;
1952 if (overall_bot>it->useheight) overall_bot=it->useheight;
1954 if (overall_top>0) {
1955 XClearArea(it->dpy, it->window,
1956 it->screen_xo, it->screen_yo,
1957 it->usewidth, overall_top, 0);
1959 if (it->useheight > overall_bot) {
1960 XClearArea(it->dpy, it->window,
1961 it->screen_xo, it->screen_yo+overall_bot,
1962 it->usewidth, it->useheight-overall_bot, 0);
1965 if (overall_bot > overall_top) {
1967 #ifdef HAVE_XSHM_EXTENSION
1968 XShmPutImage(it->dpy, it->window, it->gc, it->image,
1970 it->screen_xo, it->screen_yo+overall_top,
1971 it->usewidth, overall_bot - overall_top,
1975 XPutImage(it->dpy, it->window, it->gc, it->image,
1977 it->screen_xo, it->screen_yo+overall_top,
1978 it->usewidth, overall_bot - overall_top);
1987 gettimeofday(&tv,NULL);
1989 fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
1990 + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
1991 sprintf(buf, "FPS=%0.1f",fps);
1992 XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
1995 it->last_display_time=tv;
2001 analogtv_input_allocate()
2003 analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
2009 This takes a screen image and encodes it as a video camera would,
2010 including all the bandlimiting and YIQ modulation.
2011 This isn't especially tuned for speed.
2015 analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
2022 XColor col1[ANALOGTV_PIC_LEN];
2023 XColor col2[ANALOGTV_PIC_LEN];
2024 int multiq[ANALOGTV_PIC_LEN+4];
2025 int y_overscan=5; /* overscan this much top and bottom */
2026 int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
2028 img_w=pic_im->width;
2029 img_h=pic_im->height;
2031 for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
2032 double phase=90.0-90.0*i;
2034 multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
2037 for (y=0; y<y_scanlength; y++) {
2038 int picy1=(y*img_h)/y_scanlength;
2039 int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
2041 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2042 int picx=(x*img_w)/ANALOGTV_PIC_LEN;
2043 col1[x].pixel=XGetPixel(pic_im, picx, picy1);
2044 col2[x].pixel=XGetPixel(pic_im, picx, picy2);
2046 XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
2047 XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
2049 for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
2050 for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
2052 for (x=0; x<ANALOGTV_PIC_LEN; x++) {
2054 int filty,filti,filtq;
2057 y=0.30 r + 0.59 g + 0.11 b
2058 i=0.60 r - 0.28 g - 0.32 b
2059 q=0.21 r - 0.52 g + 0.31 b
2060 The coefficients below are in .4 format */
2062 rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
2063 5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
2064 rawi=(10*col1[x].red - 4*col1[x].green - 5*col1[x].blue +
2065 10*col2[x].red - 4*col2[x].green - 5*col2[x].blue)>>7;
2066 rawq=( 3*col1[x].red - 8*col1[x].green + 5*col1[x].blue +
2067 3*col2[x].red - 8*col2[x].green + 5*col2[x].blue)>>7;
2069 /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
2070 with an extra zero at 3.5 MHz, from
2071 mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
2073 fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
2074 fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
2075 fyx[6] = (rawy * 1897) >> 16;
2076 fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
2077 fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
2078 fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
2079 + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
2082 /* Filter I at 1.5 MHz. 3 pole Butterworth from
2083 mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
2085 fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
2086 fix[3] = (rawi * 1413) >> 16;
2087 fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
2088 fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
2089 + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
2092 /* Filter Q at 0.5 MHz. 3 pole Butterworth from
2093 mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
2095 fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
2096 fqx[3] = (rawq * 75) >> 16;
2097 fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
2098 fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
2099 + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
2103 composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
2104 composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
2105 if (composite>125) composite=125;
2106 if (composite<0) composite=0;
2107 input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
2115 void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
2118 int change=random()%ANALOGTV_V;
2119 unsigned int fastrnd=random();
2120 double hso=(int)(random()%1000)-500;
2121 int yofs=random()%ANALOGTV_V;
2124 for (y=change; y<ANALOGTV_V; y++) {
2125 int s2y=(y+yofs)%ANALOGTV_V;
2127 int noiselevel=60000 / (y-change+100);
2129 it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
2131 for (x=0; x<ANALOGTV_H; x++) {
2133 filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
2134 noise=(filt*noiselevel)>>16;
2135 newsig=s2->signal[s2y][x] + noise;
2136 if (newsig>120) newsig=120;
2137 if (newsig<0) newsig=0;
2138 it->signal[y][x]=newsig;
2148 if (it->hashnoise_times[lineno]) {
2149 int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
2151 if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
2153 double cur=frand(150.0)-20.0;
2154 int len=random()%15+3;
2155 if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
2156 for (i=0; i<len; i++) {
2157 double sig=signal[hnt];
2160 cur += frand(5.0)-5.0;
2161 maxampl = maxampl*0.9;
2172 analogtv_reception_update(analogtv_reception *rec)
2176 if (rec->multipath > 0.0) {
2177 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2178 rec->ghostfir2[i] +=
2179 -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
2181 if (random()%20==0) {
2182 rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
2183 = rec->multipath * (frand(0.08)-0.04);
2185 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2186 rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
2190 rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
2191 rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
2195 for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
2196 rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
2203 /* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
2206 #include "images/6x10font.xbm"
2209 analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
2210 int w, int h, char *fontname)
2217 XWindowAttributes xgwa;
2222 XGetWindowAttributes (dpy, window, &xgwa);
2224 if (fontname && !strcmp (fontname, "6x10")) {
2226 text_pm = XCreatePixmapFromBitmapData (dpy, window,
2227 (char *) font6x10_bits,
2231 f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
2233 XFreePixmap(dpy, text_pm);
2235 } else if (fontname) {
2237 font = XLoadQueryFont (dpy, fontname);
2239 fprintf(stderr, "analogtv: can't load font %s\n", fontname);
2243 text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
2245 memset(&gcv, 0, sizeof(gcv));
2249 gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
2251 XSetForeground(dpy, gc, 0);
2252 XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
2253 XSetForeground(dpy, gc, 1);
2254 for (i=0; i<256; i++) {
2256 int x=f->char_w*i+1;
2257 int y=f->char_h*8/10;
2258 XDrawString(dpy, text_pm, gc, x, y, &c, 1);
2260 f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
2263 XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
2264 256*f->char_w, f->char_h, -1, -1);
2267 XFreePixmap(dpy, text_pm);
2269 f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
2270 256*f->char_w, f->char_h, 8, 0);
2271 f->text_im->data = (char *)calloc(f->text_im->height,
2272 f->text_im->bytes_per_line);
2280 analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
2282 if (x<0 || x>=f->char_w) return 0;
2283 if (y<0 || y>=f->char_h) return 0;
2284 if (c<0 || c>=256) return 0;
2285 return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
2289 analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
2291 if (x<0 || x>=f->char_w) return;
2292 if (y<0 || y>=f->char_h) return;
2293 if (c<0 || c>=256) return;
2295 XPutPixel(f->text_im, c*f->char_w + x, y, value);
2299 analogtv_font_set_char(analogtv_font *f, int c, char *s)
2303 if (c<0 || c>=256) return;
2305 for (y=0; y<f->char_h; y++) {
2306 for (x=0; x<f->char_w; x++) {
2308 value=(*s==' ') ? 0 : 1;
2309 analogtv_font_set_pixel(f, c, x, y, value);
2316 analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
2319 for (i=0; i<4; i++) {
2320 double w=90.0*i + phase;
2321 double val=luma + chroma * (cos(3.1415926/180.0*w));
2322 if (val<0.0) val=0.0;
2323 if (val>127.0) val=127.0;
2329 analogtv_draw_solid(analogtv_input *input,
2330 int left, int right, int top, int bot,
2335 if (right-left<4) right=left+4;
2336 if (bot-top<1) bot=top+1;
2338 for (y=top; y<bot; y++) {
2339 for (x=left; x<right; x++) {
2340 input->signal[y][x] = ntsc[x&3];
2347 analogtv_draw_solid_rel_lcp(analogtv_input *input,
2348 double left, double right, double top, double bot,
2349 double luma, double chroma, double phase)
2353 int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
2354 int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
2355 int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
2356 int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
2358 analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
2359 analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
2364 analogtv_draw_char(analogtv_input *input, analogtv_font *f,
2365 int c, int x, int y, int ntsc[4])
2367 int yc,xc,ys,xs,pix;
2369 for (yc=0; yc<f->char_h; yc++) {
2370 for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
2371 if (ys<0 || ys>=ANALOGTV_V) continue;
2373 for (xc=0; xc<f->char_w; xc++) {
2374 pix=analogtv_font_pixel(f, c, xc, yc);
2376 for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
2377 if (xs<0 || xs>=ANALOGTV_H) continue;
2379 input->signal[ys][xs] = ntsc[xs&3];
2388 analogtv_draw_string(analogtv_input *input, analogtv_font *f,
2389 char *s, int x, int y, int ntsc[4])
2392 analogtv_draw_char(input, f, *s, x, y, ntsc);
2399 analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
2400 char *s, int x, int y, int ntsc[4])
2402 int width=strlen(s) * f->char_w * 4;
2405 analogtv_draw_string(input, f, s, x, y, ntsc);
2409 static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2411 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2412 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
2413 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2414 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2415 0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
2416 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2419 Much of this function was adapted from logo.c
2422 analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
2423 const char * const *xpm, int left, int top)
2428 int ncolors, nbytes;
2431 int r; int g; int b;
2435 if (4 != sscanf ((const char *) *xpm,
2437 &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
2439 if (ncolors < 1 || ncolors > 255)
2441 if (nbytes != 1) /* a serious limitation */
2445 for (i = 0; i < ncolors; i++) {
2446 const char *line = *xpm;
2447 int colori = ((unsigned char)*line++)&0xff;
2452 while (*line == ' ' || *line == '\t')
2455 if (which != 'c' && which != 'm')
2457 while (*line == ' ' || *line == '\t')
2459 if (!strncasecmp(line, "None", 4))
2468 r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2470 g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2472 b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
2487 for (y=0; y<xpmh; y++) {
2488 const char *line = *xpm++;
2490 if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
2492 for (x=0; x<xpmw; x++) {
2493 int cbyte=((unsigned char)line[x])&0xff;
2496 if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
2498 rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
2499 rawi=(10*cmap[cbyte].r - 4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
2500 rawq=( 3*cmap[cbyte].r - 8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
2507 for (i=0; i<4; i++) {
2508 if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
2509 if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
2512 input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
2513 input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
2514 input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
2515 input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];