Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Reorder WAV chunks
-- Reorder WAV chunks files
-- This often useful if particular program expects "WAVE" identifier
-- followed by "fmt " chunk and followed by "data " chunk. Particularly
-- GTA San Andreas User Tracks.
-- You can use part or all of this code without my permissions.
local arg = {...}
local fin = assert(io.open(assert(arg[1], "need input file"), "rb"))
local fout = assert(io.open(assert(arg[2], "need output file"), "wb"))
assert(fin:read(3) == "RIF", "Not RIFF")
local last = fin:read(1)
local big = last == "X"
assert(last == "X" or last == "F", "Not RIFF")
fin:read(4) -- ignore length, recompute later
assert(fin:read(4) == "WAVE", "Not WAVE")
local function string2dword(s, big)
local a, b, c, d = s:byte(1, 4)
if big then
return a * 16777216 + b * 65536 + c * 256 + d
else
return d * 16777216 + c * 65536 + b * 256 + a
end
end
local function dword2string(n, big)
n = n % 4294967296
local a = n % 256
local b = math.floor(n / 256) % 256
local c = math.floor(n / 65536) % 256
local d = math.floor(n / 16777216)
if big then
return string.char(d, c, b, a)
else
return string.char(a, b, c, d)
end
end
-- We store chunks in memory for sake of simplicity
local chunks = {}
while true do
io.write("position ", fin:seek("cur"), "\n")
local fourcc = fin:read(4)
if not(fourcc) then
break -- ok EOF
elseif #fourcc < 4 then
io.write("Ignoring data at ", fin:seek("cur") - #fourcc, "\n")
break
end
local length = fin:read(4)
if not(length) or #length < 4 then
io.write("Ignoring fourcc ", fourcc, " at ", fin:seek("cur") - #length, "\n")
break
end
length = string2dword(length, big)
io.write(fourcc, " size of ", length, "\n")
local chunk = fin:read(length)
if not(chunk) or #chunk < length then
io.write("Ignoring fourcc ", fourcc, " at ", fin:seek("cur") - #chunk, ". Expected ", length, ", got ", #chunk, "\n")
break
end
chunks[#chunks + 1] = {
name = fourcc,
data = chunk
}
end
fin:close()
-- Reorder "fmt " chunk
local hasfmt = false
for i, v in ipairs(chunks) do
if v.name == "fmt " then
table.insert(chunks, 1, table.remove(chunks, i))
hasfmt = true
break
end
end
assert(hasfmt, "missing \"fmt \" chunk")
-- Reorder "data" chunk
local hasdata = false
for i, v in ipairs(chunks) do
if v.name == "data" then
table.insert(chunks, 2, table.remove(chunks, i))
hasdata = true
break
end
end
assert(hasdata, "missing \"data\" chunk")
-- Rewrite wav file
local length = 4 -- "WAVE"
for _, v in ipairs(chunks) do
length = length + #v.data + 8 -- 4cc + length + data
end
-- Write header
fout:write("RIF", last, dword2string(length, big), "WAVE")
for _, v in ipairs(chunks) do
fout:write(v.name, dword2string(#v.data, big), v.data)
end
fout:close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment