Team-FLY |
An audio device (microphone, speaker) is an example of a peripheral device represented by a special file. The device designation for this device on many systems is /dev/audio . The discussion in this section illustrates the nature of special files, but it is specific to Sun systems. The audio device may behave differently on different systems. Note: If you logged in from an ASCII terminal or X-terminal, you cannot use the audio device even if the system has one. Example 6.24The following command plays the audio file sample.au on the speaker of a Sun workstation. cat sample.au > /dev/audio The audio device may support several audio formats, and you may have to set the audio device for the proper format before Example 6.24 works correctly. Audio files typically contain a header giving information about the format of the audio file. Sending the file directly to the audio device, as in this example, may cause the header to be interpreted as audio data. You will probably hear a series of clicks at the beginning of the playback. Many systems have a utility for playing audio. The utility reads the header and uses this information to program the audio device for the correct format. This command utility may be called audioplay or just play . In this section, we assume that we are using audio files in a fixed format and that the audio device has already been set for that format. Program 6.16 contains a library of functions for reading and writing from the audio device. None of these library functions pass the file descriptor corresponding to the audio device. Rather, the audio library is treated as an object that calling programs access through the provided interface ( open_audio , close_audio , read_audio and write_audio ). The open_audio opens /dev/audio for read or write access, using blocking I/O. If the audio device has already been opened, open hangs until the device is closed. If the audio device had been opened with the O_NONBLOCK flag, open would have returned with an error if the device were busy. The open_audio function attempts to open both the microphone and the speaker. A process that will only record can call open with O_RDONLY; a process that will only play can call open with O_WRONLY . If it is interrupted by a signal, open_audio restarts open . The speaker can handle data only at a predetermined rate, so write_audio may not send the entire buffer to the speaker in one write function. Similarly, read_audio reads only the data currently available from the microphone and returns the number of bytes actually read. The get_record_buffer_size function uses ioctl to retrieve the size of the blocks that the audio device driver reads from the audio device. Program 6.16 audiolib.cThe audio device object and its basic operations . #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stropts.h> #include <unistd.h> #include <sys/audio.h> #include "restart.h"; #define AUDIO "/dev/audio" static int audio_fd = -1; /* audio device file descriptor */ int open_audio(void) { while (((audio_fd = open(AUDIO, O_RDWR)) == -1) && (errno == EINTR)) ; if (audio_fd == -1) return -1; return 0; } void close_audio(void) { r_close(audio_fd); audio_fd = -1; } int read_audio(char *buffer, int maxcnt) { return r_read(audio_fd, buffer, maxcnt); } int write_audio(char *buffer, int maxcnt) { return r_write(audio_fd, buffer, maxcnt); } int get_record_buffer_size(void) { audio_info_t myaudio; if (audio_fd == -1) return -1; if (ioctl(audio_fd, AUDIO_GETINFO, &myaudio) == -1) return -1; else return myaudio.record.buffer_size; } The ioctl function provides a means of obtaining device status information or setting device control options. The ioctl function has variable syntax. Its first two parameters are an open file descriptor and an integer specifying the type of request. Different requests may require different additional parameters. SYNOPSIS #include <stropts.h> int ioctl(int fildes, int request, .... /* arg */); POSIX If successful, ioctl returns a value other than “1 that depends on the request value. If unsuccessful , ioctl returns “1 and sets errno . The mandatory errors depend on the value of request . See the man page for ioctl for further information. The ioctl function provides a means of obtaining device status information or setting device control options. The Sun Solaris operating environment uses the AUDIO_GETINFO request of ioctl to retrieve information about the audio device. The audio_info_t type defined in audioio.h holds configuration information about the audio device. typedef struct audio_info { audio_prinfo_t play; /* output status information */ audio_prinfo_t record; /* input status information */ uint_t monitor_gain; /* input to output mix */ uchar_t output_muted; /* nonzero if output muted */ uchar_t _xxx[3]; /* Reserved for future use */ uint_t _yyy[3]; /* Reserved for future use */ } audio_info_t; The audio_prinfo_t member of the preceding structure is defined as follows . struct audio_prinfo { /* The following values describe the audio data encoding */ uint_t sample_rate; /* samples per second */ uint_t channels; /* number of interleaved channels */ uint_t precision; /* number of bits per sample */ uint_t encoding; /* data encoding method */ /* The following values control audio device configuration */ uint_t gain; /* volume level */ uint_t port; /* selected I/O port */ uint_t avail_ports; /* available I/O ports */ uint_t _xxx[2]; /* reserved for future use */ uint_t buffer_size; /* I/O buffer size */ /* The following values describe the current device state */ uint_t samples; /* number of samples converted */ uint_t eof; /* end-of-file counter (play only) */ uchar_t pause; /* nonzero if paused, zero to resume */ uchar_t error; /* nonzero if overflow/underflow */ uchar_t waiting; /* nonzero if a process wants access */ uchar_t balance; /* stereo channel balance */ ushort_t minordev; /* The following values are read-only device state flags */ uchar_t open; /* nonzero if open access granted */ uchar_t active; /* nonzero if I/O active */ } audio_prinfo_t; The buffer_size member of the audio_prinfo_t structure specifies how large a chunk of audio data the device driver accumulates before passing the data to a read request. The buffer_size for play specifies how large a chunk the device driver accumulates before sending the data to the speaker. Audio tends to sound better if the program sends and receives chunks that match the corresponding buffer_size settings. Use ioctl to determine these sizes in an audio application program. The get_record_buffer_size function in Program 6.16 returns the appropriate block size to use when reading from the microphone, or “1 if an error occurs. Program 6.17 reads from the microphone and writes to the speaker. Terminate the program by entering Ctrl-C from the keyboard. It is best to use headphones when trying this program to avoid feedback caused by a microphone and speaker in close proximity. The audiolib.h header file contains the following audio function prototypes . int open_audio(void); void close_audio(void); int read_audio(char *buffer, int maxcnt); int write_audio(char *buffer, int length); Program 6.17 audiocopy.cA simple program that reads from the microphone and sends the results to the speaker . #include <stdio.h> #include <stdlib.h> #include "audiolib.h" #define BUFSIZE 1024 int main (void) { char buffer[BUFSIZE]; int bytesread; if (open_audio() == -1) { perror("Failed to open audio"); return 1; } for( ; ; ) { if ((bytesread = read_audio(buffer, BUFSIZE)) == -1) { perror("Failed to read microphone"); break; } else if (write_audio(buffer, bytesread) == -1) { perror("Failed to write to speaker"); break; } } close_audio(); return 1; } The implementation of Program 6.16 opens the audio device for blocking I/O. Nonblocking reads are complicated by the fact that read can return “1 either if there is an error or if the audio device is not ready with the data. The latter case has an errno value of EAGAIN and should not be treated as an error. The primary reason for opening the audio device in nonblocking mode is so that open does not hang when the device is already open. An alternative is to open the audio device in nonblocking mode and then to use fcntl to change the mode to blocking. Example 6.25 nonblockingaudio.cThe following program opens the audio device for nonblocking I/O. It then reads BLKSIZE bytes from the audio device into a buffer. It does nothing with the audio that is read in other than display the number of bytes read. #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "restart.h" #define AUDIO_DEVICE "/dev/audio" #define BLKSIZE 1024 int main(void) { int audiofd; char *bp; char buffer[BLKSIZE]; unsigned bytesneeded; int bytesread; if ((audiofd = open(AUDIO_DEVICE, O_NONBLOCK O_RDWR)) == -1) { perror("Failed to open audio device"); return 1; } bp = buffer; bytesneeded = BLKSIZE; while(bytesneeded != 0) { bytesread = r_read(audiofd, bp, bytesneeded); if ((bytesread == -1) && (errno != EAGAIN)) break; if (bytesread > 0) { bp += bytesread; bytesneeded -= bytesread; } } fprintf(stderr, "%d bytes read\n", BLKSIZE - bytesneeded); return 0; } In testing audio programs, keep in mind that the audio device is closed when the program exits. If the audio buffer still holds data that has not yet reached the speakers , that data may be lost. The draining of a device after a close is system dependent, so read the man page before deciding how to handle the situation. |
Team-FLY |