static void source_info_cb(pa_context * c, const pa_source_info * i, int is_last, void *userdata) { snd_ctl_pulse_t *ctl = (snd_ctl_pulse_t *) userdata; int changed = 0; assert(ctl); if (is_last) { pa_threaded_mainloop_signal(ctl->p->mainloop, 0); return; } assert(i); if (!!ctl->source_muted != !!i->mute) { ctl->source_muted = i->mute; ctl->updated |= UPDATE_SOURCE_MUTE; changed = 1; } if (!pa_cvolume_equal(&ctl->source_volume, &i->volume)) { ctl->source_volume = i->volume; ctl->updated |= UPDATE_SOURCE_VOL; changed = 1; } if (changed) pulse_poll_activate(ctl->p); }
void AudioSinksManager::InternalAudioSink::sink_info_callback(pa_context* /*c*/, const pa_sink_info* info, int eol, void* userdata) { AudioSinksManager::InternalAudioSink* sink = static_cast<AudioSinksManager::InternalAudioSink*>(userdata); if (!info) { assert(eol); return; } if (sink->sink_idx == static_cast<uint32_t>(-1)) { sink->sink_idx = info->index; sink->manager->logger->debug("(AudioSink '{}') Sink idx is: {}", sink->name, sink->sink_idx); sink->manager->sink_idx_audio_sink.emplace(sink->sink_idx, sink->shared_from_this()); } else { assert(sink->sink_idx == info->index); } if (!pa_cvolume_equal(&sink->volume, &info->volume) || sink->muted != info->mute) { sink->volume = info->volume; sink->muted = !!info->mute; assert(sink->volume.channels == 2); sink->manager->logger->trace("(AudioSink '{}') Volume changed", sink->name); if (sink->volume_callback) { sink->volume_callback(static_cast<double>(sink->volume.values[0]) / PA_VOLUME_NORM, static_cast<double>(sink->volume.values[1]) / PA_VOLUME_NORM, sink->muted); } } }
void PulseStream::setVolume(const pa_cvolume *volume) { if (mCachedVolume != -1) QMetaObject::invokeMethod(this, "applyCachedVolume", Qt::QueuedConnection); if (pa_cvolume_equal(&mVolume, volume) == 0) { memcpy(&mVolume, volume, sizeof(mVolume)); qreal vol = (qreal)pa_cvolume_avg(volume) / PA_VOLUME_NORM; // AudioOutput expects the "backend" to supply values that have been // adjusted for Stephens' law, so we need to fudge them accordingly // so that the %ages match up in KMix/the application's own slider. emit volumeChanged(qPow(vol, VOLTAGE_TO_LOUDNESS_EXPONENT)); } }
static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { pa_cvolume t; if (a->port_valid != b->port_valid || (a->port_valid && strncmp(a->port, b->port, sizeof(a->port)))) return FALSE; if (a->muted_valid != b->muted_valid || (a->muted_valid && (a->muted != b->muted))) return FALSE; t = b->volume; if (a->volume_valid != b->volume_valid || (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume))) return FALSE; return TRUE; }
void gvc_channel_map_volume_changed (GvcChannelMap *map, const pa_cvolume *cv, gboolean set) { g_return_if_fail (GVC_IS_CHANNEL_MAP (map)); g_return_if_fail (cv != NULL); g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map)); if (pa_cvolume_equal(cv, &map->priv->pa_volume)) return; map->priv->pa_volume = *cv; if (map->priv->pa_volume_is_set == FALSE) { map->priv->pa_volume_is_set = TRUE; return; } g_signal_emit (map, signals[VOLUME_CHANGED], 0, set); }
static void source_get_volume_cb(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return; /* FIXME, no volume control in source_output, get the info from the master */ pa_source_get_volume(u->source_output->source, TRUE); if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume)) /* no change */ return; s->volume = u->source_output->source->volume; pa_source_set_soft_volume(s, NULL); }
/* update the volume by firing the appropriate volume hook */ static void voice_update_volumes(struct userdata *u) { const pa_cvolume *cvol; pa_assert(u); pa_assert(u->master_sink); cvol = &u->master_sink->real_volume; /* check the volume , if its same as previous one then return */ if (pa_cvolume_equal(cvol, &u->previous_volume)) return; /* assign the current volume, will be used for the next time*/ u->previous_volume = *cvol; if (voice_voip_source_active(u)) meego_algorithm_hook_fire(u->hooks[HOOK_CALL_VOLUME], (void*)cvol); else meego_algorithm_hook_fire(u->hooks[HOOK_VOLUME], (void*)cvol); pa_log_debug("volume is updated"); }
void PulseAudioSystem::stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *userdata) { PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata); if (eol == 0) { QString name = QLatin1String(i->name); // were we looking for this restoration? if (pas->qhMissingSinks.contains(name)) { // make sure it still has the volume we gave it if (pa_cvolume_equal(&pas->qhMissingSinks[name].attenuated_volume, &i->volume) != 0) { // update the stream restore record pa_ext_stream_restore_info restore = *i; restore.volume = pas->qhMissingSinks[name].normal_volume; pas->iRemainingOperations++; pa_operation_unref(pa_ext_stream_restore_write(c, PA_UPDATE_REPLACE, &restore, 1, 1, restore_volume_success_callback, pas)); } pas->qhMissingSinks.remove(name); } } else if (eol < 0) { qWarning("PulseAudio: Couldn't read stream restore database."); pas->qhMissingSinks.clear(); } else { // verify missing list is empty if (pas->qhMissingSinks.count() > 0) { qWarning("PulseAudio: Failed to match %d stream(s).", pas->qhMissingSinks.count()); pas->qhMissingSinks.clear(); } // trigger the volume completion callback; // necessary so that shutdown actions are called restore_volume_success_callback(c, 1, pas); } }
void PulseAudioSystem::restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata); if (eol == 0) { // if we were tracking this specific sink previously if (pas->qhVolumes.contains(i->index)) { // and if it has the attenuated volume we applied to it if (pa_cvolume_equal(&i->volume, &pas->qhVolumes[i->index].attenuated_volume) != 0) { // mark it as matched pas->qlMatchedSinks.append(i->index); // reset the volume to normal pas->iRemainingOperations++; pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &pas->qhVolumes[i->index].normal_volume, restore_volume_success_callback, pas)); } // otherwise, save for matching at the end of iteration } else { QString restore_id = QLatin1String(pa_proplist_gets(i->proplist, "module-stream-restore.id")); PulseAttenuation patt; patt.index = i->index; patt.normal_volume = i->volume; pas->qhUnmatchedSinks[restore_id] = patt; } } else if (eol < 0) { qWarning("PulseAudio: Sink input introspection error."); } else { // build a list of missing streams by iterating our active list QHash<uint32_t, PulseAttenuation>::const_iterator it; for (it = pas->qhVolumes.constBegin(); it != pas->qhVolumes.constEnd(); ++it) { // skip if previously matched if (pas->qlMatchedSinks.contains(it.key())) { continue; } // check if the restore id matches. the only case where this would // happen is if the application was reopened during attenuation. if (pas->qhUnmatchedSinks.contains(it.value().stream_restore_id)) { PulseAttenuation active_sink = pas->qhUnmatchedSinks[it.value().stream_restore_id]; // if the volume wasn't changed from our attenuation if (pa_cvolume_equal(&active_sink.normal_volume, &it.value().attenuated_volume) != 0) { // reset the volume to normal pas->iRemainingOperations++; pa_operation_unref(pa_context_set_sink_input_volume(c, active_sink.index, &it.value().normal_volume, restore_volume_success_callback, pas)); } continue; } // at this point, we don't know what happened to the sink. add // it to a list to check the stream restore database for. pas->qhMissingSinks[it.value().stream_restore_id] = it.value(); } // clean up pas->qlMatchedSinks.clear(); pas->qhUnmatchedSinks.clear(); pas->qhVolumes.clear(); // if we had missing sinks, check the stream restore database // to see if we can find and update them. if (pas->qhMissingSinks.count() > 0) { pas->iRemainingOperations++; pa_operation_unref(pa_ext_stream_restore_read(c, stream_restore_read_callback, pas)); } // trigger the volume completion callback; // necessary so that shutdown actions are called restore_volume_success_callback(c, 1, pas); } }
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; pa_sink_input *si = NULL; pa_source_output *so = NULL; struct rule *r; char *name; pa_assert(c); pa_assert(u); if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) && t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) return; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) return; if (!si->client || !(name = client_name(si->client))) return; } else { pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT); if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) return; if (!so->client || !(name = client_name(so->client))) return; } if ((r = pa_hashmap_get(u->hashmap, name))) { pa_xfree(name); if (si) { if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) { pa_log_info("Saving volume for <%s>", r->name); r->volume = *pa_sink_input_get_volume(si); r->volume_is_set = TRUE; u->modified = TRUE; } if (!r->sink || strcmp(si->sink->name, r->sink) != 0) { pa_log_info("Saving sink for <%s>", r->name); pa_xfree(r->sink); r->sink = pa_xstrdup(si->sink->name); u->modified = TRUE; } } else { pa_assert(so); if (!r->source || strcmp(so->source->name, r->source) != 0) { pa_log_info("Saving source for <%s>", r->name); pa_xfree(r->source); r->source = pa_xstrdup(so->source->name); u->modified = TRUE; } } } else { pa_log_info("Creating new entry for <%s>", name); r = pa_xnew(struct rule, 1); r->name = name; if (si) { r->volume = *pa_sink_input_get_volume(si); r->volume_is_set = TRUE; r->sink = pa_xstrdup(si->sink->name); r->source = NULL; } else { pa_assert(so); r->volume_is_set = FALSE; r->sink = NULL; r->source = pa_xstrdup(so->source->name); } pa_hashmap_put(u->hashmap, r->name, r); u->modified = TRUE; } if (u->modified && !u->save_time_event) { struct timeval tv; pa_gettimeofday(&tv); tv.tv_sec += SAVE_INTERVAL; u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); } }