Last active
August 20, 2016 02:53
-
-
Save sealgair/7dc17f4d62665c91f0d089344230c618 to your computer and use it in GitHub Desktop.
pico 8 runtime music editing
This file contains hidden or 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
-------------------------------- | |
-- music | |
--[[ memory format: | |
0x3100 - song | |
each song is 4 bytes | |
the first bit of each byte is | |
a flag: | |
byte 1: loop start | |
byte 2: loop back | |
byte 3: stop | |
the remainder of the byte is | |
the index of each of the 4 sfx | |
making up the channels of the | |
song | |
0x3200 - sfx | |
each sfx is comprised of 64 two | |
byte notes, followed by two | |
bytes of metadata | |
note format: 2 bytes, backwards | |
byte 2: | |
0eeevvvi | |
byte 1: | |
iipppppp | |
eee: effect (0-7) | |
vvv: volume (0-7) | |
iii: instrument (0-7, split over bytes) | |
pppppp: pitch (0-63) | |
sfx metadata: | |
byte 1 is probably loop start & end | |
byte 2 is probably volume | |
]] | |
-------------------------------- | |
notenames={ | |
c=0, | |
cs=1, | |
d=2, | |
ds=3, | |
e=4, | |
f=5, | |
fs=6, | |
g=7, | |
gs=8, | |
a=9, | |
as=10, | |
b=11, | |
} | |
function getmusic(m) | |
local l=4 | |
local b=0x3100+m*l | |
return { | |
i=m, b=b, | |
loopstart=band(peek(b),128)!=0, | |
loopback=band(peek(b+1),128)!=0, | |
stop=band(peek(b+2),128)!=0, | |
} | |
end | |
function getmusicsounds(m) | |
local music=getmusic(m) | |
if sounds==nil then | |
sounds={} | |
end | |
for i=0,3 do | |
local s=band(peek(music.b+i),127) | |
if s!=0x42 then | |
add(sounds, s) | |
end | |
end | |
if not music.loopback and not music.stop then | |
sounds = concat(a, getmusicsounds(m+1)) | |
end | |
return sounds | |
end | |
function setmusic(m, args) | |
-- TODO | |
end | |
function getsound(s) | |
local l=68 | |
local b=0x3200+s*l | |
return { | |
speed=peek(b+65), | |
--TODO: loop start / end | |
} | |
end | |
function setsound(s, args) | |
local l=68 | |
local b=0x3200+s*l | |
if args.speed!=nil then | |
poke(b+65, args.speed) | |
end | |
--TODO: loop start / end | |
end | |
function getnote(s, n) | |
local sl=68 | |
local nl=2 | |
local b=0x3200+s*sl+n*nl | |
local b1=peek(b+1) | |
local b2=peek(b) | |
return { | |
b=b, s=s, n=n, | |
pitch=band(b2, 63), | |
instrument=shl(band(b1, 1), 2)+shr(band(b2, 192), 6), | |
volume=shr(band(b1, 14), 1), | |
effect=shr(band(b1, 112), 4), | |
} | |
end | |
function setnote(s, n, args) | |
local sl=68 | |
local nl=2 | |
local b=0x3200+s*sl+n*nl | |
local b1=peek(b+1) | |
local b2=peek(b) | |
if args.pitch!=nil then | |
poke(b, bor(band(192, b2), args.pitch)) | |
end | |
if args.volume!=nil then | |
poke(b+1, bor(band(241, b1), shl(args.volume, 1))) | |
end | |
-- TODO: instrument, effect | |
end | |
function reloadmusic(m) | |
for s in all(getmusicsounds(m)) do | |
reloadsfx(s) | |
end | |
end | |
function reloadsfx(s) | |
local l=68 | |
local b=0x3200+s*l | |
reload(b, l) | |
end | |
function altmusic(m, fn) | |
for s in all(getmusicsounds(m)) do | |
for n=0,63 do | |
local nc=fn(getnote(s, n)) | |
if nc!=nil then | |
setnote(s, n, nc) | |
end | |
end | |
end | |
end | |
function minorize(m, base) | |
base=base%12 | |
local minors={ | |
[(base+4)%12]=true, | |
[(base+9)%12]=true, | |
[(base+11)%12]=true, | |
} | |
altmusic(m, function(note) | |
local pc=note.pitch%12 | |
if minors[pc] then | |
return { | |
pitch=note.pitch-1 | |
} | |
end | |
end) | |
end | |
function settempo(m, speed) | |
for s in all(getmusicsounds(m)) do | |
setsound(s, { | |
speed=flr(speed) | |
}) | |
end | |
end | |
function altvolume(m, av) | |
altmusic(m, function(note) | |
return { | |
volume=bound(round(note.volume*av), 7, 0) | |
} | |
end) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This looks awesome! I'd like to hear it in action. Just a couple notes:
bound
andround
don't seem to be functions in Pico-8 - did you leave them out, or are they part of the Lua library that you assumed were included?shr
andshl
, exactly - a few comments there might be nice, though maybe you're not setting out to give a lesson with this :pI like the handy tables you're passing around to represent sounds and music. Overall this looks pretty slick!