Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
Full duplex means simultaneous recording and playback of audio. For example digital audio worksation (DAW) applications may play a mix of the earlier recordings to monitor speakers while recording new tracks.
For many users and programmers "full duplex" means capability to record their guitar playing, applying fancy live effects and then playing the result to the speakers. The latencies between input and output should be milliseconds (or preferably less). This is fully supported by OSS. However be warned that general purpose operating systems are not designed for this purpose. You will have to use priority levels above all gods to prevent other applications or kernel tasks from breaking the timing. The application itself must be optimized to run fast enough.
There are two different ways to do full duplex. The older method is to use the same audio device (say /dev/dsp) both for recording and playback. This is somehow easier method for programmers because they need to handle only one device. In addition earlier OSS versions didn't provide any method for starting multiple audio devices precisely at the same time.
The drivers for many modern audio devices (professional ones in particular) don't support support this method. The re are several reasons for this. In some cases there is no reliable way to correlate the inputs and outputs together. With professional devices it's more flexible to separate the inputs from the outputs. However it's possible to use the virtual mixer (vmix) driver to make separate input and output devices to look like a single duplex device. OSS 4.0 uses this approach by default for most sound cards.
It is recommended that applications support both schemes if possible and let the user to select which devices to use. If this is not possible then the 2-device approach is more recommended than the 1-device one.
This method is explained first just because it's slightly easier to use. However the two device method described next is the recommended one.
Applications may let the user to enter the device file name using a command line option or a free text entry field on a setup dialog. Another approach is to show a list of suitable devices on the screen and to let the user to pick the device from the list.
A list of full duplex capable audio devices can be obtained by calling SNDCTL_AUDIOINFO for all devices and by selecting the ones that have the PCM_CAP_DUPLEX capability listed in the caps
field.
The fulldup.c sample program demonstrates how to use the one device full duplex model (the open_one_device() routine.
The fulldup.c sample program demonstrates how to use separate input and output device files for full duplex. This is done by the open_two_devices() function.
Also in this method the application may let the user to enter any device name for the input and output devices. However another approach is to have separate input and output device selection boxes in the setup dialog.
The method to find the devices is to use SNDCTL_AUDIOINFO to find all output devices (with PCM_CAP_OUTPUT) and to show them in the output device selection box. Then the same can be done for input devices by checking the PCM_CAP_INPUT capability.
However there is one danger in the above method. Two arbitrarily selected audio devices rarely run exactly at the same same sampling rate unless they are syncronized to the same rate source (clock). The SNDCTL_AUDIOINFO ioctl call uses the rate_source
field for this. If two audio devices have the same rate source number then they are guaranteed to be in sync. However it's very important to understand that also devices with different rate sources may be slaved to the same clock (for example by using external sync capabilities provided by most professional audio devices).
Applications must not prevent the user from selecting devices with different rate sources. However in such case it may show a warning about possible syncronization problems. The application may also try to control the error in sampling rates for example by inserting or dropping samples.
One approach is to update the input device list depending on the output device that is currently selected by the user. This can be done by picking input devices with the same rate source than the current output device.
In some cases (USB audio devices in particular) OSS may not know which audio devices belong together. In such case there is no input device with rate source that matches the output device (or vice versa). Applications that check the rate source must keep this in mind. There must be some way to select devices with different rate sources even the application doesn't permit this by default.
The best approach is to let the user to select one audio device. If this device has the PCM_CAP_DUPLEX capability then there is no need to select separate input device (however the user may want to use different input device even in this case). If the device doesn't support full duplex then the application should show a separate device selection box for the input input device.
After the audio device file(s) are opened there are few possible ways to handle flow control and syncronization. Normally the timing should be driven only by the recording direction. There is no idea in checking for availability of space in the output buffer since output will always run in sync with input.
In some cases applications may do both recording and playback at the same time but the input and output streams are not related. They may even have different sample rates and formats. In such cases it might be easier to handle both directions by separate threads.
The fulldup.c sample program demonstrates how to handle timing in full duplex programs. For the time being there are two different methods implemented in this program. Other strategies are possible too but in most cases they would be too complicated.
Method | Description |
0 | This method demonstrates how to do full duplex in the easy way using blocking reads/writes. |
1 | This method shows how to use select() for flow control. |