1
/* sqUnixSoundOSS.c -- sound module for Open Sound System
3
* Author: Ian.Piumarta@squeakland.org
5
* Last edited: 2008-04-21 14:51:45 by piumarta on emilia
7
* Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors
8
* listed elsewhere in this file.
11
* This file is part of Unix Squeak.
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:
20
* The above copyright notice and this permission notice shall be included in
21
* all copies or substantial portions of the Software.
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.
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.
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.
49
#define USE_PLAY_SEMAPHORE
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 */
66
#include <sys/ioctl.h>
67
#include <sys/soundcard.h>
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
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
84
#define ERROR(MSG) ( \
85
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, MSG) \
88
#define PERROR(MSG) ( \
89
fprintf(stderr, "%s:%d: ", __FILE__, __LINE__), \
93
#define IOCTL(FD,CMD,ARG) \
94
((ioctl(FD, CMD, ARG) == -1) ? (PERROR(#CMD), -1) : 0)
96
#define min(A,B) (((A)<(B)) ? (A) : (B))
100
typedef int (*reader)(struct dsp *dsp, void *buffer, int nFrames);
101
typedef int (*writer)(struct dsp *dsp, void *buffer, int nFrames);
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);
111
# define PRINTF(ARGS)
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 */
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 */
145
struct dsp dev_dsp1 = { "/dev/dsp1", -1 };
146
struct dsp dev_dsp = { "/dev/dsp", -1 };
148
static struct dsp *in= 0;
149
static struct dsp *out= 0;
151
static int noSoundMixer= 0;
153
typedef signed char sbyte;
154
typedef unsigned char ubyte;
155
typedef signed short sword;
156
typedef unsigned short uword;
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;
169
#define CV_EU CVU CVE
171
#define CVB_U CVU CVB
173
#define CVW_U CVW CVU
175
static int output(struct dsp *dsp, void *buf, int frames)
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
184
int n= write(dsp->fd, buf, m);
189
fprintf(stderr, "sound: ");
200
#define outputFn(TYPE, CVT) (struct dsp *dsp, void *buf, int frames) \
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); \
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);
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 }
244
static int input(struct dsp *dsp, void *buf, int frames)
246
int m= frames * dsp->hw.bpf;
247
int n= read(dsp->fd, buf, m);
250
fprintf(stderr, "sound: ");
254
return n / dsp->hw.bpf;
257
#define inputFn(TYPE, CVT) (struct dsp *dsp, void *buf, register int frames) \
259
register short *out= (short *)buf; \
260
register TYPE *in= (TYPE *)alloca(dsp->hw.bpf * frames); \
261
frames= input(dsp, (void *)in, frames); \
263
register int n= frames; \
264
while (n--) { register short sample; CVT; } \
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);
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 }
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. */
316
static void dspClose(struct dsp *dsp)
318
assert(dsp->fd >= 0);
319
if (dsp->semaphore > 0)
323
PRINTF(("sound: %s: aio disabled\n", dsp->path));
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.
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
350
* [1] http://www.opensound.com/readme/README.fullduplex.html
352
static struct dsp *dspOpen(struct dsp *dsp, int mode)
356
if ((dsp->fd= open(dsp->path, mode, 0)) < 0)
360
fprintf(stderr, "sound: ");
365
PRINTF(("sound: %s: opened with mode %d\n", dsp->path, mode));
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))
373
/* driver is hosed */
374
fprintf(stderr, "sound: %s: could not read driver capabilities\n", dsp->path);
380
dsp->fmts = TEST_FMT;
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));
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.
402
static int dspSetFormat(struct dsp *dsp)
405
assert(dsp->fd >= 0);
407
/* find the closest driver format */
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 },
415
{ AFMT_U8, FMT_8 | FMT_U },
420
for (i= 0; formats[i].dsp; ++i)
421
if (dsp->fmts & formats[i].dsp)
423
/* try query before set (driver format might be locked) */
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)
435
if ((ioctl(dsp->fd, SNDCTL_DSP_SETFMT, &fmt) == 0) && (fmt == formats[i].dsp))
443
fprintf(stderr, "sound: %s: driver has no usable sample format\n", dsp->path);
446
PRINTF(("sound: %s: selected driver format %x: %s\n", dsp->path,
447
formats[found].dsp, afmtName(formats[found].dsp)));
449
dsp->hw.format= formats[found].fmt;
450
dsp->hw.bpf= ((dsp->hw.format & FMT_8) ? 1 : 2); /* bytes per sample */
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 */
461
/* Set the number of channels. Answer whether a suitable number of channels was set.
463
static int dspSetChannels(struct dsp *dsp, int nChannels)
465
int chans= nChannels;
466
PRINTF(("sound: %s: requesting %d channels\n", dsp->path, nChannels));
470
dsp->sq.channels= nChannels;
473
dsp->sq.format |= FMT_M;
478
IOCTL(dsp->fd, SNDCTL_DSP_CHANNELS, &chans);
479
if (chans != nChannels)
481
nChannels= ((nChannels == 2) ? 1 : 2);
483
IOCTL(dsp->fd, SNDCTL_DSP_CHANNELS, &chans);
484
if (chans != nChannels)
486
fprintf(stderr, "sound: %s: could not set a suitable number of channels\n",
491
dsp->hw.channels= chans;
493
if (chans == 1) dsp->hw.format |= FMT_M;
495
dsp->hw.bpf *= dsp->hw.channels; /* samples per frame */
496
dsp->sq.bpf *= dsp->sq.channels; /* samples per frame */
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));
506
/* Set the sample rate. Answer whether a suitable rate was set.
508
static int dspSetSpeed(struct dsp *dsp, int speed)
512
if (IOCTL(dsp->fd, SNDCTL_DSP_SPEED, &arg))
514
fprintf(stderr, "sound: %s: failed to set sample rate\n", dsp->path);
518
PRINTF(("sound: %s: %d samples/sec\n", dsp->path, dsp->hw.rate));
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);
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
533
static int dspSetFragSize(struct dsp *dsp, int nFrames, int nChannels)
535
int fragSize= nFrames * dsp->hw.bpf;
537
for (i= 0; fragSize; i++) fragSize >>= 1;
538
fragSize= (4 /* fragments */ << 16) | (i - 1) /* ^2 bytesPerFragment */;
540
if (( IOCTL(dsp->fd, SNDCTL_DSP_SETFRAGMENT, &fragSize))
541
|| IOCTL(dsp->fd, SNDCTL_DSP_GETBLKSIZE, &fragSize))
543
fprintf(stderr, "sound: %s: failed to set fragment size\n", dsp->path);
546
assert(fragSize > 0);
547
dsp->hw.fragSize= fragSize;
548
dsp->sq.fragSize= fragSize / dsp->hw.bpf * dsp->sq.bpf;
550
PRINTF(("sound: %s: fragment size set to %d (%d frames requested in %d channels)\n",
551
dsp->path, fragSize, nFrames, nChannels));
557
/* Set the input/output functions according to the current sq/hw
560
static int dspSetConversion(struct dsp *dsp)
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 */
569
assert(io <= FMT_MAX);
571
sm= (((dsp->sq.format & FMT_M) << 1) | (dsp->hw.format & FMT_M)) >> 3;
574
dsp->write= writers[sm][io];
575
assert(dsp->write != 0);
577
sm= (((dsp->hw.format & FMT_M) << 1) | (dsp->sq.format & FMT_M)) >> 3;
580
dsp->read= readers[sm][io];
581
assert(dsp->read != 0);
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);
590
static int dspSetSemaphore(struct dsp *dsp, int semaIndex)
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));
602
static void dspSetTrigger(struct dsp *dsp, int mask)
604
if (dsp->caps & DSP_CAP_TRIGGER)
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);
616
static int dspGetInputSpace(struct dsp *dsp)
618
struct audio_buf_info info;
619
if (ioctl(dsp->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
621
fprintf(stderr, "sound: %s: ", dsp->path);
629
static int dspGetOutputSpace(struct dsp *dsp)
631
struct audio_buf_info info;
632
if (ioctl(dsp->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
634
fprintf(stderr, "sound: %s: ", dsp->path);
645
static void dspHandler(int fd, void *data, int flags)
647
struct dsp *dsp= (struct dsp *)data;
649
assert(dsp->semaphore > 0);
650
signalSemaphoreWithIndex(dsp->semaphore);
651
aioHandle(fd, dspHandler, flags);
655
/*** sound output ***/
658
static sqInt sound_Stop(void)
660
PRINTF(("sound: stop\n"));
666
PRINTF(("sound: %s: device closed\n", out->path));
670
aioSuspend(out->fd, AIO_W);
671
PRINTF(("sound: %s: aio suspended\n", out->path));
679
static sqInt sound_Start(sqInt frameCount, sqInt samplesPerSec, sqInt stereo, sqInt semaIndex)
681
int nChannels= (stereo ? 2 : 1);
682
PRINTF(("sound: start\n"));
684
#ifndef USE_PLAY_SEMAPHORE
687
PRINTF(("sound: asynchronous output disabled\n"));
688
return success(false);
692
if ((out= dspOpen(&dev_dsp, O_WRONLY | O_NONBLOCK)))
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)
718
PRINTF(("sound: could not start\n"));
723
static sqInt sound_AvailableSpace(void)
731
# ifdef USE_PLAY_SEMAPHORE
733
aioHandle(out->fd, dspHandler, AIO_W);
737
bytes= dspGetOutputSpace(out);
739
/* hardware bytes -> frames -> squeak bytes */
740
return bytes / out->hw.bpf * out->sq.bpf;
742
PRINTF(("sound: available space: 0 bytes\n"));
747
static sqInt sound_InsertSamplesFromLeadTime(sqInt frameCount, void *srcBufPtr, sqInt samplesOfLeadTime)
749
return success(false);
753
static sqInt sound_PlaySamplesFromAtLength(sqInt frameCount, void *srcBufPtr, sqInt startIndex)
755
assert(out->write != 0);
756
return out->write(out, srcBufPtr + startIndex * out->sq.bpf, frameCount);
760
static sqInt sound_PlaySilence(void)
762
if (!out) return success(false);
763
return out->sq.fragSize;
770
static sqInt sound_StopRecording(void)
772
PRINTF(("sound: stop recording\n"));
778
PRINTF(("sound: %s: device closed\n", in->path));
782
aioSuspend(in->fd, AIO_R);
783
PRINTF(("sound: %s: aio suspended\n", in->path));
791
static sqInt sound_StartRecording(sqInt desiredSamplesPerSec, sqInt stereo, sqInt semaIndex)
793
int nChannels= (stereo ? 2 : 1);
794
PRINTF(("sound: start recording\n"));
796
if (( (in= dspOpen(&dev_dsp1, O_RDONLY)))
797
|| (in= dspOpen(&dev_dsp, O_RDONLY)))
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))
807
dspSetTrigger(in, PCM_ENABLE_INPUT);
808
aioHandle(in->fd, dspHandler, AIO_R);
812
sound_StopRecording();
814
PRINTF(("sound: could not start recording\n"));
819
static double sound_GetRecordingSampleRate(void)
821
return in ? (double)in->hw.rate : 0.0l;
825
static sqInt sound_RecordSamplesIntoAtLength(void *buf, sqInt startSliceIndex, sqInt bufferSizeInBytes)
827
/*PRINTF(("record %d %d %d\n", buf, startSliceIndex, bufferSizeInBytes));*/
831
/* start index is in samples (rather than bytes or frames???) */
832
int frameCount= ((bufferSizeInBytes / 2) - startSliceIndex) / in->sq.channels;
837
bytesAvail= dspGetInputSpace(in);
839
return 0; /* underrun */
841
else /* initial read required to start recording on some devices */
843
bytesAvail= in->hw.fragSize;
846
assert(bytesAvail > 0);
847
framesAvail= bytesAvail / in->hw.bpf;
848
frameCount= min(frameCount, framesAvail);
849
/*PRINTF(("<%d", frameCount * in->hw.bpf));*/
851
buf + startSliceIndex * 2,
864
* - output volume is connected to PCM unless there is no such
865
* device, in which case we try master VOLUME instead.
867
* - input volume is connected to RECLEVEL unless there is no such
868
* device, in which case we try IGAIN instead.
871
#define LEVEL_MAX 100
877
int devices; /* available mixer devices */
880
struct mixer dev_mixer= { "/dev/mixer", -1 };
882
struct mixer *mixer= 0;
885
static struct mixer *mixerOpen(struct mixer *mix)
888
assert(mix->fd == -1);
890
if ((mix->fd= open(mix->path, O_RDWR, 0)) < 0)
892
fprintf(stderr, "sound: ");
896
PRINTF(("sound: %s: opened with mode %d\n", mix->path, O_RDWR));
898
/* read available devices */
900
if (IOCTL(mix->fd, SOUND_MIXER_READ_DEVMASK, &mix->devices))
903
printf("sound: %s: available devices:", mix->path);
906
for (i= 0; i < SOUND_MIXER_NRDEVICES; ++i)
907
if (mix->devices & (1 << i))
908
printf(" %s", devName(i));
918
static int mixerGetLevel(struct mixer *mix, int device, int *left, int *right)
921
assert(mix->fd >= 0);
923
if (mix->devices & (1 << device))
926
if (IOCTL(mix->fd, MIXER_READ(device), &vol) >= 0)
928
*left= (vol >> 8) & 0xff;
929
*right= (vol ) & 0xff;
937
static int mixerSetLevel(struct mixer *mix, int device, int left, int right)
940
assert(mix->fd >= 0);
941
assert((left >= 0) && (left <= LEVEL_MAX));
942
assert((right >= 0) && (right <= LEVEL_MAX));
944
if (mix->devices & (1 << device))
946
int vol= (left << 8) | right;
947
if (IOCTL(mix->fd, MIXER_WRITE(device), &vol) >= 0)
949
PRINTF(("sound: %s: %s: level set to %d%% + %d%%\n", mix->path,
950
devName(device), left, right));
954
PRINTF(("sound: %s: %s: device not available\n", mix->path, devName(device)));
960
static void sound_Volume(double *left, double *right)
962
if (mixer || (mixer= mixerOpen(&dev_mixer)))
965
if (( mixerGetLevel(mixer, SOUND_MIXER_PCM, &l, &r))
966
|| mixerGetLevel(mixer, SOUND_MIXER_VOLUME, &l, &r))
968
*left= (double)l / (double)LEVEL_MAX;
969
*right= (double)r / (double)LEVEL_MAX;
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
987
static void sound_SetVolume(double left, double right)
989
if (noSoundMixer) return;
990
if (mixer || (mixer= mixerOpen(&dev_mixer)))
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))
1005
static int sound_RecordLevel(int *level)
1007
if (mixer || (mixer= mixerOpen(&dev_mixer)))
1010
if (( mixerGetLevel(mixer, SOUND_MIXER_RECLEV, &l, &r))
1011
|| mixerGetLevel(mixer, SOUND_MIXER_IGAIN, &l, &r))
1013
/* record level is average of l+r in the range 0..1000 */
1014
*level= 1000 * (l + r) / 2 / 100;
1018
return success(false);
1023
static void sound_SetRecordLevel(sqInt level)
1025
if (noSoundMixer) return;
1026
if (mixer || (mixer= mixerOpen(&dev_mixer)))
1028
level= level * LEVEL_MAX / 1000;
1031
else if (level > 255)
1034
if (mixerSetLevel(mixer, SOUND_MIXER_RECLEV, level, level)) return;
1035
if (mixerSetLevel(mixer, SOUND_MIXER_IGAIN, level, level)) return;
1039
static sqInt sound_SetSwitch(sqInt id, sqInt captureFlag, sqInt parameter)
1044
static sqInt sound_GetSwitch(sqInt id, sqInt captureFlag, sqInt channel)
1049
static sqInt sound_SetDevice(sqInt id, char *arg)
1059
static char *afmtName(int i)
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";
1074
return "*** UNKNOWN ***";
1077
static char *capName(int i)
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";
1088
return "*** UNKNOWN ***";
1091
static char *devName(int dev)
1093
static char *names[]= SOUND_DEVICE_NAMES;
1094
if ((dev >= 0) && (dev <SOUND_MIXER_NRDEVICES)) return names[dev];
1095
return "*** ILLEGAL ***";
1098
static char *rdName(reader rd)
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 ***";
1127
static char *wrName(writer wr)
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 ***";
1159
#include "SqSound.h"
1164
#include "SqModule.h"
1166
static void sound_parseEnvironment(void)
1168
if (getenv("SQUEAK_NOMIXER")) noSoundMixer= 1;
1171
static int sound_parseArgument(int argc, char **argv)
1173
if (!strcmp(argv[0], "-nomixer")) return noSoundMixer= 1;
1177
static void sound_printUsage(void)
1179
printf("\nOSS <option>s:\n");
1180
printf(" -nomixer disable mixer (volume) adjustment\n");
1183
static void sound_printUsageNotes(void)
1187
static void *sound_makeInterface(void)
1189
return &sound_OSS_itf;
1192
SqModuleDefine(sound, OSS);