KittyCat156

DFPWM Converter

Mar 31st, 2016
851
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.90 KB | None | 0 0
  1. --No credit here. It's a CC idiom.
  2. local function yield()
  3.   os.queueEvent("fake")
  4.   os.pullEvent("fake")
  5. end
  6. --From the lua guide.
  7. function newStack ()
  8.  return {""}   -- starts with an empty string
  9. end
  10.  
  11. function addString (stack, s)
  12.  table.insert(stack, s)    -- push 's' into the the stack
  13.  for i=table.getn(stack)-1, 1, -1 do
  14.   if string.len(stack[i]) > string.len(stack[i+1]) then
  15.    break
  16.   end
  17.   stack[i] = stack[i] .. table.remove(stack)
  18.  end
  19. end
  20. --Credit for the following code should go to: Youka on Github. Ported from io to fs by me.
  21.  
  22. local function collect_audio_data(filename)
  23.  local seekpos=1
  24.  -- WAVE data storage
  25.  local wav = {channels = nil, sample_rate = nil, samples = nil, data = nil}
  26.  -- Open file
  27.  local file = fs.open(filename, "rb")
  28.  --Damn whoever decided rb shouldn't have a readAll().
  29.  local s=newStack()
  30.  while true do
  31.   local r = file.read()
  32.   if r==nil then break end
  33.   addString(s,r)
  34.  end
  35.  local filecontents = table.concat(s)
  36.  if not file then
  37.   return string.format("Filename %q isn't valid!", filename)
  38.  end
  39.  local seek = function(number)--only has cur mode
  40.   seekpos=seekpos+number
  41.  end
  42.  local read = function(number)--number should be format, but this code only uses number so ill only implement that.
  43.   local ret = string.sub(filecontents,seekpos,(seekpos+number)-1);
  44.   seek(number)
  45.  end
  46.  -- File chunk
  47.  if read(4) ~= "RIFF" then
  48.   return "Isn't a 'RIFF' file"
  49.  end
  50.  seekpos=seekpos+4 -- Ignore file size
  51.  if read(4) ~= "WAVE" then
  52.   return "Isn't a 'WAVE' file"
  53.  end
  54.  -- Subchunks
  55.  while true do
  56.   yield()
  57.   -- Chunk id and size
  58.   local id = read(4)
  59.   local size = read(4)
  60.   -- No more chunks
  61.   if not size then
  62.    break
  63.   -- Chunk to read
  64.   else
  65.    size = bton(size)
  66.    -- Format chunk
  67.    if id == "fmt " then
  68.     local bytes = read(2)
  69.     if not bytes or bton(bytes) ~= 1 then
  70.      return "Data format must be PCM!"
  71.     end
  72.     bytes = read(2)
  73.     if not bytes then
  74.      return "Channels are missing!"
  75.     end
  76.     wav.channels = bton(bytes)
  77.     bytes = read(4)
  78.     if not bytes then
  79.      return "Sample rate is missing!"
  80.     end
  81.     wav.sample_rate = bton(bytes)
  82.     seek(4+2) -- Ignore byte rate and block align
  83.     bytes = read(2)
  84.     if not bytes or bton(bytes) ~= 16 then
  85.      return "Bits per sample must be 16!"
  86.     end
  87.     seek(size-16) -- Ignore extra data
  88.    -- Data chunk
  89.    elseif id == "data" then
  90.     if not wav.channels then
  91.      return "Format must be before data!"
  92.     end
  93.     wav.samples = size / (wav.channels * 2)
  94.     do
  95.      local file_cur_pos = seekpos
  96.      local file_end_pos = #filecontents
  97.      if file_end_pos - file_cur_pos < wav.samples * wav.channels * 2 then
  98.       return "Remaining file bytes aren't enough to collect all samples!"
  99.      end
  100.     end
  101.     wav.data = {}
  102.     for c = 1, wav.channels do
  103.      wav.data[c] = {n = 0}
  104.     end
  105.     for s = 1, wav.samples do
  106.      if (s% 100000)==0 then yield() end
  107.      for c = 1, wav.channels do
  108.       local sample = bton(read(2))
  109.       wav.data[c].n = wav.data[c].n + 1
  110.       wav.data[c][wav.data[c].n] = sample > 32767 and sample - 65536 or sample
  111.      end
  112.     end
  113.     break -- Was last needed chunk
  114.    -- Ignorable chunk
  115.    else
  116.     seek(size)
  117.    end
  118.   end
  119.  end
  120.  -- All WAVE data collected?
  121.  if not wav.channels then
  122.   return "Format is missing!"
  123.  elseif not wav.data then
  124.   return "Data are missing!"
  125.  end
  126.  -- Return no error and WAVE data
  127.  return nil, wav
  128. end
  129. --This code was originally by the author of lionray in java, it has been ported to computercraft by me.
  130. local function newConverter()
  131.  local respinc=7
  132.  local respdec=20
  133.  local lpfstrength=100
  134.  local resp=0
  135.  local level=0
  136.  local lastbit=false
  137.  local flastlevel=0
  138.  local lpflevel=0;
  139.  local myself= {
  140.   ctx_update=function(curbit)
  141.    local target= curbit and 127 or -128
  142.    local nlevel=(level+math.floor((response*(target-level)+128)/256))
  143.    if nlevel==level and level~=target then
  144.     nlevel=nlevel+(curbit and 1 or -1)
  145.    end
  146.    local rtarget=nil
  147.    local rdelta=nil
  148.    rtarget=(curbit==lastbit) and 255 or 0
  149.    rdelta=(curbit==lastbit) and respinc or respdec
  150.     local nresponse=response+math.floor((rdelta*(rtarget-response)+128)/256)
  151.    if nresponse == response and response~=rtarget then
  152.     nresponse=nresponse+((curbit==lastbit) and 1 or -1)
  153.    end
  154.     response=nresponse
  155.    lastbit=curbit
  156.    level=nlevel
  157.   end,
  158.   --No need for decompress, one-way converter (tldr, im lazy)
  159.   compress=function(dest,src,destoffs,srcoffs,len)
  160.    for i=1,len do
  161.     local d = 0;
  162.     for j=1,8 do
  163.      local inlevel = src[srcoffs]
  164.      srcoffs=srcoffs+1
  165.      local curbit = (inlevel>level or (inlevel==level and level==127))
  166.      d=(curbit and ((d/2)+128) or (d/2))
  167.      myself.ctx_update(curbit)
  168.     end
  169.     dest[destoffs]=d
  170.     destoffs=destoffs+1
  171.    end
  172.   end
  173.  }
  174.  return myself
  175. end
  176. ----This part, surrounded by 8 dashes, interfaces between the top code and the bottom code.
  177. local function newAudioReader(wav,verboseout,isleft)
  178.  if verboseout then verboseout.say("Converting wav data to wav samples...") end
  179.  errorstr,wavdata=collect_audio_data(wav)
  180.  if errorstr then
  181.   error("Error! "..errorstr)
  182.  end
  183.  for c=1,2 do
  184.   wavdata.data[c].n=nil
  185.   newarray={}
  186.   for i=1,#wavdata.data[c] do
  187.    newarray[math.floor(((32768/wavdata.sample_rate)*(i-1))+1)]=math.floor(wavdata.data[c][i]/256)
  188.    if i% 100000==0 then sleep(0.1) end
  189.   end
  190.   wavdata.data[c]=newarray
  191.  end
  192.  local index = 1
  193.  return {
  194.   read=function(buffertable)
  195.    local remaining=#wavdata.data[isleft and 1 or 2].n-(index-1)
  196.    if(not (#buffertable>remaining)) then
  197.     local bi=1
  198.     while true do
  199.      buffertable[bi]=wavdata.data[isleft and 1 or 2][i]
  200.      bi=bi+1
  201.      index=index+1
  202.     end
  203.    end
  204.    return bytesread
  205.   end
  206.  }
  207. end
  208. ----
  209. function convert(wav,out,isleft,verboseout)
  210.  local sampleRate=32768
  211.  --encoded samples are in litte startian
  212.  local newArray=function(length)
  213.   local ret = {}
  214.   for i=1,length do
  215.    table.insert(ret,0)
  216.   end
  217.   return ret
  218.  end
  219.  local readBuffer = newArray(1024)
  220.  local outBuffer = newArray(1024/8)
  221.  local c = newConverter()
  222.  local audioin=newAudioReader(wav,verboseout,isleft)
  223.  local fout = fs.open(out,"wb")
  224.  if verboseout then verboseout.say("Converting wav samples to DFPWM...") end
  225.  while true do
  226.   local read = audioin.read(readBuffer)
  227.   local i =0
  228.   if read < 0 then break end
  229.   c.compress(outBuffer,readBuffer,0,0,read/8)
  230.   fout.write(outBuffer)
  231.   if i%100==0 then yield() end
  232.   i=i+1
  233.  end
  234.  fout.flush()
  235.  fout.close()
  236. end
  237. --CC program to use the api above. Remove this section and loadapi to use as an api.
  238. print("Type the location of the wav file: ")
  239. local wavfileloc=read()
  240. print("Type a location for the DFPWM file to be written to.")
  241. local dfpwmout=read()
  242. print("Use left or right channel? Type just \"2\" for right and any other character for left.")
  243. local chan=read()
  244. chan=(chan~="2")
  245. convert(wavfileloc,dfpwmout,chan,{say=function(s) print(s) end})
Advertisement
Add Comment
Please, Sign In to add comment