Clem105

NBS Player

Oct 12th, 2016
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.36 KB | None | 0 0
  1. --[[
  2. **********************
  3. NBS file reader using the AdvancedNoteBlock from LIP, this is created by Clem105 for treasure disk , all credits goes to MysticT and Soni for the nbs decoding API, original link : https://github.com/SoniEx2/NBSPlayer/blob/master/nbs.lua
  4. **********************
  5. ]]--
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12. -- yield to avoid error
  13. local function yield()
  14.   os.queueEvent("fake")
  15.   os.pullEvent("fake")
  16. end
  17.  
  18. -- read short integer (16-bit) from file
  19. local function readShort(file)
  20.   return file.read() + file.read() * 256
  21. end
  22.  
  23. -- read integer (32-bit) from file
  24. local function readInt(file)
  25.   return file.read() + file.read() * 256 + file.read() * 65536 + file.read() * 16777216
  26. end
  27.  
  28. -- read string from file
  29. local function readString(file)
  30.   local s = ""
  31.   local len = readInt(file)
  32.   for i = 1, len do
  33.     local c = file.read()
  34.     if not c then
  35.       break
  36.     end
  37.     s = s..string.char(c)
  38.   end
  39.   return s
  40. end
  41.  
  42. -- read nbs file header
  43. local function readNBSHeader(file)
  44.   local header = {}
  45.   header.lenght = readShort(file)
  46.   header.height = readShort(file)
  47.   header.name = readString(file)
  48.   if header.name == "" then
  49.     header.name = "Untitled"
  50.   end
  51.   header.author = readString(file)
  52.   if header.author == "" then
  53.     header.author = "Unknown"
  54.   end
  55.   header.original_author = readString(file)
  56.   if header.original_author == "" then
  57.     header.original_author = "Unknown"
  58.   end
  59.   header.description = readString(file)
  60.   header.tempo = readShort(file) / 100
  61.   header.autosave = file.read()
  62.   header.autosave_duration = file.read()
  63.   header.time_signature = file.read()
  64.   header.minutes_spent = readInt(file)
  65.   header.left_clicks = readInt(file)
  66.   header.right_clicks = readInt(file)
  67.   header.blocks_added = readInt(file)
  68.   header.blocks_removed = readInt(file)
  69.   header.filename = readString(file)
  70.   return header
  71. end
  72.  
  73. -- jump to the next tick in the file
  74. local function nextTick(file, tSong)
  75.   local jump = readShort(file)
  76.   for i = 1, jump - 1 do
  77.     table.insert(tSong, {})
  78.   end
  79.   return jump > 0
  80. end
  81.  
  82. -- read the notes in a tick
  83. -- TODO cleanup, move checks to player:nbs.lua
  84. local function readTick(file)
  85.   local t = {}
  86.   local n = 0
  87.   local jump = readShort(file)
  88.   while jump > 0 do
  89.     n = n + jump
  90.     local instrument = file.read() + 1
  91.     if instrument > 7 then -- Soni: we actually support 7 instruments
  92.       return nil, "Can't convert custom instruments"
  93.     end
  94.     local note = file.read() - 33
  95.     if note < 0 or note > 24 then
  96.       return nil, "Notes must be in Minecraft's 2 octaves"
  97.     end
  98.     if not t[instrument] then
  99.       t[instrument] = {}
  100.     end
  101.     t[instrument][n] = note
  102.     jump = readShort(file)
  103.   end
  104.   return t
  105. end
  106.  
  107. -- API functions
  108.  
  109. -- save a converted song to a file
  110. function saveSong(tSong, sPath)
  111.   local file = fs.open(sPath, "w")
  112.   if file then
  113.     file.write(textutils.serialize(tSong))
  114.     file.close()
  115.     return true
  116.   end
  117.   return false, "Error opening file "..sPath
  118. end
  119.  
  120. -- load and convert an .nbs file and save it
  121. function load(sPath, bVerbose)
  122.   local file = fs.open(sPath, "rb")
  123.   if file then
  124.     if bVerbose then
  125.       print("Reading header...")
  126.     end
  127.     local tSong = {}
  128.     local header = readNBSHeader(file)
  129.     tSong.name = header.name
  130.     tSong.author = header.author
  131.     tSong.original_author = header.original_author
  132.     tSong.lenght = header.lenght / header.tempo
  133.     tSong.delay = 1 / header.tempo
  134.     if bVerbose then
  135.       print("Reading ticks...")
  136.     end
  137.     while nextTick(file, tSong) do
  138.       local tick, err = readTick(file, tSong)
  139.       if tick then
  140.         table.insert(tSong, tick)
  141.       else
  142.         file.close()
  143.         return nil, err
  144.       end
  145.       yield()
  146.     end
  147.  
  148.     pcall(function()
  149.         local layers = {}
  150.         for i=1, header.height do
  151.           table.insert(layers, {name=readString(file), volume=file.read() + 0})
  152.         end
  153.         tSong.layers = layers
  154.        
  155.         local insts = {}
  156.         for i=1, file.read() + 0 do
  157.           table.insert(insts, {name=readString(file), file=readString(file), pitch=file.read() + 0, key=(file.read() + 0) ~= 0})
  158.         end
  159.         tSong.instruments = insts
  160.       end)
  161.     file.close()
  162.     return tSong
  163.   end
  164.   return nil, "Error opening file "..sPath
  165. end
  166.  
  167.  
  168.  
  169.  
  170. tArgs = {...}
  171. Files = {}
  172. sSong = {}
  173. if #tArgs == 0   then
  174.     error("Usage : >NBSPlayer <Advanced NoteBlockSide> <NbsFile or Nil>")
  175. elseif peripheral.getType(tArgs[1]) ~= "AdvancedNoteBlock" then
  176.     error("Not finding an AdvancedBlockNote at "..tArgs[1])
  177. elseif tArgs[2] ~= nil and not fs.exists(tArgs[2])  then
  178.     error("Not finding the corresponding file/directory")
  179. end
  180. if not tArgs[2] then
  181.     Files = fs.find("*.nbs")
  182.     for o=1,#Files do
  183.         sSong[o] = load(Files[o])
  184.     end
  185. else
  186.     sSong[1] = load(tArgs[2])
  187. end
  188. nb = peripheral.wrap(tostring(tArgs[1]))
  189. for o,_ in ipairs(sSong) do
  190.     term.clear()
  191.     delay = sSong[o].delay
  192.     term.setCursorPos(28,8)
  193.     term.write(sSong[o].name)
  194.     term.setCursorPos(20,10)
  195.     term.write("Arranged by "..sSong[o].author)
  196.     term.setCursorPos(16,12)
  197.     term.write("Originally created by "..sSong[o].original_author)
  198.     startTime = os.clock()
  199.     for index,v in ipairs(sSong[o]) do
  200.         if v ~= {} then
  201.             for intstr,tab in ipairs(v) do
  202.                 nb.setInstrument(instr)
  203.                 for _,note in pairs(tab) do
  204.                     nb.setPitch(note)
  205.                     nb.triggerNote()
  206.                 end
  207.             end
  208.         end
  209.         sleep(delay)
  210.     end
  211. end
Add Comment
Please, Sign In to add comment