cc tweaked pastebin local runMainLoop = true function byte_lsb(handle) return handle.read() end local function int16_lsb(handle) return bit32.bor(bit32.lshift(byte_lsb(handle),8), byte_lsb(handle)) end function int16_msb(handle) local x = int16_lsb(handle) cc tweaked pastebin How to dowload it? cc tweaked pastebin --# for some reason computercraft doesn't like little endian so we convert to big endian local y = 0 y = y + bit32.lshift(bit32.band(x,0x00FF),8) y = y + bit32.rshift(bit32.band(x,0xFF00),8) return y end local function int32_lsb(handle) return bit32.bor(bit32.lshift(int16_lsb(handle),16),int16_lsb(handle)) end cc tweaked pastebin How to get it? cc tweaked pastebin function int32_msb(handle) local x = int32_lsb(handle) --# again, convert little-endian to big-endian local y = 0 y = y + bit32.lshift(bit32.band(x, 0x000000FF), 24) y = y + bit32.rshift(bit32.band(x, 0xFF000000), 24) y = y + bit32.lshift(bit32.band(x, 0x0000FF00), 8) y = y + bit32.rshift(bit32.band(x, 0x00FF0000), 8) return y cc tweaked pastebin How to get it? cc tweaked pastebin end function bit_string(handle) local len = int32_msb(handle) local str = "" for i=1, len do str = str .. string.char(byte_lsb(handle)) end return str end function stringSelector(boolean, var1, var2) cc tweaked pastebin How to get it for free? cc tweaked pastebin if boolean then if type(var1) == "table" then local str = "" for i=1, #var1 do str = str .. var1[i] end var1 = str end return var1 else cc tweaked pastebin How to get it? cc tweaked pastebin if type(var2) == "table" then local str = "" for i=1, #var2 do str = str .. var2[i] end var2 = str end return var2 end end cc tweaked pastebin How to use it? cc tweaked pastebin function songDetails(handle) local song = {} local new = int16_msb(handle) if new == 0 then song.version = byte_lsb(handle) song.fci = byte_lsb(handle) if song.version >= 3 then song.length = int16_msb(handle) else song.length = nil cc tweaked pastebin How to dowload it? cc tweaked pastebin end else song.version = 0 song.length = new end song.height = int16_msb(handle) song.name = bit_string(handle) song.author = bit_string(handle) song.ogAuthor = bit_string(handle) song.description = bit_string(handle) cc tweaked pastebin How to get it? cc tweaked pastebin song.tempo = int16_msb(handle)/100 song.autoSave = byte_lsb(handle) song.autoSaveDuration = byte_lsb(handle) song.timeSignature = byte_lsb(handle) song.minutesSpent = int32_msb(handle) song.leftClicks = int32_msb(handle) song.rightClicks = int32_msb(handle) song.blocksAdded = int32_msb(handle) song.blocksRemoved = int32_msb(handle) song.ogFileName = bit_string(handle) cc tweaked pastebin How to use it? cc tweaked pastebin song.duration = song.length/song.tempo if song.version >= 4 then song.loop = byte_lsb(handle) song.loopmax = byte_lsb(handle) song.loopstart = int16_lsb(handle) end return song end function loadSong(handle) cc tweaked pastebin How to get it for free? cc tweaked pastebin local song = songDetails(handle) song.ticks = {} local curTick = -1 local running1 = true while running1 do local tickJump = int16_msb(handle) curTick = curTick + tickJump if tickJump > 0 then local tick = {} tick.tick = curTick cc tweaked pastebin How to use it? cc tweaked pastebin tick.layers = {} local curLayer = 0 local running2 = true while running2 do local layerJump = int16_msb(handle) curLayer = curLayer + layerJump if layerJump > 0 then local layer = {} layer.instrument = byte_lsb(handle) layer.note = byte_lsb(handle)-33 cc tweaked pastebin How to get it? cc tweaked pastebin layer.layer = curLayer if song.version >= 4 then layer.vel = byte_lsb(handle) layer.pan = byte_lsb(handle) layer.pit = int16_msb(handle) else layer.vel = 100 layer.pan = 100 layer.pit = 0 end cc tweaked pastebin PasteShr cc tweaked pastebin table.insert(tick.layers,layer) else running2 = false end end table.insert(song.ticks,tick) else running1 = false end end cc tweaked pastebin PasteShr cc tweaked pastebin if song.length == nil then song.length = song.ticks[#song.ticks].tick end return song end local speakers = {} function loadSpeakers() speakers = table.pack(peripheral.find("speaker")) end local instruments = {} cc tweaked pastebin How to get it? cc tweaked pastebin instruments[0] = "harp" instruments[1] = "bass" instruments[2] = "basedrum" instruments[3] = "snare" instruments[4] = "hat" instruments[5] = "guitar" instruments[6] = "flute" instruments[7] = "bell" instruments[8] = "chime" instruments[9] = "xylophone" cc tweaked pastebin How to use it? cc tweaked pastebin local progress = 0 volume = 3 function playSong(fileName) local handle, err = fs.open(fileName,"rb") if not handle then error(err) end if #speakers == 0 then loadSpeakers() end local song = loadSong(handle) handle.close() local ticks = song.ticks local waitTime = 1/song.tempo cc tweaked pastebin PasteShr cc tweaked pastebin local startTime = os.clock() for i=1, #ticks do progress = ticks[i].tick/song.length layers = ticks[i].layers for j=1, #layers do l = layers[j] speakers[j%#speakers+1].playNote(instruments[l.instrument],volume,l.note) end if i ~= #ticks then sleep((startTime+waitTime*ticks[i+1].tick)-os.clock()) cc tweaked pastebin PasteShr cc tweaked pastebin end end end local function round(num) if num%1 >= .5 then return math.ceil(num) else return math.floor(num) end end cc tweaked pastebin How to get it? cc tweaked pastebin local songs = {} local ongoingSong = {} local nextSong = {} local paused = false local shuffle = false local autoPlay = false local songProgress = 0 function chooseNextSong() if #songs > 0 then if autoPlay and ongoingSong.pos ~= nil and nextSong.pos == nil then cc tweaked pastebin How to use it? cc tweaked pastebin if shuffle then local num = round(#songs*math.random()) nextSong = songs[num] nextSong.pos = num else local num = (ongoingSong.pos)%#songs+1 nextSong = songs[num] nextSong.pos = num end end cc tweaked pastebin PasteShr cc tweaked pastebin end end function songPlayer() local event, command, arg1 = nil, nil, nil local runStartingStatement = true while true do if runStartingStatement then if autoPlay and nextSong.pos ~= nil then ongoingSong = nextSong nextSong = {} cc tweaked pastebin How to get it for free? cc tweaked pastebin else ongoingSong = {} event, command, arg1 = os.pullEvent("song") end else runStartingStatement = true end if ongoingSong.pos ~= nil or (command == "play" and tonumber(arg1) and arg1 > 0 and arg1 < #songs) then if paused then paused = false cc tweaked pastebin How to get it for free? cc tweaked pastebin os.queueEvent("screen","update","bottomBar") end if arg1 then ongoingSong = songs[arg1] ongoingSong.pos = arg1 nextSong = {} end chooseNextSong() local songPath = ongoingSong.path local handle, err = fs.open(songPath,"rb") cc tweaked pastebin How to use it? cc tweaked pastebin event, command, arg1 = nil, nil, nil if not handle then error(err) end if #speakers == 0 then loadSpeakers() end local song = loadSong(handle) handle.close() if song.length == nil then song.length = song.ticks[#song.ticks].ticks end local ticks = song.ticks local waitTime = 1/song.tempo cc tweaked pastebin How to use it? cc tweaked pastebin local startTime = os.clock() local i = 1 while i <= #ticks do songProgress = ticks[i].tick/song.length layers = ticks[i].layers for j=1, #layers do l = layers[j] speakers[j%#speakers+1].playNote(instruments[l.instrument],volume,l.note) end local timerID = nil cc tweaked pastebin How to dowload it? cc tweaked pastebin local pauseStart = nil if paused then pauseStart = os.clock() elseif i ~= #ticks then timerID = os.startTimer(startTime+waitTime*ticks[i+1].tick-os.clock()) --sleep((startTime+waitTime*ticks[i+1].tick)-os.clock()) end local moveOn = false while i ~= #ticks and not moveOn do event, command, arg1 = os.pullEvent() cc tweaked pastebin How to get it for free? cc tweaked pastebin moveOn = true if event == "timer" and command == timerID then elseif event == "song" then if command == "resume" and pauseStart ~= nil then startTime = startTime + (os.clock()-pauseStart) elseif command == "play" and arg1 ~= nil and songPath ~= nil and songs ~= nil and songs[arg1] ~= nil then if songs[arg1].path ~= songPath then i = #ticks+1 runStartingStatement = false else cc tweaked pastebin PasteShr cc tweaked pastebin i = 0 startTime = os.clock() end paused = false os.queueEvent("screen", "update", "bottomBar") elseif command == "jump" then if arg1 > 0 and arg1 <= song.length*arg1 then local pos = math.floor(arg1*song.length) local closestTick = -1 local closestTickPos = 0 cc tweaked pastebin How to dowload it? cc tweaked pastebin for b=1, #ticks do if closestTick == -1 or math.abs(pos-ticks[b].tick) <= math.abs(pos-closestTick) then closestTick = ticks[b].tick closestTickPos = b else break end if b%100 == 0 then sleep(0.00001) end cc tweaked pastebin How to get it? cc tweaked pastebin end i=closestTickPos startTime = os.clock()-(waitTime*closestTick) end paused = false os.queueEvent("screen", "update", "bottomBar") end else moveOn = false end cc tweaked pastebin How to use it? cc tweaked pastebin end i = i+1 end end end end function recursiveFind(dir, key) local files = {} local directories = {dir} local iterations = 0 cc tweaked pastebin How to get it for free? cc tweaked pastebin while #directories > 0 do iterations = iterations + 1 if iterations%100 == 0 then sleep(0.0001) end local path = table.remove(directories,1) local dirFiles = fs.list(path) for i, v in pairs(dirFiles) do if fs.isDir(path .. "/" .. v) then table.insert(directories, path .. v .. "/") cc tweaked pastebin How to get it for free? cc tweaked pastebin elseif string.find(v,key) then table.insert(files, path .. v) end end end return files end function retrieveSongs() local files = recursiveFind("", ".nbs") songs = {} cc tweaked pastebin How to use it? cc tweaked pastebin for i=1, #files do local file, err = fs.open(files[i],"rb") if not file then error(err) end local tmpSong = songDetails(file) file.close() local song = {} song.author = tmpSong.author song.name = tmpSong.name if song.name == "" and tmpSong.ogFileName ~= "" then song.name = tmpSong.ogFileName cc tweaked pastebin How to dowload it? cc tweaked pastebin elseif song.name == "" then song.name = files[i] end song.path = files[i] song.duration = math.floor(tmpSong.duration*100)/100 table.insert(songs,song) end end screenPrototype = {screen="og"} function screenPrototype.__init__(baseClass, data) cc tweaked pastebin How to get it? cc tweaked pastebin self = {} for i,v in pairs(data) do self[i] = v end setmetatable(self, {__index=screenPrototype}) return self end setmetatable(screenPrototype, {__call=screenPrototype.__init__}) function screenPrototype:bWrite(str, posX, posY, backColor, frontColor) local screen = self cc tweaked pastebin How to dowload it? cc tweaked pastebin prevBackColor = screen.getBackgroundColor() prevFrontColor = screen.getTextColor() if backColor then screen.setBackgroundColor(backColor) end if frontColor then screen.setTextColor(frontColor) end screen.setCursorPos(posX,posY) screen.write(str) cc tweaked pastebin How to get it? cc tweaked pastebin screen.setTextColor(prevFrontColor) screen.setBackgroundColor(prevBackColor) end function xCenText(str, space) return string.rep(" ", math.floor((space-#str)/2)) .. str .. string.rep(" ", math.ceil((space-#str)/2)) end local songSelectionSpace = 6 local songScreen = {} function songScreen.drawSongSelector(screen) if not screen.name then cc tweaked pastebin How to get it for free? cc tweaked pastebin screen = screenPrototype(screen) end if #songs == 0 then retrieveSongs() end local topSong = screen.topSong local selectedSong = screen.selectedSong local x,y = screen.getSize() screen.setBackgroundColor(colors.yellow) local lines = y-songSelectionSpace screen.setCursorPos(1,1) local space = (x-11)/2 cc tweaked pastebin How to get it for free? cc tweaked pastebin local nameSpace = space + space%1 local authorSpace = space - space%1 local durationSpace = 10 if authorSpace%2 == nameSpace%2 then nameSpace = nameSpace + nameSpace%2 authorSpace = authorSpace - authorSpace%2 else nameSpace = nameSpace - nameSpace%2 authorSpace = authorSpace - authorSpace%2 durationSpace = durationSpace+1 cc tweaked pastebin How to get it for free? cc tweaked pastebin end nameStart = 1 nameEnd = nameSpace+1 authorStart = nameSpace+1 authorEnd = authorStart+authorSpace durationStart = authorEnd+1 durationEnd = durationStart+durationSpace screen:bWrite(xCenText("Name", nameSpace), nameStart,1) screen:bWrite("|", nameSpace,1, colors.red) screen:bWrite(xCenText("Author", authorSpace), authorStart,1) cc tweaked pastebin How to use it? cc tweaked pastebin screen:bWrite("|", nameSpace+1+authorSpace,1, colors.red) screen:bWrite(string.rep(" ", x-nameSpace-authorSpace-1-9) .. "Length(s)", durationStart,1) screen:bWrite(string.rep("-", nameSpace-1) .. "+" .. string.rep("-",authorSpace) .. "+" .. string.rep("-", durationSpace), 1,2, colors.red) local iter = lines-3 if #songs < iter then iter = #songs end for i=topSong, iter+topSong-1 do local song = songs[i] local yLevel = i-topSong+3 local backColor = colors.yellow local frontColor = colors.white cc tweaked pastebin PasteShr cc tweaked pastebin if i == selectedSong then backColor = colors.white frontColor = colors.black end if #song.name > nameSpace then screen:bWrite(string.sub(song.name,1,nameSpace), 1, yLevel, backColor, frontColor) else screen:bWrite(song.name .. string.rep(" ", nameSpace-#song.name), 1, yLevel, backColor, frontColor) end screen:bWrite("|", nameSpace,yLevel, colors.red) cc tweaked pastebin How to get it for free? cc tweaked pastebin if #song.author > authorSpace then screen:bWrite(string.sub(song.author,1,authorSpace), authorStart, yLevel, backColor, frontColor) else screen:bWrite(song.author .. string.rep(" ", authorSpace-#song.author), authorStart, yLevel, backColor, frontColor) end screen:bWrite("|", authorEnd,yLevel, colors.red) if #tostring(song.duration) > durationSpace then screen:bWrite(string.sub(tostring(song.duration),1,durationSpace), durationStart, yLevel, backColor, frontColor) else screen:bWrite(tostring(song.duration) .. string.rep(" ", durationSpace-#tostring(song.duration)), durationStart, yLevel, backColor, frontColor) cc tweaked pastebin How to get it? cc tweaked pastebin end end if #songs < lines then for i=#songs+3, lines-1 do screen:bWrite(string.rep(" ", nameSpace-1), 1, i) screen:bWrite("|", nameSpace,i, colors.red) screen:bWrite(string.rep(" ", authorSpace), authorStart,i) screen:bWrite("|", nameSpace+1+authorSpace,i, colors.red) screen:bWrite(string.rep(" ", durationSpace), durationStart, i) end cc tweaked pastebin How to dowload it? cc tweaked pastebin end screen:bWrite(string.rep("-", nameSpace-1) .. "+" .. string.rep("-",authorSpace) .. "+" .. string.rep("-", durationSpace), 1,lines, colors.red) end function songScreen.drawBottomBar(screen) if not screen.name then screen = screenPrototype(screen) end local selectedSong = screen.selectedSong if #songs == 0 then retrieveSongs() end local x,y = screen.getSize() cc tweaked pastebin PasteShr cc tweaked pastebin local stLine = y-songSelectionSpace+1 local playingStr = stringSelector(ongoingSong.pos ~= nil, table.pack("Playing: ",ongoingSong.name, " by ",ongoingSong.author), "Playing: ") screen:bWrite(string.sub(playingStr,1, x-3) .. string.rep(" ", x-#playingStr-3), 1, stLine, colors.black) local nextStr = stringSelector(nextSong.pos ~= nil, table.pack("Next: ",nextSong.name," by ",nextSong.author), "Next: ") screen:bWrite(string.sub(nextStr, 1, x-3) .. string.rep(" ", x-#nextStr-3), 1, stLine+1, colors.black) if selectedSong > 1 then screen:bWrite("/\\", x-1,stLine, colors.blue) else screen:bWrite(" ", x-1,stLine, colors.blue) end cc tweaked pastebin How to get it for free? cc tweaked pastebin if selectedSong ~= #songs then screen:bWrite("\\/", x-1, stLine+1, colors.blue) else screen:bWrite(" ", x-1,stLine+1, colors.blue) end screen:bWrite(">", x-2,stLine,colors.green) screen:bWrite(" ", x-2,stLine+1,colors.red) local button1Space = math.floor((x-1)/2) local button2Space = math.ceil((x-1)/2) screen:bWrite(xCenText("Pause",button1Space),1,stLine+2, stringSelector(paused,colors.white,colors.gray), colors.black) cc tweaked pastebin How to get it for free? cc tweaked pastebin screen:bWrite(xCenText("shuffle",button2Space),button1Space+2,stLine+2, stringSelector(shuffle,colors.white,colors.gray), colors.black) screen:bWrite(xCenText("Auto Play",button1Space),1,stLine+3, stringSelector(autoPlay,colors.white,colors.gray), colors.black) screen:bWrite(xCenText("Refresh Songs",button2Space),button1Space+2,stLine+3, colors.blue, colors.white) screen:bWrite(xCenText("Random",button1Space),1,stLine+4, colors.blue, colors.white) screen:bWrite(xCenText("Configure",button2Space),button1Space+2,stLine+4, colors.blue, colors.white) bars = math.floor(x*songProgress) screen:bWrite(string.rep(" ", bars), 1, y, colors.white) screen:bWrite(string.rep(" ", x-bars) .. ">", bars+2, y, colors.black) end cc tweaked pastebin How to get it? cc tweaked pastebin function songScreen.load(screen) screen.setBackgroundColor(colors.black) screen.clear() songScreen.drawSongSelector(screen) songScreen.drawBottomBar(screen) end local timerEventID = nil function songScreen.event(screen, event) local update = true if event[1] == "timer" and event[2] == timerEventID then cc tweaked pastebin How to get it? cc tweaked pastebin update = false songScreen.drawBottomBar(screen) elseif (event[1] == "monitor_touch" and event[2] == screen.name) or (event[1] == "mouse_click" and screen.name == "term") then local x,y = screen.getSize() local stLine = y-songSelectionSpace+1 local button1Space = math.floor((x-1)/2) local button2Space = math.ceil((x-1)/2) local tx = event[3] local ty = event[4] if ty > 2 and ty < y-songSelectionSpace then cc tweaked pastebin How to dowload it? cc tweaked pastebin if ty-2 <= #songs then if ty-2+screen.topSong-1 == screen.selectedSong then os.queueEvent("song","play",screen.selectedSong) else screen.selectedSong = ty-2+screen.topSong-1 end end elseif screen.selectedSong ~= 1 and ty == stLine and tx >= x-1 then screen.selectedSong = screen.selectedSong - 1 if screen.selectedSong < screen.topSong then cc tweaked pastebin How to dowload it? cc tweaked pastebin screen.topSong = screen.selectedSong end elseif screen.selectedSong < #songs and ty == stLine+1 and tx >= x-1 then screen.selectedSong = screen.selectedSong + 1 if screen.selectedSong >= screen.topSong+(y-songSelectionSpace-3) then screen.topSong = screen.topSong + 1 end elseif screen.selectedSong >= 1 and screen.selectedSong <= #songs and ty == stLine and tx == x-2 then os.queueEvent("song","play",screen.selectedSong) elseif ty == stLine+2 and tx >= 1 and tx <= button1Space then cc tweaked pastebin How to get it for free? cc tweaked pastebin paused = not paused os.queueEvent("song","resume") elseif ty == stLine+2 and tx >= button1Space+2 and tx <= x then shuffle = not shuffle if autoPlay then nextSong = {} chooseNextSong() end elseif ty == stLine+3 and tx >= 1 and tx <= button1Space then autoPlay = not autoPlay cc tweaked pastebin PasteShr cc tweaked pastebin if autoPlay then nextSong = {} chooseNextSong() else nextSong = {} end elseif ty == stLine+3 and tx >= button1Space+2 and tx <= x then retrieveSongs() os.queueEvent("screen","update") elseif ty == stLine+4 and tx >= 1 and tx <= button1Space and (ongoingSong.pos == nil or #songs > 1) then cc tweaked pastebin How to get it for free? cc tweaked pastebin nextSong = {} local songNum = round(#songs*math.random()) if ongoingSong.pos ~= nil and #songs > 1 then iterations = 0 while songNum == ongoingSong.pos do songNum = round(#songs*math.random()) iterations = iterations+1 if iterations > 10 then songNum = (orginalSong.pos)%#songs+1 end cc tweaked pastebin How to use it? cc tweaked pastebin end end os.queueEvent("song","play", songNum) elseif ty == stLine+4 and tx >= button1Space+2 and tx <= x then --configuration elseif ty == y then update = false os.queueEvent("song", "jump",tx/x) end elseif event[1] == "key" and screen.name == "term" then cc tweaked pastebin PasteShr cc tweaked pastebin local x,y = screen.getSize() if event[2] == keys.up then if screen.selectedSong ~= 1 then screen.selectedSong = screen.selectedSong - 1 if screen.selectedSong < screen.topSong then screen.topSong = screen.selectedSong end end elseif event[2] == keys.down then if screen.selectedSong ~= #songs then cc tweaked pastebin How to get it? cc tweaked pastebin screen.selectedSong = screen.selectedSong + 1 if screen.selectedSong > screen.topSong+(y-songSelectionSpace-4) then screen.topSong = screen.topSong + 1 end end elseif event[2] == keys.enter and screen.selectedSong >= 1 and screen.selectedSong then os.queueEvent("song","play",screen.selectedSong) elseif event[2] == keys.tab then runMainLoop = false update = false cc tweaked pastebin How to get it for free? cc tweaked pastebin end elseif event[1] == "screen" and event[2] == "update" then update = false if event[3] == "bottomBar" then songScreen.drawBottomBar(screen) elseif event[3] == "songSelector" then songScreen.drawSongSelector(screen) else songScreen.load(screen) end cc tweaked pastebin How to get it for free? cc tweaked pastebin else update = false end if update then songScreen.load(screen) end end local screens = {} for i,v in pairs(peripheral.getNames()) do if (peripheral.getType(v) == "monitor") then cc tweaked pastebin How to dowload it? cc tweaked pastebin local tScreen = screenPrototype(peripheral.wrap(v)) tScreen.selectedSong = 1 tScreen.topSong = 1 tScreen.name = v songScreen.load(tScreen) table.insert(screens,tScreen) end end local tScreen = screenPrototype(term) tScreen.selectedSong = 1 cc tweaked pastebin How to get it? cc tweaked pastebin tScreen.topSong = 1 tScreen.name = "term" songScreen.load(tScreen) table.insert(screens,tScreen) function mainLoop() timerEventID = os.startTimer(1) while runMainLoop do local event = table.pack(os.pullEvent()) for i=1, #screens do songScreen.event(screens[i],event) cc tweaked pastebin PasteShr cc tweaked pastebin end if event[1] == "timer" and event[2] == timerEventID then timerEventID = os.startTimer(1) end end end parallel.waitForAny(mainLoop,songPlayer) cc tweaked pastebin