/* libSoX direct to Sun Audio Driver * * Added by Chris Bagwell (cbagwell@sprynet.com) on 2/26/96 * Based on oss handler. * * Cleaned up changes of format somewhat in sunstartwrite on 03/31/98 * */ /* * Copyright 1997 Chris Bagwell And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Rick Richardson, Lance Norskog And Sundry Contributors are not * responsible for the consequences of using this software. */ #include "sox_i.h" #include "g711.h" #include #include #ifdef HAVE_SUN_AUDIOIO_H #include #else #include #endif #include #if !defined(__NetBSD__) && !defined(__OpenBSD__) #include #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif typedef struct { char* pOutput; unsigned cOutput; int device; unsigned sample_shift; } priv_t; /* * Do anything required before you start reading samples. * Read file header. * Find out sampling rate, * size and encoding of samples, * mono/stereo/quad. */ static int sunstartread(sox_format_t * ft) { char const* szDevname; priv_t* pPriv = (priv_t*)ft->priv; size_t samplesize, encoding; audio_info_t audio_if; #ifdef __SVR4 audio_device_t audio_dev; #endif char simple_hw=0; lsx_set_signal_defaults(ft); if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) { szDevname = "/dev/audio"; } else { szDevname = ft->filename; } pPriv->device = open(szDevname, O_RDONLY); if (pPriv->device < 0) { lsx_fail_errno(ft, errno, "open failed for device %s", szDevname); return SOX_EOF; } if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) ft->encoding.encoding = SOX_ENCODING_ULAW; #ifdef __SVR4 /* Read in old values, change to what we need and then send back */ if (ioctl(pPriv->device, AUDIO_GETDEV, &audio_dev) < 0) { lsx_fail_errno(ft,errno,"Unable to get information for device %s", szDevname); return(SOX_EOF); } lsx_report("Hardware detected: %s",audio_dev.name); if (strcmp("SUNW,am79c30",audio_dev.name) == 0) { simple_hw = 1; } #endif /* If simple hardware detected in force data to ulaw. */ if (simple_hw) { if (ft->encoding.bits_per_sample == 8) { if (ft->encoding.encoding != SOX_ENCODING_ULAW && ft->encoding.encoding != SOX_ENCODING_ALAW) { lsx_report("Warning: Detected simple hardware. Forcing output to ULAW"); ft->encoding.encoding = SOX_ENCODING_ULAW; } } else if (ft->encoding.bits_per_sample == 16) { lsx_report("Warning: Detected simple hardware. Forcing output to ULAW"); ft->encoding.bits_per_sample = 8; ft->encoding.encoding = SOX_ENCODING_ULAW; } } if (ft->encoding.bits_per_sample == 8) { samplesize = 8; pPriv->sample_shift = 0; if (ft->encoding.encoding != SOX_ENCODING_ULAW && ft->encoding.encoding != SOX_ENCODING_ALAW && ft->encoding.encoding != SOX_ENCODING_SIGN2) { lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports ULAW, ALAW, and signed linear for bytes."); return (SOX_EOF); } if ((ft->encoding.encoding == SOX_ENCODING_ULAW || ft->encoding.encoding == SOX_ENCODING_ALAW) && ft->signal.channels == 2) { lsx_report("Warning: only support mono for ULAW and ALAW data. Forcing to mono."); ft->signal.channels = 1; } } else if (ft->encoding.bits_per_sample == 16) { samplesize = 16; pPriv->sample_shift = 1; if (ft->encoding.encoding != SOX_ENCODING_SIGN2) { lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports signed linear for words."); return(SOX_EOF); } } else { lsx_fail_errno(ft,SOX_EFMT,"Sun audio driver only supports bytes and words"); return(SOX_EOF); } if (ft->signal.channels == 0) ft->signal.channels = 1; else if (ft->signal.channels > 1) { lsx_report("Warning: some Sun audio devices can not play stereo"); lsx_report("at all or sometimes only with signed words. If the"); lsx_report("sound seems sluggish then this is probably the case."); lsx_report("Try forcing output to signed words or use the avg"); lsx_report("filter to reduce the number of channels."); ft->signal.channels = 2; } /* Read in old values, change to what we need and then send back */ if (ioctl(pPriv->device, AUDIO_GETINFO, &audio_if) < 0) { lsx_fail_errno(ft,errno,"Unable to initialize %s", szDevname); return(SOX_EOF); } audio_if.record.precision = samplesize; audio_if.record.channels = ft->signal.channels; audio_if.record.sample_rate = ft->signal.rate; if (ft->encoding.encoding == SOX_ENCODING_ULAW) encoding = AUDIO_ENCODING_ULAW; else if (ft->encoding.encoding == SOX_ENCODING_ALAW) encoding = AUDIO_ENCODING_ALAW; else encoding = AUDIO_ENCODING_LINEAR; audio_if.record.encoding = encoding; ioctl(pPriv->device, AUDIO_SETINFO, &audio_if); if (audio_if.record.precision != samplesize) { lsx_fail_errno(ft,errno,"Unable to initialize sample size for %s", szDevname); return(SOX_EOF); } if (audio_if.record.channels != ft->signal.channels) { lsx_fail_errno(ft,errno,"Unable to initialize number of channels for %s", szDevname); return(SOX_EOF); } if (audio_if.record.sample_rate != ft->signal.rate) { lsx_fail_errno(ft,errno,"Unable to initialize rate for %s", szDevname); return(SOX_EOF); } if (audio_if.record.encoding != encoding) { lsx_fail_errno(ft,errno,"Unable to initialize encoding for %s", szDevname); return(SOX_EOF); } /* Flush any data in the buffers - its probably in the wrong format */ #if defined(__NetBSD__) || defined(__OpenBSD__) ioctl(pPriv->device, AUDIO_FLUSH); #elif defined __GLIBC__ ioctl(pPriv->device, (unsigned long int)I_FLUSH, FLUSHR); #else ioctl(pPriv->device, I_FLUSH, FLUSHR); #endif pPriv->cOutput = 0; pPriv->pOutput = NULL; return (SOX_SUCCESS); } static int sunstartwrite(sox_format_t * ft) { size_t samplesize, encoding; audio_info_t audio_if; #ifdef __SVR4 audio_device_t audio_dev; #endif char simple_hw=0; char const* szDevname; priv_t* pPriv = (priv_t*)ft->priv; if (ft->filename == 0 || ft->filename[0] == 0 || !strcasecmp("default", ft->filename)) { szDevname = "/dev/audio"; } else { szDevname = ft->filename; } pPriv->device = open(szDevname, O_WRONLY); if (pPriv->device < 0) { lsx_fail_errno(ft, errno, "open failed for device: %s", szDevname); return SOX_EOF; } #ifdef __SVR4 /* Read in old values, change to what we need and then send back */ if (ioctl(pPriv->device, AUDIO_GETDEV, &audio_dev) < 0) { lsx_fail_errno(ft,errno,"Unable to get device information."); return(SOX_EOF); } lsx_report("Hardware detected: %s",audio_dev.name); if (strcmp("SUNW,am79c30",audio_dev.name) == 0) { simple_hw = 1; } #endif if (simple_hw) { if (ft->encoding.bits_per_sample == 8) { if (ft->encoding.encoding != SOX_ENCODING_ULAW && ft->encoding.encoding != SOX_ENCODING_ALAW) { lsx_report("Warning: Detected simple hardware. Forcing output to ULAW"); ft->encoding.encoding = SOX_ENCODING_ULAW; } } else if (ft->encoding.bits_per_sample == 16) { lsx_report("Warning: Detected simple hardware. Forcing output to ULAW"); ft->encoding.bits_per_sample = 8; ft->encoding.encoding = SOX_ENCODING_ULAW; } } if (ft->encoding.bits_per_sample == 8) { samplesize = 8; pPriv->sample_shift = 0; if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) ft->encoding.encoding = SOX_ENCODING_ULAW; else if (ft->encoding.encoding != SOX_ENCODING_ULAW && ft->encoding.encoding != SOX_ENCODING_ALAW && ft->encoding.encoding != SOX_ENCODING_SIGN2) { lsx_report("Sun Audio driver only supports ULAW, ALAW, and Signed Linear for bytes."); lsx_report("Forcing to ULAW"); ft->encoding.encoding = SOX_ENCODING_ULAW; } if ((ft->encoding.encoding == SOX_ENCODING_ULAW || ft->encoding.encoding == SOX_ENCODING_ALAW) && ft->signal.channels == 2) { lsx_report("Warning: only support mono for ULAW and ALAW data. Forcing to mono."); ft->signal.channels = 1; } } else if (ft->encoding.bits_per_sample == 16) { samplesize = 16; pPriv->sample_shift = 1; if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN) ft->encoding.encoding = SOX_ENCODING_SIGN2; else if (ft->encoding.encoding != SOX_ENCODING_SIGN2) { lsx_report("Sun Audio driver only supports Signed Linear for words."); lsx_report("Forcing to Signed Linear"); ft->encoding.encoding = SOX_ENCODING_SIGN2; } } else { lsx_report("Sun Audio driver only supports bytes and words"); ft->encoding.bits_per_sample = 16; ft->encoding.encoding = SOX_ENCODING_SIGN2; samplesize = 16; pPriv->sample_shift = 1; } if (ft->signal.channels > 1) ft->signal.channels = 2; /* Read in old values, change to what we need and then send back */ if (ioctl(pPriv->device, AUDIO_GETINFO, &audio_if) < 0) { lsx_fail_errno(ft,errno,"Unable to initialize /dev/audio"); return(SOX_EOF); } audio_if.play.precision = samplesize; audio_if.play.channels = ft->signal.channels; audio_if.play.sample_rate = ft->signal.rate; if (ft->encoding.encoding == SOX_ENCODING_ULAW) encoding = AUDIO_ENCODING_ULAW; else if (ft->encoding.encoding == SOX_ENCODING_ALAW) encoding = AUDIO_ENCODING_ALAW; else encoding = AUDIO_ENCODING_LINEAR; audio_if.play.encoding = encoding; ioctl(pPriv->device, AUDIO_SETINFO, &audio_if); if (audio_if.play.precision != samplesize) { lsx_fail_errno(ft,errno,"Unable to initialize sample size for /dev/audio"); return(SOX_EOF); } if (audio_if.play.channels != ft->signal.channels) { lsx_fail_errno(ft,errno,"Unable to initialize number of channels for /dev/audio"); return(SOX_EOF); } if (audio_if.play.sample_rate != ft->signal.rate) { lsx_fail_errno(ft,errno,"Unable to initialize rate for /dev/audio"); return(SOX_EOF); } if (audio_if.play.encoding != encoding) { lsx_fail_errno(ft,errno,"Unable to initialize encoding for /dev/audio"); return(SOX_EOF); } pPriv->cOutput = sox_globals.bufsiz >> pPriv->sample_shift; pPriv->pOutput = lsx_malloc((size_t)pPriv->cOutput << pPriv->sample_shift); return (SOX_SUCCESS); } static int sunstop(sox_format_t* ft) { priv_t* pPriv = (priv_t*)ft->priv; if (pPriv->device >= 0) { close(pPriv->device); } if (pPriv->pOutput) { free(pPriv->pOutput); } return SOX_SUCCESS; } typedef sox_uint16_t sox_uint14_t; typedef sox_uint16_t sox_uint13_t; typedef sox_int16_t sox_int14_t; typedef sox_int16_t sox_int13_t; #define SOX_ULAW_BYTE_TO_SAMPLE(d,clips) SOX_SIGNED_16BIT_TO_SAMPLE(sox_ulaw2linear16(d),clips) #define SOX_ALAW_BYTE_TO_SAMPLE(d,clips) SOX_SIGNED_16BIT_TO_SAMPLE(sox_alaw2linear16(d),clips) #define SOX_SAMPLE_TO_ULAW_BYTE(d,c) sox_14linear2ulaw(SOX_SAMPLE_TO_UNSIGNED(14,d,c) - 0x2000) #define SOX_SAMPLE_TO_ALAW_BYTE(d,c) sox_13linear2alaw(SOX_SAMPLE_TO_UNSIGNED(13,d,c) - 0x1000) static size_t sunread(sox_format_t* ft, sox_sample_t* pOutput, size_t cOutput) { priv_t* pPriv = (priv_t*)ft->priv; char* pbOutput = (char*)pOutput; size_t cbOutputLeft = cOutput << pPriv->sample_shift; size_t i, cRead; int cbRead; SOX_SAMPLE_LOCALS; LSX_USE_VAR(sox_macro_temp_double); while (cbOutputLeft) { cbRead = read(pPriv->device, pbOutput, cbOutputLeft); if (cbRead <= 0) { if (cbRead < 0) { lsx_fail_errno(ft, errno, "Error reading from device"); return 0; } break; } cbOutputLeft -= cbRead; pbOutput += cbRead; } /* Convert in-place (backwards) */ cRead = cOutput - (cbOutputLeft >> pPriv->sample_shift); switch (pPriv->sample_shift) { case 0: switch (ft->encoding.encoding) { case SOX_ENCODING_SIGN2: for (i = cRead; i != 0; i--) { pOutput[i - 1] = SOX_UNSIGNED_8BIT_TO_SAMPLE( ((sox_uint8_t*)pOutput)[i - 1], dummy); } break; case SOX_ENCODING_ULAW: for (i = cRead; i != 0; i--) { pOutput[i - 1] = SOX_ULAW_BYTE_TO_SAMPLE( ((sox_uint8_t*)pOutput)[i - 1], dummy); } break; case SOX_ENCODING_ALAW: for (i = cRead; i != 0; i--) { pOutput[i - 1] = SOX_ALAW_BYTE_TO_SAMPLE( ((sox_uint8_t*)pOutput)[i - 1], dummy); } break; default: return 0; } break; case 1: for (i = cRead; i != 0; i--) { pOutput[i - 1] = SOX_SIGNED_16BIT_TO_SAMPLE( ((sox_int16_t*)pOutput)[i - 1], dummy); } break; } return cRead; } static size_t sunwrite( sox_format_t* ft, const sox_sample_t* pInput, size_t cInput) { priv_t* pPriv = (priv_t*)ft->priv; size_t cInputRemaining = cInput; unsigned cClips = 0; SOX_SAMPLE_LOCALS; while (cInputRemaining) { size_t cStride; size_t i; size_t cbStride; int cbWritten; cStride = cInputRemaining; if (cStride > pPriv->cOutput) { cStride = pPriv->cOutput; } switch (pPriv->sample_shift) { case 0: switch (ft->encoding.encoding) { case SOX_ENCODING_SIGN2: for (i = 0; i != cStride; i++) { ((sox_uint8_t*)pPriv->pOutput)[i] = SOX_SAMPLE_TO_UNSIGNED_8BIT(pInput[i], cClips); } break; case SOX_ENCODING_ULAW: for (i = 0; i != cStride; i++) { ((sox_uint8_t*)pPriv->pOutput)[i] = SOX_SAMPLE_TO_ULAW_BYTE(pInput[i], cClips); } break; case SOX_ENCODING_ALAW: for (i = 0; i != cStride; i++) { ((sox_uint8_t*)pPriv->pOutput)[i] = SOX_SAMPLE_TO_ALAW_BYTE(pInput[i], cClips); } break; default: return 0; } break; case 1: for (i = 0; i != cStride; i++) { ((sox_int16_t*)pPriv->pOutput)[i] = SOX_SAMPLE_TO_SIGNED_16BIT(pInput[i], cClips); } break; } cbStride = cStride << pPriv->sample_shift; i = 0; do { cbWritten = write(pPriv->device, &pPriv->pOutput[i], cbStride - i); i += cbWritten; if (cbWritten <= 0) { lsx_fail_errno(ft, errno, "Error writing to device"); return 0; } } while (i != cbStride); cInputRemaining -= cStride; pInput += cStride; } return cInput; } LSX_FORMAT_HANDLER(sunau) { static char const * const names[] = {"sunau", NULL}; static unsigned const write_encodings[] = { SOX_ENCODING_ULAW, 8, 0, SOX_ENCODING_ALAW, 8, 0, SOX_ENCODING_SIGN2, 8, 16, 0, 0}; static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE, "Sun audio device driver", names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO, sunstartread, sunread, sunstop, sunstartwrite, sunwrite, sunstop, NULL, write_encodings, NULL, sizeof(priv_t) }; return &handler; }