-/*\r
- * This is a simple program which demonstrates use of mmapped DMA buffer\r
- * of the sound driver directly from application program.\r
- *\r
- * This sample program works (currently) only with Linux, FreeBSD and BSD/OS\r
- * (FreeBSD and BSD/OS require OSS version 3.8-beta16 or later.\r
- *\r
- * Note! Don't use mmapped DMA buffers (direct audio) unless you have\r
- * very good reasons to do it. Programs using this feature will not\r
- * work with all soundcards. GUS (GF1) is one of them (GUS MAX works).\r
- *\r
- * This program requires version 3.5-beta7 or later of OSS\r
- * (3.8-beta16 or later in FreeBSD and BSD/OS).\r
- */\r
-\r
-#include <stdio.h>\r
-#include <unistd.h>\r
-#include <fcntl.h>\r
-#include <sys/types.h>\r
-#include <sys/mman.h>\r
-#include <sys/soundcard.h>\r
-#include <sys/time.h>\r
-\r
-main()\r
-{\r
- int fd, sz, fsz, i, tmp, n, l, have_data=0, nfrag;\r
- int caps, idx;\r
-\r
- int sd, sl=0, sp;\r
-\r
- unsigned char data[500000], *dp = data;\r
-\r
- struct buffmem_desc imemd, omemd;\r
- caddr_t buf;\r
- struct timeval tim;\r
-\r
- unsigned char *op;\r
- \r
- struct audio_buf_info info;\r
-\r
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/soundcard.h>
+#include <sys/time.h>
+#include <oss-redir.h>
+
+int main(void)
+{
+ int fd, sz, fsz, i, tmp, n, l, have_data=0, nfrag;
+ int caps, idx;
+
+ int sd, sl=0, sp;
+
+ unsigned char data[500000], *dp = data;
+
+ struct buffmem_desc imemd, omemd;
+ caddr_t buf;
+ struct timeval tim;
+
+ unsigned char *op;
+
+ struct audio_buf_info info;
+
int frag = 0xffff000c; /* Max # periods of 2^13=8k bytes */\r
-\r
- fd_set writeset;\r
-\r
- close(0);\r
- if ((fd=oss_pcm_open("/dev/dsp", O_RDWR, 0))==-1)\r
- {\r
- perror("/dev/dsp");\r
- exit(-1);\r
- }\r
-/*\r
- * Then setup sampling parameters. Just sampling rate in this case.\r
- */\r
-\r
- tmp = 48000;\r
- oss_pcm_ioctl(fd, SNDCTL_DSP_SPEED, &tmp);\r
- printf("Speed set to %d\n", tmp);\r
-\r
-/*\r
- * Load some test data.\r
- */\r
-\r
- sl = sp = 0;\r
- if ((sd=open("smpl", O_RDONLY, 0))!=-1)\r
- {\r
- sl = read(sd, data, sizeof(data));\r
- printf("%d bytes read from file.\n", sl);\r
- close(sd);\r
- }\r
- else perror("smpl");\r
-\r
- if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1)\r
- {\r
- perror("/dev/dsp");\r
- fprintf(stderr, "Sorry but your sound driver is too old\n");\r
- exit(-1);\r
- }\r
-\r
-/*\r
- * Check that the device has capability to do this. Currently just\r
- * CS4231 based cards will work.\r
- *\r
- * The application should also check for DSP_CAP_MMAP bit but this\r
- * version of driver doesn't have it yet.\r
- */\r
-/* oss_pcm_ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */\r
-\r
-/*\r
- * You need version 3.5-beta7 or later of the sound driver before next\r
- * two lines compile. There is no point to modify this program to\r
- * compile with older driver versions since they don't have working\r
- * mmap() support.\r
- */\r
- if (!(caps & DSP_CAP_TRIGGER) ||\r
- !(caps & DSP_CAP_MMAP))\r
- {\r
- fprintf(stderr, "Sorry but your soundcard can't do this\n");\r
- exit(-1);\r
- }\r
-\r
-/*\r
- * Select the period size. This is propably important only when\r
- * the program uses select(). Period size defines how often\r
- * select call returns.\r
- */\r
-\r
- oss_pcm_ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);\r
-\r
-/*\r
- * Compute total size of the buffer. It's important to use this value\r
- * in mmap() call.\r
- */\r
-\r
- if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1)\r
- {\r
- perror("GETOSPACE");\r
- exit(-1);\r
- }\r
-\r
- sz = info.fragstotal * info.fragsize;\r
- fsz = info.fragsize;\r
- printf( "info.fragstotal = %i\n", info.fragstotal );
- printf( "info.fragsize = %i\n", info.fragsize );
- printf( "info.periods = %i\n", info.fragments );
- printf( "info.bytes = %i\n", info.bytes );
-\r
-/*\r
- * Call mmap().\r
- * \r
- * IMPORTANT NOTE!!!!!!!!!!!\r
- *\r
- * Full duplex audio devices have separate input and output buffers. \r
- * It is not possible to map both of them at the same mmap() call. The buffer\r
- * is selected based on the prot argument in the following way:\r
- *\r
- * - PROT_READ (alone) selects the input buffer.\r
- * - PROT_WRITE (alone) selects the output buffer.\r
- * - PROT_WRITE|PROT_READ together select the output buffer. This combination\r
- * is required in BSD to make the buffer accessible. With just PROT_WRITE\r
- * every attempt to access the returned buffer will result in segmentation/bus\r
- * error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version\r
- * 3.8-beta16 and later (earlier versions don't accept it).\r
- *\r
- * Non duplex devices have just one buffer. When an application wants to do both\r
- * input and output it's recommended that the device is closed and re-opened when\r
- * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer\r
- * for both input and output (with OSS 3.8-beta16 and later) but the result may be\r
- * unpredictable.\r
- */\r
-\r
-#if 1
- if ((buf=mmap(NULL, sz, PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0))==(caddr_t)-1)\r
- {\r
- perror("mmap (write)");\r
- exit(-1);\r
- }\r
- printf("mmap (out) returned %08x\n", buf);\r
-#else
- buf=data;
+
+ fd_set writeset;
+
+ if ((fd=oss_pcm_open("/dev/dsp", O_RDWR, 0))==-1) {
+ perror("/dev/dsp");
+ exit(-1);
+ }
+ tmp = 48000;
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0) {
+ perror("SNDCTL_DSP_SPEED\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("Speed set to %d\n", tmp);
+
+ sl = sp = 0;
+ if ((sd=open("smpl", O_RDONLY, 0)) >= 0) {
+ sl = read(sd, data, sizeof(data));
+ printf("%d bytes read from file.\n", sl);
+ close(sd);
+ } else
+ perror("smpl");
+
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
+ perror("/dev/dsp");
+ fprintf(stderr, "Sorry but your sound driver is too old\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!(caps & DSP_CAP_TRIGGER) ||
+ !(caps & DSP_CAP_MMAP))
+ {
+ fprintf(stderr, "Sorry but your soundcard can't do this\n");
+ exit(EXIT_FAILURE);
+ }
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0)
+ perror("SNDCTL_DSP_SETFRAGMENT");
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
+ perror("SNDCTL_DSP_GETOSPACE");
+ exit(EXIT_FAILURE);
+ }
+ sz = info.fragstotal * info.fragsize;
+ fsz = info.fragsize;
+ printf("info.fragstotal = %i\n", info.fragstotal);
+ printf("info.fragsize = %i\n", info.fragsize);
+ printf("info.periods = %i\n", info.fragments);
+ printf("info.bytes = %i\n", info.bytes);
+ if ((buf=mmap(NULL, sz, PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0))==MAP_FAILED) {
+ perror("mmap (write)");
+ exit(-1);
+ }
+ printf("mmap (out) returned %08x\n", buf);
+ op=buf;
+
+ tmp = 0;
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) {
+ perror("SNDCTL_DSP_SETTRIGGER");
+ exit(EXIT_FAILURE);
+ }
+ printf("Trigger set to %08x\n", tmp);
+
+ tmp = PCM_ENABLE_OUTPUT;
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) {
+ perror("SNDCTL_DSP_SETTRIGGER");
+ exit(EXIT_FAILURE);
+ }
+ printf("Trigger set to %08x\n", tmp);
+
+ nfrag = 0;
+ for (idx=0; idx<40; idx++) {
+ struct count_info count;
+ int p, l, extra;
+
+ FD_ZERO(&writeset);
+ FD_SET(fd, &writeset);
+
+ tim.tv_sec = 10;
+ tim.tv_usec= 0;
+
+ select(fd+1, NULL, &writeset, NULL, NULL);
+ if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETOPTR, &count) < 0) {
+ perror("GETOPTR");
+ exit(EXIT_FAILURE);
+ }
+ nfrag += count.blocks;
+#ifdef VERBOSE
+ printf("Total: %09d, Period: %03d, Ptr: %06d", count.bytes, nfrag, count.ptr);\r
+ fflush(stdout);
+#endif
+ count.ptr = (count.ptr/fsz)*fsz;
+
+#ifdef VERBOSE
+ printf(" memcpy(%6d, %4d)\n", (dp-data), fsz);
+ fflush(stdout);
#endif
- op=buf;\r
-\r
-/*\r
- * op contains now a pointer to the DMA buffer\r
- */\r
-\r
-/*\r
- * Then it's time to start the engine. The driver doesn't allow read() and/or\r
- * write() when the buffer is mapped. So the only way to start operation is\r
- * to togle device's enable bits. First set them off. Setting them on enables\r
- * recording and/or playback.\r
- */\r
-\r
- tmp = 0;\r
- oss_pcm_ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);\r
- printf("Trigger set to %08x\n", tmp);\r
-\r
-/*\r
- * It might be usefull to write some data to the buffer before starting.\r
- */\r
-\r
- tmp = PCM_ENABLE_OUTPUT;\r
- oss_pcm_ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);\r
- printf("Trigger set to %08x\n", tmp);\r
-\r
-/*\r
- * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the\r
- * buffer status.\r
- *\r
- * NOTE! The driver empties each buffer fragmen after they have been\r
- * played. This prevents looping sound if there are some performance problems\r
- * in the application side. For similar reasons it recommended that the\r
- * application uses some amout of play ahead. It can rewrite the unplayed\r
- * data later if necessary.\r
- */\r
-\r
- nfrag = 0;\r
- for (idx=0; idx<40; idx++)\r
- {\r
- struct count_info count;\r
- int p, l, extra;\r
-\r
- FD_ZERO(&writeset);\r
- FD_SET(fd, &writeset);\r
-\r
- tim.tv_sec = 10;\r
- tim.tv_usec= 0;\r
-\r
- select(fd+1, NULL, &writeset, NULL, NULL);\r
-/*\r
- * SNDCTL_DSP_GETOPTR (and GETIPTR as well) return three items. The\r
- * bytes field returns number of bytes played since start. It can be used\r
- * as a real time clock.\r
- *\r
- * The blocks field returns number of period transitions (interrupts) since\r
- * previous GETOPTR call. It can be used as a method to detect underrun \r
- * situations.\r
- *\r
- * The ptr field is the DMA pointer inside the buffer area (in bytes from\r
- * the beginning of total buffer area).\r
- */\r
-\r
- if (oss_pcm_ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1)\r
- {\r
- perror("GETOPTR");\r
- exit(-1);\r
- }\r
-\r
- nfrag += count.blocks;\r
-\r
-#ifdef VERBOSE\r
-\r
- printf("Total: %09d, Period: %03d, Ptr: %06d",\r
- count.bytes, nfrag, count.ptr);\r
- fflush(stdout);\r
-#endif\r
-\r
-/*\r
- * Caution! This version doesn't check for bounds of the DMA\r
- * memory area. It's possible that the returned pointer value is not aligned\r
- * to period boundaries. It may be several samples behind the boundary\r
- * in case there was extra delay between the actual hardware interrupt and\r
- * the time when DSP_GETOPTR was called.\r
- *\r
- * Don't just call memcpy() with length set to 'period_size' without\r
- * first checking that the transfer really fits to the buffer area.\r
- * A mistake of just one byte causes seg fault. It may be easiest just\r
- * to align the returned pointer value to period boundary before using it.\r
- *\r
- * It would be very good idea to write few extra samples to next period\r
- * too. Otherwise several (uninitialized) samples from next period\r
- * will get played before your program gets chance to initialize them.\r
- * Take in count the fact thaat there are other processes batling about\r
- * the same CPU. This effect is likely to be very annoying if period\r
- * size is decreased too much.\r
- */\r
-\r
-/*\r
- * Just a minor clarification to the above. The following line alings\r
- * the pointer to period boundaries. Note! Don't trust that period\r
- * size is always a power of 2. It may not be so in future.\r
- */\r
- count.ptr = (count.ptr/fsz)*fsz;\r
-\r
-#ifdef VERBOSE\r
- printf(" memcpy(%6d, %4d)\n", (dp-data), fsz);\r
- fflush(stdout);\r
-#endif\r
-\r
-/*\r
+
+/*
* Set few bytes in the beginning of next period too.\r
- */\r
- if ((count.ptr+fsz+16) < sz) /* Last period? */\r
- extra = 16;\r
- else\r
- extra = 0;\r
-\r
- memcpy(op+count.ptr, dp, fsz+extra);\r
- \r
- dp += fsz;\r
- if (dp > (data+sl-fsz))\r
- dp = data;\r
- }\r
-\r
- close(fd);
+ */
+ if ((count.ptr+fsz+16) < sz) /* Last period? */
+ extra = 16;
+ else
+ extra = 0;
+
+ memcpy(op+count.ptr, dp, fsz+extra);
- printf( ">>>> open (2)\n" ); fflush( stdout );
+ dp += fsz;
+ if (dp > (data+sl-fsz))
+ dp = data;
+ }
- if ((fd=oss_pcm_open("/dev/dsp", O_RDWR, 0))==-1)\r
- {\r
- perror("/dev/dsp");\r
- exit(-1);\r
- }\r
- close( fd );
+ close(fd);
+
+ printf("second open test:\n");
+ if ((fd=oss_pcm_open("/dev/dsp", O_RDWR, 0))==-1) {
+ perror("/dev/dsp");
+ exit(-1);
+ }
+ close(fd);
+ printf("second open test passed\n");
- exit(0);\r
-}\r
+ exit(0);
+}