Created
July 10, 2018 16:18
-
-
Save MikuAuahDark/cef9f9bb27fffe7ff07158ea7d5c03b2 to your computer and use it in GitHub Desktop.
Play grayscale image as audio (really?)
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
--[[ | |
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