Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark
Created July 10, 2018 16:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikuAuahDark/cef9f9bb27fffe7ff07158ea7d5c03b2 to your computer and use it in GitHub Desktop.
Save MikuAuahDark/cef9f9bb27fffe7ff07158ea7d5c03b2 to your computer and use it in GitHub Desktop.
Play grayscale image as audio (really?)
--[[
Some notice:
1. This code is compatible with LOVE 0.10.0, and LOVE 11.0 (probably later version too)
2. Audio must have sample rate of 28224Hz
3. Audio is encoded to 8bit PCM (128 denotes 0) then converted to PNG
]]
local ffi = require("ffi")
function love.conf(t)
t.window = nil
t.modules.joystick = false
t.modules.math = false
t.modules.physics = false
t.modules.system = false
t.modules.thread = false
t.modules.touch = false
t.modules.video = false
end
local getChannelCount
if love._version >= "11.0" then
function getChannelCount(sd)
return sd:getChannelCount()
end
else
function getChannelCount(sd)
return sd:getChannels()
end
end
local astr = "Drag & drop grayscale image here\nImage audio must have sample rate of 28224"
local audiofile
function love.load(arg)
-- if it's not fused, that means the first arg is the project name
if love.filesystem.isFused() == false then table.remove(arg, 1) end
-- If arg is 1, that means load directly
if #arg == 1 then
local luafile = assert(io.open(arg[1], "rb"))
local f = love.filesystem.newFileData(luafile:read("*a"), "")
luafile:close()
local image = love.image.newImageData(f)
local width, height = image:getDimensions()
local sounddata = love.sound.newSoundData(width * height, 28224, 8, 1)
local ptr = ffi.cast("uint8_t*", sounddata:getPointer())
for i = 0, width * height - 1 do
ptr[i] = image:getPixel(i % width, math.floor(i / width)) * 255
end
if audiofile then audiofile:stop() end
audiofile = love.audio.newSource(sounddata)
audiofile:setLooping(true)
audiofile:play()
astr = string.format("Playing %s", arg[1]:match(".-([^\\/]-%.?[^%.\\/]*)$"))
-- If arg is 2, that mean encode audio to image
elseif #arg >= 2 then
local luafile = assert(io.open(arg[1], "rb"))
local f = love.filesystem.newFileData(luafile:read("*a"), arg[1]:match(".-([^\\/]-%.?[^%.\\/]*)$"))
luafile:close()
luafile = assert(io.open(arg[2], "wb"))
local sounddata = love.sound.newSoundData(f)
local stereo = getChannelCount(sounddata) == 2
local ratio = sounddata:getSampleRate() / 28224
local imagesize = math.floor(math.sqrt(sounddata:getSampleCount() / ratio)) -- losing few samples is okay
local image = love.image.newImageData(imagesize, imagesize)
local iptr = ffi.cast("uint32_t*", image:getPointer())
for i = 0, imagesize*imagesize-1 do
local smpidx = i * ratio
local i1, i2 = math.floor(smpidx), math.ceil(smpidx)
local ifract = math.sin((smpidx - i1) * math.pi/2)
local v
if stereo then
local sl = math.min(math.max((1 - ifract) * sounddata:getSample(i1 * 2) + ifract * sounddata:getSample(i2 * 2), -1), 1)
local sr = math.min(math.max((1 - ifract) * sounddata:getSample(i1*2+1) + ifract * sounddata:getSample(i2*2+1), -1), 1)
v = (sl + sr) * 0.5
else
v = math.min(math.max((1 - ifract) * sounddata:getSample(i1, 1) + ifract * sounddata:getSample(i2, 1), -1), 1)
end
-- 128 is 0, 255 is 1, 1 is 0
v = math.floor(v * 127 + 128)
iptr[i] = 4278190080 + v * 65536 + v * 256 + v
end
luafile:write(image:encode("png"):getString())
luafile:close()
love.event.quit()
return
end
love.window.setMode(450, 50, {
resizable = true
})
love.window.setTitle(string.format("ImagePlayer - LÖVE %s (%s)", love._version, love._version_codename))
end
function love.filedropped(file)
local basename = file:getFilename():match(".-([^\\/]-%.?[^%.\\/]*)$")
local s, image = pcall(love.image.newImageData, file)
if s then
local width, height = image:getDimensions()
local sounddata = love.sound.newSoundData(width * height, 28224, 8, 1)
local ptr = ffi.cast("uint8_t*", sounddata:getPointer())
for i = 0, width * height - 1 do
ptr[i] = image:getPixel(i % width, math.floor(i / width)) * 255
end
if audiofile then audiofile:stop() end
audiofile = love.audio.newSource(sounddata)
audiofile:setLooping(true)
audiofile:play()
astr = string.format("Playing %s", basename)
else
astr = string.format("%s\n%s", basename, image)
end
end
function love.draw()
collectgarbage()
love.graphics.print(astr, 2, 2)
end
function love.keyreleased(key)
if key == "space" and audiofile then
if audiofile:isPlaying() then
audiofile:pause()
else
audiofile:play()
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment