2016-05-27 20:51:51 +00:00
|
|
|
pico8 = {
|
|
|
|
fps = 30,
|
2016-05-27 19:16:33 +00:00
|
|
|
pal_transparent = {},
|
|
|
|
resolution = {128,128},
|
|
|
|
palette = {
|
|
|
|
{0,0,0,255},
|
|
|
|
{29,43,83,255},
|
|
|
|
{126,37,83,255},
|
|
|
|
{0,135,81,255},
|
|
|
|
{171,82,54,255},
|
|
|
|
{95,87,79,255},
|
|
|
|
{194,195,199,255},
|
|
|
|
{255,241,232,255},
|
|
|
|
{255,0,77,255},
|
|
|
|
{255,163,0,255},
|
|
|
|
{255,240,36,255},
|
|
|
|
{0,231,86,255},
|
|
|
|
{41,173,255,255},
|
|
|
|
{131,118,156,255},
|
|
|
|
{255,119,168,255},
|
|
|
|
{255,204,170,255}
|
|
|
|
},
|
2016-05-29 04:19:55 +00:00
|
|
|
spriteflags = {},
|
2016-05-27 19:16:33 +00:00
|
|
|
audio_channels = {
|
|
|
|
[0]={oscpos=0},
|
|
|
|
[1]={oscpos=0},
|
|
|
|
[2]={oscpos=0},
|
|
|
|
[3]={oscpos=0}
|
|
|
|
},
|
|
|
|
sfx = {},
|
|
|
|
music = {},
|
|
|
|
current_music = nil,
|
2016-05-29 04:19:55 +00:00
|
|
|
usermemory = {},
|
|
|
|
cartdata = {},
|
2016-05-27 19:16:33 +00:00
|
|
|
keypressed = {
|
|
|
|
[0] = {},
|
|
|
|
[1] = {}
|
|
|
|
},
|
|
|
|
keymap = {
|
|
|
|
[0] = {
|
|
|
|
[0] = {'left'},
|
|
|
|
[1] = {'right'},
|
|
|
|
[2] = {'up'},
|
|
|
|
[3] = {'down'},
|
|
|
|
[4] = {'z','n'},
|
|
|
|
[5] = {'x','m'},
|
|
|
|
},
|
|
|
|
[1] = {
|
|
|
|
[0] = {'s'},
|
|
|
|
[1] = {'f'},
|
|
|
|
[2] = {'e'},
|
|
|
|
[3] = {'d'},
|
|
|
|
[4] = {'tab','lshift'},
|
|
|
|
[5] = {'q','a'},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
cursor = {0,0},
|
|
|
|
camera_x = 0,
|
|
|
|
camera_y = 0,
|
2016-05-27 20:51:51 +00:00
|
|
|
draw_palette = {},
|
|
|
|
display_palette = {},
|
|
|
|
pal_transparent = {},
|
2015-09-11 14:49:38 +00:00
|
|
|
}
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
require("strict")
|
|
|
|
local bit = require("bit")
|
|
|
|
local QueueableSource = require "QueueableSource"
|
|
|
|
|
|
|
|
local flr,abs = math.floor, math.abs
|
|
|
|
|
|
|
|
local frametime = 1/pico8.fps
|
2015-08-20 10:03:01 +00:00
|
|
|
local cart = nil
|
|
|
|
local cartname = nil
|
|
|
|
local love_args = nil
|
2015-08-16 16:00:39 +00:00
|
|
|
local scale = 4
|
2015-08-17 14:47:42 +00:00
|
|
|
local xpadding = 8.5
|
|
|
|
local ypadding = 3.5
|
2015-08-16 16:00:39 +00:00
|
|
|
local __accum = 0
|
2015-09-12 09:30:38 +00:00
|
|
|
local __audio_buffer_size = 1024
|
2015-09-12 03:54:23 +00:00
|
|
|
|
2015-08-20 10:03:01 +00:00
|
|
|
local video_frames = nil
|
2015-08-21 16:10:34 +00:00
|
|
|
local osc
|
2015-08-17 14:47:42 +00:00
|
|
|
local host_time = 0
|
|
|
|
local retro_mode = false
|
2015-09-09 11:51:34 +00:00
|
|
|
local __audio_channels
|
2015-09-12 09:30:38 +00:00
|
|
|
local __sample_rate = 22050
|
2015-09-12 03:54:23 +00:00
|
|
|
local channels = 1
|
2015-09-12 09:30:38 +00:00
|
|
|
local bits = 16
|
2016-05-27 19:16:33 +00:00
|
|
|
local paused = false
|
|
|
|
local api, cart
|
2015-08-21 16:10:34 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
log = print
|
2016-05-27 20:51:51 +00:00
|
|
|
--log = function() end
|
|
|
|
|
|
|
|
function shdr_unpack(thing)
|
|
|
|
return unpack(thing, 0, 15)
|
|
|
|
end
|
2015-08-21 16:10:34 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local function get_bits(v,s,e)
|
2015-11-27 21:02:07 +00:00
|
|
|
local mask = bit.lshift(bit.lshift(1,s)-1,e)
|
|
|
|
return bit.rshift(bit.band(mask,v))
|
2015-08-21 16:10:34 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function restore_clip()
|
|
|
|
if pico8.clip then
|
|
|
|
love.graphics.setScissor(unpack(pico8.clip))
|
|
|
|
else
|
|
|
|
love.graphics.setScissor(0,0,pico8.resolution[1],pico8.resolution[2])
|
|
|
|
end
|
|
|
|
end
|
2015-09-13 10:12:07 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local function _load(_cartname)
|
|
|
|
love.graphics.setShader(pico8.draw_shader)
|
|
|
|
love.graphics.setCanvas(pico8.screen)
|
|
|
|
love.graphics.origin()
|
|
|
|
api.camera()
|
|
|
|
restore_clip()
|
|
|
|
cartname = _cartname
|
|
|
|
pico8.cart = cart.load_p8(_cartname)
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function love.resize(w,h)
|
|
|
|
love.graphics.clear()
|
|
|
|
-- adjust stuff to fit the screen
|
|
|
|
if w > h then
|
|
|
|
scale = h/(pico8.resolution[2]+ypadding*2)
|
|
|
|
else
|
|
|
|
scale = w/(pico8.resolution[1]+xpadding*2)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function note_to_hz(note)
|
|
|
|
return 440*math.pow(2,(note-33)/12)
|
|
|
|
end
|
2015-09-09 11:51:34 +00:00
|
|
|
|
2015-08-16 16:00:39 +00:00
|
|
|
function love.load(argv)
|
|
|
|
love_args = argv
|
2015-08-17 14:47:42 +00:00
|
|
|
if love.system.getOS() == "Android" then
|
2016-06-04 02:22:49 +00:00
|
|
|
love.resize(love.graphics.getDimensions())
|
2015-08-17 14:47:42 +00:00
|
|
|
else
|
2016-05-27 19:16:33 +00:00
|
|
|
love.window.setMode(pico8.resolution[1]*scale+xpadding*scale*2,pico8.resolution[2]*scale+ypadding*scale*2)
|
2015-08-17 14:47:42 +00:00
|
|
|
end
|
2015-08-21 16:10:34 +00:00
|
|
|
|
|
|
|
osc = {}
|
2015-11-27 23:37:53 +00:00
|
|
|
-- tri
|
|
|
|
osc[0] = function(x)
|
|
|
|
local t = x%1
|
|
|
|
return (abs(t*2-1)*2-1) * 2/3
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- uneven tri
|
|
|
|
osc[1] = function(x)
|
|
|
|
local t = x%1
|
|
|
|
return (((t < 0.875) and (t * 16 / 7) or ((1-t)*16)) -1) * 0.6
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- saw
|
|
|
|
osc[2] = function(x)
|
|
|
|
return (x%1-0.5) * 0.9
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- sqr
|
|
|
|
osc[3] = function(x)
|
|
|
|
return (x%1 < 0.5 and 1 or -1) * 1/3
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- pulse
|
2015-09-09 11:51:34 +00:00
|
|
|
osc[4] = function(x)
|
2015-11-27 23:37:53 +00:00
|
|
|
return (x%1 < 0.3 and 1 or -1) * 1/3
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- tri/2
|
2015-09-09 11:51:34 +00:00
|
|
|
osc[5] = function(x)
|
2015-11-27 23:37:53 +00:00
|
|
|
x = x * 4
|
|
|
|
return (abs((x%2)-1)-0.5 + (abs(((x*0.5)%2)-1)-0.5)/2-0.1)*0.7
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
osc[6] = function()
|
2015-09-12 03:54:23 +00:00
|
|
|
-- noise FIXME: (zep said this is brown noise)
|
2015-11-27 23:37:53 +00:00
|
|
|
local lastx = 0
|
2015-11-28 23:33:34 +00:00
|
|
|
local sample = 0
|
2015-11-27 23:37:53 +00:00
|
|
|
local lsample = 0
|
2015-11-28 23:33:34 +00:00
|
|
|
local tscale = note_to_hz(63)/__sample_rate
|
2015-11-27 23:37:53 +00:00
|
|
|
return function(x)
|
2015-11-28 23:33:34 +00:00
|
|
|
local scale = (x-lastx)/tscale
|
|
|
|
lsample = sample
|
|
|
|
sample = (lsample+scale*(love.math.random()*2-1))/(1+scale)
|
2015-11-27 23:37:53 +00:00
|
|
|
lastx = x
|
2016-05-27 19:16:33 +00:00
|
|
|
return math.min(math.max((lsample+sample)*4/3*(1.75-scale),-1),1)
|
2015-09-12 18:14:00 +00:00
|
|
|
end
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- detuned tri
|
2015-09-09 11:51:34 +00:00
|
|
|
osc[7] = function(x)
|
2015-11-27 23:37:53 +00:00
|
|
|
x = x * 2
|
|
|
|
return (abs((x%2)-1)-0.5 + (abs(((x*0.97)%2)-1)-0.5)/2) * 2/3
|
2015-09-12 18:14:00 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
-- saw from 0 to 1, used for arppregiator
|
|
|
|
osc["saw_lfo"] = function(x)
|
|
|
|
return x%1
|
2015-09-09 11:51:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
__audio_channels = {
|
2015-09-12 03:54:23 +00:00
|
|
|
[0]=QueueableSource:new(8),
|
|
|
|
QueueableSource:new(8),
|
|
|
|
QueueableSource:new(8),
|
|
|
|
QueueableSource:new(8)
|
2015-09-09 11:51:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i=0,3 do
|
|
|
|
__audio_channels[i]:play()
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.audio_channels[i].noise = osc[6]()
|
2015-08-21 16:10:34 +00:00
|
|
|
end
|
|
|
|
|
2015-08-18 09:45:04 +00:00
|
|
|
love.graphics.clear()
|
2015-08-16 16:00:39 +00:00
|
|
|
love.graphics.setDefaultFilter('nearest','nearest')
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.screen = love.graphics.newCanvas(pico8.resolution[1],pico8.resolution[2])
|
|
|
|
pico8.screen:setFilter('linear','nearest')
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2015-11-28 23:33:34 +00:00
|
|
|
local glyphs = ""
|
|
|
|
for i = 32,126 do
|
|
|
|
glyphs = glyphs..string.char(i)
|
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
local font = love.graphics.newImageFont("font.png",glyphs,1)
|
2015-08-16 16:00:39 +00:00
|
|
|
love.graphics.setFont(font)
|
|
|
|
font:setFilter('nearest','nearest')
|
|
|
|
|
|
|
|
love.mouse.setVisible(false)
|
2015-08-20 10:03:01 +00:00
|
|
|
love.window.setTitle("picolove")
|
2015-08-16 16:00:39 +00:00
|
|
|
love.graphics.setLineStyle('rough')
|
|
|
|
love.graphics.setPointSize(1)
|
|
|
|
love.graphics.setLineWidth(1)
|
|
|
|
|
|
|
|
love.graphics.origin()
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.setCanvas(pico8.screen)
|
2015-08-18 12:00:57 +00:00
|
|
|
restore_clip()
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 20:51:51 +00:00
|
|
|
for i=0,15 do
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.draw_palette[i] = i
|
2016-05-27 20:51:51 +00:00
|
|
|
pico8.pal_transparent[i] = i == 0 and 0 or 1
|
|
|
|
pico8.display_palette[i] = pico8.palette[i+1]
|
2015-08-18 09:45:04 +00:00
|
|
|
end
|
2015-08-17 14:47:42 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.draw_shader = love.graphics.newShader([[
|
2016-06-02 20:30:09 +00:00
|
|
|
extern float palette[16];
|
2015-08-17 14:47:42 +00:00
|
|
|
|
|
|
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
2016-06-04 02:22:49 +00:00
|
|
|
int index = int(color.r*255.0+0.5);
|
2016-05-27 20:51:51 +00:00
|
|
|
return vec4(palette[index]/255.0,0.0,0.0,1.0);
|
2015-08-17 14:47:42 +00:00
|
|
|
}]])
|
2016-06-02 20:30:09 +00:00
|
|
|
pico8.draw_shader:send('palette',shdr_unpack(pico8.draw_palette))
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.sprite_shader = love.graphics.newShader([[
|
2016-06-02 20:30:09 +00:00
|
|
|
extern float palette[16];
|
2015-08-18 09:45:04 +00:00
|
|
|
extern float transparent[16];
|
2015-08-17 14:47:42 +00:00
|
|
|
|
|
|
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
2016-06-04 02:22:49 +00:00
|
|
|
int index = int(Texel(texture, texture_coords).r*255.0+0.5);
|
2016-05-27 20:51:51 +00:00
|
|
|
return vec4(palette[index]/255.0,0.0,0.0,transparent[index]);
|
2015-08-17 14:47:42 +00:00
|
|
|
}]])
|
2016-06-02 20:30:09 +00:00
|
|
|
pico8.sprite_shader:send('palette',shdr_unpack(pico8.draw_palette))
|
2016-05-27 20:51:51 +00:00
|
|
|
pico8.sprite_shader:send('transparent',shdr_unpack(pico8.pal_transparent))
|
2015-08-17 14:47:42 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.text_shader = love.graphics.newShader([[
|
2016-06-02 20:30:09 +00:00
|
|
|
extern float palette[16];
|
2015-08-16 16:00:39 +00:00
|
|
|
|
|
|
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
|
|
|
vec4 texcolor = Texel(texture, texture_coords);
|
2016-06-04 02:22:49 +00:00
|
|
|
int index = int(color.r*255.0+0.5);
|
2016-05-27 20:51:51 +00:00
|
|
|
return vec4(palette[index]/255.0,0.0,0.0,texcolor.a);
|
2015-08-16 16:00:39 +00:00
|
|
|
}]])
|
2016-06-02 20:30:09 +00:00
|
|
|
pico8.text_shader:send('palette',shdr_unpack(pico8.draw_palette))
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.display_shader = love.graphics.newShader([[
|
2015-08-18 09:45:04 +00:00
|
|
|
extern vec4 palette[16];
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2015-08-17 14:47:42 +00:00
|
|
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
2016-06-04 02:22:49 +00:00
|
|
|
int index = int(Texel(texture, texture_coords).r*255.0+0.5);
|
2015-08-17 14:47:42 +00:00
|
|
|
// lookup the colour in the palette by index
|
2016-05-27 20:51:51 +00:00
|
|
|
return palette[index]/255.0;
|
2015-08-17 14:47:42 +00:00
|
|
|
}]])
|
2016-05-27 20:51:51 +00:00
|
|
|
pico8.display_shader:send('palette',shdr_unpack(pico8.display_palette))
|
2015-08-17 14:47:42 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
api=require("api")
|
|
|
|
cart=require("cart")
|
2015-08-16 16:00:39 +00:00
|
|
|
|
|
|
|
-- load the cart
|
2016-05-27 19:16:33 +00:00
|
|
|
api.clip()
|
|
|
|
api.camera()
|
|
|
|
api.pal()
|
|
|
|
api.palt()
|
|
|
|
api.color(6)
|
2015-09-11 14:49:38 +00:00
|
|
|
|
2015-09-11 11:18:24 +00:00
|
|
|
_load(argv[2] or 'nocart.p8')
|
2016-05-27 19:16:33 +00:00
|
|
|
api.run()
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function love.update(dt)
|
2015-08-18 16:35:53 +00:00
|
|
|
for p=0,1 do
|
2016-05-27 19:16:33 +00:00
|
|
|
for i=0,#pico8.keymap[p] do
|
|
|
|
for _,key in pairs(pico8.keymap[p][i]) do
|
|
|
|
local v = pico8.keypressed[p][i]
|
2015-10-12 21:06:55 +00:00
|
|
|
if v then
|
|
|
|
v = v + 1
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.keypressed[p][i] = v
|
2015-10-12 21:06:55 +00:00
|
|
|
break
|
|
|
|
end
|
2015-08-18 16:35:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
if pico8.cart._update then pico8.cart._update() end
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function restore_camera()
|
|
|
|
love.graphics.origin()
|
|
|
|
love.graphics.translate(-pico8.camera_x,-pico8.camera_y)
|
2015-08-17 14:47:42 +00:00
|
|
|
end
|
|
|
|
|
2016-05-30 04:23:51 +00:00
|
|
|
function flip_screen()
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.setShader(pico8.display_shader)
|
2016-05-27 20:51:51 +00:00
|
|
|
pico8.display_shader:send('palette',shdr_unpack(pico8.display_palette))
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.setCanvas()
|
|
|
|
love.graphics.origin()
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
-- love.graphics.setColor(255,255,255,255)
|
|
|
|
love.graphics.setScissor()
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.clear()
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local screen_w,screen_h = love.graphics.getDimensions()
|
|
|
|
if screen_w > screen_h then
|
|
|
|
love.graphics.draw(pico8.screen,screen_w/2-64*scale,ypadding*scale,0,scale,scale)
|
|
|
|
else
|
|
|
|
love.graphics.draw(pico8.screen,xpadding*scale,screen_h/2-64*scale,0,scale,scale)
|
|
|
|
end
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.present()
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
if video_frames then
|
|
|
|
local tmp = love.graphics.newCanvas(pico8.resolution[1],pico8.resolution[2])
|
|
|
|
love.graphics.setCanvas(tmp)
|
|
|
|
love.graphics.draw(pico8.screen,0,0)
|
|
|
|
table.insert(video_frames,tmp:newImageData())
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
-- get ready for next time
|
|
|
|
love.graphics.setShader(pico8.draw_shader)
|
|
|
|
love.graphics.setCanvas(pico8.screen)
|
|
|
|
restore_clip()
|
|
|
|
restore_camera()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function lowpass(y0,y1, cutoff)
|
|
|
|
local RC = 1.0/(cutoff*2*3.14)
|
|
|
|
local dt = 1.0/__sample_rate
|
|
|
|
local alpha = dt/(RC+dt)
|
|
|
|
return y0 + (alpha*(y1 - y0))
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local note_map = {
|
2015-09-12 09:30:38 +00:00
|
|
|
[0] = 'C-',
|
2015-09-13 10:12:07 +00:00
|
|
|
'C#',
|
|
|
|
'D-',
|
|
|
|
'D#',
|
|
|
|
'E-',
|
|
|
|
'F-',
|
|
|
|
'F#',
|
|
|
|
'G-',
|
|
|
|
'G#',
|
|
|
|
'A-',
|
|
|
|
'A#',
|
|
|
|
'B-',
|
2015-09-12 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local function note_to_string(note)
|
2015-09-12 09:30:38 +00:00
|
|
|
local octave = flr(note/12)
|
|
|
|
local note = flr(note%12)
|
|
|
|
return string.format("%s%d",note_map[note],octave)
|
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local function oldosc(osc)
|
2015-11-27 23:37:53 +00:00
|
|
|
local x = 0
|
|
|
|
return function(freq)
|
|
|
|
x = x + freq/__sample_rate
|
|
|
|
return osc(x)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local function lerp(a,b,t)
|
|
|
|
return (b-a)*t+a
|
|
|
|
end
|
|
|
|
|
2015-09-12 03:54:23 +00:00
|
|
|
function update_audio(time)
|
|
|
|
-- check what sfx should be playing
|
2015-09-12 09:30:38 +00:00
|
|
|
local samples = flr(time*__sample_rate)
|
2015-09-12 15:14:53 +00:00
|
|
|
|
|
|
|
for i=0,samples-1 do
|
2016-05-27 19:16:33 +00:00
|
|
|
if pico8.current_music then
|
|
|
|
pico8.current_music.offset = pico8.current_music.offset + 1/(48*15.25)*(1/pico8.current_music.speed*4)
|
|
|
|
if pico8.current_music.offset >= 32 then
|
|
|
|
local next_track = pico8.current_music.music
|
|
|
|
if pico8.music[next_track].loop == 2 then
|
2015-09-12 15:14:53 +00:00
|
|
|
-- go back until we find the loop start
|
|
|
|
while true do
|
2016-05-27 19:16:33 +00:00
|
|
|
if pico8.music[next_track].loop == 1 or next_track == 0 then
|
2015-09-12 15:14:53 +00:00
|
|
|
break
|
|
|
|
end
|
|
|
|
next_track = next_track - 1
|
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
elseif pico8.music[pico8.current_music.music].loop == 4 then
|
2015-09-12 15:14:53 +00:00
|
|
|
next_track = nil
|
2016-05-27 19:16:33 +00:00
|
|
|
elseif pico8.music[pico8.current_music.music].loop <= 1 then
|
2015-09-12 15:14:53 +00:00
|
|
|
next_track = next_track + 1
|
|
|
|
end
|
|
|
|
if next_track then
|
2016-05-27 19:16:33 +00:00
|
|
|
api.music(next_track)
|
2015-09-12 15:14:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
local music = pico8.current_music and pico8.music[pico8.current_music.music] or nil
|
2015-09-12 15:14:53 +00:00
|
|
|
|
|
|
|
for channel=0,3 do
|
2016-05-27 19:16:33 +00:00
|
|
|
local ch = pico8.audio_channels[channel]
|
2015-09-12 15:14:53 +00:00
|
|
|
local tick = 0
|
|
|
|
local tickrate = 60*16
|
|
|
|
local note,instr,vol,fx
|
|
|
|
local freq
|
|
|
|
|
2015-09-12 09:30:38 +00:00
|
|
|
if ch.bufferpos == 0 or ch.bufferpos == nil then
|
|
|
|
ch.buffer = love.sound.newSoundData(__audio_buffer_size,__sample_rate,bits,channels)
|
|
|
|
ch.bufferpos = 0
|
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
if ch.sfx and pico8.sfx[ch.sfx] then
|
|
|
|
local sfx = pico8.sfx[ch.sfx]
|
2015-11-28 23:33:34 +00:00
|
|
|
ch.offset = ch.offset + 1/(48*15.25)*(1/sfx.speed*4)
|
2015-09-12 15:14:53 +00:00
|
|
|
if sfx.loop_end ~= 0 and ch.offset >= sfx.loop_end then
|
2015-09-12 09:30:38 +00:00
|
|
|
if ch.loop then
|
|
|
|
ch.last_step = -1
|
|
|
|
ch.offset = sfx.loop_start
|
|
|
|
else
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.audio_channels[channel].sfx = nil
|
2015-09-12 09:30:38 +00:00
|
|
|
end
|
|
|
|
elseif ch.offset >= 32 then
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.audio_channels[channel].sfx = nil
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
2015-09-12 09:30:38 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
if ch.sfx and pico8.sfx[ch.sfx] then
|
|
|
|
local sfx = pico8.sfx[ch.sfx]
|
2015-09-12 09:30:38 +00:00
|
|
|
-- when we pass a new step
|
|
|
|
if flr(ch.offset) > ch.last_step then
|
|
|
|
ch.lastnote = ch.note
|
|
|
|
ch.note,ch.instr,ch.vol,ch.fx = unpack(sfx[flr(ch.offset)])
|
2015-11-27 23:37:53 +00:00
|
|
|
if ch.instr ~= 6 then
|
|
|
|
ch.osc = osc[ch.instr]
|
|
|
|
else
|
|
|
|
ch.osc = ch.noise
|
|
|
|
end
|
2015-09-12 18:14:00 +00:00
|
|
|
if ch.fx == 2 then
|
2015-11-27 23:37:53 +00:00
|
|
|
ch.lfo = oldosc(osc[0])
|
2015-09-12 18:14:00 +00:00
|
|
|
elseif ch.fx >= 6 then
|
2015-11-27 23:37:53 +00:00
|
|
|
ch.lfo = oldosc(osc["saw_lfo"])
|
2015-09-12 18:14:00 +00:00
|
|
|
end
|
2015-09-12 09:30:38 +00:00
|
|
|
if ch.vol > 0 then
|
|
|
|
ch.freq = note_to_hz(ch.note)
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
2015-09-12 09:30:38 +00:00
|
|
|
ch.last_step = flr(ch.offset)
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
2015-09-12 09:30:38 +00:00
|
|
|
if ch.vol and ch.vol > 0 then
|
|
|
|
local vol = ch.vol
|
|
|
|
if ch.fx == 1 then
|
|
|
|
-- slide from previous note over the length of a step
|
|
|
|
ch.freq = lerp(note_to_hz(ch.lastnote or 0),note_to_hz(ch.note),ch.offset%1)
|
|
|
|
elseif ch.fx == 2 then
|
|
|
|
-- vibrato one semitone?
|
2015-09-12 18:14:00 +00:00
|
|
|
ch.freq = lerp(note_to_hz(ch.note),note_to_hz(ch.note+0.5),ch.lfo(4))
|
2015-09-12 09:30:38 +00:00
|
|
|
elseif ch.fx == 3 then
|
|
|
|
-- drop/bomb slide from note to c-0
|
2015-09-12 18:14:00 +00:00
|
|
|
local off = ch.offset%1
|
|
|
|
--local freq = lerp(note_to_hz(ch.note),note_to_hz(0),off)
|
|
|
|
local freq = lerp(note_to_hz(ch.note),0,off)
|
|
|
|
ch.freq = freq
|
2015-09-12 09:30:38 +00:00
|
|
|
elseif ch.fx == 4 then
|
|
|
|
-- fade in
|
|
|
|
vol = lerp(0,ch.vol,ch.offset%1)
|
|
|
|
elseif ch.fx == 5 then
|
|
|
|
-- fade out
|
|
|
|
vol = lerp(ch.vol,0,ch.offset%1)
|
|
|
|
elseif ch.fx == 6 then
|
|
|
|
-- fast appreggio over 4 steps
|
2015-09-12 18:14:00 +00:00
|
|
|
local off = bit.band(flr(ch.offset),0xfc)
|
|
|
|
local lfo = flr(ch.lfo(8)*4)
|
|
|
|
off = off + lfo
|
|
|
|
local note = sfx[flr(off)][1]
|
|
|
|
ch.freq = note_to_hz(note)
|
2015-09-12 09:30:38 +00:00
|
|
|
elseif ch.fx == 7 then
|
|
|
|
-- slow appreggio over 4 steps
|
2015-09-12 18:14:00 +00:00
|
|
|
local off = bit.band(flr(ch.offset),0xfc)
|
|
|
|
local lfo = flr(ch.lfo(4)*4)
|
|
|
|
off = off + lfo
|
|
|
|
local note = sfx[flr(off)][1]
|
|
|
|
ch.freq = note_to_hz(note)
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
2015-11-27 23:37:53 +00:00
|
|
|
ch.sample = ch.osc(ch.oscpos) * vol/7
|
|
|
|
ch.oscpos = ch.oscpos + ch.freq/__sample_rate
|
2015-09-12 09:30:38 +00:00
|
|
|
ch.buffer:setSample(ch.bufferpos,ch.sample)
|
2015-09-12 03:54:23 +00:00
|
|
|
else
|
|
|
|
ch.buffer:setSample(ch.bufferpos,lerp(ch.sample or 0,0,0.1))
|
|
|
|
ch.sample = 0
|
|
|
|
end
|
2015-09-12 09:30:38 +00:00
|
|
|
else
|
|
|
|
ch.buffer:setSample(ch.bufferpos,lerp(ch.sample or 0,0,0.1))
|
|
|
|
ch.sample = 0
|
|
|
|
end
|
|
|
|
ch.bufferpos = ch.bufferpos + 1
|
|
|
|
if ch.bufferpos == __audio_buffer_size then
|
|
|
|
-- queue buffer and reset
|
|
|
|
__audio_channels[channel]:queue(ch.buffer)
|
|
|
|
__audio_channels[channel]:play()
|
|
|
|
ch.bufferpos = 0
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-18 12:00:57 +00:00
|
|
|
function love.draw()
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.setCanvas(pico8.screen)
|
2015-08-18 12:00:57 +00:00
|
|
|
restore_clip()
|
|
|
|
restore_camera()
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
love.graphics.setShader(pico8.draw_shader)
|
2015-08-18 12:00:57 +00:00
|
|
|
|
|
|
|
-- run the cart's draw function
|
2016-05-27 19:16:33 +00:00
|
|
|
if pico8.cart._draw then pico8.cart._draw() end
|
2015-08-18 12:00:57 +00:00
|
|
|
|
|
|
|
-- draw the contents of pico screen to our screen
|
|
|
|
flip_screen()
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function _reload()
|
|
|
|
_load(cartname)
|
|
|
|
run()
|
|
|
|
end
|
|
|
|
|
2015-08-16 16:00:39 +00:00
|
|
|
function love.keypressed(key)
|
2016-05-27 19:16:33 +00:00
|
|
|
if cart and pico8.cart._keydown then
|
|
|
|
return pico8.cart._keydown(key)
|
2015-09-11 11:30:37 +00:00
|
|
|
end
|
2015-09-16 12:50:19 +00:00
|
|
|
if key == 'r' and (love.keyboard.isDown('lctrl') or love.keyboard.isDown('lgui')) then
|
2015-11-27 00:59:33 +00:00
|
|
|
_reload()
|
2015-09-16 12:50:19 +00:00
|
|
|
elseif key == 'q' and (love.keyboard.isDown('lctrl') or love.keyboard.isDown('lgui')) then
|
2015-08-19 08:23:07 +00:00
|
|
|
love.event.quit()
|
2015-09-13 10:12:07 +00:00
|
|
|
elseif key == 'pause' then
|
|
|
|
paused = not paused
|
2015-08-18 16:35:53 +00:00
|
|
|
elseif key == 'f6' then
|
|
|
|
-- screenshot
|
|
|
|
local screenshot = love.graphics.newScreenshot(false)
|
|
|
|
local filename = cartname..'-'..os.time()..'.png'
|
|
|
|
screenshot:encode(filename)
|
2015-09-16 12:50:19 +00:00
|
|
|
log('saved screenshot to',filename)
|
2015-08-20 10:03:01 +00:00
|
|
|
elseif key == 'f8' then
|
|
|
|
-- start recording
|
|
|
|
video_frames = {}
|
|
|
|
elseif key == 'f9' then
|
|
|
|
-- stop recording and save
|
|
|
|
local basename = cartname..'-'..os.time()..'-'
|
|
|
|
for i,v in ipairs(video_frames) do
|
|
|
|
v:encode(string.format("%s%04d.png",basename,i))
|
|
|
|
end
|
|
|
|
video_frames = nil
|
|
|
|
log('saved video to',basename)
|
2015-08-18 16:35:53 +00:00
|
|
|
else
|
|
|
|
for p=0,1 do
|
2016-05-27 19:16:33 +00:00
|
|
|
for i=0,#pico8.keymap[p] do
|
|
|
|
for _,testkey in pairs(pico8.keymap[p][i]) do
|
2015-10-12 21:06:55 +00:00
|
|
|
if key == testkey then
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.keypressed[p][i] = -1 -- becomes 0 on the next frame
|
2015-10-12 21:06:55 +00:00
|
|
|
break
|
|
|
|
end
|
2015-08-18 16:35:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function love.keyreleased(key)
|
2016-05-27 19:16:33 +00:00
|
|
|
if cart and pico8.cart._keyup then
|
|
|
|
return pico8.cart._keyup(key)
|
2015-09-11 11:30:37 +00:00
|
|
|
end
|
2015-08-18 16:35:53 +00:00
|
|
|
for p=0,1 do
|
2016-05-27 19:16:33 +00:00
|
|
|
for i=0,#pico8.keymap[p] do
|
|
|
|
for _,testkey in pairs(pico8.keymap[p][i]) do
|
2015-10-12 21:06:55 +00:00
|
|
|
if key == testkey then
|
2016-05-27 19:16:33 +00:00
|
|
|
pico8.keypressed[p][i] = nil
|
2015-10-12 21:06:55 +00:00
|
|
|
break
|
|
|
|
end
|
2015-08-18 16:35:53 +00:00
|
|
|
end
|
|
|
|
end
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-09-11 15:21:04 +00:00
|
|
|
function love.textinput(text)
|
2016-05-27 19:16:33 +00:00
|
|
|
if cart and pico8.cart._textinput then return pico8.cart._textinput(text) end
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function love.graphics.point(x,y)
|
|
|
|
love.graphics.rectangle('fill',x,y,1,1)
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function setfps(fps)
|
|
|
|
pico8.fps = flr(fps)
|
|
|
|
if pico8.fps <= 0 then
|
|
|
|
pico8.fps = 30
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
frametime = 1/pico8.fps
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
function love.run()
|
|
|
|
if love.math then
|
|
|
|
love.math.setRandomSeed(os.time())
|
|
|
|
for i=1,3 do love.math.random() end
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
math.randomseed(os.time())
|
|
|
|
for i=1,3 do math.random() end
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
if love.event then
|
|
|
|
love.event.pump()
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
if love.load then love.load(arg) end
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
-- We don't want the first frame's dt to include time taken by love.load.
|
|
|
|
if love.timer then love.timer.step() end
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
local dt = 0
|
2015-08-16 16:00:39 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
-- Main loop time.
|
|
|
|
while true do
|
|
|
|
-- Process events.
|
|
|
|
if love.event then
|
|
|
|
love.event.pump()
|
|
|
|
for e,a,b,c,d in love.event.poll() do
|
|
|
|
if e == "quit" then
|
|
|
|
if not love.quit or not love.quit() then
|
|
|
|
if love.audio then
|
|
|
|
love.audio.stop()
|
2015-08-17 11:14:08 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
return
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2015-08-19 11:01:17 +00:00
|
|
|
end
|
2016-05-27 19:16:33 +00:00
|
|
|
love.handlers[e](a,b,c,d)
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2015-08-19 11:01:17 +00:00
|
|
|
end
|
2015-11-27 07:15:46 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
-- Update dt, as we'll be passing it to update
|
|
|
|
if love.timer then
|
|
|
|
love.timer.step()
|
|
|
|
dt = dt + love.timer.getDelta()
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
2015-11-27 20:06:47 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
-- Call update and draw
|
|
|
|
local render = false
|
|
|
|
while dt > frametime do
|
|
|
|
host_time = host_time + dt
|
|
|
|
if paused then
|
|
|
|
else
|
|
|
|
if love.update then love.update(frametime) end -- will pass 0 if love.timer is disabled
|
|
|
|
update_audio(frametime)
|
|
|
|
end
|
|
|
|
dt = dt - frametime
|
|
|
|
render = true
|
2015-08-16 16:00:39 +00:00
|
|
|
end
|
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
if render and love.window and love.graphics and love.window.isCreated() then
|
|
|
|
love.graphics.origin()
|
|
|
|
if paused then
|
|
|
|
api.rectfill(64-4*4,60,64+4*4-2,64+4+4,1)
|
|
|
|
api.print("paused",64 - 3*4,64,(host_time*20)%8<4 and 7 or 13)
|
|
|
|
flip_screen()
|
|
|
|
else
|
|
|
|
if love.draw then love.draw() end
|
|
|
|
end
|
2015-11-27 20:06:47 +00:00
|
|
|
end
|
2015-11-27 21:02:07 +00:00
|
|
|
|
2016-05-27 19:16:33 +00:00
|
|
|
if love.timer then love.timer.sleep(0.001) end
|
2015-09-11 11:30:37 +00:00
|
|
|
end
|
2015-09-12 03:54:23 +00:00
|
|
|
end
|