formats: clarify setting of reverse_bytes
[sox.git] / src / sunaudio.c
blobc7cad102097385e8d441ce5e37639a9b0969b0ad
1 /* libSoX direct to Sun Audio Driver
3 * Added by Chris Bagwell (cbagwell@sprynet.com) on 2/26/96
4 * Based on oss handler.
6 * Cleaned up changes of format somewhat in sunstartwrite on 03/31/98
8 */
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.
18 #include "sox_i.h"
19 #include "g711.h"
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
39 typedef struct
41 char* pOutput;
42 unsigned cOutput;
43 int device;
44 unsigned sample_shift;
45 } priv_t;
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.
54 static int sunstartread(sox_format_t * ft)
56 char const* szDevname;
57 priv_t* pPriv = (priv_t*)ft->priv;
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;
66 lsx_set_signal_defaults(ft);
68 if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) {
69 szDevname = "/dev/audio";
70 } else {
71 szDevname = ft->filename;
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;
80 if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) ft->encoding.encoding = SOX_ENCODING_ULAW;
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);
88 lsx_report("Hardware detected: %s",audio_dev.name);
89 if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
91 simple_hw = 1;
93 #endif
95 /* If simple hardware detected in force data to ulaw. */
96 if (simple_hw)
98 if (ft->encoding.bits_per_sample == 8)
100 if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
101 ft->encoding.encoding != SOX_ENCODING_ALAW)
103 lsx_report("Warning: Detected simple hardware. Forcing output to ULAW");
104 ft->encoding.encoding = SOX_ENCODING_ULAW;
107 else if (ft->encoding.bits_per_sample == 16)
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;
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);
124 if ((ft->encoding.encoding == SOX_ENCODING_ULAW ||
125 ft->encoding.encoding == SOX_ENCODING_ALAW) &&
126 ft->signal.channels == 2)
128 lsx_report("Warning: only support mono for ULAW and ALAW data. Forcing to mono.");
129 ft->signal.channels = 1;
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);
140 else {
141 lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports bytes and words");
142 return(SOX_EOF);
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;
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);
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;
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);
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);
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);
185 if (audio_if.record.encoding != encoding) {
186 lsx_fail_errno(ft,errno,"Unable to initialize encoding for %s", szDevname);
187 return(SOX_EOF);
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
198 pPriv->cOutput = 0;
199 pPriv->pOutput = NULL;
201 return (SOX_SUCCESS);
204 static int sunstartwrite(sox_format_t * ft)
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;
215 if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) {
216 szDevname = "/dev/audio";
217 } else {
218 szDevname = ft->filename;
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;
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);
233 lsx_report("Hardware detected: %s",audio_dev.name);
234 if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
236 simple_hw = 1;
238 #endif
240 if (simple_hw)
242 if (ft->encoding.bits_per_sample == 8)
244 if (ft->encoding.encoding != SOX_ENCODING_ULAW &&
245 ft->encoding.encoding != SOX_ENCODING_ALAW)
247 lsx_report("Warning: Detected simple hardware. Forcing output to ULAW");
248 ft->encoding.encoding = SOX_ENCODING_ULAW;
251 else if (ft->encoding.bits_per_sample == 16)
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;
259 if (ft->encoding.bits_per_sample == 8)
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;
272 if ((ft->encoding.encoding == SOX_ENCODING_ULAW ||
273 ft->encoding.encoding == SOX_ENCODING_ALAW) &&
274 ft->signal.channels == 2)
276 lsx_report("Warning: only support mono for ULAW and ALAW data. Forcing to mono.");
277 ft->signal.channels = 1;
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;
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;
300 if (ft->signal.channels > 1) ft->signal.channels = 2;
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);
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;
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);
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);
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);
331 if (audio_if.play.encoding != encoding) {
332 lsx_fail_errno(ft,errno,"Unable to initialize encoding for /dev/audio");
333 return(SOX_EOF);
336 pPriv->cOutput = sox_globals.bufsiz >> pPriv->sample_shift;
337 pPriv->pOutput = lsx_malloc((size_t)pPriv->cOutput << pPriv->sample_shift);
339 return (SOX_SUCCESS);
342 static int sunstop(sox_format_t* ft)
344 priv_t* pPriv = (priv_t*)ft->priv;
345 if (pPriv->device >= 0) {
346 close(pPriv->device);
348 if (pPriv->pOutput) {
349 free(pPriv->pOutput);
351 return SOX_SUCCESS;
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)
363 static size_t sunread(sox_format_t* ft, sox_sample_t* pOutput, size_t cOutput)
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);
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;
380 break;
382 cbOutputLeft -= cbRead;
383 pbOutput += cbRead;
386 /* Convert in-place (backwards) */
387 cRead = cOutput - (cbOutputLeft >> pPriv->sample_shift);
388 switch (pPriv->sample_shift)
390 case 0:
391 switch (ft->encoding.encoding)
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);
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);
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);
413 break;
414 default:
415 return 0;
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);
424 break;
427 return cRead;
430 static size_t sunwrite(
431 sox_format_t* ft,
432 const sox_sample_t* pInput,
433 size_t cInput)
435 priv_t* pPriv = (priv_t*)ft->priv;
436 size_t cInputRemaining = cInput;
437 unsigned cClips = 0;
438 SOX_SAMPLE_LOCALS;
440 while (cInputRemaining) {
441 size_t cStride;
442 size_t i;
443 size_t cbStride;
444 int cbWritten;
446 cStride = cInputRemaining;
447 if (cStride > pPriv->cOutput) {
448 cStride = pPriv->cOutput;
451 switch (pPriv->sample_shift)
453 case 0:
454 switch (ft->encoding.encoding)
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);
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);
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);
473 break;
474 default:
475 return 0;
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);
483 break;
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;
495 } while (i != cbStride);
497 cInputRemaining -= cStride;
498 pInput += cStride;
501 return cInput;
504 LSX_FORMAT_HANDLER(sunau)
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,
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)
519 return &handler;