I have no idea what can be done with AviSynth and what cannot, and have never used it for any purpose, but the NTSC filtering process for NES is rather simple, at least if you have the source material in NES palette indexes format rather than in RGB format.
Here is my code (written in PHP of all things) for encoding NES graphics signal into NTSC, and for correspondingly decoding it in a very straightforward manner.
Language: PHP
<?php
function GenNTSCsignal($pixel, $phase, $length=8) // Generate NTSC signal corresponding to NES color
{
// Voltage levels, relative to synch voltage
$levels = Array(.350, .518, .962,1.550, // Signal low
1.094,1.506,1.962,1.962); // Signal high
// Decode the NES color ("eeellcccc" format).
$color = ($pixel & 0x0F); // 0..15 "cccc"
$level = ($pixel >> 4) & 3; // 0..3 "ll"
$emphasis = ($pixel >> 6); // 0..7 "eee"
if($color > 13) { $level = 1; } // For colors 14..15, level 1 is forced.
$result = Array();
for($p=0; $p<$length; ++$p)
{
// Generate the square wave:
$sig = $levels[ $level + 4 * ($color <= 12*(($color+$phase)%12 < 6)) ];
// When de-emphasis bits are set, some parts of the signal are attenuated:
if($emphasis & (0264513 >> (3*(($phase%12)>>1)))) $sig *= 0.746;
// Normalize the signal to 0..1 range, and save:
$result[] = ($sig - $levels[1]) / ($levels[7]-$levels[1]);
++$phase;
}
return $result;
}
function DecodeNTSCsignal($signal, $begin,$end, $phase, $saturation = 1.7, $brightness = 1.0)
{
$length = $end-$begin; $factor = $brightness/($length);
if($begin < 0) $begin = 0;
$c = count($signal);
if($end > $c) $end = $c;
$y = 0; $i = 0; $q = 0;
for($p = $begin; $p < $end; ++$p)
{
$value = $signal[$p] * $factor;
$y += $value;
$value *= $saturation;
$i += $value * cos( (3.141592653/6) * ($phase + $p));
$q += $value * sin( (3.141592653/6) * ($phase + $p));
}
$gamma = 2.0;
return
0x10000*(int)min(255, 255.95 * pow(max(0,$y + 0.946882*$i + 0.623557*$q), 2.2 / $gamma))
+ 0x00100*(int)min(255, 255.95 * pow(max(0,$y + -0.274788*$i + -0.635691*$q), 2.2 / $gamma))
+ 0x00001*(int)min(255, 255.95 * pow(max(0,$y + -1.108545*$i + 1.709007*$q), 2.2 / $gamma));
}
There are many ways to use it. For instance, this loop generates the entire 64-color palette (with black duplicates and stuff) in RGB:
Language: PHP
for($c=0; $c<64; ++$c)
{
$signal = GenNTSCsignal($c, 0, 12); // Generates 12 samples of NTSC signal at phase offset = 0
$rgb = DecodeNTSCsignal($signal, 0,12, 3.9);
$palette[$c] = $rgb;
}
To generate actual NTSC cross-talk & dot-crawl artifacts, you will need to generate NTSC signal for an entire scanline (8 samples per pixel, for a total of 2048 samples per scanline, with an offset that is varied on each scanline; alternatingly subtracting and adding 4); and then decode it at horizontal resolution of your choice. Decoding the RGB color corresponding to a certain
floating point X coordinate on the screen (e.g. 120.25 is perfectly valid) would be: $rgb = DecodeNTSCsignal($signal, $x*2048/256+0, $x*2048/256+12, 3.9);
And you can change the +0 and +12 according to your wishes. You get best results if you keep their relative difference as an integer multiple of 12, such as 48, but you might use 23 for some chroma dots, too. You can also use e.g. -6 and +6, but you may need to adjust the hue (phase) correspondingly. I don't remember whether that's the case.
This is a quick & lazy algorithm with not very much sophistication to it, but should get one started.
I have posted more details at the Nesdev wiki:
http://wiki.nesdev.com/w/index.php/NTSC_video
If your input image data does not consist of NES palette indexes, but already made RGB values, then you need more work. I don't have code for that provided, but I think you need to convert the RGB into YIQ, determine the phase & amplitude and use them to form the $sig within the inner loop of GenNTSCsignal(). EDIT: Wait a second,
I do. I provided that code with the video linked to in the opening post of this thread. It just has a very significant "this is probably wrong" in it that I cannot shake.