|
|
There are a couple of routines whereby the speaker in a PC can be made to
produce a tone; you output to a port numbers corresponding to a frequency and
a duration, and the tone is generated automatically.
* This is NOT what I am after. *
What I need is information on setting the *voltage level* across the leads
of the speaker, to produce tones on a wave-by-wave basis. To wit, code looking
something like
for (w=0; w < NUM_OF_WAVES; w++) {
set_speaker_voltage(HIGH);
wait(MICROSECONDS);
set_speaker_voltage(LOW);
wait(MICROSECONDS);
}
should generate NUM_OF_WAVES wavelengths duration of a tone of frequency
1 / (2 * MICROSECONDS) MHz, or as close as the hardware is capable to getting.
Can anyone help in writing set_speaker_voltage()? Also, how many different
voltage levels can the speaker be set to?
6 responses total.
I suspect, none. The oscillator the drives the PC speaker is of the square wave variety that divides down a (10 MHz?) clock to produce an audible tone. The only command you can send the to the PC speaker is basically "N", the No. to divide the clock by. You can't control the voltage to the speaker. I suspect it is set to 5v in most PCs.
It almost has to be possible. I have a program which converts typed text into speach coming out of the speaker (NOT a sound card). There are also touch-tone phone tone generators for the PC, also using the speaker. Sending frequency- and-duration commands via the frequency divider simply cannot be done fast enough even in assembly language to handle such tasks. Or can it? I think I've tried at least once to produce varying tones, and the machine was just too slow at switching them.
I thougughght you wanted to control not only the frequency but the amplitude (volume) of the frequency? As far as I know, you can't control the volume.
That depends on how many bits "wide" the control to the speaker is. If there is only one bit controlling the voltage, then correct, the amplitude is fixed, and only a square wave of one frequency at6 a time would be possible. With two bits for speaker level, there would be either 3 or 4 possible levels, depending on whether 0,1 produces the same voltage as 1,0; and then it would be possible to generate a dual-tone signal by switching one bit at one frequency and the other bit at another frequency. However, even single-bit control might suffice, *IF* there is a way to directly control the bit itself, rather than depend on the frequency-divider chip to do the work (which it seems unable to do quickly enough).
I did this 22 years ago in 1975, so I suspect it is still possible. At the time, I had an IMSAI 8080 and D->A converter. It was crude by today's standards, but I could write to it every 5 us or so, so it could generate high enough tones, although the CPU wasn't up to that. I coded the inner loop and could write to it about every 40 to 45 us, which sets an upper limit of about 12.5 khz. The width was 8 bits, so I could set any level from -128 to 127. This is way inadequate for dynamic range, but I certainly could change the volume. What I did as far as coding it was something like this: I built a wavetable, which is just an array of voltage levels. This array should describe a full wave, and determines the timbre of the tone. For speed you might want to scale it in a temp array to produce the volume you want. I did not address "attacks" or "envelopes" which are needed to compete with what modern synthesizers can do. I just didn't have the hardware to do this. Then you want to set the pitch. This is handled by logic in the main loop. The main loop consists of picking an entry out of the wave table and then setting the D2A device to its value. You want to come through this loop at extremely regular intervals. In the loop, you have code like this phase+= pitch; if (phase >= LIMIT) phase-= LIMIT; index= (WAVETABLESIZE*phase)/LIMIT; writeD2A(WaveTable[index]); I am assuming these are all ints. WAVETABLESIZE is the number of entries in your wave table. If it is too small, your waves will not be smooth. LIMIT is a large number, because it is essentially the scale factor for phase. phase/LIMIT as an integer is always 0, because phase is always < LIMIT. However, LIMIT*WAVETABLESIZE must be less than MAXINT, so you have to juggle here. You need at least 32 bit aritmetic. If you have a fast FPU, use it. When I used that old 8080, I skipped the multiplies and divides and did it all with shifting. The concepts are the same. It's faster, but less general, and if you assume as I did that I could just let it overflow and AND off the sign bit, it becomes faster yet, but less portable.A Higher pitch causes it to cycle through the waves faster. I actually had 6 voices calculated simultaneously, and added three together for each speaker, producing stereo 6-part harmonies. The tone quality sucked, though, because I was using a digital computer to emulate what analog circuitry can do so much better. This was an interesting exercise, but not anything to produce decent music. Of course on computers of today, much better results could be obtained. Remember, though, that producing tones this way is real-time programming. You don't have time to do anything else with the computer while it is doing this. Modern synthesizers are the tops. I would do it with MIDI today.
I did similar work using the cassette output port on a TRS-80. The control port was two bits wide, so it was possible to switch one of the bits back and forth at one frequency and the other bit back and forth at another frequency to generate a dual-tone signal. In spite of it being square waves, it worked amazingly well. I want to do something similar on an Intel-based PC.
Response not possible - You must register and login before posting.
|
|
- Backtalk version 1.3.30 - Copyright 1996-2006, Jan Wolter and Steve Weiss