Advertisement
Guest User

nbs.lua

a guest
Apr 1st, 2020
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.71 KB | None | 0 0
  1. -- dump table to string
  2. function dump(o)
  3.   if type(o) == 'table' then
  4.      local s = '{ '
  5.      for k,v in pairs(o) do
  6.         if type(k) ~= 'number' then k = '"'..k..'"' end
  7.         s = s .. '['..k..'] = ' .. dump(v) .. ','
  8.      end
  9.      return s .. '} '
  10.   else
  11.      return tostring(o)
  12.   end
  13. end
  14.  
  15. -- read byte from file
  16. local function readByte(file)
  17.   local char = file.read()
  18.  
  19.   if char == nil then
  20.     return nil
  21.   end
  22.  
  23.   return string.byte(char)
  24. end
  25.  
  26. -- read short integer (16-bit) from file
  27. local function readShort(file)
  28.   local char1 = file.read()
  29.   local char2 = file.read()
  30.  
  31.   if char1 == nil or char2 == nil then
  32.     return nil
  33.   end
  34.  
  35.   return string.byte(char1) + string.byte(char2) * 256
  36. end
  37.  
  38. -- read signed short integer (16-bit) from file
  39. local function readSignedShort(file)
  40.   local uShort = readShort(file)
  41.   local sShort = nil
  42.  
  43.   if  (uShort % 2 == 0) then
  44.     sShort = uShort / 2
  45.   else
  46.     sShort = -1 * math.floor(uShort / 2)
  47.   end
  48.  
  49.   return sShort
  50. end
  51.  
  52. -- read integer (32-bit) from file
  53. local function readInt(file)
  54.   local char1 = file.read()
  55.   local char2 = file.read()
  56.   local char3 = file.read()
  57.   local char4 = file.read()
  58.  
  59.   if char4 == nil or char3 == nil or char2 == nil or char1 == nil then
  60.     return nil
  61.   end
  62.  
  63.   return string.byte(char1) + string.byte(char2) * 256 + string.byte(char3) * 65536 + string.byte(char4) * 16777216
  64. end
  65.  
  66. -- read string from file
  67. local function readString(file)
  68.   local s = ""
  69.   local len = readInt(file)
  70.  
  71.   for i = 1, len do
  72.     local c = file.read()
  73.     if not c then
  74.       break
  75.     end
  76.     s = s..c
  77.   end
  78.  
  79.   return s
  80. end
  81.  
  82. local function log(key, value, filePath)
  83.   return nil
  84. --[[
  85.   local log = nil
  86.   if filePath == nil then
  87.     log = fs.open("log", "a")
  88.     log.writeLine(key..": "..tostring(value))
  89.     log.close()
  90.   else
  91.     log = fs.open(filePath, "a")
  92.     log.writeLine(key..": "..tostring(value))
  93.     log.close()
  94.   end
  95. --]]
  96. end
  97.  
  98. local function set(table, key, value)
  99.   table[key] = value
  100.  
  101.   log(key, value)
  102. end
  103.  
  104. -- read nbs file header
  105. local function readNBSHeader(file)
  106.   local header = {}
  107.  
  108.   set(header, "isNewVersion", readShort(file) > 0)  
  109.   set(header, "nbsVersion", readByte(file))
  110.   set(header, "instrumentCount", readByte(file))
  111.   set(header, "lenght", readShort(file))
  112.   set(header, "layerCount", readShort(file))
  113.   set(header, "name", readString(file))
  114.  
  115.   set(header, "author", readString(file))
  116.   set(header, "original_author", readString(file))
  117.   set(header, "description", readString(file))
  118.   set(header, "tempo", readShort(file) / 100)
  119.   set(header, "autosave", readByte(file))
  120.   set(header, "autosave_duration", readByte(file))
  121.   set(header, "time_signature", readByte(file))
  122.   set(header, "minutes_spent", readInt(file))
  123.   set(header, "left_clicks", readInt(file))
  124.   set(header, "right_clicks", readInt(file))
  125.   set(header, "blocks_added", readInt(file))
  126.   set(header, "blocks_removed", readInt(file))
  127.   set(header, "filename", readString(file))
  128.   set(header, "isLoop", readByte(file))
  129.   set(header, "maxLoopCount", readByte(file))
  130.   set(header, "loopStartTick", readShort(file))
  131.  
  132.   return header
  133. end
  134.  
  135. local function readNBSNoteBlocks(file)
  136.   local noteBlocks = {}
  137.  
  138.   -- read first tick
  139.   local jumpsToNextTick = readShort(file)
  140.   log("jumpsToNextTick", jumpsToNextTick)
  141.   local currentTick = -1 + jumpsToNextTick
  142.  
  143.   if jumpsToNextTick == 0 then
  144.     return noteBlocks
  145.   end
  146.  
  147.   local tick = {}
  148.   tick.layers = {}
  149.   tick.jumpsToNextTick = jumpsToNextTick
  150.   local layer = {}
  151.  
  152.   local jumpsToNextLayer = readShort(file)
  153.   log("jumpsToNextLayer", jumpsToNextLayer)
  154.   local currentLayer = -1 + jumpsToNextLayer
  155.  
  156.   layer.instrument = readByte(file)
  157.   log("instrument", layer.instrument)
  158.   layer.noteKey = readByte(file)
  159.   log("noteKey", layer.noteKey)
  160.   layer.velocity = readByte(file)
  161.   log("velocity", layer.velocity)
  162.   layer.panning = readByte(file)
  163.   log("panning", layer.panning)
  164.   layer.pitch = readSignedShort(file)
  165.   log("pitch", layer.pitch)
  166.  
  167.   tick.layers[currentLayer] = layer
  168.  
  169.   jumpsToNextLayer = readShort(file)
  170.   log("jumpsToNextLayer", jumpsToNextLayer)
  171.  
  172.   while jumpsToNextLayer > 0 do
  173.     layer = {}
  174.     currentLayer = currentLayer + jumpsToNextLayer
  175.  
  176.     layer.instrument = readByte(file)
  177.     log("instrument", layer.instrument)
  178.     layer.noteKey = readByte(file)
  179.     log("noteKey", layer.noteKey)
  180.     layer.velocity = readByte(file)
  181.     log("velocity", layer.velocity)
  182.     layer.panning = readByte(file)
  183.     log("panning", layer.panning)
  184.     layer.pitch = readSignedShort(file)
  185.     log("pitch", layer.pitch)
  186.    
  187.     tick.layers[currentLayer] = layer
  188.     jumpsToNextLayer = readShort(file)
  189.     log("jumpsToNextLayer", jumpsToNextLayer)
  190.   end
  191.  
  192.   noteBlocks[currentTick] = tick
  193.  
  194.   jumpsToNextTick = readShort(file)
  195.   log("jumpsToNextTick", jumpsToNextTick)
  196.  
  197.   if jumpsToNextTick > 0 then
  198.     jumpsToNextLayer = readShort(file)
  199.     log("jumpsToNextLayer", jumpsToNextLayer)
  200.   end
  201.  
  202.   -- read next ticks
  203.   while jumpsToNextTick > 0 do
  204.     tick = {}
  205.     tick.layers = {}
  206.     tick.jumpsToNextTick = jumpsToNextTick
  207.    
  208.     currentTick = currentTick + jumpsToNextTick
  209.    
  210.     currentLayer = -1 + jumpsToNextLayer
  211.  
  212.     while jumpsToNextLayer > 0 do
  213.       layer = {}
  214.       currentLayer = currentLayer + jumpsToNextLayer
  215.  
  216.       layer.instrument = readByte(file)
  217.       log("instrument", layer.instrument)
  218.       layer.noteKey = readByte(file)
  219.       log("noteKey", layer.noteKey)
  220.       layer.velocity = readByte(file)
  221.       log("velocity", layer.velocity)
  222.       layer.panning = readByte(file)
  223.       log("panning", layer.panning)
  224.       layer.pitch = readSignedShort(file)
  225.       log("pitch", layer.pitch)
  226.      
  227.       tick.layers[currentLayer] = layer
  228.       jumpsToNextLayer = readShort(file)
  229.       log("jumpsToNextLayer", jumpsToNextLayer)
  230.     end
  231.  
  232.     noteBlocks[currentTick] = tick
  233.     jumpsToNextTick = readShort(file)
  234.     log("jumpsToNextTick", jumpsToNextTick)
  235.     if jumpsToNextTick > 0 then
  236.       jumpsToNextLayer = readShort(file)
  237.       log("jumpsToNextLayer", jumpsToNextLayer)
  238.     end
  239.   end
  240.  
  241.   return noteBlocks
  242. end
  243.  
  244. local function readNBSLayers(file)
  245.   -- TODO
  246.   return nil
  247. end
  248.  
  249. local function readNBSCustomInstruments(file)
  250.   -- TODO
  251.   return nil
  252. end
  253.  
  254. local function loadSong(filePath, isVerbose)
  255.   log("...Start Loading Song...")
  256.   local file = fs.open(filePath, "r")
  257.  
  258.   if file then
  259.     local song = {}
  260.  
  261.     if isVerbose then
  262.       print("Reading header...")
  263.     end
  264.     song.header = readNBSHeader(file)
  265.    
  266.     if isVerbose then
  267.       print("Reading note blocks...")
  268.     end
  269.     song.noteBlocks = readNBSNoteBlocks(file)
  270.  
  271.     if isVerbose then
  272.       print("Reading layers...")
  273.     end
  274.     song.layers = readNBSLayers(file)
  275.  
  276.     if isVerbose then
  277.       print("Reading custom instruments...")
  278.     end
  279.     song.customInstruments = readNBSCustomInstruments(file)
  280.  
  281.     file.close()
  282.     print("Return loaded song...")
  283.     return song
  284.   end
  285.   log("...End Loading Song...")
  286.   return nil
  287. end
  288.  
  289. local function getInstrumentById(id)
  290.   if id == 0 then
  291.     return "harp"
  292.   elseif id == 1 then
  293.     return "bass"
  294.   elseif id == 2 then
  295.     return "basedrum"
  296.   elseif id == 3 then
  297.     return "snare"
  298.   elseif id == 4 then
  299.     return "hat"
  300.   elseif id == 5 then
  301.     return "guitar"
  302.   elseif id == 6 then
  303.     return "flute"
  304.   elseif id == 7 then
  305.     return "bell"
  306.   elseif id == 8 then
  307.     return "chime"
  308.   elseif id == 9 then
  309.     return "xylophone"
  310.   elseif id == 10 then
  311.     return "iron_xylophone"
  312.   elseif id == 11 then
  313.     return "cow_bell"
  314.   elseif id == 12 then
  315.     return "didgeridoo"
  316.   elseif id == 13 then
  317.     return "bit"
  318.   elseif id == 14 then
  319.     return "banjo"
  320.   elseif id == 15 then
  321.     return "pling"
  322.   else
  323.     return nil
  324.   end
  325. end
  326.  
  327. local function playSong(song, side, volume)
  328.   print("Start playing song")
  329.   log("...Start playing song...", nil, "playLog")
  330.   speaker = peripheral.wrap(side)
  331.  
  332.   local tempo = song.header["tempo"]
  333.   log("tempo", tempo, "playLog")
  334.   local secondsPerTick = 1/tempo
  335.  
  336.   local jumpsToNextTick = 0
  337.   for tickIndex, tick in pairs(song.noteBlocks) do
  338.     jumpsToNextTick = tick.jumpsToNextTick
  339.     log("jumpsToNextTick", nil, "playLog")
  340.     sleep(jumpsToNextTick * secondsPerTick)
  341.       for layerIndex, layer in pairs(tick.layers) do
  342.         log("instrument", getInstrumentById(layer.instrument), "playLog")
  343.         speaker.playNote(getInstrumentById(layer.instrument), volume, layer.noteKey -33)
  344.       end
  345.   end
  346.   log("...End playing song...", nil, "playLog")
  347.   print("End playing song")
  348. end
  349.  
  350. local filePath, side, volume, isVerbose = ...
  351.  
  352. local song = loadSong(filePath, isVerbose)
  353.  
  354. playSong(song, side, tonumber(volume))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement