Create a GUI Piano [closed]

Challenge

Create a GUI Keyboard with as few characters as possible.

Example

Because this was an assignment in one of my courses, I can't show the source code. However, here is a screen shot of my keyboard.

In this example, my keys were of type JButton and I used a Midi Synthesizer to produce the sound (with the default ADSR envelope values).

Rules

• You are allowed to use standard external libraries.
• Be creative with your sound. You can use 8-bit, a sitar, etc.
• For simplicities sake, you may have five keys; black and white, from C to E (the first five keys on my keyboard).
• Most importantly... showcase your work!

NOTICE: Depending on what language you choose to work with, this may be a rather large task.

This is my first question on SE Code Golf. If anything is unclear, please ask for further details.

EDIT: The due date for this challenge will be 9/22/12. If you post an answer after this date, I will look over it regardless (and possibly +1 it).

• Restrictions on which language to use are not liked very much here. Consider dropping your restriction or name an important reason. – FUZxxl Sep 15 '12 at 18:08
• @FUZxxl As stated under the Example section, this was a term project for our Java class. It is still being used as a term project for that very class. But I suppose I'm just paranoid so I'll drop the restrictions. I think you meant which languages not to use... but whatever, I removed them. – Rob Sep 15 '12 at 19:02
• What are the minimal requirements to be considered a "GUI keyboard"? I infer from what's already present that it must display a GUI and produce some sound, but what restrictions are there on: a) the input mechanism; b) the sound envelope; c) the scale used; d) the accuracy of the tuning; e) the proportions of the keys? – Peter Taylor Sep 15 '12 at 22:02
• @MikeDtrick, that answers 0/5 of my questions. I'm not asking how your implementation worked: I'm asking how I can know whether my (hypothetical) implementation is a valid competitor, because there's no point shortening an entry by 20% if so doing takes it from being a valid entry to an invalid one. – Peter Taylor Sep 16 '12 at 8:03
• @MikeDtrick: For example, you could require that the buttons look exactly like the ones in your example, pixel by pixel. At the other extreme you could allow any arrangement of five GUI buttons of any type. – han Sep 16 '12 at 10:25

Mathematica 319 259 255

Edit: Keys now depress (as buttons) when clicked.

This will play the grand piano notes {"C","C#","D","D#","E"}, where "C" is middle C. z[n_] plays the note.

z@n_ := EmitSound@Sound[SoundNote[n, .3, 1]]; w = {10, 300}; b = {35, 180};
Graphics[Inset[Button["", z[#[[1]]], Background -> If[#[[2]] == w, None, Black],
ImageSize -> #〚2〛], #〚3〛] & /@ {{"C", w, {-.4, 0}}, {"D", w, {0, 0}}, {"E", w, {.4, 0}},
{"C#", b, {-.2, 0.31}}, {"D#", b, {.2, 0.31}}}, PlotRange -> 1]


The keyboard can be extended to 18-keys by using fewer than double the characters:

z@n_ := EmitSound@Sound@SoundNote[n, .3, 1];
w = {"C", "D", "E", "F", "G", "A", "B", "C5", "D5", "E5", "F5"};
b = {"C#", "D#", "", "F#", "G#", "A#", "", "C#5", "D#5"}; i = ImageSize; t = Thread;
l = List; s = Inset; m = Table; u = Button;
Graphics[Join[t[s[u @@@ t[l["", y /@ w, i -> {5, 350}]] /. y -> z, m[{90 k, 0}, {k, -5, 5}]]],
Delete[t[s[u @@@ t[l["", y /@ b, Background -> Black, i -> {28, 212}]] /.
y -> z, m[{90 k + 45, 220}, {k, -5, 3}]]], {{3}, {7}}]],
AspectRatio -> .45, PlotRange -> {{-500, 500}, {-610, 610}}, i -> {800, 430}]


• +1 I have no doubt in my mind that this will work... I just wish I could play on it. – Rob Sep 16 '12 at 0:08
• I left a .cdf version of the file in my DropBox at dropbox.com/sh/m3y0fs0v0nidqt5/UTv_0YGpz5 You can share this with others. There should be no licensing problems because it is being used for non-commercial, educational purposes. You will need to download the free Wolfram CDF player if you don't already have it. – DavidC Sep 16 '12 at 0:35
• David, I need w = {67, 300} to get your result; any idea why the difference? Also, may I edit this code to shorten it, if I am able? – Mr.Wizard Apr 6 '13 at 14:49
• Mr.Wizard. w = {67,300} works fine on v. 9 so if you want to change it, or for that matter, shorten any of the code, go right ahead. Adjusting the button size was hit or miss. Strange things happened for reasons I cannot explain. (For instance, adding more buttons affects the proportions of the original buttons.) – DavidC Apr 6 '13 at 16:14

Web page (840/796 characters)

>>> Start playing (Internet Explorer is not supported for multiple reasons; Google Chrome and Opera work best.)

I probably could make this a bit shorter, yet it is a good start. The lower score is after replacing all occurrences of &nbsp; with the character itself and removing the keyword new, the latter change breaking Google Chrome compatibility.

<style>table{border-collapse:collapse;border-width:1 0;border-style:solid;font-size:64;line-height:2}td{border-style:solid;border-width:0 1}</style><table><td colspan=3 title=0>&nbsp;<td bgcolor=black colspan=2 title=1>&nbsp;<td colspan=2 title=2>&nbsp;<td bgcolor=black colspan=2 title=3>&nbsp;<td colspan=3 title=4>&nbsp;<tr><td colspan=4 title=0>&nbsp;<td colspan=4 title=2>&nbsp;<td colspan=4 title=4>&nbsp;</table><script>for(A=[y=5];y--;){for(s=x=64e3;x--;)s+="~ "[x*(268+17*y)>>13&1];A[y]=new Audio("data:audio/wav;base64,UklGRiXuAgBXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQHuAgCA"+btoa(s))}setInterval("for(y=5;y--;)with(A[y])volume=volume&&Math.exp(-currentTime)",99);onmousedown=function(e){if(z=e.target.title)with(A[z])play(currentTime=0,volume=1)};onmouseup=function(e){if(z=e.target.title)with(A[z])pause(volume=0)}</script>


Save this code as a text file with a name ending in .htm or .html and open it in Chrome or Opera (Safari might also work), or just open the solution's JSBin page to start playing. I reused the WAV file header from my solution to the Twinkle Twinkle Little Star code golf problem.

An important feature is that the sound diminishes as time passes. To observe this behavior, try holding down a key for a few seconds and listening to what happens.

Here is a more readable version of the code:

<style>
table {
border-collapse: collapse;
border-width: 1 0;
border-style: solid;
font-size: 64;
line-height: 2;
}

td {
border-style: solid;
border-width: 0 1;
}
</style>

<table>
<td colspan=3 title=0>&nbsp;
<td bgcolor=black colspan=2 title=1>&nbsp;
<td colspan=2 title=2>&nbsp;
<td bgcolor=black colspan=2 title=3>&nbsp;
<td colspan=3 title=4>&nbsp;
<tr>
<td colspan=4 title=0>&nbsp;
<td colspan=4 title=2>&nbsp;
<td colspan=4 title=4>&nbsp;
</table>

<script>
for (A = [y = 5]; y--;) {

for (s = x = 64e3; x--;)
s += "~ "[x * (268 + 17 * y) >> 13 & 1];

A[y] = new Audio("data:audio/wav;base64,UklGRiXuAgBXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQHuAgCA" + btoa(s));
}

setInterval(function() {
for (y = 5; y--;)
with (A[y])
volume = volume && Math.exp(-currentTime);
}, 99);

onmousedown = function(e) {
if (z = e.target.title)
with (A[z])
play(currentTime = 0, volume = 1);
};

onmouseup = function(e) {
if (z = e.target.title)
with (A[z])
pause(volume = 0);
};
</script>

• +1 Works nice in Firefox 15, although I would have chosen a better sounding instrument. – DavidC Sep 17 '12 at 1:30

Groovy: 577 (703 with whitespaces)

The first 5 notes. Others could be added easily, it's somewhat dynamic.

Damn swing. Probably with a swing lib it would be better.

Plays through JFugue.

On groovy 2.0.2

import java.awt.event.*
class Note { def n; boolean s; def p() { new org.jfugue.Player().with {play n;close()}} }
notes=['C','C#','D','D#','E'].inject([]){ l,n -> l<< Note[n:n,s:n=~/#/]}
h=300
l=0
w=60
x=0
new groovy.swing.SwingBuilder().edt {
frame size:[notes.size()*30+30,h],
show:true,
defaultCloseOperation:javax.swing.JFrame.EXIT_ON_CLOSE,
{ l = layeredPane() }
notes.each { n ->
C=java.awt.Color
s=n.s
p=panel bounds:(s ? [x-15,0,w-30,h-100] : [x,0,w,h]),
background: s ? C.BLACK : C.WHITE,
border: lineBorder(1, color: C.BLACK)
if(!s)x+=w
}
}


R - 491 characters

I'm a little late but I just saw this post yesterday.

Works on a Mac, uses playRWave and packages tuneR and splancs.

a=array
x=c(7,2)
y=c(5,2)
z=c(1,1,3,3)
par(mar=rep(0,4))
plot(NA,xli=c(0,9),yli=c(0,3))
N=list(a(c(0,3,3,2,2,0,0,0,0,z,0),x),a(c(3,6,6,5,5,4,4,3,3,0,0,z,1,1,0),c(9,2)),a(c(6,6,7,7,9,9,6,0,z,0,0),x),a(c(2,4,4,2,2,z,1),y),a(c(5,7,7,5,5,z,1),y))
c=c(NA,NA,NA,1,1)
for(i in 1:5){polygon(N[[i]],c=c[i])}
h=c(261.63,293.66,329.63,277.18,311.13)
library(tuneR)
setWavPlayer("~/Library/Audio/playRwave")
repeat{P=data.frame(locator(1));play(sine(h[sapply(N,function(x)splancs::inout(P,x))],bit=16))}


Ungolfed:

par(mar=rep(0,4))
plot(NA,xlim=c(0,9),ylim=c(0,3)) #Create empty plot: due to fuzzy matching of arguments, xlim can be reduced to xli
N=list(array(c(0,3,3,2,2,0,0,0,0,1,1,3,3,0),dim=c(7,2)), #C polygon
array(c(3,6,6,5,5,4,4,3,3,0,0,1,1,3,3,1,1,0),dim=c(9,2)), #D polygon
array(c(6,6,7,7,9,9,6,0,1,1,3,3,0,0),dim=c(7,2)), #E polygon
array(c(2,4,4,2,2,1,1,3,3,1),dim=(5,2)), #Db polygon
array(c(5,7,7,5,5,1,1,3,3,1),dim=(5,2)))  #Eb polygon
c=c(NA,NA,NA,1,1) #Colors: by default 1 is "black"
for(i in 1:5){polygon(N[[i]],color=c[i])}
h=c(261.63,293.66,329.63,277.18,311.13) #Notes frequency in hertz: C4, D4, E4, Db4 and Eb4
library(tuneR)
setWavPlayer("~/Library/Audio/playRwave") #This can be change to other wav player I think
repeat{
P=data.frame(locator(1)) #Grab coordinates of selected point
H=h[sapply(N,function(x)splancs::inout(P,x))] #In which polygon does the selected point belong to, then map it to its ferquency
s=sine(H,bit=16) #By default create a 1sec note at the given frequency with 44100 sampling rate
play(s)
}