Sound programming can be classified as with PC speaker and with sound blaster card. Let’s see sound programming with PC speaker. |
Almost all systems have PC speaker. People who like to have digitized sound go for MIDI card or sound blaster card. But for normal operations, it is enough to have PC speaker. |
Programming PIT |
For sound programming with PC speakers, we must be aware of PIT (Programmable Interval Timer) that is present on our microcomputer system. PIT or 8253 chip is an LSI peripheral designed to permit easy implementation of timer. People from Electronics background may be aware that Timer is the one which produces clock signals. And so PIT can be setup to work as a one shot pulse generator, square wave generator or as rate generator. We can set the PIT to supply the required frequency by supplying values ‘N’ to the port 43h. 1.9 MHz |
![]() |
Now the PIT will produce clock signals with the frequency ‘f’. |
Producing Sound |
If we connect a timer with PC speaker, it will produce sound. We can connect PIT with PC speakers to get the required sound. The output port of speaker is 61h. bit0 of port 61h is used to enable timer to supply clock signal to speaker i.e. connects PIT with speaker. |
Now let’s write our own sound( ) and nosound( )function to produce sound. |
#define ON (1)
#define OFF (0) /*———————————————— ChangeSpeaker – Turn speaker on or off. */ void ChangeSpeaker( int status ) { int portval; portval = inportb( 0x61 ); if ( status==ON ) portval |= 0x03; else portval &= 0x03; outportb( 0x61, portval ); } /*–ChangeSpeaker( )———-*/ void Sound( int hertz ) { unsigned divisor = 1193180L / hertz; ChangeSpeaker( ON ); outportb( 0x43, 0xB6 ); outportb( 0x42, divisor & 0xFF ) ; outportb( 0x42, divisor >> 8 ) ; } /*–Sound( )—–*/ void NoSound( void ) { ChangeSpeaker( OFF ); } /*–NoSound( )——*/ int main( void ) { Sound( 355 ); delay( 1000 ); Sound( 733 ); delay( 1000 ); NoSound( ); return(0); } /*–main( )——-*/ |
TC also has sound( ) and nosound( ) functions. If you don’t want to write your own code, you can use those built-in functions. |
Notes and Frequencies |
You may want to know the frequencies of each note to produce the right sound. In general, an octave is a doubling in frequency. There are twelve distinct tones in an octave. The frequencies of higher octaves are just a multiple of frequencies for lower octaves. The note ‘A’ below “middle C” is exactly 440Hz. |
Other notes may be calculated from this by using a simple formula: |
Frequency = 440 * 2
(Offset / 12) where |
Offset is the “distance” between note ‘A’ and the note in semitones. Using the above formula, any part of the frequency table can be calculated. |
The following program demonstrates this. |
#include <math.h>
char *Note_Names[] = { “A”, “B Flat”, “B”, “C”, “C Sharp”, “D”, “E Flat”, “E”, “F”, “F Sharp”, “G”, “G Sharp” }; int main( void ) { double frequency; int offset; for( offset=0; offset<13; ++offset ) { frequency = 440.0 * pow( 2.0, offset / 12.0 ); printf( “The Frequency of %s is %f Hz\n”, Note_Names[offset%12], frequency ); } return(0); } /*–main( )——–*/ |
Piano Keys and Frequencies |
The following diagram shows the frequencies for a typical Piano. |
Piano Program |
The following is the code for a Piano program. The main idea here is you have to use port 60h to get a key, you should not use getch( ). |
Since we are using port 60h, the keyboard buffer won’t get cleared automatically. So we should clear the keyboard buffer very often to avoid unnecessary beep sound that signals the keyboard buffer’s full status. |
This program will provide you the opportunity to try 8 octaves. As the frequencies of higher octaves are just a multiple of frequencies of lower octaves, I could have used a single dimensional array notes[12]. But I have used a two dimensional array notes[7][12] to avoid calculations and to increase the speed. |
#define ESC
(129) #include <stdio.h> #include <conio.h> #include <dos.h> int main( void ) { void ClrKeyBrdBuffer( ); float notes[7][12] = { { 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.0, 196.0, 207.65, 220.0, 227.31, 246.96 }, { 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.63, 392.0, 415.3, 440.0, 454.62, 493.92 }, { 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.0, 909.24, 987.84 }, { 1046.5, 1108.73, 1174.66, 1244.51, 1328.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.0, 1818.48, 1975.68 }, { 2093.0, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.0, 3636.96, 3951.36 }, { 4186.0, 4434.92, 4698.64, 4978.04, 5274.04, 5587.86, 5919.92, 6271.92, 6644.88, 7040.0, 7273.92, 7902.72 }, { 8372.0, 8869.89, 9397.28,9956.08,10548.08,11175.32, 11839.84, 12543.84, 13289.76, 14080.0, 14547.84, 15805.44 } }; int n, i, p, q, octave = 2, note[ ] = { 1, 3, 99, 6, 8, 10, 99, 13, 15, 99, 18, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17 }; /* keys[]=”awsedftgyhujkolp;’]” <- for note[] */ clrscr( ); printf( “Piano for A to Z of C \n\n” “Note-> C Df D Ef E F Fs G Af A Bf B C Df D Ef E F Fs \n” “Keys-> a w s e d f t g y h u j k o l p ; ‘ ] \n\n” “Octave-> 1 2 3 4 5 6 7 8 \n\n” “Quit-> ESC \n” ); while( (n=inportb(0x60)) != ESC ) { ClrKeyBrdBuffer( ); p = 2; /*dummy*/ if ( n>=2&&n<=8 ) octave = n-2; else switch( n ) { case 79: case 80: case 81: octave = n-79; break; case 75: case 76: case 77: octave = n-72; break; case 71: octave = 6; } if ( n>=17&&n<=27 ) p = n-17; else if ( n>=30&&n<=40 ) p = n-19; p = note[p]; if ( p>=0&&p<=21 ) sound( (int)notes[octave][p] ); if ( n>136 ) nosound( ); } printf( “Quiting…” ); getch( ); return(0); } /*–main( )———-*/ void ClrKeyBrdBuffer(void) { outportb( 0x20, 0x20 ); /* reset PIC */ while( bioskey(1) ) /* read all chars until it empty */ bioskey( 0 ); } /*–ClrKeyBrd( )——*/ |
Keyboard Interrupt |
To get scan code of ASCII character of the key pressed, we can use the INT 16, AH=10h (Get Enhanced Keystroke). This function returns BIOS scan code in AH and ASCII character in AL register. If no keystroke is available, this function waits until one is placed in the keyboard buffer. The BIOS scan code is usually, but not always, the same as the hardware scan code processed by INT 09 or the one we get from Port 60h. It is the same for ASCII keystrokes and most unshifted special keys (F-keys, arrow keys, etc.), but differs for shifted special keys. |