14,979,401 members
Articles / Web Development / HTML
Article
Posted 15 Oct 2014

20.5K views
13 bookmarked

# Simple Keyboard Electric Organ

Rate me:
This is a simple electric organ (piano) to teach how to synthesize sound with directsound.

## Introduction

This article will show how to synthesize simple sound with mathematical formulas, and play it without using any sound files, i.e., wav or mp3. All sound waves are synthesized with the help of `Math.Sine `function. This is the very first step, if somebody wants to create his/her own synthesizer. I use two sound systems: the everywhere-used "well-tempered" or called "equal-temperated" sounds, and early used "pythagorean-diatonic" sound systems. With this program, everybody can hear the difference between two sound systems. This electric piano can play multiple sounds in the same time. The sounds can be played with mouse, or computer keyboard. I wanted to make this program as simple as possible, so this program is a fun, please nobody think of any complex thing.

## Background

To understand the basics of this, it's very useful to study some basics of working of Directx-directsound. I found a very useful article here. This is a working example, and I could start with the help of it. Some other basics of working directsound systems can be found in the official Microsoft's pages here. There are many articles about of directsound, but I needed specially directsound with .NET. Many articles use C++, so there aren't good for me at this time.

The other thing is everybody can ask how could I calculate the frequencies of the sounds. There are also many-many articles in the net, or lexicons. I think, everybody knows the basic 'A' sound's frequency is 440Hz (cycle per second) (In medieval ages there were 415 and others...). One octave higher sound's frequency is multiplicated with 2, one octave below is divided with 2. To calculate the "well tempered" sounds frequency is relative easy: the well tempered frequencies make a geometric progression with quotient 12-th root of 2. This is a constant approximately 1.05946. So, for calculating, we have to multiply the frequency with the first, second, third, etc. power of this constant. All this is well written in this article. The other, the Pythagorean sound system is more difficult. Some musical qualification is needed, i.e., to understand what does a quint, or an octave mean... And what is quint-round.

So, I don't want to make a singing or music lesson, (I'm not a theoretical musician) but I offer to read these articles about Pythagorean tuning or Chromatic scale can be also interesting. Both articles are understable, I think not only for musicians.

## Using the Code

Lets' look into the machine. First, I made class: the name is `Sound`. Each sound is a representative of this class. The class has two constants, the normal A sounds frequency, and the quotient of the equal tempered frequency's geometric progression. There are `static`, because this constant is used in the class not only in representatives. The class has a "`SecondaryBuffer`" typed `private `property this is the "array" that will contain the synthetized sound samples. The class's constructor can calculate the sound samples, and puts into the wave-samples into the `SecondaryBuffer`. (For understanding what is secondary buffer, you have to understand the working of directsound, so please read articles that I offered before). For calculating, I use Sine mathematical function in a cycle.

Some preparing:

C#
```//How many samples we need
int samples = (int)(format.SamplesPerSecond * format.Channels) ;
//Make a short-array with length of number calculated before.
short[] buffer = new short[samples];
//calculate how many bytes we need into buffer
description.BufferBytes = buffer.Length * format.BlockAlign;```

The sample calculating cycle:

C#
```//cycle to calculate sine wave sound from 0 to number of samples
for (int i = 0; i < buffer.Length; i++)
{
//calculate the i-th member of array with Sin function. multiplicate with 2 will make higher amplitude of voice.
buffer[i] = (short)(AMPL * Math.Sin(2 * Math.PI * i * freq / format.SamplesPerSecond));
}```

`Buffer `is an array of short, then I write buffer into secondary buffer:

C#
`sec_buffer.Write(0, buffer, LockFlag.None);`

Last, `sound `class has three methods, to start and to stop the sound, and play a short sound that I use at mouse-click event.

C#
```public void ClickPlay()
{
sec_buffer.Play(0, BufferPlayFlags.Default);
}

//Let's start to play sound by button pressing
public void StartPlay()
{
sec_buffer.Play(0, BufferPlayFlags.Looping);
}
//Let's stop the sound by button releasing
public void StopPlay()
{
sec_buffer.Stop();
}```

If somebody wants to make a medieval, or any kind of "exotic" sounded piano: set the NORMALA constant to 415 (or any other value that you want), and run the program. The sounds will be consistent - but not "normal".

Then, let's look at the Form. Each sound is a global representative of `Sound `class:

C#
```Sound c;
Sound cis;
Sound d;
Sound dis;
...```

Variables represent the well-tempered waves, and variables with `_p` suffix represent the Pythagorean sounds.
The `Form_Load `event initializes all of sounds. When the form is ready, then all voices have calculated, when I used the `new` keyword to create (construct) the class. Now, the program is ready to catch the keyboard or mouseclick event on piano-keyboard. I formatted the keyboard from simple buttons, but I had to pay attention to the order of placing buttons. The second button will cover the first. So that is the reason why the order of button numbers (button 1..2..15 etc.) are a bit confusing. First, I made some preparation to initialize directsound like below. For understanding details, please read articles about directsound that I offered in the "Background" section.

C#
```WaveFormat format = new WaveFormat();
//format tag=pcm
format.FormatTag = WaveFormatTag.Pcm;
//one channel
format.Channels = 1;
//How many bits per sample
format.BitsPerSample = 16;
//how many samples per second of the voice. 44100 like CD quality
format.SamplesPerSecond = 44100;
//Block align calculating
format.BlockAlign = (short)(format.Channels * format.BitsPerSample / 8);
//Bytes per sec calculating
format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;

//Create a buffer description with the waveformat
BufferDescription description = new BufferDescription(format);
//Can control the volume
description.ControlVolume = true;

//Defines a Directsound device
Device device = new Device();
//Assign as a new windows control
device.SetCooperativeLevel(new System.Windows.Forms.Control(), CooperativeLevel.Priority);

//Now create the sound-class members. Each sound is a member of Sound class.
//In Sound's constructor  calculates the exact numbers of sound-samples.
//First creating the well temperated sounds, then Pythagorean.

//********************* Well temperated sounds *************************

//The sounds frequencies are members of a geometric progression with quotient is TEMPER (see below). And I calculate each frequency with multiplicate the  powers of TEMPER.
c = new Sound(Sound.NORMALA / Math.Pow(Sound.TEMPER, 9), format, description, device);```

The last row means: I create the (well-temperated) C sound which frequency is equal the A sounds frequency (440Hz) divided by the 9-th power of quotient, where the quotient is 12-th root of 2. It is simple, isn't it?

Every button has a `mouseclick `event. In `mouseclick `event with help of `if `structure, I choose the appropriate sound, and call the sound (class-member's) `ClickPlay `method. With `checkbox1 `and `2`, we can choose which sound system we want to use. We can choose both of them: in fact the difference can be heard best in this situation.

C#
```if (checkBox1.Checked)
{
cis.ClickPlay();
}
if (checkBox2.Checked)
{
cis_p.ClickPlay();
}```

Last, I have to catch the keyboard event. At `key_down `event, the sound will start, and `key_up `will stop. In normal case, we can't catch the Forms' key events, just the key-events of components, i.e., textbox, etc. which can have focus. But the Form has no focus. The problem is solved in this line in Form-Load event:

C#
`this.KeyPreview = true;`

After this line, we are able to catch the key events. In `Form_Keydown`, and `Form_Keyup `with the help of `switch`-`case `structure, the program chooses the good sound (of course, we can choose well tempered or Pythagorean sound like in `mouseclick `event) and call `StartPlay `or `StopPlay `methods of the `Sound`-class.

C#
```//Check whether temperated or diatonic is selected
if (checkBox1.Checked)
{
//Select the key-events what to do
switch (ee.KeyCode)
{
//In case of A button pressed down, then C sound will start...
case Keys.A:
//and the button's colour let's be grey
button1.BackColor = Color.Silver;
c.StartPlay();
break;```

The `Key_Down `and `Key_Up `eventhandlers are really similar: first start the sound, second stops. And of course, both of them recolorize the button. The `Key_down `will make silver color, the `Key_up `makes back the original black or white color.

## Points of Interest

• How to use Directx/Directsound
• How to synthesize sounds without using wav or mp3 files, just with the help of sine function
• Last, but not least, some theory of music

## History

This is the very first version of this example.

## Share

 Software Developer Hungary
No Biography provided

 First Prev Next
 haha this takes me back :) Wombaticus21-Apr-15 0:17 Wombaticus 21-Apr-15 0:17
 Re: haha this takes me back :) hevesir24-Apr-15 1:57 hevesir 24-Apr-15 1:57
 My vote of 5! jediYL6-Nov-14 19:13 jediYL 6-Nov-14 19:13
 Re: My vote of 5! hevesir6-Nov-14 23:17 hevesir 6-Nov-14 23:17
 Loader lock on startup Arkitec16-Oct-14 11:56 Arkitec 16-Oct-14 11:56
 Re: Loader lock on startup Arkitec16-Oct-14 11:59 Arkitec 16-Oct-14 11:59
 Re: Loader lock on startup hevesir16-Oct-14 23:13 hevesir 16-Oct-14 23:13
 directx dlls? oddtim16-Oct-14 4:33 oddtim 16-Oct-14 4:33
 Re: directx dlls? hevesir16-Oct-14 4:42 hevesir 16-Oct-14 4:42
 Last Visit: 31-Dec-99 18:00     Last Update: 1-Aug-21 19:38 Refresh 1