Advertisement
JuicyPenguin

Jukebox script

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