mirror of
https://github.com/nucular/sfxrlua.git
synced 2024-12-24 18:44:20 +00:00
Merge pull request #9 from jorio/master
Load/Save sfxr's original binary format
This commit is contained in:
commit
25736975fd
36
main.lua
36
main.lua
@ -374,7 +374,7 @@ function createActionButtons()
|
|||||||
frt:SetPos(5, 30)
|
frt:SetPos(5, 30)
|
||||||
|
|
||||||
local sb = lf.Create("button")
|
local sb = lf.Create("button")
|
||||||
sb:SetText("Save")
|
sb:SetText("Save Lua")
|
||||||
sb:SetWidth(67)
|
sb:SetWidth(67)
|
||||||
sb.OnClick = function(o)
|
sb.OnClick = function(o)
|
||||||
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.lua"
|
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.lua"
|
||||||
@ -385,7 +385,7 @@ function createActionButtons()
|
|||||||
f:AddItem(sb)
|
f:AddItem(sb)
|
||||||
|
|
||||||
local lb = lf.Create("button")
|
local lb = lf.Create("button")
|
||||||
lb:SetText("Load")
|
lb:SetText("Load Lua")
|
||||||
lb:SetWidth(67)
|
lb:SetWidth(67)
|
||||||
lb.OnClick = function(o)
|
lb.OnClick = function(o)
|
||||||
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.lua"
|
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.lua"
|
||||||
@ -396,6 +396,28 @@ function createActionButtons()
|
|||||||
end
|
end
|
||||||
f:AddItem(lb)
|
f:AddItem(lb)
|
||||||
|
|
||||||
|
local bsb = lf.Create("button")
|
||||||
|
bsb:SetText("Save binary")
|
||||||
|
bsb:SetWidth(67)
|
||||||
|
bsb.OnClick = function(o)
|
||||||
|
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.sfxr"
|
||||||
|
sound:saveBinary(p)
|
||||||
|
frt:SetText("Saved to\n" .. p)
|
||||||
|
fr:SetVisible(true):SetModal(true):Center()
|
||||||
|
end
|
||||||
|
f:AddItem(bsb)
|
||||||
|
|
||||||
|
local blb = lf.Create("button")
|
||||||
|
blb:SetText("Load binary")
|
||||||
|
blb:SetWidth(67)
|
||||||
|
blb.OnClick = function(o)
|
||||||
|
local p = love.filesystem.getSaveDirectory() .. "/" .. "sound.sfxr"
|
||||||
|
sound:loadBinary(p)
|
||||||
|
frt:SetText("Loaded from\n" .. p)
|
||||||
|
fr:SetVisible(true):SetModal(true):Center()
|
||||||
|
end
|
||||||
|
f:AddItem(blb)
|
||||||
|
|
||||||
local eb = lf.Create("button")
|
local eb = lf.Create("button")
|
||||||
eb:SetText("Export WAV")
|
eb:SetText("Export WAV")
|
||||||
eb:SetWidth(140)
|
eb:SetWidth(140)
|
||||||
@ -407,12 +429,14 @@ function createActionButtons()
|
|||||||
end
|
end
|
||||||
f:AddItem(eb)
|
f:AddItem(eb)
|
||||||
|
|
||||||
f:SetPos(485, 485)
|
f:SetPos(485, 455)
|
||||||
f:SetSize(150, 110)
|
f:SetSize(150, 140)
|
||||||
|
|
||||||
-- well ugh
|
-- well ugh
|
||||||
lb:SetPos(78, 47)
|
lb:SetPos(78, 47)
|
||||||
eb:SetY(77)
|
bsb:SetY(77)
|
||||||
|
blb:SetPos(78, 77)
|
||||||
|
eb:SetY(107)
|
||||||
end
|
end
|
||||||
|
|
||||||
function createOther()
|
function createOther()
|
||||||
@ -461,7 +485,7 @@ function createOther()
|
|||||||
s:SetValue(sound.volume.sound)
|
s:SetValue(sound.volume.sound)
|
||||||
f:AddItem(s)
|
f:AddItem(s)
|
||||||
|
|
||||||
f:SetPos(485, 370)
|
f:SetPos(485, 340)
|
||||||
f:SetWidth(150)
|
f:SetWidth(150)
|
||||||
|
|
||||||
|
|
||||||
|
199
sfxr.lua
199
sfxr.lua
@ -99,6 +99,66 @@ local function setseed(seed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- IEEE754 32-bit big-endian floating point numbers
|
||||||
|
-- source: https://stackoverflow.com/questions/14416734/
|
||||||
|
|
||||||
|
local function packIEEE754(number)
|
||||||
|
if number == 0 then
|
||||||
|
return string.char(0x00, 0x00, 0x00, 0x00)
|
||||||
|
elseif number ~= number then
|
||||||
|
return string.char(0xFF, 0xFF, 0xFF, 0xFF)
|
||||||
|
else
|
||||||
|
local sign = 0x00
|
||||||
|
if number < 0 then
|
||||||
|
sign = 0x80
|
||||||
|
number = -number
|
||||||
|
end
|
||||||
|
local mantissa, exponent = math.frexp(number)
|
||||||
|
exponent = exponent + 0x7F
|
||||||
|
if exponent <= 0 then
|
||||||
|
mantissa = math.ldexp(mantissa, exponent - 1)
|
||||||
|
exponent = 0
|
||||||
|
elseif exponent > 0 then
|
||||||
|
if exponent >= 0xFF then
|
||||||
|
return string.char(sign + 0x7F, 0x80, 0x00, 0x00)
|
||||||
|
elseif exponent == 1 then
|
||||||
|
exponent = 0
|
||||||
|
else
|
||||||
|
mantissa = mantissa * 2 - 1
|
||||||
|
exponent = exponent - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mantissa = math.floor(math.ldexp(mantissa, 23) + 0.5)
|
||||||
|
return string.char(
|
||||||
|
sign + math.floor(exponent / 2),
|
||||||
|
(exponent % 2) * 0x80 + math.floor(mantissa / 0x10000),
|
||||||
|
math.floor(mantissa / 0x100) % 0x100,
|
||||||
|
mantissa % 0x100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpackIEEE754(packed)
|
||||||
|
local b1, b2, b3, b4 = string.byte(packed, 1, 4)
|
||||||
|
local exponent = (b1 % 0x80) * 0x02 + math.floor(b2 / 0x80)
|
||||||
|
local mantissa = math.ldexp(((b2 % 0x80) * 0x100 + b3) * 0x100 + b4, -23)
|
||||||
|
if exponent == 0xFF then
|
||||||
|
if mantissa > 0 then
|
||||||
|
return 0 / 0
|
||||||
|
else
|
||||||
|
mantissa = math.huge
|
||||||
|
exponent = 0x7F
|
||||||
|
end
|
||||||
|
elseif exponent > 0 then
|
||||||
|
mantissa = mantissa + 1
|
||||||
|
else
|
||||||
|
exponent = exponent + 1
|
||||||
|
end
|
||||||
|
if b1 >= 0x80 then
|
||||||
|
mantissa = -mantissa
|
||||||
|
end
|
||||||
|
return math.ldexp(mantissa, exponent - 0x7F)
|
||||||
|
end
|
||||||
|
|
||||||
-- Constructor
|
-- Constructor
|
||||||
|
|
||||||
function sfxr.newSound(...)
|
function sfxr.newSound(...)
|
||||||
@ -756,7 +816,7 @@ function sfxr.Sound:randomHit(seed)
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.frequency.start = random(0.2, 0.8)
|
self.frequency.start = random(0.2, 0.8)
|
||||||
self.frequency.glide = -0.3 - random(0, 0.4)
|
self.frequency.slide = -0.3 - random(0, 0.4)
|
||||||
self.envelope.attack = 0
|
self.envelope.attack = 0
|
||||||
self.envelope.sustain = random(0, 0.1)
|
self.envelope.sustain = random(0, 0.1)
|
||||||
self.envelope.decay = random(0.1, 0.3)
|
self.envelope.decay = random(0.1, 0.3)
|
||||||
@ -905,14 +965,6 @@ function sfxr.Sound:exportWAV(f, freq, bits)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sfxr.Sound:load(f)
|
|
||||||
local close = false
|
|
||||||
if type(f) == "string" then
|
|
||||||
f = io.open(f, "wb")
|
|
||||||
close = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sfxr.Sound:save(f, compressed)
|
function sfxr.Sound:save(f, compressed)
|
||||||
local close = false
|
local close = false
|
||||||
if type(f) == "string" then
|
if type(f) == "string" then
|
||||||
@ -995,4 +1047,133 @@ function sfxr.Sound:load(f)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function sfxr.Sound:saveBinary(f)
|
||||||
|
local close = false
|
||||||
|
if type(f) == "string" then
|
||||||
|
f = io.open(f, "w")
|
||||||
|
close = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function writeFloat(x)
|
||||||
|
local packed = packIEEE754(x):reverse()
|
||||||
|
assert(packed:len() == 4)
|
||||||
|
f:write(packed)
|
||||||
|
end
|
||||||
|
|
||||||
|
f:write('\x66\x00\x00\x00') -- version 102
|
||||||
|
assert(self.wavetype < 256)
|
||||||
|
f:write(string.char(self.wavetype) .. '\x00\x00\x00')
|
||||||
|
writeFloat(self.volume.sound)
|
||||||
|
|
||||||
|
writeFloat(self.frequency.start)
|
||||||
|
writeFloat(self.frequency.min)
|
||||||
|
writeFloat(self.frequency.slide)
|
||||||
|
writeFloat(self.frequency.dslide)
|
||||||
|
writeFloat(self.duty.ratio)
|
||||||
|
writeFloat(self.duty.sweep)
|
||||||
|
|
||||||
|
writeFloat(self.vibrato.depth)
|
||||||
|
writeFloat(self.vibrato.speed)
|
||||||
|
writeFloat(self.vibrato.delay)
|
||||||
|
|
||||||
|
writeFloat(self.envelope.attack)
|
||||||
|
writeFloat(self.envelope.sustain)
|
||||||
|
writeFloat(self.envelope.decay)
|
||||||
|
writeFloat(self.envelope.punch)
|
||||||
|
|
||||||
|
f:write('\x00') -- unused filter_on boolean
|
||||||
|
writeFloat(self.lowpass.resonance)
|
||||||
|
writeFloat(self.lowpass.cutoff)
|
||||||
|
writeFloat(self.lowpass.sweep)
|
||||||
|
writeFloat(self.highpass.cutoff)
|
||||||
|
writeFloat(self.highpass.sweep)
|
||||||
|
|
||||||
|
writeFloat(self.phaser.offset)
|
||||||
|
writeFloat(self.phaser.sweep)
|
||||||
|
|
||||||
|
writeFloat(self.repeatspeed)
|
||||||
|
|
||||||
|
writeFloat(self.change.speed)
|
||||||
|
writeFloat(self.change.amount)
|
||||||
|
|
||||||
|
if close then
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function sfxr.Sound:loadBinary(f)
|
||||||
|
local close = false
|
||||||
|
if type(f) == "string" then
|
||||||
|
f = io.open(f, "r")
|
||||||
|
close = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local s
|
||||||
|
if io.type(f) == "file" then
|
||||||
|
s = f:read("*a")
|
||||||
|
else
|
||||||
|
s = f:read()
|
||||||
|
end
|
||||||
|
|
||||||
|
if close then
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local off = 1
|
||||||
|
|
||||||
|
local function readFloat()
|
||||||
|
local f = unpackIEEE754(s:sub(off, off+3):reverse())
|
||||||
|
off = off + 4
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start reading the string
|
||||||
|
|
||||||
|
local version = s:byte(off)
|
||||||
|
off = off + 4
|
||||||
|
if version < 100 or version > 102 then
|
||||||
|
return nil, "unknown version number "..version
|
||||||
|
end
|
||||||
|
|
||||||
|
self.wavetype = s:byte(off)
|
||||||
|
off = off + 4
|
||||||
|
self.volume.sound = version==102 and readFloat() or 0.5
|
||||||
|
|
||||||
|
self.frequency.start = readFloat()
|
||||||
|
self.frequency.min = readFloat()
|
||||||
|
self.frequency.slide = readFloat()
|
||||||
|
self.frequency.dslide = version>=101 and readFloat() or 0
|
||||||
|
|
||||||
|
self.duty.ratio = readFloat()
|
||||||
|
self.duty.sweep = readFloat()
|
||||||
|
|
||||||
|
self.vibrato.depth = readFloat()
|
||||||
|
self.vibrato.speed = readFloat()
|
||||||
|
self.vibrato.delay = readFloat()
|
||||||
|
|
||||||
|
self.envelope.attack = readFloat()
|
||||||
|
self.envelope.sustain = readFloat()
|
||||||
|
self.envelope.decay = readFloat()
|
||||||
|
self.envelope.punch = readFloat()
|
||||||
|
|
||||||
|
off = off + 1 -- filter_on - seems to be ignored in the C++ version
|
||||||
|
self.lowpass.resonance = readFloat()
|
||||||
|
self.lowpass.cutoff = readFloat()
|
||||||
|
self.lowpass.sweep = readFloat()
|
||||||
|
self.highpass.cutoff = readFloat()
|
||||||
|
self.highpass.sweep = readFloat()
|
||||||
|
|
||||||
|
self.phaser.offset = readFloat()
|
||||||
|
self.phaser.sweep = readFloat()
|
||||||
|
|
||||||
|
self.repeatspeed = readFloat()
|
||||||
|
|
||||||
|
if version >= 101 then
|
||||||
|
self.change.speed = readFloat()
|
||||||
|
self.change.amount = readFloat()
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(off-1 == s:len())
|
||||||
|
end
|
||||||
|
|
||||||
return sfxr
|
return sfxr
|
||||||
|
Loading…
Reference in New Issue
Block a user