Advertisement
GeoffAlexander

memusicplayer.lua

Jan 8th, 2021
875
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.84 KB | None | 0 0
  1. ------------------------ Params
  2. local songVolume = 1
  3. local firstSpeaker = 0
  4. local lastSpeaker = 100
  5. local redstoneStartSide = nil
  6. local loopAmount = 2 -- Does nothing if redstoneStartSide is nil
  7. local diskSide = "bottom"
  8.  
  9. ----------------------------
  10. local songBpm = 240
  11. local songLength = 1
  12. local songCursor = 1
  13. local songBeats = 0
  14. local loopsLeft = 0
  15.  
  16. local sp = {}
  17. local _i = 0
  18. for i=firstSpeaker, lastSpeaker do
  19.     local p = peripheral.wrap("speaker_"..i)
  20.     if (p ~= nil) then
  21.         sp[_i] = p
  22.         _i = _i+1
  23.     end
  24. end
  25.  
  26. local nextSpeaker = 0
  27. local speakerCount = table.getn(sp)
  28.  
  29. local instrumentCount = 11
  30. local instruments = {
  31.     -- musical
  32.     {["name"] = "piano", ["file"] = "block.note_block.harp"},
  33.     {["name"] = "bass", ["file"] = "block.note_block.bass"},
  34.     {["name"] = "bell", ["file"] = "block.note_block.bell"},
  35.     {["name"] = "chime", ["file"] = "block.note_block.chime"},
  36.     {["name"] = "xhylophone", ["file"] = "block.note_block.xylophone"},
  37.     {["name"] = "flute", ["file"] = "block.note_block.flute"},
  38.     {["name"] = "guitar", ["file"] = "block.note_block.guitar"},
  39.     {["name"] = "kick", ["file"] = "block.note_block.basedrum"},
  40.     {["name"] = "hat", ["file"] = "block.note_block.hat"},
  41.     {["name"] = "cimbal", ["file"] = "block.sand_block.break"},
  42.     {["name"] = "snare", ["file"] = "block.note_block.snare"},
  43.     -- sfx
  44.     {["name"] = "levelup", ["file"] ="entity.player.levelup"}
  45. }
  46.  
  47. local song = {}
  48. local patterns = {}
  49.  
  50. local currentPattern = 1
  51. local currentSongPattern = 1
  52. local cursor = 1
  53. local playing = false
  54.  
  55. local playingTimer = nil
  56. local delayedNoteTimer = nil
  57. local delayedAllChannels = false
  58. local delayTime = 1
  59.  
  60. local progressTimer = nil
  61.  
  62. -- Data of the current pattern
  63. local function getCurrentNotes(time)
  64.     local notes = patterns[currentPattern].channels[currentChannel].notes[time]
  65.     if (notes == nil) then
  66.         return ""
  67.     else
  68.         return notes
  69.     end
  70. end
  71.  
  72. -- Data of the pattern for the specified channel
  73. local function getChannelNotes(channel, time)
  74.     local notes = patterns[currentPattern].channels[channel].notes[time]
  75.     if (notes == nil) then
  76.         return ""
  77.     else
  78.         return notes
  79.     end
  80. end
  81.  
  82. -- Get an instrument by name
  83. local function getInstrumentByName(name)
  84.     for k, v in pairs(instruments) do
  85.         if (v.name == name) then
  86.             return v
  87.         end
  88.     end
  89.     error("Invalid instrument name")
  90. end
  91.  
  92. -- Get an instrument ID by name
  93. local function getInstrumentIDByName(name)
  94.     for i=1, instrumentCount do
  95.         if (instruments[i].name == name) then
  96.             return i
  97.         end
  98.     end
  99.     error("Invalid instrument name")
  100. end
  101.  
  102. -- Note character to note number
  103. local function characterToNote(c)
  104.     local delayed = true
  105.     if (string.byte(c) >= 97) then
  106.         delayed = false
  107.     end
  108.    
  109.     if (delayed) then
  110.         return string.byte(c)-65, delayed
  111.     else
  112.         return string.byte(c)-97, delayed
  113.     end
  114. end
  115.  
  116. -- Note number to note character
  117. local function noteToCharacter(n, delayed)
  118.     if (delayed) then
  119.         return string.char(tonumber(n)+65) --Mayuscula
  120.     else
  121.         return string.char(tonumber(n)+97) --minuscula
  122.     end
  123. end
  124.  
  125. -- Pitch of a note number
  126. local function notePitch(n)
  127.     return math.pow(2, (n-12)/12)
  128. end
  129.  
  130. -- Pitch of a note character
  131. local function charPitch(c)
  132.     return notePitch(characterToNote(c))
  133. end
  134.  
  135. local function playSoundEffect(soundname, vol, pitch)
  136.     sp[nextSpeaker].playSound(getInstrumentByName(soundname).file, vol, pitch)
  137.     nextSpeaker = nextSpeaker+1
  138.     if (nextSpeaker > speakerCount) then
  139.         nextSpeaker = 0
  140.     end
  141. end
  142.  
  143. -- Play a single note number
  144. local function playNote(inst, vol, note)
  145.     sp[nextSpeaker].playSound(instruments[inst].file, vol, note)
  146.     nextSpeaker = nextSpeaker+1
  147.     if (nextSpeaker > speakerCount) then
  148.         nextSpeaker = 0
  149.     end
  150. end
  151.  
  152. -- Play a string of notes
  153. local function playBar(inst, vol, notes, delayed)
  154.     for n=1, string.len(notes) do
  155.         local note = string.sub(notes, n, n)
  156.         if (delayed and string.byte(note) < 97) then -- If its delayed, play only uppercases
  157.             playNote(inst, vol, charPitch(note))
  158.         elseif (not delayed and string.byte(note) >= 97) then -- If not, play only lowercases
  159.             playNote(inst, vol, charPitch(note))
  160.         end
  161.     end
  162. end
  163.  
  164. -- Play the delayed bar
  165. local function playCurrentBarDelayed()
  166.     if (delayedAllChannels) then
  167.         for i=1, instrumentCount do
  168.             playBar(i, songVolume, getChannelNotes(i, delayTime), true)
  169.         end
  170.     else
  171.         playBar(currentChannel, songVolume, getCurrentNotes(delayTime), true)
  172.     end
  173. end
  174.  
  175. -- Play the bar at a specified time
  176. local function playSpecificBar(time, allChannels)
  177.     delayTime = time
  178.     if (allChannels) then
  179.         for i=1, instrumentCount do
  180.             playBar(i, songVolume, getChannelNotes(i, time), false)
  181.         end
  182.     else
  183.         playBar(currentChannel, songVolume, getCurrentNotes(time), false)
  184.     end
  185.     delayedNoteTimer = os.startTimer(30/(songBpm*patterns[currentPattern].speed)-0.001)
  186.     delayedAllChannels = allChannels
  187. end
  188.  
  189. -- Play the current channel's bar currently indicated by the cursor
  190. local function playCurrentChannelBar()
  191.     playSpecificBar(cursor, false)
  192. end
  193.  
  194. -- Play the every bar currently indicated by the cursor
  195. local function playCurrentBar()
  196.     playSpecificBar(cursor, true)
  197. end
  198.  
  199. local function updateProgress()
  200.     term.setCursorPos(6, 13)
  201.     local percent = songCursor/songBeats
  202.     local characters = math.ceil(percent*40)
  203.     term.setBackgroundColor(colors.orange)
  204.     term.write(string.rep(" ", characters))
  205.     term.setBackgroundColor(colors.brown)
  206.     term.write(string.rep(" ", 40-characters))
  207.     term.setBackgroundColor(colors.black)
  208. end
  209.  
  210. -- StartPlaying
  211. local function playFromCursor()
  212.     if (songBeats > 0) then
  213.         playing = true
  214.         playingTimer = os.startTimer(60/(songBpm*patterns[currentPattern].speed))
  215.         progressTimer = os.startTimer(1)
  216.         updateProgress()
  217.         playCurrentBar()
  218.         term.setCursorPos(5, 12)
  219.         term.setTextColor(colors.lime)
  220.         term.write(" > Play        ")
  221.     end
  222. end
  223.  
  224. -- StartPlaying
  225. local function playFromStart()
  226.     if (not loadSong()) then return end
  227.     cursor = 1
  228.     songCursor = 1
  229.     currentXOffset = 0
  230.     playing = true
  231.     playingTimer = os.startTimer(60/(songBpm*patterns[currentPattern].speed))
  232.     progressTimer = os.startTimer(1)
  233.     updateProgress()
  234.     playCurrentBar()
  235.     term.setCursorPos(5, 12)
  236.     term.setTextColor(colors.lime)
  237.     term.write("|> Play         ")
  238. end
  239.  
  240. -- Pause
  241. local function stop()
  242.     playing = false
  243.     cursor = 1
  244.     songCursor = 1
  245.     term.setCursorPos(5, 12)
  246.     term.setTextColor(colors.red)
  247.     term.write("[] Stop     ")
  248. end
  249.  
  250. -- Pause
  251. local function pause()
  252.     playing = false
  253.     term.setCursorPos(5, 12)
  254.     term.setTextColor(colors.red)
  255.     term.write("|| Pause    ")
  256. end
  257.  
  258. local function newPattern()
  259.     local p = {
  260.         ["channels"] = {},
  261.         ["length"] = defaultPatternLength,
  262.         ["speed"] = 1,
  263.         ["measure"] = defaultMeasure
  264.     }
  265.     for i=1, instrumentCount do
  266.         p.channels[i] = {
  267.             ["notes"] = {}
  268.         }
  269.     end
  270.     return p
  271. end
  272.  
  273. local function newSong()
  274.     song = {
  275.         ["patterns"] = {}
  276.     }
  277.     currentPattern = 1
  278.     patterns[currentPattern] = newPattern()
  279. end
  280.  
  281. -- Load song from disk
  282. function loadSong()
  283.  if (disk.isPresent(diskSide)) then
  284.     local f = fs.open("disk/memsong.txt", "r")
  285.  if (f == nil) then
  286.      return false
  287.  end
  288.     local fileTitle = "unknown"
  289.     local fileBpm = 240
  290.     local fileSong = {}
  291.  
  292.     newSong()
  293.     patterns = {}
  294.  songBeats = 0
  295.  
  296.     local line = f.readLine()
  297.  
  298.     repeat
  299.         if (line == "title") then
  300.             fileTitle = f.readLine()
  301.         elseif (line == "bpm") then
  302.             fileBpm = tonumber(f.readLine())
  303.         elseif (line == "structure") then
  304.             local songPart = f.readLine()
  305.             local i=1
  306.             repeat
  307.                 fileSong[i] = tonumber(songPart)
  308.                 i = i+1
  309.                 songPart = f.readLine()
  310.             until songPart == ""
  311.         elseif (line == "pattern") then
  312.             local currentPattern = tonumber(f.readLine())
  313.             if (currentPattern ~= nil) then
  314.                 patterns[currentPattern] = newPattern()
  315.                 local pLine = f.readLine()
  316.                 repeat
  317.                     local broken = false
  318.                     if (pLine == "length") then
  319.                         patterns[currentPattern].length = tonumber(f.readLine())
  320.                     elseif (pLine == "measure") then
  321.                         patterns[currentPattern].measure = tonumber(f.readLine())
  322.                     elseif (pLine == "speed") then
  323.                         patterns[currentPattern].speed = tonumber(f.readLine())
  324.                     elseif (pLine == "notes") then
  325.                         local instrument = f.readLine()
  326.                         if (instrument ~= "") then
  327.                             repeat
  328.                                 local noteString = f.readLine()
  329.                                 local notes = {}
  330.                                 local pointer = 1
  331.                                 local spaces = 0
  332.  
  333.                                 for i=1, string.len(noteString) do
  334.                                    
  335.                                     local char = string.sub(noteString, i, i)
  336.  
  337.                                     if (tonumber(char)) then -- If the character is a number, count spaces
  338.                                         spaces = spaces*10
  339.                                         spaces = spaces+tonumber(char)
  340.                                     else
  341.                                         pointer = pointer+spaces
  342.                                         if (notes[pointer] == nil) then
  343.                                             notes[pointer] = ""
  344.                                         end
  345.                                         notes[pointer] = notes[pointer]..char
  346.                                         spaces = 0
  347.                                     end
  348.                                 end
  349.  
  350.                                 patterns[currentPattern].channels[getInstrumentIDByName(instrument)].notes = notes
  351.                                 songBeats = songBeats + patterns[currentPattern].length
  352.  
  353.                                 instrument = f.readLine()
  354.                             until instrument == ""
  355.                             broken = true
  356.                         end
  357.                     end
  358.                     if (broken) then
  359.                         pLine = nil
  360.                     else
  361.                         pLine = f.readLine()
  362.                     end
  363.                 until pLine == "" or pLine == nil
  364.             end
  365.         end
  366.         line = f.readLine()
  367.     until line == "" or line == nil
  368.     f.close()
  369.  
  370.     song.patterns = fileSong
  371.     songLength = #song.patterns
  372.  songBeats = 0
  373.  for i=1, songLength do
  374.      songBeats = songBeats+patterns[song.patterns[i]].length
  375.  end
  376.     songBpm = fileBpm
  377.     currentSongPattern = 1
  378.     currentPattern = song.patterns[1]
  379.  
  380.  term.setCursorPos(1, 8)
  381.  term.setBackgroundColor(colors.black)
  382.  term.setTextColor(colors.yellow)
  383.  term.write("                                         ")
  384.  term.setCursorPos(1, 8)
  385.  term.write("Now Playing: "..fileTitle)
  386.  return true
  387.  else
  388.  return false
  389.  end
  390. end
  391.  
  392. term.clear()
  393. term.setCursorPos(1,1)
  394. term.setBackgroundColor(colors.black)
  395. term.setTextColor(colors.orange)
  396. print("~ Media Player ~")
  397. print("")
  398. print("Enter: Load Song / Start from beginning / Pause")
  399. print("Space: Pause / Play")
  400.  
  401. -- Update
  402. while true do
  403.     local event, a, b, c = os.pullEvent()
  404.     if (event == "timer") then
  405.         local completed = a
  406.         if (completed == playingTimer) then
  407.             if (playing) then
  408.                 local patternLength = patterns[currentPattern].length
  409.                 cursor = cursor+1
  410.                 songCursor = songCursor+1
  411.                 if (cursor > patternLength) then
  412.                     currentSongPattern = currentSongPattern+1
  413.                     if (currentSongPattern > songLength) then
  414.                         if (redstoneStartSide ~= nil) then
  415.                             loopsLeft = loopsLeft-1
  416.                             if (loopsLeft <= 0) then
  417.                                 loopsLeft = loopAmount
  418.                                 while (not rs.getInput(redstoneStartSide)) do
  419.                                     os.pullEvent("redstone")
  420.                                 end
  421.                             end
  422.                         end
  423.                         currentSongPattern = 1
  424.                         songCursor = 1
  425.                         updateProgress()
  426.                     end
  427.                     currentPattern = song.patterns[currentSongPattern]
  428.                     cursor = 1
  429.                     currentXOffset = 0
  430.                 end
  431.                 playCurrentBar()
  432.                 playingTimer = os.startTimer(60/(songBpm*patterns[currentPattern].speed))
  433.             end
  434.         elseif (completed == delayedNoteTimer) then
  435.             playCurrentBarDelayed()
  436.         elseif (completed == progressTimer) then
  437.             if (playing) then
  438.                 updateProgress()
  439.                 progressTimer = os.startTimer(1)
  440.             end
  441.         end
  442.     elseif (event == "key") then
  443.         local key = a
  444.         if (key == keys.space) then
  445.             if (playing) then
  446.                 pause()
  447.             else
  448.                 playFromCursor()
  449.             end
  450.            
  451.         elseif (key == keys.enter) then
  452.             if (playing) then
  453.                 pause()
  454.             else
  455.                 playFromStart()
  456.             end
  457.            
  458.         end
  459.     end
  460. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement