Merge branch 'master' into master

This commit is contained in:
Cynthia
2025-10-07 08:45:23 +05:30
committed by GitHub
8 changed files with 418 additions and 364 deletions

View File

@@ -557,6 +557,10 @@ position pointOnBezier(double t, position points[4])
return pos;
}
#if defined(_WIN32) || defined(_WIN64)
# define strtok_r strtok_s
#endif
/**
* Splits given string into sub-strings at given delimiter.
* \param string input string
@@ -569,11 +573,11 @@ int tokenise(char *string, const char *delimiter, char ***tokens)
int count = 0;
char *input = strdup(string);
char *result = NULL;
result = strtok(input, delimiter);
result = strtok_r(string, delimiter, &input);
while (result != NULL) {
*tokens = realloc(*tokens, (count + 1) * sizeof(char *));
(*tokens)[count++] = strdup(result);
result = strtok(NULL, delimiter);
result = strtok_r(NULL, delimiter, &input);
}
free(input);
return count;

View File

@@ -38,7 +38,7 @@ Copyright (C) 2010 Marko Cebokli http://lea.hamradio.si/~s57uuu
double PI=3.14159265358979;
//---------------------------------------------------------------
void draw_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float_rgba c)
static inline void draw_rectangle(float_rgba *s, int w, int h, float x, float y, float wr, float hr, float_rgba c)
{
int i,j;
int zx,kx,zy,ky;
@@ -139,7 +139,7 @@ draw_rectangle(s, w, h, x1, y1+1, v, 1, black);
//justified
//p=0 one decimal place p=1 three decimal places
//m=1 always show sign
void forstr(float a, int p, int m, char *s)
inline static void forstr(float a, int p, int m, char *s)
{
float b;
char *p3=" %5.3f";
@@ -192,14 +192,14 @@ if (mm==1)
forstr(s.rms,1-u,0,rs);
forstr(s.min,1-u,m,ns);
forstr(s.max,1-u,m,xs);
sprintf(fs,"%s%s%s %s%s", lab, as, rs, ns, xs);
snprintf(fs,255,"%s%s%s %s%s", lab, as, rs, ns, xs);
sprintf(str,fs,s.avg,s.rms,s.min,s.max);
}
else
{
forstr(s.avg,1-u,m,as);
forstr(s.rms,1-u,0,rs);
sprintf(fs,"%s%s%s", lab, as, rs);
snprintf(fs,255,"%s%s%s", lab, as, rs);
sprintf(str,fs,s.avg,s.rms);
}
}
@@ -251,7 +251,7 @@ if (sx>np)
s[w*y+x]=white;
x=x0+(np+2)*vp-i-1;
s[w*y+x]=white;
}
}
}
if (sy>np)
{
@@ -263,7 +263,7 @@ if (sy>np)
s[w*y+x]=white;
y=y0+(np+2)*vp-i-1;
s[w*y+x]=white;
}
}
}
}
@@ -301,7 +301,7 @@ y0=h/20;
if (bw==1) //big window
{
vx=240;
vy = (m<=2) ? 320 : 300;
vy = (m<=2) ? 320 : 300;
x0 = (*poz==0) ? h/20 : w-h/20-vx;
np=25; //size of magnifier
xn = (m<=2) ? x0+8 : x0+70;
@@ -745,4 +745,3 @@ crosshair(in->sl, in->w, in->h, in->x, in->y, 2*in->sx+1, 2*in->sy+1, 15);
floatrgba2color(in->sl, outframe, in->w , in->h);
}

View File

@@ -114,7 +114,7 @@ while (c[i]!=0)
//justified
//p=0 one decimal place p=1 three decimal places
//m=1 always show sign
void forstr(float a, int p, int m, char *s)
inline static void forstr(float a, int p, int m, char *s)
{
float b;
char *p3=" %5.3f";
@@ -320,7 +320,7 @@ if ((dit&0x00000001)!=0) //marker 1 value
if (m1>0)
{
forstr(data[0],1-u,0,frs);
sprintf(fs,"%%s Mk1=%s", frs);
snprintf(fs,255,"%%s Mk1=%s", frs);
sprintf(str,fs,str,data[0]);
}
else
@@ -331,7 +331,7 @@ if ((dit&0x00000004)!=0) //marker 2 value
if (m2>0)
{
forstr(data[1],1-u,0,frs);
sprintf(fs,"%%s Mk2=%s", frs);
snprintf(fs,255,"%%s Mk2=%s", frs);
sprintf(str,fs,str,data[1]);
}
else
@@ -342,7 +342,7 @@ if ((dit&0x00000010)!=0) //difference marker2-marker1
if ((m2>0)&&(m1>0))
{
forstr(data[2],1-u,0,frs);
sprintf(fs,"%%s D=%s", frs);
snprintf(fs,255,"%%s D=%s", frs);
sprintf(str,fs,str,data[2]);
}
else
@@ -351,25 +351,25 @@ if ((dit&0x00000010)!=0) //difference marker2-marker1
if ((dit&0x00000020)!=0) //average of profile
{
forstr(data[3],1-u,0,frs);
sprintf(fs,"%%s Avg=%s", frs);
snprintf(fs,255,"%%s Avg=%s", frs);
sprintf(str,fs,str,data[3]);
}
if ((dit&0x00000040)!=0) //RMS of profile
{
forstr(data[4],1-u,0,frs);
sprintf(fs,"%%s RMS=%s", frs);
snprintf(fs,255,"%%s RMS=%s", frs);
sprintf(str,fs,str,data[4]);
}
if ((dit&0x00000080)!=0) //MIN of profile
{
forstr(data[5],1-u,0,frs);
sprintf(fs,"%%s Min=%s", frs);
snprintf(fs,255,"%%s Min=%s", frs);
sprintf(str,fs,str,data[5]);
}
if ((dit&0x00000100)!=0) //MAX of profile
{
forstr(data[6],1-u,0,frs);
sprintf(fs,"%%s Max=%s", frs);
snprintf(fs,255,"%%s Max=%s", frs);
sprintf(str,fs,str,data[6]);
}
}
@@ -408,7 +408,7 @@ if (y<h/2-20) *poz=1; //bottom
if (y>h/2+20) *poz=0; //top
x0=h/20;
vx=w*15/16;
vy = h*6/16;
vy = h*6/16;
y0 = (*poz==0) ? h/20 : h-h/20-vy;
//end points of profile
@@ -1035,4 +1035,3 @@ prof(in->sl, in->w, in->h, &in->poz, in->x, in->y, in->tilt, in->len, 1, in->mer
floatrgba2color(in->sl, outframe, in->w , in->h);
}

View File

@@ -60,6 +60,23 @@ crt_sincos14(int *s, int *c, int n)
}
}
extern int
crt_bpp4fmt(int format)
{
switch (format) {
case CRT_PIX_FORMAT_RGB:
case CRT_PIX_FORMAT_BGR:
return 3;
case CRT_PIX_FORMAT_ARGB:
case CRT_PIX_FORMAT_RGBA:
case CRT_PIX_FORMAT_ABGR:
case CRT_PIX_FORMAT_BGRA:
return 4;
default:
return 0;
}
}
/*****************************************************************************/
/********************************* FILTERS ***********************************/
/*****************************************************************************/
@@ -222,10 +239,11 @@ eqf(struct EQF *f, int s)
/*****************************************************************************/
extern void
crt_resize(struct CRT *v, int w, int h, unsigned char *out)
crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out)
{
v->outw = w;
v->outh = h;
v->out_format = f;
v->out = out;
}
@@ -239,22 +257,14 @@ crt_reset(struct CRT *v)
v->black_point = 0;
v->white_point = 100;
v->hsync = 0;
v->vsync = 0;
v->scanlines = 0; /* leave gaps between lines if necessary */
v->blend = 0; /* blend new field onto previous image */
// these options were previously #defined in crt_core.h
v->crt_do_vsync = 1;
v->crt_do_hsync = 1;
v->do_vhs_noise = 0;
v->vsync = 0;
}
extern void
crt_init(struct CRT *v, int w, int h, unsigned char *out)
crt_init(struct CRT *v, int w, int h, int f, unsigned char *out)
{
memset(v, 0, sizeof(struct CRT));
crt_resize(v, w, h, out);
crt_resize(v, w, h, f, out);
crt_reset(v);
v->rn = 194;
@@ -293,58 +303,61 @@ crt_demodulate(struct CRT *v, int noise)
int huesn, huecs;
int xnudge = -3, ynudge = 3;
int bright = v->brightness - (BLACK_LEVEL + v->black_point);
int pitch;
int bpp, pitch;
#if CRT_DO_BLOOM
int prev_e; /* filtered beam energy per scan line */
int max_e; /* approx maximum energy in a scan line */
#endif
pitch = v->outw * BPP;
bpp = crt_bpp4fmt(v->out_format);
if (bpp == 0) {
return;
}
pitch = v->outw * bpp;
crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180);
huesn >>= 11; /* make 4-bit */
huecs >>= 11;
rn = v->rn;
if(!v->crt_do_vsync)
{
/* determine field before we add noise,
* otherwise it's not reliably recoverable
*/
for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) {
line = POSMOD(v->vsync + i, CRT_VRES);
sig = v->analog + line * CRT_HRES;
s = 0;
for (j = 0; j < CRT_HRES; j++) {
s += sig[j];
if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) {
goto found_field;
}
#if !CRT_DO_VSYNC
/* determine field before we add noise,
* otherwise it's not reliably recoverable
*/
for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) {
line = POSMOD(v->vsync + i, CRT_VRES);
sig = v->analog + line * CRT_HRES;
s = 0;
for (j = 0; j < CRT_HRES; j++) {
s += sig[j];
if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) {
goto found_field;
}
}
found_field:
/* if vsync signal was in second half of line, odd field */
field = (j > (CRT_HRES / 2));
v->vsync = -3;
}
if(v->do_vhs_noise)
{
line = ((rand() % 8) - 4) + 14;
}
found_field:
/* if vsync signal was in second half of line, odd field */
field = (j > (CRT_HRES / 2));
v->vsync = -3;
#endif
#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE)
line = ((rand() % 8) - 4) + 14;
#endif
for (i = 0; i < CRT_INPUT_SIZE; i++) {
int nn = noise;
if(v->do_vhs_noise)
{
rn = rand();
if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) &&
i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) {
int ln, sn, cs;
#if ((CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) && CRT_VHS_NOISE)
rn = rand();
if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) &&
i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) {
int ln, sn, cs;
ln = (i * line) / CRT_HRES;
crt_sincos14(&sn, &cs, ln * 8192 / 180);
nn = cs >> 8;
}
}
else
{
rn = (214019 * rn + 140327895);
ln = (i * line) / CRT_HRES;
crt_sincos14(&sn, &cs, ln * 8192 / 180);
nn = cs >> 8;
}
#else
rn = (214019 * rn + 140327895);
#endif
/* signal + noise */
s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8);
if (s > 127) { s = 127; }
@@ -353,37 +366,40 @@ crt_demodulate(struct CRT *v, int noise)
}
v->rn = rn;
if(v->crt_do_vsync)
{
/* Look for vertical sync.
*
* This is done by integrating the signal and
* seeing if it exceeds a threshold. The threshold of
* the vertical sync pulse is much higher because the
* vsync pulse is a lot longer than the hsync pulse.
* The signal needs to be integrated to lessen
* the noise in the signal.
*/
for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) {
line = POSMOD(v->vsync + i, CRT_VRES);
sig = v->inp + line * CRT_HRES;
s = 0;
for (j = 0; j < CRT_HRES; j++) {
s += sig[j];
/* increase the multiplier to make the vsync
* more stable when there is a lot of noise
*/
if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) {
goto vsync_found;
}
#if CRT_DO_VSYNC
/* Look for vertical sync.
*
* This is done by integrating the signal and
* seeing if it exceeds a threshold. The threshold of
* the vertical sync pulse is much higher because the
* vsync pulse is a lot longer than the hsync pulse.
* The signal needs to be integrated to lessen
* the noise in the signal.
*/
for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) {
line = POSMOD(v->vsync + i, CRT_VRES);
sig = v->inp + line * CRT_HRES;
s = 0;
for (j = 0; j < CRT_HRES; j++) {
s += sig[j];
/* increase the multiplier to make the vsync
* more stable when there is a lot of noise
*/
if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) {
goto vsync_found;
}
}
vsync_found:
v->vsync = line; /* vsync found (or gave up) at this line */
/* if vsync signal was in second half of line, odd field */
field = (j > (CRT_HRES / 2));
}
}
vsync_found:
v->vsync = line; /* vsync found (or gave up) at this line */
/* if vsync signal was in second half of line, odd field */
field = (j > (CRT_HRES / 2));
#endif
#if CRT_DO_BLOOM
max_e = (128 + (noise / 2)) * AV_LEN;
prev_e = (16384 / 8);
#endif
/* ratio of output height to active video lines in the signal */
ratio = (v->outh << 16) / CRT_LINES;
ratio = (ratio + 32768) >> 16;
@@ -405,6 +421,9 @@ crt_demodulate(struct CRT *v, int noise)
int xpos, ypos;
int beg, end;
int phasealign;
#if CRT_DO_BLOOM
int line_w;
#endif
beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field;
end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field;
@@ -424,14 +443,11 @@ crt_demodulate(struct CRT *v, int noise)
break;
}
}
if(v->crt_do_hsync)
{
v->hsync = POSMOD(i + v->hsync, CRT_HRES);
}
else
{
v->hsync = 0;
}
#if CRT_DO_HSYNC
v->hsync = POSMOD(i + v->hsync, CRT_HRES);
#else
v->hsync = 0;
#endif
xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES);
ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES);
@@ -493,13 +509,28 @@ crt_demodulate(struct CRT *v, int noise)
}
#endif
sig = v->inp + pos;
#if CRT_DO_BLOOM
s = 0;
for (i = 0; i < AV_LEN; i++) {
s += sig[i]; /* sum up the scan line */
}
/* bloom emulation */
prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e);
line_w = (AV_LEN * 112 / 128) + (prev_e >> 9);
dx = (line_w << 12) / v->outw;
scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12;
scanR = (AV_LEN - 1) << 12;
L = (scanL >> 12);
R = (scanR >> 12);
#else
dx = ((AV_LEN - 1) << 12) / v->outw;
scanL = 0;
scanR = (AV_LEN - 1) << 12;
L = 0;
R = AV_LEN;
R = AV_LEN;
#endif
reset_eq(&eqY);
reset_eq(&eqI);
reset_eq(&eqQ);
@@ -552,8 +583,26 @@ crt_demodulate(struct CRT *v, int noise)
if (v->blend) {
aa = (r << 16 | g << 8 | b);
bb = cL[0] << 16 | cL[1] << 8 | cL[2];
switch (v->out_format) {
case CRT_PIX_FORMAT_RGB:
case CRT_PIX_FORMAT_RGBA:
bb = cL[0] << 16 | cL[1] << 8 | cL[2];
break;
case CRT_PIX_FORMAT_BGR:
case CRT_PIX_FORMAT_BGRA:
bb = cL[2] << 16 | cL[1] << 8 | cL[0];
break;
case CRT_PIX_FORMAT_ARGB:
bb = cL[1] << 16 | cL[2] << 8 | cL[3];
break;
case CRT_PIX_FORMAT_ABGR:
bb = cL[3] << 16 | cL[2] << 8 | cL[1];
break;
default:
bb = 0;
break;
}
/* blend with previous color there */
bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1));
@@ -561,12 +610,52 @@ crt_demodulate(struct CRT *v, int noise)
bb = (r << 16 | g << 8 | b);
}
cL[0] = bb >> 16 & 0xff;
cL[1] = bb >> 8 & 0xff;
cL[2] = bb >> 0 & 0xff;
cL[3] = 0xff;
switch (v->out_format) {
case CRT_PIX_FORMAT_RGB:
cL[0] = bb >> 16 & 0xff;
cL[1] = bb >> 8 & 0xff;
cL[2] = bb >> 0 & 0xff;
break;
cL += BPP;
case CRT_PIX_FORMAT_RGBA:
cL[0] = bb >> 16 & 0xff;
cL[1] = bb >> 8 & 0xff;
cL[2] = bb >> 0 & 0xff;
cL[3] = 0xff;
break;
case CRT_PIX_FORMAT_BGR:
cL[0] = bb >> 0 & 0xff;
cL[1] = bb >> 8 & 0xff;
cL[2] = bb >> 16 & 0xff;
break;
case CRT_PIX_FORMAT_BGRA:
cL[0] = bb >> 0 & 0xff;
cL[1] = bb >> 8 & 0xff;
cL[2] = bb >> 16 & 0xff;
cL[3] = 0xff;
break;
case CRT_PIX_FORMAT_ARGB:
cL[0] = 0xff;
cL[1] = bb >> 16 & 0xff;
cL[2] = bb >> 8 & 0xff;
cL[3] = bb >> 0 & 0xff;
break;
case CRT_PIX_FORMAT_ABGR:
cL[0] = 0xff;
cL[1] = bb >> 0 & 0xff;
cL[2] = bb >> 8 & 0xff;
cL[3] = bb >> 16 & 0xff;
break;
default:
break;
}
cL += bpp;
}
/* duplicate extra lines */

View File

@@ -18,36 +18,72 @@ extern "C" {
/* crt_core.h
*
* The demodulator. This is also where you can define which system to emulate.
*
*
*/
/* library version */
#define CRT_MAJOR 2
#define CRT_MINOR 3
#define CRT_PATCH 2
#include "crt_ntsc.h"
// frei0r always uses 4 bits per pixel so it is pointless for there to be any other possibilities
#define BPP 4
#define CRT_SYSTEM_NTSC 0 /* standard NTSC */
#define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */
#define CRT_SYSTEM_PV1K 2 /* Casio PV-1000 */
#define CRT_SYSTEM_SNES 3 /* SNES - uses RGB */
#define CRT_SYSTEM_TEMP 4 /* template implementation */
#define CRT_SYSTEM_NTSCVHS 5 /* standard NTSC VHS */
#define CRT_SYSTEM_NESRGB 6 /* encode RGB image with NES artifacts */
/* the system to be compiled */
#ifndef CRT_SYSTEM
#define CRT_SYSTEM CRT_SYSTEM_NTSC
#endif
#if (CRT_SYSTEM == CRT_SYSTEM_NES)
#include "crt_nes.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_SNES)
#include "crt_snes.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_NTSC)
#include "crt_ntsc.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_PV1K)
#include "crt_pv1k.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_TEMP)
#include "crt_template.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS)
#include "crt_ntscvhs.h"
#elif (CRT_SYSTEM == CRT_SYSTEM_NESRGB)
#include "crt_nesrgb.h"
#else
#error No system defined
#endif
/* NOTE: this library does not use the alpha channel at all */
#define CRT_PIX_FORMAT_RGB 0 /* 3 bytes per pixel [R,G,B,R,G,B,R,G,B...] */
#define CRT_PIX_FORMAT_BGR 1 /* 3 bytes per pixel [B,G,R,B,G,R,B,G,R...] */
#define CRT_PIX_FORMAT_ARGB 2 /* 4 bytes per pixel [A,R,G,B,A,R,G,B...] */
#define CRT_PIX_FORMAT_RGBA 3 /* 4 bytes per pixel [R,G,B,A,R,G,B,A...] */
#define CRT_PIX_FORMAT_ABGR 4 /* 4 bytes per pixel [A,B,G,R,A,B,G,R...] */
#define CRT_PIX_FORMAT_BGRA 5 /* 4 bytes per pixel [B,G,R,A,B,G,R,A...] */
/* do bloom emulation (side effect: makes screen have black borders) */
#define CRT_DO_BLOOM 0 /* does not work for NES */
#define CRT_DO_VSYNC 1 /* look for VSYNC */
#define CRT_DO_HSYNC 1 /* look for HSYNC */
struct CRT {
signed char analog[CRT_INPUT_SIZE];
signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */
int outw, outh; /* output width/height */
int out_format; /* output pixel format (one of the CRT_PIX_FORMATs) */
unsigned char *out; /* output image */
int hue, brightness, contrast, saturation; /* common monitor settings */
int black_point, white_point; /* user-adjustable */
int scanlines; /* leave gaps between lines if necessary */
int blend; /* blend new field onto previous image */
unsigned v_fac; /* factor to stretch img vertically onto the output img */
// these options were previously #defined in crt_core.h
int crt_do_vsync;
int crt_do_hsync;
int do_vhs_noise; /* 0 = no additional vhs noise, 1 = with additional vhs noise */
unsigned v_fac; /* factor to stretch img vertically onto the output img */
/* internal data */
int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */
@@ -61,7 +97,7 @@ struct CRT {
* f - format of the output image
* out - pointer to output image data
*/
extern void crt_init(struct CRT *v, int w, int h, unsigned char *out);
extern void crt_init(struct CRT *v, int w, int h, int f, unsigned char *out);
/* Updates the output image parameters
* w - width of the output image
@@ -69,7 +105,7 @@ extern void crt_init(struct CRT *v, int w, int h, unsigned char *out);
* f - format of the output image
* out - pointer to output image data
*/
extern void crt_resize(struct CRT *v, int w, int h, unsigned char *out);
extern void crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out);
/* Resets the CRT settings back to their defaults */
extern void crt_reset(struct CRT *v);
@@ -78,12 +114,20 @@ extern void crt_reset(struct CRT *v);
* s - struct containing settings to apply to this field
*/
extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s);
/* Demodulates the NTSC signal generated by crt_modulate()
* noise - the amount of noise added to the signal (0 - inf)
*/
extern void crt_demodulate(struct CRT *v, int noise);
/* Get the bytes per pixel for a certain CRT_PIX_FORMAT_
*
* format - the format to get the bytes per pixel for
*
* returns 0 if the specified format does not exist
*/
extern int crt_bpp4fmt(int format);
/*****************************************************************************/
/*************************** FIXED POINT SIN/COS *****************************/
/*****************************************************************************/

View File

@@ -1,9 +1,9 @@
/*****************************************************************************/
/*
* NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation
*
*
* by EMMIR 2018-2023
*
*
* YouTube: https://www.youtube.com/@EMMIR_KC/videos
* Discord: https://discord.com/invite/hdYctSmyQJ
*/
@@ -11,7 +11,7 @@
#include "crt_core.h"
#if (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS)
#if (CRT_SYSTEM == CRT_SYSTEM_NTSC)
#include <stdlib.h>
#include <string.h>
@@ -35,7 +35,7 @@ static int e11[] = {
15133, /* e^2 */
41135, /* e^3 */
111817 /* e^4 */
};
};
/* fixed point e^x */
static int
@@ -61,7 +61,7 @@ expx(int n)
if (idx > 0) {
res = EXP_MUL(res, e11[idx]);
}
n &= EXP_MASK;
nxt = EXP_ONE;
acc = 0;
@@ -99,7 +99,7 @@ static void
init_iir(struct IIRLP *f, int freq, int limit)
{
int rate; /* cycles/pixel rate */
memset(f, 0, sizeof(struct IIRLP));
rate = (freq << 9) / limit;
f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate));
@@ -137,41 +137,29 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */
int sn, cs, n, ph;
int inv_phase = 0;
int aberration = 0;
int bpp;
if (!s->iirs_initialized) {
int y_freq = Y_FREQ_OFF;
int i_freq = I_FREQ_OFF;
int q_freq = Q_FREQ_OFF;
switch(s->vhs_mode)
{
case VHS_OFF:
break;
case VHS_SP:
y_freq = Y_FREQ_SP;
i_freq = I_FREQ_SP;
q_freq = Q_FREQ_SP;
break;
case VHS_LP:
y_freq = Y_FREQ_LP;
i_freq = I_FREQ_LP;
q_freq = Q_FREQ_LP;
break;
case VHS_EP:
y_freq = Y_FREQ_EP;
i_freq = I_FREQ_EP;
q_freq = Q_FREQ_EP;
break;
default:
break;
}
init_iir(&iirY, L_FREQ, y_freq);
init_iir(&iirI, L_FREQ, i_freq);
init_iir(&iirQ, L_FREQ, q_freq);
init_iir(&iirY, L_FREQ, Y_FREQ);
init_iir(&iirI, L_FREQ, I_FREQ);
init_iir(&iirQ, L_FREQ, Q_FREQ);
s->iirs_initialized = 1;
}
#if CRT_DO_BLOOM
if (s->raw) {
destw = s->w;
desth = s->h;
if (destw > ((AV_LEN * 55500) >> 16)) {
destw = ((AV_LEN * 55500) >> 16);
}
if (desth > ((CRT_LINES * 63500) >> 16)) {
desth = ((CRT_LINES * 63500) >> 16);
}
} else {
destw = (AV_LEN * 55500) >> 16;
desth = (CRT_LINES * 63500) >> 16;
}
#else
if (s->raw) {
destw = s->w;
desth = s->h;
@@ -182,7 +170,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
desth = ((CRT_LINES * 64500) >> 16);
}
}
#endif
if (s->as_color) {
for (x = 0; x < CRT_CC_SAMPLES; x++) {
n = s->hue + x * (360 / CRT_CC_SAMPLES);
@@ -198,10 +186,14 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
memset(ccmodI, 0, sizeof(ccmodI));
memset(ccmodQ, 0, sizeof(ccmodQ));
}
bpp = crt_bpp4fmt(s->format);
if (bpp == 0) {
return; /* just to be safe */
}
xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2;
yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2;
s->field &= 1;
s->frame &= 1;
inv_phase = (s->field == s->frame);
@@ -209,13 +201,11 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
/* align signal */
xo = (xo & ~3);
if (s->do_aberration) {
aberration = ((rand() % 12) - 8) + 14;
}
for (n = 0; n < CRT_VRES; n++) {
int t; /* time */
signed char *line = &v->analog[n * CRT_HRES];
t = LINE_BEG;
if (n <= 3 || (n >= 7 && n <= 9)) {
@@ -238,13 +228,11 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
} else {
int cb;
if (n < (CRT_VRES - aberration)) {
/* video line */
while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */
while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */
}
while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */
/* video line */
while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */
while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */
while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */
if (n < CRT_TOP) {
while (t < CRT_HRES) line[t++] = BLANK_LEVEL;
}
@@ -261,50 +249,67 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
iccf[t % CRT_CC_SAMPLES] = line[t];
}
}
}
if(s->vhs_mode != VHS_OFF)
{
/* reset hsync every frame so only the bottom part is warped */
v->hsync = 0;
}
for (y = 0; y < desth; y++) {
int field_offset;
int sy;
field_offset = (s->field * s->h + desth) / desth / 2;
sy = (y * s->h) / desth;
sy += field_offset;
if (sy >= s->h) sy = s->h;
sy *= s->w;
reset_iir(&iirY);
reset_iir(&iirI);
reset_iir(&iirQ);
for (x = 0; x < destw; x++) {
int fy, fi, fq;
int rA, gA, bA;
const unsigned char *pix;
int ire; /* composite signal */
int xoff;
pix = s->data + ((((x * s->w) / destw) + sy) * BPP);
rA = pix[0];
gA = pix[1];
bA = pix[2];
pix = s->data + ((((x * s->w) / destw) + sy) * bpp);
switch (s->format) {
case CRT_PIX_FORMAT_RGB:
case CRT_PIX_FORMAT_RGBA:
rA = pix[0];
gA = pix[1];
bA = pix[2];
break;
case CRT_PIX_FORMAT_BGR:
case CRT_PIX_FORMAT_BGRA:
rA = pix[2];
gA = pix[1];
bA = pix[0];
break;
case CRT_PIX_FORMAT_ARGB:
rA = pix[1];
gA = pix[2];
bA = pix[3];
break;
case CRT_PIX_FORMAT_ABGR:
rA = pix[3];
gA = pix[2];
bA = pix[1];
break;
default:
rA = gA = bA = 0;
break;
}
/* RGB to YIQ */
fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14;
fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14;
fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14;
ire = BLACK_LEVEL + v->black_point;
xoff = (x + xo) % CRT_CC_SAMPLES;
/* bandlimit Y,I,Q */
fy = iirf(&iirY, fy);
@@ -319,14 +324,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
}
for (n = 0; n < CRT_CC_VPER; n++) {
for (x = 0; x < CRT_CC_SAMPLES; x++) {
if(s->vhs_mode != VHS_OFF)
{
v->ccf[n][x] = 0;
}
else
{
v->ccf[n][x] = iccf[x] << 7;
}
v->ccf[n][x] = iccf[x] << 7;
}
}
}

View File

@@ -8,18 +8,17 @@
* Discord: https://discord.com/invite/hdYctSmyQJ
*/
/*****************************************************************************/
#ifndef _CRT_NTSC_VHS_H_
#define _CRT_NTSC_VHS_H_
#ifndef _CRT_NTSC_H_
#define _CRT_NTSC_H_
#ifdef __cplusplus
extern "C" {
#endif
/* crt_ntscvhs.h
*
* An interface to convert a digital image to an analog NTSC signal with VHS
* quality and some optional signal aberration at the bottom.
/* crt_ntsc.h
*
* An interface to convert a digital image to an analog NTSC signal.
*
*/
/* 0 = vertical chroma (228 chroma clocks per line) */
/* 1 = checkered chroma (227.5 chroma clocks per line) */
@@ -42,7 +41,7 @@ extern "C" {
#define CRT_TOP 21 /* first line with active video */
#define CRT_BOT 261 /* final line with active video */
#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */
#define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */
#define CRT_CC_VPER 1 /* vertical period in which the artifacts repeat */
@@ -62,14 +61,14 @@ extern "C" {
* |---------------------------------------------------------------------------|
* HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns)
* |-------------------||------------------------------------------------------|
*
*
*
*
* WITHIN HBLANK PERIOD:
*
*
* FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns)
* |--------------||---------------||------------||-------------||-------------|
* BLANK SYNC BLANK BLANK BLANK
*
*
*/
#define LINE_BEG 0
#define FP_ns 1500 /* front porch */
@@ -95,33 +94,12 @@ extern "C" {
/* somewhere between 7 and 12 cycles */
#define CB_CYCLES 10
#define VHS_OFF 0
#define VHS_SP 1
#define VHS_LP 2
#define VHS_EP 3
#define VHS_MODE VHS_OFF
/* frequencies for bandlimiting */
#define L_FREQ 1431818 /* full line */
#define Y_FREQ_OFF 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */
#define I_FREQ_OFF 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */
#define Q_FREQ_OFF 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */
#define Y_FREQ_SP 300000 /* Luma (Y) 3.0 MHz of the 14.31818 MHz */
#define I_FREQ_SP 62700 /* Chroma (I) 627 kHz of the 14.31818 MHz */
#define Q_FREQ_SP 62700 /* Chroma (Q) 627 kHz of the 14.31818 MHz */
#define Y_FREQ_LP 240000 /* Luma (Y) 2.4 MHz of the 14.31818 MHz */
#define I_FREQ_LP 40000 /* Chroma (I) 400 kHz of the 14.31818 MHz */
#define Q_FREQ_LP 40000 /* Chroma (Q) 400 kHz of the 14.31818 MHz */
#define Y_FREQ_EP 200000 /* Luma (Y) 2.0 MHz of the 14.31818 MHz */
#define I_FREQ_EP 37000 /* Chroma (I) 370 kHz of the 14.31818 MHz */
#define Q_FREQ_EP 37000 /* Chroma (Q) 370 kHz of the 14.31818 MHz */
/* frequencies for bandlimiting */
#define L_FREQ 1431818 /* full line */
#define Y_FREQ 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */
#define I_FREQ 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */
#define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */
/* IRE units (100 = 1.0V, -40 = 0.0V) */
#define WHITE_LEVEL 100
@@ -132,6 +110,7 @@ extern "C" {
struct NTSC_SETTINGS {
const unsigned char *data; /* image data */
int format; /* pix format (one of the CRT_PIX_FORMATs in crt_core.h) */
int w, h; /* width and height of image */
int raw; /* 0 = scale image to fit monitor, 1 = don't scale */
int as_color; /* 0 = monochrome, 1 = full color */
@@ -140,14 +119,6 @@ struct NTSC_SETTINGS {
int hue; /* 0-359 */
int xoffset; /* x offset in sample space. 0 is minimum value */
int yoffset; /* y offset in # of lines. 0 is minimum value */
int do_aberration; /* 0 = no aberration, 1 = with aberration */
// these are changed by the vhs mode
int vhs_mode;
int y_freq;
int i_freq;
int q_freq;
/* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */
int iirs_initialized; /* internal state */
};

View File

@@ -1,11 +1,13 @@
#include <stdlib.h>
#include <string.h>
#include "frei0r.h"
#include "frei0r/math.h"
#include "crt_core.h"
// the actual NTSC emulation code is modified from here: https://github.com/LMP88959/NTSC-CRT
// the actual NTSC emulation code is from here: https://github.com/LMP88959/NTSC-CRT
typedef struct ntsc_instance
{
@@ -15,16 +17,15 @@ typedef struct ntsc_instance
// parameters
struct CRT crt;
struct NTSC_SETTINGS ntsc_settings;
struct NTSC_SETTINGS ntsc;
int noise;
double vhs_speed;
int field;
int progressive;
} ntsc_instance_t;
// these functions are for frei0r
// mostly copy/paste/slightly modified from the other frei0r effects
int f0r_init()
@@ -44,8 +45,8 @@ void f0r_get_plugin_info(f0r_plugin_info_t* info)
info->color_model = F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version = FREI0R_MAJOR_VERSION;
info->major_version = 0;
info->minor_version = 1;
info->num_params = 8;
info->minor_version = 2;
info->num_params = 3;
}
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
@@ -57,51 +58,22 @@ void f0r_get_param_info(f0r_param_info_t* info, int param_index)
info->explanation = "Amount of noise introduced into the NTSC signal.";
info->type = F0R_PARAM_DOUBLE;
break;
case 1:
info->name = "VHS Speed";
info->explanation = "Simulates VHS. 0 = off, 1 = SP, 2 = LP, 3 = EP";
info->type = F0R_PARAM_DOUBLE;
break;
case 2:
info->name = "VHS Noise";
info->explanation = "Toggles VHS noise at the bottom of the screen";
info->type = F0R_PARAM_BOOL;
break;
case 3:
info->name = "Aberration";
info->explanation = "Toggles VHS aberration at the bottom of the screen. Not visible if V-sync is on.";
info->type = F0R_PARAM_BOOL;
break;
case 4:
info->name = "Force V-sync";
info->explanation = "Forces V-sync even when the signal is really noisy.";
info->type = F0R_PARAM_BOOL;
break;
case 5:
info->name = "Force H-sync";
info->explanation = "Forces V-sync even when the signal is really noisy.";
info->type = F0R_PARAM_BOOL;
break;
case 6:
info->name = "Progressive Scan";
info->explanation = "Toggles progressive scan (Interlaced if off).";
info->type = F0R_PARAM_BOOL;
break;
case 7:
info->name = "Blend";
info->explanation = "Blends frames.";
case 2:
info->name = "Scanlines";
info->explanation = "Draw borders between scanlines.";
info->type = F0R_PARAM_BOOL;
break;
}
}
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
ntsc_instance_t* inst = (ntsc_instance_t*)calloc(1, sizeof(*inst));
@@ -109,36 +81,28 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
inst->width = width;
inst->height = height;
inst->vhs_speed = 0.0;
inst->field = 0;
inst->progressive = 1;
crt_init(&(inst->crt), width, height, NULL);
// init NTSC_SETTINGS
inst->ntsc_settings.data = NULL;
inst->ntsc_settings.w = width;
inst->ntsc_settings.h = height;
inst->ntsc_settings.raw = 0;
inst->ntsc_settings.as_color = 1;
inst->ntsc_settings.field = 0;
inst->ntsc_settings.frame = 0;
inst->ntsc_settings.hue = 0;
inst->ntsc_settings.xoffset = 0;
inst->ntsc_settings.yoffset = 0;
inst->ntsc_settings.do_aberration = 0;
// these are changed by the vhs mode
inst->ntsc_settings.vhs_mode = 0;
inst->ntsc_settings.y_freq = 0;
inst->ntsc_settings.i_freq = 0;
inst->ntsc_settings.q_freq = 0;
/* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */
inst->ntsc_settings.iirs_initialized = 0;
inst->ntsc.format = CRT_PIX_FORMAT_RGBA;
inst->ntsc.w = width;
inst->ntsc.h = height;
inst->ntsc.raw = 0;
inst->ntsc.field = 0;
inst->ntsc.frame = 0;
inst->ntsc.as_color = 1;
inst->ntsc.hue = 0;
inst->ntsc.xoffset = 0;
inst->ntsc.yoffset = 0;
inst->ntsc.iirs_initialized = 0;
inst->noise = 0;
inst->progressive = 0;
inst->field = 0;
crt_init(&(inst->crt), width, height, CRT_PIX_FORMAT_RGBA, NULL);
inst->crt.blend = 0;
inst->crt.scanlines = 0;
return (f0r_instance_t)inst;
}
@@ -155,29 +119,13 @@ void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_i
switch(param_index)
{
case 0:
inst->noise = *((double*)param) * 100;
inst->noise = *((double*)param) * 200;
break;
case 1:
inst->ntsc_settings.vhs_mode = (int)CLAMP(*((double*)param) * 4, 0, 4);
inst->ntsc_settings.iirs_initialized = 0;
break;
case 2:
inst->crt.do_vhs_noise = (*((double*)param) >= 0.5);
break;
case 3:
inst->ntsc_settings.do_aberration = (*((double*)param) >= 0.5);
break;
case 4:
inst->crt.crt_do_vsync = !(*((double*)param) >= 0.5);
break;
case 5:
inst->crt.crt_do_hsync = !(*((double*)param) >= 0.5);
break;
case 6:
inst->progressive = (*((double*)param) >= 0.5);
break;
case 7:
inst->crt.blend = (*((double*)param) >= 0.5);
case 2:
inst->crt.scanlines = (*((double*)param) >= 0.5);
break;
}
}
@@ -188,50 +136,52 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_i
switch(param_index)
{
case 0:
*((double*)param) = (inst->noise / 100);
*((double*)param) = (inst->noise / 200);
break;
case 1:
*((double*)param) = (inst->ntsc_settings.vhs_mode / 4);
break;
case 2:
*((double*)param) = (inst->crt.do_vhs_noise ? 1.0 : 0.0);
break;
case 3:
*((double*)param) = (inst->ntsc_settings.do_aberration ? 1.0 : 0.0);
break;
case 4:
*((double*)param) = !(inst->crt.crt_do_vsync ? 1.0 : 0.0);
break;
case 5:
*((double*)param) = !(inst->crt.crt_do_hsync ? 1.0 : 0.0);
break;
case 6:
*((double*)param) = (inst->progressive ? 1.0 : 0.0);
break;
case 7:
*((double*)param) = (inst->crt.blend ? 1.0 : 0.0);
case 2:
*((double*)param) = (inst->crt.scanlines ? 1.0 : 0.0);
break;
}
}
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
{
ntsc_instance_t* inst = (ntsc_instance_t*)instance;
// clear the output frame
memset(outframe, 0, inst->width * inst->height * sizeof(uint32_t));
inst->crt.blend = 0;
// set everything up for the simulation
inst->crt.out = (char*)outframe;
inst->ntsc_settings.data = (const char*)inframe;
inst->ntsc.data = (const char*)inframe;
inst->ntsc_settings.field = inst->field & 1;
if (inst->ntsc_settings.field == 0) {
_render_field:
inst->ntsc.field = inst->field & 1;
if (inst->ntsc.field == 0) {
/* a frame is two fields */
inst->ntsc_settings.frame ^= 1;
inst->ntsc.frame ^= 1;
}
crt_modulate(&(inst->crt), &(inst->ntsc_settings));
// encode and decode ntsc signal
crt_modulate(&(inst->crt), &(inst->ntsc));
crt_demodulate(&(inst->crt), inst->noise);
if(!inst->progressive)
inst->field ^= 1;
// if we are in progressive mode, we render both fields onto the frame.
// in interlaced mode, we will hit the opposite field on the next frame.
if(inst->field && inst->progressive)
{
inst->field ^= 1;
// if we are not leaving scanlines blank, we will want to blend the frames in progressive mode
if(!inst->crt.scanlines)
{
inst->crt.blend = 1;
}
goto _render_field;
}
}