static int pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { pa_sample_spec ss; cubeb_stream * stm; pa_operation * o; pa_buffer_attr battr; pa_channel_map map; int r; assert(context); *stream = NULL; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: ss.format = PA_SAMPLE_S16LE; break; case CUBEB_SAMPLE_S16BE: ss.format = PA_SAMPLE_S16BE; break; case CUBEB_SAMPLE_FLOAT32LE: ss.format = PA_SAMPLE_FLOAT32LE; break; case CUBEB_SAMPLE_FLOAT32BE: ss.format = PA_SAMPLE_FLOAT32BE; break; default: return CUBEB_ERROR_INVALID_FORMAT; } ss.rate = stream_params.rate; ss.channels = stream_params.channels; /* XXX check that this does the right thing for Vorbis and WaveEx */ WRAP(pa_channel_map_init_auto)(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->sample_spec = ss; battr.maxlength = -1; battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec); battr.prebuf = -1; battr.minreq = battr.tlength / 4; battr.fragsize = -1; WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &map); WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm); WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm); WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED, NULL, NULL); r = wait_until_stream_ready(stm); if (r == 0) { /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->stream, o); WRAP(pa_operation_unref)(o); } } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); if (r != 0) { pulse_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }
static int pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; pa_buffer_attr battr; int r; assert(context); // If the connection failed for some reason, try to reconnect if (context->error == 1 && pulse_context_init(context) != 0) { return CUBEB_ERROR; } *stream = NULL; stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->volume = PULSE_NO_GAIN; WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); if (output_stream_params) { r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream)); WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm); WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm); battr = set_buffering_attribute(latency, &stm->output_sample_spec); WRAP(pa_stream_connect_playback)(stm->output_stream, output_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, NULL, NULL); } // Set up input stream if (input_stream_params) { r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream)); WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm); WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm); battr = set_buffering_attribute(latency, &stm->input_sample_spec); WRAP(pa_stream_connect_record)(stm->input_stream, input_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); } r = wait_until_stream_ready(stm); if (r == 0) { /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ r = stream_update_timing_info(stm); } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); if (r != 0) { pulse_stream_destroy(stm); return CUBEB_ERROR; } #ifdef LOGGING_ENABLED if (output_stream_params){ const pa_buffer_attr * output_att; output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream); LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",output_att->maxlength, output_att->tlength, output_att->prebuf, output_att->minreq, output_att->fragsize); } if (input_stream_params){ const pa_buffer_attr * input_att; input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream); LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",input_att->maxlength, input_att->tlength, input_att->prebuf, input_att->minreq, input_att->fragsize); } #endif *stream = stm; return CUBEB_OK; }