Introduction:
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
Me.Focus()
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.
Summary
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.