static audio_stream * alsa_create_stream(audio_stream_direction direction, unsigned int sample_rate, unsigned int sample_frame_count, const char *pcm_name) { audio_stream *as; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; int dir; if (!g_atomic_int_get(&audio_thread_started)) { pthread_barrier_init(&stream_list_update_barrier, NULL, 2); pthread_create(&audio_thread_id, NULL, audio_thread, NULL); g_atomic_int_set(&audio_thread_started, 1); pthread_barrier_wait(&stream_list_update_barrier); } as = calloc(1, sizeof(*as)); if (!as) goto err; as->sample_frame_count = sample_frame_count; g_atomic_int_set(&as->paused, 1); #define CHECK_A(funcname, params) \ do { \ int errcode___ = funcname params; \ if (errcode___ < 0) { \ trace_error("%s, " #funcname ", %s\n", __func__, snd_strerror(errcode___)); \ goto err; \ } \ } while (0) if (direction == STREAM_PLAYBACK) CHECK_A(snd_pcm_open, (&as->pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)); else CHECK_A(snd_pcm_open, (&as->pcm, pcm_name, SND_PCM_STREAM_CAPTURE, 0)); CHECK_A(snd_pcm_hw_params_malloc, (&hw_params)); CHECK_A(snd_pcm_hw_params_any, (as->pcm, hw_params)); CHECK_A(snd_pcm_hw_params_set_access, (as->pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); CHECK_A(snd_pcm_hw_params_set_format, (as->pcm, hw_params, SND_PCM_FORMAT_S16_LE)); dir = 0; unsigned int rate = sample_rate; CHECK_A(snd_pcm_hw_params_set_rate_near, (as->pcm, hw_params, &rate, &dir)); const int channel_count = (direction == STREAM_PLAYBACK) ? 2 : 1; CHECK_A(snd_pcm_hw_params_set_channels, (as->pcm, hw_params, channel_count)); unsigned int period_time = (long long)sample_frame_count * 1000 * 1000 / sample_rate; period_time = CLAMP(period_time, 1000 * (unsigned int)config.audio_buffer_min_ms, 1000 * (unsigned int)config.audio_buffer_max_ms); dir = 1; CHECK_A(snd_pcm_hw_params_set_period_time_near, (as->pcm, hw_params, &period_time, &dir)); unsigned int buffer_time = 4 * period_time; dir = 1; CHECK_A(snd_pcm_hw_params_set_buffer_time_near, (as->pcm, hw_params, &buffer_time, &dir)); dir = 0; CHECK_A(snd_pcm_hw_params_get_buffer_time, (hw_params, &buffer_time, &dir)); CHECK_A(snd_pcm_hw_params, (as->pcm, hw_params)); snd_pcm_hw_params_free(hw_params); CHECK_A(snd_pcm_sw_params_malloc, (&sw_params)); CHECK_A(snd_pcm_sw_params_current, (as->pcm, sw_params)); CHECK_A(snd_pcm_sw_params, (as->pcm, sw_params)); CHECK_A(snd_pcm_prepare, (as->pcm)); snd_pcm_sw_params_free(sw_params); CHECK_A(snd_pcm_prepare, (as->pcm)); if (direction == STREAM_CAPTURE) CHECK_A(snd_pcm_start, (as->pcm)); as->nfds = snd_pcm_poll_descriptors_count(as->pcm); as->fds = calloc(as->nfds, sizeof(struct pollfd)); if (!as->fds) { trace_error("%s, memory allocation failure\n", __func__); goto err; } snd_pcm_poll_descriptors(as->pcm, as->fds, as->nfds); g_hash_table_insert(active_streams_ht, as, GINT_TO_POINTER(1)); for (uintptr_t k = 0; k < as->nfds; k ++) g_hash_table_insert(stream_by_fd_ht, GINT_TO_POINTER(as->fds[k].fd), as); wakeup_audio_thread(); #undef CHECK_A return as; err: free(as); return NULL; }
static int do_connect_pcm(pa_stream *s, snd_pcm_stream_t stream_direction) { snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; int dir; unsigned int rate; const char *dev_name; switch (stream_direction) { default: case SND_PCM_STREAM_PLAYBACK: dev_name = getenv("APULSE_PLAYBACK_DEVICE"); CHECK_A(snd_pcm_open, (&s->ph, dev_name ? dev_name : "default", stream_direction, 0)); break; case SND_PCM_STREAM_CAPTURE: dev_name = getenv("APULSE_CAPTURE_DEVICE"); CHECK_A(snd_pcm_open, (&s->ph, dev_name ? dev_name : "default", stream_direction, 0)); break; } CHECK_A(snd_pcm_hw_params_malloc, (&hw_params)); CHECK_A(snd_pcm_hw_params_any, (s->ph, hw_params)); CHECK_A(snd_pcm_hw_params_set_access, (s->ph, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); CHECK_A(snd_pcm_hw_params_set_format, (s->ph, hw_params, pa_format_to_alsa(s->ss.format))); CHECK_A(snd_pcm_hw_params_set_rate_resample, (s->ph, hw_params, 1)); rate = s->ss.rate; dir = 0; CHECK_A(snd_pcm_hw_params_set_rate_near, (s->ph, hw_params, &rate, &dir)); CHECK_A(snd_pcm_hw_params_set_channels, (s->ph, hw_params, s->ss.channels)); unsigned int period_time = 20 * 1000; dir = 1; CHECK_A(snd_pcm_hw_params_set_period_time_near, (s->ph, hw_params, &period_time, &dir)); unsigned int buffer_time = 4 * period_time; dir = 1; CHECK_A(snd_pcm_hw_params_set_buffer_time_near, (s->ph, hw_params, &buffer_time, &dir)); CHECK_A(snd_pcm_hw_params, (s->ph, hw_params)); snd_pcm_hw_params_free(hw_params); CHECK_A(snd_pcm_sw_params_malloc, (&sw_params)); CHECK_A(snd_pcm_sw_params_current, (s->ph, sw_params)); const snd_pcm_uframes_t period_size = (uint64_t)period_time * rate / (1000 * 1000); CHECK_A(snd_pcm_sw_params_set_avail_min, (s->ph, sw_params, period_size)); // no period event requested CHECK_A(snd_pcm_sw_params, (s->ph, sw_params)); snd_pcm_sw_params_free(sw_params); CHECK_A(snd_pcm_prepare, (s->ph)); int nfds = snd_pcm_poll_descriptors_count(s->ph); struct pollfd *fds = calloc(nfds, sizeof(struct pollfd)); s->ioe = calloc(nfds, sizeof(pa_io_event *)); s->nioe = nfds; snd_pcm_poll_descriptors(s->ph, fds, nfds); for (int k = 0; k < nfds; k ++) { pa_mainloop_api *api = s->c->mainloop_api; s->ioe[k] = api->io_new(api, fds[k].fd, 0x80000000 | fds[k].events, data_available_for_stream, s); s->ioe[k]->pcm = s->ph; } free(fds); s->state = PA_STREAM_READY; pa_stream_ref(s); s->c->mainloop_api->defer_new(s->c->mainloop_api, deh_stream_state_changed, s); pa_stream_ref(s); s->c->mainloop_api->defer_new(s->c->mainloop_api, deh_stream_first_readwrite_callback, s); return 0; err: return -1; }