formats: clarify setting of reverse_bytes
[sox.git] / src / oss.c
blob35c7cd2fae9bbf035f60484e706580dda368a439
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.
19 * Changes:
21 * Nov. 26, 1999 Stan Brooks <stabro@megsinet.net>
22 * Moved initialization code common to startread and startwrite
23 * into a single function ossdspinit().
27 #include "sox_i.h"
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #ifdef HAVE_SYS_SOUNDCARD_H
33 #include <sys/soundcard.h>
34 #endif
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
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.
43 #ifndef AFMT_S32_LE
44 #define AFMT_S32_LE 0x00001000
45 #endif
46 #ifndef AFMT_S32_BE
47 #define AFMT_S32_BE 0x00002000
48 #endif
50 #include <sys/ioctl.h>
52 typedef struct
54 char* pOutput;
55 unsigned cOutput;
56 int device;
57 unsigned sample_shift;
58 } priv_t;
60 /* common r/w initialization code */
61 static int ossinit(sox_format_t* ft)
63 int sampletype, samplesize;
64 int tmp, rc;
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);
75 else
77 szDevname = "/dev/dsp";
78 lsx_report("Using default OSS device name: %s", szDevname);
81 else
83 szDevname = ft->filename;
84 lsx_report("Using user-specified device name: %s", szDevname);
87 pPriv->device = open(
88 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);
92 return SOX_EOF;
95 if (ft->encoding.bits_per_sample == 8) {
96 sampletype = AFMT_U8;
97 samplesize = 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;
111 else
112 sampletype = (MACHINE_IS_BIGENDIAN) ? AFMT_S16_BE : AFMT_S16_LE;
113 samplesize = 16;
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;
127 else
128 sampletype = (MACHINE_IS_BIGENDIAN) ? AFMT_S32_BE : AFMT_S32_LE;
129 samplesize = 32;
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;
139 else {
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;
143 else
144 sampletype = (MACHINE_IS_BIGENDIAN) ? AFMT_S16_BE : AFMT_S16_LE;
145 samplesize = 16;
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);
158 return(SOX_EOF);
161 /* Query the supported formats and find the best match
163 rc = ioctl(pPriv->device, SNDCTL_DSP_GETFMTS, &tmp);
164 if (rc == 0) {
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;
176 samplesize = 8;
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;
187 samplesize = 16;
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;
203 tmp = sampletype;
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);
210 return (SOX_EOF);
213 tmp = 1;
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");
238 return (SOX_EOF);
241 if (ft->mode == 'r') {
242 pPriv->cOutput = 0;
243 pPriv->pOutput = NULL;
244 } else {
245 size_t cbOutput = sox_globals.bufsiz;
246 pPriv->cOutput = cbOutput >> pPriv->sample_shift;
247 pPriv->pOutput = lsx_malloc(cbOutput);
250 return(SOX_SUCCESS);
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);
262 return SOX_SUCCESS;
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;
270 size_t i, cRead;
271 int cbRead;
272 SOX_SAMPLE_LOCALS;
273 LSX_USE_VAR(sox_macro_temp_double);
275 while (cbOutputLeft) {
276 cbRead = read(pPriv->device, pbOutput, cbOutputLeft);
277 if (cbRead <= 0) {
278 if (cbRead < 0) {
279 lsx_fail_errno(ft, errno, "Error reading from device");
280 return 0;
282 break;
284 cbOutputLeft -= cbRead;
285 pbOutput += cbRead;
288 /* Convert in-place (backwards) */
289 cRead = cOutput - (cbOutputLeft >> pPriv->sample_shift);
290 if (ft->encoding.reverse_bytes) {
291 switch (pPriv->sample_shift)
293 case 0:
294 for (i = cRead; i != 0; i--) {
295 pOutput[i - 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE(
296 ((sox_uint8_t*)pOutput)[i - 1],
297 dummy);
299 break;
300 case 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]),
304 dummy);
306 break;
307 case 2:
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]),
311 dummy);
313 break;
315 } else {
316 switch (pPriv->sample_shift)
318 case 0:
319 for (i = cRead; i != 0; i--) {
320 pOutput[i - 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE(
321 ((sox_uint8_t*)pOutput)[i - 1],
322 dummy);
324 break;
325 case 1:
326 for (i = cRead; i != 0; i--) {
327 pOutput[i - 1] = SOX_SIGNED_16BIT_TO_SAMPLE(
328 ((sox_int16_t*)pOutput)[i - 1],
329 dummy);
331 break;
332 case 2:
333 for (i = cRead; i != 0; i--) {
334 pOutput[i - 1] = SOX_SIGNED_32BIT_TO_SAMPLE(
335 ((sox_int32_t*)pOutput)[i - 1],
336 dummy);
338 break;
342 return cRead;
345 static size_t osswrite(
346 sox_format_t* ft,
347 const sox_sample_t* pInput,
348 size_t cInput)
350 priv_t* pPriv = (priv_t*)ft->priv;
351 size_t cInputRemaining = cInput;
352 unsigned cClips = 0;
353 SOX_SAMPLE_LOCALS;
355 while (cInputRemaining) {
356 size_t cStride;
357 size_t i;
358 size_t cbStride;
359 int cbWritten;
361 cStride = cInputRemaining;
362 if (cStride > pPriv->cOutput) {
363 cStride = pPriv->cOutput;
366 if (ft->encoding.reverse_bytes)
368 switch (pPriv->sample_shift)
370 case 0:
371 for (i = 0; i != cStride; i++) {
372 ((sox_uint8_t*)pPriv->pOutput)[i] =
373 SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput[i], cClips);
375 break;
376 case 1:
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);
381 break;
382 case 2:
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));
387 break;
389 } else {
390 switch (pPriv->sample_shift)
392 case 0:
393 for (i = 0; i != cStride; i++) {
394 ((sox_uint8_t*)pPriv->pOutput)[i] =
395 SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput[i], cClips);
397 break;
398 case 1:
399 for (i = 0; i != cStride; i++) {
400 ((sox_int16_t*)pPriv->pOutput)[i] =
401 SOX_SAMPLE_TO_SIGNED_16BIT(pInput[i], cClips);
403 break;
404 case 2:
405 for (i = 0; i != cStride; i++) {
406 ((sox_int32_t*)pPriv->pOutput)[i] =
407 SOX_SAMPLE_TO_SIGNED_32BIT(pInput[i], cClips);
409 break;
413 cbStride = cStride << pPriv->sample_shift;
414 i = 0;
415 do {
416 cbWritten = write(pPriv->device, &pPriv->pOutput[i], cbStride - i);
417 i += cbWritten;
418 if (cbWritten <= 0) {
419 lsx_fail_errno(ft, errno, "Error writing to device");
420 return 0;
422 } while (i != cbStride);
424 cInputRemaining -= cStride;
425 pInput += cStride;
428 return cInput;
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)
445 return &handler;