home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sound Sensations!
/
sound_sensations.iso
/
voice
/
polymusi
/
polymusi.txt
< prev
next >
Wrap
Text File
|
1985-07-20
|
6KB
|
122 lines
Polyphonic Music on the IBM PC
Steve Muenter
Rocketdyne Microcomputer Users Group
Sitting at the keyboard of my IBM PC, I realized that other computers could
produce music with three simultaneous tones for chords and harmony while my
IBM was limited to only one tone at a time. To rectify this, I wrote an
assembly language program which produces three tones from the IBM
PC's internal speaker without any hardware modification.
Normally, a single tone is generated through the speaker using the 8253
Programmable Interval Timer. The microprocessor loads a divisor into
the 8253's register for counter 2. The 1.19 MHz system clock is divided
by this number and the resulting square wave is sent to the speaker.
Once the divisor is loaded, the microprocessor is free to do
something else while the tone plays in the background.
Since the hardware is not set up to produce three tones simultaneously,
it becomes necessary to get the microprocessor to do the dividing of
the system clock. The program loop which performs the divisions for the
three tones must be very quick in order to produce tones with high
audio frequencies. I found when using memory to store intermediate
division results, the relatively slow memory accesses took too many clock
cycles giving three low growls at best. Using the 8088 internal
registers speeded up the loop considerably since the register
accesses are about ten times faster than memory accesses.
The assembly language routine presented here was written to be
called from BASIC. The code is divided into three sections. The
first section (from "tri" to "sort") sets up the pointers to the data
passed to the routine. Also, the 8088 internal registers are zeroed.
The second section (from "sort" to "loop") sorts the 16-bit integer
numbers passed to the routine. The three most significant bits of the
integer identify the remaining 13 bits as either voice 1, 2, or 3
period data, note duration data, tempo data or end-of-song flag. The
bit pattern 000 identifies that the song has ended and causes the routine
to return to BASIC. The pattern 001 sets the duration of time that the
current notes will play before the next notes start. The pattern 010
sets the tempo of the playback. The pattern 100 identifies that the 13
remaining bits represent period data for voice 1. The pattern 101
identifies voice 2 period data, and the pattern 110 identifies voice 3
data.
The last section (from "loop" to "end") performs the division of the
system clock and switching of the speaker for each of the three voices.
This is the section of the program that actually makes the tones. For
speed, the routine for each voice is written entirely rather than written
as a single subroutine called three times. This was done because jumps
and calls require at least fifteen clock cycles.
Although the assembly language routine parses the 16-bit binary data
into three bits identifying data type and thirteen bits representing a
numerical value, BASIC treats this data as an array of 2's complement
16-bit single precision integers. This means that the decimal
equivalent of the number is given by the following equation.
N = -2^15*a(15) + 2^14*a(14) +
2^13*a(13) + 2^12*a(12) +
2^11*a(11) + 2^10*a(10) +
2^9*a(9) + 2^8*a(8) + 2^7*a(7) +
2^6*a(6) + 2^5*a(5) + 2^4*a(4) +
2^3*a(3) + 2^2*a(2) + 2^1*a(1) +
2^0*a(0)
where a(n) is a 1 or 0 in the nth bit of the 16 bit number. Notice that
the most significant bit (the 16th bit) represents a negative number
(-32768) whereas all the other bits represent positive numbers. The
numerical range of the 2's complement integers is -32768 to 32767.
It can be seen that the pattern of the three most significant bits can
be represented as a decimal number. The pattern for identifying duration
data (001) corresponds to a decimal value of 8192. Tempo data (010)
equals 16384, voice 1 data (100) equals -32768, voice 2 data (101)
equals -24576, and voice three data (110) equals -16384. The 13
remaining bits can represent decimal numbers ranging from 0 to 16383.
The accompanying BASIC program contains data statements which load
an integer array with the data for playing the first part of the Rondo
Alla Turca by Wolfgang A. Mozart. The first entry in the integer data
array sets the tempo of the playback. A typical tempo value of 512 plus the
tempo identifier 16384 gives the integer data value 16896.
A note's duration is specified by its length relative to a 32nd note. For
example, a quarter note equals eight 32nd notes, so a decimal value of 8
is added to the duration identifier, 8192. The resulting number is 8192 +
8 = 8200. Likewise, a whole note is represented by the decimal integer
8192 + 32 = 8224.
The voice data is determined by adding the number representing the
frequency of the desired note to the number identifying the desired voice
of the three possible. The following table gives the numbers representing
the notes of the scale. For example, middle C on voice 1 is represented by
adding the voice 1 identifier with the value for middle C. The
resulting number is -32768 + 1024 = -31744. To turn voice 1 off, the
data would be -32768 + 0 = -32768.
NOTE DATA TABLE
LOW MIDDLE HIGH
--- ------ ----
C = 512 C = 1024 C = 2048
C# = 542 C# = 1085 C# = 2170
D = 575 D = 1149 D = 2299
D# = 609 D# = 1218 D# = 2435
E = 645 E = 1290 E = 2580
F = 683 F = 1367 F = 2734
F# = 724 F# = 1448 F# = 2896
G = 767 G = 1534 G = 3069
G# = 813 G# = 1625 G# = 3251
A = 861 A = 1722 A = 3444
A# = 912 A# = 1825 A# = 3649
B = 967 B = 1933 B = 3866
No tone = 0
[Editor's note : The programs discussed above can be found on this diskette
as separate files, TRI.BIN, TRI.ASM and TRTEST.BAS. The programs do not
operate properly on the PCjr or the PC AT due to timing considerations.]