Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local patternLength = 32
- local beatLength = 4
- local bpm = 240
- local songLength = 1
- local sp = {}
- local i = 0
- local p = peripheral.wrap("speaker_"..i)
- repeat
- sp[i] = p
- i = i+1
- p = peripheral.wrap("speaker_"..i)
- until p == nil
- local nextSpeaker = 0
- local speakerCount = table.getn(sp)
- local instrumentCount = 10
- local instruments = {
- -- musical
- {["name"] = "piano", ["file"] = "block.note.harp"},
- {["name"] = "bajo", ["file"] = "block.note.bass"},
- {["name"] = "campana", ["file"] = "block.note.bell"},
- {["name"] = "tintineo", ["file"] = "block.note.chime"},
- {["name"] = "xylofón", ["file"] = "block.note.xylophone"},
- {["name"] = "flauta", ["file"] = "block.note.flute"},
- {["name"] = "guitarra", ["file"] = "block.note.guitar"},
- {["name"] = "bombo", ["file"] = "block.note.basedrum"},
- {["name"] = "platillo", ["file"] = "block.note.hat"},
- {["name"] = "redoblante", ["file"] = "block.note.snare"},
- -- sfx
- {["name"] = "levelup", ["file"] ="entity.player.levelup"}
- }
- local notes = {
- "F#", "G ", "G#", "A ", "A#", "B ",
- "C ", "C#", "D ", "D#", "E ", "F "
- }
- local song = {}
- local patterns = {}
- local currentPattern = 1
- local currentSongPattern = 1
- local currentChannel = 1
- local currentYOffset = 0
- local currentXOffset = 0
- local cursor = 1
- local playing = false
- local pressingShift = false
- local playingTimer = nil
- local delayedNoteTimer = nil
- local delayedAllChannels = false
- local delayTime = 1
- local menuSongPatternScroll = 1
- local menuChannelScroll = 1
- local beatsOnScreen = 30
- local scrW, scrH = term.getSize()
- -- Data of the current pattern
- local function getCurrentNotes(time)
- local notes = patterns[currentPattern].channels[currentChannel].notes[time]
- if (notes == nil) then
- return ""
- else
- return notes
- end
- end
- -- Data of the pattern for the specified channel
- local function getChannelNotes(channel, time)
- local notes = patterns[currentPattern].channels[channel].notes[time]
- if (notes == nil) then
- return ""
- else
- return notes
- end
- end
- -- Get an instrument by name
- local function getInstrumentByName(name)
- for k, v in pairs(instruments) do
- if (v.name == name) then
- return v
- end
- end
- error("Invalid instrument name")
- end
- -- Note character to note number
- local function characterToNote(c)
- local delayed = true
- if (string.byte(c) >= 97) then
- delayed = false
- end
- if (delayed) then
- return string.byte(c)-65, delayed
- else
- return string.byte(c)-97, delayed
- end
- end
- -- Note number to note character
- local function noteToCharacter(n, delayed)
- if (delayed) then
- return string.char(tonumber(n)+65) --Mayuscula
- else
- return string.char(tonumber(n)+97) --minuscula
- end
- end
- -- Pitch of a note number
- local function notePitch(n)
- return math.pow(2, (n-12)/12)
- end
- -- Pitch of a note character
- local function charPitch(c)
- return notePitch(characterToNote(c))
- end
- local function playSoundEffect(soundname, vol, pitch)
- sp[nextSpeaker].playSound(getInstrumentByName(soundname).file, vol, pitch)
- nextSpeaker = nextSpeaker+1
- if (nextSpeaker > speakerCount) then
- nextSpeaker = 0
- end
- end
- -- Play a single note number
- local function playNote(inst, vol, note)
- sp[nextSpeaker].playSound(instruments[inst].file, vol, note)
- nextSpeaker = nextSpeaker+1
- if (nextSpeaker > speakerCount) then
- nextSpeaker = 0
- end
- end
- -- Play a string of notes
- local function playBar(inst, vol, notes, delayed)
- for n=1, string.len(notes) do
- local note = string.sub(notes, n, n)
- if (delayed and string.byte(note) < 97) then -- If its delayed, play only uppercases
- playNote(inst, vol, charPitch(note))
- elseif (not delayed and string.byte(note) >= 97) then -- If not, play only lowercases
- playNote(inst, vol, charPitch(note))
- end
- end
- end
- -- Play the delayed bar
- local function playCurrentBarDelayed()
- if (delayedAllChannels) then
- for i=1, instrumentCount do
- playBar(i, 1, getChannelNotes(i, delayTime), true)
- end
- else
- playBar(currentChannel, 1, getCurrentNotes(delayTime), true)
- end
- end
- -- Play the bar at a specified time
- local function playSpecificBar(time, allChannels)
- delayTime = time
- if (allChannels) then
- for i=1, instrumentCount do
- playBar(i, 1, getChannelNotes(i, time), false)
- end
- else
- playBar(currentChannel, 1, getCurrentNotes(time), false)
- end
- delayedNoteTimer = os.startTimer(30/bpm)
- delayedAllChannels = allChannels
- end
- -- Play the current channel's bar currently indicated by the cursor
- local function playCurrentChannelBar()
- playSpecificBar(cursor, false)
- end
- -- Play the every bar currently indicated by the cursor
- local function playCurrentBar()
- playSpecificBar(cursor, true)
- end
- -- Add a character to the middle of a string
- local function addCharAt(str, char, pos)
- local prev = string.sub(str, 1, pos)
- local post = string.sub(str, pos+1, string.len(str))
- return prev..char..post
- end
- -- Remove a character from a string
- local function removeCharacterAt(str, pos)
- local prev = string.sub(str, 1, pos-1)
- local post = string.sub(str, pos+1, string.len(str))
- return prev..post
- end
- -- Replace a character in the middle of a string
- local function replaceCharAt(str, char, pos)
- local prev = string.sub(str, 1, pos)
- local post = string.sub(str, pos+2, string.len(str))
- return prev..char..post
- end
- -- Pixel coordinates to time and note character
- local function pixelToMusic(x, y, delayed)
- local time = x-3+currentXOffset
- local note = noteToCharacter(scrH-y+currentYOffset, delayed)
- return time, note
- end
- -- Time and note character to pixel coordinates
- local function musicToPixel(time, char)
- local noteNumber, delayed = characterToNote(char)
- local x = time+3-currentXOffset
- local y = -noteNumber+currentYOffset+scrH
- return x, y
- end
- -- StartPlaying
- local function playFromCursor()
- playing = true
- playingTimer = os.startTimer(60/bpm)
- playCurrentBar()
- end
- -- StartPlaying
- local function playFromStart()
- cursor = 1
- currentXOffset = 0
- playing = true
- playingTimer = os.startTimer(60/bpm)
- playCurrentBar()
- end
- -- Pause
- local function stop()
- playing = false
- cursor = 1
- end
- -- Pause
- local function pause()
- playing = false
- end
- -- Draw the musical sheet section of the screen
- local function DrawPattern()
- local channel = currentChannel
- local pattern = currentPattern
- local xoffset = currentXOffset
- local yoffset = currentYOffset
- for _y=1, scrH-2 do
- local y = scrH-_y
- local octave = math.floor((_y+yoffset-7)/12)+1
- local n = (_y+yoffset)%12
- local c = xoffset
- term.setCursorPos(1,y+1)
- term.setBackgroundColor(colors.black)
- if (n==1 or n==3 or n==5 or n==8 or n==10) then
- term.setTextColor(colors.blue)
- else
- term.setTextColor(colors.cyan)
- end
- term.write(notes[(n-1)%12+1]..tostring(octave))
- local lineBackgroundColor = colors.white
- local lineTextColor = colors.lightGray
- local blackNote = false
- if (n==1 or n==3 or n==5 or n==8 or n==10) then
- lineBackgroundColor = colors.lightGray
- lineTextColor = colors.gray
- blackNote = true
- end
- for c=xoffset, math.min(patternLength-1, xoffset+beatsOnScreen-1) do
- term.setBackgroundColor(lineBackgroundColor)
- term.setTextColor(lineTextColor)
- -- Draw Cursor Bar
- if (cursor == c+1) then
- if (playing) then
- term.setBackgroundColor(colors.red)
- term.setTextColor(colors.black)
- else
- term.setBackgroundColor(colors.yellow)
- if (blackNote) then
- term.setTextColor(colors.brown)
- else
- term.setTextColor(colors.orange)
- end
- end
- end
- if (c%(beatLength*4) == 0) then
- term.write("|")
- elseif (c%beatLength == 0) then
- term.write(",")
- else
- term.write("_")
- end
- end
- end
- term.setCursorPos(1,1)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.yellow)
- term.write(string.rep(" ", beatLength+3))
- local i = 1+(-xoffset)%beatLength
- local firstBeat = math.floor((xoffset-1)/beatLength)+1
- for c=firstBeat, math.min(firstBeat+beatsOnScreen/beatLength-1, patternLength/beatLength-1) do
- term.setCursorPos(3+i, 2)
- term.write(tonumber(c+1))
- term.write(string.rep(" ", beatLength))
- i = i+beatLength
- end
- -- Draw other instruments notes
- for ch=1, instrumentCount do
- if (ch ~= currentChannel) then
- for time=xoffset+1, math.min(xoffset+beatsOnScreen, patternLength) do
- local notes = getChannelNotes(ch, time)
- local paintedNotes = {}
- for n=1, string.len(notes) do
- local noteCharacter = string.sub(notes, n, n)
- local delayed = true
- if (string.byte(noteCharacter) >= 97) then
- delayed = false
- end
- local x, y = musicToPixel(time, noteCharacter)
- term.setTextColor(colors.lightGray)
- if (y > 2) then
- if (paintedNotes[y]) then
- term.setBackgroundColor(colors.pink)
- term.setCursorPos(x, y)
- term.write("x")
- else
- if (delayed) then
- term.setBackgroundColor(colors.pink)
- term.setCursorPos(x, y)
- term.write(">")
- paintedNotes[y] = true
- else
- term.setBackgroundColor(colors.lightBlue)
- term.setCursorPos(x, y)
- term.write(" ")
- paintedNotes[y] = true
- end
- end
- end
- end
- end
- end
- end
- -- Draw current instruments notes
- for time=xoffset+1, math.min(xoffset+beatsOnScreen, patternLength) do
- local notes = getCurrentNotes(time)
- local paintedNotes = {}
- for n=1, string.len(notes) do
- local noteCharacter = string.sub(notes, n, n)
- local delayed = true
- if (string.byte(noteCharacter) >= 97) then
- delayed = false
- end
- local x, y = musicToPixel(time, noteCharacter)
- term.setTextColor(colors.black)
- if (y > 2) then
- if (paintedNotes[y]) then
- term.setBackgroundColor(colors.purple)
- term.setCursorPos(x, y)
- term.write("x")
- else
- if (delayed) then
- term.setBackgroundColor(colors.red)
- term.setCursorPos(x, y)
- term.write(">")
- paintedNotes[y] = true
- else
- term.setBackgroundColor(colors.blue)
- term.setCursorPos(x, y)
- term.write(" ")
- paintedNotes[y] = true
- end
- end
- end
- end
- end
- term.setBackgroundColor(colors.pink)
- term.setTextColor(colors.black)
- term.setCursorPos(1,1)
- term.write(" Parte "..tostring(song.patterns[currentSongPattern]).." ")
- term.setBackgroundColor(colors.black)
- term.write(" ")
- term.setBackgroundColor(colors.lime)
- term.write(" "..instruments[currentChannel].name.." ")
- end
- -- Draw the control panel
- local function DrawPanel()
- local panelWidth = scrW-(beatsOnScreen+3)
- local x = scrW-panelWidth+1
- term.setBackgroundColor(colors.brown)
- for y=1, scrH do
- term.setCursorPos(x,y)
- term.write(string.rep(" ", panelWidth))
- end
- term.setBackgroundColor(colors.orange)
- term.setTextColor(colors.black)
- -- Menues
- term.setCursorPos(x+1, 1)
- term.write("File")
- term.setCursorPos(x+7, 1)
- term.write("Opc.")
- -- Patterns
- term.setTextColor(colors.black)
- if (menuSongPatternScroll == 1) then
- term.setBackgroundColor(colors.gray)
- else
- term.setBackgroundColor(colors.lightGray)
- end
- term.setCursorPos(x+1, 3)
- term.write(" ^ ")
- local sp = menuSongPatternScroll
- for y= 4, 8 do
- if (sp == currentSongPattern) then
- term.setBackgroundColor(colors.pink)
- else
- term.setBackgroundColor(colors.black)
- end
- term.setCursorPos(x+1, y)
- term.write(" ")
- if (sp <= songLength) then
- if (sp == currentSongPattern) then
- term.setBackgroundColor(colors.pink)
- term.setTextColor(colors.black)
- else
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.pink)
- end
- term.setCursorPos(x+1, y)
- term.write("#"..sp)
- term.setCursorPos(x+5, y)
- term.write("P"..sp)
- sp = sp+1
- end
- end
- term.setTextColor(colors.black)
- if (menuSongPatternScroll >= songLength-4) then
- term.setBackgroundColor(colors.gray)
- else
- term.setBackgroundColor(colors.lightGray)
- end
- term.setCursorPos(x+1, 9)
- term.write(" v ")
- -- Instruments
- term.setTextColor(colors.black)
- if (menuChannelScroll == 1) then
- term.setBackgroundColor(colors.gray)
- else
- term.setBackgroundColor(colors.lightGray)
- end
- term.setCursorPos(x+10, 3)
- term.write(" ^ ")
- local ch = menuChannelScroll
- local y = 4
- for y= 4, 8 do
- if (ch == currentChannel) then
- term.setBackgroundColor(colors.lime)
- else
- term.setBackgroundColor(colors.black)
- end
- term.setCursorPos(x+10, y)
- term.write(" ")
- term.setCursorPos(x+10, y)
- if (ch == currentChannel) then
- term.setBackgroundColor(colors.lime)
- term.setTextColor(colors.black)
- else
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.lime)
- end
- term.write(string.sub(instruments[ch].name, 1, 7))
- ch = ch+1
- end
- term.setTextColor(colors.black)
- if (menuChannelScroll >= instrumentCount-4) then
- term.setBackgroundColor(colors.gray)
- else
- term.setBackgroundColor(colors.lightGray)
- end
- term.setCursorPos(x+10, 9)
- term.write(" v ")
- term.setTextColor(colors.black)
- -- Play button
- --term.setCursorPos(x+3, 11)
- --if (playing) then
- -- term.setBackgroundColor(colors.red)
- -- term.write("||")
- --else
- -- term.setBackgroundColor(colors.lime)
- -- term.write(" >")
- --end
- -- Play from the beggining button
- --term.setCursorPos(x+7, 11)
- --if (playing) then
- -- term.setBackgroundColor(colors.red)
- -- term.write("[]")
- --else
- -- term.setBackgroundColor(colors.lime)
- -- term.write("|>")
- --end
- term.setCursorPos(x+2, 11)
- term.setBackgroundColor(colors.brown)
- term.setTextColor(colors.white)
- term.write("Vel: ")
- term.setBackgroundColor(colors.white)
- term.setTextColor(colors.black)
- term.write(" "..bpm.." ")
- term.setCursorPos(x+2, 13)
- term.setBackgroundColor(colors.brown)
- term.setTextColor(colors.white)
- term.write("Len: ")
- term.setBackgroundColor(colors.white)
- term.setTextColor(colors.black)
- term.write(" "..patternLength.." ")
- term.setCursorPos(x+2, 15)
- term.setBackgroundColor(colors.brown)
- term.setTextColor(colors.white)
- term.write("Beat:")
- term.setBackgroundColor(colors.white)
- term.setTextColor(colors.black)
- term.write(" "..beatLength.." ")
- end
- -- Draw the entire screen
- local function DrawScreen()
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setTextColor(colors.white)
- DrawPattern()
- DrawPanel()
- end
- -- GUI Functions
- -- Scroll sheet
- local function scrollRight() if (currentXOffset < patternLength-beatsOnScreen) then currentXOffset = currentXOffset+1
- DrawScreen() end end
- local function scrollLeft() if (currentXOffset > 0) then currentXOffset = currentXOffset-1
- DrawScreen() end end
- local function scrollUp() if (currentYOffset < 27-scrH) then currentYOffset = currentYOffset+1
- DrawScreen() end end
- local function scrollDown() if (currentYOffset > 0) then currentYOffset = currentYOffset-1
- DrawScreen() end end
- -- Scroll song patterns
- local function nextSongPattern() if (menuSongPatternScroll < songLength-4) then menuSongPatternScroll = menuSongPatternScroll+1
- DrawPanel() end end
- local function prevSongPattern() if (menuSongPatternScroll > 1) then menuSongPatternScroll = menuSongPatternScroll-1
- DrawPanel() end end
- -- Scroll instruments
- local function nextSongPattern() if (menuChannelScroll < instrumentCount-4) then menuChannelScroll = menuChannelScroll+1
- DrawPanel() end end
- local function prevSongPattern() if (menuChannelScroll > 1) then menuChannelScroll = menuChannelScroll-1
- DrawPanel() end end
- -- Load pattern from a string, or create a blank one
- local function loadPattern(str)
- if (str == nil) then
- if (patterns[currentPattern] == nil) then
- patterns[currentPattern] = {
- ["channels"] = {},
- ["length"] = patternLength,
- }
- end
- for i=1, instrumentCount do
- patterns[currentPattern].channels[i] = {
- ["notes"] = {}
- }
- end
- end
- end
- -- Load song from file, or create a new one
- local function loadSong(str)
- if (str == nil) then
- song = {
- ["patterns"] = {1},
- ["instrument"] = {
- "bass", "bassdrumbell", "bell", "chime", "flute", "guitar", "hat", "snare", "harp", "xylophone"
- }
- }
- currentPattern = 1
- loadPattern()
- end
- end
- -- Place a note at a specific x, y coordinate on the screen
- local function placeNote(x, y, delayed)
- local time, note = pixelToMusic(x,y,delayed)
- local notes = getCurrentNotes(time)
- local done = false
- -- See if the note is already there, if it is, delete it
- for n=1, string.len(notes) do
- local character = string.sub(notes, n, n)
- if (character == note) then
- notes = removeCharacterAt(notes, n)
- patterns[currentPattern].channels[currentChannel].notes[time] = notes
- done = true
- break
- end
- end
- -- If there is no note there, add it
- if (not done) then
- notes = notes..note
- patterns[currentPattern].channels[currentChannel].notes[time] = notes
- end
- -- Play remaining notes
- playSpecificBar(time, false)
- -- Redraw sheet
- DrawScreen()
- end
- -- Start
- loadSong()
- playSoundEffect("levelup", 0.5, 3)
- DrawScreen()
- -- Update
- while true do
- local event, a, b, c = os.pullEvent()
- if (event == "timer") then
- local completed = a
- if (completed == playingTimer) then
- if (playing) then
- cursor = cursor+1
- if (cursor > patternLength) then
- cursor = 1
- currentXOffset = 1
- end
- if (cursor > currentXOffset+beatsOnScreen) then
- currentXOffset = math.min(currentXOffset+beatsOnScreen, patternLength-beatsOnScreen)
- end
- playCurrentBar()
- DrawScreen()
- playingTimer = os.startTimer(60/bpm)
- end
- elseif (completed == delayedNoteTimer) then
- playCurrentBarDelayed()
- end
- elseif (event == "key") then
- local key = a
- if (key == keys.up) then
- scrollUp()
- elseif (key == keys.down) then
- scrollDown()
- elseif (key == keys.left) then
- scrollLeft()
- elseif (key == keys.right) then
- scrollRight()
- elseif (key == keys.leftShift) then
- pressingShift = true
- elseif (key == keys.space) then
- if (playing) then
- pause()
- else
- playFromCursor()
- end
- DrawScreen()
- elseif (key == keys.enter) then
- if (playing) then
- stop()
- else
- playFromStart()
- end
- DrawScreen()
- end
- elseif (event == "key_up") then
- local key = a
- if (key == keys.leftShift) then
- pressingShift = false
- end
- elseif (event == "mouse_scroll") then
- local scrollDirection = a
- if (scrollDirection == 1) then
- if (not pressingShift) then scrollDown()
- else scrollRight() end
- else
- if (not pressingShift) then scrollUp()
- else scrollLeft() end
- end
- elseif (event == "mouse_click") then
- local button = a
- local x = b
- local y = c
- -- Note Painter
- if (x > 3 and x < 4+math.min(beatsOnScreen, patternLength)) then
- if (y > 2) then
- if (button == 1) then
- placeNote(x,y,false) -- Left click = normal note
- else
- placeNote(x,y,true) -- Right click = delayed note
- end
- else
- local time, note = pixelToMusic(x,y)
- cursor = time
- playCurrentBar()
- DrawScreen()
- end
- else
- -- Panel
- local panelx = beatsOnScreen+3
- -- Patterns
- if (x >= panelx+1 and x <= panelx+8) then
- if (y == 3) then
- prevSongPattern()
- end
- for i=4, 8 do
- if (y == i) then
- local selectedSongPattern = i-4+menuSongPatternScroll
- if (selectedSongPattern <= songLength) then
- currentSongPattern = selectedSongPattern
- DrawScreen()
- end
- break
- end
- end
- if (y == 9) then
- nextSongPattern()
- end
- end
- -- Patterns
- if (x >= panelx+10 and x <= panelx+17) then
- if (y == 3) then
- prevSongPattern()
- end
- for i=4, 8 do
- if (y == i) then
- local selectedChannel = i-4+menuChannelScroll
- if (selectedChannel <= instrumentCount) then
- currentChannel = selectedChannel
- playNote(currentChannel, 1, 1)
- DrawScreen()
- end
- break
- end
- end
- if (y == 9) then
- nextSongPattern()
- end
- end
- if (y == 11) then -- Velocidad
- term.setCursorPos(panelx+8, 11)
- term.setBackgroundColor(colors.yellow)
- term.setTextColor(colors.black)
- term.write(" ")
- term.setCursorPos(panelx+9, 11)
- pause()
- local newbpm = tonumber(read())
- if (newbpm ~= nil) then
- bpm = math.floor(newbpm)
- end
- DrawPanel()
- end
- if (y == 13) then -- PatternLength
- term.setCursorPos(panelx+8, 13)
- term.setBackgroundColor(colors.yellow)
- term.setTextColor(colors.black)
- term.write(" ")
- term.setCursorPos(panelx+9, 13)
- pause()
- local newLength = tonumber(read())
- if (newLength ~= nil) then
- patternLength = math.floor(newLength)
- end
- DrawScreen()
- end
- if (y == 15) then -- Beat
- term.setCursorPos(panelx+8, 15)
- term.setBackgroundColor(colors.yellow)
- term.setTextColor(colors.black)
- term.write(" ")
- term.setCursorPos(panelx+9, 15)
- pause()
- local newBeat = tonumber(read())
- if (newBeat ~= nil) then
- beatLength = math.floor(newBeat)
- end
- DrawScreen()
- end
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement