#include <stdlib.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
typedef struct ntsc_instance
{
// image dimensions
int width;
int height;
// parameters
struct CRT crt;
struct NTSC_SETTINGS ntsc_settings;
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()
{
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 = 1;
info->num_params = 8;
}
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 = "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.";
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->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->noise = 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) * 100;
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);
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 / 100);
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);
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;
inst->crt.out = (char*)outframe;
inst->ntsc_settings.data = (const char*)inframe;
inst->ntsc_settings.field = inst->field & 1;
if (inst->ntsc_settings.field == 0) {
/* a frame is two fields */
inst->ntsc_settings.frame ^= 1;
}
crt_modulate(&(inst->crt), &(inst->ntsc_settings));
crt_demodulate(&(inst->crt), inst->noise);
if(!inst->progressive)
{
inst->field ^= 1;
}
}