diff options
Diffstat (limited to 'tutorials/sndkit/samples/mmap_duplex.c')
-rw-r--r-- | tutorials/sndkit/samples/mmap_duplex.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/tutorials/sndkit/samples/mmap_duplex.c b/tutorials/sndkit/samples/mmap_duplex.c new file mode 100644 index 0000000..42f4762 --- /dev/null +++ b/tutorials/sndkit/samples/mmap_duplex.c @@ -0,0 +1,333 @@ +/* + * Purpose: A simple sample program for doing dull duplex using mmap + * + * Description: + * + * This is a sample program for doing full duplex using mmap. + * + * Unfortunately this program doesn't work at this moment (under construction). + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/types.h> +#include <soundcard.h> + +/* + * Note that the mmap approach bypasses the OSS drivers completely so you are + * on your own. You will need to write the application so that it + * can work with any sample rate or sample format one can imagine. + * + * We will try to use 48 kHz / 16 bits / stereo which is the most likely + * format supported by the consumer audio devices. + */ +#define SAMPLE_RATE 48000 +#define SAMPLE_FORMAT AFMT_S16_NE +#define NUM_CHANNELS 2 +/* + * NOTICE! + * + * The mmap() feature is not supported by all devices. Using it will make + * the application to fail with some devices (professional ones in particular). + * + * When using the mmap mode it's very important to disable any format + * conversions done by OSS. This can be done by calling SNDCTL_DSP_COOKEDMODE + * _IMMEDIATELY_ after opening the device. + */ + +int sample_rate, num_channels, sample_format; + +int +main (int argc, char *argv[]) +{ + int fd, sz, i, tmp, n, l, have_data = 0; + +/* + * /dev/dsp_mmap is the best default device. Another alternative is /dev/dsp + * but in some systems the user may want to use different devices with + * mmap applications than with the normal ones. + * + * /dev/dsp_mmap will not be present in pre OSS 4.0 systems. The application + * may automatically try to open /dev/dsp if /dev/dsp_mmap is missing. + * Another approach is asking the user to create /dev/dsp_mmap. + * + * ln -s /dev/dsp /dev/dsp_mmap + * + * It's recommended that there is some method for configuring or selecting + * the audio device (such as a command line option or an environment + * variable. + */ + char *audio_dev = "/dev/dsp_mmap"; + + struct buffmem_desc imemd, omemd; + caddr_t buf; + + char *ip, *op; + + struct audio_buf_info info; + + int frag = 0x00200008; /* 32 fragments of 2^8=256 bytes */ + + fd_set reads, writes; + +/* + * Getting the device name to open. First the command line method + * (simplified). + */ + + if (argc >= 2) + { + audio_dev = argv[1]; + } + else + { + /* + * No device given on command line. Try to see if an environment + * variable is set. + * + * We will use MYAPP_AUDIODEV as the variable name. Replace the + * MYAPP_ prefix with your application name. + */ + + char *p; + + if ((p = getenv ("MYAPP_AUDIODEV")) != NULL) + audio_dev = p; + } + + if ((fd = open (audio_dev, O_RDWR, 0)) == -1) + { + perror (audio_dev); + exit (-1); + } + /* + * Disable cooked mode to permit mmap() with some devices. + * Don't do any error checking since usually this call will fail. + * There is no need to care about the return value. + */ + tmp = 0; + ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Don't check the error return */ + + ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &frag); /* No error checking needed */ + +/* + * Set the sample format. AFMT_S16_NE is best because it is supported by + * practically all devices. Also it equals to the "short" data type. + */ + + sample_format = SAMPLE_FORMAT; + if (ioctl (fd, SNDCTL_DSP_SETFMT, &sample_format) == -1) + { /* Something really fatal occurred. */ + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + +/* + * Check that we can support the returned sample format. + */ + switch (sample_format) + { + case AFMT_S16_NE: + /* + * Everything is OK. The sample format equals to 16 bit native + * integer (short in C/C++) so we do not need to do any + * format conversions. + */ + break; + +#if 0 +/* + * We do not support any other formats. However you must be prepared to do + * so. Please take a look at the formats section of the OSS API + * manual. + * + * Here are the most common other formats. + */ + + case AFMT_S16_OE: + /* + * 16 bits but with alien endianess. We can use short as the data type + * but the samples must be "byte swapped" prior writing them to + * the device. + */ + break; + + case AFMT_U8: + /* 8 bits unsigned format (unsigned char). Note that signed + * 16 bit samples can be converted to this format by + * doing u8_sample = (s16_ne_sample >> 8) - 128. + */ + break; + + case AFMT_S24_NE: + case AFMT_S24_OE: + case AFMT_S32_NE: + case AFMT_S32_OE: + /* Please see the formats section of the OSS API + * manual. + */ + +#endif + + default: + fprintf (stderr, "The device doesn't support the requested format\n"); + fprintf (stderr, "0x%08x != 0x%08x\n", tmp, SAMPLE_FORMAT); + exit (-1); + } + +/* + * Set up the number of channels. + * This program will automatically support any number of channels by + * writing the same sample value to each of the channels (mono). + * + * Equally well stereo samples can be mixed for a 1 channel + * device by summing the channel values together. Stereo samples + * can be played with multi channel devices by setting the remaining + * channel samples to a silent value (0 with signed formats). + */ + + num_channels = NUM_CHANNELS; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &num_channels) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + +/* + * Set the sample rate. + */ + tmp = SAMPLE_RATE; + if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + if (tmp != SAMPLE_RATE) + { + /* + * In some cases the device is not capable to support exactly + * the requested sampling rate. + * + * We will tolerate a 5% error in between the requested and + * granted sampling rates. + * + * If the error is larger then we cannot continue. + * + * NOTE! Applications written for the mass market must be prepared + * to support every possible sampling rate locally. + */ + int v; + + v = abs (tmp - SAMPLE_RATE); + + if (v > ((SAMPLE_RATE * 5) / 100)) + { + fprintf (stderr, + "The device doesn't support sampling rate of %d Hz.\n", + SAMPLE_RATE); + fprintf (stderr, "The nearest rate it supports is %d Hz\n", tmp); + exit (-1); + } + } + + if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) == -1) + { + perror ("GETISPACE"); + exit (-1); + } + + sz = info.fragstotal * info.fragsize; + + if ((buf = + mmap (NULL, sz, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, + 0)) == (caddr_t) - 1) + { + perror ("mmap (read)"); + exit (-1); + } + printf ("mmap (in) returned %08x\n", buf); + ip = buf; + + if ((buf = + mmap (NULL, sz, PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, + 0)) == (caddr_t) - 1) + { + perror ("mmap (write)"); + exit (-1); + } + printf ("mmap (out) returned %08x\n", buf); + op = buf; + +/* + * Prepare for launch. Set the trigger bits to 0 + */ + tmp = 0; + ioctl (fd, SNDCTL_DSP_GETTRIGGER, &tmp); + printf ("Trigger was %08x\n", tmp); + + tmp = PCM_ENABLE_OUTPUT; + ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp); + printf ("Trigger set to %08x\n", tmp); + + for (i = 0; i < sz; i++) + op[i] = 0x00; + + while (1) + { + struct timeval time; + + FD_ZERO (&reads); + FD_ZERO (&writes); + + /* FD_SET(fd, &writes); */ + FD_SET (fd, &reads); + + time.tv_sec = 1; + time.tv_usec = 0; + if (select (fd + 1, &reads, &writes, NULL, &time) == -1) + { + perror ("select"); + exit (-1); + } + + if (FD_ISSET (fd, &reads)) + { + count_info count, ocount; + int l, p; + + if (ioctl (fd, SNDCTL_DSP_GETIPTR, &count) == -1) + perror ("GETIPTR"); + + if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ocount) == -1) + perror ("GETOPTR"); + + printf ("read(%08x/%08x/%d)\n", count.bytes, count.ptr, + count.blocks); + printf ("write(%08x/%08x/%d)\n", ocount.bytes, ocount.ptr, + ocount.blocks); + + l = count.ptr; + p = ocount.ptr; + + if (l > (sz - ocount.ptr)) + l = sz - ocount.ptr; + + for (i = 0; i < l; i++) + op[i + p] = ip[i]; + } + + if (FD_ISSET (fd, &writes)) + { + printf ("write()\n"); + have_data = 0; + } + } + + exit (0); +} |