The Note of Destiny - place a note on a staff The zombie apocalypse has come, and the world is coming to an end. Suddenly, someone discovers a formula that takes the current hour, minute, and day, and spits out the perfect note to play on a piano that instantly kills every zombie that hears it. Unfortunately, there is only one piano player left in the world, and he has forgotten how to read notes, but he still knows how to read sheet music. Of course, this is a very time-sensitive thing, so it seems natural to have a computer do it.1

Your challenge is to take a note, such as G, and output the note placed on a staff (in treble clef), like this:

-----

-----
   |
---|-
   |
--O--

-----


Specification:


You must output a staff of alternating lines of ----- (5 dashes) and a blank line. There will be 5 -----s total. The note must be superimposed on top of this staff.
The input will specify where the note is located. The input will be:

an optional H or L, specifying "high" or "low"
a letter from A to G, specifying the pitch
an optional # or b, specifying sharp or flat.

The "note" is defined as:

One O (capital O) aligned to the middle of the staff, which is in the place of the note. (The top line is HF (high F), and the bottom line is E (a normal E).)
Three |s (vertical bars), the stem, which will be:

one space to the left of the note and going downwards (starting one space below the note) if the note is on the middle line (B) or above, or
one space to the right of the note and going upwards (starting one space above the note) if the note is below the middle line.

A # or b one space directly to the left of the note if specified in the input.

Ledger lines must be added if the note is too high or low. These lines will be --- (only 3 dashes in width, as opposed to 5) and will only appear if the note is on or above/below (for top/bottom ledger lines respectively) the ledger lines.
Extraneous spaces may be placed anywhere you want; for example, you could make the blank lines have spaces or have a space after the ledger lines if it helps you save any characters.


Here is a visualization, to understand the specification more easily, with all the note names next to the lines:

      HB
 ---  HA
      HG
----- HF
      HE
----- HD
      HC
----- B
      A
----- G
      F
----- E
      D
 ---  C
      LB
 ---  LA
      LG
 ---  LF
... (bottom cut off for brevity, you get the idea anyway)


Here are some more examples that you can use to test your program:

Input: HG#

 #O
-|---
 |
-|---

-----

-----

-----


Input: LAb

-----

-----

-----

-----

-----
   |
 --|
   |
 bO-


Input: HB

  O
 |--
 |
-|---

-----

-----

-----

-----


Input: C

-----

-----

-----

-----
   |
---|-
   |
 -O-


This is code-golf, so the shortest code in bytes will win!

1: most realistic exposition evar! :-P
Ruby – 271 267 252 249 234 229 220 214 characters

I literally just learned Ruby for this. So there is certainly room for improvement in golfing it down. Or doing anything really. But I needed a language with mutable strings. :)



def f(n)s=[0]*20
s.fill{|i|i%2&gt;0?i&lt;3||i&gt;11?" ---":?-*5:" "*5}
s[l=(3-n[(p="H_L".index n[0])?1:0].ord)%7+7*(p||1)][1,2]=("#b"[n[-1]]||s[l][1])+?O
s[l+3-2*o=l&gt;7?3:1,3].map{|t|t[o]=?|}
puts s[[3,l].min..[11,l].max]end


Somewhat ungolfed:

def f(note)
  staff=[]
  0.step(20) {|i| staff[i] = " "*5}
  1.step(19,2) {|i| staff[i] = " ---"}
  3.step(11,2) {|i| staff[i] = "-"*5}
  level = 7
  if !(pos="HL".index note[i=0]).nil?
    level = 14*pos
    i += 1
  end
  level += (73-note[i].ord)%7
  staff[level][2] = "O"
  mark = note[-1]
  if !"#b".index(mark).nil?
    staff[level][1] = mark
  end
  offset = (level &gt; 7) ? 3 : 1
  staff[level-2*offset+3,3].map {|line| line[offset] = "|"}
  first = [3,level].min
  last = [11,level].max
  puts s[first..last]
end


I can cut it by another 2 characters down to 212 characters if leading blank lines are allowed. This solution doesn't fill the lines that aren't printed anyway:

def f(n)s=[]
[3,l=(3-n[(p="H_L".index n[0])?1:0].ord)%7+7*(p||1)].min.step(l&gt;11?l:11){|i|s[i]=i%2&gt;0?i&lt;3||i&gt;11?" ---":?-*5:" "*5}
s[l][1,2]=("#b"[n[-1]]||s[l][1])+?O
s[l+3-2*o=l&gt;7?3:1,3].map{|t|t[o]=?|}
puts s
end


Are lambda's fair game? Then I can get 210 characters with the first approach

f=-&gt;n{s=[0]*20
s.fill{|i|i%2&gt;0?i&lt;3||i&gt;11?" ---":?-*5:" "*5}
s[l=(3-n[(p="H_L".index n[0])?1:0].ord)%7+7*(p||1)][1,2]=("#b"[n[-1]]||s[l][1])+?O
s[l+3-2*o=l&gt;7?3:1,3].map{|t|t[o]=?|}
puts s[[3,l].min..[11,l].max]}


Or 207 characters with additional blank lines:

f=-&gt;n{s=[]
[3,l=(3-n[(p="H_L".index n[0])?1:0].ord)%7+7*(p||1)].min.step(l&gt;11?l:11){|i|s[i]=i%2&gt;0?i&lt;3||i&gt;11?" ---":?-*5:" "*5}
s[l][1,2]=("#b"[n[-1]]||s[l][1])+?O
s[l+3-2*o=l&gt;7?3:1,3].map{|t|t[o]=?|}
puts s}


Of course, now you'd need to do f.call("HGb").