int pa_scache_add_item( pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx) { pa_scache_entry *e; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_channel_map tmap; pa_assert(c); pa_assert(name); pa_assert(!ss || pa_sample_spec_valid(ss)); pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss))); if (ss && !map) { pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT); map = &tmap; } if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX) return -1; if (!(e = scache_add_item(c, name))) return -1; pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); e->volume_is_set = FALSE; if (ss) { e->sample_spec = *ss; pa_cvolume_reset(&e->volume, ss->channels); } if (map) e->channel_map = *map; if (chunk) { e->memchunk = *chunk; pa_memblock_ref(e->memchunk.memblock); } if (p) pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p); if (idx) *idx = e->index; pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s", name, e->index, (unsigned long) e->memchunk.length, pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec)); return 0; }
static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct priv *priv = ao->priv; switch (cmd) { case AOCONTROL_GET_MUTE: case AOCONTROL_GET_VOLUME: { uint32_t devidx = pa_stream_get_index(priv->stream); pa_threaded_mainloop_lock(priv->mainloop); if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx, info_func, ao))) { GENERIC_ERR_MSG(priv->context, "pa_stream_get_sink_input_info() failed"); return CONTROL_ERROR; } // Warning: some information in pi might be unaccessible, because // we naively copied the struct, without updating pointers etc. // Pointers might point to invalid data, accessors might fail. if (cmd == AOCONTROL_GET_VOLUME) { ao_control_vol_t *vol = arg; if (priv->pi.volume.channels != 2) vol->left = vol->right = pa_cvolume_avg(&priv->pi.volume) * 100 / PA_VOLUME_NORM; else { vol->left = priv->pi.volume.values[0] * 100 / PA_VOLUME_NORM; vol->right = priv->pi.volume.values[1] * 100 / PA_VOLUME_NORM; } } else if (cmd == AOCONTROL_GET_MUTE) { bool *mute = arg; *mute = priv->pi.mute; } return CONTROL_OK; } case AOCONTROL_SET_MUTE: case AOCONTROL_SET_VOLUME: { pa_operation *o; pa_threaded_mainloop_lock(priv->mainloop); uint32_t stream_index = pa_stream_get_index(priv->stream); if (cmd == AOCONTROL_SET_VOLUME) { const ao_control_vol_t *vol = arg; struct pa_cvolume volume; pa_cvolume_reset(&volume, ao->channels); if (volume.channels != 2) pa_cvolume_set(&volume, volume.channels, vol->left * PA_VOLUME_NORM / 100); else { volume.values[0] = vol->left * PA_VOLUME_NORM / 100; volume.values[1] = vol->right * PA_VOLUME_NORM / 100; } o = pa_context_set_sink_input_volume(priv->context, stream_index, &volume, NULL, NULL); if (!o) { pa_threaded_mainloop_unlock(priv->mainloop); GENERIC_ERR_MSG(priv->context, "pa_context_set_sink_input_volume() failed"); return CONTROL_ERROR; } } else if (cmd == AOCONTROL_SET_MUTE) { const bool *mute = arg; o = pa_context_set_sink_input_mute(priv->context, stream_index, *mute, NULL, NULL); if (!o) { pa_threaded_mainloop_unlock(priv->mainloop); GENERIC_ERR_MSG(priv->context, "pa_context_set_sink_input_mute() failed"); return CONTROL_ERROR; } } else abort(); /* We don't wait for completion here */ pa_operation_unref(o); pa_threaded_mainloop_unlock(priv->mainloop); return CONTROL_OK; } default: return CONTROL_UNKNOWN; } }
bool AudioOutputPulseAudio::ConnectPlaybackStream(void) { QString fn_log_tag = "ConnectPlaybackStream, "; pstream = pa_stream_new(pcontext, "MythTV playback", &sample_spec, &channel_map); if (!pstream) { VBERROR(fn_log_tag + QString("failed to create new playback stream")); return false; } pa_stream_set_state_callback(pstream, StreamStateCallback, this); pa_stream_set_write_callback(pstream, WriteCallback, this); pa_stream_set_overflow_callback(pstream, BufferFlowCallback, (char*)"over"); pa_stream_set_underflow_callback(pstream, BufferFlowCallback, (char*)"under"); if (set_initial_vol) { int volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80); pa_cvolume_set(&volume_control, channels, (float)volume * (float)PA_VOLUME_NORM / 100.0f); } else pa_cvolume_reset(&volume_control, channels); fragment_size = (samplerate * 25 * output_bytes_per_frame) / 1000; buffer_settings.maxlength = (uint32_t)-1; buffer_settings.tlength = fragment_size * 4; buffer_settings.prebuf = (uint32_t)-1; buffer_settings.minreq = (uint32_t)-1; int flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NO_REMIX_CHANNELS; pa_stream_connect_playback(pstream, NULL, &buffer_settings, (pa_stream_flags_t)flags, &volume_control, NULL); pa_context_state_t cstate; pa_stream_state_t sstate; bool connected = false, failed = false; while (!(connected || failed)) { switch (cstate = pa_context_get_state(pcontext)) { case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag + QString("context is stuffed, %1") .arg(pa_strerror(pa_context_errno( pcontext)))); failed = true; break; default: switch (sstate = pa_stream_get_state(pstream)) { case PA_STREAM_READY: connected = true; break; case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: VBERROR(fn_log_tag + QString("stream failed or was terminated, " "context state %1, stream state %2") .arg(cstate).arg(sstate)); failed = true; break; default: pa_threaded_mainloop_wait(mainloop); break; } } } const pa_buffer_attr *buf_attr = pa_stream_get_buffer_attr(pstream); fragment_size = buf_attr->tlength >> 2; soundcard_buffer_size = buf_attr->maxlength; VBAUDIO(fn_log_tag + QString("fragment size %1, soundcard buffer size %2") .arg(fragment_size).arg(soundcard_buffer_size)); return (connected && !failed); }
static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) { const char *hpos, *vpos, *role, *id; double f; char t[PA_CVOLUME_SNPRINT_MAX]; pa_cvolume v; pa_assert(data); if (!(role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE))) return PA_HOOK_OK; if (!pa_streq(role, "event")) return PA_HOOK_OK; if ((id = pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID))) { /* The test sounds should never be positioned in space, since * they might be triggered themselves to configure the speakers * in space, which we don't want to mess up. */ if (pa_startswith(id, "audio-channel-")) return PA_HOOK_OK; if (pa_streq(id, "audio-volume-change")) return PA_HOOK_OK; if (pa_streq(id, "audio-test-signal")) return PA_HOOK_OK; } if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) hpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_HPOS); if (!(vpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_VPOS))) vpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_VPOS); if (!hpos && !vpos) return PA_HOOK_OK; pa_cvolume_reset(&v, data->sink->sample_spec.channels); if (hpos) { if (parse_pos(hpos, &f) < 0) return PA_HOOK_OK; if (pa_channel_map_can_balance(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' horizontally at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); pa_cvolume_set_balance(&v, &data->sink->channel_map, f*2.0-1.0); } } if (vpos) { if (parse_pos(vpos, &f) < 0) return PA_HOOK_OK; if (pa_channel_map_can_fade(&data->sink->channel_map)) { pa_log_debug("Positioning event sound '%s' vertically at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); pa_cvolume_set_fade(&v, &data->sink->channel_map, f*2.0-1.0); } } pa_log_debug("Final volume factor %s.", pa_cvolume_snprint(t, sizeof(t), &v)); pa_sink_input_new_data_add_volume_factor_sink(data, u->name, &v); return PA_HOOK_OK; }
/* Called from main context */ pa_source* pa_source_new( pa_core *core, pa_source_new_data *data, pa_source_flags_t flags) { pa_source *s; const char *name; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pt; pa_assert(core); pa_assert(data); pa_assert(data->name); pa_assert_ctl_context(); s = pa_msgobject_new(pa_source); if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { pa_log_debug("Failed to register name %s.", data->name); pa_xfree(s); return NULL; } pa_source_new_data_set_name(data, name); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); return NULL; } /* FIXME, need to free s here on failure */ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec)); if (!data->channel_map_is_set) pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); if (!data->volume_is_set) pa_cvolume_reset(&data->volume, data->sample_spec.channels); pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); if (!data->muted_is_set) data->muted = FALSE; if (data->card) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist); pa_device_init_description(data->proplist); pa_device_init_icon(data->proplist, FALSE); pa_device_init_intended_roles(data->proplist); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); return NULL; } s->parent.parent.free = source_free; s->parent.process_msg = pa_source_process_msg; s->core = core; s->state = PA_SOURCE_INIT; s->flags = flags; s->priority = 0; s->suspend_cause = 0; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); s->module = data->module; s->card = data->card; s->priority = pa_device_init_priority(s->proplist); s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->monitor_of = NULL; s->output_from_master = NULL; s->volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); s->base_volume = PA_VOLUME_NORM; s->n_volume_steps = PA_VOLUME_NORM+1; s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; reset_callbacks(s); s->userdata = NULL; s->asyncmsgq = NULL; /* As a minor optimization we just steal the list instead of * copying it here */ s->ports = data->ports; data->ports = NULL; s->active_port = NULL; s->save_port = FALSE; if (data->active_port && s->ports) if ((s->active_port = pa_hashmap_get(s->ports, data->active_port))) s->save_port = data->save_port; if (!s->active_port && s->ports) { void *state; pa_device_port *p; PA_HASHMAP_FOREACH(p, s->ports, state) if (!s->active_port || p->priority > s->active_port->priority) s->active_port = p; }
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_scache_entry *e; pa_cvolume r; pa_proplist *merged; pa_bool_t pass_volume; pa_assert(c); pa_assert(name); pa_assert(sink); if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return -1; merged = pa_proplist_new(); pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, name); pa_proplist_sets(merged, PA_PROP_EVENT_ID, name); if (e->lazy && !e->memchunk.memblock) { pa_channel_map old_channel_map = e->channel_map; if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0) goto fail; pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); if (e->volume_is_set) { if (pa_cvolume_valid(&e->volume)) pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map); else pa_cvolume_reset(&e->volume, e->sample_spec.channels); } } if (!e->memchunk.memblock) goto fail; pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name); pass_volume = TRUE; if (e->volume_is_set && volume != PA_VOLUME_INVALID) { pa_cvolume_set(&r, e->sample_spec.channels, volume); pa_sw_cvolume_multiply(&r, &r, &e->volume); } else if (e->volume_is_set) r = e->volume; else if (volume != PA_VOLUME_INVALID) pa_cvolume_set(&r, e->sample_spec.channels, volume); else pass_volume = FALSE; pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); if (p) pa_proplist_update(merged, PA_UPDATE_REPLACE, p); if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND, sink_input_idx) < 0) goto fail; pa_proplist_free(merged); if (e->lazy) time(&e->last_used_time); return 0; fail: pa_proplist_free(merged); return -1; }
static int init(int rate_hz, int channels, int format, int flags) { struct pa_sample_spec ss; struct pa_channel_map map; const struct format_map_s *fmt_map; char *devarg = NULL; char *host = NULL; char *sink = NULL; char *version = pa_get_library_version(); if (ao_subdevice) { devarg = strdup(ao_subdevice); sink = strchr(devarg, ':'); if (sink) *sink++ = 0; if (devarg[0]) host = devarg; } broken_pause = 0; // not sure which versions are affected, assume 0.9.11* to 0.9.14* // known bad: 0.9.14, 0.9.13 // known good: 0.9.9, 0.9.10, 0.9.15 // to test: pause, wait ca. 5 seconds framestep and see if MPlayer hangs somewhen if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') { mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n" " see http://www.pulseaudio.org/ticket/440\n"); broken_pause = 1; } ss.channels = channels; ss.rate = rate_hz; ao_data.samplerate = rate_hz; ao_data.channels = channels; fmt_map = format_maps; while (fmt_map->mp_format != format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n"); fmt_map = format_maps; break; } fmt_map++; } ao_data.format = fmt_map->mp_format; ss.format = fmt_map->pa_format; if (!pa_sample_spec_valid(&ss)) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); goto fail; } pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); ao_data.bps = pa_bytes_per_second(&ss); pa_cvolume_reset(&volume, ss.channels); if (!(mainloop = pa_threaded_mainloop_new())) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); goto fail; } if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n"); goto fail; } pa_context_set_state_callback(context, context_state_cb, NULL); if (pa_context_connect(context, host, 0, NULL) < 0) goto fail; pa_threaded_mainloop_lock(mainloop); if (pa_threaded_mainloop_start(mainloop) < 0) goto unlock_and_fail; /* Wait until the context is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_context_get_state(context) != PA_CONTEXT_READY) goto unlock_and_fail; if (!(stream = pa_stream_new(context, "audio stream", &ss, &map))) goto unlock_and_fail; pa_stream_set_state_callback(stream, stream_state_cb, NULL); pa_stream_set_write_callback(stream, stream_request_cb, NULL); pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0) goto unlock_and_fail; /* Wait until the stream is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_stream_get_state(stream) != PA_STREAM_READY) goto unlock_and_fail; pa_threaded_mainloop_unlock(mainloop); free(devarg); return 1; unlock_and_fail: if (mainloop) pa_threaded_mainloop_unlock(mainloop); fail: if (context) GENERIC_ERR_MSG(context, "Init failed"); free(devarg); uninit(1); return 0; }