static void set_params(void) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size; int err; size_t n; snd_pcm_uframes_t xfer_align; unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(handle, params); if (err < 0) { error(_("Broken configuration for this PCM: no configurations available")); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { error(_("Access type not available")); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_format(handle, params, hwparams.format); if (err < 0) { error(_("Sample format non available")); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels); if (err < 0) { error(_("Channels count non available")); exit(EXIT_FAILURE); } #if 0 err = snd_pcm_hw_params_set_periods_min(handle, params, 2); assert(err >= 0); #endif rate = hwparams.rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0); assert(err >= 0); if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) { if (!quiet_mode) { char plugex[64]; const char *pcmname = snd_pcm_name(handle); fprintf(stderr, _("Warning: rate is not accurate (requested = %iHz, got = %iHz)\n"), rate, hwparams.rate); if (! pcmname || strchr(snd_pcm_name(handle), ':')) *plugex = 0; else snprintf(plugex, sizeof(plugex), "(-Dplug:%s)", snd_pcm_name(handle)); fprintf(stderr, _(" please, try the plug plugin %s\n"), plugex); } } rate = hwparams.rate; if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000) buffer_time = 500000; } if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } if (period_time > 0) err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames); } assert(err >= 0); err = snd_pcm_hw_params(handle, params); if (err < 0) { error(_("Unable to install hw params:")); snd_pcm_hw_params_dump(params, log); exit(EXIT_FAILURE); } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { error(_("Can't use period equal to buffer size (%lu == %lu)"), chunk_size, buffer_size); exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); if (err < 0) { error(_("Unable to obtain xfer align\n")); exit(EXIT_FAILURE); } if (sleep_min) xfer_align = 1; err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min); assert(err >= 0); if (avail_min < 0) n = chunk_size; else n = (double) rate * avail_min / 1000000; err = snd_pcm_sw_params_set_avail_min(handle, swparams, n); // round up to closest transfer boundary n = (buffer_size / xfer_align) * xfer_align; start_threshold = n; if (start_threshold < 1) start_threshold = 1; if (start_threshold > n) start_threshold = n; err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); assert(err >= 0); stop_threshold = buffer_size; err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); assert(err >= 0); err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align); assert(err >= 0); if (snd_pcm_sw_params(handle, swparams) < 0) { error(_("unable to install sw params:")); snd_pcm_sw_params_dump(swparams, log); exit(EXIT_FAILURE); } bits_per_sample = snd_pcm_format_physical_width(hwparams.format); bits_per_frame = bits_per_sample * hwparams.channels; chunk_bytes = chunk_size * bits_per_frame / 8; audiobuf = realloc(audiobuf, chunk_bytes); if (audiobuf == NULL) { error(_("not enough memory")); exit(EXIT_FAILURE); } // fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size); }
static int pcm_open(struct alsa_pcm *alsa, const char *device_name, snd_pcm_stream_t stream, int rate, int buffer_time) { int r, dir; unsigned int p; size_t bytes; snd_pcm_hw_params_t *hw_params; r = snd_pcm_open(&alsa->pcm, device_name, stream, SND_PCM_NONBLOCK); if (!chk("open", r)) return -1; snd_pcm_hw_params_alloca(&hw_params); r = snd_pcm_hw_params_any(alsa->pcm, hw_params); if (!chk("hw_params_any", r)) return -1; r = snd_pcm_hw_params_set_access(alsa->pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (!chk("hw_params_set_access", r)) return -1; r = snd_pcm_hw_params_set_format(alsa->pcm, hw_params, SND_PCM_FORMAT_S16); if (!chk("hw_params_set_format", r)) { fprintf(stderr, "16-bit signed format is not available. " "You may need to use a 'plughw' device.\n"); return -1; } r = snd_pcm_hw_params_set_rate(alsa->pcm, hw_params, rate, 0); if (!chk("hw_params_set_rate", r )) { fprintf(stderr, "%dHz sample rate not available. You may need to use " "a 'plughw' device.\n", rate); return -1; } alsa->rate = rate; r = snd_pcm_hw_params_set_channels(alsa->pcm, hw_params, DEVICE_CHANNELS); if (!chk("hw_params_set_channels", r)) { fprintf(stderr, "%d channel audio not available on this device.\n", DEVICE_CHANNELS); return -1; } p = buffer_time * 1000; /* microseconds */ dir = -1; r = snd_pcm_hw_params_set_buffer_time_near(alsa->pcm, hw_params, &p, &dir); if (!chk("hw_params_set_buffer_time_near", r)) { fprintf(stderr, "Buffer of %dms may be too small for this hardware.\n", buffer_time); return -1; } p = 2; /* double buffering */ dir = 1; r = snd_pcm_hw_params_set_periods_min(alsa->pcm, hw_params, &p, &dir); if (!chk("hw_params_set_periods_min", r)) { fprintf(stderr, "Buffer of %dms may be too small for this hardware.\n", buffer_time); return -1; } r = snd_pcm_hw_params(alsa->pcm, hw_params); if (!chk("hw_params", r)) return -1; r = snd_pcm_hw_params_get_period_size(hw_params, &alsa->period, &dir); if (!chk("get_period_size", r)) return -1; bytes = alsa->period * DEVICE_CHANNELS * sizeof(signed short); alsa->buf = malloc(bytes); if (!alsa->buf) { perror("malloc"); return -1; } /* snd_pcm_readi() returns uninitialised memory on first call, * possibly caused by premature POLLIN. Keep valgrind happy. */ memset(alsa->buf, 0, bytes); return 0; }
static void set_params(void) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size; int err; size_t n; unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(handle, params); if (err < 0) { error("Broken configuration for this PCM: no configurations available"); exit(EXIT_FAILURE); } if (mmap_flag) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); err = snd_pcm_hw_params_set_access_mask(handle, params, mask); } else if (interleaved) err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); else err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED); if (err < 0) { error("Access type not available"); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_format(handle, params, hwparams.format); if (err < 0) { error("Sample format non available"); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels); if (err < 0) { error("Channels count non available"); exit(EXIT_FAILURE); } #if 0 err = snd_pcm_hw_params_set_periods_min(handle, params, 2); assert(err >= 0); #endif rate = hwparams.rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0); assert(err >= 0); if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) { if (!quiet_mode) { char plugex[64]; const char *pcmname = snd_pcm_name(handle); fprintf(stderr, "Warning: rate is not accurate (requested = %iHz, got = %iHz)\n", rate, hwparams.rate); if (! pcmname || strchr(snd_pcm_name(handle), ':')) *plugex = 0; else snprintf(plugex, sizeof(plugex), "(-Dplug:%s)", snd_pcm_name(handle)); fprintf(stderr, " please, try the plug plugin %s\n", plugex); } } rate = hwparams.rate; if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000) buffer_time = 500000; } if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } if (period_time > 0) err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames); } assert(err >= 0); err = snd_pcm_hw_params(handle, params); if (err < 0) { error("Unable to install hw params:"); snd_pcm_hw_params_dump(params, log); exit(EXIT_FAILURE); } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { error("Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size); exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); if (avail_min < 0) n = chunk_size; else n = (double) rate * avail_min / 1000000; err = snd_pcm_sw_params_set_avail_min(handle, swparams, n); if (err < 0) { error("Setting sw params failed\n"); } /* round up to closest transfer boundary */ n = buffer_size; if (start_delay <= 0) { start_threshold = n + (double) rate * start_delay / 1000000; } else start_threshold = (double) rate * start_delay / 1000000; if (start_threshold < 1) start_threshold = 1; if (start_threshold > n) start_threshold = n; err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); assert(err >= 0); if (stop_delay <= 0) stop_threshold = buffer_size + (double) rate * stop_delay / 1000000; else stop_threshold = (double) rate * stop_delay / 1000000; err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); assert(err >= 0); if (snd_pcm_sw_params(handle, swparams) < 0) { error("unable to install sw params:"); snd_pcm_sw_params_dump(swparams, log); exit(EXIT_FAILURE); } if (verbose) snd_pcm_dump(handle, log); bits_per_sample = snd_pcm_format_physical_width(hwparams.format); bits_per_frame = bits_per_sample * hwparams.channels; chunk_bytes = chunk_size * bits_per_frame / 8; audiobuf = realloc(audiobuf, chunk_bytes); if (audiobuf == NULL) { error("not enough memory"); exit(EXIT_FAILURE); } // fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size); /* stereo VU-meter isn't always available... */ if (vumeter == VUMETER_STEREO) { if (hwparams.channels != 2 || !interleaved || verbose > 2) vumeter = VUMETER_MONO; } /* show mmap buffer arragment */ if (mmap_flag && verbose) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset; int i; err = snd_pcm_mmap_begin(handle, &areas, &offset, &chunk_size); if (err < 0) { error("snd_pcm_mmap_begin problem: %s", snd_strerror(err)); exit(EXIT_FAILURE); } for (i = 0; i < hwparams.channels; i++) fprintf(stderr, "mmap_area[%i] = %p,%u,%u (%u)\n", i, areas[i].addr, areas[i].first, areas[i].step, snd_pcm_format_physical_width(hwparams.format)); /* not required, but for sure */ snd_pcm_mmap_commit(handle, offset, 0); } buffer_frames = buffer_size; /* for position test */ }
static int audio_open(audiodevice_t *dev, chanfmt_t fmt) { audio_alsa09_t *alsa = (audio_alsa09_t *)dev->data_pcm; struct pollfd pfds; snd_pcm_hw_params_t *hwparams; #if SND_LIB_VERSION >= SND_PROTOCOL_VERSION(1,0,0) snd_pcm_uframes_t len; #else snd_pcm_sframes_t len; #endif int err, periods; if (0 > snd_pcm_open(&alsa->handle, alsa->dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC)) { WARNING("Opening audio device %d failed\n", alsa->dev); goto _err_exit; } snd_pcm_hw_params_alloca(&hwparams); if (0 > snd_pcm_hw_params_any(alsa->handle, hwparams)) { WARNING("param get failed\n"); goto _err_exit; } if (0 > snd_pcm_hw_params_set_access(alsa->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) { WARNING("set access fail\n"); goto _err_exit; } if (0 > snd_pcm_hw_params_set_format(alsa->handle, hwparams, fmt.bit == 16 ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8)) { WARNING("set format fail\n"); goto _err_exit; } if (0 > snd_pcm_hw_params_set_channels(alsa->handle, hwparams, fmt.ch)) { WARNING("set channel fail\n"); goto _err_exit; } #if SND_LIB_VERSION >= SND_PROTOCOL_VERSION(1,0,0) int tmp = fmt.rate; err = snd_pcm_hw_params_set_rate_near(alsa->handle, hwparams, &tmp, 0); #else err = snd_pcm_hw_params_set_rate_near(alsa->handle, hwparams, fmt.rate, 0); #endif if (err < 0) { WARNING("set rate fail\n"); goto _err_exit; } if (tmp != fmt.rate) { WARNING("set rate fail\n"); goto _err_exit; } if (0 > snd_pcm_hw_params_set_periods_integer(alsa->handle, hwparams)) { WARNING("set periods fail\n"); goto _err_exit; } periods = 2; if (0 > snd_pcm_hw_params_set_periods_min(alsa->handle, hwparams, &periods, 0)) { WARNING("set priods min fail\n"); goto _err_exit; } if (0 > snd_pcm_hw_params_set_buffer_size(alsa->handle, hwparams, BUFFERSIZE)) { WARNING("set buffer fail\n"); goto _err_exit; } if (0 > snd_pcm_hw_params(alsa->handle, hwparams)) { WARNING("set hw parmas fail\n"); goto _err_exit; } #if SND_LIB_VERSION >= SND_PROTOCOL_VERSION(1,0,0) snd_pcm_hw_params_get_buffer_size(hwparams, &len); #else len = snd_pcm_hw_params_get_buffer_size(hwparams); #endif dev->buf.len = (int)len; snd_pcm_poll_descriptors(alsa->handle, &pfds, 1); dev->fd = pfds.fd; return OK; _err_exit: if (alsa->handle) { snd_pcm_close(alsa->handle); } dev->fd = -1; return NG; }