Advertisement
Guest User

MusicPlayerCC - Changes

a guest
May 9th, 2024
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.25 KB | Source Code | 0 0
  1. local dfpwm = require("cc.audio.dfpwm")
  2. local speakers = { peripheral.find("speaker") } -- this way i can utilyze ALL speakers
  3.  
  4.  
  5. -- Initial Boot Setup Script
  6. local basaltExists = fs.exists("basalt.lua")
  7. local musicDir = "/disk/"
  8. local configFilePath = "/config.txt"
  9. local defaultConfig = {
  10.     chunkSize = 1024*6, -- Default chunk size
  11. }
  12.  
  13.  
  14. local function downloadAndRunInstaller(url, ...)
  15.     local args = {...}  -- Capture any additional arguments to pass to the script
  16.  
  17.     -- Use http.get to fetch the script
  18.     local response = http.get(url)
  19.     if not response then
  20.         error("Failed to download the installer script")
  21.     end
  22.  
  23.     local scriptContent = response.readAll()
  24.     response.close()
  25.  
  26.     -- Use loadstring to load the script content.
  27.     -- Note: loadstring is available in Lua 5.1, but in newer versions, load may be used.
  28.     local script, errorMessage = loadstring(scriptContent)
  29.     if not script then
  30.         error("Failed to load the installer script: " .. errorMessage)
  31.     end
  32.  
  33.     -- Execute the loaded script, passing any additional arguments
  34.     script(table.unpack(args))
  35. end
  36.  
  37.  
  38. -- Function to check and install Basalt
  39. local function installBasalt()
  40.     if not basaltExists then
  41.         print("Basalt not found. Attempting to download...")
  42.         downloadAndRunInstaller("https://basalt.madefor.cc/install.lua", "release", "latest.lua")
  43.         print("Basalt installed successfully.")
  44.     else
  45.         print("Basalt is already installed.")
  46.     end
  47. end
  48.  
  49. -- Function to create the music directory if it doesn't exist
  50. local function createMusicDirectory()
  51.     if not fs.exists(musicDir) then
  52.         print("Missing Disk. Please, insert Disk and Restart.")
  53.         os.pullEventRaw("terminate")
  54.     else
  55.         print("Disk detected")
  56.     end
  57. end
  58.  
  59. -- Function to create or update the configuration file
  60. local function createOrUpdateConfig()
  61.     local configData = textutils.serialize(defaultConfig)
  62.     local configFile = fs.open(configFilePath, "w")
  63.     configFile.write(configData)
  64.     configFile.close()
  65.     print("Configuration file created/updated.")
  66. end
  67.  
  68. -- Main function to run the initial setup
  69. local function runInitialSetup()
  70.     installBasalt()
  71.     createMusicDirectory()
  72.     createOrUpdateConfig()
  73.     if basaltExists == false then
  74.     term.clear()
  75.     print("Initial setup completed.")
  76.     print("Program is terminating to allow user upload of music.")
  77.     print("Use the /music/ folder created for music storage.")
  78.     print("Sub folders are supported to allow for user organization")
  79.     print("Thank you for using this program and enjoy!")
  80.     print()
  81.     print("-coppertj")
  82.     os.sleep(5)
  83.     error()
  84. end
  85. end
  86.  
  87. -- Execute the initial setup
  88. runInitialSetup()
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96. -- Ensure these are initialized at the start of your script
  97. local basalt = require("basalt")
  98. local isPaused = false
  99. local lastChunkData = nil
  100. local currentPosition = 1
  101. local playbackStarted = false
  102. local trackStartTime = 0
  103. local backPressedOnce = false
  104. local basalt = require("basalt")
  105. local breakit = false
  106. local main = basalt.createFrame()
  107.  
  108. local screenWidth, screenHeight = main:getSize()
  109. local numBars = 16  -- Number of bars in the visualizer
  110. local barWidth = 1  -- Width of each bar (1 character)
  111. local visualizerHeight = 10  -- Max height of the visualizer
  112. local visualizerStartX = math.floor((screenWidth - (numBars * barWidth)) / 2)
  113. local visualizerStartY = screenHeight - visualizerHeight - 5  -- Position above the bottom buttons
  114.  
  115. local buttonWidth = screenWidth / 4
  116. local buttonHeight = 3 -- Height of buttons
  117. local spacing = (screenWidth - (buttonWidth * 3)) / 4
  118. local startY = screenHeight - buttonHeight - 1 -- Y position for buttons
  119. local returnToMenu = false
  120. -- Define rootFolder where your music files are located
  121. local rootFolder = "/disk/"
  122. local shuffleEnabled = false
  123. -- Function to read the contents of a folder and return the filenames as a table
  124. function readFolderContents(folderPath)
  125.     local fileNames = {}
  126.     for _, file in ipairs(fs.list(folderPath)) do
  127.         table.insert(fileNames, file)
  128.     end
  129.     return fileNames
  130. end
  131.  
  132. -- This is the function that i took from someone elses code
  133. function playChunk(chunk)
  134.     local returnValue = nil
  135.     local callbacks = {}
  136.    
  137.     for i, speaker in pairs(speakers) do
  138.         if i > 1 then
  139.             table.insert(callbacks, function()
  140.                 speaker.playAudio(chunk, volume or 1.0)
  141.             end)
  142.         else
  143.             table.insert(callbacks, function()
  144.                 returnValue = speaker.playAudio(chunk, volume or 1.0)
  145.             end)
  146.         end
  147.     end
  148.        
  149.         parallel.waitForAll(table.unpack(callbacks))
  150.        
  151.         return returnValue
  152. end
  153.  
  154.  
  155.  
  156. -- Shuffle State Label
  157. local shuffleStateLabel = main:addLabel()
  158.     :setText("Shuffle: Off")
  159.     :setPosition(12, 2) -- Adjust to position next to the shuffle button
  160.     :setSize(15, 1)
  161.  
  162. local function updateShuffleStateLabel()
  163.     if shuffleEnabled then
  164.         shuffleStateLabel:setText("Shuffle: On")
  165.     else
  166.         shuffleStateLabel:setText("Shuffle: Off")
  167.     end
  168.     -- Refresh the frame to show the updated label text if necessary
  169. end
  170.  
  171. local function onShufflePressed()
  172.     shuffleEnabled = not shuffleEnabled
  173.     updateShuffleStateLabel()
  174. end
  175.  
  176.  
  177. -- Assuming you have variables for spacing and startY like before
  178. -- Adjust these values as needed to position your shuffle button
  179. local shuffleButtonX = 10  -- Adjust based on your layout, for example, placing it at the top left
  180. local shuffleButtonY = 20  -- Adjust based on your layout, perhaps just below the top edge
  181.  
  182.  
  183.  
  184.  
  185. -- Initialize fileList using the readFolderContents function
  186.  
  187.  
  188.  
  189. -- File Explorer
  190. local mainFrame = basalt.createFrame("firstBaseFrame"):show()
  191. local fileList = mainFrame:addList()
  192.     :setPosition(2, 2)
  193.     :setSize(20, 15) -- Adjust size as needed
  194.  
  195. local function updateFileList(path)
  196.     fileList:clear() -- Clear the list for new directory contents
  197.     local files = fs.list(path) -- Use the passed 'path' parameter
  198.     for _, file in ipairs(files) do
  199.         fileList:addItem(file) -- Add each file or directory to the list
  200.     end
  201. end
  202.  
  203. fileList:onSelect(function(self, event, item)
  204.     if item and item.text then
  205.         local selectedFileName = item.text
  206.         local selectedItemPath = fs.combine(rootFolder, selectedFileName)
  207.  
  208.         if fs.isDir(selectedItemPath) then
  209.             -- Navigate into the directory
  210.             rootFolder = selectedItemPath .. "/" -- Update the rootFolder to the new path
  211.             updateFileList(rootFolder) -- Update the list to show contents of the selected directory
  212.         else
  213.             -- Handle file selection
  214.             basalt.setActiveFrame(main) -- Switch to the playback frame
  215.                         parallel.waitForAny(
  216.                 function() mainLoop(selectedItemPath) end, -- Play the selected file
  217.                 waitForUserInput -- Continue listening for user input
  218.             )
  219.         end
  220.     end
  221. end)
  222.  
  223.  
  224.  
  225.  
  226. -- Add any additional UI elements like a back button here
  227.  
  228.  
  229.  
  230.  
  231. local function togglePauseState()
  232.     isPaused = not isPaused
  233.  
  234. end
  235.  
  236. local function onPlaybackPressed()
  237.             local currentTime = os.clock()
  238.             if backPressedOnce and (currentTime - trackStartTime) <= 3 then
  239.                 -- If back button is pressed twice within 3 seconds, go to the previous song
  240.                 currentPosition = math.max(1, currentPosition - 1)
  241.                 backPressedOnce = false  -- Reset the flag
  242.             else
  243.                 -- If pressed once or after 3 seconds, restart the current song or go to the previous song
  244.                 if (currentTime - trackStartTime) > 3 then
  245.                     -- Consider as a request to go back if beyond the threshold
  246.                     currentPosition = math.max(1, currentPosition - 1)
  247.                 end
  248.                 backPressedOnce = true  -- Set the flag for the first press
  249.                 trackStartTime = currentTime  -- Update start time to prevent immediate trigger
  250.             end
  251.             breakit = true
  252.               -- Exit to restart the current song or go to the previous song
  253. end
  254.  
  255.  
  256. local playedTracks = {}
  257. local fileLister = readFolderContents(rootFolder)
  258. function shuffleTrack()
  259.     -- Reset shuffle if all tracks have been played
  260.     if tablelength(playedTracks) >= #fileLister then
  261.         playedTracks = {}  -- Clear played tracks for reshuffle
  262.         -- Optional: Return to the menu or start shuffle again
  263.     end
  264.  
  265.     local nextTrack
  266.     repeat
  267.         nextTrack = math.random(1, #fileLister)
  268.     until not playedTracks[nextTrack]
  269.  
  270.     playedTracks[nextTrack] = true
  271.     currentPosition = nextTrack  -- Update current position for shuffle track
  272.     return true
  273. end
  274.  
  275.  
  276. local function onSkipPressed()
  277.  
  278.         -- If shuffle is not enabled, simply move to the next track in order
  279.        
  280.         if currentPosition > #fileLister then
  281.             currentPosition = 1 -- Optionally loop back to the start or stop playback
  282.         end
  283.  
  284.     breakit = true
  285. end
  286.  
  287. local bars = {}
  288.  
  289. for i = 1, numBars do
  290.     local xPosition = visualizerStartX + (i - 1) * barWidth
  291.     bars[i] = main:addLabel()
  292.         :setPosition(xPosition, visualizerStartY)
  293.         :setSize(barWidth, visualizerHeight)
  294.         :setText("|")
  295. end
  296.  
  297. local labelYPosition = visualizerStartY + 7  -- Position the label above the visualizer, adjust as needed
  298. local trackLabel = main:addLabel()
  299.     :setPosition(1, labelYPosition)
  300.     :setSize(screenWidth, 5)
  301.     :setText("")
  302.  
  303.  
  304. local function onMainMenuButtonPressed()
  305.     breakit = true
  306.     returnToMenu = true
  307.     rootFolder = "/disk/"
  308.     updateFileList(rootFolder)
  309.         basalt.setActiveFrame(mainFrame)
  310.         mainFrame:show()
  311.         main:hide()
  312.         basalt.update()
  313.         -- Initialize the list with contents from the starting directory
  314.  
  315. end
  316.  
  317. function waitForUserInput()
  318.     breakit = false
  319.     returnToMenu = false
  320.             basalt.setActiveFrame(main)
  321.             main:show()
  322.             mainFrame:hide()
  323.             basalt.update()
  324.     while true do
  325.     if returnToMenu == true then
  326.             basalt.setActiveFrame(mainFrame)
  327.         main:show()
  328.         mainFrame:hide()
  329.         basalt.update()
  330.         -- Initialize the list with contents from the starting directory
  331.     break
  332.     end
  333.     -- Creating buttons
  334. -- Create the main menu button at the top left corner of the screen
  335. local mainMenuButton = main:addButton()
  336.     :setText("=")
  337.     :setPosition(1, 1)  -- Top left corner
  338.     :setSize(3, 1)  -- Adjust size as needed
  339.     :onClick(onMainMenuButtonPressed)
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346. -- Create the shuffle button with a smaller size
  347. local shuffleButton = main:addButton()
  348.     :setText("Shuffle")
  349.     :setPosition(shuffleButtonX, shuffleButtonY)
  350.     :setSize(7, 1)  -- Smaller width and standard height for a single character
  351.     :onClick(onShufflePressed)
  352.    
  353.    
  354. -- Playback Button
  355. local playbackButton = main:addButton()
  356.     :setText("<")
  357.     :setPosition(spacing * 2, startY)
  358.     :setSize(buttonWidth, buttonHeight)
  359.     :onClick(onPlaybackPressed)
  360.  
  361. -- Pause Button
  362. local pauseButton = main:addButton()
  363.     :setText("||")
  364.     :setPosition(spacing * 3 + buttonWidth, startY)
  365.     :setSize(buttonWidth, buttonHeight)
  366.     :onClick(togglePauseState)
  367.  
  368. -- Skip Button
  369. local skipButton = main:addButton()
  370.     :setText(">")
  371.     :setPosition(spacing * 4 + buttonWidth * 2, startY)
  372.     :setSize(buttonWidth, buttonHeight)
  373.     :onClick(onSkipPressed)
  374. for i = 1, numBars do
  375.     local height
  376.     if isPaused then
  377.         height = 1  -- Set height to 0 to clear the bars if paused
  378.     else
  379.         height = math.random(1, visualizerHeight)  -- Otherwise, set a random height
  380.     end
  381.     local barText = string.rep("|", height) .. string.rep(" ", visualizerHeight - height)
  382.     bars[i]:setText(barText)
  383. end
  384.  
  385.             local ev = table.pack(os.pullEventRaw())
  386.             basalt.update(table.unpack(ev))
  387. end
  388.  
  389. end
  390.  
  391.  
  392. local function updateShuffleStateLabel()
  393.     if shuffleEnabled then
  394.         shuffleStateLabel:setText("Shuffle: On")
  395.     else
  396.         shuffleStateLabel:setText("Shuffle: Off")
  397.     end
  398.     -- Refresh the frame to show the updated label text if necessary
  399. end
  400.  
  401.  
  402.  
  403.  
  404. -- Updated function to play a DFPWM file, incorporating pause logic
  405. function playSound(filePath)
  406. breakit = false
  407. trackStartTime = os.clock()
  408.     local file = fs.open(filePath, "rb")
  409.     if not file then
  410.         return
  411.     end
  412.  
  413.     local decoder = dfpwm.make_decoder()
  414.     local chunkData, buffer
  415.     local fileName = filePath:match("^.+/(.+)$") or filePath  -- Pattern extracts the name after the last slash
  416.     trackLabel:setText(fileName)  -- Update the label text with the current file name
  417.    
  418.     while true do
  419.     if breakit == true then break end
  420.         if isPaused then
  421.             os.sleep(0.5)  -- Simple pause handling
  422.         else
  423.             chunkData = file.read(1024*16)
  424.             if not chunkData then break end  -- End of file
  425.             buffer = decoder(chunkData)
  426.  
  427.             while not playChunk(buffer) do
  428.                 if isPaused then
  429.                     lastChunkData = chunkData  -- Save the chunk data if paused mid-playback
  430.                     break
  431.                 end
  432.                 os.pullEvent("speaker_audio_empty")
  433.             end
  434.         end
  435.  
  436. end
  437.     file.close()
  438. end
  439.  
  440. -- Main playback loop
  441. -- Initialize playedTracks outside the main loop to track played files across shuffle iterations
  442. local playedTracks = {}
  443.  
  444. -- Main playback loop
  445. function mainLoop(initialTrackPath)
  446.     fileLister = readFolderContents(rootFolder)
  447.  
  448.     if initialTrackPath then
  449.         -- Directly play the provided track
  450.         playSound(initialTrackPath)
  451.         -- Find and set currentPosition for the next track
  452.         for index, fileName in ipairs(fileLister) do
  453.             local filePath = fs.combine(rootFolder, fileName)
  454.             if filePath == initialTrackPath then
  455.                 currentPosition = index + 1
  456.                 break
  457.             end
  458.         end
  459.     end
  460.  
  461.     -- Check if shuffle is enabled and reset playedTracks if necessary
  462.     if shuffleEnabled and #playedTracks == #fileLister then
  463.         playedTracks = {} -- Reset after all tracks have been played
  464.     end
  465.  
  466.     while currentPosition <= #fileLister do
  467.         if returnToMenu then break end
  468.  
  469.         local fileName = fileLister[currentPosition]
  470.         local filePath = fs.combine(rootFolder, fileName) -- Correctly combine paths
  471.  
  472.         if shuffleEnabled then
  473.             local nextTrack
  474.             repeat
  475.                 nextTrack = math.random(1, #fileLister)
  476.             until not playedTracks[nextTrack] or #playedTracks == #fileLister
  477.  
  478.             playedTracks[nextTrack] = true
  479.             currentPosition = nextTrack
  480.             filePath = fs.combine(rootFolder, fileLister[currentPosition]) -- Update filePath for shuffle
  481.             playSound(filePath)
  482.         else
  483.             playSound(filePath)
  484.             currentPosition = currentPosition + 1
  485.         end
  486.  
  487.         if currentPosition > #fileLister and not shuffleEnabled then
  488.                 breakit = true
  489.     returnToMenu = true
  490.         basalt.setActiveFrame(mainFrame)
  491.         mainFrame:show()
  492.         main:hide()
  493.         basalt.update()
  494.         -- Initialize the list with contents from the starting directory
  495. updateFileList("/disk/")
  496.  
  497.             break
  498.         end
  499.     end
  500. end
  501.  
  502.  
  503. updateFileList(rootFolder)
  504. basalt.autoUpdate()
  505.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement