GstCaps * gst_alsa_probe_supported_formats (GstObject * obj, gchar * device, snd_pcm_t * handle, const GstCaps * template_caps) { snd_pcm_hw_params_t *hw_params; snd_pcm_stream_t stream_type; GstCaps *caps; gint err; snd_pcm_hw_params_malloc (&hw_params); if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) goto error; stream_type = snd_pcm_stream (handle); caps = gst_caps_copy (template_caps); if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps))) goto subroutine_error; if (!(caps = gst_alsa_detect_rates (obj, hw_params, caps))) goto subroutine_error; if (!(caps = gst_alsa_detect_channels (obj, hw_params, caps))) goto subroutine_error; /* Try opening IEC958 device to see if we can support that format (playback * only for now but we could add SPDIF capture later) */ if (stream_type == SND_PCM_STREAM_PLAYBACK) { snd_pcm_t *pcm = gst_alsa_open_iec958_pcm (obj, device); if (G_LIKELY (pcm)) { gst_caps_append (caps, gst_caps_from_string (PASSTHROUGH_CAPS)); snd_pcm_close (pcm); } } snd_pcm_hw_params_free (hw_params); return caps; /* ERRORS */ error: { GST_ERROR_OBJECT (obj, "failed to query formats: %s", snd_strerror (err)); snd_pcm_hw_params_free (hw_params); return NULL; } subroutine_error: { GST_ERROR_OBJECT (obj, "failed to query formats"); snd_pcm_hw_params_free (hw_params); return NULL; } }
/************************************************************************** * wine_snd_pcm_recover [internal] * * Code slightly modified from alsa-lib v1.0.23 snd_pcm_recover implementation. * used to recover from XRUN errors (buffer underflow/overflow) */ int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent) { if (err > 0) err = -err; if (err == -EINTR) /* nothing to do, continue */ return 0; if (err == -EPIPE) { const char *s; if (snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK) s = "underrun"; else s = "overrun"; if (!silent) ERR("%s occurred\n", s); err = snd_pcm_prepare(pcm); if (err < 0) { ERR("cannot recover from %s, prepare failed: %s\n", s, snd_strerror(err)); return err; } return 0; } if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(pcm)) == -EAGAIN) /* wait until suspend flag is released */ poll(NULL, 0, 1000); if (err < 0) { err = snd_pcm_prepare(pcm); if (err < 0) { ERR("cannot recover from suspend, prepare failed: %s\n", snd_strerror(err)); return err; } } return 0; } return err; }
bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle) { #define TRY(call, error) do { \ if (ALSA_CALL(call, error) < 0) \ return false; \ } while(0) snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); const unsigned SFL_ALSA_PERIOD_SIZE = 160; const unsigned SFL_ALSA_NB_PERIOD = 8; const unsigned SFL_ALSA_BUFFER_SIZE = SFL_ALSA_PERIOD_SIZE * SFL_ALSA_NB_PERIOD; snd_pcm_uframes_t period_size = SFL_ALSA_PERIOD_SIZE; snd_pcm_uframes_t buffer_size = SFL_ALSA_BUFFER_SIZE; unsigned int periods = SFL_ALSA_NB_PERIOD; snd_pcm_uframes_t period_size_min = 0; snd_pcm_uframes_t period_size_max = 0; snd_pcm_uframes_t buffer_size_min = 0; snd_pcm_uframes_t buffer_size_max = 0; #define HW pcm_handle, hwparams /* hardware parameters */ TRY(snd_pcm_hw_params_any(HW), "hwparams init"); TRY(snd_pcm_hw_params_set_access(HW, SND_PCM_ACCESS_RW_INTERLEAVED), "access type"); TRY(snd_pcm_hw_params_set_format(HW, SND_PCM_FORMAT_S16_LE), "sample format"); TRY(snd_pcm_hw_params_set_rate_resample(HW, 0), "hardware sample rate"); /* prevent software resampling */ TRY(snd_pcm_hw_params_set_rate_near(HW, &audioFormat_.sample_rate, nullptr), "sample rate"); // TODO: use snd_pcm_query_chmaps or similar to get hardware channel num audioFormat_.nb_channels = 2; TRY(snd_pcm_hw_params_set_channels_near(HW, &audioFormat_.nb_channels), "channel count"); snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min); snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max); snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, nullptr); snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, nullptr); DEBUG("Buffer size range from %lu to %lu", buffer_size_min, buffer_size_max); DEBUG("Period size range from %lu to %lu", period_size_min, period_size_max); buffer_size = buffer_size > buffer_size_max ? buffer_size_max : buffer_size; buffer_size = buffer_size < buffer_size_min ? buffer_size_min : buffer_size; period_size = period_size > period_size_max ? period_size_max : period_size; period_size = period_size < period_size_min ? period_size_min : period_size; TRY(snd_pcm_hw_params_set_buffer_size_near(HW, &buffer_size), "Unable to set buffer size for playback"); TRY(snd_pcm_hw_params_set_period_size_near(HW, &period_size, nullptr), "Unable to set period size for playback"); TRY(snd_pcm_hw_params_set_periods_near(HW, &periods, nullptr), "Unable to set number of periods for playback"); TRY(snd_pcm_hw_params(HW), "hwparams"); snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr); snd_pcm_hw_params_get_rate(hwparams, &audioFormat_.sample_rate, nullptr); snd_pcm_hw_params_get_channels(hwparams, &audioFormat_.nb_channels); DEBUG("Was set period_size = %lu", period_size); DEBUG("Was set buffer_size = %lu", buffer_size); if (2 * period_size > buffer_size) { ERROR("buffer to small, could not use"); return false; } #undef HW DEBUG("%s using format %s", (snd_pcm_stream(pcm_handle) == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture", audioFormat_.toString().c_str() ); snd_pcm_sw_params_t *swparams = NULL; snd_pcm_sw_params_alloca(&swparams); #define SW pcm_handle, swparams /* software parameters */ snd_pcm_sw_params_current(SW); TRY(snd_pcm_sw_params_set_start_threshold(SW, period_size * 2), "start threshold"); TRY(snd_pcm_sw_params(SW), "sw parameters"); #undef SW return true; #undef TRY }