Advertisement
Guest User

dwi converter

a guest
Aug 4th, 2013
307
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- make global functions local for faster lookup
  2. local table = table
  3. local append, concat = table.insert, table.concat
  4. local string = string
  5. local sub, gsub, lower = string.sub, string.gsub, string.lower
  6. local find, match, gmatch = string.find, string.match, string.gmatch
  7. local ipairs, pairs = ipairs, pairs
  8.  
  9. local function file_search(filter, dir_path)
  10.     local function c_in(value, tab)
  11.         for _, v in pairs(tab) do
  12.             if v == value then return true end
  13.         end
  14.         return false
  15.     end
  16.  
  17.     function string:split(sep)
  18.         local sep, fields = sep or ":", {}
  19.         local pattern = string.format("([^%s]+)", sep)
  20.         self:gsub(pattern, function(c) fields[#fields+1] = c end)
  21.         return fields
  22.     end
  23.  
  24.     local fs = require 'lfs'
  25.     local filter = string.lower(filter) or "*"
  26.     local dir_path = dir_path or fs.currentdir()
  27.     local extensions = filter:split(";")
  28.  
  29.     local files = {}
  30.     for _, path in ipairs(dir_path:split(";")) do
  31.         for f in fs.dir(path) do
  32.             if f ~= "." and f ~= ".." then
  33.                 local attr = fs.attributes(path..'\\'..f)
  34.                 if attr.mode == "file" then
  35.                     if filter=="*" or
  36.                     c_in(lower(f:match('%.%w*$')), extensions) then
  37.                         append(files, {path = path..'\\', name = f})
  38.                     end
  39.                 elseif attr.mode == "directory" then
  40.                     local subf = file_search(filter, path..'\\'..f)
  41.                     for _, v in ipairs(subf) do
  42.                         append(files, {path = v.path, name = v.name})
  43.                     end
  44.                 end
  45.             end
  46.         end
  47.     end
  48.     return files
  49. end
  50.  
  51. local function processMeasure(measure)
  52.     local note_lookup_table = {
  53.         ['1000'] = '4',    ['0100'] = '2',
  54.         ['0010'] = '8',    ['0001'] = '6',
  55.         ['1100'] = '1',    ['1010'] = '7',
  56.         ['1001'] = 'B',    ['0110'] = 'A',
  57.         ['0101'] = '3',    ['0011'] = '9',
  58.         ['1110'] = '<81>', ['0111'] = '<83>',
  59.         ['1101'] = '<61>', ['1011'] = '<8B>',
  60.         ['1111'] = '<19>', ['0000'] = '0',
  61.     }
  62.     local beat_notes = {''} -- reserve beat_notes[1] for opening tag if necessary
  63.     local note_count = 0
  64.  
  65.     for note in measure:gmatch('%d%d%d%d') do
  66.         append(beat_notes, note_lookup_table[note])
  67.         append(beat_notes, '') -- for possible note padding
  68.         note_count = note_count + 1
  69.     end
  70.  
  71.     if note_count == 4 then
  72.         for i=3, 9, 2 do beat_notes[i] = '0' end -- add appropriate note padding
  73.     elseif note_count == 8 then
  74.     elseif note_count == 12 then
  75.         for i=3, 25, 2 do beat_notes[i] = '0' end
  76.         beat_notes[1] = '['; append(beat_notes, ']')
  77.     elseif note_count == 16 then
  78.         beat_notes[1] = '('; append(beat_notes, ')')
  79.     elseif note_count == 24 then
  80.         beat_notes[1] = '['; append(beat_notes, ']')
  81.     elseif note_count == 32 then
  82.         for i=3, 65, 2 do beat_notes[i] = '0' end
  83.         beat_notes[1] = '{'; append(beat_notes, '}')
  84.     elseif note_count == 48 then
  85.         for i=3, 97, 2 do beat_notes[i] = '000' end
  86.         beat_notes[1] = '`'; append(beat_notes, '\'')
  87.     elseif note_count == 64 then
  88.         beat_notes[1] = '{'; append(beat_notes, '}')
  89.     elseif note_count == 96 then
  90.         for i=3, 193, 2 do beat_notes[i] = '0' end
  91.         beat_notes[1] = '`'; append(beat_notes, '\'')
  92.     elseif note_count == 192 then
  93.         beat_notes[1] = '`'; append(beat_notes, '\'')
  94.     else
  95.         return nil -- invalid note count
  96.     end
  97.  
  98.     return concat(beat_notes) .. '\n'
  99. end
  100.  
  101. -------------------------------------------------------------------------------------------------
  102.  
  103. local function smToDwi(full_path)
  104.     -- open sm file to read contents into string
  105.     local sm_file
  106.     do
  107.         local tmp = io.open(full_path)
  108.         sm_file = tmp:read('*a')
  109.         tmp:close()
  110.     end
  111.  
  112.     function string:getFieldContents(field)
  113.         local _, idx = self:find('#'..field..':')
  114.         local str = self:match('%b:;', idx)
  115.         return str:sub(2, #str - 1)
  116.     end
  117.  
  118.     local changebpm = sm_file:getFieldContents('BPMS')
  119.     local start_bpm
  120.     do -- separate first bpm and bpm changes
  121.         start_bpm = changebpm:match('%d+%.?%d*%s*=%s*(%d+%.?%d*)')
  122.         local _, idx = changebpm:find('%d+%.?%d*%s*=%s*%d+%.?%d*%s*,')
  123.  
  124.         if idx then changebpm = changebpm:sub(idx + 1)
  125.         else changebpm = '' end
  126.     end
  127.  
  128.     if changebpm then -- multiply all measures by 4 to convert to beats
  129.         changebpm = changebpm:gsub('(%d+%.?%d*)(%s*=%s*%d+%.?%d*,?)', function(beat, bpm)
  130.             return string.format('%.3f', beat * 4)..bpm
  131.         end)
  132.     end
  133.  
  134.     -- process common .sm file fields
  135.     local file_contents = {}
  136.     append(file_contents, '#TITLE:'         .. sm_file:getFieldContents('TITLE')                    .. ';\n')
  137.     append(file_contents, '#ARTIST:'        .. sm_file:getFieldContents('ARTIST')                   .. ';\n')
  138.     append(file_contents, '#FILE:'          .. sm_file:getFieldContents('MUSIC')                    .. ';\n')
  139.     append(file_contents, '#BPM:'           .. start_bpm                                            .. ';\n')
  140.     append(file_contents, '#GAP:'           .. tonumber(sm_file:getFieldContents('OFFSET')) * -1000 .. ';\n')
  141.     append(file_contents, '#SAMPLESTART:'   .. sm_file:getFieldContents('SAMPLESTART')              .. ';\n')
  142.     append(file_contents, '#SAMPLELENGTH:'  .. sm_file:getFieldContents('SAMPLELENGTH')             .. ';\n\n')
  143.     if changebpm then
  144.         append(file_contents, '#CHANGEBPM:' .. changebpm                                            .. ';\n')
  145.     end
  146.     append(file_contents, '#SINGLE:MANIAC:1:\n')
  147.  
  148.     sm_file = sm_file:match(':.+$') -- extract start of note data to end of file
  149.  
  150.     for measure in sm_file:gmatch('.-[,;]') do
  151.         append(file_contents, processMeasure(measure))
  152.     end
  153.  
  154.     do -- save converted file
  155.         local contents = concat(file_contents)
  156.         local dwi_file = io.open(full_path:gsub('%.%w*$', '.dwi'), 'w')
  157.         dwi_file:write(contents..'\n')
  158.         dwi_file:close()
  159.     end
  160. end
  161.  
  162. -------------------------------------------------------------------------------------------------
  163.  
  164. for _, file in ipairs(file_search('.sm;.ssc;.ds')) do
  165.     smToDwi(file.path .. file.name)
  166. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement