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