Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / sys / i386 / isa / sound / mmap_test.c
1 /*
2  * This is a simple program which demonstrates use of mmapped DMA buffer
3  * of the sound driver directly from application program.
4  *
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.
7  *
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).
11  *
12  * This program requires version 3.5-beta7 or later of OSS
13  * (3.8-beta16 or later in FreeBSD and BSD/OS).
14  *
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 $
17  */
18
19 #include <err.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/mman.h>
26 #include <sys/soundcard.h>
27 #include <sys/time.h>
28
29 int
30 main()
31 {
32         int fd, sz, fsz, tmp, nfrag;
33         int caps;
34
35         int sd, sl=0, sp;
36
37         unsigned char data[500000], *dp = data;
38
39         caddr_t buf;
40         struct timeval tim;
41
42         unsigned char *op;
43         
44         struct audio_buf_info info;
45
46         int frag = 0xffff000c;  /* Max # fragments of 2^13=8k bytes */
47
48         fd_set writeset;
49
50         close(0);
51         if ((fd=open("/dev/dsp", O_RDWR, 0))==-1)
52                 err(1, "/dev/dsp");
53 /*
54  * Then setup sampling parameters. Just sampling rate in this case.
55  */
56
57         tmp = 8000;
58         ioctl(fd, SNDCTL_DSP_SPEED, &tmp);
59
60 /*
61  * Load some test data.
62  */
63
64   sl = sp = 0;
65   if ((sd=open("smpl", O_RDONLY, 0))!=-1)
66   {
67         sl = read(sd, data, sizeof(data));
68         printf("%d bytes read from file.\n", sl);
69         close(sd);
70   }
71   else warn("smpl");
72
73         if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
74         {
75                 warn("sorry but your sound driver is too old");
76                 err(1, "/dev/dsp");
77         }
78
79 /*
80  * Check that the device has capability to do this. Currently just
81  * CS4231 based cards will work.
82  *
83  * The application should also check for DSP_CAP_MMAP bit but this
84  * version of driver doesn't have it yet.
85  */
86 /*      ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */
87
88 /*
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
92  * mmap() support.
93  */
94         if (!(caps & DSP_CAP_TRIGGER) ||
95             !(caps & DSP_CAP_MMAP))
96                 errx(1, "sorry but your soundcard can't do this");
97
98 /*
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.
102  */
103
104         ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
105
106 /*
107  * Compute total size of the buffer. It's important to use this value
108  * in mmap() call.
109  */
110
111         if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
112                 err(1, "GETOSPACE");
113
114         sz = info.fragstotal * info.fragsize;
115         fsz = info.fragsize;
116 /*
117  * Call mmap().
118  * 
119  * IMPORTANT NOTE!!!!!!!!!!!
120  *
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:
124  *
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).
132  *
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
137  * unpredictable.
138  */
139
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);
143         op=buf;
144
145 /*
146  * op contains now a pointer to the DMA buffer
147  */
148
149 /*
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.
154  */
155
156         tmp = 0;
157         ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);  
158
159 /*
160  * It might be usefull to write some data to the buffer before starting.
161  */
162
163         tmp = PCM_ENABLE_OUTPUT;
164         ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp);
165
166 /*
167  * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the
168  * buffer status.
169  *
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.
175  */
176
177         nfrag = 0;
178         while (1)
179         {
180                 struct count_info count;
181                 int extra;
182
183                 FD_ZERO(&writeset);
184                 FD_SET(fd, &writeset);
185
186                 tim.tv_sec = 10;
187                 tim.tv_usec= 0;
188
189                 select(fd+1, &writeset, &writeset, NULL, NULL);
190 /*
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.
194  *
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 
197  * situations.
198  *
199  * The ptr field is the DMA pointer inside the buffer area (in bytes from
200  * the beginning of total buffer area).
201  */
202
203                 if (ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1)
204                         err(1, "GETOPTR");
205                 if (count.ptr < 0 ) count.ptr = 0;
206                 nfrag += count.blocks;
207
208
209 #ifdef VERBOSE
210
211                 printf("\rTotal: %09d, Fragment: %03d, Ptr: %06d",
212                         count.bytes, nfrag, count.ptr);
213                 fflush(stdout);
214 #endif
215
216 /*
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.
222  *
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.
227  *
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.
234  */
235
236 /*
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.
240  */
241                 count.ptr = ((count.ptr+16)/fsz )*fsz;
242 #ifdef VERBOSE
243                 printf(" memcpy(%6d, %4d)", (dp-data), fsz);
244                 fflush(stdout);
245 #endif
246
247 /*
248  * Set few bytes in the beginning of next fragment too.
249  */
250
251                 if ((count.ptr+fsz+16) < sz)    /* Last fragment? */
252                    extra = 16;
253                 else
254                    extra = 0;
255                 memcpy(op+count.ptr, dp, (fsz+extra));
256                 dp += fsz;
257                 if (dp > (data+sl-fsz))
258                    dp = data;
259
260         }
261
262         exit(0);
263 }