You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:08:05