1 /* Copyright 1997 Chris Bagwell And Sundry Contributors
2 * This source code is freely redistributable and may be used for
3 * any purpose. This copyright notice must be maintained.
4 * Chris Bagwell And Sundry Contributors are not
5 * responsible for the consequences of using this software.
7 * Direct to Open Sound System (OSS) sound driver
8 * OSS is a popular unix sound driver for Intel x86 unices (eg. Linux)
9 * and several other unixes (such as SunOS/Solaris).
10 * This driver is compatible with OSS original source that was called
11 * USS, Voxware and TASD.
13 * added by Chris Bagwell (cbagwell@sprynet.com) on 2/19/96
14 * based on info grabed from vplay.c in Voxware snd-utils-3.5 package.
15 * and on LINUX_PLAYER patches added by Greg Lee
16 * which was originally from Directo to Sound Blaster device driver (sbdsp.c).
17 * SBLAST patches by John T. Kohl.
21 * Nov. 26, 1999 Stan Brooks <stabro@megsinet.net>
22 * Moved initialization code common to startread and startwrite
23 * into a single function ossdspinit().
32 #ifdef HAVE_SYS_SOUNDCARD_H
33 #include <sys/soundcard.h>
39 /* these appear in the sys/soundcard.h of OSS 4.x, and in Linux's
40 * sound/core/oss/pcm_oss.c (2.6.24 and later), but are typically
41 * not included in system header files.
44 #define AFMT_S32_LE 0x00001000
47 #define AFMT_S32_BE 0x00002000
50 #include <sys/ioctl.h>
57 unsigned sample_shift
;
60 /* common r/w initialization code */
61 static int ossinit(sox_format_t
* ft
)
63 int sampletype
, samplesize
;
65 char const* szDevname
;
66 priv_t
* pPriv
= (priv_t
*)ft
->priv
;
68 if (ft
->filename
== 0 || ft
->filename
[0] == 0 || !strcasecmp("default", ft
->filename
))
70 szDevname
= getenv("OSS_AUDIODEV");
71 if (szDevname
!= NULL
)
73 lsx_report("Using device name from OSS_AUDIODEV environment variable: %s", szDevname
);
77 szDevname
= "/dev/dsp";
78 lsx_report("Using default OSS device name: %s", szDevname
);
83 szDevname
= ft
->filename
;
84 lsx_report("Using user-specified device name: %s", szDevname
);
89 ft
->mode
== 'r' ? O_RDONLY
: O_WRONLY
);
90 if (pPriv
->device
< 0) {
91 lsx_fail_errno(ft
, errno
, "open failed for device: %s", szDevname
);
95 if (ft
->encoding
.bits_per_sample
== 8) {
98 pPriv
->sample_shift
= 0;
99 if (ft
->encoding
.encoding
== SOX_ENCODING_UNKNOWN
)
100 ft
->encoding
.encoding
= SOX_ENCODING_UNSIGNED
;
101 if (ft
->encoding
.encoding
!= SOX_ENCODING_UNSIGNED
) {
102 lsx_report("OSS driver only supports unsigned with bytes");
103 lsx_report("Forcing to unsigned");
104 ft
->encoding
.encoding
= SOX_ENCODING_UNSIGNED
;
107 else if (ft
->encoding
.bits_per_sample
== 16) {
108 /* Attempt to use endian that user specified */
109 if (ft
->encoding
.reverse_bytes
)
110 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S16_LE
: AFMT_S16_BE
;
112 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S16_BE
: AFMT_S16_LE
;
114 pPriv
->sample_shift
= 1;
115 if (ft
->encoding
.encoding
== SOX_ENCODING_UNKNOWN
)
116 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
117 if (ft
->encoding
.encoding
!= SOX_ENCODING_SIGN2
) {
118 lsx_report("OSS driver only supports signed with words");
119 lsx_report("Forcing to signed linear");
120 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
123 else if (ft
->encoding
.bits_per_sample
== 32) {
124 /* Attempt to use endian that user specified */
125 if (ft
->encoding
.reverse_bytes
)
126 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S32_LE
: AFMT_S32_BE
;
128 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S32_BE
: AFMT_S32_LE
;
130 pPriv
->sample_shift
= 2;
131 if (ft
->encoding
.encoding
== SOX_ENCODING_UNKNOWN
)
132 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
133 if (ft
->encoding
.encoding
!= SOX_ENCODING_SIGN2
) {
134 lsx_report("OSS driver only supports signed with words");
135 lsx_report("Forcing to signed linear");
136 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
140 /* Attempt to use endian that user specified */
141 if (ft
->encoding
.reverse_bytes
)
142 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S16_LE
: AFMT_S16_BE
;
144 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S16_BE
: AFMT_S16_LE
;
146 pPriv
->sample_shift
= 1;
147 ft
->encoding
.bits_per_sample
= 16;
148 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
149 lsx_report("OSS driver only supports bytes and words");
150 lsx_report("Forcing to signed linear word");
153 ft
->signal
.channels
= 2;
155 if (ioctl(pPriv
->device
, (size_t) SNDCTL_DSP_RESET
, 0) < 0)
157 lsx_fail_errno(ft
,SOX_EOF
,"Unable to reset OSS device %s. Possibly accessing an invalid file/device", szDevname
);
161 /* Query the supported formats and find the best match
163 rc
= ioctl(pPriv
->device
, SNDCTL_DSP_GETFMTS
, &tmp
);
165 if ((tmp
& sampletype
) == 0)
167 /* is 16-bit supported? */
168 if (samplesize
== 16 && (tmp
& (AFMT_S16_LE
|AFMT_S16_BE
)) == 0)
170 /* Must not like 16-bits, try 8-bits */
171 ft
->encoding
.bits_per_sample
= 8;
172 ft
->encoding
.encoding
= SOX_ENCODING_UNSIGNED
;
173 lsx_report("OSS driver doesn't like signed words");
174 lsx_report("Forcing to unsigned bytes");
175 tmp
= sampletype
= AFMT_U8
;
177 pPriv
->sample_shift
= 0;
179 /* is 8-bit supported */
180 else if (samplesize
== 8 && (tmp
& AFMT_U8
) == 0)
182 ft
->encoding
.bits_per_sample
= 16;
183 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
184 lsx_report("OSS driver doesn't like unsigned bytes");
185 lsx_report("Forcing to signed words");
186 sampletype
= (MACHINE_IS_BIGENDIAN
) ? AFMT_S16_BE
: AFMT_S16_LE
;
188 pPriv
->sample_shift
= 1;
190 /* determine which 16-bit format to use */
191 if (samplesize
== 16 && (tmp
& sampletype
) == 0)
193 /* Either user requested something not supported
194 * or hardware doesn't support machine endian.
195 * Force to opposite as the above test showed
196 * it supports at least one of the two endians.
198 sampletype
= (sampletype
== AFMT_S16_BE
) ? AFMT_S16_LE
: AFMT_S16_BE
;
199 ft
->encoding
.reverse_bytes
= !ft
->encoding
.reverse_bytes
;
204 rc
= ioctl(pPriv
->device
, SNDCTL_DSP_SETFMT
, &tmp
);
206 /* Give up and exit */
207 if (rc
< 0 || tmp
!= sampletype
)
209 lsx_fail_errno(ft
,SOX_EOF
,"Unable to set the sample size to %d", samplesize
);
214 if (ioctl(pPriv
->device
, SNDCTL_DSP_STEREO
, &tmp
) < 0 || tmp
!= 1)
216 lsx_warn("Couldn't set to stereo");
217 ft
->signal
.channels
= 1;
220 tmp
= ft
->signal
.rate
;
221 if (ioctl(pPriv
->device
, SNDCTL_DSP_SPEED
, &tmp
) < 0 ||
222 (int)ft
->signal
.rate
!= tmp
) {
223 /* If the rate the sound card is using is not within 1% of what
224 * the user specified then override the user setting.
225 * The only reason not to always override this is because of
226 * clock-rounding problems. Sound cards will sometimes use
227 * things like 44101 when you ask for 44100. No need overriding
228 * this and having strange output file rates for something that
229 * we can't hear anyways.
231 if ((int)ft
->signal
.rate
- tmp
> (tmp
* .01) ||
232 tmp
- (int)ft
->signal
.rate
> (tmp
* .01))
233 ft
->signal
.rate
= tmp
;
236 if (ioctl(pPriv
->device
, (size_t) SNDCTL_DSP_SYNC
, NULL
) < 0) {
237 lsx_fail_errno(ft
,SOX_EOF
,"Unable to sync dsp");
241 if (ft
->mode
== 'r') {
243 pPriv
->pOutput
= NULL
;
245 size_t cbOutput
= sox_globals
.bufsiz
;
246 pPriv
->cOutput
= cbOutput
>> pPriv
->sample_shift
;
247 pPriv
->pOutput
= lsx_malloc(cbOutput
);
253 static int ossstop(sox_format_t
* ft
)
255 priv_t
* pPriv
= (priv_t
*)ft
->priv
;
256 if (pPriv
->device
>= 0) {
257 close(pPriv
->device
);
259 if (pPriv
->pOutput
) {
260 free(pPriv
->pOutput
);
265 static size_t ossread(sox_format_t
* ft
, sox_sample_t
* pOutput
, size_t cOutput
)
267 priv_t
* pPriv
= (priv_t
*)ft
->priv
;
268 char* pbOutput
= (char*)pOutput
;
269 size_t cbOutputLeft
= cOutput
<< pPriv
->sample_shift
;
273 LSX_USE_VAR(sox_macro_temp_double
);
275 while (cbOutputLeft
) {
276 cbRead
= read(pPriv
->device
, pbOutput
, cbOutputLeft
);
279 lsx_fail_errno(ft
, errno
, "Error reading from device");
284 cbOutputLeft
-= cbRead
;
288 /* Convert in-place (backwards) */
289 cRead
= cOutput
- (cbOutputLeft
>> pPriv
->sample_shift
);
290 if (ft
->encoding
.reverse_bytes
) {
291 switch (pPriv
->sample_shift
)
294 for (i
= cRead
; i
!= 0; i
--) {
295 pOutput
[i
- 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE(
296 ((sox_uint8_t
*)pOutput
)[i
- 1],
301 for (i
= cRead
; i
!= 0; i
--) {
302 pOutput
[i
- 1] = SOX_SIGNED_16BIT_TO_SAMPLE(
303 lsx_swapw(((sox_int16_t
*)pOutput
)[i
- 1]),
308 for (i
= cRead
; i
!= 0; i
--) {
309 pOutput
[i
- 1] = SOX_SIGNED_32BIT_TO_SAMPLE(
310 lsx_swapdw(((sox_int32_t
*)pOutput
)[i
- 1]),
316 switch (pPriv
->sample_shift
)
319 for (i
= cRead
; i
!= 0; i
--) {
320 pOutput
[i
- 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE(
321 ((sox_uint8_t
*)pOutput
)[i
- 1],
326 for (i
= cRead
; i
!= 0; i
--) {
327 pOutput
[i
- 1] = SOX_SIGNED_16BIT_TO_SAMPLE(
328 ((sox_int16_t
*)pOutput
)[i
- 1],
333 for (i
= cRead
; i
!= 0; i
--) {
334 pOutput
[i
- 1] = SOX_SIGNED_32BIT_TO_SAMPLE(
335 ((sox_int32_t
*)pOutput
)[i
- 1],
345 static size_t osswrite(
347 const sox_sample_t
* pInput
,
350 priv_t
* pPriv
= (priv_t
*)ft
->priv
;
351 size_t cInputRemaining
= cInput
;
355 while (cInputRemaining
) {
361 cStride
= cInputRemaining
;
362 if (cStride
> pPriv
->cOutput
) {
363 cStride
= pPriv
->cOutput
;
366 if (ft
->encoding
.reverse_bytes
)
368 switch (pPriv
->sample_shift
)
371 for (i
= 0; i
!= cStride
; i
++) {
372 ((sox_uint8_t
*)pPriv
->pOutput
)[i
] =
373 SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput
[i
], cClips
);
377 for (i
= 0; i
!= cStride
; i
++) {
378 sox_int16_t s16
= SOX_SAMPLE_TO_SIGNED_16BIT(pInput
[i
], cClips
);
379 ((sox_int16_t
*)pPriv
->pOutput
)[i
] = lsx_swapw(s16
);
383 for (i
= 0; i
!= cStride
; i
++) {
384 ((sox_int32_t
*)pPriv
->pOutput
)[i
] =
385 lsx_swapdw(SOX_SAMPLE_TO_SIGNED_32BIT(pInput
[i
], cClips
));
390 switch (pPriv
->sample_shift
)
393 for (i
= 0; i
!= cStride
; i
++) {
394 ((sox_uint8_t
*)pPriv
->pOutput
)[i
] =
395 SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput
[i
], cClips
);
399 for (i
= 0; i
!= cStride
; i
++) {
400 ((sox_int16_t
*)pPriv
->pOutput
)[i
] =
401 SOX_SAMPLE_TO_SIGNED_16BIT(pInput
[i
], cClips
);
405 for (i
= 0; i
!= cStride
; i
++) {
406 ((sox_int32_t
*)pPriv
->pOutput
)[i
] =
407 SOX_SAMPLE_TO_SIGNED_32BIT(pInput
[i
], cClips
);
413 cbStride
= cStride
<< pPriv
->sample_shift
;
416 cbWritten
= write(pPriv
->device
, &pPriv
->pOutput
[i
], cbStride
- i
);
418 if (cbWritten
<= 0) {
419 lsx_fail_errno(ft
, errno
, "Error writing to device");
422 } while (i
!= cbStride
);
424 cInputRemaining
-= cStride
;
431 LSX_FORMAT_HANDLER(oss
)
433 static char const* const names
[] = {"ossdsp", "oss", NULL
};
434 static unsigned const write_encodings
[] = {
435 SOX_ENCODING_SIGN2
, 32, 16, 0,
436 SOX_ENCODING_UNSIGNED
, 8, 0,
438 static sox_format_handler_t
const handler
= {SOX_LIB_VERSION_CODE
,
439 "Open Sound System device driver for unix-like systems",
440 names
, SOX_FILE_DEVICE
| SOX_FILE_NOSTDIO
,
441 ossinit
, ossread
, ossstop
,
442 ossinit
, osswrite
, ossstop
,
443 NULL
, write_encodings
, NULL
, sizeof(priv_t
)