nitrogenfingers

horriblemakerofhorriblenoises

Sep 22nd, 2014
247
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.63 KB | None | 0 0
  1. local noteblock = nil
  2. local notes = {
  3.     { name = "F#", color = "black"};
  4.     { name = "G", color = "white"}; { name = "G#", color = "black"};
  5.     { name = "A", color = "white"}; { name = "Bb", color = "black"};
  6.     { name = "B", color = "white"};
  7.     { name = "C", color = "white"}; { name = "C#", color = "black"};
  8.     { name = "D", color = "white"}; { name = "D#", color = "black"};
  9.     { name = "E", color = "white"};
  10.     { name = "F", color = "white"}; { name = "F#", color = "black"};
  11.     { name = "G", color = "white"}; { name = "G#", color = "black"};
  12.     { name = "A", color = "white"}; { name = "Bb", color = "black"};
  13.     { name = "B", color = "white"};
  14.     { name = "C", color = "white"}; { name = "C#", color = "black"};
  15.     { name = "D", color = "white"}; { name = "D#", color = "black"};
  16.     { name = "E", color = "white"};
  17.     { name = "F", color = "white"}; { name = "F#", color = "black"};
  18. }
  19.  
  20. local scales = {
  21.     major = {2,2,1,2,2,2,1}, minor = {2,1,2,2,1,3,1}
  22. }
  23. local chordscale = { 2, 2, 3 }
  24.  
  25. local voice = 0
  26.  
  27. --Sets up the program
  28. for _,side in pairs(rs.getSides()) do
  29.   if peripheral.getType(side) == "iron_note" then
  30.     noteblock = peripheral.wrap(side)
  31.     break
  32.   end
  33. end
  34. if noteblock == nil then
  35.   print("No noteblock block found")
  36.   return
  37. end
  38.  
  39. --Half debug half for fun, this shows which notes have just been played
  40. --on a virtual keyboard
  41. function drawKeyboard(pressed)
  42.     local xoff = term.getSize()/2 - #notes/2
  43.     local yoff = 5;
  44.    
  45.     --Drawing each key on the keyboard. This really only needs to be done
  46.     --once, but lazy...
  47.     term.setBackgroundColour(colours.black)
  48.     term.clear()
  49.     for i=1,#notes do
  50.         term.setBackgroundColour(colours[notes[i].color])
  51.         for y=1,2 do
  52.             term.setCursorPos(xoff+i,yoff+y)
  53.             term.write(" ")
  54.         end
  55.         term.setBackgroundColour(colours.white)
  56.         for y=3,4 do
  57.             term.setCursorPos(xoff+i,yoff+y)
  58.             term.write(" ")
  59.         end
  60.     end
  61.    
  62.     term.setBackgroundColour(colours.black)
  63.     term.setTextColour(colours.white)
  64.     for i=1,#pressed do
  65.         local ypos = yoff + 5
  66.         if notes[pressed[i]+1].color == "black" then ypos = yoff end
  67.         term.setCursorPos(xoff+pressed[i]+1,ypos)
  68.         term.write(notes[pressed[i]+1].name)
  69.     end
  70. end
  71.  
  72. --This takes a tune, a sequence of notes, chords and intervals in a chosen
  73. --voice and plays them sequentially.
  74. function playTune(tune)
  75.     drawKeyboard({})
  76.     for i=1,#tune.seq do
  77.         sleep(tune.int[i])
  78.         if type(tune.seq[i]) == "number" then
  79.             noteblock.playNote(tune.voice,tune.seq[i])
  80.             drawKeyboard( {tune.seq[i]} )
  81.         else for _,note in pairs(tune.seq[i]) do
  82.             noteblock.playNote(tune.voice,note)
  83.             drawKeyboard( tune.seq[i] )
  84.         end end
  85.     end
  86. end
  87.  
  88. --This takes a list of tunse and plays them each simultaneously.
  89. function playTuneList(tuneList)
  90.     drawKeyboard({})
  91.     --We keep two lists; a list of progress in each track, and the length of
  92.     --the next interval, or delay, before a note is played.
  93.     local delayList,progList = {
  94.         min = function(self)
  95.             local minval = math.huge
  96.             for _,v in ipairs(self) do
  97.                 if type(v) ~= "string" then
  98.                     minval = math.min(minval, v)
  99.                 end
  100.             end
  101.             if minval == math.huge then minval = 0 end
  102.             return minval
  103.         end
  104.     }, {
  105.         sum = function(self)
  106.             local sumval = 0
  107.             for _,v in ipairs(self) do
  108.                 sumval = sumval + v
  109.             end
  110.             return sumval
  111.         end
  112.     }
  113.     local tuneSum = 0
  114.     for i=1,#tuneList do
  115.         progList[i] = 1
  116.         delayList[i] = tuneList[i].int[progList[i]]
  117.         tuneSum = tuneSum + #tuneList[i].seq
  118.     end
  119.    
  120.     --So long we still have notes in any sequence to play, we keep going
  121.     while progList:sum() <= tuneSum do
  122.         local playedNotes = {}
  123.         --First we see if there are any notes we can play. With the exception
  124.         --of the first call, there should always be at least one. Remember
  125.         --only 5 notes can play at once; we catch this and cut off the extra
  126.         --in the order they're put in (so least important track last if you
  127.         --anticipate overlap!)
  128.         for i,v in ipairs(delayList) do
  129.             if type(v) == "number" and v <= 0 then
  130.                 local tune = tuneList[i]
  131.                 if type(tune.seq[i]) == "number" then
  132.                     if #playedNotes <= 5 then
  133.                         noteblock.playNote(tune.voice, tune.seq[progList[i]])
  134.                     end
  135.                     table.insert(playedNotes, tune.seq[progList[i]])
  136.                 else for _,note in pairs(tune.seq[progList[i]]) do
  137.                     if #playedNotes <= 5 then
  138.                         noteblock.playNote(tune.voice,note)
  139.                     end
  140.                     table.insert(playedNotes, note)
  141.                 end end
  142.                 progList[i] = progList[i] + 1
  143.                 if progList[i] > #tuneList[i].seq then
  144.                     --Completed songs are marked as such
  145.                     delayList[i] = "complete"
  146.                 else
  147.                     --Incomplete songs have their delays updated
  148.                     delayList[i] = tuneList[i].int[progList[i]]
  149.                 end
  150.             end
  151.         end
  152.         --We then draw our keyboard (cos it's fun)
  153.         drawKeyboard(playedNotes)
  154.         --And then find the smallest next delay, subtracting this from all
  155.         --next delays.
  156.         local minint = delayList:min()
  157.         for i = 1,#delayList do
  158.             if type(delayList[i]) == "number" then
  159.                 delayList[i] = delayList[i] - minint
  160.                 --Correcting for precision errors, half a tick is enough
  161.                 if delayList[i] < 1/40 then delayList[i] = 0 end
  162.             end
  163.         end
  164.         --And capturing similar errors to stop less than tick updates...
  165.         if minint <= 1/20 then minint = 1/20 + 1/40 end
  166.         os.sleep(minint)
  167.     end
  168. end
  169.  
  170. --Constructs a chord, or three notes in a given scale of a pitch. The pitch
  171. --of the leading note is dropped by a perfect 3rd on the 1st inversion and
  172. --a 5th on the 2nd inversion (inversion is 0, 1 or 2). The scale will wrap
  173. --around the keyboard if there isn't enough space.
  174. function makeChord(pitch, scale, inversion, length)
  175.     if not length then length = 3 end
  176.     if not inversion then inversion = 0 end
  177.     local chordprog,noteprog = 1, 1
  178.    
  179.     --We firstly invert the chord, by going back the number of steps we
  180.     --need to to get to our starting pitch. As each scale step is different,
  181.     --we have to do this incrementally for each pitch progression in our chord
  182.     for i=1,inversion do
  183.         chordprog = chordprog - 1
  184.         if chordprog < 1 then chordprog = chordprog + 3 end
  185.        
  186.         for j=1,chordscale[chordprog] do
  187.             noteprog = noteprog - 1
  188.             if noteprog < 1 then noteprog = noteprog + 7 end
  189.             pitch = pitch - scales[scale][noteprog]
  190.             if pitch < 0 then pitch = pitch + 24 end
  191.         end
  192.     end
  193.    
  194.     local chord = { pitch }
  195.    
  196.     --We then increment the pitch by each chord step and add them to our tune
  197.     for i=1,length-1 do
  198.         for j=1,chordscale[chordprog] do
  199.             pitch = pitch + scales[scale][noteprog]
  200.             if pitch > 24 then pitch = pitch - 24 end
  201.             noteprog = noteprog + 1
  202.             if noteprog > 7 then noteprog = noteprog - 7 end
  203.         end
  204.         chordprog = chordprog + 1
  205.         if chordprog > 3 then chordprog = chordprog - 3 end
  206.            
  207.         table.insert(chord, pitch)
  208.     end
  209.    
  210.     --The final result is the notes form a complete melodic chord
  211.     return{ voice = voice, seq = { chord }, int = { 0 } }
  212. end
  213.  
  214. --Plays a simple arpeggio, or broken chord of user-defined length forward and
  215. --back. The arpeggio will wrap around the keyboard if there isn't enough space.
  216. function makeArpeggio(pitch,scale,length)
  217.     if not length then length = 4 end
  218.     local seq, int = { pitch }, { 0 }
  219.     local chordprog,noteprog = 1, 1
  220.    
  221.     --Identical to forming a chord, we start by going up our chord scale...
  222.     for i=1,length-1 do
  223.         for j=1,chordscale[chordprog] do
  224.             pitch = pitch + scales[scale][noteprog]
  225.             if pitch > 24 then pitch = pitch - 24 end
  226.             noteprog = noteprog + 1
  227.             if noteprog > 7 then noteprog = noteprog - 7 end
  228.         end
  229.         chordprog = chordprog + 1
  230.         if chordprog > 3 then chordprog = chordprog - 3 end
  231.            
  232.         table.insert(seq, pitch)
  233.         table.insert(int, 0.3)
  234.     end
  235.    
  236.     --...and then back down it again.
  237.     for i=1,length-1 do
  238.         chordprog = chordprog - 1
  239.         if chordprog < 1 then chordprog = chordprog + 3 end
  240.        
  241.         for j=1,chordscale[chordprog] do
  242.             noteprog = noteprog - 1
  243.             if noteprog < 1 then noteprog = noteprog + 7 end
  244.             pitch = pitch - scales[scale][noteprog]
  245.             if pitch < 0 then pitch = pitch + 24 end
  246.         end
  247.        
  248.         table.insert(seq, pitch)
  249.         table.insert(int, 0.3)
  250.     end
  251.    
  252.     return { voice = voice, seq = seq, int = int }
  253. end
  254.  
  255. --This plays a musical scale in a given key, either major or minor harmonic.
  256. --It plays the scale forwards and backwards, and wrap if it runs out of room.
  257. function makeScale(pitch,scale)
  258.     if not scales[scale] then return end
  259.     local seq,int = { pitch }, { 0 }
  260.  
  261.     --We simply increase our pitch according to the scale progression one
  262.     --step at a time to get each note in the scale
  263.     for i=1,#scales[scale] do
  264.         pitch = pitch + scales[scale][i]
  265.         if pitch < 0 then pitch = pitch + 24 end
  266.         table.insert(seq, pitch)
  267.         table.insert(int, 0.3)
  268.     end
  269.  
  270.     --And then decrease to go back down it again.
  271.     for i=#scales[scale], 1, -1 do
  272.         pitch = pitch - scales[scale][i]
  273.         if pitch > 24 then pitch = pitch - 24 end
  274.         table.insert(seq, pitch)
  275.         table.insert(int, 0.3)
  276.     end
  277.  
  278.     return { voice = voice, seq = seq, int = int }
  279. end
  280.  
  281. --This function plays a chromatic scale, that is every key on the keyboard
  282. --very quickly. It is the simplest scale to program.
  283. function makeChromatic()
  284.     local seq, int = { }, { }
  285.     --Start at the bottom, and play each note until we reach the top.
  286.     for pitch=0,24 do
  287.         table.insert(seq, pitch)
  288.         table.insert(int, 0.1)
  289.     end
  290.    
  291.     return { voice = voice, seq = seq, int = int }
  292. end
  293.  
  294. --This just makes a simple drum beat with a delay for the timer.
  295. function makeSimpleBeat(pitch, delay)
  296.     local hpitch = pitch + 12
  297.     seq = { pitch, hpitch, pitch, pitch, hpitch }
  298.     int = { delay, delay, delay, delay/2, delay/2 }
  299.     return { voice = voice, seq = seq, int = int }
  300. end
  301.  
  302. --This constructs a tune made from a purely random selection of notes
  303. --and plays them back at a fixed interval. Works surprisingly great with
  304. --the snare drums
  305. function makePureRandom(count,delay)
  306.     local seq, int = { }, { }
  307.    
  308.     for i=1,count do
  309.         table.insert(seq, math.random(0,24))
  310.         table.insert(int, delay)
  311.     end
  312.    
  313.     return { voice = voice, seq = seq, int = int }
  314. end
  315.  
  316. --This constructs a slightly more structured but still totally random tune.
  317. --It only plays notes on the scale of the key specified and works by randomly
  318. --incrementing or decrementing the pitch by that scale, with larger 'jumps'
  319. --being exponentially less likely to occur. It produces something more
  320. --consistent; perhaps a track I'd play to represent losing one's mind.
  321. function makeScaledRandom(pitch,scale,count,delay)
  322.     if not delay then delay = 0.2 end
  323.     local seq, int = { pitch }, { delay }
  324.    
  325.     local prog = 1
  326.     for i=1,count do
  327.         --This makes each additional jump exponentially less likely
  328.         local jump = math.ceil(math.log(16/math.random(1,16))/math.log(2))
  329.         if 1 == math.random(0,1) then
  330.             for j=1,jump do
  331.                 prog = prog - 1
  332.                 if prog < 1 then prog = prog + 7 end
  333.                 pitch = pitch - scales[scale][prog]
  334.             end
  335.         else
  336.             for j=1,jump do
  337.                 pitch = pitch + scales[scale][prog]
  338.                 prog = prog + 1
  339.                 if prog > 7 then prog = prog - 7 end
  340.             end
  341.         end
  342.    
  343.         if pitch < 0 then pitch = pitch + 24 end
  344.         if pitch > 24 then pitch = pitch - 24 end
  345.         table.insert(seq, pitch)
  346.         table.insert(int, delay)
  347.     end
  348.    
  349.     return { voice = voice, seq = seq, int = int }
  350. end
  351.  
  352. voice = 1
  353. drums = makeSimpleBeat(6,0.9)
  354. voice = 2
  355. percussion = makePureRandom(50,0.15)
  356. voice = 4
  357. bass = makeScaledRandom(6,"major",25,0.3)
  358. voice = 0
  359. melody = makeScaledRandom(12,"major",50,0.15)
  360.  
  361. playTune(makeChromatic())
  362. sleep(0.5)
  363. playTune(makeArpeggio(0,"major",7))
  364. sleep(0.5)
  365. playTune( makeScale(12, "minor") )
  366. sleep(0.5)
  367. playTuneList( { makeScale( 0, "major" ), makeScale (12, "major") } )
  368. sleep(0.5)
  369.  
  370. playTune(drums)
  371. sleep(0.5)
  372. playTune(percussion)
  373. sleep(0.5)
  374. playTune(bass)
  375. sleep(0.5)
  376. playTune(melody)
  377. sleep(0.5)
  378.  
  379. --The horrible noise machine!!!!!
  380. playTuneList( {melody, percussion, drums, bass} )
Advertisement
Add Comment
Please, Sign In to add comment