2 * This is a simple program which demonstrates use of mmapped DMA buffer
3 * of the sound driver directly from application program.
5 * This sample program works (currently) only with Linux, FreeBSD and BSD/OS
6 * (FreeBSD and BSD/OS require OSS version 3.8-beta16 or later.
8 * Note! Don't use mmapped DMA buffers (direct audio) unless you have
9 * very good reasons to do it. Programs using this feature will not
10 * work with all soundcards. GUS (GF1) is one of them (GUS MAX works).
12 * This program requires version 3.5-beta7 or later of OSS
13 * (3.8-beta16 or later in FreeBSD and BSD/OS).
15 * $FreeBSD: src/sys/i386/isa/sound/mmap_test.c,v 1.6 1999/09/04 15:21:28 peter Exp $
16 * $DragonFly: src/sys/i386/isa/sound/Attic/mmap_test.c,v 1.2 2003/06/17 04:28:38 dillon Exp $
24 #include <sys/types.h>
26 #include <sys/soundcard.h>
32 int fd, sz, fsz, tmp, nfrag;
37 unsigned char data[500000], *dp = data;
44 struct audio_buf_info info;
46 int frag = 0xffff000c; /* Max # fragments of 2^13=8k bytes */
51 if ((fd=open("/dev/dsp", O_RDWR, 0))==-1)
54 * Then setup sampling parameters. Just sampling rate in this case.
58 ioctl(fd, SNDCTL_DSP_SPEED, &tmp);
61 * Load some test data.
65 if ((sd=open("smpl", O_RDONLY, 0))!=-1)
67 sl = read(sd, data, sizeof(data));
68 printf("%d bytes read from file.\n", sl);
73 if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
75 warn("sorry but your sound driver is too old");
80 * Check that the device has capability to do this. Currently just
81 * CS4231 based cards will work.
83 * The application should also check for DSP_CAP_MMAP bit but this
84 * version of driver doesn't have it yet.
86 /* ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */
89 * You need version 3.5-beta7 or later of the sound driver before next
90 * two lines compile. There is no point to modify this program to
91 * compile with older driver versions since they don't have working
94 if (!(caps & DSP_CAP_TRIGGER) ||
95 !(caps & DSP_CAP_MMAP))
96 errx(1, "sorry but your soundcard can't do this");
99 * Select the fragment size. This is propably important only when
100 * the program uses select(). Fragment size defines how often
101 * select call returns.
104 ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
107 * Compute total size of the buffer. It's important to use this value
111 if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
114 sz = info.fragstotal * info.fragsize;
119 * IMPORTANT NOTE!!!!!!!!!!!
121 * Full duplex audio devices have separate input and output buffers.
122 * It is not possible to map both of them at the same mmap() call. The buffer
123 * is selected based on the prot argument in the following way:
125 * - PROT_READ (alone) selects the input buffer.
126 * - PROT_WRITE (alone) selects the output buffer.
127 * - PROT_WRITE|PROT_READ together select the output buffer. This combination
128 * is required in BSD to make the buffer accessible. With just PROT_WRITE
129 * every attempt to access the returned buffer will result in segmentation/bus
130 * error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version
131 * 3.8-beta16 and later (earlier versions don't accept it).
133 * Non duplex devices have just one buffer. When an application wants to do both
134 * input and output it's recommended that the device is closed and re-opened when
135 * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer
136 * for both input and output (with OSS 3.8-beta16 and later) but the result may be
140 if ((buf=mmap(NULL, sz, PROT_WRITE | PROT_READ, MAP_FILE|MAP_SHARED, fd, 0))==(caddr_t)-1)
141 err(1, "mmap (write)");
142 printf("mmap (out) returned %08x\n", buf);
146 * op contains now a pointer to the DMA buffer
150 * Then it's time to start the engine. The driver doesn't allow read() and/or
151 * write() when the buffer is mapped. So the only way to start operation is
152 * to togle device's enable bits. First set them off. Setting them on enables
153 * recording and/or playback.
157 ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
160 * It might be usefull to write some data to the buffer before starting.
163 tmp = PCM_ENABLE_OUTPUT;
164 ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
167 * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the
170 * NOTE! The driver empties each buffer fragmen after they have been
171 * played. This prevents looping sound if there are some performance problems
172 * in the application side. For similar reasons it recommended that the
173 * application uses some amout of play ahead. It can rewrite the unplayed
174 * data later if necessary.
180 struct count_info count;
184 FD_SET(fd, &writeset);
189 select(fd+1, &writeset, &writeset, NULL, NULL);
191 * SNDCTL_DSP_GETOPTR (and GETIPTR as well) return three items. The
192 * bytes field returns number of bytes played since start. It can be used
193 * as a real time clock.
195 * The blocks field returns number of fragment transitions (interrupts) since
196 * previous GETOPTR call. It can be used as a method to detect underrun
199 * The ptr field is the DMA pointer inside the buffer area (in bytes from
200 * the beginning of total buffer area).
203 if (ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1)
205 if (count.ptr < 0 ) count.ptr = 0;
206 nfrag += count.blocks;
211 printf("\rTotal: %09d, Fragment: %03d, Ptr: %06d",
212 count.bytes, nfrag, count.ptr);
217 * Caution! This version doesn't check for bounds of the DMA
218 * memory area. It's possible that the returned pointer value is not aligned
219 * to fragment boundaries. It may be several samples behind the boundary
220 * in case there was extra delay between the actual hardware interrupt and
221 * the time when DSP_GETOPTR was called.
223 * Don't just call memcpy() with length set to 'fragment_size' without
224 * first checking that the transfer really fits to the buffer area.
225 * A mistake of just one byte causes seg fault. It may be easiest just
226 * to align the returned pointer value to fragment boundary before using it.
228 * It would be very good idea to write few extra samples to next fragment
229 * too. Otherwise several (uninitialized) samples from next fragment
230 * will get played before your program gets chance to initialize them.
231 * Take in count the fact thaat there are other processes batling about
232 * the same CPU. This effect is likely to be very annoying if fragment
233 * size is decreased too much.
237 * Just a minor clarification to the above. The following line alings
238 * the pointer to fragment boundaries. Note! Don't trust that fragment
239 * size is always a power of 2. It may not be so in future.
241 count.ptr = ((count.ptr+16)/fsz )*fsz;
243 printf(" memcpy(%6d, %4d)", (dp-data), fsz);
248 * Set few bytes in the beginning of next fragment too.
251 if ((count.ptr+fsz+16) < sz) /* Last fragment? */
255 memcpy(op+count.ptr, dp, (fsz+extra));
257 if (dp > (data+sl-fsz))