Just for Fun - A Small Piano Keyboard in VB.NET


This one was just for fun; the article describes a project used to build a simple piano keyboard that plays some not too terrific sounding notes courtesy of the Kernel32.dll's beep function.  Whilst the notes are set to be roughly the correct frequency, the sound produced will not encourage you to sell your piano and take up a new career as a concert computer keyboardist.  On the other hand, it is fun to mess with and likely it would be entertaining for someone in the 8 year old range (at least if you were not within earshot when the entertaining begins).


Figure 1:  A small keyboard

I suppose that if one were to look hard enough, you could probably come up with something useful that you can do with this keyboard (like annoy your cat).  It could probably be turned into something instructional that could be used to teach someone the notes associated with the keys or some swell thing like that (of course that may well take the fun out of messing with it).  Further, if you were to dodge using the beep function and use the PlaySound function from the winmm.dll, and if you had access to recorded wave files for each of the notes played on the piano, you could probably come up with something that sounds a lot better without losing all of the charm and elegant styling associated with this project.

Getting Started:

In order to get started, unzip the attachment and load the solution into Visual Studio 2005.  Examine the solution explorer and note that the project contains one class:


Figure 2:  The Solution Explorer Showing the Project Files

The small keyboard project's single class is a windows form; that form contains a collection of buttons used to simulate the appearance (if not the sound) of piano keyboard.

The Code

I'm sure that it seems inconceivable that this much raw power can be packed into a single class but it is true; I just suppose that is part of the awe inspiring thing we know as .NET.  To have a look at the magic going on behind the scenes, open up the class into the code view window and take a look.  The first thing that you will note is that the code imports System.Runtime.InteropServices.  This is necessary to support the DllImport function used in the first section of code following the class declaration:

Imports System.Runtime.InteropServices


Public Class frmKeyboard


    <DllImport("KERNEL32.DLL")> _

    Public Shared Sub Beep(ByVal freq As Integer, ByVal dur As Integer)

    End Sub

The DLL import call made here is used to bring the Kernel32.dll library into the project; the Kernel32.dll contains the Beep function and it is through this function that we can produce our simulated musical notes through this project's magnificent piano keyboard emulation.  The beep function mercifully accepts only two arguments, the frequency and the duration.  In this application we will pass in the frequency associated with a specific keyboard key (which is actually a float so we have to crop it off to make it fly here).  I think that you will agree that this loss of precision in no way impairs the quality of the audio output.  The duration passed in to the second argument is the same of each of the keys, it was arbitrary and sounded about right for a quick strike on a keyboard key (at least it sounds right if you bought your keyboard on sale at the dollar store).

Now onto the next bit of magic; the button handlers.  This bit of code makes it possible to produce beautiful strains of high quality music through the keyboard:

Private Sub Play_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) _

    Handles btnMC.KeyDown, btnHA.KeyDown, btnHAs.KeyDown, btnHB.KeyDown, _

    btnHC.KeyDown, btnHCs.KeyDown, btnHD.KeyDown, btnHDs.KeyDown, btnHE.KeyDown, _

    btnHF.KeyDown, btnHFs.KeyDown, btnHG.KeyDown, btnHGs.KeyDown, btnLA.KeyDown, _

    btnLAs.KeyDown, btnLB.KeyDown, btnLC.KeyDown, btnLCs.KeyDown, btnLD.KeyDown, _

    btnLDs.KeyDown, btnLE.KeyDown, btnLF.KeyDown, btnLFs.KeyDown, btnLG.KeyDown, _

    btnLGs.KeyDown, btnMA.KeyDown, btnMAs.KeyDown, btnMB.KeyDown, btnMC.KeyDown, _

    btnMCs.KeyDown, btnMD.KeyDown, btnMDs.KeyDown, btnME.KeyDown, btnMF.KeyDown, _

    btnMFs.KeyDown, btnMG.KeyDown, btnMGs.KeyDown, Me.KeyDown 




        Select Case e.KeyData.ToString()

            Case "A"

                Me.btnMC_Click(sender, e)

            Case "S"

                Me.btnMD_Click(sender, e)

            Case "D"

                Me.btnME_Click(sender, e)

            Case "F"

                Me.btnMF_Click(sender, e)

            Case "G"

                Me.btnMG_Click(sender, e)

            Case "H"

                Me.btnMA_Click(sender, e)

            Case "J"

                Me.btnHC_Click(sender, e)

            Case "K"

                Me.btnHD_Click(sender, e)

            Case "L"

                Me.btnHE_Click(sender, e)

            Case "Z"

                Me.btnHF_Click(sender, e)

            Case "X"

                Me.btnHG_Click(sender, e)

            Case "C"

                Me.btnHA_Click(sender, e)

        End Select


End Sub

Taking a look at this code you will note that it handles all of the key down events associated with each of the buttons.  A select case statement is used to figure out which keyboard button was pressed and that is turn is used to figure which button click event to evoke.  The click event handler will then call the beep function and pass to it the correct frequency and the canned duration (but all your user will know is that they are making beautiful music).

Next up is the button click event handlers;  since they are all pretty much the same, I will only show one in this document, the event handler for middle C:

Private Sub btnMC_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles btnMC.Click

    ' middle C

    Beep(261, 150)

End Sub

As advertised, this event handler evokes the beep function and passes the frequency and duration arguments to that function.  I set the duration on all of the keys to be 150 milliseconds but you can use any value of your choosing.


The project demonstrates a few useful things like using the DLL Import function supported by the InteropServices library, but overall, it was just for fun.