Last active
June 17, 2020 09:27
-
-
Save ZakBlystone/f13e2dd5872a8387fd27c9151e4b0d8a to your computer and use it in GitHub Desktop.
GLua experiment to take a screenshot and generate an SSTV signal from it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- SSTV experiment (CC0 Public Domain, free to use in whatever you want) | |
-- Created by: Zachary Blystone ( [email protected] ) | |
local rate = 44100 | |
local image = {} | |
local function co() | |
local dt = 1 / rate | |
local p = 0 | |
local function tone( t, freq ) | |
for i=1, math.Round(t * rate) do | |
coroutine.yield( math.sin( p * math.pi * 2 ) * .2 ) | |
p = p + dt * freq | |
end | |
end | |
local function bit( b ) | |
tone(.03, b == 1 and 1100 or 1300) -- 1100 = 1, 1300 = 0 | |
end | |
local function brt( b ) | |
return 1500 * (1-b) + 2300 * (b) | |
end | |
-- I don't know if this first part is correct, I'm just guessing by ear | |
-- Couldn't find any documentation on it | |
tone(.1, 1900) | |
tone(.1, 1500) | |
tone(.1, 1900) | |
tone(.1, 1500) | |
tone(.1, 2300) | |
tone(.1, 1500) | |
tone(.1, 2300) | |
tone(.1, 1500) | |
tone(.1, 1900) | |
tone(.3, 1900) -- Leader tone | |
tone(.01, 1200) -- break | |
tone(.3, 1900) -- Leader tone | |
local mode = 8 | |
tone(.03, 1200) -- VIS Start bit | |
bit(0) -- bit 0 | |
bit(0) | |
bit(1) | |
bit(1) | |
bit(0) | |
bit(0) | |
bit(0) -- bit 6 | |
bit(0) -- Parity (0 = even, 1 = odd) | |
tone(.03, 1200) -- VIS Stop bit | |
-- Samples an RGB pixel from the image | |
local function sampleRGB(x,y) | |
local col = image[y * 256 + x] | |
return col.r/255, col.g/255, col.b/255 | |
end | |
-- Converts RGB to Y, R-Y, B-Y | |
local function sampleYRB(x,y) | |
local r,g,b = sampleRGB(x,y) | |
local Y = 16 + (65.738 * r + 129.057 * g + 25.064 * b) | |
local R = 128 + (112.439 * r -94.154 * g - 18.285 * b) | |
local B = 128 + (-37.945 * r -74.494 * g + 112.439 * b) | |
return Y/255,R/255,B/255 | |
end | |
for i=0, 239 do | |
tone(0.009, 1200) -- Sync | |
tone(0.003, 1500) -- Porch | |
for j=0, 255 do local y,r,b = sampleYRB(j, i) tone(0.138 / 256, brt(y)) end -- Y scan | |
tone(0.0045, 1500) -- Separator pulse | |
tone(0.0015, 1900) -- Porch | |
for j=0, 127 do local y,r,b = sampleYRB(j*2, i) tone(0.069 / 128, brt(r)) end -- R-Y scan | |
tone(0.0045, 2300) -- Separator pulse | |
tone(0.0015, 1500) -- Porch | |
for j=0, 127 do local y,r,b = sampleYRB(j*2, i) tone(0.069 / 128, brt(b)) end -- B-Y scan | |
end | |
-- Couldn't find documentation on the footer signal | |
end | |
local capturedFrame = false | |
local doCapture = false | |
hook.Add("PostRender", "SSTV_Capture", function() | |
if doCapture and not capturedFrame then | |
render.CapturePixels() | |
-- Save pixels into image | |
for y=0, 240-1 do | |
for x=0, 256-1 do | |
local u = x/256 | |
local v = y/240 | |
local r,g,b = render.ReadPixel(u * ScrW(), v * ScrH()) | |
image[y * 256 + x] = Color(r,g,b) | |
end | |
end | |
doCapture = false | |
capturedFrame = true | |
end | |
end) | |
concommand.Add("sstv", function() | |
capturedFrame = false | |
doCapture = true | |
LocalPlayer():ConCommand("stopsound") | |
timer.Simple(.2, function() | |
if capturedFrame then | |
sstv_id = (sstv_id or 0) + 1 | |
local sstv_file = "sstvz_" .. sstv_id | |
local c = coroutine.create(co) | |
local function data(t) | |
local b, x = coroutine.resume(c) | |
if b and x then return x end | |
return 0 | |
end | |
sound.Generate(sstv_file, rate, 78, data) | |
surface.PlaySound(sstv_file) | |
else | |
print("NO FRAME") | |
end | |
end) | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment