Code Golf: Piano
Language AgnosticCode GolfRosetta StoneLanguage Agnostic Problem Overview
The challenge
The shortest code by character count to output a part of a piano keyboard starting from input note in a given length.
Input will be composed of a note ([ACDFG]#|[A-G]
) to start printing the keyboard from and a positive number representing length of keys to print including the first note.
The first key should be printed in full - if it has a left sharp key, it will be cut, same for when the start key is sharp, the left key will be cut as well.
A Sharp key is not counted, only white keys.
Test cases
Input
C 14
Output
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
Input
D# 1
Output
### |
### |
### |
### |
### |
| |
| |
| |
_|____|
Input
A 7
Output
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
| | | | | | | |
| | | | | | | |
| | | | | | | |
|____|____|____|____|____|____|____|
Code count includes input/output (i.e full program).
Language Agnostic Solutions
Solution 1 - Language Agnostic
Perl, 133 (129) chars
Perl, 167 160 156 147 142 133 D'oh! 147 144 137 134 133 chars
$=shift;die grep{5>$n?y//#/:y/#/ |/;$n++-8||y/ //} @Q=(substr(("# #".("# ## | #")x2)x9, 4/#/+(7&ord)5,1+/#/+5pop).$/)x9
Here's a 129 char solution, borrowing several ideas from molf's Ruby solution:
($,$c)=@ARGV;$j=($k=/#/)+ord; for$n(0..8){print$/,substr+(join'',map{8-$n?5>$n&&$%7%4?"### " :" | ":"|__"}$j..$j+$c),!$k,-3}
and with help from the Acme::AsciiArtinator:
sub init_piano{$S=$";$H='#';
( $
T ,$P,$U)=qw(
T |
_ |
/ )
; $
d =$T.$H.$S.$S;$t =
$ d . $H
. $ d . $
S .$P.$S.$S.$S.$H;$ t= $
d.$H.$t.$t;};$_=shift;-/-/;&init_piano();$Z=/#/;-/| |
| | |
| YAMAHA | /
;die grep{4>$n++?y/T/#/:y/#T/ |/;$n-8||y/ /_/;}@q=(m{
// // /// // /// // /// // /// // /// // /// // /// /
// // /// // /// // /// // /// // /// // /// // /// /
} .
substr(${t}x(9),4*${Z}+(7&ord)*5,1+$Z+5*pop).$/)x(8)
; ' \ /
| | | |
/_\ /';
Solution 2 - Language Agnostic
Ruby - 125 chars
146 144 140 137 134 126 125 chars
a,n=$*;h=a[1]?0:1;s=a[0]-h+1
9.times{|t|puts (s..s+n.to_i).map{|i|i%7%4<1||t>4?" | ":"### "
}.join[h..-4].tr t>7?" ":n,"_"}
(The second newline is not necessary and added only to avoid a scrollbar on SO. Semi-colons can be replaced by newlines if desired.)
The Ruby 1.9 version is different but of equal length (replacing a[0]
by a.ord
and "_"
by ?_
):
a,n=$*;h=a[1]?0:1;s=a.ord-h+1
9.times{|t|puts (s..s+n.to_i).map{|i|i%7%4<1||t>4?" | ":"### "
}.join[h..-4].tr t>7?" ":n,?_}
Call with
$ ruby piano.rb C 14
Solution 3 - Language Agnostic
288 291 310 315 330 333 340 350 characters
LilyPond, 285 In keeping with the music theme, here's a solution in a language designed for typesetting music scores, LilyPond:
x=#ly:string-substitute
u=#(x"*""### ""| *** | ** ")t=#(x"###"" | "u)q=#read-char
z=#(q)v=#(if(eq?(q)#\#)1 0)y=#(iota(+(*(read)5)1 v))#(format #t"~{~{~a~}
~}"(map(lambda(s)(map(lambda(i)(string-ref s(modulo(+(*(char->integer z)5)i(* v
4))35)))y))(list u u u u u t t t(x" ""_"t))))
Usage: $ lilypond thisfile.ly <input.in >output.out 2>/dev/null
Solution 4 - Language Agnostic
GolfScript - 80 Characters
Fits in one line of #SO without the scroll bar :)
' ':s/~~5*\(7&5*\,.4*@+):k;+):c;9,{5<'#'9**' | '4*+3/4<.1>+c*s.+*k>c<n+}%)s/'_'*
GolfScript - 81 Characters
' ': /((7&\,.4*@5*+:k;\~~5*+):c;9,{5<'#'9**' | '4*+3/4<.1>+c* +*k)>c<n+}%) /'_'*
Nicely formatted version (27 x 3)
' ': /((7&\,.4*@5*+:k;\~~5*
+):c;9,{5<'#'9**' | '4*+3/4
<.1>+9* +*k)>c<n+}%) /'_'*
GolfScript - 82 Characters
' '/((7&\,.4*@5*+:k;\~~5*+):c;9,{5<3*'### '*' | '4*+20<.5>+c*k)>c<n+}%)' '/'_'*
GolfScript - 85 Characters
' '/((7&\,.4*@5*+:k;\~~5*+):c;9,{.5<3*'### '*' _'1/@8=='|'1$3*++4*+20<.5>+c*k)>c<n}%
Nicely formatted version (17 x 5)
' '/((7&\,.4*@5*+
:k;\~~5*+):c;9,{.
5<3*'### '*' _'1
/@8=='|'1$3*++4*+
20<.5>+c*k)>c<n}%
GolfScript - 94 Characters
' ': /~~5*:c;(7&5*:^;,:&;['### '3*' | '+.5>+c*1>{^4&*+>&c+)<n}:f~]5*' _'1/{'|'\4*+7*c*f}%
GolfScript - 98 Characters
' ': /~~5*:c;(7&5*:^;,:&;['### '3*' | '+.5>+c*^4&*+:^)>&c+):&<n]5*[ '_']{['|'\4*+7*c*^>&<n]}%
GolfScript - 101 Characters
' ': /~~5*:c;(7&5*:^;,:&;['### '3*' | '+.5>+c*^4&*+)>&c+)<n]5*[ '_']{['|'\4*+7*c*^4&*+>&c+)<n]}%
GolfScript - 109 Characters
' ': /~~5*:c;(7&5*:^;,:&;['##'' ###'.' | '++.' #'+++c*^4&*+>&c+)<n]5*[ '_']{['|'\4*+7*c*^4&*+>&c+)<n]}%
GolfScript - 120 Characters
' '/~~5*:c;(7&5*:^;,:&;['##'' ###'.' | '++.' #'+++c*^4&*+>&c+)<n]5*['| '7*c*^4&*+>&c+)<n]3*'|____'7*c*^4&*+>&c+)<
GolfScript - 127 Characters
' '/~~5*:c;(7&5*:^;,:&;['## ### ### | ### ### | #'c*^4&*+>&c+)<n]5*['| '7*c*^4&*+>&c+)<n]3*'|____'7*c*^4&*+>&c+)<
$ echo -n C 14 | ruby golfscript.rb piano.gs
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
$ echo -n D# 1| ruby golfscript.rb piano.gs
### |
### |
### |
### |
### |
| |
| |
| |
_|____|
$ echo -n A 7| ruby golfscript.rb piano.gs
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
| | | | | | | |
| | | | | | | |
| | | | | | | |
|____|____|____|____|____|____|____|
Solution 5 - Language Agnostic
239 245 249 257 245 267 285
RetroGolf - Applesoft BASIC: 236 Shown in multiple lines for readability, but should be a single line:
1K$="## | ### #":K$="## #"+K$+K$:
FORI=1TO21:F$=F$+"|____":L$=L$+"| ":NEXT:
INPUTN$:S=MID$(N$,2,1)="#":O=(ASC(N$)-65)*5+1+S*4:L=VAL(RIGHT$(N$,2))*5+1+S:
FORI=1TO5:?MID$(K$+K$+K$,O,L):NEXT:FORI=1TO3:?MID$(L$,O,L):NEXT:?MID$(F$,O,L)
Can be tested with this Applesoft BASIC Interpreter in Javascript or an emulator.
Solution 6 - Language Agnostic
C# - 315
I persist to golf in C# although it's not a very terse language...
using C=System.Console;class P{static void L(int o,int c,string s){while(c-->0)C.Write(s[(420>>o++%5*2&3)+(91>>(o+2)/5%7&1)*3]);C.WriteLine();}static void Main(string[]a){int i=0,s=a[0].Length-1,c=int.Parse(a[1])*5+1+s,o=(a[0][0]-65+s)*5-s;while(i++<5)L(o,c,"| ## ");while(i++<8)L(o,c,"| | ");L(o,c,"|__|__");}}
Solution 7 - Language Agnostic
Python - 164
k,n=raw_input().split()
m=k[1:]>""
n=int(n)*5+1
o=(ord(k[0])-65)*5+4*m
for x in["## ### | ### "]*5+[n*"| "]*3+[n*"|____"]:print((x+x[::-1][:-1])*n)[o:o+n+m]
Solution 8 - Language Agnostic
154 155 158 159 162 172 180 186 185 188 197 199 200 206 207 209 212 214 215 219 240 244 268 characters
Octave, 153 Why use just C or C# or F# (or B or D) when you can program with a full Octave?
(wrapped every 60 chars for clarity)
x=5*scanf("%2c%d");for k=-8:0disp((s={[t="| ### ### ","#
## ",t" "]"| ","|____"}{(k>-4)+!k+1})(1+mod(5*(y=x(2)>1
60)+(-y:x(3))+x(1),rows(s'))))end
Yes ... this solution really does compute the complex conjugate transpose of a string.
Usage: $ octave -q thisfile.m <input.in >output.out
Solution 9 - Language Agnostic
203 207 216 224 232 240 characters
C — 197 #define S"# | ### ### ##"
main(i,j,l,h,t){char*X[]={"____|"," |",S S,S S},s[i=11];for(scanf("%s%n%d",s,&h,&l);--i>1;puts(""))for(j=t=*s%7*5+h*4;j<t+l*5+h;putchar(X[i/3][j++%(i>5?35:5)]));}
This equivalent 194-character version assumes buffer overflow is fine.
#define S"# | ### ### ##"
i=11;main(j,l,h,t){char*X[]={"____|"," |",S S,S S},s;for(scanf("%s%n%d",&s,&h,&l);--i>1;puts(""))for(j=t=s%7*5+h*4;j<t+l*5+h;putchar(X[i/3][j++%(i>5?35:5)]));}
Solution 10 - Language Agnostic
245 293 312 (regular); 219 224 225 231 (ASCII85)
PostScript: 239 /r{(%stdin)(r)file token pop}def[(]){mul add}/z r(:-)cvs dup length 1
sub/v exch/p r/s(| ### ### ### | ### ### )([){0 1 v p 5]{s
exch z 0 get 5]v 4]s length mod 1 getinterval print}for/
=}>>begin[[[[[/s(| )def[[[/s(|____)def[
Binary version expanded through ASCII85 encoding into a 219-character program with only ASCII printable characters:
/(|____)/(| )[/r/(| ### ### ### | ### ### )<~Ou%G5-$+0=Bl5@JE[d/;P,jagI?HCK@<*JGP,4<rOuSV60p8LhG*5%O8oc=a.=3b)!HsVu23Md=!IHJ_A<K->@5*j;23Md=!HoSBP&-9^09Tk/@ZkI\P"_$^I?I,S+?b-:5*?@Z>?b<9Ou$<H>EUc~>cvx
exec[
Usage: $ gs -q -dNOPROMPT -dNODISPLAY -dBATCH thisfile.ps <input.in >output.out
Solution 11 - Language Agnostic
225, 226, 248, 252, 270, 276, 299, 306 Chars
F#: 224 let x,(/)=System.Console.ReadLine(),String.replicate
let t,p=(int x.[0]-60)%7*5,int x.[1]%2
let g s i=printf"%s"(i/((99/s).[t+4*p..t+int x.[2..]*5+5*p]+"\n"))
g"| ### ### | ### ### ### "5
g"| "3
g"|____"1
I used modules of 2 to detect for a space or pound. ' ' is 32 % 2 = 0 '#' is 35 % 2 = 1 and since my conditional returned zeros for false I just multiplied the modules result.
Used the <| operator to shave off one space char. Used operator overloading to save another char.
original
let piano_long (input:string) =
let sharp, flat = if input.[1] = '#' then 4, 1 else 0, 0
let note = (int input.[0] - 60) % 7
let num = int (input.Substring 2)
let start = note * 5 + sharp
let stop = num * 5 + 1 + flat
let top = "| ### ### | ### ### ### | ### ### | ### ### ### |"
let middle = "| | | | | | | | | | | | | | |"
let bottom = "|____|____|____|____|____|____|____|____|____|____|____|____|____|____|"
let mutable piano = ""
for i in 1..5 do
piano <- piano + top.Substring(start, stop) + "\n"
for i in 1..3 do
piano <- piano + middle.Substring(start, stop) + "\n"
piano <- piano + bottom.Substring(start, stop)
printf "%s\n\n" piano
Solution 12 - Language Agnostic
235 234 235 237 238 244 268 269 270 276 279 280 282 287 300 307 314 329 338 characters
sed, 231 Works for up to 99 keys. The standard piano has 52 white keys, so this should be sufficient.
s/.*/CDEFGABC&=0123456789-/
s/(.).=(.*)\1.*/&\2\2\2\2\2\2\2\2\2\2/
s/ .?(.)=(.*)\1.*-/\2/
s/.*#/%&/
:
s/((.)(.).*\2)[#-9]/\1 \3/
t
s/[^ %CF]/###/g
s/C|F/ | /g
s/(%....)?.{25}(.*)./\2/p
p
p
p
p
s/## /| /g
s/#[|#]/ |/g
p
p
p
y/ /_/
Examples:
$ echo C 14 | sed -rf piano.sed
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| ### ### | ### ### ### | ### ### | ### ### ### |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | |
|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
$ echo D# 1 | sed -rf piano.sed
### |
### |
### |
### |
### |
| |
| |
| |
_|____|
$ echo A 7 | sed -rf piano.sed
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
## ### | ### ### | ### ##
| | | | | | | |
| | | | | | | |
| | | | | | | |
|____|____|____|____|____|____|____|
$ echo A 52 | sed -rf piano.sed
## ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ##
## ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ##
## ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ##
## ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ##
## ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ### ### | ### ### ### | ##
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
The last example prints the standard keyboard, along with imaginary black keys on either end.
Solution 13 - Language Agnostic
JavaScript - 195 chars
Hey, golf is a game where you only compete against yourself right? :)
k=readFile(0).split(' ')
q=!k[0][1]
r=k[1]
o=''
for(x=10;x--;){p=k[0].charCodeAt(0)-65+!q
s=''
for(j=+r+1;j--;){p=++p%7
s+=x>4&&!p|p%3?'### ':x?' | ':'_|___'}o+=s.substring(q,r*5+2)+'\n'}print(o)
Solution by gnarf; ported to Rhino (with a minor fix and formatting changes) by KirarinSnow; further chipped away by gnarf; error corrected by KirarinSnow. cache k[1]
by cwallenpoole
Usage: $ cp input.in 0; rhino thisfile.js
Quick HTML Demo Version:
Golf Test -
Adds readFile=prompt;print=function(a) {document.write("<pre>"+a);}
Solution 14 - Language Agnostic
PianoScript - 2 characters
It's a one-liner:
go
Usage:
PianoScript piano.ps G# 11
Output:
### ### | ### ### | ### ### ### | ### ##
### ### | ### ### | ### ### ### | ### ##
### ### | ### ### | ### ### ### | ### ##
### ### | ### ### | ### ### ### | ### ##
### ### | ### ### | ### ### ### | ### ##
| | | | | | | | | | | |
| | | | | | | | | | | |
_|____|____|____|____|____|____|____|____|____|____|____|
More information on the PianoScript language can be found here.
Solution 15 - Language Agnostic
Python3 - 158
Save on input
vs raw_input
. Lose on ()
for print
k,n=input().split()
o=(ord(k[0])-65)*5
n=int(n)*5+1
for x in["## ### | ### "]*5+[n*"| "]*3+[n*"|____"]:print(((x+x[::-1][:-1])*n)[o+3*len(k[1:]):o+n])
Solution 16 - Language Agnostic
F#: 355 significant chars
All on one line:
let[|x;y|]=System.Console.ReadLine().Split([|' '|])in[for i in 1..9->let r (a:string) b j (s:string)=s.Replace(a,if i>j then b else a)in((String.replicate(int y+1)"23012123012121").Substring(int(x.[0])-65,int y*2+x.Length).Replace("0","| ")|>r"1""#"0|>r"2""## "0|>r"3"" "0).TrimEnd()|>r"###"" | "5|>r"##""| "5|>r" ""_"8]|>String.concat"\n"|>printfn "%s"
Expanded:
let piano() =
let[|x;y|]=System.Console.ReadLine().Split([|' '|])in
[for i in 1..9->
let r (a:string) b j (s:string) = s.Replace(a,if i>j then b else a) in
((String.replicate (int y+1) "23012123012121")
.Substring(int(x.[0])-65,int y*2+x.Length).Replace("0","| ")
|> r "1" "#" 0
|> r "2" "## " 0
|> r "3" " " 0)
.TrimEnd()|> r "###" " | " 5|> r "##" "| " 5|> r " " "_" 8]
|> String.concat "\n"
|> printfn "%s"
Solution 17 - Language Agnostic
SETL
165 characters; Translation of gribblers Python solution.
get(l);[k,n]:=split(l);o:=(abs k(1)-65)*5;n:=1+5*val n;(for x in['## ### | ### ']*5+[n*'| ']*3+[n*'|____'])print(((x+reverse x(2..))*n)(o+4*#k-3..o+n));end;
Solution 18 - Language Agnostic
370 400 + 17 characters
D2 (templates): 331 (based on the Ruby solution.)
Compressed:
template J(alias T,int b,int e,r...){static if(e)enum J=T!(b,r)~J!(T,b+1,e-1,r);else enum J="";}template K(int i,int t){enum K=t>7?"_|___":t<5&&3&i%7?"### ":" | ";}template R(int t,int s,int l,int h){enum R=J!(K,s-h,l,t)[h..$-3]~"\n";}template M(alias k){enum M=J!(R,0,9,k[0]+1,k[$-2]>32?k[$-1]+10*k[$-2]-527:k[$-1]-47,k[0]&1);}
Explained:
/**
Macros:
D = <tt>$0</tt>
*/
;
/**
$(D_PSYMBOL J) (short for "join") will evaluate $(D T!(i,r)) for
$(D_PARAM i) in $(D [b..b+e]). Then, these compile-time strings will be
concatenated.
*/
template J(alias T,int b,int e,r...){
static if(e)
enum J=T!(b,r)~J!(T,b+1,e-1,r);
else
enum J="";
}
/**
$(D_PSYMBOL K) (short for "key") will generate 5 characters as a row of
key $(D_PARAM i) at row $(D_PARAM t).
*/
template K(int i,int t){
enum K=t>7?"_|___":t<5&&3&i%7?"### ":" | ";
}
/**
$(D_PSYMBOL R) (short for "row") will generate the keyboard at row
$(D_PARAM t), from key $(D_PARAM s) and sharpness $(D_PARAM h) with a
length of $(D_PARAM l) keys.
*/
template R(int t,int s,int l,int h){
enum R=J!(K,s-h,l,t)[h..$-3]~"\n";
}
/**
$(D_PSYMBOL M) (short for "main") results in the whole keyboard as a string.
Example:
-----
pragma(msg,M!("C 14"));
pragma(msg,M!("D# 1"));
pragma(msg,M!("A 7"));
-----
*/
template M(alias k){
enum M=J!(R,0,9,k[0]+1,k[$-2]>32?k[$-1]+10*k[$-2]-527:k[$-1]-47,k[0]&1);
}
Since we can't pass parameters from dmd
the input must be done in the code. Supports only up to 99 keys.
Solution 19 - Language Agnostic
Haskell: 212 211 208 characters
a=" | "
b=" ### "
d=concat.cycle
e=d[b,b,a,b,b,a,b]
f=d[a]
t x s m n=map(take(5*read s+m).drop(5*length['@'..x]-n))[e,e,e,e,e,f,f,f,d["__|__"]]
u(x:'#':s)=t x s 2 4
u(x:s)=t x s 1 8
main=interact$unlines.u
It still assumes ascii-compatible letters (specifically, the sequence "@ABCDEFG"), but no longer requires Char.ord
Solution 20 - Language Agnostic
Ruby - 113 chars
Runs with command line arguments
$ ruby piano.rb A 7
k,c=$*
9.times{|x|puts (((b=x<8?' | ':'__|__')+(a=x<5?' ### ':b)*3+b+a*2)*j=k[0]*5-2+4*s=k.size)[j,c.to_i*5+s]}
Ruby - 118 chars
k,c=$*
9.times{|x|puts (((b=x<8?' | ':'__|__')+(a=x<5?' ### ':b)*3+b+a*2)*j=2+k[0]*5+4*s=k.size-1)[j..c.to_i*5+s+j]}
Solution 21 - Language Agnostic
PHP - 208 chars
<?$e=45*substr($a=PIANO,2+$d=!($a[1]^~ì))+9+$d*45;$j=9*$c=4*$d;for($b=ord($a[0])-65,--$c;$j<$e;$f[$i=$j++%9].=($c=($c+!$i)%5)%4<2&$i>3&$b%3!=2?Ü:($c?$i?ß: :))$j%45-36?:$b=++$b%7;for(;$a=$f[$i--];)echo~$a,~õ;
Shall have to be improved.
The input has to be delivered in the constant named PIANO.
Solution 22 - Language Agnostic
414 386 372 significant characters:
F# //wins me 2 characters
open System
//String.replicate, didn't know this existed before reading Juliet
let r=String.replicate
//print s n times, each time on a newline
let P n s=printf"%s"(r n (s+"\n"))
//define top rows
let t="## ### | ### ### | ### #"
//middle and bottom rows can be defined using 'r'
let m,b=r 7"| ",r 7"|____"
//pick of chars from O to n+O from string, wrap round if we go beyond s.Length
let L(s:string)O n=String([|5*O..5*(n+O)|]|>Array.map(fun i->s.[i%35]))
//match input string into two halves
let[|k;n|]=Console.ReadLine().Split([|' '|])
//work out start pos and length (in chars, not keys)
let O,N=
let K=int k.[0]-65 //'A'=65, this is why t starts at A
if k.[0]='#'then(K+3,int n+2)else(K,int n)
//Print 5 top rows, 3 middle rows and the bottom row
P 5(L t O N)
P 3(L m O N)
P 1(L b O N)
Oh, and one bonus, this script will actually handle "F# 372" correctly - I won't annoy you by pasting it here though...
System.Console.ReadLine() is such a bummer...