~ubuntu-branches/ubuntu/oneiric/oss4/oneiric-proposed

« back to all changes in this revision

Viewing changes to tutorials/sndkit/samples/mmap_test.c

  • Committer: Bazaar Package Importer
  • Author(s): Stefano Rivera
  • Date: 2011-06-16 20:37:48 UTC
  • mfrom: (5.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110616203748-jbrxik6ql33z54co
Tags: 4.2-build2004-1ubuntu1
* Merge from Debian unstable.
  - Supports our current kernel (LP: #746048)
  Remaining changes:
  - debian/oss4-dkms.dkms.in: s/source/build/ in Kernel headers paths.
* ld-as-needed.patch: Re-order CC arguments to enable building with ld
  --as-needed (LP: #770972)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Purpose: A sample program for using mmap()
 
3
 * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
 
4
 *
 
5
 * Description:
 
6
 * This is a simple program which demonstrates use of mmapped DMA buffer
 
7
 * of the sound driver directly from application program.
 
8
 *
 
9
 * This program tries to open a file called "smpl" in the current directory and
 
10
 * play it. If present this file must be a "raw" audio file recorded with
 
11
 * the same sample rate and format as this program uses. There is no checking 
 
12
 * for the format in this program.
 
13
 *
 
14
 * {!notice This program needs some fine tuning. At this moment it doesn't
 
15
 * perform adequate error checkings.}
 
16
 *
 
17
 */
 
18
 
 
19
#define VERBOSE
 
20
 
 
21
#include <stdio.h>
 
22
#include <stdlib.h>
 
23
#include <unistd.h>
 
24
#include <string.h>
 
25
#include <fcntl.h>
 
26
#include <sys/types.h>
 
27
#include <sys/mman.h>
 
28
#include <soundcard.h>
 
29
#include <sys/time.h>
 
30
#include <sys/ioctl.h>
 
31
 
 
32
#ifndef MAP_FILE
 
33
#define MAP_FILE 0
 
34
#endif
 
35
 
 
36
static int sinebuf[48] = {
 
37
  0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
 
38
  28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
 
39
  28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
 
40
  0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
 
41
  -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
 
42
  -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
 
43
};
 
44
static int sinep = 0;
 
45
 
 
46
static void
 
47
produce_output (short *op, int offs, int len)
 
48
{
 
49
  int i;
 
50
 
 
51
  op += offs * 2;
 
52
 
 
53
  for (i = 0; i < len; i++)
 
54
    {
 
55
      int v = sinebuf[sinep];
 
56
      sinep = (sinep + 1) % 48;
 
57
 
 
58
      *op++ = v;                /* Left channel */
 
59
      *op++ = v;                /* Right channel */
 
60
    }
 
61
}
 
62
 
 
63
int
 
64
main (int argc, char *argv[])
 
65
{
 
66
  int fd, tmp;
 
67
  int sz, fsz, num_samples;
 
68
  int caps;
 
69
  struct audio_buf_info info;
 
70
  count_info ci;
 
71
  caddr_t buf;
 
72
  oss_audioinfo ai;
 
73
  unsigned char *op;
 
74
  int device_p, app_p;
 
75
 
 
76
/*
 
77
 * This program must use O_RDWR in some operating systems like Linux.
 
78
 * However in some other operating systems it may need to be O_WRONLY.
 
79
 *
 
80
 * {!code /dev/dsp_mmap} is the default device for mmap applications. 
 
81
 */
 
82
 
 
83
  if ((fd = open ("/dev/dsp_mmap", O_RDWR, 0)) == -1)
 
84
    {
 
85
      perror ("/dev/dsp_mmap");
 
86
      exit (-1);
 
87
    }
 
88
 
 
89
  ai.dev = -1;
 
90
  if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) != -1)
 
91
    {
 
92
      printf ("Using audio device %s (engine %d)\n", ai.name, ai.dev);
 
93
    }
 
94
/*
 
95
 * Disable cooked mode to permit mmap() with some devices.
 
96
 * Don't do any error checking since usually this call will fail.
 
97
 * There is no need to care about the return value.
 
98
 *
 
99
 * Cooked mode must be disabled before setting the sample rate and format.
 
100
 */
 
101
  tmp = 0;
 
102
  ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp);      /* Don't check the error return */
 
103
 
 
104
/*
 
105
 * Set up the sample format. We will use AFMT_S16_LE because it's the most
 
106
 * common audio file format. AFMT_S16_NE is better in programs that
 
107
 * generate the audio signal themselves.
 
108
 */
 
109
 
 
110
  tmp = AFMT_S16_LE;
 
111
  if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1)
 
112
    {
 
113
      perror ("SNDCTL_DSP_SETFMT");
 
114
      exit (-1);
 
115
    }
 
116
 
 
117
/*
 
118
 * Check the format returned by the driver.
 
119
 *
 
120
 * This program will simply refuse to work if it doesn't get the format it
 
121
 * supports. Playing with incompatible formats will cause terrible noise so 
 
122
 * it must be avoided.
 
123
 */
 
124
  if (tmp != AFMT_S16_LE)
 
125
    {
 
126
      fprintf (stderr,
 
127
               "Error: The device doesn't support the requested sample format\n");
 
128
      exit (-1);
 
129
    }
 
130
 
 
131
/*
 
132
 * Set the number of channels and the sample rate. We do not care about the 
 
133
 * returned values. They will just be reported to the user.
 
134
 *
 
135
 * {!notice Real applications must be prepared to support sampling rates
 
136
 * between 8 kHz and 192 kHz (at least). Equally well the number of channels
 
137
 * may be between 1 and 16 (or even more).}
 
138
 *
 
139
 * Two channels and 48 kHz is the most likely combination that works.
 
140
 */
 
141
  tmp = 2;                      /* Stereo */
 
142
  if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1)
 
143
    {
 
144
      perror ("SNDCTL_DSP_CHANNELS");
 
145
      exit (-1);
 
146
    }
 
147
 
 
148
  printf ("Number of channels is %d\n", tmp);
 
149
 
 
150
  tmp = 44100;                  /* 48000 is the most recommended rate */
 
151
  if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1)
 
152
    {
 
153
      perror ("SNDCTL_DSP_SPEED");
 
154
      exit (-1);
 
155
    }
 
156
 
 
157
  printf ("Sample rate set to %d\n", tmp);
 
158
 
 
159
  if (ioctl (fd, SNDCTL_DSP_GETCAPS, &caps) == -1)
 
160
    {
 
161
      perror ("/dev/dsp");
 
162
      fprintf (stderr, "Sorry but your sound driver is too old\n");
 
163
      exit (-1);
 
164
    }
 
165
 
 
166
  if (!(caps & PCM_CAP_TRIGGER))
 
167
    {
 
168
      fprintf (stderr, "Sorry but your soundcard can't do this (TRIGGER)\n");
 
169
      exit (-1);
 
170
    }
 
171
 
 
172
  if (!(caps & PCM_CAP_MMAP))
 
173
    {
 
174
      fprintf (stderr, "Sorry but your soundcard can't do this (MMAP)\n");
 
175
      exit (-1);
 
176
    }
 
177
 
 
178
/*
 
179
 * Compute total size of the buffer. It's important to use this value
 
180
 * in mmap() call.
 
181
 */
 
182
 
 
183
  if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
 
184
    {
 
185
      perror ("GETOSPACE");
 
186
      exit (-1);
 
187
    }
 
188
 
 
189
  sz = info.fragstotal * info.fragsize;
 
190
  fsz = info.fragsize;
 
191
 
 
192
/*
 
193
 * Call mmap().
 
194
 * 
 
195
 * IMPORTANT NOTE!!!!!!!!!!!
 
196
 *
 
197
 * Full duplex audio devices have separate input and output buffers. 
 
198
 * It is not possible to map both of them at the same mmap() call. The buffer
 
199
 * is selected based on the prot argument in the following way:
 
200
 *
 
201
 * - PROT_READ (alone) selects the input buffer.
 
202
 * - PROT_WRITE (alone) selects the output buffer.
 
203
 * - PROT_WRITE|PROT_READ together select the output buffer. This combination
 
204
 *   is required in BSD to make the buffer accessible. With just PROT_WRITE
 
205
 *   every attempt to access the returned buffer will result in segmentation/bus
 
206
 *   error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version
 
207
 *   3.8-beta16 and later (earlier versions don't accept it).
 
208
 *
 
209
 * Non duplex devices have just one buffer. When an application wants to do both
 
210
 * input and output it's recommended that the device is closed and re-opened when
 
211
 * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer
 
212
 * for both input and output (with OSS 3.8-beta16 and later) but the result may be
 
213
 * unpredictable.
 
214
 */
 
215
 
 
216
  if ((buf =
 
217
       mmap (NULL, sz, PROT_WRITE, MAP_FILE | MAP_SHARED, fd,
 
218
             0)) == (caddr_t) - 1)
 
219
    {
 
220
      perror ("mmap (write)");
 
221
      exit (-1);
 
222
    }
 
223
  printf ("mmap (out) returned %08lx\n", (long) buf);
 
224
  op = buf;
 
225
 
 
226
/*
 
227
 * op contains now a pointer to the DMA buffer. Preload some audio data.
 
228
 */
 
229
 
 
230
  num_samples = sz / 4;
 
231
  produce_output ((short *) op, 0, num_samples);
 
232
  app_p = 0;
 
233
 
 
234
/*
 
235
 * Then it's time to start the engine. The driver doesn't allow read() and/or
 
236
 * write() when the buffer is mapped. So the only way to start operation is
 
237
 * to togle device's enable bits. First set them off. Setting them on enables
 
238
 * recording and/or playback.
 
239
 */
 
240
 
 
241
  tmp = 0;
 
242
  ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp);
 
243
  printf ("Trigger set to %08x\n", tmp);
 
244
 
 
245
/*
 
246
 * It might be usefull to write some data to the buffer before starting.
 
247
 */
 
248
 
 
249
  tmp = PCM_ENABLE_OUTPUT;
 
250
  ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp);
 
251
  printf ("Trigger set to %08x\n", tmp);
 
252
 
 
253
/*
 
254
 * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the
 
255
 * buffer status.
 
256
 *
 
257
 * NOTE! The driver empties each buffer fragmen after they have been
 
258
 * played. This prevents looping sound if there are some performance problems
 
259
 * in the application side. For similar reasons it recommended that the
 
260
 * application uses some amout of play ahead. It can rewrite the unplayed
 
261
 * data later if necessary.
 
262
 */
 
263
 
 
264
  while (1)
 
265
    {
 
266
      usleep (50 * 1000);
 
267
 
 
268
      if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ci) == -1)
 
269
        {
 
270
          perror ("SNDCTL_DSP_GETOPTR");
 
271
          exit (-1);
 
272
        }
 
273
 
 
274
      device_p = ci.ptr / 4;
 
275
 
 
276
      if (device_p < app_p)
 
277
        {
 
278
          produce_output ((short *) op, app_p, num_samples - app_p);
 
279
          app_p = 0;
 
280
        }
 
281
 
 
282
      if (device_p > app_p)
 
283
        {
 
284
          produce_output ((short *) op, app_p, device_p - app_p);
 
285
          app_p = device_p;
 
286
        }
 
287
    }
 
288
 
 
289
  exit (0);
 
290
}