Skip to content

Commit e267248

Browse files
authored
Merge pull request #30 from LMP88959/add-vhs
Add vhs
2 parents 58da350 + 5a5327d commit e267248

File tree

5 files changed

+798
-8
lines changed

5 files changed

+798
-8
lines changed

crt_core.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,26 @@ crt_demodulate(struct CRT *v, int noise)
320320
huecs >>= 11;
321321

322322
rn = v->rn;
323+
#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE)
324+
line = ((rand() % 8) - 4) + 14;
325+
#endif
323326
for (i = 0; i < CRT_INPUT_SIZE; i++) {
327+
int nn = noise;
328+
#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE)
329+
rn = rand();
330+
if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) &&
331+
i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) {
332+
int ln, sn, cs;
333+
334+
ln = (i * line) / CRT_HRES;
335+
crt_sincos14(&sn, &cs, ln * 8192 / 180);
336+
nn = cs >> 8;
337+
}
338+
#else
324339
rn = (214019 * rn + 140327895);
325-
340+
#endif
326341
/* signal + noise */
327-
s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * noise) >> 8);
342+
s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8);
328343
if (s > 127) { s = 127; }
329344
if (s < -127) { s = -127; }
330345
v->inp[i] = s;

crt_core.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ extern "C" {
2424
/* library version */
2525
#define CRT_MAJOR 2
2626
#define CRT_MINOR 2
27-
#define CRT_PATCH 0
27+
#define CRT_PATCH 1
2828

2929

30-
#define CRT_SYSTEM_NTSC 0 /* standard NTSC */
31-
#define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */
32-
#define CRT_SYSTEM_PV1K 2 /* Casio PV-1000 */
33-
#define CRT_SYSTEM_SNES 3 /* SNES - uses RGB */
34-
#define CRT_SYSTEM_TEMP 4 /* template implementation */
30+
#define CRT_SYSTEM_NTSC 0 /* standard NTSC */
31+
#define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */
32+
#define CRT_SYSTEM_PV1K 2 /* Casio PV-1000 */
33+
#define CRT_SYSTEM_SNES 3 /* SNES - uses RGB */
34+
#define CRT_SYSTEM_TEMP 4 /* template implementation */
35+
#define CRT_SYSTEM_NTSCVHS 5 /* standard NTSC VHS */
3536

3637
/* the system to be compiled */
3738
#define CRT_SYSTEM CRT_SYSTEM_NTSC
@@ -46,6 +47,8 @@ extern "C" {
4647
#include "crt_pv1k.h"
4748
#elif (CRT_SYSTEM == CRT_SYSTEM_TEMP)
4849
#include "crt_template.h"
50+
#elif (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS)
51+
#include "crt_ntscvhs.h"
4952
#else
5053
#error No system defined
5154
#endif

crt_ntscvhs.c

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*****************************************************************************/
2+
/*
3+
* NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation
4+
*
5+
* by EMMIR 2018-2023
6+
*
7+
* YouTube: https://www.youtube.com/@EMMIR_KC/videos
8+
* Discord: https://discord.com/invite/hdYctSmyQJ
9+
*/
10+
/*****************************************************************************/
11+
12+
#include "crt_core.h"
13+
14+
#if (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS)
15+
#include <stdlib.h>
16+
#include <string.h>
17+
18+
#if (CRT_CHROMA_PATTERN == 1)
19+
/* 227.5 subcarrier cycles per line means every other line has reversed phase */
20+
#define CC_PHASE(ln) (((ln) & 1) ? -1 : 1)
21+
#else
22+
#define CC_PHASE(ln) (1)
23+
#endif
24+
25+
#define EXP_P 11
26+
#define EXP_ONE (1 << EXP_P)
27+
#define EXP_MASK (EXP_ONE - 1)
28+
#define EXP_PI 6434
29+
#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P)
30+
#define EXP_DIV(x, y) (((x) << EXP_P) / (y))
31+
32+
static int e11[] = {
33+
EXP_ONE,
34+
5567, /* e */
35+
15133, /* e^2 */
36+
41135, /* e^3 */
37+
111817 /* e^4 */
38+
};
39+
40+
/* fixed point e^x */
41+
static int
42+
expx(int n)
43+
{
44+
int neg, idx, res;
45+
int nxt, acc, del;
46+
int i;
47+
48+
if (n == 0) {
49+
return EXP_ONE;
50+
}
51+
neg = n < 0;
52+
if (neg) {
53+
n = -n;
54+
}
55+
idx = n >> EXP_P;
56+
res = EXP_ONE;
57+
for (i = 0; i < idx / 4; i++) {
58+
res = EXP_MUL(res, e11[4]);
59+
}
60+
idx &= 3;
61+
if (idx > 0) {
62+
res = EXP_MUL(res, e11[idx]);
63+
}
64+
65+
n &= EXP_MASK;
66+
nxt = EXP_ONE;
67+
acc = 0;
68+
del = 1;
69+
for (i = 1; i < 17; i++) {
70+
acc += nxt / del;
71+
nxt = EXP_MUL(nxt, n);
72+
del *= i;
73+
if (del > nxt || nxt <= 0 || del <= 0) {
74+
break;
75+
}
76+
}
77+
res = EXP_MUL(res, acc);
78+
79+
if (neg) {
80+
res = EXP_DIV(EXP_ONE, res);
81+
}
82+
return res;
83+
}
84+
85+
/*****************************************************************************/
86+
/********************************* FILTERS ***********************************/
87+
/*****************************************************************************/
88+
89+
/* infinite impulse response low pass filter for bandlimiting YIQ */
90+
static struct IIRLP {
91+
int c;
92+
int h; /* history */
93+
} iirY, iirI, iirQ;
94+
95+
/* freq - total bandwidth
96+
* limit - max frequency
97+
*/
98+
static void
99+
init_iir(struct IIRLP *f, int freq, int limit)
100+
{
101+
int rate; /* cycles/pixel rate */
102+
103+
memset(f, 0, sizeof(struct IIRLP));
104+
rate = (freq << 9) / limit;
105+
f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate));
106+
}
107+
108+
static void
109+
reset_iir(struct IIRLP *f)
110+
{
111+
f->h = 0;
112+
}
113+
114+
/* hi-pass for debugging */
115+
#define HIPASS 0
116+
117+
static int
118+
iirf(struct IIRLP *f, int s)
119+
{
120+
f->h += EXP_MUL(s - f->h, f->c);
121+
#if HIPASS
122+
return s - f->h;
123+
#else
124+
return f->h;
125+
#endif
126+
}
127+
128+
extern void
129+
crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
130+
{
131+
int x, y, xo, yo;
132+
int destw = AV_LEN;
133+
int desth = ((CRT_LINES * 64500) >> 16);
134+
int iccf[CRT_CC_SAMPLES];
135+
int ccmodI[CRT_CC_SAMPLES]; /* color phase for mod */
136+
int ccmodQ[CRT_CC_SAMPLES]; /* color phase for mod */
137+
int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */
138+
int sn, cs, n, ph;
139+
int inv_phase = 0;
140+
int bpp;
141+
int aberration = 0;
142+
143+
if (!s->iirs_initialized) {
144+
init_iir(&iirY, L_FREQ, Y_FREQ);
145+
init_iir(&iirI, L_FREQ, I_FREQ);
146+
init_iir(&iirQ, L_FREQ, Q_FREQ);
147+
s->iirs_initialized = 1;
148+
}
149+
#if CRT_DO_BLOOM
150+
if (s->raw) {
151+
destw = s->w;
152+
desth = s->h;
153+
if (destw > ((AV_LEN * 55500) >> 16)) {
154+
destw = ((AV_LEN * 55500) >> 16);
155+
}
156+
if (desth > ((CRT_LINES * 63500) >> 16)) {
157+
desth = ((CRT_LINES * 63500) >> 16);
158+
}
159+
} else {
160+
destw = (AV_LEN * 55500) >> 16;
161+
desth = (CRT_LINES * 63500) >> 16;
162+
}
163+
#else
164+
if (s->raw) {
165+
destw = s->w;
166+
desth = s->h;
167+
if (destw > AV_LEN) {
168+
destw = AV_LEN;
169+
}
170+
if (desth > ((CRT_LINES * 64500) >> 16)) {
171+
desth = ((CRT_LINES * 64500) >> 16);
172+
}
173+
}
174+
#endif
175+
if (s->as_color) {
176+
for (x = 0; x < CRT_CC_SAMPLES; x++) {
177+
n = s->hue + x * (360 / CRT_CC_SAMPLES);
178+
crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180);
179+
ccburst[x] = sn >> 10;
180+
crt_sincos14(&sn, &cs, n * 8192 / 180);
181+
ccmodI[x] = sn >> 10;
182+
crt_sincos14(&sn, &cs, (n - 90) * 8192 / 180);
183+
ccmodQ[x] = sn >> 10;
184+
}
185+
} else {
186+
memset(ccburst, 0, sizeof(ccburst));
187+
memset(ccmodI, 0, sizeof(ccmodI));
188+
memset(ccmodQ, 0, sizeof(ccmodQ));
189+
}
190+
191+
bpp = crt_bpp4fmt(s->format);
192+
if (bpp == 0) {
193+
return; /* just to be safe */
194+
}
195+
xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2;
196+
yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2;
197+
198+
s->field &= 1;
199+
s->frame &= 1;
200+
inv_phase = (s->field == s->frame);
201+
ph = CC_PHASE(inv_phase);
202+
203+
/* align signal */
204+
xo = (xo & ~3);
205+
if (s->do_aberration) {
206+
aberration = ((rand() % 12) - 8) + 14;
207+
}
208+
for (n = 0; n < CRT_VRES; n++) {
209+
int t; /* time */
210+
signed char *line = &v->analog[n * CRT_HRES];
211+
212+
t = LINE_BEG;
213+
214+
if (n <= 3 || (n >= 7 && n <= 9)) {
215+
/* equalizing pulses - small blips of sync, mostly blank */
216+
while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
217+
while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
218+
while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
219+
while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
220+
} else if (n >= 4 && n <= 6) {
221+
int even[4] = { 46, 50, 96, 100 };
222+
int odd[4] = { 4, 50, 96, 100 };
223+
int *offs = even;
224+
if (s->field == 1) {
225+
offs = odd;
226+
}
227+
/* vertical sync pulse - small blips of blank, mostly sync */
228+
while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
229+
while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
230+
while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
231+
while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
232+
} else {
233+
int cb;
234+
if (n < (CRT_VRES - aberration)) {
235+
/* video line */
236+
while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */
237+
while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */
238+
}
239+
while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */
240+
241+
if (n < CRT_TOP) {
242+
while (t < CRT_HRES) line[t++] = BLANK_LEVEL;
243+
}
244+
245+
/* CB_CYCLES of color burst at 3.579545 Mhz */
246+
for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) {
247+
#if (CRT_CHROMA_PATTERN == 1)
248+
int off180 = CRT_CC_SAMPLES / 2;
249+
cb = ccburst[(t + inv_phase * off180) % CRT_CC_SAMPLES];
250+
#else
251+
cb = ccburst[t % CRT_CC_SAMPLES];
252+
#endif
253+
line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5;
254+
iccf[t % CRT_CC_SAMPLES] = line[t];
255+
}
256+
}
257+
}
258+
/* reset hsync every frame so only the bottom part is warped */
259+
v->hsync = 0;
260+
261+
for (y = 0; y < desth; y++) {
262+
int field_offset;
263+
int sy;
264+
265+
field_offset = (s->field * s->h + desth) / desth / 2;
266+
sy = (y * s->h) / desth;
267+
268+
sy += field_offset;
269+
270+
if (sy >= s->h) sy = s->h;
271+
272+
sy *= s->w;
273+
274+
reset_iir(&iirY);
275+
reset_iir(&iirI);
276+
reset_iir(&iirQ);
277+
278+
for (x = 0; x < destw; x++) {
279+
int fy, fi, fq;
280+
int rA, gA, bA;
281+
const unsigned char *pix;
282+
int ire; /* composite signal */
283+
int xoff;
284+
285+
pix = s->data + ((((x * s->w) / destw) + sy) * bpp);
286+
switch (s->format) {
287+
case CRT_PIX_FORMAT_RGB:
288+
case CRT_PIX_FORMAT_RGBA:
289+
rA = pix[0];
290+
gA = pix[1];
291+
bA = pix[2];
292+
break;
293+
case CRT_PIX_FORMAT_BGR:
294+
case CRT_PIX_FORMAT_BGRA:
295+
rA = pix[2];
296+
gA = pix[1];
297+
bA = pix[0];
298+
break;
299+
case CRT_PIX_FORMAT_ARGB:
300+
rA = pix[1];
301+
gA = pix[2];
302+
bA = pix[3];
303+
break;
304+
case CRT_PIX_FORMAT_ABGR:
305+
rA = pix[3];
306+
gA = pix[2];
307+
bA = pix[1];
308+
break;
309+
default:
310+
rA = gA = bA = 0;
311+
break;
312+
}
313+
314+
/* RGB to YIQ */
315+
fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14;
316+
fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14;
317+
fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14;
318+
ire = BLACK_LEVEL + v->black_point;
319+
320+
xoff = (x + xo) % CRT_CC_SAMPLES;
321+
/* bandlimit Y,I,Q */
322+
fy = iirf(&iirY, fy);
323+
fi = iirf(&iirI, fi) * ph * ccmodI[xoff] >> 4;
324+
fq = iirf(&iirQ, fq) * ph * ccmodQ[xoff] >> 4;
325+
ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10;
326+
if (ire < 0) ire = 0;
327+
if (ire > 110) ire = 110;
328+
329+
v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire;
330+
}
331+
}
332+
for (n = 0; n < CRT_CC_VPER; n++) {
333+
for (x = 0; x < CRT_CC_SAMPLES; x++) {
334+
v->ccf[n][x] = 0;
335+
}
336+
}
337+
}
338+
#endif

0 commit comments

Comments
 (0)