1 /* libSoX direct to Sun Audio Driver
2  *
3  * Added by Chris Bagwell (cbagwell@sprynet.com) on 2/26/96
4  * Based on oss handler.
5  *
6  * Cleaned up changes of format somewhat in sunstartwrite on 03/31/98
7  *
8  */
9 
10 /*
11  * Copyright 1997 Chris Bagwell And Sundry Contributors
12  * This source code is freely redistributable and may be used for
13  * any purpose.  This copyright notice must be maintained.
14  * Rick Richardson, Lance Norskog And Sundry Contributors are not
15  * responsible for the consequences of using this software.
16  */
17 
18 #include "sox_i.h"
19 #include "g711.h"
20 
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #ifdef HAVE_SUN_AUDIOIO_H
24   #include <sun/audioio.h>
25 #else
26   #include <sys/audioio.h>
27 #endif
28 #include <errno.h>
29 #if !defined(__NetBSD__) && !defined(__OpenBSD__)
30 #include <stropts.h>
31 #endif
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 
39 typedef struct
40 {
41     char* pOutput;
42     unsigned cOutput;
43     int device;
44     unsigned sample_shift;
45 } priv_t;
46 
47 /*
48  * Do anything required before you start reading samples.
49  * Read file header.
50  *      Find out sampling rate,
51  *      size and encoding of samples,
52  *      mono/stereo/quad.
53  */
sunstartread(sox_format_t * ft)54 static int sunstartread(sox_format_t * ft)
55 {
56     char const* szDevname;
57     priv_t* pPriv = (priv_t*)ft->priv;
58 
59     size_t samplesize, encoding;
60     audio_info_t audio_if;
61 #ifdef __SVR4
62     audio_device_t audio_dev;
63 #endif
64     char simple_hw=0;
65 
66     lsx_set_signal_defaults(ft);
67 
68     if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) {
69         szDevname = "/dev/audio";
70     } else {
71         szDevname = ft->filename;
72     }
73 
74     pPriv->device = open(szDevname, O_RDONLY);
75     if (pPriv->device < 0) {
76         lsx_fail_errno(ft, errno, "open failed for device %s", szDevname);
77         return SOX_EOF;
78     }
79 
80     if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) ft->encoding.encoding = SOX_ENCODING_ULAW;
81 
82 #ifdef __SVR4
83     /* Read in old values, change to what we need and then send back */
84     if (ioctl(pPriv->device, AUDIO_GETDEV, &audio_dev) < 0) {
85         lsx_fail_errno(ft,errno,"Unable to get information for device %s", szDevname);
86         return(SOX_EOF);
87     }
88     lsx_report("Hardware detected:  %s",audio_dev.name);
89     if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
90     {
91         simple_hw = 1;
92     }
93 #endif
94 
95     /* If simple hardware detected in force data to ulaw. */
96     if (simple_hw)
97     {
98         if (ft->encoding.bits_per_sample == 8)
99         {
100             if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
101                 ft->encoding.encoding != SOX_ENCODING_ALAW)
102             {
103                 lsx_report("Warning: Detected simple hardware.  Forcing output to ULAW");
104                 ft->encoding.encoding = SOX_ENCODING_ULAW;
105             }
106         }
107         else if (ft->encoding.bits_per_sample == 16)
108         {
109             lsx_report("Warning: Detected simple hardware.  Forcing output to ULAW");
110             ft->encoding.bits_per_sample = 8;
111             ft->encoding.encoding = SOX_ENCODING_ULAW;
112         }
113     }
114 
115     if (ft->encoding.bits_per_sample == 8) {
116         samplesize = 8;
117         pPriv->sample_shift = 0;
118         if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
119             ft->encoding.encoding != SOX_ENCODING_ALAW &&
120             ft->encoding.encoding != SOX_ENCODING_SIGN2) {
121             lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports ULAW, ALAW, and signed linear for bytes.");
122                 return (SOX_EOF);
123         }
124         if ((ft->encoding.encoding == SOX_ENCODING_ULAW ||
125              ft->encoding.encoding == SOX_ENCODING_ALAW) &&
126             ft->signal.channels == 2)
127         {
128             lsx_report("Warning: only support mono for ULAW and ALAW data.  Forcing to mono.");
129             ft->signal.channels = 1;
130         }
131     }
132     else if (ft->encoding.bits_per_sample == 16) {
133         samplesize = 16;
134         pPriv->sample_shift = 1;
135         if (ft->encoding.encoding != SOX_ENCODING_SIGN2) {
136             lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports signed linear for words.");
137             return(SOX_EOF);
138         }
139     }
140     else {
141         lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports bytes and words");
142         return(SOX_EOF);
143     }
144 
145     if (ft->signal.channels == 0)
146         ft->signal.channels = 1;
147     else if (ft->signal.channels > 1) {
148         lsx_report("Warning: some Sun audio devices can not play stereo");
149         lsx_report("at all or sometimes only with signed words.  If the");
150         lsx_report("sound seems sluggish then this is probably the case.");
151         lsx_report("Try forcing output to signed words or use the avg");
152         lsx_report("filter to reduce the number of channels.");
153         ft->signal.channels = 2;
154     }
155 
156     /* Read in old values, change to what we need and then send back */
157     if (ioctl(pPriv->device, AUDIO_GETINFO, &audio_if) < 0) {
158         lsx_fail_errno(ft,errno,"Unable to initialize %s", szDevname);
159         return(SOX_EOF);
160     }
161     audio_if.record.precision = samplesize;
162     audio_if.record.channels = ft->signal.channels;
163     audio_if.record.sample_rate = ft->signal.rate;
164     if (ft->encoding.encoding == SOX_ENCODING_ULAW)
165         encoding = AUDIO_ENCODING_ULAW;
166     else if (ft->encoding.encoding == SOX_ENCODING_ALAW)
167         encoding = AUDIO_ENCODING_ALAW;
168     else
169         encoding = AUDIO_ENCODING_LINEAR;
170     audio_if.record.encoding = encoding;
171 
172     ioctl(pPriv->device, AUDIO_SETINFO, &audio_if);
173     if (audio_if.record.precision != samplesize) {
174         lsx_fail_errno(ft,errno,"Unable to initialize sample size for %s", szDevname);
175         return(SOX_EOF);
176     }
177     if (audio_if.record.channels != ft->signal.channels) {
178         lsx_fail_errno(ft,errno,"Unable to initialize number of channels for %s", szDevname);
179         return(SOX_EOF);
180     }
181     if (audio_if.record.sample_rate != ft->signal.rate) {
182         lsx_fail_errno(ft,errno,"Unable to initialize rate for %s", szDevname);
183         return(SOX_EOF);
184     }
185     if (audio_if.record.encoding != encoding) {
186         lsx_fail_errno(ft,errno,"Unable to initialize encoding for %s", szDevname);
187         return(SOX_EOF);
188     }
189     /* Flush any data in the buffers - its probably in the wrong format */
190 #if defined(__NetBSD__) || defined(__OpenBSD__)
191     ioctl(pPriv->device, AUDIO_FLUSH);
192 #elif defined __GLIBC__
193     ioctl(pPriv->device, (unsigned long int)I_FLUSH, FLUSHR);
194 #else
195     ioctl(pPriv->device, I_FLUSH, FLUSHR);
196 #endif
197 
198     pPriv->cOutput = 0;
199     pPriv->pOutput = NULL;
200 
201     return (SOX_SUCCESS);
202 }
203 
sunstartwrite(sox_format_t * ft)204 static int sunstartwrite(sox_format_t * ft)
205 {
206     size_t samplesize, encoding;
207     audio_info_t audio_if;
208 #ifdef __SVR4
209     audio_device_t audio_dev;
210 #endif
211     char simple_hw=0;
212     char const* szDevname;
213     priv_t* pPriv = (priv_t*)ft->priv;
214 
215     if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) {
216         szDevname = "/dev/audio";
217     } else {
218         szDevname = ft->filename;
219     }
220 
221     pPriv->device = open(szDevname, O_WRONLY);
222     if (pPriv->device < 0) {
223         lsx_fail_errno(ft, errno, "open failed for device: %s", szDevname);
224         return SOX_EOF;
225     }
226 
227 #ifdef __SVR4
228     /* Read in old values, change to what we need and then send back */
229     if (ioctl(pPriv->device, AUDIO_GETDEV, &audio_dev) < 0) {
230         lsx_fail_errno(ft,errno,"Unable to get device information.");
231         return(SOX_EOF);
232     }
233     lsx_report("Hardware detected:  %s",audio_dev.name);
234     if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
235     {
236         simple_hw = 1;
237     }
238 #endif
239 
240     if (simple_hw)
241     {
242         if (ft->encoding.bits_per_sample == 8)
243         {
244             if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
245                 ft->encoding.encoding != SOX_ENCODING_ALAW)
246             {
247                 lsx_report("Warning: Detected simple hardware.  Forcing output to ULAW");
248                 ft->encoding.encoding = SOX_ENCODING_ULAW;
249             }
250         }
251         else if (ft->encoding.bits_per_sample == 16)
252         {
253             lsx_report("Warning: Detected simple hardware.  Forcing output to ULAW");
254             ft->encoding.bits_per_sample = 8;
255             ft->encoding.encoding = SOX_ENCODING_ULAW;
256         }
257     }
258 
259     if (ft->encoding.bits_per_sample == 8)
260     {
261         samplesize = 8;
262         pPriv->sample_shift = 0;
263         if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN)
264             ft->encoding.encoding = SOX_ENCODING_ULAW;
265         else if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
266             ft->encoding.encoding != SOX_ENCODING_ALAW &&
267             ft->encoding.encoding != SOX_ENCODING_SIGN2) {
268             lsx_report("Sun Audio driver only supports ULAW, ALAW, and Signed Linear for bytes.");
269             lsx_report("Forcing to ULAW");
270             ft->encoding.encoding = SOX_ENCODING_ULAW;
271         }
272         if ((ft->encoding.encoding == SOX_ENCODING_ULAW ||
273              ft->encoding.encoding == SOX_ENCODING_ALAW) &&
274             ft->signal.channels == 2)
275         {
276             lsx_report("Warning: only support mono for ULAW and ALAW data.  Forcing to mono.");
277             ft->signal.channels = 1;
278         }
279 
280     }
281     else if (ft->encoding.bits_per_sample == 16) {
282         samplesize = 16;
283         pPriv->sample_shift = 1;
284         if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN)
285             ft->encoding.encoding = SOX_ENCODING_SIGN2;
286         else if (ft->encoding.encoding != SOX_ENCODING_SIGN2) {
287             lsx_report("Sun Audio driver only supports Signed Linear for words.");
288             lsx_report("Forcing to Signed Linear");
289             ft->encoding.encoding = SOX_ENCODING_SIGN2;
290         }
291     }
292     else {
293         lsx_report("Sun Audio driver only supports bytes and words");
294         ft->encoding.bits_per_sample = 16;
295         ft->encoding.encoding = SOX_ENCODING_SIGN2;
296         samplesize = 16;
297         pPriv->sample_shift = 1;
298     }
299 
300     if (ft->signal.channels > 1) ft->signal.channels = 2;
301 
302     /* Read in old values, change to what we need and then send back */
303     if (ioctl(pPriv->device, AUDIO_GETINFO, &audio_if) < 0) {
304         lsx_fail_errno(ft,errno,"Unable to initialize /dev/audio");
305         return(SOX_EOF);
306     }
307     audio_if.play.precision = samplesize;
308     audio_if.play.channels = ft->signal.channels;
309     audio_if.play.sample_rate = ft->signal.rate;
310     if (ft->encoding.encoding == SOX_ENCODING_ULAW)
311         encoding = AUDIO_ENCODING_ULAW;
312     else if (ft->encoding.encoding == SOX_ENCODING_ALAW)
313         encoding = AUDIO_ENCODING_ALAW;
314     else
315         encoding = AUDIO_ENCODING_LINEAR;
316     audio_if.play.encoding = encoding;
317 
318     ioctl(pPriv->device, AUDIO_SETINFO, &audio_if);
319     if (audio_if.play.precision != samplesize) {
320         lsx_fail_errno(ft,errno,"Unable to initialize sample size for /dev/audio");
321         return(SOX_EOF);
322     }
323     if (audio_if.play.channels != ft->signal.channels) {
324         lsx_fail_errno(ft,errno,"Unable to initialize number of channels for /dev/audio");
325         return(SOX_EOF);
326     }
327     if (audio_if.play.sample_rate != ft->signal.rate) {
328         lsx_fail_errno(ft,errno,"Unable to initialize rate for /dev/audio");
329         return(SOX_EOF);
330     }
331     if (audio_if.play.encoding != encoding) {
332         lsx_fail_errno(ft,errno,"Unable to initialize encoding for /dev/audio");
333         return(SOX_EOF);
334     }
335 
336     pPriv->cOutput = sox_globals.bufsiz >> pPriv->sample_shift;
337     pPriv->pOutput = lsx_malloc((size_t)pPriv->cOutput << pPriv->sample_shift);
338 
339     return (SOX_SUCCESS);
340 }
341 
sunstop(sox_format_t * ft)342 static int sunstop(sox_format_t* ft)
343 {
344     priv_t* pPriv = (priv_t*)ft->priv;
345     if (pPriv->device >= 0) {
346         close(pPriv->device);
347     }
348     if (pPriv->pOutput) {
349         free(pPriv->pOutput);
350     }
351     return SOX_SUCCESS;
352 }
353 
354 typedef sox_uint16_t sox_uint14_t;
355 typedef sox_uint16_t sox_uint13_t;
356 typedef sox_int16_t sox_int14_t;
357 typedef sox_int16_t sox_int13_t;
358 #define SOX_ULAW_BYTE_TO_SAMPLE(d,clips)   SOX_SIGNED_16BIT_TO_SAMPLE(sox_ulaw2linear16(d),clips)
359 #define SOX_ALAW_BYTE_TO_SAMPLE(d,clips)   SOX_SIGNED_16BIT_TO_SAMPLE(sox_alaw2linear16(d),clips)
360 #define SOX_SAMPLE_TO_ULAW_BYTE(d,c) sox_14linear2ulaw(SOX_SAMPLE_TO_UNSIGNED(14,d,c) - 0x2000)
361 #define SOX_SAMPLE_TO_ALAW_BYTE(d,c) sox_13linear2alaw(SOX_SAMPLE_TO_UNSIGNED(13,d,c) - 0x1000)
362 
sunread(sox_format_t * ft,sox_sample_t * pOutput,size_t cOutput)363 static size_t sunread(sox_format_t* ft, sox_sample_t* pOutput, size_t cOutput)
364 {
365     priv_t* pPriv = (priv_t*)ft->priv;
366     char* pbOutput = (char*)pOutput;
367     size_t cbOutputLeft = cOutput << pPriv->sample_shift;
368     size_t i, cRead;
369     int cbRead;
370     SOX_SAMPLE_LOCALS;
371     LSX_USE_VAR(sox_macro_temp_double);
372 
373     while (cbOutputLeft) {
374         cbRead = read(pPriv->device, pbOutput, cbOutputLeft);
375         if (cbRead <= 0) {
376             if (cbRead < 0) {
377                 lsx_fail_errno(ft, errno, "Error reading from device");
378                 return 0;
379             }
380             break;
381         }
382         cbOutputLeft -= cbRead;
383         pbOutput += cbRead;
384     }
385 
386     /* Convert in-place (backwards) */
387     cRead = cOutput - (cbOutputLeft >> pPriv->sample_shift);
388     switch (pPriv->sample_shift)
389     {
390     case 0:
391         switch (ft->encoding.encoding)
392         {
393         case SOX_ENCODING_SIGN2:
394             for (i = cRead; i != 0; i--) {
395                 pOutput[i - 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE(
396                 ((sox_uint8_t*)pOutput)[i - 1],
397                 dummy);
398             }
399             break;
400         case SOX_ENCODING_ULAW:
401             for (i = cRead; i != 0; i--) {
402                 pOutput[i - 1] = SOX_ULAW_BYTE_TO_SAMPLE(
403                 ((sox_uint8_t*)pOutput)[i - 1],
404                 dummy);
405             }
406             break;
407         case SOX_ENCODING_ALAW:
408             for (i = cRead; i != 0; i--) {
409                 pOutput[i - 1] = SOX_ALAW_BYTE_TO_SAMPLE(
410                 ((sox_uint8_t*)pOutput)[i - 1],
411                 dummy);
412             }
413             break;
414         default:
415             return 0;
416         }
417         break;
418     case 1:
419         for (i = cRead; i != 0; i--) {
420             pOutput[i - 1] = SOX_SIGNED_16BIT_TO_SAMPLE(
421             ((sox_int16_t*)pOutput)[i - 1],
422             dummy);
423         }
424         break;
425     }
426 
427     return cRead;
428 }
429 
sunwrite(sox_format_t * ft,const sox_sample_t * pInput,size_t cInput)430 static size_t sunwrite(
431     sox_format_t* ft,
432     const sox_sample_t* pInput,
433     size_t cInput)
434 {
435     priv_t* pPriv = (priv_t*)ft->priv;
436     size_t cInputRemaining = cInput;
437     unsigned cClips = 0;
438     SOX_SAMPLE_LOCALS;
439 
440     while (cInputRemaining) {
441         size_t cStride;
442         size_t i;
443         size_t cbStride;
444         int cbWritten;
445 
446         cStride = cInputRemaining;
447         if (cStride > pPriv->cOutput) {
448             cStride = pPriv->cOutput;
449         }
450 
451         switch (pPriv->sample_shift)
452         {
453         case 0:
454             switch (ft->encoding.encoding)
455             {
456             case SOX_ENCODING_SIGN2:
457                 for (i = 0; i != cStride; i++) {
458                     ((sox_uint8_t*)pPriv->pOutput)[i] =
459                         SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput[i], cClips);
460                 }
461                 break;
462             case SOX_ENCODING_ULAW:
463                 for (i = 0; i != cStride; i++) {
464                     ((sox_uint8_t*)pPriv->pOutput)[i] =
465                         SOX_SAMPLE_TO_ULAW_BYTE(pInput[i], cClips);
466                 }
467                 break;
468             case SOX_ENCODING_ALAW:
469                 for (i = 0; i != cStride; i++) {
470                     ((sox_uint8_t*)pPriv->pOutput)[i] =
471                         SOX_SAMPLE_TO_ALAW_BYTE(pInput[i], cClips);
472                 }
473                 break;
474             default:
475                 return 0;
476             }
477             break;
478         case 1:
479             for (i = 0; i != cStride; i++) {
480                 ((sox_int16_t*)pPriv->pOutput)[i] =
481                     SOX_SAMPLE_TO_SIGNED_16BIT(pInput[i], cClips);
482             }
483             break;
484         }
485 
486         cbStride = cStride << pPriv->sample_shift;
487         i = 0;
488         do {
489             cbWritten = write(pPriv->device, &pPriv->pOutput[i], cbStride - i);
490             i += cbWritten;
491             if (cbWritten <= 0) {
492                 lsx_fail_errno(ft, errno, "Error writing to device");
493                 return 0;
494             }
495         } while (i != cbStride);
496 
497         cInputRemaining -= cStride;
498         pInput += cStride;
499     }
500 
501     return cInput;
502 }
503 
LSX_FORMAT_HANDLER(sunau)504 LSX_FORMAT_HANDLER(sunau)
505 {
506   static char const * const names[] = {"sunau", NULL};
507   static unsigned const write_encodings[] = {
508     SOX_ENCODING_ULAW, 8, 0,
509     SOX_ENCODING_ALAW, 8, 0,
510     SOX_ENCODING_SIGN2, 8, 16, 0,
511     0};
512   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
513     "Sun audio device driver",
514     names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
515     sunstartread, sunread, sunstop,
516     sunstartwrite, sunwrite, sunstop,
517     NULL, write_encodings, NULL, sizeof(priv_t)
518   };
519   return &handler;
520 }
521