Create a GUI Piano

15

1

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.

piano

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).

Rob

Posted 2012-09-15T16:27:30.130

Reputation: 1 277

Question was closed 2016-12-20T04:23:36.867

2Restrictions on which language to use are not liked very much here. Consider dropping your restriction or name an important reason. – FUZxxl – 2012-09-15T18:08:53.130

I agree with FUZxxl. Java and C♯ suck at CG anyway so I can see even less why you would want to forbid them. Still, I like this task. – ceased to turn counterclockwis – 2012-09-15T18:37:12.527

1@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 – 2012-09-15T19:02:29.607

@leftaroundabout Thank you, I made some changes to the Rules. – Rob – 2012-09-15T19:05:48.523

2What 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 – 2012-09-15T22:02:27.657

@PeterTaylor I used buttons for my piano along with a midi synthesizer (default ADSR values) in Java. Since I am only asking for five keys, you can use C, C#, D, D# and E. These keys correspond to the first five keys farthest to the left in the image that I provided. – Rob – 2012-09-16T00:11:42.717

2@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 – 2012-09-16T08:03:22.940

1@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 – 2012-09-16T10:25:47.320

@PeterTaylor As long as it looks like a piano and sounds like a piano (perfect pitch), then it's a valid answer. – Rob – 2012-09-16T12:41:31.090

Answers

11

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]

keyboard


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}]   

large keyboard

DavidC

Posted 2012-09-15T16:27:30.130

Reputation: 24 524

1+1 I have no doubt in my mind that this will work... I just wish I could play on it. – Rob – 2012-09-16T00:08:33.860

1

I left a .cdf version of the file in my DropBox at https://www.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 – 2012-09-16T00:35:21.443

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 – 2013-04-06T14:49:24.620

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 – 2013-04-06T16:14:00.020

10

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   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>

PleaseStand

Posted 2012-09-15T16:27:30.130

Reputation: 5 369

1+1 Works nice in Firefox 15, although I would have chosen a better sounding instrument. – DavidC – 2012-09-17T01:30:11.403

6

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.

enter image description here

Plays through JFugue.

On github: https://github.com/wpiasecki/glissando/blob/master/src/br/glissando/Piano.groovy

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)
    p.addMouseListener({ if(it.id==MouseEvent.MOUSE_CLICKED)n.p() }as MouseListener)
    if(!s)x+=w
    l.add p,s?0:1
  }
}

Will Lp

Posted 2012-09-15T16:27:30.130

Reputation: 797

1

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))}

enter image description here

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)
    }

plannapus

Posted 2012-09-15T16:27:30.130

Reputation: 8 610