18107

CC: Tweaked Jukebox

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