Advertisement
18107

Computercraft nbs player

Jan 17th, 2018
167
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.60 KB | None | 0 0
  1. --18107's Jukebox
  2. --version 1.2.0
  3. --last updated 13 November 2017
  4. --requires 'project red' or 'computronics'
  5. --Noteblock player at http://pastebin.com/aX8quTFy
  6.  
  7. local monitor = peripheral.find("monitor")
  8. if monitor == nil then
  9.   term.setTextColor(colors.red)
  10.   print("Error: No monitor detected")
  11.   return
  12. end
  13. local modem = peripheral.find("modem")
  14. local noteblock = peripheral.find("iron_noteblock")
  15.  
  16. local musicList = {}
  17. local monitorWidth, monitorHeight = monitor.getSize()
  18. local currentlyPlaying = nil
  19. local screen = "list"
  20. local page = 1
  21. local repeatMode = "off" -- off, single, all ordered, all random
  22. local lastUpdate = 1
  23. local paused = false
  24. local timerRunning = false
  25. local songFinished = false
  26. local forceStop = false
  27.  
  28. local songLength, songName, songAuth, origSongAuth, songDesc
  29. local tempo = 100
  30. local currentTick = 1
  31. local notes = {}
  32.  
  33. function newLine()
  34.     local x, y = monitor.getCursorPos()
  35.     monitor.setCursorPos(1, y + 1)
  36. end --newline()
  37.  
  38. function refreshMusicList()
  39.     local length = #musicList
  40.     local thisSong
  41.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  42.         thisSong = musicList[currentlyPlaying]
  43.     end
  44.    
  45.     local list = fs.list("")
  46.     local disks = {}
  47.     musicList = {}
  48.     local songCounter = 1
  49.     local diskCounter = 1
  50.    
  51.     --get music from computer, get list of disks
  52.     for i = 1, #list do
  53.         if string.sub(list[i],-4) == ".nbs" then
  54.             musicList[songCounter] = list[i]
  55.             songCounter = songCounter + 1
  56.         elseif string.sub(list[i],0,4) == "disk" and fs.isDir(list[i]) then
  57.             disks[diskCounter] = list[i]
  58.             diskCounter = diskCounter + 1
  59.         end
  60.     end --for #list
  61.    
  62.     --get music from disks
  63.     for a = 1, #disks do
  64.         list = fs.list(disks[a])
  65.         for b = 1, #list do
  66.             if string.sub(list[b],-4) == ".nbs" then
  67.                 musicList[songCounter] = disks[a].."/"..list[b]
  68.                 songCounter = songCounter + 1
  69.             end --if
  70.         end --for #list
  71.     end --for #disks
  72.    
  73.     --if song does not exist, stop playing it
  74.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  75.         if thisSong ~= musicList[currentlyPlaying] then
  76.             currentlyPlaying = 0
  77.         end --if thisSong
  78.     end --if currentlyPlaying
  79.    
  80.     --check if musicList has shrunk, and decrease page number if needed
  81.     if page > #musicList/(monitorHeight-3) and #musicList > 0 then
  82.         page = math.ceil(#musicList/(monitorHeight-3))
  83.     end --if page
  84. end --refreshMusicList()
  85.  
  86. function displayMusicList()
  87.     --draw top line
  88.     monitor.clear()
  89.     monitor.setCursorPos(monitorWidth-4-20, 1)
  90.     monitor.setTextColor(colors.yellow)
  91.     monitor.write("Repeat: "..repeatMode)
  92.     monitor.setCursorPos(1, 1)
  93.     monitor.setTextColor(colors.cyan)
  94.     monitor.write("18107's Juke Box")
  95.     monitor.setCursorPos(monitorWidth-3, 1)
  96.     monitor.setTextColor(colors.red)
  97.     monitor.write("Stop")
  98.     monitor.setTextColor(colors.white)
  99.     newLine()
  100.    
  101.     --draw songs
  102.     if #musicList > 0 then
  103.         for i = (monitorHeight-3)*(page-1)+1, (monitorHeight-3)*(page-1)+monitorHeight-3 do
  104.             if currentlyPlaying == i then
  105.                 monitor.setTextColor(colors.blue)
  106.             else
  107.                 monitor.setTextColor(colors.white)
  108.             end --if currentlyPlaying
  109.             if i < #musicList then
  110.                 if string.sub(musicList[i],0,4) == "disk" then
  111.                     if string.sub(musicList[i],5,5) == "/" then
  112.                         monitor.write("Disk : "..string.sub(musicList[i],6,-5))
  113.                     else
  114.                         monitor.write("Disk "..string.sub(musicList[i],5,5).." : "..string.sub(musicList[i],7,-5))
  115.                     end --if "/"
  116.                 else
  117.                     monitor.write(string.sub(musicList[i],0,-5))
  118.                 end --if disk
  119.             end --if #musicList
  120.             newLine()
  121.         end --for
  122.     end --if #musicList
  123.    
  124.     --draw second bottom line
  125.     monitor.setCursorPos(1, monitorHeight-1)
  126.     if currentlyPlaying == 0 or currentlyPlaying == nil then
  127.         monitor.setTextColor(colors.lightGray)
  128.     else
  129.         monitor.setTextColor(colors.cyan)
  130.     end --if currentlyPlaying
  131.     monitor.write("Currently Playing")
  132.     monitor.setCursorPos(monitorWidth-9, monitorHeight-1)
  133.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  134.         monitor.setTextColor(colors.red)
  135.     end --if currentlyPlaying
  136.     if paused then
  137.         monitor.write("Play  ")
  138.     else
  139.         monitor.write("Pause ")
  140.     end --if paused
  141.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  142.         monitor.setTextColor(colors.blue)
  143.     end --if currentlyPlaying
  144.     monitor.write("Skip")
  145.    
  146.     --draw bottom line
  147.     monitor.setTextColor(colors.green)
  148.     monitor.setCursorPos(1, monitorHeight)
  149.     monitor.write("Previous")
  150.     monitor.setCursorPos(monitorWidth-3, monitorHeight)
  151.     monitor.write("Next")
  152.     monitor.setTextColor(colors.yellow)
  153.     monitor.setCursorPos(monitorWidth/2+1, monitorHeight)
  154.     monitor.write(tostring(page))
  155. end --displayMusicList()
  156.  
  157. function displayCurrentlyPlaying()
  158.     --draw top line
  159.     monitor.clear()
  160.     monitor.setCursorPos(monitorWidth-4-20, 1)
  161.     monitor.setTextColor(colors.yellow)
  162.     monitor.write("Repeat: "..repeatMode)
  163.     monitor.setCursorPos(1, 1)
  164.     monitor.setTextColor(colors.cyan)
  165.     monitor.write("18107's Juke Box")
  166.     monitor.setCursorPos(monitorWidth-3, 1)
  167.     monitor.setTextColor(colors.red)
  168.     monitor.write("Stop")
  169.     monitor.setTextColor(colors.white)
  170.     newLine()
  171.     newLine()
  172.    
  173.     --song info
  174.     if string.sub(musicList[currentlyPlaying],0,4) == "disk" then
  175.         if string.sub(musicList[currentlyPlaying],5,5) == "/" then
  176.             monitor.write("Disk :"..string.sub(musicList[currentlyPlaying],6,-5))
  177.         else
  178.             monitor.write("Disk "..string.sub(musicList[currentlyPlaying],5,5).." : "..string.sub(musicList[currentlyPlaying],7,-5))
  179.         end --if "/"
  180.     else
  181.         monitor.write(string.sub(musicList[currentlyPlaying],0,-5))
  182.     end --if disk
  183.     newLine()
  184.     newLine()
  185.     if type(songAuth) == "string" then
  186.         monitor.write("Author: "..songAuth)
  187.     end --if songAuth
  188.     newLine()
  189.     newLine()
  190.     if type(origSongAuth) == "string" then
  191.         monitor.write("Original author: "..origSongAuth)
  192.     end --if origSongAuth
  193.     newLine()
  194.     newLine()
  195.     if type(songDesc) == "string" then
  196.         for desc = 0, #songDesc/monitorWidth+1 do
  197.             monitor.write(string.sub(songDesc, desc*monitorWidth+1, (desc+1)*monitorWidth))
  198.             newLine()
  199.         end --for desc
  200.     end --songDesc
  201.    
  202.     --second bottom line
  203.     monitor.setCursorPos(1, monitorHeight-1)
  204.     monitor.setTextColor(colors.cyan)
  205.     monitor.write("Song list")
  206.     monitor.setCursorPos(monitorWidth-9, monitorHeight-1)
  207.     monitor.setTextColor(colors.red)
  208.     if paused then
  209.         monitor.write("Play  ")
  210.     else
  211.         monitor.write("Pause ")
  212.     end --if paused
  213.     monitor.setTextColor(colors.blue)
  214.     monitor.write("Skip")
  215.    
  216.     --bottom line
  217.     monitor.setTextColor(colors.lightGray)
  218.     local minutes = math.floor(songLength*100/tempo/60) --right side
  219.     local seconds = math.floor(songLength*100/tempo) % 60
  220.     monitor.setCursorPos(monitorWidth-4, monitorHeight)
  221.     if minutes < 10 then
  222.         monitor.write(" ")
  223.     end --if minutes
  224.     monitor.write(minutes..":")
  225.     if seconds < 10 then
  226.         monitor.write("0")
  227.     end --if seconds
  228.     monitor.write(seconds.."")
  229.    
  230.     minutes = math.floor(currentTick*100/tempo/60)--left side
  231.     seconds = math.floor(currentTick*100/tempo) % 60
  232.     monitor.setCursorPos(1, monitorHeight)
  233.     if minutes < 10 then
  234.         monitor.write(" ")
  235.     end --if minutes
  236.     monitor.write(minutes..":")
  237.     if seconds < 10 then
  238.         monitor.write("0")
  239.     end --if seconds
  240.     monitor.write(seconds.."")
  241.    
  242.     monitor.write(" <") --center
  243.     for i = 8, monitorWidth-7 do
  244.         monitor.write("=")
  245.     end --for monitorWidth
  246.     monitor.write(">")
  247.     monitor.setCursorPos((currentTick/songLength)*(monitorWidth-14)+8, monitorHeight)
  248.     monitor.write("|")
  249. end --displayCurrentlyPlaying
  250.  
  251. function refreshDisplay()
  252.     if screen == "list" then
  253.         displayMusicList()
  254.     elseif screen == "playing" then
  255.         displayCurrentlyPlaying()
  256.     end --if screen
  257. end --refreshDisplay()
  258.  
  259. function readNumber(file, size)
  260.     local number = 0
  261.     for siz = 1, size do
  262.         number = number + bit.blshift(file.read(), 8*(siz-1))
  263.     end --for size
  264.     return number
  265. end --readNumber()
  266.  
  267. function readString(file, length)
  268.     if length ~= 0 then
  269.         local text = string.char(file.read())
  270.         for char = 2, length do
  271.             text = text..string.char(file.read())
  272.         end --for char
  273.         return text
  274.     end --if length
  275. end --readString()
  276.  
  277. function loadSong()
  278.     local song = fs.open(musicList[currentlyPlaying], "rb")
  279.     if song == nil then
  280.         print("File not found")
  281.         currentlyPlaying = 0
  282.         return
  283.     end --if nil
  284.    
  285.     --header
  286.     local strLen = 0
  287.     songLength = readNumber(song, 2) --song length (in ticks)
  288.     readNumber(song, 2) --song height
  289.     strLen = readNumber(song, 4)
  290.     songName = readString(song, strLen) --song name
  291.     strLen = readNumber(song, 4)
  292.     songAuth = readString(song, strLen) --song author
  293.     strLen = readNumber(song, 4)
  294.     origSongAuth = readString(song, strLen) --original song author
  295.     strLen = readNumber(song, 4)
  296.     songDesc = readString(song, strLen) --song description
  297.     tempo = readNumber(song, 2) --tempo (ticks per second x100)
  298.     readNumber(song, 1) --auto saving
  299.     readNumber(song, 1) --auto saving duration
  300.     readNumber(song, 1) --time signature
  301.     readNumber(song, 4) --minutes spent
  302.     readNumber(song, 4) --left clicks
  303.     readNumber(song, 4) --right clicks
  304.     readNumber(song, 4) --blocks added
  305.     readNumber(song, 4) --blocks removed
  306.     strLen = readNumber(song, 4)
  307.     readString(song, strLen) --MIDI/schematic file name
  308.    
  309.     local tick = 1
  310.     local nextTick = 1
  311.     local hasNext = true
  312.    
  313.     local instrument = 0
  314.     local key = 0
  315.     local chord = 1
  316.    
  317.     currentTick = 1
  318.     notes = {}
  319.    
  320.     --read song
  321.     while hasNext do
  322.         nextTick = readNumber(song, 2)
  323.        
  324.         while nextTick > 1 do
  325.             notes[tick] = {}
  326.             tick = tick + 1
  327.             nextTick = nextTick - 1
  328.         end --while nextTick
  329.        
  330.         if nextTick ~= 0 then
  331.             notes[tick] = {}
  332.             chord = 1
  333.             while readNumber(song, 2) ~= 0 do --play chord
  334.                 instrument = readNumber(song, 1)
  335.                 key = readNumber(song, 1)
  336.                 notes[tick][chord] = {instrument, key}
  337.                 chord = chord + 1
  338.             end --while readNumber
  339.             tick = tick + 1
  340.         else
  341.             hasNext = false
  342.         end --if nextTick
  343.     end --while hasNext
  344.    
  345.     song.close()
  346.     if timerRunning == false then
  347.         timerRunning = true
  348.         os.startTimer(100/tempo)
  349.     end --if timerRunning
  350. end --loadSong()
  351.  
  352. function nextSong()
  353.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  354.         if repeatMode == "all random" then
  355.             currentlyPlaying = math.random(#musicList)
  356.         elseif repeatMode == "all ordered" then
  357.             currentlyPlaying = currentlyPlaying + 1
  358.             if currentlyPlaying >= #musicList then
  359.                 currentlyPlaying = 1
  360.             end --if #musicList
  361.         elseif repeatMode == "off" then
  362.             currentlyPlaying = 0
  363.             songFinished = true
  364.             screen = "list"
  365.         end --if repeatMode
  366.     end --if currentlyPlaying
  367.    
  368.     if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  369.         page = math.floor((currentlyPlaying-1)/(monitorHeight-3)) + 1
  370.         loadSong()
  371.     end --if currentlyPlaying
  372. end --nextSong()
  373.  
  374. function playNote(instrument, key)
  375.     if noteblock ~= nil then
  376.         if instrument == 0 then
  377.             noteblock.playNote(0, key-33)
  378.         elseif instrument == 1 then
  379.             noteblock.playNote(4, key-33)
  380.         else
  381.             noteblock.playNote(instrument-1, key-33)
  382.         end --if instrument
  383.     end --if noteblock
  384.    
  385.     if modem ~= nil then
  386.         modem.transmit(instrument, 0, bit.blshift(1, key-33))
  387.     end --if modem
  388. end --playNote()
  389.  
  390. function transmitNotes(notes)
  391.     if modem ~= nil then
  392.         for instrument = 1, 5 do
  393.             modem.transmit(instrument, 0, notes[instrument])
  394.         end --for i
  395.     end --if modem
  396. end --transmitNotes
  397.  
  398. function playSong()
  399.     local modemData = {0, 0, 0, 0, 0}
  400.     if not paused then
  401.         if notes[currentTick] ~= nil then
  402.             if (#notes[currentTick] ~= 0) then
  403.                 for _,note in pairs(notes[currentTick]) do
  404.                     playNote(note[1], note[2])
  405.                     if note[2] >= 33 then
  406.                         modemData[note[1]+1] = bit.bor(modemData[note[1]+1], bit.blshift(1, note[2]-33))
  407.                     end
  408.                 end --for notes
  409.                 transmitNotes(modemData)
  410.             end --if #notes
  411.             currentTick = currentTick + 1
  412.         else
  413.             songFinished = true
  414.         end --if notes
  415.     end --if not paused
  416. end --playSong()
  417.  
  418. function monitorTouch(x, y)
  419.     if screen == "list" then
  420.         --top line
  421.         if y == 1 then
  422.             if x > monitorWidth-4 then --stop
  423.                 forceStop = true
  424.                 paused = false
  425.             elseif x > 16 and x <= monitorWidth-6 then --repeatMode
  426.                 if repeatMode == "off" then
  427.                     repeatMode = "single"
  428.                 elseif repeatMode == "single" then
  429.                     repeatMode = "all ordered"
  430.                 elseif repeatMode == "all ordered" then
  431.                     repeatMode = "all random"
  432.                 else--if repeatMode == "all random" then
  433.                     repeatMode = "off"
  434.                 end --if repeatMode
  435.             end --x
  436.         --bottom line
  437.         elseif y == monitorHeight then
  438.             if x < 9 then --previous
  439.                 if page > 1 then
  440.                     page = page - 1
  441.                 end --if page
  442.             elseif x > monitorWidth-4 then --next
  443.                 if page*(monitorHeight-3) < #musicList then
  444.                     page = page + 1
  445.                 end --if page
  446.             end --if x
  447.         --second bottom line
  448.         elseif y == monitorHeight-1 then
  449.             if x <= 17 then --currentlyPlaying
  450.                 if currentlyPlaying ~= 0 and currentlyPlaying ~= nil then
  451.                     screen = "playing"
  452.                 end --if currentlyPlaying
  453.             elseif x >= monitorWidth-9 and x < monitorWidth-4 then --pause
  454.                 paused = not paused
  455.             elseif x >= monitorWidth-3 then --skip
  456.                 nextSong()
  457.             end --if x
  458.         --song list
  459.         else
  460.             currentlyPlaying = (y-1) + (page-1)*(monitorHeight-3)
  461.             loadSong()
  462.             if forceStop then
  463.                 forceStop = false
  464.             end --if forceStop
  465.         end --if y
  466.        
  467.     elseif screen == "playing" then
  468.         --top line
  469.         if y == 1 then
  470.             if x > monitorWidth-4 then --stop
  471.                 forceStop = true
  472.                 paused = false
  473.             elseif x > 16 and x <= monitorWidth-6 then --repeatMode
  474.                 if repeatMode == "off" then
  475.                     repeatMode = "single"
  476.                 elseif repeatMode == "single" then
  477.                     repeatMode = "all ordered"
  478.                 elseif repeatMode == "all ordered" then
  479.                     repeatMode = "all random"
  480.                 else--if repeatMode == "all random" then
  481.                     repeatMode = "off"
  482.                 end --if repeatMode
  483.             end --if x
  484.         --second bottom line
  485.         elseif y == monitorHeight-1 then
  486.             if x <= 9 then --song list
  487.                 screen = "list"
  488.             elseif x >= monitorWidth-9 and x < monitorWidth-4 then --pause
  489.                 paused = not paused
  490.             elseif x >= monitorWidth-3 then --skip
  491.                 nextSong()
  492.             end --if x
  493.         --bottom line
  494.         elseif y == monitorHeight then
  495.             if x == 8 then
  496.                 currentTick = 1
  497.             elseif x >= 8 and x <= monitorWidth-7 then
  498.                 currentTick = math.floor((x-8)/(monitorWidth-14)*songLength+0.5)
  499.             end --if x
  500.         end --if y
  501.     end --if screen
  502.    
  503.     refreshDisplay()
  504. end --monitorTouch
  505.  
  506. function start()
  507.     math.randomseed(os.time())
  508.     math.random()
  509.     refreshMusicList()
  510.     displayMusicList()
  511. end --start()
  512.  
  513. function run()
  514.     local running = true
  515.     local event, side, x, y
  516.     while running do
  517.         event, side, x, y = os.pullEventRaw()
  518.        
  519.         if event == "terminate" then
  520.             running = false
  521.            
  522.         elseif event == "monitor_touch" then
  523.             monitorTouch(x, y)
  524.            
  525.         elseif event == "timer" then
  526.             if forceStop == true then
  527.                 forceStop = false
  528.                 timerRunning = false
  529.                 currentlyPlaying = 0
  530.                 screen = "list"
  531.                 refreshDisplay()
  532.             else
  533.                 if songFinished then
  534.                     nextSong()
  535.                     songFinished = false
  536.                     timerRunning = false
  537.                     refreshDisplay()
  538.                 else
  539.                     os.startTimer(100/tempo)
  540.                     playSong()
  541.                     if math.floor(currentTick*100/tempo) ~= math.floor(lastUpdate*100/tempo) then
  542.                         lastUpdate = currentTick
  543.                         refreshDisplay()
  544.                     end --if lastUpdate
  545.                 end --if not songFinished
  546.             end --if forceStop
  547.            
  548.         elseif event == "peripheral" or event == "peripheral_detach" or event == "monitor_resize" then
  549.             --reload everything
  550.             modem = peripheral.find("modem")
  551.             noteblock = peripheral.find("iron_noteblock")
  552.             monitor = peripheral.find("monitor")
  553.             if monitor == nil then
  554.                 term.setTextColor(colors.red)
  555.                 print("Error: monitor detached")
  556.                 return
  557.             end --if monitor
  558.             monitorWidth, monitorHeight = monitor.getSize()
  559.             refreshDisplay()
  560.         end --if event
  561.        
  562.     end --while running
  563. end --run()
  564.  
  565. function stop()
  566.     monitor.clear()
  567. end --stop()
  568.  
  569. start()
  570. run()
  571. stop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement