mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-10-29 05:40:23 -04:00 
			
		
		
		
	move flat volume logic into the core. while doing so add n_volume_steps field to sinks/sources
This commit is contained in:
		
							parent
							
								
									4bfa5d7d13
								
							
						
					
					
						commit
						d5f46e824e
					
				
					 32 changed files with 562 additions and 361 deletions
				
			
		|  | @ -140,7 +140,6 @@ USA. | |||
|       precedence.</p> | ||||
|     </option> | ||||
| 
 | ||||
| 
 | ||||
|     <option> | ||||
|       <p><opt>system-instance=</opt> Run the daemon as system-wide | ||||
|       instance, requires root priviliges. Takes a boolean argument, | ||||
|  | @ -148,7 +147,6 @@ USA. | |||
|       argument takes precedence.</p> | ||||
|     </option> | ||||
| 
 | ||||
| 
 | ||||
|     <option> | ||||
|       <p><opt>disable-shm=</opt> Disable data transfer via POSIX | ||||
|       shared memory. Takes a boolean argument, defaults to | ||||
|  | @ -165,6 +163,13 @@ USA. | |||
|       memory overcommit.</p> | ||||
|     </option> | ||||
| 
 | ||||
|     <option> | ||||
|       <p><opt>flat-volumes=</opt> Enable 'flat' volumes, i.e. where | ||||
|       possible let the sink volume equal the maximum of the volumes of | ||||
|       the inputs connected to it. Takes a boolean argument, defaults | ||||
|       to <opt>yes</opt>.</p> | ||||
|     </option> | ||||
| 
 | ||||
|   </section> | ||||
| 
 | ||||
|   <section name="Scheduling"> | ||||
|  |  | |||
|  | @ -64,6 +64,7 @@ static const pa_daemon_conf default_conf = { | |||
|     .realtime_priority = 5,  /* Half of JACK's default rtprio */ | ||||
|     .disallow_module_loading = FALSE, | ||||
|     .disallow_exit = FALSE, | ||||
|     .flat_volumes = TRUE, | ||||
|     .exit_idle_time = 20, | ||||
|     .scache_idle_time = 20, | ||||
|     .auto_log_target = 1, | ||||
|  | @ -418,6 +419,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { | |||
|         { "system-instance",            pa_config_parse_bool,     NULL }, | ||||
|         { "no-cpu-limit",               pa_config_parse_bool,     NULL }, | ||||
|         { "disable-shm",                pa_config_parse_bool,     NULL }, | ||||
|         { "flat-volumes",               pa_config_parse_bool,     NULL }, | ||||
|         { "exit-idle-time",             pa_config_parse_int,      NULL }, | ||||
|         { "scache-idle-time",           pa_config_parse_int,      NULL }, | ||||
|         { "realtime-priority",          parse_rtprio,             NULL }, | ||||
|  | @ -490,6 +492,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { | |||
|     table[i++].data = &c->system_instance; | ||||
|     table[i++].data = &c->no_cpu_limit; | ||||
|     table[i++].data = &c->disable_shm; | ||||
|     table[i++].data = &c->flat_volumes; | ||||
|     table[i++].data = &c->exit_idle_time; | ||||
|     table[i++].data = &c->scache_idle_time; | ||||
|     table[i++].data = c; | ||||
|  | @ -650,6 +653,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { | |||
|     pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance)); | ||||
|     pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit)); | ||||
|     pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm)); | ||||
|     pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes)); | ||||
|     pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); | ||||
|     pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); | ||||
|     pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path)); | ||||
|  |  | |||
|  | @ -70,7 +70,8 @@ typedef struct pa_daemon_conf { | |||
|         load_default_script_file, | ||||
|         disallow_exit, | ||||
|         log_meta, | ||||
|         log_time; | ||||
|         log_time, | ||||
|         flat_volumes; | ||||
|     int exit_idle_time, | ||||
|         scache_idle_time, | ||||
|         auto_log_target, | ||||
|  |  | |||
|  | @ -53,6 +53,8 @@ | |||
| ; disable-remixing = no | ||||
| ; disable-lfe-remixing = yes | ||||
| 
 | ||||
| ; flat-volumes = yes | ||||
| 
 | ||||
| ; no-cpu-limit = no | ||||
| 
 | ||||
| ; rlimit-fsize = -1 | ||||
|  |  | |||
|  | @ -917,6 +917,7 @@ int main(int argc, char *argv[]) { | |||
|     c->disable_lfe_remixing = !!conf->disable_lfe_remixing; | ||||
|     c->running_as_daemon = !!conf->daemonize; | ||||
|     c->disallow_exit = conf->disallow_exit; | ||||
|     c->flat_volumes = conf->flat_volumes; | ||||
| 
 | ||||
|     pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); | ||||
|     pa_signal_new(SIGINT, signal_callback, c); | ||||
|  |  | |||
|  | @ -757,7 +757,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { | |||
|     return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); | ||||
| } | ||||
| 
 | ||||
| static int sink_get_volume_cb(pa_sink *s) { | ||||
| static void sink_get_volume_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
|     unsigned i; | ||||
|  | @ -820,27 +820,24 @@ static int sink_get_volume_cb(pa_sink *s) { | |||
| 
 | ||||
|     if (!pa_cvolume_equal(&u->hardware_volume, &r)) { | ||||
| 
 | ||||
|         u->hardware_volume = s->volume = r; | ||||
|         s->virtual_volume = u->hardware_volume = r; | ||||
| 
 | ||||
|         if (u->hw_dB_supported) { | ||||
|             pa_cvolume reset; | ||||
| 
 | ||||
|             /* Hmm, so the hardware volume changed, let's reset our software volume */ | ||||
| 
 | ||||
|             pa_cvolume_reset(&reset, s->sample_spec.channels); | ||||
|             pa_sink_set_soft_volume(s, &reset); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
|     return; | ||||
| 
 | ||||
| fail: | ||||
|     pa_log_error("Unable to read volume: %s", snd_strerror(err)); | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int sink_set_volume_cb(pa_sink *s) { | ||||
| static void sink_set_volume_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
|     unsigned i; | ||||
|  | @ -857,7 +854,7 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
|             long alsa_vol; | ||||
|             pa_volume_t vol; | ||||
| 
 | ||||
|             vol = s->volume.values[i]; | ||||
|             vol = s->virtual_volume.values[i]; | ||||
| 
 | ||||
|             if (u->hw_dB_supported) { | ||||
| 
 | ||||
|  | @ -894,7 +891,7 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
|         pa_volume_t vol; | ||||
|         long alsa_vol; | ||||
| 
 | ||||
|         vol = pa_cvolume_max(&s->volume); | ||||
|         vol = pa_cvolume_max(&s->virtual_volume); | ||||
| 
 | ||||
|         if (u->hw_dB_supported) { | ||||
|             alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); | ||||
|  | @ -911,7 +908,7 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
|             VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); | ||||
| #endif | ||||
| 
 | ||||
|             pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); | ||||
|             pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); | ||||
| 
 | ||||
|         } else { | ||||
|             alsa_vol = to_alsa_volume(u, vol); | ||||
|  | @ -932,11 +929,9 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
|         char t[PA_CVOLUME_SNPRINT_MAX]; | ||||
| 
 | ||||
|         /* Match exactly what the user requested by software */ | ||||
|         pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r); | ||||
| 
 | ||||
|         pa_sw_cvolume_divide(&r, &s->volume, &r); | ||||
|         pa_sink_set_soft_volume(s, &r); | ||||
| 
 | ||||
|         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); | ||||
|         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); | ||||
|         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); | ||||
|         pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); | ||||
| 
 | ||||
|  | @ -945,17 +940,15 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
|         /* We can't match exactly what the user requested, hence let's
 | ||||
|          * at least tell the user about it */ | ||||
| 
 | ||||
|         s->volume = r; | ||||
|         s->virtual_volume = r; | ||||
| 
 | ||||
|     return 0; | ||||
|     return; | ||||
| 
 | ||||
| fail: | ||||
|     pa_log_error("Unable to set volume: %s", snd_strerror(err)); | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int sink_get_mute_cb(pa_sink *s) { | ||||
| static void sink_get_mute_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err, sw; | ||||
| 
 | ||||
|  | @ -964,15 +957,13 @@ static int sink_get_mute_cb(pa_sink *s) { | |||
| 
 | ||||
|     if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) { | ||||
|         pa_log_error("Unable to get switch: %s", snd_strerror(err)); | ||||
|         return -1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     s->muted = !sw; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int sink_set_mute_cb(pa_sink *s) { | ||||
| static void sink_set_mute_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
| 
 | ||||
|  | @ -981,10 +972,8 @@ static int sink_set_mute_cb(pa_sink *s) { | |||
| 
 | ||||
|     if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) { | ||||
|         pa_log_error("Unable to set switch: %s", snd_strerror(err)); | ||||
|         return -1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void sink_update_requested_latency_cb(pa_sink *s) { | ||||
|  | @ -1552,6 +1541,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca | |||
|                 u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); | ||||
|                 pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); | ||||
| 
 | ||||
|                 if (!u->hw_dB_supported) | ||||
|                     u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1; | ||||
|             } else | ||||
|                 pa_log_info("Using software volume control."); | ||||
|         } | ||||
|  |  | |||
|  | @ -703,7 +703,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) { | |||
|     return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); | ||||
| } | ||||
| 
 | ||||
| static int source_get_volume_cb(pa_source *s) { | ||||
| static void source_get_volume_cb(pa_source *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
|     unsigned i; | ||||
|  | @ -766,27 +766,24 @@ static int source_get_volume_cb(pa_source *s) { | |||
| 
 | ||||
|     if (!pa_cvolume_equal(&u->hardware_volume, &r)) { | ||||
| 
 | ||||
|         u->hardware_volume = s->volume = r; | ||||
|         s->virtual_volume = u->hardware_volume = r; | ||||
| 
 | ||||
|         if (u->hw_dB_supported) { | ||||
|             pa_cvolume reset; | ||||
| 
 | ||||
|             /* Hmm, so the hardware volume changed, let's reset our software volume */ | ||||
| 
 | ||||
|             pa_cvolume_reset(&reset, s->sample_spec.channels); | ||||
|             pa_source_set_soft_volume(s, &reset); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
|     return; | ||||
| 
 | ||||
| fail: | ||||
|     pa_log_error("Unable to read volume: %s", snd_strerror(err)); | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int source_set_volume_cb(pa_source *s) { | ||||
| static void source_set_volume_cb(pa_source *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
|     unsigned i; | ||||
|  | @ -803,7 +800,7 @@ static int source_set_volume_cb(pa_source *s) { | |||
|             long alsa_vol; | ||||
|             pa_volume_t vol; | ||||
| 
 | ||||
|             vol = s->volume.values[i]; | ||||
|             vol = s->virtual_volume.values[i]; | ||||
| 
 | ||||
|             if (u->hw_dB_supported) { | ||||
| 
 | ||||
|  | @ -840,7 +837,7 @@ static int source_set_volume_cb(pa_source *s) { | |||
|         pa_volume_t vol; | ||||
|         long alsa_vol; | ||||
| 
 | ||||
|         vol = pa_cvolume_max(&s->volume); | ||||
|         vol = pa_cvolume_max(&s->virtual_volume); | ||||
| 
 | ||||
|         if (u->hw_dB_supported) { | ||||
|             alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); | ||||
|  | @ -857,7 +854,7 @@ static int source_set_volume_cb(pa_source *s) { | |||
|             VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); | ||||
| #endif | ||||
| 
 | ||||
|             pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); | ||||
|             pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0)); | ||||
| 
 | ||||
|         } else { | ||||
|             alsa_vol = to_alsa_volume(u, vol); | ||||
|  | @ -879,10 +876,9 @@ static int source_set_volume_cb(pa_source *s) { | |||
| 
 | ||||
|         /* Match exactly what the user requested by software */ | ||||
| 
 | ||||
|         pa_sw_cvolume_divide(&r, &s->volume, &r); | ||||
|         pa_source_set_soft_volume(s, &r); | ||||
|         pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r); | ||||
| 
 | ||||
|         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); | ||||
|         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); | ||||
|         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); | ||||
|         pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); | ||||
| 
 | ||||
|  | @ -891,17 +887,15 @@ static int source_set_volume_cb(pa_source *s) { | |||
|         /* We can't match exactly what the user requested, hence let's
 | ||||
|          * at least tell the user about it */ | ||||
| 
 | ||||
|         s->volume = r; | ||||
|         s->virtual_volume = r; | ||||
| 
 | ||||
|     return 0; | ||||
|     return; | ||||
| 
 | ||||
| fail: | ||||
|     pa_log_error("Unable to set volume: %s", snd_strerror(err)); | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int source_get_mute_cb(pa_source *s) { | ||||
| static void source_get_mute_cb(pa_source *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err, sw; | ||||
| 
 | ||||
|  | @ -910,15 +904,13 @@ static int source_get_mute_cb(pa_source *s) { | |||
| 
 | ||||
|     if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) { | ||||
|         pa_log_error("Unable to get switch: %s", snd_strerror(err)); | ||||
|         return -1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     s->muted = !sw; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int source_set_mute_cb(pa_source *s) { | ||||
| static void source_set_mute_cb(pa_source *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int err; | ||||
| 
 | ||||
|  | @ -927,10 +919,8 @@ static int source_set_mute_cb(pa_source *s) { | |||
| 
 | ||||
|     if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) { | ||||
|         pa_log_error("Unable to set switch: %s", snd_strerror(err)); | ||||
|         return -1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void source_update_requested_latency_cb(pa_source *s) { | ||||
|  | @ -1372,6 +1362,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p | |||
|                 u->source->set_volume = source_set_volume_cb; | ||||
|                 u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); | ||||
|                 pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); | ||||
| 
 | ||||
|                 if (!u->hw_dB_supported) | ||||
|                     u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1; | ||||
|             } else | ||||
|                 pa_log_info("Using software volume control."); | ||||
|         } | ||||
|  |  | |||
|  | @ -135,7 +135,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event | |||
|                                     cv.values[i] = PA_VOLUME_NORM; | ||||
|                             } | ||||
| 
 | ||||
|                             pa_sink_set_volume(s, &cv); | ||||
|                             pa_sink_set_volume(s, &cv, TRUE, TRUE); | ||||
|                             break; | ||||
| 
 | ||||
|                         case DOWN: | ||||
|  | @ -146,7 +146,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event | |||
|                                     cv.values[i] = PA_VOLUME_MUTED; | ||||
|                             } | ||||
| 
 | ||||
|                             pa_sink_set_volume(s, &cv); | ||||
|                             pa_sink_set_volume(s, &cv, TRUE, TRUE); | ||||
|                             break; | ||||
| 
 | ||||
|                         case MUTE: | ||||
|  |  | |||
|  | @ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event | |||
|                                     cv.values[i] = PA_VOLUME_NORM; | ||||
|                             } | ||||
| 
 | ||||
|                             pa_sink_set_volume(s, &cv); | ||||
|                             pa_sink_set_volume(s, &cv, TRUE, TRUE); | ||||
|                             break; | ||||
| 
 | ||||
|                         case DOWN: | ||||
|  | @ -137,7 +137,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event | |||
|                                     cv.values[i] = PA_VOLUME_MUTED; | ||||
|                             } | ||||
| 
 | ||||
|                             pa_sink_set_volume(s, &cv); | ||||
|                             pa_sink_set_volume(s, &cv, TRUE, TRUE); | ||||
|                             break; | ||||
| 
 | ||||
|                         case MUTE_TOGGLE: | ||||
|  |  | |||
|  | @ -101,23 +101,23 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i | |||
| 
 | ||||
|     pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); | ||||
| 
 | ||||
|     if (!data->volume_is_set) { | ||||
|         pa_cvolume_reset(&data->volume, data->sample_spec.channels); | ||||
|         data->volume_is_set = TRUE; | ||||
|     if (!data->virtual_volume_is_set) { | ||||
|         pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); | ||||
|         data->virtual_volume_is_set = TRUE; | ||||
|     } | ||||
| 
 | ||||
|     for (c = 0; c < data->sample_spec.channels; c++) { | ||||
| 
 | ||||
|         if (is_left(data->channel_map.map[c])) | ||||
|             data->volume.values[c] = | ||||
|                 pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f))); | ||||
|             data->virtual_volume.values[c] = | ||||
|                 pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f))); | ||||
| 
 | ||||
|         if (is_right(data->channel_map.map[c])) | ||||
|             data->volume.values[c] = | ||||
|                 pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f)); | ||||
|             data->virtual_volume.values[c] = | ||||
|                 pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f)); | ||||
|     } | ||||
| 
 | ||||
|     pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume)); | ||||
|     pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume)); | ||||
| 
 | ||||
|     return PA_HOOK_OK; | ||||
| } | ||||
|  |  | |||
|  | @ -255,20 +255,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse | |||
|     return pa_sink_process_msg(o, code, data, offset, chunk); | ||||
| } | ||||
| 
 | ||||
| static int sink_get_volume_cb(pa_sink *s) { | ||||
| static void sink_get_volume_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int i; | ||||
| 
 | ||||
|     pa_assert(u); | ||||
| 
 | ||||
|     for (i = 0; i < s->sample_spec.channels; i++) { | ||||
|         s->volume.values[i] = u->volume; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
|     for (i = 0; i < s->sample_spec.channels; i++) | ||||
|         s->virtual_volume.values[i] = u->volume; | ||||
| } | ||||
| 
 | ||||
| static int sink_set_volume_cb(pa_sink *s) { | ||||
| static void sink_set_volume_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int rv; | ||||
| 
 | ||||
|  | @ -276,39 +273,34 @@ static int sink_set_volume_cb(pa_sink *s) { | |||
| 
 | ||||
|     /* If we're muted, we fake it */ | ||||
|     if (u->muted) | ||||
|         return 0; | ||||
|         return; | ||||
| 
 | ||||
|     pa_assert(s->sample_spec.channels > 0); | ||||
| 
 | ||||
|     /* Avoid pointless volume sets */ | ||||
|     if (u->volume == s->volume.values[0]) | ||||
|         return 0; | ||||
|     if (u->volume == s->virtual_volume.values[0]) | ||||
|         return; | ||||
| 
 | ||||
|     rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]); | ||||
|     rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]); | ||||
|     if (0 == rv) | ||||
|         u->volume = s->volume.values[0]; | ||||
| 
 | ||||
|     return rv; | ||||
|         u->volume = s->virtual_volume.values[0]; | ||||
| } | ||||
| 
 | ||||
| static int sink_get_mute_cb(pa_sink *s) { | ||||
| static void sink_get_mute_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
| 
 | ||||
|     pa_assert(u); | ||||
| 
 | ||||
|     s->muted = u->muted; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int sink_set_mute_cb(pa_sink *s) { | ||||
| static void sink_set_mute_cb(pa_sink *s) { | ||||
|     struct userdata *u = s->userdata; | ||||
|     int rv; | ||||
| 
 | ||||
|     pa_assert(u); | ||||
| 
 | ||||
|     rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); | ||||
|     pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); | ||||
|     u->muted = s->muted; | ||||
|     return rv; | ||||
| } | ||||
| 
 | ||||
| static void thread_func(void *userdata) { | ||||
|  |  | |||
|  | @ -334,9 +334,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu | |||
| 
 | ||||
|         if (u->restore_volume) { | ||||
| 
 | ||||
|             if (!new_data->volume_is_set) { | ||||
|             if (!new_data->virtual_volume_is_set) { | ||||
|                 pa_log_info("Restoring volume for sink input %s.", name); | ||||
|                 pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); | ||||
|                 pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); | ||||
|             } else | ||||
|                 pa_log_debug("Not restoring volume for sink input %s, because already set.", name); | ||||
|         } | ||||
|  |  | |||
|  | @ -1056,10 +1056,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag | |||
|     pa_assert(u->sink); | ||||
| 
 | ||||
|     if ((u->version < 11 || !!mute == !!u->sink->muted) && | ||||
|         pa_cvolume_equal(&volume, &u->sink->volume)) | ||||
|         pa_cvolume_equal(&volume, &u->sink->virtual_volume)) | ||||
|         return; | ||||
| 
 | ||||
|     memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume)); | ||||
|     memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume)); | ||||
| 
 | ||||
|     if (u->version >= 11) | ||||
|         u->sink->muted = !!mute; | ||||
|  | @ -1621,7 +1621,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata | |||
| #ifdef TUNNEL_SINK | ||||
| 
 | ||||
| /* Called from main context */ | ||||
| static int sink_set_volume(pa_sink *sink) { | ||||
| static void sink_set_volume(pa_sink *sink) { | ||||
|     struct userdata *u; | ||||
|     pa_tagstruct *t; | ||||
|     uint32_t tag; | ||||
|  | @ -1634,14 +1634,12 @@ static int sink_set_volume(pa_sink *sink) { | |||
|     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); | ||||
|     pa_tagstruct_putu32(t, tag = u->ctag++); | ||||
|     pa_tagstruct_putu32(t, u->device_index); | ||||
|     pa_tagstruct_put_cvolume(t, &sink->volume); | ||||
|     pa_tagstruct_put_cvolume(t, &sink->virtual_volume); | ||||
|     pa_pstream_send_tagstruct(u->pstream, t); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* Called from main context */ | ||||
| static int sink_set_mute(pa_sink *sink) { | ||||
| static void sink_set_mute(pa_sink *sink) { | ||||
|     struct userdata *u; | ||||
|     pa_tagstruct *t; | ||||
|     uint32_t tag; | ||||
|  | @ -1651,7 +1649,7 @@ static int sink_set_mute(pa_sink *sink) { | |||
|     pa_assert(u); | ||||
| 
 | ||||
|     if (u->version < 11) | ||||
|         return -1; | ||||
|         return; | ||||
| 
 | ||||
|     t = pa_tagstruct_new(NULL, 0); | ||||
|     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE); | ||||
|  | @ -1659,8 +1657,6 @@ static int sink_set_mute(pa_sink *sink) { | |||
|     pa_tagstruct_putu32(t, u->device_index); | ||||
|     pa_tagstruct_put_boolean(t, !!sink->muted); | ||||
|     pa_pstream_send_tagstruct(u->pstream, t); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -443,7 +443,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu | |||
| 
 | ||||
|         if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) { | ||||
|             pa_log_info("Restoring volume for <%s>", r->name); | ||||
|             pa_sink_input_new_data_set_volume(data, &r->volume); | ||||
|             pa_sink_input_new_data_set_virtual_volume(data, &r->volume); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -443,7 +443,6 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) { | |||
|     return r; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static pa_usec_t io_source_get_latency(struct userdata *u) { | ||||
|     pa_usec_t r = 0; | ||||
| 
 | ||||
|  | @ -527,9 +526,6 @@ static int suspend(struct userdata *u) { | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int sink_get_volume(pa_sink *s); | ||||
| static int source_get_volume(pa_source *s); | ||||
| 
 | ||||
| static int unsuspend(struct userdata *u) { | ||||
|     int m; | ||||
|     pa_sample_spec ss, *ss_original; | ||||
|  | @ -620,10 +616,10 @@ static int unsuspend(struct userdata *u) { | |||
| 
 | ||||
|     build_pollfd(u); | ||||
| 
 | ||||
|     if (u->sink && u->sink->get_volume) | ||||
|         u->sink->get_volume(u->sink); | ||||
|     if (u->source && u->source->get_volume) | ||||
|         u->source->get_volume(u->source); | ||||
|     if (u->sink) | ||||
|         pa_sink_get_volume(u->sink, TRUE); | ||||
|     if (u->source) | ||||
|         pa_source_get_volume(u->source, TRUE); | ||||
| 
 | ||||
|     pa_log_info("Resumed successfully..."); | ||||
| 
 | ||||
|  | @ -798,84 +794,76 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int sink_get_volume(pa_sink *s) { | ||||
| static void sink_get_volume(pa_sink *s) { | ||||
|     struct userdata *u; | ||||
|     int r; | ||||
| 
 | ||||
|     pa_assert_se(u = s->userdata); | ||||
| 
 | ||||
|     pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_VOLUME) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_PCM) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int sink_set_volume(pa_sink *s) { | ||||
| static void sink_set_volume(pa_sink *s) { | ||||
|     struct userdata *u; | ||||
|     int r; | ||||
| 
 | ||||
|     pa_assert_se(u = s->userdata); | ||||
| 
 | ||||
|     pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM)); | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_VOLUME) | ||||
|         if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_PCM) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int source_get_volume(pa_source *s) { | ||||
| static void source_get_volume(pa_source *s) { | ||||
|     struct userdata *u; | ||||
|     int r; | ||||
| 
 | ||||
|     pa_assert_se(u = s->userdata); | ||||
| 
 | ||||
|     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_IGAIN) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_RECLEV) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static int source_set_volume(pa_source *s) { | ||||
| static void source_set_volume(pa_source *s) { | ||||
|     struct userdata *u; | ||||
|     int r; | ||||
| 
 | ||||
|     pa_assert_se(u = s->userdata); | ||||
| 
 | ||||
|     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_IGAIN) | ||||
|         if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     if (u->mixer_devmask & SOUND_MASK_RECLEV) | ||||
|         if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0) | ||||
|             return r; | ||||
|         if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) | ||||
|             return; | ||||
| 
 | ||||
|     pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static void thread_func(void *userdata) { | ||||
|  | @ -1417,6 +1405,7 @@ int pa__init(pa_module*m) { | |||
|                 u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; | ||||
|                 u->sink->get_volume = sink_get_volume; | ||||
|                 u->sink->set_volume = sink_set_volume; | ||||
|                 u->sink->n_volume_steps = 101; | ||||
|                 do_close = FALSE; | ||||
|             } | ||||
| 
 | ||||
|  | @ -1425,6 +1414,7 @@ int pa__init(pa_module*m) { | |||
|                 u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; | ||||
|                 u->source->get_volume = source_get_volume; | ||||
|                 u->source->set_volume = source_set_volume; | ||||
|                 u->source->n_volume_steps = 101; | ||||
|                 do_close = FALSE; | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -600,9 +600,13 @@ typedef enum pa_sink_flags { | |||
|     PA_SINK_HW_MUTE_CTRL = 0x0010U, | ||||
|     /**< Supports hardware mute control \since 0.9.11 */ | ||||
| 
 | ||||
|     PA_SINK_DECIBEL_VOLUME = 0x0020U | ||||
|     PA_SINK_DECIBEL_VOLUME = 0x0020U, | ||||
|     /**< Volume can be translated to dB with pa_sw_volume_to_dB()
 | ||||
|      * \since 0.9.11 */ | ||||
| 
 | ||||
|     PA_SINK_FLAT_VOLUME = 0x0040U | ||||
|     /**< This sink is in flat volume mode, i.e. always the maximum of
 | ||||
|      * the volume of all connected inputs. \since 0.9.15 */ | ||||
| } pa_sink_flags_t; | ||||
| 
 | ||||
| /** \cond fulldocs */ | ||||
|  | @ -612,6 +616,7 @@ typedef enum pa_sink_flags { | |||
| #define PA_SINK_NETWORK PA_SINK_NETWORK | ||||
| #define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL | ||||
| #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME | ||||
| #define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME | ||||
| /** \endcond */ | ||||
| 
 | ||||
| /** Sink state. \since 0.9.15 */ | ||||
|  |  | |||
|  | @ -159,6 +159,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u | |||
|             memset(&i, 0, sizeof(i)); | ||||
|             i.proplist = pa_proplist_new(); | ||||
|             i.base_volume = PA_VOLUME_NORM; | ||||
|             i.n_volume_steps = PA_VOLUME_NORM+1; | ||||
|             mute = FALSE; | ||||
|             state = PA_SINK_INVALID_STATE; | ||||
| 
 | ||||
|  | @ -180,7 +181,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u | |||
|                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) || | ||||
|                 (o->context->version >= 15 && | ||||
|                  (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || | ||||
|                   pa_tagstruct_getu32(t, &state) < 0))) { | ||||
|                   pa_tagstruct_getu32(t, &state) < 0 || | ||||
|                   pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) { | ||||
| 
 | ||||
|                 pa_context_fail(o->context, PA_ERR_PROTOCOL); | ||||
|                 pa_proplist_free(i.proplist); | ||||
|  | @ -288,6 +290,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, | |||
|             memset(&i, 0, sizeof(i)); | ||||
|             i.proplist = pa_proplist_new(); | ||||
|             i.base_volume = PA_VOLUME_NORM; | ||||
|             i.n_volume_steps = PA_VOLUME_NORM+1; | ||||
|             mute = FALSE; | ||||
|             state = PA_SOURCE_INVALID_STATE; | ||||
| 
 | ||||
|  | @ -309,7 +312,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, | |||
|                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) || | ||||
|                 (o->context->version >= 15 && | ||||
|                  (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || | ||||
|                   pa_tagstruct_getu32(t, &state) < 0))) { | ||||
|                   pa_tagstruct_getu32(t, &state) < 0 || | ||||
|                   pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) { | ||||
| 
 | ||||
|                 pa_context_fail(o->context, PA_ERR_PROTOCOL); | ||||
|                 pa_proplist_free(i.proplist); | ||||
|  |  | |||
|  | @ -214,6 +214,7 @@ typedef struct pa_sink_info { | |||
|     pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */ | ||||
|     pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */ | ||||
|     pa_sink_state_t state;             /**< State \since 0.9.15 */ | ||||
|     uint32_t n_volume_steps;           /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */ | ||||
| } pa_sink_info; | ||||
| 
 | ||||
| /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ | ||||
|  | @ -271,6 +272,7 @@ typedef struct pa_source_info { | |||
|     pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */ | ||||
|     pa_volume_t base_volume;            /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */ | ||||
|     pa_source_state_t state;            /**< State \since 0.9.15 */ | ||||
|     uint32_t n_volume_steps;            /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */ | ||||
| } pa_source_info; | ||||
| 
 | ||||
| /** Callback prototype for pa_context_get_source_info_by_name() and friends */ | ||||
|  |  | |||
|  | @ -518,7 +518,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu | |||
|     } | ||||
| 
 | ||||
|     pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); | ||||
|     pa_sink_set_volume(sink, &cvolume); | ||||
|     pa_sink_set_volume(sink, &cvolume, TRUE, TRUE); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -232,11 +232,12 @@ char *pa_sink_list_to_string(pa_core *c) { | |||
|             "  %c index: %u\n" | ||||
|             "\tname: <%s>\n" | ||||
|             "\tdriver: <%s>\n" | ||||
|             "\tflags: %s%s%s%s%s%s\n" | ||||
|             "\tflags: %s%s%s%s%s%s%s\n" | ||||
|             "\tstate: %s\n" | ||||
|             "\tvolume: %s%s%s\n" | ||||
|             "\t        balance %0.2f\n" | ||||
|             "\tbase volume: %s%s%s\n" | ||||
|             "\tvolume steps: %u\n" | ||||
|             "\tmuted: %s\n" | ||||
|             "\tcurrent latency: %0.2f ms\n" | ||||
|             "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" | ||||
|  | @ -257,6 +258,7 @@ char *pa_sink_list_to_string(pa_core *c) { | |||
|             sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", | ||||
|             sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", | ||||
|             sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", | ||||
|             sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "", | ||||
|             sink_state_to_string(pa_sink_get_state(sink)), | ||||
|             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)), | ||||
|             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "", | ||||
|  | @ -265,6 +267,7 @@ char *pa_sink_list_to_string(pa_core *c) { | |||
|             pa_volume_snprint(v, sizeof(v), sink->base_volume), | ||||
|             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "", | ||||
|             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "", | ||||
|             sink->n_volume_steps, | ||||
|             pa_yes_no(pa_sink_get_mute(sink, FALSE)), | ||||
|             (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC, | ||||
|             (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC, | ||||
|  | @ -327,6 +330,7 @@ char *pa_source_list_to_string(pa_core *c) { | |||
|             "\tvolume: %s%s%s\n" | ||||
|             "\t        balance %0.2f\n" | ||||
|             "\tbase volume: %s%s%s\n" | ||||
|             "\tvolume steps: %u\n" | ||||
|             "\tmuted: %s\n" | ||||
|             "\tcurrent latency: %0.2f ms\n" | ||||
|             "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" | ||||
|  | @ -353,6 +357,7 @@ char *pa_source_list_to_string(pa_core *c) { | |||
|             pa_volume_snprint(v, sizeof(v), source->base_volume), | ||||
|             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "", | ||||
|             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "", | ||||
|             source->n_volume_steps, | ||||
|             pa_yes_no(pa_source_get_mute(source, FALSE)), | ||||
|             (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC, | ||||
|             (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, | ||||
|  |  | |||
|  | @ -127,6 +127,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { | |||
| 
 | ||||
|     c->exit_idle_time = -1; | ||||
|     c->scache_idle_time = 20; | ||||
|     c->flat_volumes = TRUE; | ||||
| 
 | ||||
|     c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3; | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,7 +55,6 @@ typedef enum pa_core_hook { | |||
|     PA_CORE_HOOK_SINK_UNLINK_POST, | ||||
|     PA_CORE_HOOK_SINK_STATE_CHANGED, | ||||
|     PA_CORE_HOOK_SINK_PROPLIST_CHANGED, | ||||
|     PA_CORE_HOOK_SINK_SET_VOLUME, | ||||
|     PA_CORE_HOOK_SOURCE_NEW, | ||||
|     PA_CORE_HOOK_SOURCE_FIXATE, | ||||
|     PA_CORE_HOOK_SOURCE_PUT, | ||||
|  | @ -129,6 +128,7 @@ struct pa_core { | |||
|     pa_silence_cache silence_cache; | ||||
| 
 | ||||
|     int exit_idle_time, scache_idle_time; | ||||
|     pa_bool_t flat_volumes; | ||||
| 
 | ||||
|     pa_time_event *exit_event; | ||||
| 
 | ||||
|  |  | |||
|  | @ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new( | |||
|     data.driver = __FILE__; | ||||
|     pa_sink_input_new_data_set_sample_spec(&data, ss); | ||||
|     pa_sink_input_new_data_set_channel_map(&data, map); | ||||
|     pa_sink_input_new_data_set_volume(&data, volume); | ||||
|     pa_sink_input_new_data_set_virtual_volume(&data, volume); | ||||
|     pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); | ||||
| 
 | ||||
|     u->sink_input = pa_sink_input_new(sink->core, &data, 0); | ||||
|  |  | |||
|  | @ -1012,7 +1012,7 @@ static playback_stream* playback_stream_new( | |||
|     pa_sink_input_new_data_set_sample_spec(&data, ss); | ||||
|     pa_sink_input_new_data_set_channel_map(&data, map); | ||||
|     if (volume) | ||||
|         pa_sink_input_new_data_set_volume(&data, volume); | ||||
|         pa_sink_input_new_data_set_virtual_volume(&data, volume); | ||||
|     if (muted_set) | ||||
|         pa_sink_input_new_data_set_muted(&data, muted); | ||||
|     data.sync_base = ssync ? ssync->sink_input : NULL; | ||||
|  | @ -2691,6 +2691,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin | |||
|         if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE)) | ||||
|             pa_log_error("Internal sink state is invalid."); | ||||
|         pa_tagstruct_putu32(t, pa_sink_get_state(sink)); | ||||
|         pa_tagstruct_putu32(t, sink->n_volume_steps); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -2729,6 +2730,7 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s | |||
|         if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE)) | ||||
|             pa_log_error("Internal source state is invalid."); | ||||
|         pa_tagstruct_putu32(t, pa_source_get_state(source)); | ||||
|         pa_tagstruct_putu32(t, source->n_volume_steps); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -3164,7 +3166,7 @@ static void command_set_volume( | |||
|     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY); | ||||
| 
 | ||||
|     if (sink) | ||||
|         pa_sink_set_volume(sink, &volume); | ||||
|         pa_sink_set_volume(sink, &volume, TRUE, TRUE); | ||||
|     else if (source) | ||||
|         pa_source_set_volume(source, &volume); | ||||
|     else if (si) | ||||
|  |  | |||
|  | @ -72,11 +72,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const | |||
|         data->channel_map = *map; | ||||
| } | ||||
| 
 | ||||
| void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { | ||||
| void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { | ||||
|     pa_assert(data); | ||||
| 
 | ||||
|     if ((data->volume_is_set = !!volume)) | ||||
|         data->volume = data->virtual_volume = *volume; | ||||
|     if ((data->soft_volume_is_set = !!volume)) | ||||
|         data->soft_volume = *volume; | ||||
| } | ||||
| 
 | ||||
| void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { | ||||
|     pa_assert(data); | ||||
| 
 | ||||
|     if ((data->virtual_volume_is_set = !!volume)) | ||||
|         data->virtual_volume = *volume; | ||||
| } | ||||
| 
 | ||||
| void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { | ||||
|  | @ -153,17 +160,34 @@ pa_sink_input* pa_sink_input_new( | |||
|     pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); | ||||
|     pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec)); | ||||
| 
 | ||||
|     if (!data->volume_is_set) { | ||||
|         pa_cvolume_reset(&data->volume, data->sample_spec.channels); | ||||
|         pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); | ||||
|     } | ||||
|     if (!data->virtual_volume_is_set) { | ||||
| 
 | ||||
|     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->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|             data->virtual_volume = data->sink->virtual_volume; | ||||
|             pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map); | ||||
|         } else | ||||
|             pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); | ||||
| 
 | ||||
|     } else if (!data->virtual_volume_is_absolute) { | ||||
| 
 | ||||
|         /* When the 'absolute' bool is set then we'll treat the volume
 | ||||
|          * as relative to the sink volume even in flat volume mode */ | ||||
|         if (data->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|             pa_cvolume t = data->sink->virtual_volume; | ||||
|             pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); | ||||
|             pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume)); | ||||
|     pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec)); | ||||
| 
 | ||||
|     if (!data->soft_volume_is_set) | ||||
|         data->soft_volume = data->virtual_volume; | ||||
| 
 | ||||
|     pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume)); | ||||
|     pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec)); | ||||
| 
 | ||||
|     if (!data->muted_is_set) | ||||
|         data->muted = FALSE; | ||||
| 
 | ||||
|  | @ -184,7 +208,8 @@ pa_sink_input* pa_sink_input_new( | |||
|     pa_assert(pa_channel_map_valid(&data->channel_map)); | ||||
| 
 | ||||
|     /* Due to the fixing of the sample spec the volume might not match anymore */ | ||||
|     pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); | ||||
|     pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map); | ||||
|     pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map); | ||||
| 
 | ||||
|     if (data->resample_method == PA_RESAMPLER_INVALID) | ||||
|         data->resample_method = core->resample_method; | ||||
|  | @ -239,7 +264,7 @@ pa_sink_input* pa_sink_input_new( | |||
|     i->channel_map = data->channel_map; | ||||
| 
 | ||||
|     i->virtual_volume = data->virtual_volume; | ||||
|     i->volume = data->volume; | ||||
|     i->soft_volume = data->soft_volume; | ||||
| 
 | ||||
|     i->muted = data->muted; | ||||
| 
 | ||||
|  | @ -263,7 +288,7 @@ pa_sink_input* pa_sink_input_new( | |||
|     pa_atomic_store(&i->thread_info.drained, 1); | ||||
|     i->thread_info.sample_spec = i->sample_spec; | ||||
|     i->thread_info.resampler = resampler; | ||||
|     i->thread_info.volume = i->volume; | ||||
|     i->thread_info.soft_volume = i->soft_volume; | ||||
|     i->thread_info.muted = i->muted; | ||||
|     i->thread_info.requested_sink_latency = (pa_usec_t) -1; | ||||
|     i->thread_info.rewrite_nbytes = 0; | ||||
|  | @ -395,9 +420,17 @@ void pa_sink_input_unlink(pa_sink_input *i) { | |||
|     update_n_corked(i, PA_SINK_INPUT_UNLINKED); | ||||
|     i->state = PA_SINK_INPUT_UNLINKED; | ||||
| 
 | ||||
|     if (linked && i->sink) | ||||
|     if (linked && i->sink) { | ||||
|         /* We might need to update the sink's volume if we are in flat volume mode. */ | ||||
|         if (i->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|             pa_cvolume new_volume; | ||||
|             pa_sink_update_flat_volume(i->sink, &new_volume); | ||||
|             pa_sink_set_volume(i->sink, &new_volume, 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); | ||||
|     } | ||||
| 
 | ||||
|     reset_callbacks(i); | ||||
| 
 | ||||
|  | @ -459,7 +492,7 @@ void pa_sink_input_put(pa_sink_input *i) { | |||
|     pa_assert(i->process_rewind); | ||||
|     pa_assert(i->kill); | ||||
| 
 | ||||
|     i->thread_info.volume = i->volume; | ||||
|     i->thread_info.soft_volume = i->soft_volume; | ||||
|     i->thread_info.muted = i->muted; | ||||
| 
 | ||||
|     state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; | ||||
|  | @ -467,6 +500,13 @@ void pa_sink_input_put(pa_sink_input *i) { | |||
|     update_n_corked(i, state); | ||||
|     i->state = state; | ||||
| 
 | ||||
|     /* We might need to update the sink's volume if we are in flat volume mode. */ | ||||
|     if (i->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|         pa_cvolume new_volume; | ||||
|         pa_sink_update_flat_volume(i->sink, &new_volume); | ||||
|         pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); | ||||
|     } | ||||
| 
 | ||||
|     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); | ||||
| 
 | ||||
|     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); | ||||
|  | @ -552,7 +592,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa | |||
|      * it after and leave it for the sink code */ | ||||
| 
 | ||||
|     do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); | ||||
|     volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted; | ||||
|     volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted; | ||||
| 
 | ||||
|     while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) { | ||||
|         pa_memchunk tchunk; | ||||
|  | @ -598,7 +638,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa | |||
|                 if (i->thread_info.muted) | ||||
|                     pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); | ||||
|                 else | ||||
|                     pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume); | ||||
|                     pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume); | ||||
|             } | ||||
| 
 | ||||
|             if (!i->thread_info.resampler) | ||||
|  | @ -644,7 +684,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa | |||
|         /* We've both the same channel map, so let's have the sink do the adjustment for us*/ | ||||
|         pa_cvolume_mute(volume, i->sink->sample_spec.channels); | ||||
|     else | ||||
|         *volume = i->thread_info.volume; | ||||
|         *volume = i->thread_info.soft_volume; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | @ -816,34 +856,41 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { | |||
| 
 | ||||
| /* Called from main context */ | ||||
| void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { | ||||
|     pa_sink_input_set_volume_data data; | ||||
| 
 | ||||
|     pa_sink_input_assert_ref(i); | ||||
|     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); | ||||
|     pa_assert(volume); | ||||
|     pa_assert(pa_cvolume_valid(volume)); | ||||
|     pa_assert(pa_cvolume_compatible(volume, &i->sample_spec)); | ||||
| 
 | ||||
|     data.sink_input = i; | ||||
|     data.virtual_volume = *volume; | ||||
|     data.volume = *volume; | ||||
| 
 | ||||
|     /* If you change something here, consider looking into
 | ||||
|      * module-flat-volume.c as well since it uses very similar | ||||
|      * code. */ | ||||
| 
 | ||||
|     if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0) | ||||
|     if (pa_cvolume_equal(volume, &i->virtual_volume)) | ||||
|         return; | ||||
| 
 | ||||
|     if (!pa_cvolume_equal(&i->volume, &data.volume)) { | ||||
|         i->volume = data.volume; | ||||
|         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0); | ||||
|     i->virtual_volume = *volume; | ||||
| 
 | ||||
|     if (i->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|         pa_cvolume new_volume; | ||||
| 
 | ||||
|         /* We are in flat volume mode, so let's update all sink input
 | ||||
|          * volumes and update the flat volume of the sink */ | ||||
| 
 | ||||
|         pa_sink_update_flat_volume(i->sink, &new_volume); | ||||
|         pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE); | ||||
| 
 | ||||
|     } else { | ||||
| 
 | ||||
|         /* OK, we are in normal volume mode. The volume only affects
 | ||||
|          * ourselves */ | ||||
| 
 | ||||
|         i->soft_volume = *volume; | ||||
| 
 | ||||
|         /* Hooks have the ability to play games with i->soft_volume */ | ||||
|         pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); | ||||
| 
 | ||||
|         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); | ||||
|     } | ||||
| 
 | ||||
|     if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) { | ||||
|         i->virtual_volume = data.virtual_volume; | ||||
|     /* The virtual volume changed, let's tell people so */ | ||||
|     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Called from main context */ | ||||
|  | @ -865,7 +912,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { | |||
| 
 | ||||
|     i->muted = mute; | ||||
| 
 | ||||
|     pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); | ||||
|     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); | ||||
|     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); | ||||
| } | ||||
| 
 | ||||
|  | @ -1013,15 +1060,22 @@ int pa_sink_input_start_move(pa_sink_input *i) { | |||
|     } | ||||
|     pa_assert(pa_idxset_isempty(i->direct_outputs)); | ||||
| 
 | ||||
|     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); | ||||
|     pa_idxset_remove_by_data(i->sink->inputs, i, NULL); | ||||
| 
 | ||||
|     if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) | ||||
|         pa_assert_se(i->sink->n_corked-- >= 1); | ||||
| 
 | ||||
|     pa_idxset_remove_by_data(i->sink->inputs, i, NULL); | ||||
|     i->sink = NULL; | ||||
|     /* We might need to update the sink's volume if we are in flat volume mode. */ | ||||
|     if (i->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|         pa_cvolume new_volume; | ||||
|         pa_sink_update_flat_volume(i->sink, &new_volume); | ||||
|         pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); | ||||
|     } | ||||
| 
 | ||||
|     pa_sink_update_status(origin); | ||||
|     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); | ||||
| 
 | ||||
|     pa_sink_update_status(i->sink); | ||||
|     i->sink = NULL; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | @ -1092,6 +1146,14 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) { | |||
|     } | ||||
| 
 | ||||
|     pa_sink_update_status(dest); | ||||
| 
 | ||||
|     /* We might need to update the sink's volume if we are in flat volume mode. */ | ||||
|     if (i->sink->flags & PA_SINK_FLAT_VOLUME) { | ||||
|         pa_cvolume new_volume; | ||||
|         pa_sink_update_flat_volume(i->sink, &new_volume); | ||||
|         pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|  | @ -1176,14 +1238,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t | |||
| 
 | ||||
|     switch (code) { | ||||
| 
 | ||||
|         case PA_SINK_INPUT_MESSAGE_SET_VOLUME: | ||||
|             i->thread_info.volume = *((pa_cvolume*) userdata); | ||||
|         case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME: | ||||
|             if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) { | ||||
|                 i->thread_info.soft_volume = i->soft_volume; | ||||
|                 pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); | ||||
|             } | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_INPUT_MESSAGE_SET_MUTE: | ||||
|             i->thread_info.muted = PA_PTR_TO_UINT(userdata); | ||||
|         case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: | ||||
|             if (i->thread_info.muted != i->muted) { | ||||
|                 i->thread_info.muted = i->muted; | ||||
|                 pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); | ||||
|             } | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { | ||||
|  |  | |||
|  | @ -90,9 +90,7 @@ struct pa_sink_input { | |||
| 
 | ||||
|     pa_sink_input *sync_prev, *sync_next; | ||||
| 
 | ||||
|     pa_cvolume virtual_volume; | ||||
| 
 | ||||
|     pa_cvolume volume; | ||||
|     pa_cvolume virtual_volume, soft_volume; | ||||
|     pa_bool_t muted; | ||||
| 
 | ||||
|     pa_resample_method_t requested_resample_method, actual_resample_method; | ||||
|  | @ -170,7 +168,15 @@ struct pa_sink_input { | |||
|         pa_sink_input_state_t state; | ||||
|         pa_atomic_t drained; | ||||
| 
 | ||||
|         pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ | ||||
|         pa_cvolume soft_volume; | ||||
|         pa_bool_t muted:1; | ||||
| 
 | ||||
|         pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */ | ||||
| 
 | ||||
|         /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */ | ||||
|         pa_bool_t rewrite_flush:1, dont_rewind_render:1; | ||||
|         size_t rewrite_nbytes; | ||||
|         uint64_t underrun_for, playing_for; | ||||
| 
 | ||||
|         pa_sample_spec sample_spec; | ||||
| 
 | ||||
|  | @ -179,16 +185,8 @@ struct pa_sink_input { | |||
|         /* We maintain a history of resampled audio data here. */ | ||||
|         pa_memblockq *render_memblockq; | ||||
| 
 | ||||
|         /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */ | ||||
|         size_t rewrite_nbytes; | ||||
|         pa_bool_t rewrite_flush, dont_rewind_render; | ||||
|         uint64_t underrun_for, playing_for; | ||||
| 
 | ||||
|         pa_sink_input *sync_prev, *sync_next; | ||||
| 
 | ||||
|         pa_cvolume volume; | ||||
|         pa_bool_t muted; | ||||
| 
 | ||||
|         /* The requested latency for the sink */ | ||||
|         pa_usec_t requested_sink_latency; | ||||
| 
 | ||||
|  | @ -202,8 +200,8 @@ PA_DECLARE_CLASS(pa_sink_input); | |||
| #define PA_SINK_INPUT(o) pa_sink_input_cast(o) | ||||
| 
 | ||||
| enum { | ||||
|     PA_SINK_INPUT_MESSAGE_SET_VOLUME, | ||||
|     PA_SINK_INPUT_MESSAGE_SET_MUTE, | ||||
|     PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, | ||||
|     PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, | ||||
|     PA_SINK_INPUT_MESSAGE_GET_LATENCY, | ||||
|     PA_SINK_INPUT_MESSAGE_SET_RATE, | ||||
|     PA_SINK_INPUT_MESSAGE_SET_STATE, | ||||
|  | @ -228,30 +226,26 @@ typedef struct pa_sink_input_new_data { | |||
|     pa_sample_spec sample_spec; | ||||
|     pa_channel_map channel_map; | ||||
| 
 | ||||
|     pa_cvolume virtual_volume; | ||||
| 
 | ||||
|     pa_cvolume volume; | ||||
|     pa_cvolume virtual_volume, soft_volume; | ||||
|     pa_bool_t muted:1; | ||||
| 
 | ||||
|     pa_bool_t sample_spec_is_set:1; | ||||
|     pa_bool_t channel_map_is_set:1; | ||||
|     pa_bool_t volume_is_set:1; | ||||
| 
 | ||||
|     pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1; | ||||
|     pa_bool_t muted_is_set:1; | ||||
| 
 | ||||
|     pa_bool_t virtual_volume_is_absolute:1; | ||||
| } pa_sink_input_new_data; | ||||
| 
 | ||||
| pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); | ||||
| void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); | ||||
| void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); | ||||
| void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); | ||||
| void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); | ||||
| void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); | ||||
| void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); | ||||
| void pa_sink_input_new_data_done(pa_sink_input_new_data *data); | ||||
| 
 | ||||
| typedef struct pa_sink_set_input_volume_data { | ||||
|   pa_sink_input *sink_input; | ||||
|   pa_cvolume virtual_volume; | ||||
|   pa_cvolume volume; | ||||
| } pa_sink_input_set_volume_data; | ||||
| 
 | ||||
| /* To be called by the implementing module only */ | ||||
| 
 | ||||
| pa_sink_input* pa_sink_input_new( | ||||
|  |  | |||
|  | @ -195,10 +195,10 @@ pa_sink* pa_sink_new( | |||
|     s->inputs = pa_idxset_new(NULL, NULL); | ||||
|     s->n_corked = 0; | ||||
| 
 | ||||
|     s->volume = data->volume; | ||||
|     s->virtual_volume = data->volume; | ||||
|     pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); | ||||
|     s->base_volume = PA_VOLUME_NORM; | ||||
|     s->virtual_volume = s->volume; | ||||
| 
 | ||||
|     s->n_volume_steps = PA_VOLUME_NORM+1; | ||||
|     s->muted = data->muted; | ||||
|     s->refresh_volume = s->refresh_muted = FALSE; | ||||
| 
 | ||||
|  | @ -216,8 +216,8 @@ pa_sink* pa_sink_new( | |||
|             0); | ||||
| 
 | ||||
|     s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); | ||||
|     pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); | ||||
|     s->thread_info.soft_muted = FALSE; | ||||
|     s->thread_info.soft_volume =  s->soft_volume; | ||||
|     s->thread_info.soft_muted = s->muted; | ||||
|     s->thread_info.state = s->state; | ||||
|     s->thread_info.rewind_nbytes = 0; | ||||
|     s->thread_info.rewind_requested = FALSE; | ||||
|  | @ -335,10 +335,17 @@ void pa_sink_put(pa_sink* s) { | |||
|     if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) { | ||||
|         s->flags |= PA_SINK_DECIBEL_VOLUME; | ||||
| 
 | ||||
|         s->thread_info.soft_volume = s->volume; | ||||
|         s->thread_info.soft_volume = s->soft_volume; | ||||
|         s->thread_info.soft_muted = s->muted; | ||||
|     } | ||||
| 
 | ||||
|     if (s->flags & PA_SINK_DECIBEL_VOLUME) | ||||
|         s->n_volume_steps = PA_VOLUME_NORM+1; | ||||
| 
 | ||||
|     if (s->core->flat_volumes) | ||||
|         if (s->flags & PA_SINK_DECIBEL_VOLUME) | ||||
|             s->flags |= PA_SINK_FLAT_VOLUME; | ||||
| 
 | ||||
|     pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); | ||||
| 
 | ||||
|     pa_source_put(s->monitor_source); | ||||
|  | @ -911,9 +918,100 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { | |||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { | ||||
|     pa_bool_t changed; | ||||
|     pa_sink_set_volume_data data; | ||||
| void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) { | ||||
|     pa_sink_input *i; | ||||
|     uint32_t idx; | ||||
| 
 | ||||
|     /* This is called whenever a sink input volume changes and we
 | ||||
|      * might need to fix up the sink volume accordingly. Please note | ||||
|      * that we don't actually update the sinks volume here, we only | ||||
|      * return how it needs to be updated. The caller should then call | ||||
|      * pa_sink_set_flat_volume().*/ | ||||
| 
 | ||||
|     pa_cvolume_mute(new_volume, s->channel_map.channels); | ||||
| 
 | ||||
|     /* First let's determine the new maximum volume of all inputs
 | ||||
|      * connected to this sink */ | ||||
|     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { | ||||
|         unsigned c; | ||||
|         pa_cvolume remapped_volume; | ||||
| 
 | ||||
|         remapped_volume = i->virtual_volume; | ||||
|         pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map); | ||||
| 
 | ||||
|         for (c = 0; c < new_volume->channels; c++) | ||||
|             if (remapped_volume.values[c] > new_volume->values[c]) | ||||
|                 new_volume->values[c] = remapped_volume.values[c]; | ||||
|     } | ||||
| 
 | ||||
|     /* Then, let's update the soft volumes of all inputs connected
 | ||||
|      * to this sink */ | ||||
|     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { | ||||
|         pa_cvolume remapped_new_volume; | ||||
| 
 | ||||
|         remapped_new_volume = *new_volume; | ||||
|         pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map); | ||||
|         pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume); | ||||
| 
 | ||||
|         /* Hooks have the ability to play games with i->soft_volume */ | ||||
|         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); | ||||
| 
 | ||||
|         /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
 | ||||
|          * we want the update to have atomically with the sink | ||||
|          * volume update, hence we do it within the | ||||
|          * pa_sink_set_flat_volume() call below*/ | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) { | ||||
|     pa_sink_input *i; | ||||
|     uint32_t idx; | ||||
| 
 | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(PA_SINK_IS_LINKED(s->state)); | ||||
|     pa_assert(old_volume); | ||||
|     pa_assert(s->flags & PA_SINK_FLAT_VOLUME); | ||||
| 
 | ||||
|     /* This is called whenever the sink volume changes that is not
 | ||||
|      * caused by a sink input volume change. We need to fix up the | ||||
|      * sink input volumes accordingly */ | ||||
| 
 | ||||
|     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { | ||||
|         pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume; | ||||
|         unsigned c; | ||||
| 
 | ||||
|         remapped_new_volume = s->virtual_volume; | ||||
|         pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map); | ||||
| 
 | ||||
|         remapped_old_volume = *old_volume; | ||||
|         pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map); | ||||
| 
 | ||||
|         for (c = 0; c < i->sample_spec.channels; c++) | ||||
| 
 | ||||
|             if (remapped_old_volume.values[c] == PA_VOLUME_MUTED) | ||||
|                 fixed_volume.values[c] = PA_VOLUME_MUTED; | ||||
|             else | ||||
|                 fixed_volume.values[c] = (pa_volume_t) | ||||
|                     ((uint64_t) i->virtual_volume.values[c] * | ||||
|                      (uint64_t) remapped_new_volume.values[c] / | ||||
|                      (uint64_t) remapped_old_volume.values[c]); | ||||
| 
 | ||||
|         fixed_volume.channels = i->virtual_volume.channels; | ||||
| 
 | ||||
|         if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) { | ||||
|             i->virtual_volume = fixed_volume; | ||||
| 
 | ||||
|             /* The virtual volume changed, let's tell people so */ | ||||
|             pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) { | ||||
|     pa_cvolume old_virtual_volume; | ||||
|     pa_bool_t virtual_volume_changed; | ||||
| 
 | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(PA_SINK_IS_LINKED(s->state)); | ||||
|  | @ -921,39 +1019,45 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { | |||
|     pa_assert(pa_cvolume_valid(volume)); | ||||
|     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); | ||||
| 
 | ||||
|     data.sink = s; | ||||
|     data.virtual_volume = data.volume = *volume; | ||||
|     old_virtual_volume = s->virtual_volume; | ||||
|     s->virtual_volume = *volume; | ||||
|     virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); | ||||
| 
 | ||||
|     changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume) || | ||||
|         !pa_cvolume_equal(&data.volume, &s->volume); | ||||
|     /* Propagate this volume change back to the inputs */ | ||||
|     if (virtual_volume_changed) | ||||
|         if (propagate && (s->flags & PA_SINK_FLAT_VOLUME)) | ||||
|             pa_sink_propagate_flat_volume(s, &old_virtual_volume); | ||||
| 
 | ||||
|     if (changed) { | ||||
|         if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0) | ||||
|             return; | ||||
|     if (s->set_volume) { | ||||
|         /* If we have a function set_volume(), then we do not apply a
 | ||||
|          * soft volume by default. However, set_volume() is apply one | ||||
|          * to s->soft_volume */ | ||||
| 
 | ||||
|         changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume); /* from client-side view */ | ||||
|     } | ||||
|         pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); | ||||
|         s->set_volume(s); | ||||
| 
 | ||||
|     s->volume = data.volume; | ||||
|     s->virtual_volume = data.virtual_volume; | ||||
|     } else | ||||
|         /* If we have no function set_volume(), then the soft volume
 | ||||
|          * becomes the virtual volume */ | ||||
|         s->soft_volume = s->virtual_volume; | ||||
| 
 | ||||
|     if (s->set_volume && s->set_volume(s) < 0) | ||||
|         s->set_volume = NULL; | ||||
|     /* This tells the sink that soft and/or virtual volume changed */ | ||||
|     if (sendmsg) | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|     if (!s->set_volume) | ||||
|         pa_sink_set_soft_volume(s, &s->volume); | ||||
| 
 | ||||
|     if (changed) | ||||
|     if (virtual_volume_changed) | ||||
|         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| /* Called from main thread. Only to be called by sink implementor */ | ||||
| void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(volume); | ||||
| 
 | ||||
|     s->soft_volume = *volume; | ||||
| 
 | ||||
|     if (PA_SINK_IS_LINKED(s->state)) | ||||
|         pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL); | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); | ||||
|     else | ||||
|         s->thread_info.soft_volume = *volume; | ||||
| } | ||||
|  | @ -961,43 +1065,43 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { | |||
| /* Called from main thread */ | ||||
| const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) { | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(PA_SINK_IS_LINKED(s->state)); | ||||
| 
 | ||||
|     if (s->refresh_volume || force_refresh) { | ||||
|         struct pa_cvolume old_volume = s->virtual_volume; | ||||
|         struct pa_cvolume old_virtual_volume = s->virtual_volume; | ||||
| 
 | ||||
|         if (s->get_volume && s->get_volume(s) < 0) | ||||
|             s->get_volume = NULL; | ||||
|         if (s->get_volume) | ||||
|             s->get_volume(s); | ||||
| 
 | ||||
|         if (!s->get_volume) { | ||||
|             pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); | ||||
|             s->virtual_volume = s->volume; | ||||
|         } | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|         if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) { | ||||
| 
 | ||||
|             if (s->flags & PA_SINK_FLAT_VOLUME) | ||||
|                 pa_sink_propagate_flat_volume(s, &old_virtual_volume); | ||||
| 
 | ||||
|         if (!pa_cvolume_equal(&old_volume, &s->virtual_volume)) | ||||
|             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return &s->virtual_volume; | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { | ||||
|     pa_bool_t changed; | ||||
|     pa_bool_t old_muted; | ||||
| 
 | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(PA_SINK_IS_LINKED(s->state)); | ||||
| 
 | ||||
|     changed = s->muted != mute; | ||||
|     old_muted = s->muted; | ||||
|     s->muted = mute; | ||||
| 
 | ||||
|     if (s->set_mute && s->set_mute(s) < 0) | ||||
|         s->set_mute = NULL; | ||||
|     if (s->set_mute) | ||||
|         s->set_mute(s); | ||||
| 
 | ||||
|     if (!s->set_mute) | ||||
|         pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); | ||||
|     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|     if (changed) | ||||
|     if (old_muted != s->muted) | ||||
|         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
| } | ||||
| 
 | ||||
|  | @ -1005,16 +1109,14 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { | |||
| pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { | ||||
| 
 | ||||
|     pa_sink_assert_ref(s); | ||||
|     pa_assert(PA_SINK_IS_LINKED(s->state)); | ||||
| 
 | ||||
|     if (s->refresh_muted || force_refresh) { | ||||
|         pa_bool_t old_muted = s->muted; | ||||
| 
 | ||||
|         if (s->get_mute && s->get_mute(s) < 0) | ||||
|             s->get_mute = NULL; | ||||
|         if (s->get_mute) | ||||
|             s->get_mute(s); | ||||
| 
 | ||||
|         if (!s->get_mute) | ||||
|             pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL); | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|         if (old_muted != s->muted) | ||||
|             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
|  | @ -1138,6 +1240,22 @@ unsigned pa_sink_check_suspend(pa_sink *s) { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /* Called from the IO thread */ | ||||
| static void sync_input_volumes_within_thread(pa_sink *s) { | ||||
|     pa_sink_input *i; | ||||
|     void *state = NULL; | ||||
| 
 | ||||
|     pa_sink_assert_ref(s); | ||||
| 
 | ||||
|     while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) { | ||||
|         if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) | ||||
|             continue; | ||||
| 
 | ||||
|         i->thread_info.soft_volume = i->soft_volume; | ||||
|         pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Called from IO thread, except when it is not */ | ||||
| int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { | ||||
|     pa_sink *s = PA_SINK(o); | ||||
|  | @ -1192,7 +1310,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse | |||
|              * slow start, i.e. need some time to buffer client | ||||
|              * samples before beginning streaming. */ | ||||
| 
 | ||||
|             return 0; | ||||
|             /* In flat volume mode we need to update the volume as
 | ||||
|              * well */ | ||||
|             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); | ||||
|         } | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_REMOVE_INPUT: { | ||||
|  | @ -1233,7 +1353,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse | |||
|             pa_sink_invalidate_requested_latency(s); | ||||
|             pa_sink_request_rewind(s, (size_t) -1); | ||||
| 
 | ||||
|             return 0; | ||||
|             /* In flat volume mode we need to update the volume as
 | ||||
|              * well */ | ||||
|             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); | ||||
|         } | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_START_MOVE: { | ||||
|  | @ -1279,7 +1401,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse | |||
|             pa_log_debug("Requesting rewind due to started move"); | ||||
|             pa_sink_request_rewind(s, (size_t) -1); | ||||
| 
 | ||||
|             return 0; | ||||
|             /* In flat volume mode we need to update the volume as
 | ||||
|              * well */ | ||||
|             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); | ||||
|         } | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_FINISH_MOVE: { | ||||
|  | @ -1323,27 +1447,36 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse | |||
|                 pa_sink_request_rewind(s, nbytes); | ||||
|             } | ||||
| 
 | ||||
|             return 0; | ||||
|             /* In flat volume mode we need to update the volume as
 | ||||
|              * well */ | ||||
|             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL); | ||||
|         } | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_SET_VOLUME: | ||||
|             s->thread_info.soft_volume = *((pa_cvolume*) userdata); | ||||
| 
 | ||||
|             if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) { | ||||
|                 s->thread_info.soft_volume = s->soft_volume; | ||||
|                 pa_sink_request_rewind(s, (size_t) -1); | ||||
|             return 0; | ||||
|             } | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_SET_MUTE: | ||||
|             s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); | ||||
|             if (s->flags & PA_SINK_FLAT_VOLUME) | ||||
|                 sync_input_volumes_within_thread(s); | ||||
| 
 | ||||
|             pa_sink_request_rewind(s, (size_t) -1); | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_GET_VOLUME: | ||||
|             *((pa_cvolume*) userdata) = s->thread_info.soft_volume; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_SET_MUTE: | ||||
| 
 | ||||
|             if (!s->thread_info.soft_muted != s->muted) { | ||||
|                 s->thread_info.soft_muted = s->muted; | ||||
|                 pa_sink_request_rewind(s, (size_t) -1); | ||||
|             } | ||||
| 
 | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_GET_MUTE: | ||||
|             *((pa_bool_t*) userdata) = s->thread_info.soft_muted; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SINK_MESSAGE_SET_STATE: | ||||
|  | @ -1676,6 +1809,7 @@ void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t m | |||
|     pa_source_update_latency_range(s->monitor_source, min_latency, max_latency); | ||||
| } | ||||
| 
 | ||||
| /* Called from main context */ | ||||
| size_t pa_sink_get_max_rewind(pa_sink *s) { | ||||
|     size_t r; | ||||
|     pa_sink_assert_ref(s); | ||||
|  | @ -1688,6 +1822,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) { | |||
|     return r; | ||||
| } | ||||
| 
 | ||||
| /* Called from main context */ | ||||
| size_t pa_sink_get_max_request(pa_sink *s) { | ||||
|     size_t r; | ||||
|     pa_sink_assert_ref(s); | ||||
|  |  | |||
|  | @ -71,11 +71,11 @@ struct pa_sink { | |||
|     unsigned n_corked; | ||||
|     pa_source *monitor_source; | ||||
| 
 | ||||
|     pa_cvolume volume; | ||||
|     pa_cvolume virtual_volume; | ||||
|     pa_bool_t muted; | ||||
| 
 | ||||
|     pa_volume_t base_volume; /* shall be constant */ | ||||
|     unsigned n_volume_steps; /* shall be constant */ | ||||
| 
 | ||||
|     pa_cvolume virtual_volume, soft_volume; | ||||
|     pa_bool_t muted:1; | ||||
| 
 | ||||
|     pa_bool_t refresh_volume:1; | ||||
|     pa_bool_t refresh_muted:1; | ||||
|  | @ -94,23 +94,23 @@ struct pa_sink { | |||
|      * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message | ||||
|      * will be sent to the IO thread instead. If refresh_volume is | ||||
|      * FALSE neither this function is called nor a message is sent. */ | ||||
|     int (*get_volume)(pa_sink *s);             /* may be NULL */ | ||||
|     void (*get_volume)(pa_sink *s);             /* may be NULL */ | ||||
| 
 | ||||
|     /* Called when the volume shall be changed. Called from main loop
 | ||||
|      * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message | ||||
|      * will be sent to the IO thread instead. */ | ||||
|     int (*set_volume)(pa_sink *s);             /* dito */ | ||||
|     void (*set_volume)(pa_sink *s);             /* dito */ | ||||
| 
 | ||||
|     /* Called when the mute setting is queried. Called from main loop
 | ||||
|      * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message | ||||
|      * will be sent to the IO thread instead. If refresh_mute is | ||||
|      * FALSE neither this function is called nor a message is sent.*/ | ||||
|     int (*get_mute)(pa_sink *s);               /* dito */ | ||||
|     void (*get_mute)(pa_sink *s);               /* dito */ | ||||
| 
 | ||||
|     /* Called when the mute setting shall be changed. Called from main
 | ||||
|      * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE | ||||
|      * message will be sent to the IO thread instead. */ | ||||
|     int (*set_mute)(pa_sink *s);               /* dito */ | ||||
|     void (*set_mute)(pa_sink *s);               /* dito */ | ||||
| 
 | ||||
|     /* Called when a rewind request is issued. Called from IO thread
 | ||||
|      * context. */ | ||||
|  | @ -125,6 +125,7 @@ struct pa_sink { | |||
|     struct { | ||||
|         pa_sink_state_t state; | ||||
|         pa_hashmap *inputs; | ||||
| 
 | ||||
|         pa_cvolume soft_volume; | ||||
|         pa_bool_t soft_muted:1; | ||||
| 
 | ||||
|  | @ -203,13 +204,7 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum | |||
| void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); | ||||
| void pa_sink_new_data_done(pa_sink_new_data *data); | ||||
| 
 | ||||
| typedef struct pa_sink_set_volume_data { | ||||
|   pa_sink *sink; | ||||
|   pa_cvolume volume; | ||||
|   pa_cvolume virtual_volume; | ||||
| } pa_sink_set_volume_data; | ||||
| 
 | ||||
| /* To be called exclusively by the sink driver, from main context */ | ||||
| /*** To be called exclusively by the sink driver, from main context */ | ||||
| 
 | ||||
| pa_sink* pa_sink_new( | ||||
|         pa_core *core, | ||||
|  | @ -228,7 +223,9 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_ | |||
| void pa_sink_detach(pa_sink *s); | ||||
| void pa_sink_attach(pa_sink *s); | ||||
| 
 | ||||
| /* May be called by everyone, from main context */ | ||||
| void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); | ||||
| 
 | ||||
| /**** May be called by everyone, from main context */ | ||||
| 
 | ||||
| /* The returned value is supposed to be in the time domain of the sound card! */ | ||||
| pa_usec_t pa_sink_get_latency(pa_sink *s); | ||||
|  | @ -242,11 +239,13 @@ int pa_sink_update_status(pa_sink*s); | |||
| int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); | ||||
| int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend); | ||||
| 
 | ||||
| void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume); | ||||
| void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume); | ||||
| void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); | ||||
| void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume); | ||||
| 
 | ||||
| void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg); | ||||
| const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh); | ||||
| void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); | ||||
| pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres); | ||||
| pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh); | ||||
| 
 | ||||
| pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); | ||||
| 
 | ||||
|  | @ -260,7 +259,7 @@ pa_queue *pa_sink_move_all_start(pa_sink *s); | |||
| void pa_sink_move_all_finish(pa_sink *s, pa_queue *q); | ||||
| void pa_sink_move_all_fail(pa_queue *q); | ||||
| 
 | ||||
| /* To be called exclusively by the sink driver, from IO context */ | ||||
| /*** To be called exclusively by the sink driver, from IO context */ | ||||
| 
 | ||||
| void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); | ||||
| void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); | ||||
|  | @ -281,7 +280,7 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request); | |||
| 
 | ||||
| void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency); | ||||
| 
 | ||||
| /* To be called exclusively by sink input drivers, from IO context */ | ||||
| /*** To be called exclusively by sink input drivers, from IO context */ | ||||
| 
 | ||||
| void pa_sink_request_rewind(pa_sink*s, size_t nbytes); | ||||
| 
 | ||||
|  |  | |||
|  | @ -322,7 +322,7 @@ int pa_play_file( | |||
|     data.sink = sink; | ||||
|     data.driver = __FILE__; | ||||
|     pa_sink_input_new_data_set_sample_spec(&data, &ss); | ||||
|     pa_sink_input_new_data_set_volume(&data, volume); | ||||
|     pa_sink_input_new_data_set_virtual_volume(&data, volume); | ||||
|     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); | ||||
|     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); | ||||
| 
 | ||||
|  |  | |||
|  | @ -685,15 +685,15 @@ int pa_source_output_start_move(pa_source_output *o) { | |||
| 
 | ||||
|     origin = o->source; | ||||
| 
 | ||||
|     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); | ||||
|     pa_idxset_remove_by_data(o->source->outputs, o, NULL); | ||||
| 
 | ||||
|     if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) | ||||
|         pa_assert_se(origin->n_corked-- >= 1); | ||||
| 
 | ||||
|     pa_idxset_remove_by_data(o->source->outputs, o, NULL); | ||||
|     o->source = NULL; | ||||
|     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); | ||||
| 
 | ||||
|     pa_source_update_status(origin); | ||||
|     pa_source_update_status(o->source); | ||||
|     o->source = NULL; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | @ -707,6 +707,9 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) { | |||
|     pa_assert(!o->source); | ||||
|     pa_source_assert_ref(dest); | ||||
| 
 | ||||
|     if (!pa_source_output_may_move_to(o, dest)) | ||||
|         return -1; | ||||
| 
 | ||||
|     o->source = dest; | ||||
|     pa_idxset_put(o->source->outputs, o, NULL); | ||||
| 
 | ||||
|  |  | |||
|  | @ -187,10 +187,12 @@ pa_source* pa_source_new( | |||
|     s->n_corked = 0; | ||||
|     s->monitor_of = NULL; | ||||
| 
 | ||||
|     s->volume = data->volume; | ||||
|     s->virtual_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; | ||||
|     s->base_volume = PA_VOLUME_NORM; | ||||
| 
 | ||||
|     reset_callbacks(s); | ||||
|     s->userdata = NULL; | ||||
|  | @ -206,8 +208,8 @@ pa_source* pa_source_new( | |||
|             0); | ||||
| 
 | ||||
|     s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); | ||||
|     pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels); | ||||
|     s->thread_info.soft_muted = FALSE; | ||||
|     s->thread_info.soft_volume = s->soft_volume; | ||||
|     s->thread_info.soft_muted = s->muted; | ||||
|     s->thread_info.state = s->state; | ||||
|     s->thread_info.max_rewind = 0; | ||||
|     s->thread_info.requested_latency_valid = FALSE; | ||||
|  | @ -293,10 +295,13 @@ void pa_source_put(pa_source *s) { | |||
|     if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) { | ||||
|         s->flags |= PA_SOURCE_DECIBEL_VOLUME; | ||||
| 
 | ||||
|         s->thread_info.soft_volume = s->volume; | ||||
|         s->thread_info.soft_volume = s->soft_volume; | ||||
|         s->thread_info.soft_muted = s->muted; | ||||
|     } | ||||
| 
 | ||||
|     if (s->flags & PA_SOURCE_DECIBEL_VOLUME) | ||||
|         s->n_volume_steps = PA_VOLUME_NORM+1; | ||||
| 
 | ||||
|     pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); | ||||
| 
 | ||||
|     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); | ||||
|  | @ -568,32 +573,38 @@ pa_usec_t pa_source_get_latency(pa_source *s) { | |||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { | ||||
|     pa_bool_t changed; | ||||
|     pa_cvolume old_virtual_volume; | ||||
|     pa_bool_t virtual_volume_changed; | ||||
| 
 | ||||
|     pa_source_assert_ref(s); | ||||
|     pa_assert(PA_SOURCE_IS_LINKED(s->state)); | ||||
|     pa_assert(volume); | ||||
|     pa_assert(pa_cvolume_valid(volume)); | ||||
|     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); | ||||
| 
 | ||||
|     changed = !pa_cvolume_equal(volume, &s->volume); | ||||
|     s->volume = *volume; | ||||
|     old_virtual_volume = s->virtual_volume; | ||||
|     s->virtual_volume = *volume; | ||||
|     virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); | ||||
| 
 | ||||
|     if (s->set_volume && s->set_volume(s) < 0) | ||||
|         s->set_volume = NULL; | ||||
|     if (s->set_volume) { | ||||
|         pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); | ||||
|         s->set_volume(s); | ||||
|     } else | ||||
|         s->soft_volume = s->virtual_volume; | ||||
| 
 | ||||
|     if (!s->set_volume) | ||||
|         pa_source_set_soft_volume(s, volume); | ||||
|     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|     if (changed) | ||||
|     if (virtual_volume_changed) | ||||
|         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| /* Called from main thread. Only to be called by source implementor */ | ||||
| void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) { | ||||
|     pa_source_assert_ref(s); | ||||
|     pa_assert(volume); | ||||
| 
 | ||||
|     if (PA_SOURCE_IS_LINKED(s->state)) | ||||
|         pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL); | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); | ||||
|     else | ||||
|         s->thread_info.soft_volume = *volume; | ||||
| } | ||||
|  | @ -604,38 +615,36 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) { | |||
|     pa_assert(PA_SOURCE_IS_LINKED(s->state)); | ||||
| 
 | ||||
|     if (s->refresh_volume || force_refresh) { | ||||
|         pa_cvolume old_volume = s->volume; | ||||
|         pa_cvolume old_virtual_volume = s->virtual_volume; | ||||
| 
 | ||||
|         if (s->get_volume && s->get_volume(s) < 0) | ||||
|             s->get_volume = NULL; | ||||
|         if (s->get_volume) | ||||
|             s->get_volume(s); | ||||
| 
 | ||||
|         if (!s->get_volume) | ||||
|             pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL); | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|         if (!pa_cvolume_equal(&old_volume, &s->volume)) | ||||
|         if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) | ||||
|             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
|     } | ||||
| 
 | ||||
|     return &s->volume; | ||||
|     return &s->virtual_volume; | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| void pa_source_set_mute(pa_source *s, pa_bool_t mute) { | ||||
|     pa_bool_t changed; | ||||
|     pa_bool_t old_muted; | ||||
| 
 | ||||
|     pa_source_assert_ref(s); | ||||
|     pa_assert(PA_SOURCE_IS_LINKED(s->state)); | ||||
| 
 | ||||
|     changed = s->muted != mute; | ||||
|     old_muted = s->muted; | ||||
|     s->muted = mute; | ||||
| 
 | ||||
|     if (s->set_mute && s->set_mute(s) < 0) | ||||
|         s->set_mute = NULL; | ||||
|     if (s->set_mute) | ||||
|         s->set_mute(s); | ||||
| 
 | ||||
|     if (!s->set_mute) | ||||
|         pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL); | ||||
|     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|     if (changed) | ||||
|     if (old_muted != s->muted) | ||||
|         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
| } | ||||
| 
 | ||||
|  | @ -648,11 +657,10 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { | |||
|     if (s->refresh_muted || force_refresh) { | ||||
|         pa_bool_t old_muted = s->muted; | ||||
| 
 | ||||
|         if (s->get_mute && s->get_mute(s) < 0) | ||||
|             s->get_mute = NULL; | ||||
|         if (s->get_mute) | ||||
|             s->get_mute(s); | ||||
| 
 | ||||
|         if (!s->get_mute) | ||||
|             pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL); | ||||
|         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); | ||||
| 
 | ||||
|         if (old_muted != s->muted) | ||||
|             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); | ||||
|  | @ -661,6 +669,7 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) { | |||
|     return s->muted; | ||||
| } | ||||
| 
 | ||||
| /* Called from main thread */ | ||||
| pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) { | ||||
| 
 | ||||
|     pa_source_assert_ref(s); | ||||
|  | @ -814,19 +823,17 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ | |||
|         } | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_SET_VOLUME: | ||||
|             s->thread_info.soft_volume = *((pa_cvolume*) userdata); | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_SET_MUTE: | ||||
|             s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); | ||||
|             s->thread_info.soft_volume = s->soft_volume; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_GET_VOLUME: | ||||
|             *((pa_cvolume*) userdata) = s->thread_info.soft_volume; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_SET_MUTE: | ||||
|             s->thread_info.soft_muted = s->muted; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_GET_MUTE: | ||||
|             *((pa_bool_t*) userdata) = s->thread_info.soft_muted; | ||||
|             return 0; | ||||
| 
 | ||||
|         case PA_SOURCE_MESSAGE_SET_STATE: | ||||
|  |  | |||
|  | @ -73,10 +73,11 @@ struct pa_source { | |||
|     unsigned n_corked; | ||||
|     pa_sink *monitor_of;                     /* may be NULL */ | ||||
| 
 | ||||
|     pa_cvolume volume; | ||||
|     pa_bool_t muted; | ||||
| 
 | ||||
|     pa_volume_t base_volume; /* shall be constant */ | ||||
|     unsigned n_volume_steps; /* shall be constant */ | ||||
| 
 | ||||
|     pa_cvolume virtual_volume, soft_volume; | ||||
|     pa_bool_t muted:1; | ||||
| 
 | ||||
|     pa_bool_t refresh_volume:1; | ||||
|     pa_bool_t refresh_muted:1; | ||||
|  | @ -95,23 +96,23 @@ struct pa_source { | |||
|      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message | ||||
|      * will be sent to the IO thread instead. If refresh_volume is | ||||
|      * FALSE neither this function is called nor a message is sent. */ | ||||
|     int (*get_volume)(pa_source *s);         /* dito */ | ||||
|     void (*get_volume)(pa_source *s);         /* dito */ | ||||
| 
 | ||||
|     /* Called when the volume shall be changed. Called from main loop
 | ||||
|      * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message | ||||
|      * will be sent to the IO thread instead. */ | ||||
|     int (*set_volume)(pa_source *s);         /* dito */ | ||||
|     void (*set_volume)(pa_source *s);         /* dito */ | ||||
| 
 | ||||
|     /* Called when the mute setting is queried. Called from main loop
 | ||||
|      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message | ||||
|      * will be sent to the IO thread instead. If refresh_mute is | ||||
|      * FALSE neither this function is called nor a message is sent.*/ | ||||
|     int (*get_mute)(pa_source *s);           /* dito */ | ||||
|     void (*get_mute)(pa_source *s);           /* dito */ | ||||
| 
 | ||||
|     /* Called when the mute setting shall be changed. Called from main
 | ||||
|      * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE | ||||
|      * message will be sent to the IO thread instead. */ | ||||
|     int (*set_mute)(pa_source *s);           /* dito */ | ||||
|     void (*set_mute)(pa_source *s);           /* dito */ | ||||
| 
 | ||||
|     /* Called when a the requested latency is changed. Called from IO
 | ||||
|      * thread context. */ | ||||
|  | @ -122,6 +123,7 @@ struct pa_source { | |||
|     struct { | ||||
|         pa_source_state_t state; | ||||
|         pa_hashmap *outputs; | ||||
| 
 | ||||
|         pa_cvolume soft_volume; | ||||
|         pa_bool_t soft_muted:1; | ||||
| 
 | ||||
|  | @ -189,7 +191,7 @@ void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *v | |||
| void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); | ||||
| void pa_source_new_data_done(pa_source_new_data *data); | ||||
| 
 | ||||
| /* To be called exclusively by the source driver, from main context */ | ||||
| /*** To be called exclusively by the source driver, from main context */ | ||||
| 
 | ||||
| pa_source* pa_source_new( | ||||
|         pa_core *core, | ||||
|  | @ -208,7 +210,9 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t | |||
| void pa_source_detach(pa_source *s); | ||||
| void pa_source_attach(pa_source *s); | ||||
| 
 | ||||
| /* May be called by everyone, from main context */ | ||||
| void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); | ||||
| 
 | ||||
| /*** May be called by everyone, from main context */ | ||||
| 
 | ||||
| /* The returned value is supposed to be in the time domain of the sound card! */ | ||||
| pa_usec_t pa_source_get_latency(pa_source *s); | ||||
|  | @ -222,7 +226,6 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend); | |||
| int pa_source_suspend_all(pa_core *c, pa_bool_t suspend); | ||||
| 
 | ||||
| void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); | ||||
| void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume); | ||||
| const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); | ||||
| void pa_source_set_mute(pa_source *source, pa_bool_t mute); | ||||
| pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); | ||||
|  | @ -239,7 +242,7 @@ pa_queue *pa_source_move_all_start(pa_source *s); | |||
| void pa_source_move_all_finish(pa_source *s, pa_queue *q); | ||||
| void pa_source_move_all_fail(pa_queue *q); | ||||
| 
 | ||||
| /* To be called exclusively by the source driver, from IO context */ | ||||
| /*** To be called exclusively by the source driver, from IO context */ | ||||
| 
 | ||||
| void pa_source_post(pa_source*s, const pa_memchunk *chunk); | ||||
| void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk); | ||||
|  | @ -255,7 +258,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s); | |||
| void pa_source_set_max_rewind(pa_source *s, size_t max_rewind); | ||||
| void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency); | ||||
| 
 | ||||
| /* To be called exclusively by source output drivers, from IO context */ | ||||
| /*** To be called exclusively by source output drivers, from IO context */ | ||||
| 
 | ||||
| void pa_source_invalidate_requested_latency(pa_source *s); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lennart Poettering
						Lennart Poettering