Skip to content

Instantly share code, notes, and snippets.

@ZakBlystone
Last active June 17, 2020 09:27
Show Gist options
  • Save ZakBlystone/f13e2dd5872a8387fd27c9151e4b0d8a to your computer and use it in GitHub Desktop.
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
-- 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