Ошибка "IOCTL SNDRV_PCM_IOCTL_WRITEI_FRAMELinux

Ответить
Anonymous
 Ошибка "IOCTL SNDRV_PCM_IOCTL_WRITEI_FRAME

Сообщение Anonymous »

Я пытаюсь написать программу C (на Linux), которая играет синусоидальную волну от динамиков, используя интерфейс ядра ALSA: < /p>

Код: Выделить всё

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SND_MASK_MAX 64 // idk why, alsalib used this value
#define MASK_OFS(i) ((i) >> 5)
#define MASK_BIT(i) (1U 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 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 masks[i]), 0xff, SND_MASK_MAX * sizeof(__u32));
for (unsigned i = 0; i 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");
if (set_param(params,SNDRV_PCM_HW_PARAM_RATE,48000) < 0 ) pexit("set param SNDRV_PCM_HW_PARAM_RATE");
if (ioctl(fd,SNDRV_PCM_IOCTL_HW_PARAMS,params) < 0) pexit("ioctl SNDRV_PCM_IOCTL_HW_PARAMS");

// get period size
unsigned int period_size;
if (get_param_int(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,&period_size) < 0) pexit("set param SNDRV_PCM_HW_PARAM_PERIOD_SIZE");

// set sw_params
struct snd_pcm_sw_params* sparams = calloc(1,sizeof(struct snd_pcm_sw_params));
sparams->tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
sparams->period_step = 1;
sparams->avail_min = period_size;
sparams->start_threshold = ALSA_BUFFER_SIZE - period_size;
sparams->stop_threshold = ALSA_BUFFER_SIZE;
sparams->xfer_align = period_size / 2;
if(ioctl(fd, SNDRV_PCM_IOCTL_SW_PARAMS, sparams) < 0) pexit("ioctl SNDRV_PCM_IOCTL_SW_PARAMS");

// create mmaps for sound playing
snd_pcm_uframes_t boundary = sparams->boundary;
struct snd_pcm_mmap_status* mmap_status = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
if(!mmap_status || mmap_status == MAP_FAILED) pexit("mmap PROT_READ MAP_SHARED SNDRV_PCM_MMAP_OFFSET_STATUS");
struct snd_pcm_mmap_control* mmap_control = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
if(!mmap_control || mmap_control == MAP_FAILED) pexit("mmap PROT_READ|PROT_WRITE MAP_SHARED SNDRV_PCM_MMAP_OFFSET_CONTROL");
if(ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
perror("ioctl SNDRV_PCM_IOCTL_PREPARE");
munmap(mmap_status, 4096); munmap(mmap_control, 4096);
exit(1);
}

unsigned int numframes=48000;
short* samples = calloc(numframes,sizeof(short));

// generate frames
for (int i = 0; i < numframes; i++) {
samples[i] = 30000 * sinf(2 * M_PI * 200 *((float)i / (numframes)));
}

unsigned char *data = (unsigned char*)samples;
numframes*=4; // (sizeof(int16_t)/sizeof(byte))
struct snd_xferi xfer = { 0 };
int ret, avail;

if (ioctl(fd,SNDRV_PCM_IOCTL_PREPARE) < 0) pexit("ioctl SNDRV_PCM_IOCTL_PREPARE");

// play the frames
int correct_count = 0;
do {
xfer.buf = data;
xfer.frames = numframes > period_size ? period_size : numframes;
xfer.result = 0;
if(!(ret = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xfer))) {
avail = mmap_status->hw_ptr + ALSA_BUFFER_SIZE - mmap_control->appl_ptr;
if(avail < 0) avail += boundary; else
if((unsigned int)avail >= boundary) avail -= boundary;
numframes -= xfer.result;
data += xfer.result * 4; // 4==channels*(int16_t/byte)
++correct_count;
} else  if (errno == EPIPE) {
if (ioctl(fd,SNDRV_PCM_IOCTL_PREPARE) < 0) pexit("ioctl SNDRV_PCM_IOCTL_PREPARE");
} else {
ioctl(fd, SNDRV_PCM_IOCTL_DRAIN);
close(fd);
fprintf(stderr,"ioctl SNDRV_PCM_IOCTL_WRITEI_FRAMES failed after successfully sending %li bytes in %d portions: ", ((size_t)data-(size_t)samples),correct_count);
pexit("");
}
} while(numframes >  0);

// clean up
free(sparams);
free(params);

munmap(mmap_status, 4096);
munmap(mmap_control, 4096);
ioctl(fd, SNDRV_PCM_IOCTL_DRAIN);
close(fd);
}
< /code>
и после нескольких итераций цикла while и lecmiting ~ 0,1 секунды (всегда одного и того же) звука на моем ноутбуке, но не в виртуальной машине Qemu, программа всегда выходит с: < /p>
ioctl SNDRV_PCM_IOCTL_WRITEI_FRAMES failed after successfully sending 4096 bytes in 32 portions: Input/output error
... вместо того, чтобы записать целые 48000 байтов в буфере
(и по какой -то причине 4096 - это то же число, что и Macro ALSA_BUFFER_SIZE
, почему это может произойти (что я делаю неправильно) и как это исправить? Ноутбук с ubuntu gnome x11 и федорой quemu vm без Pulseaudio, Jack или даже Asoundlib, и я проверил, этот звук работает в виртуальной машине.

Подробнее здесь: https://stackoverflow.com/questions/797 ... writing-fr
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Linux»