#include <stdlib.h>
#include <string.h>
#include "frei0r.h"
#include "frei0r/math.h"
#include "crt_core.h"
// the actual NTSC emulation code is from here: https://github.com/LMP88959/NTSC-CRT
typedef struct ntsc_instance
{
// image dimensions
int width;
int height;
// parameters
struct CRT crt;
struct NTSC_SETTINGS ntsc;
int noise;
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()
{
return 1;
}
void f0r_deinit()
{}
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{
info->name = "NTSC";
info->author = "EMMIR, esmane";
info->explanation = "Simulates NTSC analog video.";
info->plugin_type = F0R_PLUGIN_TYPE_FILTER;
info->color_model = F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version = FREI0R_MAJOR_VERSION;
info->major_version = 0;
info->minor_version = 2;
info->num_params = 3;
}
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
{
case 0:
info->name = "Signal Noise";
info->explanation = "Amount of noise introduced into the NTSC signal.";
info->type = F0R_PARAM_DOUBLE;
break;
case 1:
info->name = "Progressive Scan";
info->explanation = "Toggles progressive scan (Interlaced if off).";
info->type = F0R_PARAM_BOOL;
break;
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));
inst->width = width;
inst->height = height;
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;
}
void f0r_destruct(f0r_instance_t instance)
{
ntsc_instance_t* inst = (ntsc_instance_t*)instance;
free(instance);
}
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
ntsc_instance_t* inst = (ntsc_instance_t*)instance;
switch(param_index)
{
case 0:
inst->noise = *((double*)param) * 200;
break;
case 1:
inst->progressive = (*((double*)param) >= 0.5);
break;
case 2:
inst->crt.scanlines = (*((double*)param) >= 0.5);
break;
}
}
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
ntsc_instance_t* inst = (ntsc_instance_t*)instance;
switch(param_index)
{
case 0:
*((double*)param) = (inst->noise / 200);
break;
case 1:
*((double*)param) = (inst->progressive ? 1.0 : 0.0);
break;
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.data = (const char*)inframe;
_render_field:
inst->ntsc.field = inst->field & 1;
if (inst->ntsc.field == 0) {
/* a frame is two fields */
inst->ntsc.frame ^= 1;
}
// encode and decode ntsc signal
crt_modulate(&(inst->crt), &(inst->ntsc));
crt_demodulate(&(inst->crt), inst->noise);
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)
{
// 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;
}
}