Mercurial > pmdwin
view alsa_pcm_api.c @ 0:c55ea9478c80
Hello Gensokyo!
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 21 May 2013 10:29:21 +0200 |
parents | |
children |
line wrap: on
line source
#include <stdint.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include "alsa_pcm_api.h" int snd_pcm_hw_params_any(int snd_fd, snd_pcm_hw_params_t *params); int snd_pcm_hw_params_set_access(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_access_t access); int snd_pcm_hw_params_set_format(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_format_t format); int snd_pcm_hw_params_set_channels(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val); int snd_pcm_hw_params_set_rate(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val, int dir); int snd_pcm_hw_params_set_buffer_size(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val); #define MASK_SIZE (SND_MASK_MAX / 32) #define MASK_OFS(i) ((i) >> 5) #define MASK_BIT(i) (1U << ((i) & 31)) static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, int var) { return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, int var) { return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var); } size_t snd_pcm_hw_params_sizeof(void) { return sizeof(snd_pcm_hw_params_t); } static void snd_interval_any(snd_interval_t *i) { i->min = 0; i->openmin = 0; i->max = -1; i->openmax = 0; i->integer = 0; i->empty = 0; } static inline int snd_interval_empty(const snd_interval_t *i) { return i->empty; } static inline int snd_interval_checkempty(const snd_interval_t *i) { return (i->min > i->max || (i->min == i->max && (i->openmin || i->openmax))); } /* Return the minimum value for field PAR. */ int _snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params, int var, unsigned int *val, int *dir) { const snd_interval_t *i = hw_param_interval_c(params, var); if (dir) *dir = i->openmin; if (val) *val = i->min; return 0; } /* Return the maximum value for field PAR. */ int _snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params, int var, unsigned int *val, int *dir) { const snd_interval_t *i = hw_param_interval_c(params, var); if (dir) *dir = - (int) i->openmax; if (val) *val = i->max; return 0; } static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, int var) { snd_interval_any(hw_param_interval(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; } int snd_pcm_hw_params_any(int snd_fd, snd_pcm_hw_params_t *params) { unsigned int k; memset(params, 0, sizeof(*params)); for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { snd_mask_t *mask = ¶ms->masks[k - SNDRV_PCM_HW_PARAM_FIRST_MASK]; memset(mask, 0xff, sizeof(*mask)); params->cmask |= 1 << k; params->rmask |= 1 << k; } for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) _snd_pcm_hw_param_any(params, k); params->rmask = ~0U; params->cmask = 0; params->info = ~0U; return(ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params)); } int snd_pcm_hw_params_store(int snd_fd, snd_pcm_hw_params_t *params) { ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params); if (ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0) return -1; if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE) < 0) return -1; //-errno; return 0; } snd_pcm_sframes_t snd_pcm_writei(int snd_fd, const void *buffer, snd_pcm_uframes_t size) { struct snd_xferi xferi; xferi.result = 0; xferi.buf = (char*) buffer; xferi.frames = size; if (ioctl(snd_fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0) return -1; return xferi.result; } snd_pcm_sframes_t snd_pcm_writen(int snd_fd, void **bufs, snd_pcm_uframes_t size) { struct snd_xfern xfern; xfern.result = 0; xfern.bufs = bufs; xfern.frames = size; if (ioctl(snd_fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern) < 0) return -1; return xfern.result; } int snd_pcm_prepare(int snd_fd) { return ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE); } int snd_pcm_recover(int snd_fd, int err, int silent) { if (err > 0) err = -err; switch (err) { case -EINTR: return 0; case -EPIPE: if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE) < 0) return -1; //-errno; return 0; } return err; } int snd_pcm_status(int snd_fd, snd_pcm_status_t *status) { if (ioctl(snd_fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) return -1; return 0; } static int hw_param_update_var(int snd_fd, snd_pcm_hw_params_t *params, int var, int changed) { if (changed < 0) return changed; if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } if (params->rmask) { changed = ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params); if (changed < 0) return changed; if (snd_interval_empty(hw_param_interval_c(params, var))) return -ENOENT; } return 0; } static int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) { int changed = 0; if (snd_interval_empty(i)) return -ENOENT; if (i->min < v->min) { i->min = v->min; i->openmin = v->openmin; changed = 1; } else if (i->min == v->min && !i->openmin && v->openmin) { i->openmin = 1; changed = 1; } if (i->max > v->max) { i->max = v->max; i->openmax = v->openmax; changed = 1; } else if (i->max == v->max && !i->openmax && v->openmax) { i->openmax = 1; changed = 1; } if (!i->integer && v->integer) { i->integer = 1; changed = 1; } if (i->integer) { if (i->openmin) { i->min++; i->openmin = 0; } if (i->openmax) { i->max--; i->openmax = 0; } } else if (!i->openmin && !i->openmax && i->min == i->max) i->integer = 1; if (snd_interval_checkempty(i)) { i->empty = 1; return -EINVAL; } return changed; } int _snd_pcm_hw_param_get(const snd_pcm_hw_params_t *params, int var, unsigned int *val, int *dir) { const snd_interval_t *i = hw_param_interval_c(params, var); if (snd_interval_empty(i) || !(i->min == i->max || ((i->min + 1 == i->max) && i->openmax))) return -EINVAL; if (dir) *dir = i->openmin; if (val) *val = i->min; return 0; } int _snd_pcm_hw_param_set(int snd_fd, snd_pcm_hw_params_t *params, int var, unsigned int val, int dir) { int err = 0; snd_pcm_hw_params_t save = *params; snd_interval_t *i = hw_param_interval(params, var); snd_interval_t t; t.empty = 0; t.min = t.max = var; t.openmin = t.openmax = 0; t.integer = 1; err = snd_interval_refine(i, &t); err = hw_param_update_var(snd_fd, params, var, err); if (err) *params = save; return err; } static inline int _snd_pcm_hw_param_get64(const snd_pcm_hw_params_t *params, int type, unsigned long *val, int *dir) { unsigned int _val; int err = _snd_pcm_hw_param_get(params, type, &_val, dir); *val = _val; return err; } #if 0 int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params, unsigned int *val) { return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_CHANNELS, val, NULL); } int snd_pcm_hw_params_set_channels(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val) { return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_CHANNELS, val, 0); } int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_RATE, val, dir); } int snd_pcm_hw_params_set_rate(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val, int dir) { return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_RATE, val, dir); } int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, val, dir); } int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { return _snd_pcm_hw_param_get64(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, val, dir); } int snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { return _snd_pcm_hw_param_get64(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, val, NULL); } int snd_pcm_hw_params_set_buffer_size(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val) { return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, val, 0); } #endif static inline unsigned int popcount32(uint32_t mask) { register unsigned int y; y = (mask >> 1) &033333333333; y = mask - y - ((y >>1) & 033333333333); return (((y + (y >> 3)) & 030707070707) % 077); } int snd_pcm_hw_params_set_access(int pcm_fd, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { snd_pcm_hw_params_t save = *params; snd_mask_t *param = (snd_mask_t*)¶ms->masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK]; uint32_t i, v = 0; int err = 0, changed; for (i = 0; i < MASK_SIZE; i++) { v += popcount32(param->bits[i]); } changed = (v != 1); v = param->bits[MASK_OFS(access)] & MASK_BIT(access); memset(param, 0, sizeof(*param)); param->bits[MASK_OFS(access)] = v; if (changed) { params->cmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS; params->rmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS; } if (params->rmask) { err = ioctl(pcm_fd, SNDRV_PCM_IOCTL_HW_REFINE, params); if (err) *params = save; } return err; } int snd_pcm_hw_params_set_format(int pcm_fd, snd_pcm_hw_params_t *params, snd_pcm_format_t format) { snd_pcm_hw_params_t save = *params; snd_mask_t *param = (snd_mask_t*)¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; uint32_t i, v = 0; int err = 0, changed; for (i = 0; i < MASK_SIZE; i++) { v += popcount32(param->bits[i]); } changed = (v != 1); v = param->bits[MASK_OFS(format)] & MASK_BIT(format); memset(param, 0, sizeof(*param)); param->bits[MASK_OFS(format)] = v; if (changed) { params->cmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS; params->rmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS; } if (params->rmask) { err = ioctl(pcm_fd, SNDRV_PCM_IOCTL_HW_REFINE, params); if (err) *params = save; } return err; } int alsa_audio_close(int snd_fd) { ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_FREE); return close(snd_fd); } int alsa_audio_open(uint32_t *rate, uint8_t channels, uint32_t *_ver, int mode) { uint32_t actual_channels; snd_pcm_hw_params_t *hwparams; *&hwparams = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof()); int ver, snd_fd = open("/dev/snd/pcmC0D0p", O_WRONLY|mode); if (snd_fd < 0) return -1; if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PVERSION, &ver) < 0) { if (snd_fd >= 0) close(snd_fd); return -1; } if(_ver) *_ver = ver; // Initialize hwparams structure. snd_pcm_hw_params_any(snd_fd, hwparams); // this *must* be set, or else the writei (actually an ioctl(), wtf?) below will fail with -EBADF snd_pcm_hw_params_set_access(snd_fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(snd_fd, hwparams, SND_PCM_FORMAT_S16_LE); _snd_pcm_hw_param_set(snd_fd, hwparams, SNDRV_PCM_HW_PARAM_CHANNELS, channels, 0); _snd_pcm_hw_param_get(hwparams, SNDRV_PCM_HW_PARAM_CHANNELS, &actual_channels, NULL); if ((channels == 1) && (actual_channels == 2)) { // This is an offensively dumb hack that works around the fact that the ALSA devs erroneously believe that HDAudio chipsets cannot playback mono. // On any other chipset, it's a terrible idea. In general, it's awful. I'm just lazy. That, and I don't use ALSA myself. :P *rate >>= 1; } _snd_pcm_hw_param_set(snd_fd, hwparams, SNDRV_PCM_HW_PARAM_RATE, *rate, 0); snd_pcm_hw_params_store(snd_fd, hwparams); _snd_pcm_hw_param_get(hwparams, SNDRV_PCM_HW_PARAM_RATE, rate, NULL); if ((channels == 1) && (actual_channels == 2)) { *rate <<= 1; } return snd_fd; }