diff options
author | wm4 <wm4@mplayer2.org> | 2012-01-18 04:21:58 +0100 |
---|---|---|
committer | wm4 <wm4@mplayer2.org> | 2012-01-18 04:21:58 +0100 |
commit | 6e41497d5be1f107c18b2143fa45b3c46d6e95d3 (patch) | |
tree | ba18bcab5e209d3c48658bac046fa48a9c5f053a | |
parent | 064f8c2fb656462db9662c67bdbc6716958a4de4 (diff) | |
parent | f7c2ecebccc4b3c5d6299aee5b8f4d382fa78987 (diff) | |
download | mpv-6e41497d5be1f107c18b2143fa45b3c46d6e95d3.tar.bz2 mpv-6e41497d5be1f107c18b2143fa45b3c46d6e95d3.tar.xz |
Merge branch 'softvol' into my_master
-rw-r--r-- | command.c | 23 | ||||
-rw-r--r-- | libaf/af.c | 44 | ||||
-rw-r--r-- | libao2/ao_alsa.c | 68 | ||||
-rw-r--r-- | libao2/ao_coreaudio.c | 8 | ||||
-rw-r--r-- | libao2/ao_dsound.c | 2 | ||||
-rw-r--r-- | libao2/ao_esd.c | 2 | ||||
-rw-r--r-- | libao2/ao_nas.c | 2 | ||||
-rw-r--r-- | libao2/ao_openal.c | 1 | ||||
-rw-r--r-- | libao2/ao_pulse.c | 55 | ||||
-rw-r--r-- | libao2/ao_sdl.c | 2 | ||||
-rw-r--r-- | libao2/audio_out.h | 14 | ||||
-rw-r--r-- | mixer.c | 347 | ||||
-rw-r--r-- | mixer.h | 16 | ||||
-rw-r--r-- | mp_core.h | 3 | ||||
-rw-r--r-- | mplayer.c | 37 |
15 files changed, 413 insertions, 211 deletions
@@ -719,10 +719,6 @@ static int mp_property_volume(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; - mpctx->user_muted = 0; - switch (action) { case M_PROPERTY_SET: if (!arg) @@ -756,30 +752,17 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, switch (action) { case M_PROPERTY_SET: - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; if (!arg) return M_PROPERTY_ERROR; - if ((!!*(int *) arg) != mpctx->mixer.muted) - mixer_mute(&mpctx->mixer); - mpctx->user_muted = mpctx->mixer.muted; + mixer_setmuted(&mpctx->mixer, *(int *) arg); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; mixer_mute(&mpctx->mixer); - mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - if (!arg) - return M_PROPERTY_ERROR; - if (mpctx->edl_muted) { - *(char **) arg = talloc_strdup(NULL, mp_gtext("enabled (EDL)")); - return M_PROPERTY_OK; - } default: - return m_property_flag(prop, action, arg, &mpctx->mixer.muted); + return m_property_flag_ro(prop, action, arg, + mixer_getmuted(&mpctx->mixer)); } } diff --git a/libaf/af.c b/libaf/af.c index 80f9871bfb..e4015727ac 100644 --- a/libaf/af.c +++ b/libaf/af.c @@ -246,6 +246,43 @@ void af_remove(af_stream_t* s, af_instance_t* af) free(af); } +static void print_fmt(af_data_t *d) +{ + if (d) { + mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch, + af_fmt2str_short(d->format)); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "(?)"); + } +} + +static void af_print_filter_chain(af_stream_t* s) +{ + mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n"); + + mp_msg(MSGT_AFILTER, MSGL_V, " [in] "); + print_fmt(&s->input); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + af_instance_t *af = s->first; + while (af) { + mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name); + print_fmt(af->data); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + af = af->next; + } + + mp_msg(MSGT_AFILTER, MSGL_V, " [out] "); + print_fmt(&s->output); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); +} + +// Warning: +// A failed af_reinit() leaves the audio chain behind in a useless, broken +// state (for example, format filters that were tentatively inserted stay +// inserted). +// In that case, you should always rebuild the filter chain, or abort. int af_reinit(af_stream_t* s, af_instance_t* af) { do{ @@ -343,6 +380,9 @@ int af_reinit(af_stream_t* s, af_instance_t* af) return AF_ERROR; } }while(af); + + af_print_filter_chain(s); + return AF_OK; } @@ -552,7 +592,9 @@ af_instance_t* af_add(af_stream_t* s, char* name){ // Reinitalize the filter list if(AF_OK != af_reinit(s, s->first) || AF_OK != fixup_output_format(s)){ - free(new); + while (s->first) + af_remove(s, s->first); + af_init(s); return NULL; } return new; diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index 1806a36d56..61837066d5 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -106,6 +106,8 @@ static int control(int cmd, void *arg) switch(cmd) { case AOCONTROL_QUERY_FORMAT: return CONTROL_TRUE; + case AOCONTROL_GET_MUTE: + case AOCONTROL_SET_MUTE: case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { @@ -116,7 +118,7 @@ static int control(int cmd, void *arg) snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; - char *mix_name = "PCM"; + char *mix_name = "Master"; char *card = "default"; int mix_index = 0; @@ -192,16 +194,15 @@ static int control(int cmd, void *arg) snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); f_multi = (100 / (float)(pmax - pmin)); - if (cmd == AOCONTROL_SET_VOLUME) { - + switch (cmd) { + case AOCONTROL_SET_VOLUME: { set_vol = vol->left / f_multi + pmin + 0.5; //setting channels if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) { mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; + goto mixer_error; } mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol); @@ -210,33 +211,52 @@ static int control(int cmd, void *arg) if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) { mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; + goto mixer_error; } mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n", set_vol, pmin, pmax, f_multi); - - if (snd_mixer_selem_has_playback_switch(elem)) { - int lmute = (vol->left == 0.0); - int rmute = (vol->right == 0.0); - if (snd_mixer_selem_has_playback_switch_joined(elem)) { - lmute = rmute = lmute && rmute; - } else { - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !rmute); - } - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !lmute); + break; + } + case AOCONTROL_GET_VOLUME: { + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); + vol->left = (get_vol - pmin) * f_multi; + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); + vol->right = (get_vol - pmin) * f_multi; + mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); + break; + } + case AOCONTROL_SET_MUTE: { + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + bool m_l = vol->left == 0.0f, m_r = vol->right == 0.0f; + if (snd_mixer_selem_has_playback_switch_joined(elem)) { + m_l = m_l || m_r; + } else { + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !m_r); } + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !m_l); + break; + } + case AOCONTROL_GET_MUTE: { + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + int tmp = 1; + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &tmp); + vol->left = tmp ? 1.0f : 0.0f; + if (snd_mixer_selem_has_playback_switch_joined(elem)) { + vol->right = vol->left; + } else { + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + vol->right = tmp ? 1.0f : 0.0f; + } + break; } - else { - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); - vol->left = (get_vol - pmin) * f_multi; - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); - vol->right = (get_vol - pmin) * f_multi; - - mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); } snd_mixer_close(handle); return CONTROL_OK; + mixer_error: + snd_mixer_close(handle); + return CONTROL_ERROR; } } //end switch diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c index 34374f4c9c..d1a93c85e2 100644 --- a/libao2/ao_coreaudio.c +++ b/libao2/ao_coreaudio.c @@ -169,7 +169,11 @@ Float32 vol; control_vol = (ao_control_vol_t*)arg; if (ao->b_digital) { // Digital output has no volume adjust. - return CONTROL_FALSE; + int vol = ao->b_muted ? 0 : 100; + *control_vol = (ao_control_vol_t) { + .left = vol, .right = vol, + }; + return CONTROL_TRUE; } err = AudioUnitGetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &vol); @@ -450,6 +454,8 @@ int device_id, display_help = 0; ao->b_revert = 0; ao->b_changed_mixing = 0; + global_ao->no_persistent_volume = true; + if (device_id == 0) { /* Find the ID of the default Device. */ err = GetAudioProperty(kAudioObjectSystemObject, diff --git a/libao2/ao_dsound.c b/libao2/ao_dsound.c index 605b0f16e0..a7d30ec357 100644 --- a/libao2/ao_dsound.c +++ b/libao2/ao_dsound.c @@ -418,6 +418,8 @@ static int init(int rate, int channels, int format, int flags) int res; if (!InitDirectSound()) return 0; + global_ao->no_persistent_volume = true; + // ok, now create the buffers WAVEFORMATEXTENSIBLE wformat; DSBUFFERDESC dsbpridesc; diff --git a/libao2/ao_esd.c b/libao2/ao_esd.c index d5423991bd..e7c6701aa0 100644 --- a/libao2/ao_esd.c +++ b/libao2/ao_esd.c @@ -164,6 +164,8 @@ static int init(int rate_hz, int channels, int format, int flags) float lag_seconds, lag_net = 0., lag_serv; struct timeval proto_start, proto_end; + global_ao->no_persistent_volume = true; + if (esd_fd < 0) { esd_fd = esd_open_sound(server); if (esd_fd < 0) { diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c index fb49c5e60e..d3274df9a5 100644 --- a/libao2/ao_nas.c +++ b/libao2/ao_nas.c @@ -424,6 +424,8 @@ static int init(int rate,int channels,int format,int flags) (void)flags; /* shut up 'unused parameter' warning */ + global_ao->no_persistent_volume = true; + nas_data=malloc(sizeof(struct ao_nas_data)); memset(nas_data, 0, sizeof(struct ao_nas_data)); diff --git a/libao2/ao_openal.c b/libao2/ao_openal.c index e425b5769c..490aac0eb8 100644 --- a/libao2/ao_openal.c +++ b/libao2/ao_openal.c @@ -108,6 +108,7 @@ static int init(int rate, int channels, int format, int flags) { const opt_t subopts[] = { {NULL} }; + global_ao->no_persistent_volume = true; if (subopt_parse(ao_subdevice, subopts) != 0) { print_help(); return 0; diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index a07bf31da0..24a448b5f9 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -21,6 +21,7 @@ */ #include <string.h> +#include <stdlib.h> #include <pulse/pulseaudio.h> @@ -342,42 +343,49 @@ static float get_delay(void) { } /** A callback function that is called when the - * pa_context_get_sink_input_info() operation completes. Saves the - * volume field of the specified structure to the global variable volume. */ + * pa_context_get_sink_input_info() operation completes. */ static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { - struct pa_cvolume *volume = userdata; + struct pa_sink_input_info *pi = userdata; if (is_last < 0) { GENERIC_ERR_MSG(context, "Failed to get sink input info"); return; } if (!i) return; - *volume = i->volume; + *pi = *i; pa_threaded_mainloop_signal(mainloop, 0); } static int control(int cmd, void *arg) { switch (cmd) { + case AOCONTROL_GET_MUTE: // fallthrough case AOCONTROL_GET_VOLUME: { + struct pa_sink_input_info pi; ao_control_vol_t *vol = arg; uint32_t devidx = pa_stream_get_index(stream); - struct pa_cvolume volume; pa_threaded_mainloop_lock(mainloop); - if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &volume))) { + if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &pi))) { GENERIC_ERR_MSG(context, "pa_stream_get_sink_input_info() failed"); return CONTROL_ERROR; } - - if (volume.channels != 2) - vol->left = vol->right = pa_cvolume_avg(&volume)*100/PA_VOLUME_NORM; - else { - vol->left = volume.values[0]*100/PA_VOLUME_NORM; - vol->right = volume.values[1]*100/PA_VOLUME_NORM; + // Warning: some information in pi might be unaccessible, because + // we naively copied the struct, without updating pointers etc. + // Pointers might point to invalid data, accessors might fail. + if (cmd == AOCONTROL_GET_VOLUME) { + if (pi.volume.channels != 2) + vol->left = vol->right = pa_cvolume_avg(&pi.volume)*100/PA_VOLUME_NORM; + else { + vol->left = pi.volume.values[0]*100/PA_VOLUME_NORM; + vol->right = pi.volume.values[1]*100/PA_VOLUME_NORM; + } + } else if (cmd == AOCONTROL_GET_MUTE) { + vol->left = vol->right = pi.mute ? 0.0f : 1.0f; } return CONTROL_OK; } + case AOCONTROL_SET_MUTE: // fallthrough case AOCONTROL_SET_VOLUME: { const ao_control_vol_t *vol = arg; pa_operation *o; @@ -392,12 +400,23 @@ static int control(int cmd, void *arg) { } pa_threaded_mainloop_lock(mainloop); - o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL); - if (!o) { - pa_threaded_mainloop_unlock(mainloop); - GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed"); - return CONTROL_ERROR; - } + if (cmd == AOCONTROL_SET_VOLUME) { + o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(mainloop); + GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed"); + return CONTROL_ERROR; + } + } else if (cmd == AOCONTROL_SET_MUTE) { + int mute = vol->left == 0.0f || vol->right == 0.0f; + o = pa_context_set_sink_input_mute(context, pa_stream_get_index(stream), mute, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(mainloop); + GENERIC_ERR_MSG(context, "pa_context_set_sink_input_mute() failed"); + return CONTROL_ERROR; + } + } else + abort(); /* We don't wait for completion here */ pa_operation_unref(o); pa_threaded_mainloop_unlock(mainloop); diff --git a/libao2/ao_sdl.c b/libao2/ao_sdl.c index e9ae7298d5..6ff8b83cb3 100644 --- a/libao2/ao_sdl.c +++ b/libao2/ao_sdl.c @@ -135,6 +135,8 @@ static int init(int rate,int channels,int format,int flags){ /* SDL Audio Specifications */ SDL_AudioSpec aspec, obtained; + global_ao->no_persistent_volume = true; + /* Allocate ring-buffer memory */ buffer = av_fifo_alloc(BUFFSIZE); diff --git a/libao2/audio_out.h b/libao2/audio_out.h index e96e123700..c5ddb5bf60 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -78,6 +78,7 @@ struct ao { int buffer_playable_size; bool initialized; bool untimed; + bool no_persistent_volume; const struct ao_driver *driver; void *priv; }; @@ -96,10 +97,19 @@ void list_audio_out(void); #define AOCONTROL_SET_DEVICE 1 #define AOCONTROL_GET_DEVICE 2 #define AOCONTROL_QUERY_FORMAT 3 /* test for availabilty of a format */ +// Uses ao_control_vol_t* as arg. +// If the volume controls are joint, the average of both volumes should be used. #define AOCONTROL_GET_VOLUME 4 #define AOCONTROL_SET_VOLUME 5 -#define AOCONTROL_SET_PLUGIN_DRIVER 6 -#define AOCONTROL_SET_PLUGIN_LIST 7 +// Uses ao_control_vol_t* as arg. +// left==0.0f means muted, left==1.0f means not muted (right likewise) +// Other values between are invalid. +// If the mtue controls are joint, the output should be muted if either of the +// two channels are muted. +#define AOCONTROL_GET_MUTE 6 +#define AOCONTROL_SET_MUTE 7 +#define AOCONTROL_SET_PLUGIN_DRIVER 8 +#define AOCONTROL_SET_PLUGIN_LIST 9 #define AOPLAY_FINAL_CHUNK 1 @@ -30,150 +30,283 @@ #include "mixer.h" -char * mixer_device=NULL; -char * mixer_channel=NULL; +char *mixer_device = NULL; +char *mixer_channel = NULL; int soft_vol = 0; float soft_vol_max = 110.0; -void mixer_getvolume(mixer_t *mixer, float *l, float *r) +static void internal_setvolume(mixer_t *mixer, float l, float r); + + +// Called after the audio filter chain is built or rebuilt. +void mixer_reinit(mixer_t *mixer) { - ao_control_vol_t vol; - *l=0; *r=0; - if (mixer->ao) { - if(soft_vol || - CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol)) { - if (!mixer->afilter) + if (!mixer->ao) return; - else { - float db_vals[AF_NCH]; - if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, db_vals)) - db_vals[0] = db_vals[1] = 1.0; - else - af_from_dB (2, db_vals, db_vals, 20.0, -200.0, 60.0); - vol.left = (db_vals[0] / (soft_vol_max / 100.0)) * 100.0; - vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0; - } + // Some of this might be incorrect when the AO behavior changes (e.g. + // different AO due to file specific -ao options), but we assume this + // doesn't happen. We could attempt to handle this by trying to detect + // whether a system mixer or mute is supported, but it would probably add + // even more bugs to the code. + if (mixer->restore_volume) { + // restore previous volume (softvol, or no persistent AO volume) + internal_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); + } + if (mixer->muted && + (mixer->mute_emulation || mixer->ao->no_persistent_volume)) + { + // undo mixer_uninit(), or restore mute state + mixer_setmuted(mixer, true); + } + if (mixer->restore_balance) { + // balance control always uses af_pan, it always needs to be restored + mixer_setbalance(mixer, mixer->balance); } - *r=vol.right; - *l=vol.left; - } } -void mixer_setvolume(mixer_t *mixer, float l, float r) +// Called before the audio output is uninitialized. +// Note that this doesn't necessarily terminate the mixer_t instance, and it's +// possible that mixer_reinit() will be called later. +void mixer_uninit(mixer_t *mixer) { - ao_control_vol_t vol; - vol.right=r; vol.left=l; - if (mixer->ao) { - if(soft_vol || - CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) { - if (!mixer->afilter) + if (!mixer->ao) return; - else { - // af_volume uses values in dB - float db_vals[AF_NCH]; - int i; - db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); - db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); - for (i = 2; i < AF_NCH; i++) { - db_vals[i] = ((l + r) / 100.0) * (soft_vol_max / 100.0) / 2.0; + // The player is supposed to restore the volume, when mute was enabled, and + // the player terminates. No other attempts at restoring anything are done. + // One complication is that the mute state should survive audio + // reinitialization (e.g. when switching to a new file), so we have to be + // sure mixer_reinit() will restore the mute state. + // This is only needed when a global system mixer without mute control is + // used, i.e. we emulate mute by setting the volume to 0. + if (mixer->mute_emulation && mixer_getmuted(mixer)) { + // avoid playing the rest of the audio buffer at restored volume + ao_reset(mixer->ao); + mixer_setmuted(mixer, false); + mixer->muted = true; + } +} + +static void internal_getvolume(mixer_t *mixer, float *l, float *r) +{ + ao_control_vol_t vol; + *l = 0; + *r = 0; + if (mixer->ao) { + if (soft_vol || + CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol)) + { + if (!mixer->afilter) + return; + float db_vals[AF_NCH]; + if (!af_control_any_rev(mixer->afilter, + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, db_vals)) + { + db_vals[0] = db_vals[1] = 1.0; + } else { + af_from_dB(2, db_vals, db_vals, 20.0, -200.0, 60.0); + } + vol.left = (db_vals[0] / (soft_vol_max / 100.0)) * 100.0; + vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0; + } + *r = vol.right; + *l = vol.left; + } +} + +static float clip_vol(float v) +{ + return v > 100 ? 100 : (v < 0 ? 0 : v); +} + +static void internal_setvolume(mixer_t *mixer, float l, float r) +{ + l = clip_vol(l); + r = clip_vol(r); + ao_control_vol_t vol; + vol.right = r; + vol.left = l; + if (mixer->ao) { + bool use_softvol = soft_vol; + if (!use_softvol) { + if (CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) + { + use_softvol = true; + } else { + mixer->restore_volume = mixer->ao->no_persistent_volume; + } } - af_to_dB (AF_NCH, db_vals, db_vals, 20.0); - if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) { - mp_tmsg(MSGT_GLOBAL, MSGL_INFO, "[Mixer] No hardware mixing, inserting volume filter.\n"); - if (af_add(mixer->afilter, "volume")) { + if (use_softvol) { + if (!mixer->afilter) + return; + // af_volume uses values in dB + float db_vals[AF_NCH]; + int i; + db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); + db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); + for (i = 2; i < AF_NCH; i++) + db_vals[i] = ((l + r) / 100.0) * (soft_vol_max / 100.0) / 2.0; + af_to_dB(AF_NCH, db_vals, db_vals, 20.0); if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) { - mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No volume control available.\n"); - return; + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) + { + mp_tmsg(MSGT_GLOBAL, MSGL_INFO, + "[Mixer] No hardware mixing, inserting volume filter.\n"); + if (!(af_add(mixer->afilter, "volume") + && af_control_any_rev(mixer->afilter, + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals))) + { + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] No volume control available.\n"); + return; + } } - } - } - } + mixer->restore_volume = true; + } + if (mixer->restore_volume) { + mixer->restore_vol_l = l; + mixer->restore_vol_r = r; + } } - } - mixer->muted=0; +} + +void mixer_setvolume(mixer_t *mixer, float l, float r) +{ + internal_setvolume(mixer, l, r); + // Changing the volume clears mute; these are mplayer semantics. (If this + // is not desired, this would be removed, and code for restoring the softvol + // volume had to be added.) + mixer_setmuted(mixer, false); +} + +void mixer_getvolume(mixer_t *mixer, float *l, float *r) +{ + *l = 0; + *r = 0; + if (mixer->ao) { + float real_l, real_r; + internal_getvolume(mixer, &real_l, &real_r); + // consider the case when the system mixer volumes change independently + if (real_l != 0 || real_r != 0) + mixer->muted = false; + if (mixer->muted) { + *l = mixer->last_l; + *r = mixer->last_r; + } else { + *l = real_l; + *r = real_r; + } + } +} + +static void mixer_addvolume(mixer_t *mixer, float d) +{ + float mixer_l, mixer_r; + mixer_getvolume(mixer, &mixer_l, &mixer_r); + mixer_setvolume(mixer, mixer_l + d, mixer_r + d); } void mixer_incvolume(mixer_t *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l += mixer->volstep; - if ( mixer_l > 100 ) mixer_l = 100; - mixer_r += mixer->volstep; - if ( mixer_r > 100 ) mixer_r = 100; - mixer_setvolume(mixer, mixer_l, mixer_r); + mixer_addvolume(mixer, +mixer->volstep); } void mixer_decvolume(mixer_t *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l -= mixer->volstep; - if ( mixer_l < 0 ) mixer_l = 0; - mixer_r -= mixer->volstep; - if ( mixer_r < 0 ) mixer_r = 0; - mixer_setvolume(mixer, mixer_l, mixer_r); + mixer_addvolume(mixer, -mixer->volstep); } void mixer_getbothvolume(mixer_t *mixer, float *b) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - *b = ( mixer_l + mixer_r ) / 2; + float mixer_l, mixer_r; + mixer_getvolume(mixer, &mixer_l, &mixer_r); + *b = (mixer_l + mixer_r) / 2; } void mixer_mute(mixer_t *mixer) { - if (mixer->muted) mixer_setvolume(mixer, mixer->last_l, mixer->last_r); - else - { - mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r); - mixer_setvolume(mixer, 0, 0); - mixer->muted=1; - } + mixer_setmuted(mixer, !mixer_getmuted(mixer)); +} + +bool mixer_getmuted(mixer_t *mixer) +{ + ao_control_vol_t vol = {0}; + if (!soft_vol && + CONTROL_OK == ao_control(mixer->ao, AOCONTROL_GET_MUTE, &vol)) + { + mixer->muted = vol.left == 0.0f || vol.right == 0.0f; + } else { + float l, r; + mixer_getvolume(mixer, &l, &r); // updates mixer->muted + } + return mixer->muted; +} + +void mixer_setmuted(mixer_t *mixer, bool mute) +{ + bool muted = mixer_getmuted(mixer); + if (mute == muted) + return; + ao_control_vol_t vol; + vol.left = vol.right = mute ? 0.0f : 1.0f; + mixer->mute_emulation = soft_vol || + CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_MUTE, &vol); + if (mixer->mute_emulation) { + // mute is emulated by setting volume to 0 + if (!mute) { + internal_setvolume(mixer, mixer->last_l, mixer->last_r); + } else { + mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r); + internal_setvolume(mixer, 0, 0); + } + } + mixer->muted = mute; } void mixer_getbalance(mixer_t *mixer, float *val) { - *val = 0.f; - if(!mixer->afilter) - return; - af_control_any_rev(mixer->afilter, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, val); + *val = 0.f; + if (!mixer->afilter) + return; + af_control_any_rev(mixer->afilter, AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, + val); } void mixer_setbalance(mixer_t *mixer, float val) { - float level[AF_NCH]; - int i; - af_control_ext_t arg_ext = { .arg = level }; - af_instance_t* af_pan_balance; - - if(!mixer->afilter) - return; - if (af_control_any_rev(mixer->afilter, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) - return; - - if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { - mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); - return; - } - - af_init(mixer->afilter); - /* make all other channels pass thru since by default pan blocks all */ - memset(level, 0, sizeof(level)); - for (i = 2; i < AF_NCH; i++) { - arg_ext.ch = i; - level[i] = 1.f; - af_pan_balance->control(af_pan_balance, - AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET, &arg_ext); - level[i] = 0.f; - } + float level[AF_NCH]; + int i; + af_control_ext_t arg_ext = { .arg = level }; + af_instance_t *af_pan_balance; + + if (!mixer->afilter) + return; + + mixer->balance = val; + mixer->restore_balance = true; + + if (af_control_any_rev(mixer->afilter, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) + return; + + if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] No balance control available.\n"); + mixer->restore_balance = false; + return; + } - af_pan_balance->control(af_pan_balance, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); + af_init(mixer->afilter); + /* make all other channels pass thru since by default pan blocks all */ + memset(level, 0, sizeof(level)); + for (i = 2; i < AF_NCH; i++) { + arg_ext.ch = i; + level[i] = 1.f; + af_pan_balance->control(af_pan_balance, + AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET, + &arg_ext); + level[i] = 0.f; + } + + af_pan_balance->control(af_pan_balance, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } @@ -19,6 +19,8 @@ #ifndef MPLAYER_MIXER_H #define MPLAYER_MIXER_H +#include <stdbool.h> + #include "libaf/af.h" #include "libao2/audio_out.h" @@ -31,20 +33,26 @@ typedef struct mixer_s { struct ao *ao; af_stream_t *afilter; int volstep; - int muted; + bool muted; + bool mute_emulation; float last_l, last_r; + float restore_vol_l, restore_vol_r; + bool restore_volume; + float balance |