ref: f952fd79317fbcb8885c9c6903ff7949f6b2ef23
dir: /src/alsa.c/
/* * Copyright 1997 Jimen Ching And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Jimen Ching And Sundry Contributors are not * responsible for the consequences of using this software. */ /* Direct to ALSA sound driver * * added by Jimen Ching (jching@flex.com) 19990207 * based on info grabed from aplay.c in alsa-utils package. * Updated for ALSA 0.9.X API 20020824. */ #include "st_i.h" #if defined(HAVE_ALSA) #include <string.h> #include <fcntl.h> #include <sys/ioctl.h> #ifdef HAVE_ERRNO_H #include <errno.h> #endif static int get_format(ft_t ft, int formats, int *fmt); #if HAVE_ALSA9 /* Temporary hack until glibc and alsa kernel drivers match each other. */ #ifndef __user #define __user #endif #ifndef __kernel #define __kernel #endif #include <limits.h> #include <sound/asound.h> /* Backwards compatibility. */ #define SND_PCM_SFMT_S8 SNDRV_PCM_FORMAT_S8 #define SND_PCM_SFMT_U8 SNDRV_PCM_FORMAT_U8 #define SND_PCM_SFMT_S16_LE SNDRV_PCM_FORMAT_S16 #define SND_PCM_SFMT_U16_LE SNDRV_PCM_FORMAT_U16 #define SND_PCM_FMT_S8 (1 << SNDRV_PCM_FORMAT_S8) #define SND_PCM_FMT_U8 (1 << SNDRV_PCM_FORMAT_U8) #define SND_PCM_FMT_S16 (1 << SNDRV_PCM_FORMAT_S16) #define SND_PCM_FMT_U16 (1 << SNDRV_PCM_FORMAT_U16) #define alsa_params_masks(p, i) \ (&((p)->masks[(i)-SNDRV_PCM_HW_PARAM_FIRST_MASK])) #define alsa_params_intervals(p, i) \ (&((p)->intervals[(i)-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL])) struct alsa_info { unsigned int formats; unsigned int min_buffer_size; unsigned int max_buffer_size; unsigned int min_channels; unsigned int max_channels; unsigned int min_rate; unsigned int max_rate; unsigned int min_periods; unsigned int max_periods; unsigned int min_period_size; unsigned int max_period_size; }; struct alsa_setup { int format; char channels; st_rate_t rate; size_t buffer_size; int periods; size_t period_size; }; int alsa_hw_info_get(fd, a_info, params) int fd; struct alsa_info *a_info; struct sndrv_pcm_hw_params *params; { unsigned int i; struct sndrv_mask *msk; struct sndrv_interval *intr; memset(params, '\0', sizeof(struct sndrv_pcm_hw_params)); for (i = 0; i <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; ++i) { if (i >= SNDRV_PCM_HW_PARAM_FIRST_MASK && i <= SNDRV_PCM_HW_PARAM_LAST_MASK) { msk = alsa_params_masks(params, i); memset(msk->bits, 0xff, sizeof(msk->bits)); } else { intr = alsa_params_intervals(params, i); intr->min = 0; intr->openmin = 0; intr->max = UINT_MAX; intr->openmax = 0; intr->integer = 0; intr->empty = 0; } params->cmask |= 1 << i; params->rmask |= 1 << i; } if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params) < 0) { return -1; } msk = alsa_params_masks(params, SNDRV_PCM_HW_PARAM_FORMAT); a_info->formats = msk->bits[0]; /* Only care about first 32 bits. */ a_info->min_buffer_size = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min; a_info->max_buffer_size = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->max; a_info->min_channels = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min; a_info->max_channels = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max; a_info->min_rate = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_RATE)->min; a_info->max_rate = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_RATE)->max; a_info->min_periods = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_PERIODS)->min; a_info->max_periods = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_PERIODS)->max; a_info->min_period_size = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->min; a_info->max_period_size = alsa_params_intervals(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->max; return 0; } int alsa_hw_info_set(fd, params, setup) int fd; struct sndrv_pcm_hw_params *params; struct alsa_setup *setup; { int i; struct sndrv_mask *msk; struct sndrv_interval *intr; i = SNDRV_PCM_HW_PARAM_ACCESS; msk = alsa_params_masks(params, i); msk->bits[0] &= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; /* Only care about first 32 bits. */ params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_FORMAT; msk = alsa_params_masks(params, i); msk->bits[0] &= 1 << setup->format; /* Only care about first 32 bits. */ params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_CHANNELS; intr = alsa_params_intervals(params, i); intr->empty = 0; intr->min = intr->max = setup->channels; intr->openmin = intr->openmax = 0; intr->integer = 1; params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_RATE; intr = alsa_params_intervals(params, i); intr->empty = 0; intr->min = intr->max = setup->rate; intr->openmin = intr->openmax = 0; intr->integer = 1; params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_BUFFER_BYTES; intr = alsa_params_intervals(params, i); intr->empty = 0; intr->min = intr->max = setup->buffer_size; intr->openmin = intr->openmax = 0; intr->integer = 1; params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_PERIODS; intr = alsa_params_intervals(params, i); intr->empty = 0; intr->min = intr->max = setup->periods; intr->openmin = intr->openmax = 0; intr->integer = 1; params->cmask = 1 << i; params->rmask = 1 << i; i = SNDRV_PCM_HW_PARAM_PERIOD_BYTES; intr = alsa_params_intervals(params, i); intr->empty = 0; intr->min = intr->max = setup->period_size; intr->openmin = intr->openmax = 0; intr->integer = 1; params->cmask = 1 << i; params->rmask = 1 << i; if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0) { return -1; } if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) { return -1; } return 0; } /* * Do anything required before you start reading samples. * Read file header. * Find out sampling rate, * size and encoding of samples, * mono/stereo/quad. */ int st_alsastartread(ft) ft_t ft; { int fmt; struct alsa_info a_info; struct alsa_setup a_setup; struct sndrv_pcm_hw_params params; if (alsa_hw_info_get(fileno(ft->fp), &a_info, ¶ms) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.count = 0; ft->file.pos = 0; ft->file.eof = 0; ft->file.size = a_info.max_buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < a_info.min_rate) ft->info.rate = a_info.min_rate; else if (ft->info.rate > a_info.max_rate) ft->info.rate = a_info.max_rate; if (ft->info.channels == -1) ft->info.channels = a_info.min_channels; else if (ft->info.channels > a_info.max_channels) ft->info.channels = a_info.max_channels; else if (ft->info.channels < a_info.min_channels) ft->info.channels = a_info.min_channels; if (get_format(ft, a_info.formats, &fmt) < 0) return (ST_EOF); a_setup.format = fmt; a_setup.channels = ft->info.channels; a_setup.rate = ft->info.rate; a_setup.buffer_size = ft->file.size; a_setup.periods = 16; if (a_setup.periods < a_info.min_periods) a_setup.periods = a_info.min_periods; else if (a_setup.periods > a_info.max_periods) a_setup.periods = a_info.max_periods; a_setup.period_size = a_setup.buffer_size / a_setup.periods; if (a_setup.period_size < a_info.min_period_size) a_setup.period_size = a_info.min_period_size; else if (a_setup.period_size > a_info.max_period_size) a_setup.period_size = a_info.max_period_size; if (alsa_hw_info_set(fileno(ft->fp), ¶ms, &a_setup) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); sigintreg(ft); /* Prepare to catch SIGINT */ return (ST_SUCCESS); } int st_alsastartwrite(ft) ft_t ft; { int fmt; struct alsa_info a_info; struct alsa_setup a_setup; struct sndrv_pcm_hw_params params; if (alsa_hw_info_get(fileno(ft->fp), &a_info, ¶ms) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.pos = 0; ft->file.eof = 0; ft->file.size = a_info.max_buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < a_info.min_rate) ft->info.rate = a_info.min_rate; else if (ft->info.rate > a_info.max_rate) ft->info.rate = a_info.max_rate; if (ft->info.channels == -1) ft->info.channels = a_info.min_channels; else if (ft->info.channels > a_info.max_channels) ft->info.channels = a_info.max_channels; else if (ft->info.channels < a_info.min_channels) ft->info.channels = a_info.min_channels; if (get_format(ft, a_info.formats, &fmt) < 0) return (ST_EOF); a_setup.format = fmt; a_setup.channels = ft->info.channels; a_setup.rate = ft->info.rate; a_setup.buffer_size = ft->file.size; a_setup.periods = 16; if (a_setup.periods < a_info.min_periods) a_setup.periods = a_info.min_periods; else if (a_setup.periods > a_info.max_periods) a_setup.periods = a_info.max_periods; a_setup.period_size = a_setup.buffer_size / a_setup.periods; if (a_setup.period_size < a_info.min_period_size) a_setup.period_size = a_info.min_period_size; else if (a_setup.period_size > a_info.max_period_size) a_setup.period_size = a_info.max_period_size; if (alsa_hw_info_set(fileno(ft->fp), ¶ms, &a_setup) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); return(ST_SUCCESS); } int st_alsastopwrite(ft) ft_t ft; { int rc; rc = st_rawstopwrite(ft); ioctl(fileno(ft->fp), SNDRV_PCM_IOCTL_DRAIN); return(rc); } #else /* ! HAVE_ALSA9 */ #include <linux/asound.h> #if HAVE_ALSA4 /* Start 0.4.x API */ int st_alsastartread(ft) ft_t ft; { int bps, fmt, size; snd_pcm_capture_info_t c_info; snd_pcm_format_t format; snd_pcm_capture_params_t c_params; memset(&c_info, 0, sizeof(c_info)); if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CAPTURE_INFO, &c_info) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.count = 0; ft->file.pos = 0; ft->file.eof = 0; ft->file.size = c_info.buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < c_info.min_rate) ft->info.rate = 2 * c_info.min_rate; else if (ft->info.rate > c_info.max_rate) ft->info.rate = c_info.max_rate; if (ft->info.channels == -1) ft->info.channels = c_info.min_channels; else if (ft->info.channels > c_info.max_channels) ft->info.channels = c_info.max_channels; if (get_format(ft, c_info.hw_formats, &fmt) < 0) return(ST_EOF); memset(&format, 0, sizeof(format)); format.format = fmt; format.rate = ft->info.rate; format.channels = ft->info.channels; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CAPTURE_FORMAT, &format) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } size = ft->file.size; bps = format.rate * format.channels; if (ft->info.size == ST_SIZE_WORD) bps <<= 1; bps >>= 2; while (size > bps) size >>= 1; if (size < 16) size = 16; memset(&c_params, 0, sizeof(c_params)); c_params.fragment_size = size; c_params.fragments_min = 1; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CAPTURE_PARAMS, &c_params) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); sigintreg(ft); /* Prepare to catch SIGINT */ return (ST_SUCCESS); } int st_alsastartwrite(ft) ft_t ft; { int bps, fmt, size; snd_pcm_playback_info_t p_info; snd_pcm_format_t format; snd_pcm_playback_params_t p_params; memset(&p_info, 0, sizeof(p_info)); if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_PLAYBACK_INFO, &p_info) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.pos = 0; ft->file.eof = 0; ft->file.size = p_info.buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < p_info.min_rate) ft->info.rate = 2 * p_info.min_rate; else if (ft->info.rate > p_info.max_rate) ft->info.rate = p_info.max_rate; if (ft->info.channels == -1) ft->info.channels = p_info.min_channels; else if (ft->info.channels > p_info.max_channels) ft->info.channels = p_info.max_channels; if (get_format(ft, p_info.hw_formats, &fmt) < 0) return(ST_EOF); memset(&format, 0, sizeof(format)); format.format = fmt; format.rate = ft->info.rate; format.channels = ft->info.channels; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_PLAYBACK_FORMAT, &format) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } size = ft->file.size; bps = format.rate * format.channels; if (ft->info.size == ST_SIZE_WORD) bps <<= 1; bps >>= 2; while (size > bps) size >>= 1; if (size < 16) size = 16; memset(&p_params, 0, sizeof(p_params)); p_params.fragment_size = size; p_params.fragments_max = -1; p_params.fragments_room = 1; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_PLAYBACK_PARAMS, &p_params) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); return(ST_SUCCESS); } int st_alsastopwrite(ft) ft_t ft; { /* Is there a drain operation for ALSA 0.4.X? */ return(st_rawstopwrite(ft)); } #elif HAVE_ALSA5 /* Start 0.5.x API */ int st_alsastartread(ft) ft_t ft; { int bps, fmt, size; snd_pcm_channel_info_t c_info; snd_pcm_channel_params_t c_params; memset(&c_info, 0, sizeof(c_info)); if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_INFO, &c_info) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.count = 0; ft->file.pos = 0; ft->file.eof = 0; ft->file.size = c_info.buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < c_info.min_rate) ft->info.rate = 2 * c_info.min_rate; else if (ft->info.rate > c_info.max_rate) ft->info.rate = c_info.max_rate; if (ft->info.channels == -1) ft->info.channels = c_info.min_voices; else if (ft->info.channels > c_info.max_voices) ft->info.channels = c_info.max_voices; if (get_format(ft, c_info.formats, &fmt) < 0) return (ST_EOF); memset(&c_params, 0, sizeof(c_params)); c_params.format.format = fmt; c_params.format.rate = ft->info.rate; c_params.format.voices = ft->info.channels; c_params.format.interleave = 1; c_params.channel = SND_PCM_CHANNEL_CAPTURE; c_params.mode = SND_PCM_MODE_BLOCK; c_params.start_mode = SND_PCM_START_DATA; c_params.stop_mode = SND_PCM_STOP_STOP; bps = c_params.format.rate * c_params.format.voices; if (ft->info.size == ST_SIZE_WORD) bps <<= 1; bps >>= 2; size = 1; while ((size << 1) < bps) size <<= 1; if (size > ft->file.size) size = ft->file.size; if (size < c_info.min_fragment_size) size = c_info.min_fragment_size; else if (size > c_info.max_fragment_size) size = c_info.max_fragment_size; c_params.buf.block.frag_size = size; c_params.buf.block.frags_max = 32; c_params.buf.block.frags_min = 1; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_PARAMS, &c_params) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_PREPARE) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); sigintreg(ft); /* Prepare to catch SIGINT */ return (ST_SUCCESS); } int st_alsastartwrite(ft) ft_t ft; { int bps, fmt, size; snd_pcm_channel_info_t p_info; snd_pcm_channel_params_t p_params; memset(&p_info, 0, sizeof(p_info)); if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_INFO, &p_info) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } ft->file.pos = 0; ft->file.eof = 0; ft->file.size = p_info.buffer_size; if ((ft->file.buf = malloc (ft->file.size)) == NULL) { st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size); return(ST_EOF); } if (ft->info.rate < p_info.min_rate) ft->info.rate = 2 * p_info.min_rate; else if (ft->info.rate > p_info.max_rate) ft->info.rate = p_info.max_rate; if (ft->info.channels == -1) ft->info.channels = p_info.min_voices; else if (ft->info.channels > p_info.max_voices) ft->info.channels = p_info.max_voices; if (get_format(ft, p_info.formats, &fmt) < 0) return(ST_EOF); memset(&p_params, 0, sizeof(p_params)); p_params.format.format = fmt; p_params.format.rate = ft->info.rate; p_params.format.voices = ft->info.channels; p_params.format.interleave = 1; p_params.channel = SND_PCM_CHANNEL_PLAYBACK; p_params.mode = SND_PCM_MODE_BLOCK; p_params.start_mode = SND_PCM_START_DATA; p_params.stop_mode = SND_PCM_STOP_STOP; bps = p_params.format.rate * p_params.format.voices; if (ft->info.size == ST_SIZE_WORD) bps <<= 1; bps >>= 2; size = 1; while ((size << 1) < bps) size <<= 1; if (size > ft->file.size) size = ft->file.size; if (size < p_info.min_fragment_size) size = p_info.min_fragment_size; else if (size > p_info.max_fragment_size) size = p_info.max_fragment_size; p_params.buf.block.frag_size = size; p_params.buf.block.frags_max = -1; /* Little trick (playback only) */ p_params.buf.block.frags_min = 1; if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_PARAMS, &p_params) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } if (ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_PREPARE) < 0) { st_fail_errno(ft,ST_EPERM,"ioctl operation failed %d",errno); return(ST_EOF); } /* Change to non-buffered I/O */ setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size); return(ST_SUCCESS); } int st_alsastopwrite(ft) ft_t ft; { ioctl(fileno(ft->fp), SND_PCM_IOCTL_CHANNEL_DRAIN); return(st_rawstopwrite(ft)); } #endif /* HAVE_ALSA4/5 */ #endif /* HAVE_ALSA9 */ #define EMSGFMT "ALSA driver does not support %s %s output" static int get_format(ft_t ft, int formats, int *fmt) { if (ft->info.size == -1) ft->info.size = ST_SIZE_WORD; /* Some hardware only wants to work with 8-bit or 16-bit data */ if (ft->info.size == ST_SIZE_BYTE) { if (!(formats & SND_PCM_FMT_U8) && !(formats & SND_PCM_FMT_S8)) { st_report("ALSA driver doesn't supported byte samples. Changing to words."); ft->info.size = ST_SIZE_WORD; } } else if (ft->info.size == ST_SIZE_WORD) { if (!(formats & SND_PCM_FMT_U16) && !(formats & SND_PCM_FMT_S16)) { st_report("ALSA driver doesn't supported word samples. Changing to bytes."); ft->info.size = ST_SIZE_BYTE; } } else { if ((formats & SND_PCM_FMT_U16) || (formats & SND_PCM_FMT_S16)) { st_report("ALSA driver doesn't supported %s samples. Changing to words.", st_sizes_str[(unsigned char)ft->info.size]); ft->info.size = ST_SIZE_WORD; } else { st_report("ALSA driver doesn't supported %s samples. Changing to bytes.", st_sizes_str[(unsigned char)ft->info.size]); ft->info.size = ST_SIZE_BYTE; } } if (ft->info.size == ST_SIZE_BYTE) { if (ft->info.encoding == -1) ft->info.encoding = ST_ENCODING_UNSIGNED; switch (ft->info.encoding) { case ST_ENCODING_SIGN2: if (!(formats & SND_PCM_FMT_S8)) { st_report("ALSA driver doesn't supported signed byte samples. Changing to unsigned bytes."); ft->info.encoding = ST_ENCODING_UNSIGNED; } break; case ST_ENCODING_UNSIGNED: if (!(formats & SND_PCM_FMT_U8)) { st_report("ALSA driver doesn't supported unsigned byte samples. Changing to signed bytes."); ft->info.encoding = ST_ENCODING_SIGN2; } break; default: if (formats & SND_PCM_FMT_S8) { st_report("ALSA driver doesn't support %s %s. Changing to signed bytes.", st_encodings_str[(unsigned char)ft->info.encoding], "byte"); ft->info.encoding = ST_ENCODING_SIGN2; } else { st_report("ALSA driver doesn't support %s %s. Changing to unsigned bytes.", st_encodings_str[(unsigned char)ft->info.encoding], "byte"); ft->info.encoding = ST_ENCODING_UNSIGNED; } } switch (ft->info.encoding) { case ST_ENCODING_SIGN2: if (!(formats & SND_PCM_FMT_S8)) { st_fail_errno(ft,ST_EFMT,"ALSA driver does not support signed byte samples"); return ST_EOF; } *fmt = SND_PCM_SFMT_S8; break; case ST_ENCODING_UNSIGNED: if (!(formats & SND_PCM_FMT_U8)) { st_fail_errno(ft,ST_EFMT,"ALSA driver does not support unsigned byte samples"); return ST_EOF; } *fmt = SND_PCM_SFMT_U8; break; } } else if (ft->info.size == ST_SIZE_WORD) { if (ft->info.encoding == -1) ft->info.encoding = ST_ENCODING_SIGN2; switch (ft->info.encoding) { case ST_ENCODING_SIGN2: if (!(formats & SND_PCM_FMT_S16)) { st_report("ALSA driver does not support signed word samples. Changing to unsigned words."); ft->info.encoding = ST_ENCODING_UNSIGNED; } break; case ST_ENCODING_UNSIGNED: if (!(formats & SND_PCM_FMT_U16)) { st_report("ALSA driver does not support unsigned word samples. Changing to signed words."); ft->info.encoding = ST_ENCODING_SIGN2; } break; default: st_report("ALSA driver doesn't support %s %s. Changing to signed words.", st_encodings_str[(unsigned char)ft->info.encoding], "word"); ft->info.encoding = ST_ENCODING_SIGN2; break; } switch (ft->info.encoding) { case ST_ENCODING_SIGN2: if (!(formats & SND_PCM_FMT_S16)) { st_fail_errno(ft,ST_EFMT,"ALSA driver does not support signed word samples"); return ST_EOF; } *fmt = SND_PCM_SFMT_S16_LE; break; case ST_ENCODING_UNSIGNED: if (!(formats & SND_PCM_FMT_U16)) { st_fail_errno(ft,ST_EFMT,"ALSA driver does not support unsigned word samples"); return ST_EOF; } *fmt = SND_PCM_SFMT_U16_LE; break; } } else { st_fail_errno(ft,ST_EFMT,EMSGFMT,st_encodings_str[(unsigned char)ft->info.encoding], st_sizes_str[(unsigned char)ft->info.size]); return ST_EOF; } return 0; } st_ssize_t st_alsaread(ft_t ft, st_sample_t *buf, st_ssize_t nsamp) { st_ssize_t len; len = st_rawread(ft, buf, nsamp); #if HAVE_ALSA9 /* ALSA 0.9 and above require that we detects underruns and * reset the driver if it occurs. */ if (len != nsamp) { /* Reset the driver. A future enhancement would be to * fill up the empty spots in the buffer (starting at * nsamp - len). But I'm being lazy (cbagwell) and just * returning with a partial buffer. */ ioctl(fileno(ft->fp), SNDRV_PCM_IOCTL_PREPARE); /* Raw routines use eof flag to store when we've * hit EOF or if an internal error occurs. The * above ioctl is much like calling the stdio clearerr() function * and so we should reset libst's flag as well. If the * error condition is still really there, it will be * detected on a future read. */ ft->file.eof = ST_SUCCESS; } #endif return len; } st_ssize_t st_alsawrite(ft_t ft, st_sample_t *buf, st_ssize_t nsamp) { st_ssize_t len; len = st_rawwrite(ft, buf, nsamp); #if HAVE_ALSA9 /* ALSA 0.9 and above require that we detects overruns and * reset the driver if it occurs. */ if (len != nsamp) { /* Reset the driver. A future enhancement would be to * resend the remaining data (starting at (nsamp - len) in the buffer). * But since we've already lost some data, I'm being lazy * and letting a little more data be lost as well. */ ioctl(fileno(ft->fp), SNDRV_PCM_IOCTL_PREPARE); /* Raw routines use eof flag to store when an internal error * the above ioctl is much like calling the stdio clearerr() function * and so we should reset libst's flag as well. If the * error condition is still really there, it will be * detected on a future write. */ ft->file.eof = ST_SUCCESS; } #endif return len; } #endif /* HAVE_ALSA */