~hilaire-fernandes/drgeo/trunk

« back to all changes in this revision

Viewing changes to VMs/iPad/source/unix/vm-sound-OSS/sqUnixSoundOSS.c

  • Committer: Hilaire Fernandes
  • Date: 2012-01-27 21:15:40 UTC
  • Revision ID: hilaire.fernandes@gmail.com-20120127211540-912spf97bhpx6mve
Initial additions

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sqUnixSoundOSS.c -- sound module for Open Sound System
 
2
 *
 
3
 * Author: Ian.Piumarta@squeakland.org
 
4
 * 
 
5
 * Last edited: 2008-04-21 14:51:45 by piumarta on emilia
 
6
 *
 
7
 *   Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors
 
8
 *                              listed elsewhere in this file.
 
9
 *   All rights reserved.
 
10
 *   
 
11
 *   This file is part of Unix Squeak.
 
12
 * 
 
13
 *   Permission is hereby granted, free of charge, to any person obtaining a
 
14
 *   copy of this software and associated documentation files (the "Software"),
 
15
 *   to deal in the Software without restriction, including without limitation
 
16
 *   the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
17
 *   and/or sell copies of the Software, and to permit persons to whom the
 
18
 *   Software is furnished to do so, subject to the following conditions:
 
19
 * 
 
20
 *   The above copyright notice and this permission notice shall be included in
 
21
 *   all copies or substantial portions of the Software.
 
22
 * 
 
23
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
24
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
25
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
26
 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
27
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
28
 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
29
 *   DEALINGS IN THE SOFTWARE.
 
30
 */
 
31
 
 
32
#include "sq.h"
 
33
 
 
34
/* The SoundPlayer playLoop does not attempt to play sounds unless at
 
35
 * least 100 frames of output space are available.  select() says
 
36
 * output space is available when one byte can be written.  Net
 
37
 * result: Squeak sits at 100% CPU thrashing between playSema wait and
 
38
 * select() signaling the playSema.  OSS does not provide any means to
 
39
 * increase the output low water mark from one byte (to 100 frames, or
 
40
 * even an entire fragment) either.  Disabling the Semaphore entirely
 
41
 * increases audio performace noticably and Squeak idles for almost
 
42
 * all of the time (consuming < 1% CPU) when only playing sound.
 
43
 * 
 
44
 * If you turn the play Semaphore back on then you should at least
 
45
 * consider also turning on `soundStopWhenDone' in the image
 
46
 * preferences, otherwise Squeak WILL eat ALL of your CPU from the
 
47
 * moment you play the first sound.
 
48
 */
 
49
#define USE_PLAY_SEMAPHORE
 
50
 
 
51
#undef  DEBUG
 
52
#undef  NDEBUG
 
53
 
 
54
#if 0
 
55
#define TEST_FMT        AFMT_U8         /* forced h/w format for input/output */
 
56
#define TEST_CHANS      2               /* forced h/w channels for input/output */
 
57
#endif
 
58
 
 
59
#include "sqaio.h"
 
60
 
 
61
#include <stdio.h>
 
62
#include <stdlib.h>
 
63
#include <unistd.h>
 
64
#include <string.h>
 
65
#include <fcntl.h>
 
66
#include <sys/ioctl.h>
 
67
#include <sys/soundcard.h>
 
68
#include <assert.h>
 
69
#include <errno.h>
 
70
 
 
71
 
 
72
#ifdef WORDS_BIGENDIAN
 
73
# define AFMT_S16_HE    AFMT_S16_BE
 
74
# define AFMT_U16_HE    AFMT_U16_BE
 
75
# define AFMT_S16_RE    AFMT_S16_LE
 
76
# define AFMT_U16_RE    AFMT_U16_LE
 
77
#else
 
78
# define AFMT_S16_HE    AFMT_S16_LE
 
79
# define AFMT_U16_HE    AFMT_U16_LE
 
80
# define AFMT_S16_RE    AFMT_S16_BE
 
81
# define AFMT_U16_RE    AFMT_U16_BE
 
82
#endif
 
83
 
 
84
#define ERROR(MSG) (                                            \
 
85
  fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, MSG)       \
 
86
)
 
87
 
 
88
#define PERROR(MSG) (                                           \
 
89
  fprintf(stderr, "%s:%d: ", __FILE__, __LINE__),               \
 
90
  perror(MSG)                                                   \
 
91
)
 
92
 
 
93
#define IOCTL(FD,CMD,ARG)                                       \
 
94
  ((ioctl(FD, CMD, ARG) == -1) ? (PERROR(#CMD), -1) : 0)
 
95
 
 
96
#define min(A,B)        (((A)<(B)) ? (A) : (B))
 
97
 
 
98
struct dsp;
 
99
 
 
100
typedef int (*reader)(struct dsp *dsp, void *buffer, int nFrames);
 
101
typedef int (*writer)(struct dsp *dsp, void *buffer, int nFrames);
 
102
 
 
103
#ifdef DEBUG
 
104
# define PRINTF(ARGS) ((printf ARGS), fflush(stdout))
 
105
  static char *afmtName(int i);
 
106
  static char *capName(int i);
 
107
  static char *rdName(reader rd);
 
108
  static char *wrName(writer wr);
 
109
  static char *devName(int dev);
 
110
#else
 
111
# define PRINTF(ARGS)
 
112
#endif
 
113
 
 
114
 
 
115
struct format
 
116
{
 
117
  int   format;         /* internal format */
 
118
# define FMT_S   0x0000 /* default: 16-bit, signed, native order, stereo */
 
119
# define FMT_U   0x0001 /* unsigned */
 
120
# define FMT_E   0x0002 /* byte-swapped */
 
121
# define FMT_8   0x0004 /* 8-bit */
 
122
# define FMT_M   0x0008 /* mono */
 
123
# define FMT_MAX (FMT_8 | FMT_U)
 
124
  int   channels;       /* number of channels */
 
125
  int   rate;           /* frames per second */
 
126
  int   bpf;            /* bytes per frame */
 
127
  int   fragSize;       /* bytes per fragment */
 
128
};
 
129
 
 
130
struct dsp
 
131
{
 
132
  char  *path;
 
133
  int    fd;
 
134
  int    caps;          /* capabilities */
 
135
  int    fmts;          /* driver formats */
 
136
  struct format hw;     /* h/w frame format */
 
137
  struct format sq;     /* Squeak frame format (FMT_S or FMT_M) */
 
138
  reader read;          /* input function */
 
139
  writer write;         /* output function */
 
140
  int    semaphore;     /* read/write semaphore */
 
141
  int    running;
 
142
};
 
143
 
 
144
 
 
145
struct dsp dev_dsp1 = { "/dev/dsp1", -1 };
 
146
struct dsp dev_dsp  = { "/dev/dsp",  -1 };
 
147
 
 
148
static struct dsp *in=  0;
 
149
static struct dsp *out= 0;
 
150
 
 
151
static int noSoundMixer= 0;
 
152
 
 
153
typedef   signed char  sbyte;
 
154
typedef unsigned char  ubyte;
 
155
typedef   signed short sword;
 
156
typedef unsigned short uword;
 
157
 
 
158
#define CVI     sample= *in++;
 
159
#define CVIM    CVI; sample= (sample / 2) + (*in++ / 2);
 
160
#define CVU     sample^= 0x8000;
 
161
#define CVE     sample= ((sample >> 8) & 0xff) | (sample << 8);
 
162
#define CVB     sample>>= 8;
 
163
#define CVW     sample<<= 8;
 
164
#define CVO     *out++= sample;
 
165
#define CVOS    CVO CVO
 
166
 
 
167
#define CV__U       CVU
 
168
#define CV_E_           CVE
 
169
#define CV_EU       CVU CVE
 
170
#define CVB__               CVB
 
171
#define CVB_U       CVU     CVB
 
172
#define CVW__   CVW
 
173
#define CVW_U   CVW CVU
 
174
 
 
175
static int output(struct dsp *dsp, void *buf, int frames)
 
176
{
 
177
  int m= frames * dsp->hw.bpf;
 
178
  /* try to ensure that the entire buffer is written.  (the write
 
179
     should complete fully in all cases, but some alsa drivers
 
180
     apparently don't behave quite right in write() and/or
 
181
     select().) */
 
182
  while (m)
 
183
    {
 
184
      int n= write(dsp->fd, buf, m);
 
185
      if (n < 0)
 
186
        {
 
187
          if (errno != EAGAIN)
 
188
            {
 
189
              fprintf(stderr, "sound: ");
 
190
              PERROR(dsp->path);
 
191
              return 0;
 
192
            }
 
193
        }
 
194
      else
 
195
        m -= n;
 
196
    }
 
197
  return frames;
 
198
}
 
199
 
 
200
#define outputFn(TYPE, CVT) (struct dsp *dsp, void *buf, int frames)    \
 
201
{                                                                       \
 
202
  TYPE *cvt= (TYPE *)alloca(dsp->hw.bpf * frames);                      \
 
203
  register short *in= (short *)buf;                                     \
 
204
  register TYPE *out= cvt;                                              \
 
205
  register int n= frames;                                               \
 
206
  while (n--) { register short sample; CVT; }                           \
 
207
  return output(dsp, cvt, frames);                                      \
 
208
}
 
209
 
 
210
#define    wrMM___ output
 
211
static int wrMM__U outputFn(uword, CVI  CV__U CVO);
 
212
static int wrMM_E_ outputFn(sword, CVI  CV_E_ CVO);
 
213
static int wrMM_EU outputFn(uword, CVI  CV_EU CVO);
 
214
static int wrMM8__ outputFn(sbyte, CVI  CVB__ CVO);
 
215
static int wrMM8_U outputFn(ubyte, CVI  CVB_U CVO);
 
216
static int wrMS___ outputFn(sword, CVI        CVOS);
 
217
static int wrMS__U outputFn(uword, CVI  CV__U CVOS);
 
218
static int wrMS_E_ outputFn(sword, CVI  CV_E_ CVOS);
 
219
static int wrMS_EU outputFn(uword, CVI  CV_EU CVOS);
 
220
static int wrMS8__ outputFn(sbyte, CVI  CVB__ CVOS);
 
221
static int wrMS8_U outputFn(ubyte, CVI  CVB_U CVOS);
 
222
static int wrSM___ outputFn(sword, CVIM       CVO);
 
223
static int wrSM__U outputFn(uword, CVIM CV__U CVO);
 
224
static int wrSM_E_ outputFn(sword, CVIM CV_E_ CVO);
 
225
static int wrSM_EU outputFn(uword, CVIM CV_EU CVO);
 
226
static int wrSM8__ outputFn(sbyte, CVIM CVB__ CVO);
 
227
static int wrSM8_U outputFn(ubyte, CVIM CVB_U CVO);
 
228
#define    wrSS___ output
 
229
static int wrSS__U outputFn(uword, CVI  CV__U CVO  CVI CV__U CVO);
 
230
static int wrSS_E_ outputFn(sword, CVI  CV_E_ CVO  CVI CV_E_ CVO);
 
231
static int wrSS_EU outputFn(uword, CVI  CV_EU CVO  CVI CV_EU CVO);
 
232
static int wrSS8__ outputFn(sbyte, CVI  CVB__ CVO  CVI CVB__ CVO);
 
233
static int wrSS8_U outputFn(ubyte, CVI  CVB_U CVO  CVI CVB_U CVO);
 
234
 
 
235
#undef outputFn
 
236
 
 
237
static writer writers[4][FMT_MAX+1]= {
 
238
  { wrSS___, wrSS__U, wrSS_E_, wrSS_EU, wrSS8__, wrSS8_U },
 
239
  { wrSM___, wrSM__U, wrSM_E_, wrSM_EU, wrSM8__, wrSM8_U },
 
240
  { wrMS___, wrMS__U, wrMS_E_, wrMS_EU, wrMS8__, wrMS8_U },
 
241
  { wrMM___, wrMM__U, wrMM_E_, wrMM_EU, wrMM8__, wrMM8_U }
 
242
};
 
243
 
 
244
static int input(struct dsp *dsp, void *buf, int frames)
 
245
{
 
246
  int m= frames * dsp->hw.bpf;
 
247
  int n= read(dsp->fd, buf, m);
 
248
  if (n < 0)
 
249
    {
 
250
      fprintf(stderr, "sound: ");
 
251
      PERROR(dsp->path);
 
252
      return 0;
 
253
    }
 
254
  return n / dsp->hw.bpf;
 
255
}
 
256
 
 
257
#define inputFn(TYPE, CVT) (struct dsp *dsp, void *buf, register int frames)    \
 
258
{                                                                               \
 
259
  register short *out= (short *)buf;                                            \
 
260
  register TYPE *in= (TYPE *)alloca(dsp->hw.bpf * frames);                      \
 
261
  frames= input(dsp, (void *)in, frames);                                       \
 
262
  {                                                                             \
 
263
    register int n= frames;                                                     \
 
264
    while (n--) { register short sample; CVT; }                                 \
 
265
  }                                                                             \
 
266
  return frames;                                                                \
 
267
}
 
268
 
 
269
#define    rdMM___ input
 
270
static int rdMM__U inputFn(uword, CVI  CV__U CVO);
 
271
static int rdMM_E_ inputFn(sword, CVI  CV_E_ CVO);
 
272
static int rdMM_EU inputFn(uword, CVI  CV_EU CVO);
 
273
static int rdMM8__ inputFn(sbyte, CVI  CVW__ CVO);
 
274
static int rdMM8_U inputFn(ubyte, CVI  CVW_U CVO);
 
275
static int rdMS___ inputFn(sword, CVI         CVOS);
 
276
static int rdMS__U inputFn(uword, CVI  CV__U CVOS);
 
277
static int rdMS_E_ inputFn(sword, CVI  CV_E_ CVOS);
 
278
static int rdMS_EU inputFn(uword, CVI  CV_EU CVOS);
 
279
static int rdMS8__ inputFn(sbyte, CVI  CVW__ CVOS);
 
280
static int rdMS8_U inputFn(ubyte, CVI  CVW_U CVOS);
 
281
static int rdSM___ inputFn(sword, CVIM       CVO);
 
282
static int rdSM__U inputFn(uword, CVIM CV__U CVO);
 
283
static int rdSM_E_ inputFn(sword, CVIM CV_E_ CVO);
 
284
static int rdSM_EU inputFn(uword, CVIM CV_EU CVO);
 
285
static int rdSM8__ inputFn(sbyte, CVIM CVW__ CVO);
 
286
static int rdSM8_U inputFn(ubyte, CVIM CVW_U CVO);
 
287
#define    rdSS___ input
 
288
static int rdSS__U inputFn(uword, CVI  CV__U CVO  CVI CV__U CVO);
 
289
static int rdSS_E_ inputFn(sword, CVI  CV_E_ CVO  CVI CV_E_ CVO);
 
290
static int rdSS_EU inputFn(uword, CVI  CV_EU CVO  CVI CV_EU CVO);
 
291
static int rdSS8__ inputFn(sbyte, CVI  CVW__ CVO  CVI CVW__ CVO);
 
292
static int rdSS8_U inputFn(ubyte, CVI  CVW_U CVO  CVI CVW_U CVO);
 
293
 
 
294
static reader readers[4][FMT_MAX+1]= {
 
295
  { rdSS___, rdSS__U, rdSS_E_, rdSS_EU, rdSS8__, rdSS8_U },
 
296
  { rdSM___, rdSM__U, rdSM_E_, rdSM_EU, rdSM8__, rdSM8_U },
 
297
  { rdMS___, rdMS__U, rdMS_E_, rdMS_EU, rdMS8__, rdMS8_U },
 
298
  { rdMM___, rdMM__U, rdMM_E_, rdMM_EU, rdMM8__, rdMM8_U }
 
299
};
 
300
 
 
301
/* NOTE: The vast majority of the above functions will almost
 
302
   certainly never be used since OSS appears to guarantee support for
 
303
   at least one of AFMT_S16_NE, AFMT_S16_LE and AFMT_U8 on all cards
 
304
   regardless of the h/w capabilities [OSS Programmer's Guide,
 
305
   revision 1.11, 7 Nov 2000, page 32].  Nonetheless, we're going to
 
306
   implement every possible conversion explicitly since the first
 
307
   person with a weirdo card/driver that doesn't work as advertised is
 
308
   bound to scream and moan (loudly and in public).  The result is
 
309
   that this file is much larger and more complicated than necessary,
 
310
   but that's the price you pay for my avoiding any possibility of
 
311
   disproportionate criticism. */
 
312
 
 
313
 
 
314
/* Close dsp.
 
315
 */
 
316
static void dspClose(struct dsp *dsp)
 
317
{
 
318
  assert(dsp->fd >= 0);
 
319
  if (dsp->semaphore > 0)
 
320
    {
 
321
      aioDisable(dsp->fd);
 
322
      dsp->semaphore= 0;
 
323
      PRINTF(("sound: %s: aio disabled\n", dsp->path));
 
324
    }
 
325
  close(dsp->fd);
 
326
  dsp->fd= -1;
 
327
}
 
328
 
 
329
 
 
330
/* Open dsp for playback.  If the dsp is capable of full-duplex
 
331
 * operation and recording is currently in progress with a compatible
 
332
 * sample format then attempt to open the dsp in parallel.  (For this
 
333
 * to even be possible it is first necessary to enable the
 
334
 * `canRecordWhilePlaying' preference in the image which will
 
335
 * otherwise ensure mutual exclusion beteen sound input and output.)
 
336
 * If full-duplex is not supported (or if the current sample format is
 
337
 * incompatible) then stop recording before opening for playback.
 
338
 * Answer whether we successfully opened the device for output.
 
339
 * 
 
340
 * NOTE: for full-duplex operation we simply attempt to open the
 
341
 * device twice (O_RDONLY and O_WRONLY) which is NOT supported by all
 
342
 * versions of OSS [1].  If this fails we should try at least two
 
343
 * alternative approaches:
 
344
 * 1) close and reopen /dev/dsp with O_RDWR, and then share it between
 
345
 *    recording and playback; or, if pressed,
 
346
 * 2) find and open a shadow device /dev/dspN (N > 0).
 
347
 * Unfortunately I'm feeling too lazy to implement all that nonsense
 
348
 * today.
 
349
 * 
 
350
 * [1] http://www.opensound.com/readme/README.fullduplex.html
 
351
 */
 
352
static struct dsp *dspOpen(struct dsp *dsp, int mode)
 
353
{
 
354
  assert(dsp);
 
355
  assert(dsp->fd < 0);
 
356
  if ((dsp->fd= open(dsp->path, mode, 0)) < 0)
 
357
    {
 
358
      if (dsp == &dev_dsp)
 
359
        {
 
360
          fprintf(stderr, "sound: ");
 
361
          perror(dsp->path);
 
362
        }
 
363
      return 0;
 
364
    }
 
365
  PRINTF(("sound: %s: opened with mode %d\n", dsp->path, mode));
 
366
 
 
367
  dsp->semaphore= 0;
 
368
 
 
369
  ioctl(dsp->fd, SNDCTL_DSP_SETDUPLEX,  0);     /* allow this to fail silently */
 
370
  if ((  IOCTL(dsp->fd, SNDCTL_DSP_GETCAPS, &dsp->caps))
 
371
      || IOCTL(dsp->fd, SNDCTL_DSP_GETFMTS, &dsp->fmts))
 
372
    {
 
373
      /* driver is hosed */
 
374
      fprintf(stderr, "sound: %s: could not read driver capabilities\n", dsp->path);
 
375
      dspClose(dsp);
 
376
      return 0;
 
377
    }
 
378
 
 
379
#ifdef TEST_FMT
 
380
  dsp->fmts = TEST_FMT;
 
381
#endif
 
382
 
 
383
#ifdef DEBUG
 
384
  {
 
385
    int i;
 
386
    printf("sound: %s: driver formats (%x):", dsp->path, dsp->fmts);
 
387
    for (i= 1; i; i <<= 1) if (dsp->fmts & i) printf(" %s", afmtName(i));
 
388
    printf("\nsound: %s: driver capabilities (%x):", dsp->path, dsp->caps);
 
389
    for (i= 0x100; i; i <<= 1) if (dsp->caps & i) printf(" %s", capName(i));
 
390
    printf("\n");
 
391
  }
 
392
#endif
 
393
 
 
394
  return dsp;
 
395
}
 
396
 
 
397
 
 
398
/* Find and set a supported sample format as close as possible to
 
399
 * Squeak's native format (16-bit signed).
 
400
 * Answer whether a format was found and set.
 
401
 */
 
402
static int dspSetFormat(struct dsp *dsp)
 
403
{
 
404
  assert(dsp);
 
405
  assert(dsp->fd >= 0);
 
406
 
 
407
  /* find the closest driver format */
 
408
  {
 
409
    static struct { int dsp, fmt; } formats[]= {
 
410
      { AFMT_S16_HE, FMT_S         },
 
411
      { AFMT_U16_HE, FMT_U         },
 
412
      { AFMT_S16_RE, FMT_E         },
 
413
      { AFMT_U16_RE, FMT_E | FMT_U },
 
414
      { AFMT_S8,     FMT_8         },
 
415
      { AFMT_U8,     FMT_8 | FMT_U },
 
416
      { 0,           0 }
 
417
    };
 
418
    int found= -1;
 
419
    int i;
 
420
    for (i= 0;  formats[i].dsp;  ++i)
 
421
      if (dsp->fmts & formats[i].dsp)
 
422
        {
 
423
          /* try query before set (driver format might be locked) */
 
424
          int fmt= AFMT_QUERY;
 
425
          PRINTF(("sound: %s: trying format %x: %s\n", dsp->path,
 
426
                  formats[i].dsp, afmtName(formats[i].dsp)));
 
427
          if (IOCTL(dsp->fd, SNDCTL_DSP_SETFMT, &fmt))
 
428
            fprintf(stderr, "sound: %s: could not query driver format\n", dsp->path);
 
429
          else if (fmt == formats[i].dsp)
 
430
            {
 
431
              found= i;
 
432
              break;
 
433
            }
 
434
          fmt= formats[i].dsp;
 
435
          if ((ioctl(dsp->fd, SNDCTL_DSP_SETFMT, &fmt) == 0) && (fmt == formats[i].dsp))
 
436
            {
 
437
              found= i;
 
438
              break;
 
439
            }
 
440
        }
 
441
    if (found < 0)
 
442
      {
 
443
        fprintf(stderr, "sound: %s: driver has no usable sample format\n", dsp->path);
 
444
        return 0;
 
445
      }
 
446
    PRINTF(("sound: %s: selected driver format %x: %s\n", dsp->path,
 
447
            formats[found].dsp, afmtName(formats[found].dsp)));
 
448
 
 
449
    dsp->hw.format= formats[found].fmt;
 
450
    dsp->hw.bpf= ((dsp->hw.format & FMT_8) ? 1 : 2);    /* bytes per sample */
 
451
  }
 
452
 
 
453
  /* the Squeak side is always S16_NE */
 
454
  dsp->sq.format= FMT_S;        /* until modified by dspSetChannels() */
 
455
  dsp->sq.bpf= 2;               /* bytes per sample */
 
456
 
 
457
  return 1;
 
458
}
 
459
 
 
460
 
 
461
/* Set the number of channels.  Answer whether a suitable number of channels was set.
 
462
 */
 
463
static int dspSetChannels(struct dsp *dsp, int nChannels)
 
464
{
 
465
  int chans= nChannels;
 
466
  PRINTF(("sound: %s: requesting %d channels\n", dsp->path, nChannels));
 
467
  assert(chans >= 1);
 
468
  assert(chans <= 2);
 
469
 
 
470
  dsp->sq.channels= nChannels;
 
471
 
 
472
  if (nChannels == 1)
 
473
    dsp->sq.format |= FMT_M;
 
474
 
 
475
#ifdef TEST_CHANS
 
476
  chans= TEST_CHANS;
 
477
#endif
 
478
  IOCTL(dsp->fd, SNDCTL_DSP_CHANNELS, &chans);
 
479
  if (chans != nChannels)
 
480
    {
 
481
      nChannels= ((nChannels == 2) ? 1 : 2);
 
482
      chans= nChannels;
 
483
      IOCTL(dsp->fd, SNDCTL_DSP_CHANNELS, &chans);
 
484
      if (chans != nChannels)
 
485
        {
 
486
          fprintf(stderr, "sound: %s: could not set a suitable number of channels\n",
 
487
                  dsp->path);
 
488
          return 0;
 
489
        }
 
490
    }
 
491
  dsp->hw.channels= chans;
 
492
 
 
493
  if (chans == 1) dsp->hw.format |= FMT_M;
 
494
 
 
495
  dsp->hw.bpf *= dsp->hw.channels;      /* samples per frame */
 
496
  dsp->sq.bpf *= dsp->sq.channels;      /* samples per frame */
 
497
 
 
498
  PRINTF(("sound: %s: using %d channels\n",      dsp->path, dsp->hw.channels));
 
499
  PRINTF(("sound: %s: driver: %d bytes/frame\n", dsp->path, dsp->hw.bpf));
 
500
  PRINTF(("sound: %s: squeak: %d bytes/frame\n", dsp->path, dsp->sq.bpf));
 
501
 
 
502
  return 1;
 
503
}
 
504
 
 
505
 
 
506
/* Set the sample rate.  Answer whether a suitable rate was set.
 
507
 */
 
508
static int dspSetSpeed(struct dsp *dsp, int speed)
 
509
{
 
510
  int arg= speed;
 
511
  dsp->sq.rate= speed;
 
512
  if (IOCTL(dsp->fd, SNDCTL_DSP_SPEED, &arg))
 
513
    {
 
514
      fprintf(stderr, "sound: %s: failed to set sample rate\n", dsp->path);
 
515
      return 0;
 
516
    }
 
517
  dsp->hw.rate= arg;
 
518
  PRINTF(("sound: %s: %d samples/sec\n", dsp->path, dsp->hw.rate));
 
519
 
 
520
  if (dsp->hw.rate != dsp->sq.rate)
 
521
    fprintf(stderr, "sound: %s: using %d samples/sec (requested %d)\n", dsp->path,
 
522
            dsp->hw.rate, dsp->sq.rate);
 
523
 
 
524
  return 1;
 
525
}
 
526
 
 
527
 
 
528
/* Request a fragment size approximating the number of frames of lead
 
529
 * time requested by Squeak, and record the actual fragment size set
 
530
 * by the driver.  Answer whether the fragment size was successfuly
 
531
 * modified.
 
532
 */
 
533
static int dspSetFragSize(struct dsp *dsp, int nFrames, int nChannels)
 
534
{
 
535
  int fragSize= nFrames * dsp->hw.bpf;
 
536
  int i;
 
537
  for (i= 0;  fragSize;  i++) fragSize >>= 1;
 
538
  fragSize= (4 /* fragments */ << 16) | (i - 1) /* ^2 bytesPerFragment */;
 
539
 
 
540
  if ((  IOCTL(dsp->fd, SNDCTL_DSP_SETFRAGMENT, &fragSize))
 
541
      || IOCTL(dsp->fd, SNDCTL_DSP_GETBLKSIZE,  &fragSize))
 
542
    {
 
543
      fprintf(stderr, "sound: %s: failed to set fragment size\n", dsp->path);
 
544
      return 0;
 
545
    }
 
546
  assert(fragSize > 0);
 
547
  dsp->hw.fragSize= fragSize;
 
548
  dsp->sq.fragSize= fragSize / dsp->hw.bpf * dsp->sq.bpf;
 
549
 
 
550
  PRINTF(("sound: %s: fragment size set to %d (%d frames requested in %d channels)\n",
 
551
          dsp->path, fragSize, nFrames, nChannels));
 
552
 
 
553
  return 1;
 
554
}
 
555
 
 
556
 
 
557
/* Set the input/output functions according to the current sq/hw
 
558
 * formats.
 
559
 */
 
560
static int dspSetConversion(struct dsp *dsp)
 
561
{
 
562
  int sm, io;
 
563
  assert(dsp->sq.format >= 0);
 
564
  assert(dsp->sq.format <= (FMT_MAX | FMT_M));
 
565
  assert(dsp->hw.format >= 0);
 
566
  assert(dsp->hw.format <= (FMT_MAX | FMT_M));
 
567
  io= dsp->hw.format & 0x7;     /* don't care about channels */
 
568
  assert(io >= 0);
 
569
  assert(io <= FMT_MAX);
 
570
  /* output */
 
571
  sm= (((dsp->sq.format & FMT_M) << 1) | (dsp->hw.format & FMT_M)) >> 3;
 
572
  assert(sm >= 0);
 
573
  assert(sm <= 3);
 
574
  dsp->write= writers[sm][io];
 
575
  assert(dsp->write != 0);
 
576
  /* input */
 
577
  sm= (((dsp->hw.format & FMT_M) << 1) | (dsp->sq.format & FMT_M)) >> 3;
 
578
  assert(sm >= 0);
 
579
  assert(sm <= 3);
 
580
  dsp->read= readers[sm][io];
 
581
  assert(dsp->read != 0);
 
582
#ifdef DEBUG
 
583
  printf("sound: input conversion: %s (%p)\n", rdName(dsp->read), dsp->read);
 
584
  printf("sound: output conversion: %s (%p)\n", wrName(dsp->write), dsp->write);
 
585
#endif /* DEBUG */
 
586
  return 1;
 
587
}
 
588
 
 
589
 
 
590
static int dspSetSemaphore(struct dsp *dsp, int semaIndex)
 
591
{
 
592
  if (semaIndex > 0)
 
593
    {
 
594
      dsp->semaphore= semaIndex;
 
595
      aioEnable(dsp->fd, (void *)dsp, AIO_EXT);
 
596
      PRINTF(("sound: %s: aio enabled, semaphore %d\n", dsp->path, dsp->semaphore));
 
597
    }
 
598
  return 1;
 
599
}
 
600
 
 
601
 
 
602
static void dspSetTrigger(struct dsp *dsp, int mask)
 
603
{
 
604
  if (dsp->caps & DSP_CAP_TRIGGER)
 
605
    {
 
606
      int triggers= 0, toggle= 0;
 
607
      IOCTL(dsp->fd, SNDCTL_DSP_GETTRIGGER, &triggers);
 
608
      toggle= triggers & ~mask;
 
609
      IOCTL(in->fd, SNDCTL_DSP_SETTRIGGER, &toggle);
 
610
      toggle= triggers | mask;
 
611
      IOCTL(in->fd, SNDCTL_DSP_SETTRIGGER, &toggle);
 
612
    }
 
613
}
 
614
 
 
615
 
 
616
static int dspGetInputSpace(struct dsp *dsp)
 
617
{
 
618
  struct audio_buf_info info;
 
619
  if (ioctl(dsp->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
 
620
    {
 
621
      fprintf(stderr, "sound: %s: ", dsp->path);
 
622
      perror("GETISPACE");
 
623
      return -1;
 
624
    }
 
625
  return info.bytes;
 
626
}
 
627
 
 
628
 
 
629
static int dspGetOutputSpace(struct dsp *dsp)
 
630
{
 
631
  struct audio_buf_info info;
 
632
  if (ioctl(dsp->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
 
633
    {
 
634
      fprintf(stderr, "sound: %s: ", dsp->path);
 
635
      perror("GETOSPACE");
 
636
      return -1;
 
637
    }
 
638
  return info.bytes;
 
639
}
 
640
 
 
641
 
 
642
/*** aio ***/
 
643
 
 
644
 
 
645
static void dspHandler(int fd, void *data, int flags)
 
646
{
 
647
  struct dsp *dsp= (struct dsp *)data;
 
648
  assert(dsp != 0);
 
649
  assert(dsp->semaphore > 0);
 
650
  signalSemaphoreWithIndex(dsp->semaphore);
 
651
  aioHandle(fd, dspHandler, flags);
 
652
}
 
653
 
 
654
 
 
655
/*** sound output ***/
 
656
 
 
657
 
 
658
static sqInt sound_Stop(void)
 
659
{
 
660
  PRINTF(("sound: stop\n"));
 
661
  if (out)
 
662
    {
 
663
      if (out != in)
 
664
        {
 
665
          dspClose(out);
 
666
          PRINTF(("sound: %s: device closed\n", out->path));
 
667
        }
 
668
      else
 
669
        {
 
670
          aioSuspend(out->fd, AIO_W);
 
671
          PRINTF(("sound: %s: aio suspended\n", out->path));
 
672
        }
 
673
      out= 0;
 
674
    }
 
675
  return true;
 
676
}
 
677
 
 
678
 
 
679
static sqInt sound_Start(sqInt frameCount, sqInt samplesPerSec, sqInt stereo, sqInt semaIndex)
 
680
{
 
681
  int nChannels= (stereo ? 2 : 1);
 
682
  PRINTF(("sound: start\n"));
 
683
 
 
684
#ifndef USE_PLAY_SEMAPHORE
 
685
  if (semaIndex > 0)
 
686
    {
 
687
      PRINTF(("sound: asynchronous output disabled\n"));
 
688
      return success(false);
 
689
    }
 
690
#endif
 
691
 
 
692
  if ((out= dspOpen(&dev_dsp, O_WRONLY | O_NONBLOCK)))
 
693
    {
 
694
      /* NOTE: The OSSPG says we should set fragsize immediately after
 
695
         opening the device (before setting sampling parameters) but this
 
696
         causes some drivers to refuse subsequent changes to the sample
 
697
         format.  We therefore set it after the sampling parameters.
 
698
         (This normally won't cause problems since the block size should
 
699
         not yet have been locked by OSS.  The worst that can happen is
 
700
         that we'll get a longer than optimal lead time if the blocking
 
701
         size cannot be successfully changed -- which is a far lesser evil
 
702
         than being unable to change the sample format.) */
 
703
      if ((  dspSetFormat(out))
 
704
          && dspSetChannels(out, nChannels)
 
705
          && dspSetSpeed(out, samplesPerSec)
 
706
          && dspSetFragSize(out, frameCount, nChannels)
 
707
          && dspSetConversion(out)
 
708
#        ifdef USE_PLAY_SEMAPHORE
 
709
          && dspSetSemaphore(out, semaIndex)
 
710
#        endif
 
711
          )
 
712
        {
 
713
          out->running= 0;
 
714
          return true;
 
715
        }
 
716
      sound_Stop();
 
717
    }
 
718
  PRINTF(("sound: could not start\n"));
 
719
  return false;
 
720
}
 
721
 
 
722
 
 
723
static sqInt sound_AvailableSpace(void)
 
724
{
 
725
  int bytes= 0;
 
726
 
 
727
  if (out)
 
728
    {
 
729
      if (!out->running)
 
730
        {
 
731
#        ifdef USE_PLAY_SEMAPHORE
 
732
          if (out->semaphore)
 
733
            aioHandle(out->fd, dspHandler, AIO_W);
 
734
#        endif
 
735
          out->running= 1;
 
736
        }
 
737
      bytes= dspGetOutputSpace(out);
 
738
      if (bytes >= 0)
 
739
        /* hardware bytes -> frames -> squeak bytes */
 
740
        return bytes / out->hw.bpf * out->sq.bpf;
 
741
    }
 
742
  PRINTF(("sound: available space: 0 bytes\n"));
 
743
  return 0;
 
744
}
 
745
 
 
746
 
 
747
static sqInt sound_InsertSamplesFromLeadTime(sqInt frameCount, void *srcBufPtr, sqInt samplesOfLeadTime)
 
748
{
 
749
  return success(false);
 
750
}
 
751
 
 
752
 
 
753
static sqInt sound_PlaySamplesFromAtLength(sqInt frameCount, void *srcBufPtr, sqInt startIndex)
 
754
{
 
755
  assert(out->write != 0);
 
756
  return out->write(out, srcBufPtr + startIndex * out->sq.bpf, frameCount);
 
757
}
 
758
 
 
759
 
 
760
static sqInt sound_PlaySilence(void)
 
761
{
 
762
  if (!out) return success(false);
 
763
  return out->sq.fragSize;
 
764
}
 
765
 
 
766
 
 
767
/* sound input */
 
768
 
 
769
 
 
770
static sqInt sound_StopRecording(void)
 
771
{
 
772
  PRINTF(("sound: stop recording\n"));
 
773
  if (in)
 
774
    {
 
775
      if (in != out)
 
776
        {
 
777
          dspClose(in);
 
778
          PRINTF(("sound: %s: device closed\n", in->path));
 
779
        }
 
780
      else
 
781
        {
 
782
          aioSuspend(in->fd, AIO_R);
 
783
          PRINTF(("sound: %s: aio suspended\n", in->path));
 
784
        }
 
785
      in= 0;
 
786
    }
 
787
  return true;
 
788
}
 
789
 
 
790
 
 
791
static sqInt sound_StartRecording(sqInt desiredSamplesPerSec, sqInt stereo, sqInt semaIndex)
 
792
{
 
793
  int nChannels= (stereo ? 2 : 1);
 
794
  PRINTF(("sound: start recording\n"));
 
795
 
 
796
  if ((  (in= dspOpen(&dev_dsp1, O_RDONLY)))
 
797
      || (in= dspOpen(&dev_dsp,  O_RDONLY)))
 
798
    {
 
799
      if ((  dspSetFormat(in))
 
800
          && dspSetChannels(in, nChannels)
 
801
          && dspSetSpeed(in, desiredSamplesPerSec)
 
802
          /* try for 1/10 second input latency */
 
803
          && dspSetFragSize(in, desiredSamplesPerSec / 10, nChannels)
 
804
          && dspSetConversion(in)
 
805
          && dspSetSemaphore(in, semaIndex))
 
806
        {
 
807
          dspSetTrigger(in, PCM_ENABLE_INPUT);
 
808
          aioHandle(in->fd, dspHandler, AIO_R);
 
809
          in->running= 0;
 
810
          return true;
 
811
        }
 
812
      sound_StopRecording();
 
813
    }
 
814
  PRINTF(("sound: could not start recording\n"));
 
815
  return false;
 
816
}
 
817
 
 
818
 
 
819
static double sound_GetRecordingSampleRate(void)
 
820
{
 
821
  return in ? (double)in->hw.rate : 0.0l;
 
822
}
 
823
 
 
824
 
 
825
static sqInt sound_RecordSamplesIntoAtLength(void *buf, sqInt startSliceIndex, sqInt bufferSizeInBytes)
 
826
{
 
827
  /*PRINTF(("record %d %d %d\n", buf, startSliceIndex, bufferSizeInBytes));*/
 
828
 
 
829
  if (in)
 
830
    {
 
831
      /* start index is in samples (rather than bytes or frames???) */
 
832
      int frameCount= ((bufferSizeInBytes / 2) - startSliceIndex) / in->sq.channels;
 
833
      int bytesAvail= 0;
 
834
      int framesAvail= 0;
 
835
      if (in->running)
 
836
        {
 
837
          bytesAvail= dspGetInputSpace(in);
 
838
          if (bytesAvail <= 0)
 
839
            return 0;           /* underrun */
 
840
        }
 
841
      else /* initial read required to start recording on some devices */
 
842
        {
 
843
          bytesAvail= in->hw.fragSize;
 
844
          in->running= 1;
 
845
        }
 
846
      assert(bytesAvail > 0);
 
847
      framesAvail= bytesAvail / in->hw.bpf;
 
848
      frameCount= min(frameCount, framesAvail);
 
849
      /*PRINTF(("<%d", frameCount * in->hw.bpf));*/
 
850
      return in->read(in,
 
851
                      buf + startSliceIndex * 2,
 
852
                      frameCount)
 
853
        * in->sq.channels;
 
854
    }
 
855
  return 0;
 
856
}
 
857
 
 
858
 
 
859
/*** mixer ***/
 
860
 
 
861
 
 
862
/* NOTES:
 
863
 * 
 
864
 *   - output volume is connected to PCM unless there is no such
 
865
 *     device, in which case we try master VOLUME instead.
 
866
 * 
 
867
 *   - input volume is connected to RECLEVEL unless there is no such
 
868
 *     device, in which case we try IGAIN instead.
 
869
 */
 
870
 
 
871
#define LEVEL_MAX       100
 
872
 
 
873
struct mixer
 
874
{
 
875
  char  *path;
 
876
  int    fd;
 
877
  int    devices;       /* available mixer devices */
 
878
};
 
879
 
 
880
struct mixer dev_mixer= { "/dev/mixer", -1 };
 
881
 
 
882
struct mixer *mixer= 0;
 
883
 
 
884
 
 
885
static struct mixer *mixerOpen(struct mixer *mix)
 
886
{
 
887
  assert(mix);
 
888
  assert(mix->fd == -1);
 
889
 
 
890
  if ((mix->fd= open(mix->path, O_RDWR, 0)) < 0)
 
891
    {
 
892
      fprintf(stderr, "sound: ");
 
893
      perror(mix->path);
 
894
      return 0;
 
895
    }
 
896
  PRINTF(("sound: %s: opened with mode %d\n", mix->path, O_RDWR));
 
897
 
 
898
  /* read available devices */
 
899
  {
 
900
    if (IOCTL(mix->fd, SOUND_MIXER_READ_DEVMASK, &mix->devices))
 
901
      mix->devices= 0;
 
902
#  ifdef DEBUG
 
903
    printf("sound: %s: available devices:", mix->path);
 
904
    {
 
905
      int i= 0;
 
906
      for (i= 0; i < SOUND_MIXER_NRDEVICES; ++i)
 
907
        if (mix->devices & (1 << i))
 
908
          printf(" %s", devName(i));
 
909
    }
 
910
    printf("\n");
 
911
#  endif
 
912
  }
 
913
 
 
914
  return mix;
 
915
}
 
916
 
 
917
 
 
918
static int mixerGetLevel(struct mixer *mix, int device, int *left, int *right)
 
919
{
 
920
  assert(mix);
 
921
  assert(mix->fd >= 0);
 
922
 
 
923
  if (mix->devices & (1 << device))
 
924
    {
 
925
      int vol= 0;
 
926
      if (IOCTL(mix->fd, MIXER_READ(device), &vol) >= 0)
 
927
        {
 
928
          *left=  (vol >> 8) & 0xff;
 
929
          *right= (vol     ) & 0xff;
 
930
          return 1;
 
931
        }
 
932
    }
 
933
  return 0;
 
934
}
 
935
 
 
936
 
 
937
static int mixerSetLevel(struct mixer *mix, int device, int left, int right)
 
938
{
 
939
  assert(mix);
 
940
  assert(mix->fd >= 0);
 
941
  assert((left  >= 0) && (left  <= LEVEL_MAX));
 
942
  assert((right >= 0) && (right <= LEVEL_MAX));
 
943
 
 
944
  if (mix->devices & (1 << device))
 
945
    {
 
946
      int vol= (left << 8) | right;
 
947
      if (IOCTL(mix->fd, MIXER_WRITE(device), &vol) >= 0)
 
948
        {
 
949
          PRINTF(("sound: %s: %s: level set to %d%% + %d%%\n", mix->path,
 
950
                  devName(device), left, right));
 
951
          return 1;
 
952
        }
 
953
    }
 
954
  PRINTF(("sound: %s: %s: device not available\n", mix->path, devName(device)));
 
955
  return 0;
 
956
}
 
957
 
 
958
 
 
959
 
 
960
static void sound_Volume(double *left, double *right)
 
961
{
 
962
  if (mixer || (mixer= mixerOpen(&dev_mixer)))
 
963
    {
 
964
      int l= 0, r= 0;
 
965
      if ((  mixerGetLevel(mixer, SOUND_MIXER_PCM,     &l, &r))
 
966
          || mixerGetLevel(mixer, SOUND_MIXER_VOLUME,  &l, &r))
 
967
        {
 
968
          *left=  (double)l / (double)LEVEL_MAX;
 
969
          *right= (double)r / (double)LEVEL_MAX;
 
970
          return;
 
971
        }
 
972
    }
 
973
  success(false);
 
974
}
 
975
 
 
976
 
 
977
/* NOTE: I have a fundamental objection to the existence of this
 
978
 * primitive (and snd_SetRecLevel).  Unix audio philosophy is that
 
979
 * sound input/output and mixer control are totally seperate
 
980
 * activities.  Choice of input source, output destination and device
 
981
 * levels should always be left to an external mixer program.  The
 
982
 * option `-nommixer' therefore disables all writing to the mixer
 
983
 * device for those of us who object to Squeak abritrarily resetting
 
984
 * our carefully-adjusted input levels whenever sound recording is
 
985
 * started.
 
986
 */
 
987
static void sound_SetVolume(double left, double right)
 
988
{
 
989
  if (noSoundMixer) return;
 
990
  if (mixer || (mixer= mixerOpen(&dev_mixer)))
 
991
    {
 
992
      int l= (int)(left  * (double)LEVEL_MAX);
 
993
      int r= (int)(right * (double)LEVEL_MAX);
 
994
      if (l < 0) l= 0; if (l > LEVEL_MAX) l= LEVEL_MAX;
 
995
      if (r < 0) r= 0; if (r > LEVEL_MAX) r= LEVEL_MAX;
 
996
      if ((  mixerSetLevel(mixer, SOUND_MIXER_PCM,    l, r))
 
997
          || mixerSetLevel(mixer, SOUND_MIXER_VOLUME, l, r))
 
998
        return;
 
999
    }
 
1000
  success(false);
 
1001
}
 
1002
 
 
1003
 
 
1004
#if 0
 
1005
static int sound_RecordLevel(int *level)
 
1006
{
 
1007
  if (mixer || (mixer= mixerOpen(&dev_mixer)))
 
1008
    {
 
1009
      int l= 0, r= 0;
 
1010
      if ((  mixerGetLevel(mixer, SOUND_MIXER_RECLEV, &l, &r))
 
1011
          || mixerGetLevel(mixer, SOUND_MIXER_IGAIN,  &l, &r))
 
1012
        {
 
1013
          /* record level is average of l+r in the range 0..1000 */
 
1014
          *level= 1000 * (l + r) / 2 / 100;
 
1015
          return 1;
 
1016
        }
 
1017
    }
 
1018
  return success(false);
 
1019
}
 
1020
#endif
 
1021
 
 
1022
 
 
1023
static void sound_SetRecordLevel(sqInt level)
 
1024
{
 
1025
  if (noSoundMixer) return;
 
1026
  if (mixer || (mixer= mixerOpen(&dev_mixer)))
 
1027
    {
 
1028
      level= level * LEVEL_MAX / 1000;
 
1029
      if (level < 0)
 
1030
        level= 0;
 
1031
      else if (level > 255)
 
1032
        level= 255;
 
1033
 
 
1034
      if (mixerSetLevel(mixer, SOUND_MIXER_RECLEV, level, level)) return;
 
1035
      if (mixerSetLevel(mixer, SOUND_MIXER_IGAIN,  level, level)) return;
 
1036
    }
 
1037
}
 
1038
 
 
1039
static sqInt sound_SetSwitch(sqInt id, sqInt captureFlag, sqInt parameter)
 
1040
{
 
1041
  return -1;
 
1042
}
 
1043
 
 
1044
static sqInt sound_GetSwitch(sqInt id, sqInt captureFlag, sqInt channel)
 
1045
{
 
1046
  return -1;
 
1047
}
 
1048
 
 
1049
static sqInt sound_SetDevice(sqInt id, char *arg)
 
1050
{
 
1051
  return -1;
 
1052
}
 
1053
 
 
1054
/*** debugging ***/
 
1055
 
 
1056
 
 
1057
#ifdef DEBUG
 
1058
 
 
1059
static char *afmtName(int i)
 
1060
{
 
1061
  switch (i)
 
1062
    {
 
1063
    case AFMT_MU_LAW:    return "MU_LAW";
 
1064
    case AFMT_A_LAW:     return "A_LAW";
 
1065
    case AFMT_IMA_ADPCM: return "IMA_ADPCM";
 
1066
    case AFMT_U8:        return "U8";
 
1067
    case AFMT_S16_LE:    return "S16_LE";
 
1068
    case AFMT_S16_BE:    return "S16_BE";
 
1069
    case AFMT_S8:        return "S8";
 
1070
    case AFMT_U16_LE:    return "U16_LE";
 
1071
    case AFMT_U16_BE:    return "U16_BE";
 
1072
    case AFMT_MPEG:      return "MPEG";
 
1073
    }
 
1074
  return "*** UNKNOWN ***";
 
1075
}
 
1076
 
 
1077
static char *capName(int i)
 
1078
{
 
1079
  switch (i)
 
1080
    {
 
1081
      case DSP_CAP_DUPLEX:   return "DUPLEX";
 
1082
      case DSP_CAP_REALTIME: return "REALTIME";
 
1083
      case DSP_CAP_BATCH:    return "BATCH";
 
1084
      case DSP_CAP_COPROC:   return "COPROC";
 
1085
      case DSP_CAP_TRIGGER:  return "TRIGGER";
 
1086
      case DSP_CAP_MMAP:     return "MMAP";
 
1087
    }
 
1088
  return "*** UNKNOWN ***";
 
1089
}
 
1090
 
 
1091
static char *devName(int dev)
 
1092
{
 
1093
  static char *names[]= SOUND_DEVICE_NAMES;
 
1094
  if ((dev >= 0) && (dev <SOUND_MIXER_NRDEVICES)) return names[dev];
 
1095
  return "*** ILLEGAL ***";
 
1096
}
 
1097
 
 
1098
static char *rdName(reader rd)
 
1099
{
 
1100
  if (rd == input)   return "none";
 
1101
  if (rd == rdMM__U) return "MM__U";
 
1102
  if (rd == rdMM_E_) return "MM_E_";
 
1103
  if (rd == rdMM_EU) return "MM_EU";
 
1104
  if (rd == rdMM8__) return "MM8__";
 
1105
  if (rd == rdMM8_U) return "MM8_U";
 
1106
  if (rd == rdMS___) return "MS___";
 
1107
  if (rd == rdMS__U) return "MS__U";
 
1108
  if (rd == rdMS_E_) return "MS_E_";
 
1109
  if (rd == rdMS_EU) return "MS_EU";
 
1110
  if (rd == rdMS8__) return "MS8__";
 
1111
  if (rd == rdMS8_U) return "MS8_U";
 
1112
  if (rd == rdSM___) return "SM___";
 
1113
  if (rd == rdSM__U) return "SM__U";
 
1114
  if (rd == rdSM_E_) return "SM_E_";
 
1115
  if (rd == rdSM_EU) return "SM_EU";
 
1116
  if (rd == rdSM8__) return "SM8__";
 
1117
  if (rd == rdSM8_U) return "SM8_U";
 
1118
  if (rd == rdSS__U) return "SS__U";
 
1119
  if (rd == rdSS_E_) return "SS_E_";
 
1120
  if (rd == rdSS_EU) return "SS_EU";
 
1121
  if (rd == rdSS8__) return "SS8__";
 
1122
  if (rd == rdSS8_U) return "SS8_U";
 
1123
  if (rd == 0)       return "*** NULL ***";
 
1124
  return "*** ILLEGAL ***";
 
1125
}
 
1126
 
 
1127
static char *wrName(writer wr)
 
1128
{
 
1129
  if (wr == output)  return "none";
 
1130
  if (wr == wrMM__U) return "MM__U";
 
1131
  if (wr == wrMM_E_) return "MM_E_";
 
1132
  if (wr == wrMM_EU) return "MM_EU";
 
1133
  if (wr == wrMM8__) return "MM8__";
 
1134
  if (wr == wrMM8_U) return "MM8_U";
 
1135
  if (wr == wrMS___) return "MS___";
 
1136
  if (wr == wrMS__U) return "MS__U";
 
1137
  if (wr == wrMS_E_) return "MS_E_";
 
1138
  if (wr == wrMS_EU) return "MS_EU";
 
1139
  if (wr == wrMS8__) return "MS8__";
 
1140
  if (wr == wrMS8_U) return "MS8_U";
 
1141
  if (wr == wrSM___) return "SM___";
 
1142
  if (wr == wrSM__U) return "SM__U";
 
1143
  if (wr == wrSM_E_) return "SM_E_";
 
1144
  if (wr == wrSM_EU) return "SM_EU";
 
1145
  if (wr == wrSM8__) return "SM8__";
 
1146
  if (wr == wrSM8_U) return "SM8_U";
 
1147
  if (wr == wrSS__U) return "SS__U";
 
1148
  if (wr == wrSS_E_) return "SS_E_";
 
1149
  if (wr == wrSS_EU) return "SS_EU";
 
1150
  if (wr == wrSS8__) return "SS8__";
 
1151
  if (wr == wrSS8_U) return "SS8_U";
 
1152
  if (wr == 0)       return "*** NULL ***";
 
1153
  return "*** ILLEGAL ***";
 
1154
}
 
1155
 
 
1156
#endif /* DEBUG */
 
1157
 
 
1158
 
 
1159
#include "SqSound.h"
 
1160
 
 
1161
SqSoundDefine(OSS);
 
1162
 
 
1163
 
 
1164
#include "SqModule.h"
 
1165
 
 
1166
static void  sound_parseEnvironment(void)
 
1167
{
 
1168
  if (getenv("SQUEAK_NOMIXER")) noSoundMixer= 1;
 
1169
}
 
1170
 
 
1171
static int   sound_parseArgument(int argc, char **argv)
 
1172
{
 
1173
  if (!strcmp(argv[0], "-nomixer")) return noSoundMixer= 1;
 
1174
  return 0;
 
1175
}
 
1176
 
 
1177
static void  sound_printUsage(void)
 
1178
{
 
1179
  printf("\nOSS <option>s:\n");
 
1180
  printf("  -nomixer              disable mixer (volume) adjustment\n");
 
1181
}
 
1182
 
 
1183
static void  sound_printUsageNotes(void)
 
1184
{
 
1185
}
 
1186
 
 
1187
static void *sound_makeInterface(void)
 
1188
{
 
1189
  return &sound_OSS_itf;
 
1190
}
 
1191
 
 
1192
SqModuleDefine(sound, OSS);