Advertisement
Guest User

memdiaplayer

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