使用ALSA内核接口编写播放正弦波的C程序时遭遇ioctl输入输出错误
使用ALSA内核接口编写播放正弦波的C程序时遭遇ioctl输入输出错误
我正在Linux环境下编写一个直接调用ALSA内核接口播放正弦波的C程序,但在执行ioctl相关操作时碰到了输入输出错误。下面是我完整的代码实现,希望能帮忙排查问题:
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <sound/asound.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <strings.h> #include <unistd.h> #include <assert.h> #include <errno.h> #include <sys/mman.h> #include <math.h> #define SND_MASK_MAX 64 // idk why, alsalib used this value #define MASK_OFS(i) ((i) >> 5) #define MASK_BIT(i) (1U << ((i) & 31)) #ifndef ALSA_BUFFER_SIZE #define ALSA_BUFFER_SIZE 4096 #endif #ifndef ALSA_PERIOD_SIZE #define ALSA_PERIOD_SIZE 1024 #endif #define fail(err) {errno = EINVAL;return -1;} #define success(operation) {operation;return 0;} #define pexit(s) {perror(s);exit(1);} #define complain(f) {f;exit(1);} #define is_mask_empty(m) (m->bits[0] && m->bits[1]) #define is_interval_empty(i) (i->min > i->max || (i->min == i->max && (i->openmin || i->openmax))) int set_param_mask(struct snd_pcm_hw_params* params, int var, int val) { if (var-SNDRV_PCM_HW_PARAM_FIRST_MASK < 0) fail(EINVAL); struct snd_mask* mask = &(params->masks[var-SNDRV_PCM_HW_PARAM_FIRST_MASK]); if (is_mask_empty(mask)) fail(EFAULT); unsigned int v; if (val > SND_MASK_MAX) fail(EFAULT); mask->bits[MASK_OFS(val)] &= MASK_BIT(val); mask->bits[!MASK_OFS(val)] = 0; if (!(mask->bits[MASK_OFS(val)])) fail(EFAULT); return 0; } // dest - refined, src - set from int snd_interval_refine(struct snd_interval* dest, const struct snd_interval *src) { int changed = 0; if (dest->empty) fail(ENOENT); if (dest->min < src->min) { dest->min = src->min; dest->openmin = src->openmin; changed = 1; } else if (dest->min == src->min && !dest->openmin && src->openmin) { dest->openmin = 1; changed = 1; } if (dest->max > src->max) { dest->max = src->max; dest->openmax = src->openmax; changed = 1; } else if (dest->max == src->max && !dest->openmax && src->openmax) { dest->openmax = 1; changed = 1; } if (!dest->integer && src->integer) { dest->integer = 1; changed = 1; } if (dest->integer) { if (dest->openmin) { dest->min++; dest->openmin = 0; } if (dest->openmax) { dest->max--; dest->openmax = 0; } } else if (!dest->openmin && !dest->openmax && dest->min == dest->max) dest->integer = 1; if (is_interval_empty(dest)) { dest->empty = 1; fail(EFAULT); } return changed; } int set_param_interval(struct snd_pcm_hw_params* params, int var, int val) { if (var-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL < 0) fail(EINVAL); struct snd_interval* interval = &(params->intervals[var-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); struct snd_interval test; test.empty = 0; test.min = test.max = val; test.openmin = test.openmax = 0; test.integer = 1; return snd_interval_refine(interval, &test); } int set_param(struct snd_pcm_hw_params* params, int var, int val) { if (var <= SNDRV_PCM_HW_PARAM_LAST_MASK) return set_param_mask(params, var, val); else if (var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL) return set_param_interval(params,var,val); fail(EINVAL); } int get_param_int(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, unsigned int *val) { struct snd_interval *i = &(params->intervals[var-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); if (i->integer) success(*val = i->max); fail(EINVAL); } int main() { // open fd int fd = open("/dev/snd/pcmC0D0p", O_RDWR | __O_CLOEXEC); if (fd < 0) pexit("open /dev/snd/pcmC0D0p (O_RDWR | __O_CLOEXEC)"); // setup params struct snd_pcm_hw_params* params = calloc(1,sizeof(struct snd_pcm_hw_params)); for (unsigned i = 0; i <= SNDRV_PCM_HW_PARAM_LAST_MASK-SNDRV_PCM_HW_PARAM_FIRST_MASK; i++) memset(&(params->masks[i]), 0xff, SND_MASK_MAX * sizeof(__u32)); for (unsigned i = 0; i <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; i++) { (params->intervals)[i].min = 0; (params->intervals)[i].openmin = 0; (params->intervals)[i].max = 0xffffffffU; (params->intervals)[i].openmax = 0; (params->intervals)[i].integer = 0; (params->intervals)[i].empty = 0; } params->rmask = ~0U; params->cmask = 0; params->info = ~0U; // set specific params if (ioctl(fd,SNDRV_PCM_IOCTL_HW_REFINE,params) < 0) pexit("ioctl SNDRV_PCM_IOCTL_HW_REFINE"); if (set_param(params,SNDRV_PCM_HW_PARAM_ACCESS,SNDRV_PCM_ACCESS_RW_INTERLEAVED) < 0 ) pexit("set param SNDRV_PCM_HW_PARAM_ACCESS"); if (set_param(params,SNDRV_PCM_HW_PARAM_FORMAT,SNDRV_PCM_FORMAT_S16_LE) < 0 ) pexit("set param SNDRV_PCM_HW_PARAM_FORMAT"); if (set_param(params,SNDRV_PCM_HW_PARAM_CHANNELS,2) < 0 ) pexit("set param SNDRV_PCM_HW_PARAM_CHANNELS"); }
内容来源于stack exchange
相关产品推荐
相关产品推荐

