diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4ffa03bf8..8f157eced 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -320,10 +320,13 @@ int pa_sink_input_new( pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); - /* Don't restore (or save) stream volume for passthrough streams */ + /* Don't restore (or save) stream volume for passthrough streams and + * prevent attenuation/gain */ if (pa_sink_input_new_data_is_passthrough(data)) { - data->volume_is_set = FALSE; - data->volume_factor_is_set = FALSE; + data->volume_is_set = TRUE; + pa_cvolume_reset(&data->volume, data->sample_spec.channels); + data->volume_is_absolute = TRUE; + data->save_volume = FALSE; } if (!data->volume_is_set) { @@ -610,16 +613,15 @@ void pa_sink_input_unlink(pa_sink_input *i) { i->state = PA_SINK_INPUT_UNLINKED; if (linked && i->sink) { + if (pa_sink_input_is_passthrough(i)) + pa_sink_leave_passthrough(i->sink); + /* We might need to update the sink's volume if we are in flat volume mode. */ if (pa_sink_flat_volume_enabled(i->sink)) pa_sink_set_volume(i->sink, NULL, FALSE, FALSE); if (i->sink->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); - - /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ - if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) - pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); } reset_callbacks(i); @@ -710,9 +712,8 @@ void pa_sink_input_put(pa_sink_input *i) { set_real_ratio(i, &i->volume); } - /* If we're entering passthrough mode, disable the monitor */ - if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) - pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + if (pa_sink_input_is_passthrough(i)) + pa_sink_enter_passthrough(i->sink); i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; @@ -1407,6 +1408,9 @@ int pa_sink_input_start_move(pa_sink_input *i) { if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) pa_assert_se(i->sink->n_corked-- >= 1); + if (pa_sink_input_is_passthrough(i)) + pa_sink_leave_passthrough(i->sink); + if (pa_sink_flat_volume_enabled(i->sink)) /* We might need to update the sink's volume if we are in flat * volume mode. */ @@ -1414,10 +1418,6 @@ int pa_sink_input_start_move(pa_sink_input *i) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); - /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ - if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) - pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); - pa_sink_update_status(i->sink); pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map); i->sink = NULL; @@ -1666,11 +1666,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { update_volume_due_to_moving(i, dest); - pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); + if (pa_sink_input_is_passthrough(i)) + pa_sink_enter_passthrough(i->sink); - /* If we're entering passthrough mode, disable the monitor */ - if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) - pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index e6e410c4d..3aaa5b7dc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1394,6 +1394,35 @@ pa_bool_t pa_sink_is_passthrough(pa_sink *s) { return FALSE; } +/* Called from main context */ +void pa_sink_enter_passthrough(pa_sink *s) { + pa_cvolume volume; + + /* disable the monitor in passthrough mode */ + if (s->monitor_source) + pa_source_suspend(s->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + + /* set the volume to NORM */ + s->saved_volume = *pa_sink_get_volume(s, TRUE); + s->saved_save_volume = s->save_volume; + + pa_cvolume_set(&volume, s->sample_spec.channels, PA_VOLUME_NORM); + pa_sink_set_volume(s, &volume, TRUE, FALSE); +} + +/* Called from main context */ +void pa_sink_leave_passthrough(pa_sink *s) { + /* Unsuspend monitor */ + if (s->monitor_source) + pa_source_suspend(s->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); + + /* Restore sink volume to what it was before we entered passthrough mode */ + pa_sink_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume); + + pa_cvolume_init(&s->saved_volume); + s->saved_save_volume = FALSE; +} + /* Called from main context. */ static void compute_reference_ratio(pa_sink_input *i) { unsigned c = 0; @@ -1784,9 +1813,9 @@ void pa_sink_set_volume( pa_assert(volume || pa_sink_flat_volume_enabled(s)); pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec)); - /* make sure we don't change the volume when a PASSTHROUGH input is connected */ - if (pa_sink_is_passthrough(s)) { - /* FIXME: Need to notify client that volume control is disabled */ + /* make sure we don't change the volume when a PASSTHROUGH input is connected ... + * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */ + if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) { pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input"); return; } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 3f8978441..e5eaab3a7 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -110,6 +110,10 @@ struct pa_sink { pa_bool_t save_volume:1; pa_bool_t save_muted:1; + /* Saved volume state while we're in passthrough mode */ + pa_cvolume saved_volume; + pa_bool_t saved_save_volume:1; + pa_asyncmsgq *asyncmsgq; pa_memchunk silence; @@ -418,6 +422,9 @@ pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s); /* Is the sink in passthrough mode? (that is, is there a passthrough sink input * connected to this sink? */ pa_bool_t pa_sink_is_passthrough(pa_sink *s); +/* These should be called when a sink enters/leaves passthrough mode */ +void pa_sink_enter_passthrough(pa_sink *s); +void pa_sink_leave_passthrough(pa_sink *s); void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);