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.
6  *
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.
12  *
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.
18  *
19  * Changes:
20  *
21  * Nov. 26, 1999 Stan Brooks <stabro@megsinet.net>
22  *   Moved initialization code common to startread and startwrite
23  *   into a single function ossdspinit().
24  *
25  */
26 
27 #include "sox_i.h"
28 
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
38 
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.
42  */
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
49 
50 #include <sys/ioctl.h>
51 
52 typedef struct
53 {
54     char* pOutput;
55     unsigned cOutput;
56     int device;
57     unsigned sample_shift;
58 } priv_t;
59 
60 /* common r/w initialization code */
ossinit(sox_format_t * ft)61 static int ossinit(sox_format_t* ft)
62 {
63     int sampletype, samplesize;
64     int tmp, rc;
65     char const* szDevname;
66     priv_t* pPriv = (priv_t*)ft->priv;
67 
68     if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename))
69     {
70         szDevname = getenv("OSS_AUDIODEV");
71         if (szDevname != NULL)
72         {
73             lsx_report("Using device name from OSS_AUDIODEV environment variable: %s", szDevname);
74         }
75         else
76         {
77             szDevname = "/dev/dsp";
78             lsx_report("Using default OSS device name: %s", szDevname);
79         }
80     }
81     else
82     {
83         szDevname = ft->filename;
84         lsx_report("Using user-specified device name: %s", szDevname);
85     }
86 
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;
93     }
94 
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;
105         }
106     }
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;
121         }
122     }
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;
137         }
138     }
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");
151     }
152 
153     ft->signal.channels = 2;
154 
155     if (ioctl(pPriv->device, (size_t) SNDCTL_DSP_RESET, 0) < 0)
156     {
157         lsx_fail_errno(ft,SOX_EOF,"Unable to reset OSS device %s. Possibly accessing an invalid file/device", szDevname);
158         return(SOX_EOF);
159     }
160 
161     /* Query the supported formats and find the best match
162      */
163     rc = ioctl(pPriv->device, SNDCTL_DSP_GETFMTS, &tmp);
164     if (rc == 0) {
165         if ((tmp & sampletype) == 0)
166         {
167             /* is 16-bit supported? */
168             if (samplesize == 16 && (tmp & (AFMT_S16_LE|AFMT_S16_BE)) == 0)
169             {
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;
178             }
179             /* is 8-bit supported */
180             else if (samplesize == 8 && (tmp & AFMT_U8) == 0)
181             {
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;
189             }
190             /* determine which 16-bit format to use */
191             if (samplesize == 16 && (tmp & sampletype) == 0)
192             {
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.
197                  */
198                 sampletype = (sampletype == AFMT_S16_BE) ? AFMT_S16_LE : AFMT_S16_BE;
199                 ft->encoding.reverse_bytes = !ft->encoding.reverse_bytes;
200             }
201 
202         }
203         tmp = sampletype;
204         rc = ioctl(pPriv->device, SNDCTL_DSP_SETFMT, &tmp);
205     }
206     /* Give up and exit */
207     if (rc < 0 || tmp != sampletype)
208     {
209         lsx_fail_errno(ft,SOX_EOF,"Unable to set the sample size to %d", samplesize);
210         return (SOX_EOF);
211     }
212 
213     tmp = 1;
214     if (ioctl(pPriv->device, SNDCTL_DSP_STEREO, &tmp) < 0 || tmp != 1)
215     {
216         lsx_warn("Couldn't set to stereo");
217         ft->signal.channels = 1;
218     }
219 
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.
230          */
231         if ((int)ft->signal.rate - tmp > (tmp * .01) ||
232             tmp - (int)ft->signal.rate > (tmp * .01))
233             ft->signal.rate = tmp;
234     }
235 
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);
239     }
240 
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);
248     }
249 
250     return(SOX_SUCCESS);
251 }
252 
ossstop(sox_format_t * ft)253 static int ossstop(sox_format_t* ft)
254 {
255     priv_t* pPriv = (priv_t*)ft->priv;
256     if (pPriv->device >= 0) {
257         close(pPriv->device);
258     }
259     if (pPriv->pOutput) {
260         free(pPriv->pOutput);
261     }
262     return SOX_SUCCESS;
263 }
264 
ossread(sox_format_t * ft,sox_sample_t * pOutput,size_t cOutput)265 static size_t ossread(sox_format_t* ft, sox_sample_t* pOutput, size_t cOutput)
266 {
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);
274 
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;
281             }
282             break;
283         }
284         cbOutputLeft -= cbRead;
285         pbOutput += cbRead;
286     }
287 
288     /* Convert in-place (backwards) */
289     cRead = cOutput - (cbOutputLeft >> pPriv->sample_shift);
290     if (ft->encoding.reverse_bytes) {
291         switch (pPriv->sample_shift)
292         {
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);
298             }
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);
305             }
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);
312             }
313             break;
314         }
315     } else {
316         switch (pPriv->sample_shift)
317         {
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);
323             }
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);
330             }
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);
337             }
338             break;
339         }
340      }
341 
342     return cRead;
343 }
344 
osswrite(sox_format_t * ft,const sox_sample_t * pInput,size_t cInput)345 static size_t osswrite(
346     sox_format_t* ft,
347     const sox_sample_t* pInput,
348     size_t cInput)
349 {
350     priv_t* pPriv = (priv_t*)ft->priv;
351     size_t cInputRemaining = cInput;
352     unsigned cClips = 0;
353     SOX_SAMPLE_LOCALS;
354 
355     while (cInputRemaining) {
356         size_t cStride;
357         size_t i;
358         size_t cbStride;
359         int cbWritten;
360 
361         cStride = cInputRemaining;
362         if (cStride > pPriv->cOutput) {
363             cStride = pPriv->cOutput;
364         }
365 
366         if (ft->encoding.reverse_bytes)
367         {
368             switch (pPriv->sample_shift)
369             {
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);
374                 }
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);
380                 }
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));
386                 }
387                 break;
388             }
389         } else {
390             switch (pPriv->sample_shift)
391             {
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);
396                 }
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);
402                 }
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);
408                 }
409                 break;
410             }
411         }
412 
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;
421             }
422         } while (i != cbStride);
423 
424         cInputRemaining -= cStride;
425         pInput += cStride;
426     }
427 
428     return cInput;
429 }
430 
LSX_FORMAT_HANDLER(oss)431 LSX_FORMAT_HANDLER(oss)
432 {
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,
437     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)
444   };
445   return &handler;
446 }
447