static void pulse_stop_capture(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_operation *o; o = pa_stream_cork(data->stream, 1, stream_success_callback, device); wait_for_operation(o, data->loop); }
/** Issue special libao controls on the device */ static int control(int cmd, void *arg) { if (!context || !stream) return CONTROL_ERROR; switch (cmd) { case AOCONTROL_SET_DEVICE: /* Change the playback device */ sink = (char*)arg; return CONTROL_OK; case AOCONTROL_GET_DEVICE: /* Return the playback device */ *(char**)arg = sink; return CONTROL_OK; case AOCONTROL_GET_VOLUME: { /* Return the current volume of the playback stream */ ao_control_vol_t *vol = (ao_control_vol_t*) arg; volume = PA_VOLUME_NORM; wait_for_operation(pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_func, NULL)); vol->left = vol->right = (int) (pa_volume_to_user(volume)*100); return CONTROL_OK; } case AOCONTROL_SET_VOLUME: { /* Set the playback volume of the stream */ const ao_control_vol_t *vol = (ao_control_vol_t*) arg; int v = vol->left; if (vol->right > v) v = vol->left; wait_for_operation(pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), pa_volume_from_user((double)v/100), NULL, NULL)); return CONTROL_OK; } default: /* Unknown CONTROL command */ return CONTROL_UNKNOWN; } }
/** Play the specified data to the polypaudio server */ static int play(void* data, int len, int flags) { assert(stream && context); if (pa_stream_get_state(stream) != PA_STREAM_READY) return -1; if (!len) wait_for_operation(pa_stream_trigger(stream, NULL, NULL)); else pa_stream_write(stream, data, len, NULL, 0); wait_for_completion(); if (pa_stream_get_state(stream) != PA_STREAM_READY) return -1; return len; }
/** Destroy libao driver */ static void uninit(int immed) { if (stream) { if (!immed && pa_stream_get_state(stream) == PA_STREAM_READY) wait_for_operation(pa_stream_drain(stream, NULL, NULL)); pa_stream_unref(stream); stream = NULL; } if (context) { pa_context_unref(context); context = NULL; } if (mainloop) { pa_mainloop_free(mainloop); mainloop = NULL; } }
static void pulse_stop_playback(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_operation *o; if(!data->stream) return; data->killNow = AL_TRUE; if(data->thread) { StopThread(data->thread); data->thread = NULL; } data->killNow = AL_FALSE; pa_threaded_mainloop_lock(data->loop); o = pa_stream_cork(data->stream, 1, stream_success_callback, device); wait_for_operation(o, data->loop); pa_threaded_mainloop_unlock(data->loop); }
/** Reset the audio stream, i.e. flush the playback buffer on the server side */ static void reset(void) { assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY); wait_for_operation(pa_stream_flush(stream, NULL, NULL)); }
/** Resume the audio stream by uncorking it on the server */ static void audio_resume(void) { assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY); wait_for_operation(pa_stream_cork(stream, 0, NULL, NULL)); }
/* OpenAL */ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name) { const char *pulse_name = NULL; pa_stream_flags_t flags; pa_sample_spec spec; pulse_data *data; pa_operation *o; if(device_name) { ALuint i; if(!allDevNameMap) probe_devices(AL_FALSE); for(i = 0; i < numDevNames; i++) { if(strcmp(device_name, allDevNameMap[i].name) == 0) { pulse_name = allDevNameMap[i].device_name; break; } } if(i == numDevNames) return ALC_INVALID_VALUE; } if(pulse_open(device) == ALC_FALSE) return ALC_INVALID_VALUE; data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; spec.format = PA_SAMPLE_S16NE; spec.rate = 44100; spec.channels = 2; data->stream = connect_playback_stream(pulse_name, data->loop, data->context, flags, NULL, &spec, NULL); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); pulse_close(device); return ALC_INVALID_VALUE; } data->device_name = strdup(pa_stream_get_device_name(data->stream)); o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_name_callback, device); wait_for_operation(o, data->loop); pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; }
static void probe_devices(ALboolean capture) { pa_threaded_mainloop *loop; if(capture == AL_FALSE) allDevNameMap = malloc(sizeof(DevMap) * 1); else allCaptureDevNameMap = malloc(sizeof(DevMap) * 1); if((loop=pa_threaded_mainloop_new()) && pa_threaded_mainloop_start(loop) >= 0) { pa_context *context; pa_threaded_mainloop_lock(loop); context = connect_context(loop, AL_FALSE); if(context) { pa_operation *o; if(capture == AL_FALSE) { pa_stream_flags_t flags; pa_sample_spec spec; pa_stream *stream; flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; spec.format = PA_SAMPLE_S16NE; spec.rate = 44100; spec.channels = 2; stream = connect_playback_stream(NULL, loop, context, flags, NULL, &spec, NULL); if(stream) { o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream), sink_device_callback, loop); wait_for_operation(o, loop); pa_stream_disconnect(stream); pa_stream_unref(stream); stream = NULL; } o = pa_context_get_sink_info_list(context, sink_device_callback, loop); } else { pa_stream_flags_t flags; pa_sample_spec spec; pa_stream *stream; flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE; spec.format = PA_SAMPLE_S16NE; spec.rate = 44100; spec.channels = 1; stream = connect_record_stream(NULL, loop, context, flags, NULL, &spec, NULL); if(stream) { o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream), source_device_callback, loop); wait_for_operation(o, loop); pa_stream_disconnect(stream); pa_stream_unref(stream); stream = NULL; } o = pa_context_get_source_info_list(context, source_device_callback, loop); } wait_for_operation(o, loop); pa_context_disconnect(context); pa_context_unref(context); } pa_threaded_mainloop_unlock(loop); pa_threaded_mainloop_stop(loop); } if(loop) pa_threaded_mainloop_free(loop); }
static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name) { const char *pulse_name = NULL; pa_stream_flags_t flags = 0; pa_channel_map chanmap; pulse_data *data; pa_operation *o; ALuint samples; if(device_name) { ALuint i; if(!allCaptureDevNameMap) probe_devices(AL_TRUE); for(i = 0; i < numCaptureDevNames; i++) { if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) { pulse_name = allCaptureDevNameMap[i].device_name; break; } } if(i == numCaptureDevNames) return ALC_INVALID_VALUE; } if(pulse_open(device) == ALC_FALSE) return ALC_INVALID_VALUE; data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); switch(device->FmtType) { case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtInt: data->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; case DevFmtByte: case DevFmtUShort: case DevFmtUInt: ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); goto fail; } samples = device->UpdateSize * device->NumUpdates; samples = maxu(samples, 100 * device->Frequency / 1000); data->attr.minreq = -1; data->attr.prebuf = -1; data->attr.maxlength = samples * pa_frame_size(&data->spec); data->attr.tlength = -1; data->attr.fragsize = minu(samples, 50*device->Frequency/1000) * pa_frame_size(&data->spec); flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; data->stream = connect_record_stream(pulse_name, data->loop, data->context, flags, &data->attr, &data->spec, &chanmap); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); goto fail; } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); data->device_name = strdup(pa_stream_get_device_name(data->stream)); o = pa_context_get_source_info_by_name(data->context, data->device_name, source_name_callback, device); wait_for_operation(o, data->loop); pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; fail: pulse_close(device); return ALC_INVALID_VALUE; }
static ALCboolean pulse_reset_playback(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_stream_flags_t flags = 0; pa_channel_map chanmap; ALuint len; pa_threaded_mainloop_lock(data->loop); if(data->stream) { pa_stream_set_moved_callback(data->stream, NULL, NULL); #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); #endif pa_stream_disconnect(data->stream); pa_stream_unref(data->stream); data->stream = NULL; } if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) { pa_operation *o; o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); wait_for_operation(o, data->loop); } if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) flags |= PA_STREAM_FIX_RATE; flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; flags |= PA_STREAM_ADJUST_LATENCY; flags |= PA_STREAM_START_CORKED; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; switch(device->FmtType) { case DevFmtByte: device->FmtType = DevFmtUByte; /* fall-through */ case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtUShort: device->FmtType = DevFmtShort; /* fall-through */ case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtUInt: device->FmtType = DevFmtInt; /* fall-through */ case DevFmtInt: data->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; } data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } SetDefaultWFXChannelOrder(device); data->attr.fragsize = -1; data->attr.prebuf = 0; data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); data->attr.maxlength = -1; data->stream = connect_playback_stream(data->device_name, data->loop, data->context, flags, &data->attr, &data->spec, &chanmap); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); data->spec = *(pa_stream_get_sample_spec(data->stream)); if(device->Frequency != data->spec.rate) { pa_operation *o; /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency * data->spec.rate + 0.5); data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16); data->attr.maxlength = -1; data->attr.prebuf = 0; o = pa_stream_set_buffer_attr(data->stream, &data->attr, stream_success_callback, device); wait_for_operation(o, data->loop); device->Frequency = data->spec.rate; } pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); #endif stream_buffer_attr_callback(data->stream, device); len = data->attr.minreq / pa_frame_size(&data->spec); if((CPUCapFlags&CPU_CAP_SSE)) len = (len+3)&~3; device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5); device->NumUpdates = clampu(device->NumUpdates, 2, 16); device->UpdateSize = len; pa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; }