From 3a01606dc05b2cedb9792a6f8adefeba6e434ab0 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 7 Apr 2012 16:26:56 +0300 Subject: libao2: change control() types to enum, remove unused ones Change the audio driver control() command argument from "int" to "enum aocontrol". Remove unused control types (SET_DEVICE, GET_DEVICE, QUERY_FORMAT, SET_PLUGIN_DRIVER, SET_PLUGIN_LIST). The QUERY_FORMAT one looks like there's a possibility such functionality could be useful in the future, but as ao_oss was the only driver to have an actual implementation of it, the current code wasn't worth keeping. --- libao2/ao_alsa.c | 2 -- libao2/ao_oss.c | 16 ---------------- libao2/ao_pulse.c | 2 +- libao2/ao_sgi.c | 7 ------- libao2/ao_sun.c | 5 ----- libao2/audio_out.c | 4 ++-- libao2/audio_out.h | 49 ++++++++++++++++++++++++------------------------- 7 files changed, 27 insertions(+), 58 deletions(-) diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index 1581be4b80..e155a07cd5 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -97,8 +97,6 @@ static void alsa_error_handler(const char *file, int line, const char *function, static int control(int cmd, void *arg) { switch(cmd) { - case AOCONTROL_QUERY_FORMAT: - return CONTROL_TRUE; case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { diff --git a/libao2/ao_oss.c b/libao2/ao_oss.c index 82a0dd51e5..9290a73380 100644 --- a/libao2/ao_oss.c +++ b/libao2/ao_oss.c @@ -179,22 +179,6 @@ static int volume_oss4(ao_control_vol_t *vol, int cmd) { // to set/get/query special features/parameters static int control(int cmd,void *arg){ switch(cmd){ - case AOCONTROL_SET_DEVICE: - dsp=(char*)arg; - return CONTROL_OK; - case AOCONTROL_GET_DEVICE: - *(char**)arg=dsp; - return CONTROL_OK; -#ifdef SNDCTL_DSP_GETFMTS - case AOCONTROL_QUERY_FORMAT: - { - int format; - if (!ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format)) - if ((unsigned int)format & (unsigned long)arg) - return CONTROL_TRUE; - return CONTROL_FALSE; - } -#endif case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index ba95ccf2d2..fb331933d6 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -420,7 +420,7 @@ static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, pa_threaded_mainloop_signal(priv->mainloop, 0); } -static int control(struct ao *ao, int cmd, void *arg) +static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct priv *priv = ao->priv; switch (cmd) { diff --git a/libao2/ao_sgi.c b/libao2/ao_sgi.c index 40bc6b9177..492c8ff3ba 100644 --- a/libao2/ao_sgi.c +++ b/libao2/ao_sgi.c @@ -118,13 +118,6 @@ static int control(int cmd, void *arg){ mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] control.\n"); - switch(cmd) { - case AOCONTROL_QUERY_FORMAT: - /* Do not reject any format: return the closest matching - * format if the request is not supported natively. */ - return CONTROL_TRUE; - } - return CONTROL_UNKNOWN; } diff --git a/libao2/ao_sun.c b/libao2/ao_sun.c index ef8417c5f9..ecdb23d4af 100644 --- a/libao2/ao_sun.c +++ b/libao2/ao_sun.c @@ -402,11 +402,6 @@ static void setup_device_paths(void) // to set/get/query special features/parameters static int control(int cmd,void *arg){ switch(cmd){ - case AOCONTROL_SET_DEVICE: - audio_dev=(char*)arg; - return CONTROL_OK; - case AOCONTROL_QUERY_FORMAT: - return CONTROL_TRUE; case AOCONTROL_GET_VOLUME: { int fd; diff --git a/libao2/audio_out.c b/libao2/audio_out.c index 6130e2ed33..268c17d749 100644 --- a/libao2/audio_out.c +++ b/libao2/audio_out.c @@ -232,7 +232,7 @@ int ao_play(struct ao *ao, void *data, int len, int flags) return ao->driver->play(ao, data, len, flags); } -int ao_control(struct ao *ao, int cmd, void *arg) +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) { if (ao->driver->control) return ao->driver->control(ao, cmd, arg); @@ -299,7 +299,7 @@ int old_ao_play(struct ao *ao, void *data, int len, int flags) return ao->driver->old_functions->play(data, len, flags); } -int old_ao_control(struct ao *ao, int cmd, void *arg) +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg) { return ao->driver->old_functions->control(cmd, arg); } diff --git a/libao2/audio_out.h b/libao2/audio_out.h index 1c472565a0..2e59c42aa4 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -23,6 +23,27 @@ #include "bstr.h" +#define CONTROL_OK 1 +#define CONTROL_TRUE 1 +#define CONTROL_FALSE 0 +#define CONTROL_UNKNOWN -1 +#define CONTROL_ERROR -2 +#define CONTROL_NA -3 + +enum aocontrol { + // _VOLUME commands take struct ao_control_vol pointer for input/output. + // If there's only one volume, SET should use average of left/right. + AOCONTROL_GET_VOLUME, + AOCONTROL_SET_VOLUME, +}; + +#define AOPLAY_FINAL_CHUNK 1 + +typedef struct ao_control_vol { + float left; + float right; +} ao_control_vol_t; + typedef struct ao_info { /* driver name ("Matrox Millennium G200/G400" */ const char *name; @@ -53,7 +74,7 @@ struct ao_driver { bool is_new; const struct ao_info *info; const struct ao_old_functions *old_functions; - int (*control)(struct ao *ao, int cmd, void *arg); + int (*control)(struct ao *ao, enum aocontrol cmd, void *arg); int (*init)(struct ao *ao, char *params); void (*uninit)(struct ao *ao, bool cut_audio); void (*reset)(struct ao*ao); @@ -88,40 +109,18 @@ extern char *ao_subdevice; void list_audio_out(void); -#define CONTROL_OK 1 -#define CONTROL_TRUE 1 -#define CONTROL_FALSE 0 -#define CONTROL_UNKNOWN -1 -#define CONTROL_ERROR -2 -#define CONTROL_NA -3 - -#define AOCONTROL_SET_DEVICE 1 -#define AOCONTROL_GET_DEVICE 2 -#define AOCONTROL_QUERY_FORMAT 3 /* test for availabilty of a format */ -#define AOCONTROL_GET_VOLUME 4 -#define AOCONTROL_SET_VOLUME 5 -#define AOCONTROL_SET_PLUGIN_DRIVER 6 -#define AOCONTROL_SET_PLUGIN_LIST 7 - -#define AOPLAY_FINAL_CHUNK 1 - -typedef struct ao_control_vol { - float left; - float right; -} ao_control_vol_t; - struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input); void ao_init(struct ao *ao, char **ao_list); void ao_uninit(struct ao *ao, bool cut_audio); int ao_play(struct ao *ao, void *data, int len, int flags); -int ao_control(struct ao *ao, int cmd, void *arg); +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); double ao_get_delay(struct ao *ao); int ao_get_space(struct ao *ao); void ao_reset(struct ao *ao); void ao_pause(struct ao *ao); void ao_resume(struct ao *ao); -int old_ao_control(struct ao *ao, int cmd, void *arg); +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg); int old_ao_init(struct ao *ao, char *params); void old_ao_uninit(struct ao *ao, bool cut_audio); void old_ao_reset(struct ao*ao); -- cgit v1.2.3 From 7807f46cd1b8f58ee8f2e5f6d7240d7ca7502311 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 9 Apr 2012 17:39:01 +0300 Subject: audio: keep volume level internally (not only in AO) Current volume was always queried from the the audio output driver (or filter in case of --softvol). The only case where it was stored on mixer level was that when turning off mute, volume was set to the value it had before mute was activated. Change the mixer code to always store the current target volume internally. It still checks for significant changes from external sources and resets the internal value in that case. The main functionality changes are: Volume will now be kept separately from mute status. Increasing or decreasing volume will now change it relative to the original value before mute, even if mute is implemented by setting AO level volume to 0. Volume changes no longer automatically disable mute. The exception is relative changes up (like the volume increase key in default keybindings); that's the only case which still disables mute. Keeping the value internally avoids problems with granularity of possible volume values supported by AO. Increase/decrease keys could work unsymmetrically, or when specifying a smaller than default --volstep, even fail completely. In one case occurring in practice, if the AO only supports changing volume in steps of about 2 and rounds down the requested volume, then volume down key would decrease by 4 but volume up would increase by 2 (previous volume plus or minus the default change of 3, rounded down to a multiple of 2). Now, the internal value will keep full precision. --- command.c | 9 +++-- mixer.c | 110 +++++++++++++++++++++++++++++++++++++------------------------- mixer.h | 7 ++-- mplayer.c | 2 +- 4 files changed, 75 insertions(+), 53 deletions(-) diff --git a/command.c b/command.c index 7ce0e7e924..3c1805b412 100644 --- a/command.c +++ b/command.c @@ -756,18 +756,17 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, case M_PROPERTY_SET: if (!arg) return M_PROPERTY_ERROR; - if ((!!*(int *) arg) != mpctx->mixer.muted) - mixer_mute(&mpctx->mixer); + mixer_setmute(&mpctx->mixer, *(int *) arg); mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - mixer_mute(&mpctx->mixer); + mixer_setmute(&mpctx->mixer, !mixer_getmute(&mpctx->mixer)); mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; default: - return m_property_flag(prop, action, arg, &mpctx->mixer.muted); - + return m_property_flag_ro(prop, action, arg, + mixer_getmute(&mpctx->mixer)); } } diff --git a/mixer.c b/mixer.c index f28fea7b09..25fb79f76c 100644 --- a/mixer.c +++ b/mixer.c @@ -18,6 +18,8 @@ #include +#include + #include "config.h" #include "libao2/audio_out.h" #include "libaf/af.h" @@ -25,16 +27,16 @@ #include "mixer.h" -void mixer_getvolume(mixer_t *mixer, float *l, float *r) +static void checkvolume(struct mixer *mixer) { - *l = 0; - *r = 0; if (!mixer->ao) return; ao_control_vol_t vol; if (mixer->softvol || 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)) @@ -44,19 +46,34 @@ void mixer_getvolume(mixer_t *mixer, float *l, float *r) vol.left = (db_vals[0] / (mixer->softvol_max / 100.0)) * 100.0; vol.right = (db_vals[1] / (mixer->softvol_max / 100.0)) * 100.0; } - *r = vol.right; - *l = vol.left; + float l = mixer->vol_l; + float r = mixer->vol_r; + if (mixer->muted) + l = r = 0; + /* Try to detect cases where the volume has been changed by some external + * action (such as something else changing a shared system-wide volume). + * We don't test for exact equality, as some AOs may round the value + * we last set to some nearby supported value. 3 has been the default + * volume step for increase/decrease keys, and is apparently big enough + * to step to the next possible value in most setups. + */ + if (FFABS(vol.left - l) >= 3 || FFABS(vol.right - r) >= 3) { + mixer->vol_l = vol.left; + mixer->vol_r = vol.right; + mixer->muted = false; + } } -void mixer_setvolume(mixer_t *mixer, float l, float r) +void mixer_getvolume(mixer_t *mixer, float *l, float *r) { - if (!mixer->ao) { - mixer->muted = 0; - return; - } - ao_control_vol_t vol; - vol.right = r; - vol.left = l; + checkvolume(mixer); + *l = mixer->vol_l; + *r = mixer->vol_r; +} + +static void setvolume_internal(mixer_t *mixer, float l, float r) +{ + struct ao_control_vol vol = {.left = l, .right = r}; if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) { // af_volume uses values in dB @@ -81,33 +98,16 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) } } } - mixer->muted = 0; } -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); -} - -void mixer_decvolume(mixer_t *mixer) +void mixer_setvolume(mixer_t *mixer, float l, float r) { - 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); + checkvolume(mixer); // to check mute status + mixer->vol_l = av_clip(l, 0, 100); + mixer->vol_r = av_clip(r, 0, 100); + if (!mixer->ao || mixer->muted) + return; + setvolume_internal(mixer, mixer->vol_l, mixer->vol_r); } void mixer_getbothvolume(mixer_t *mixer, float *b) @@ -117,17 +117,39 @@ void mixer_getbothvolume(mixer_t *mixer, float *b) *b = (mixer_l + mixer_r) / 2; } -void mixer_mute(mixer_t *mixer) +void mixer_setmute(struct mixer *mixer, bool mute) { - 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; + checkvolume(mixer); + if (mute != mixer->muted) { + mixer->muted = mute; + setvolume_internal(mixer, mixer->vol_l * !mute, mixer->vol_r * !mute); } } +bool mixer_getmute(struct mixer *mixer) +{ + checkvolume(mixer); + return mixer->muted; +} + +static void addvolume(struct mixer *mixer, float d) +{ + checkvolume(mixer); + mixer_setvolume(mixer, mixer->vol_l + d, mixer->vol_r + d); + if (d > 0) + mixer_setmute(mixer, false); +} + +void mixer_incvolume(mixer_t *mixer) +{ + addvolume(mixer, mixer->volstep); +} + +void mixer_decvolume(mixer_t *mixer) +{ + addvolume(mixer, -mixer->volstep); +} + void mixer_getbalance(mixer_t *mixer, float *val) { *val = 0.f; diff --git a/mixer.h b/mixer.h index eaf81c1ba1..0c6d6a91db 100644 --- a/mixer.h +++ b/mixer.h @@ -30,8 +30,8 @@ typedef struct mixer { int volstep; bool softvol; float softvol_max; - int muted; - float last_l, last_r; + bool muted; + float vol_l, vol_r; } mixer_t; void mixer_getvolume(mixer_t *mixer, float *l, float *r); @@ -39,7 +39,8 @@ void mixer_setvolume(mixer_t *mixer, float l, float r); void mixer_incvolume(mixer_t *mixer); void mixer_decvolume(mixer_t *mixer); void mixer_getbothvolume(mixer_t *mixer, float *b); -void mixer_mute(mixer_t *mixer); +void mixer_setmute(mixer_t *mixer, bool mute); +bool mixer_getmute(mixer_t *mixer); void mixer_getbalance(mixer_t *mixer, float *bal); void mixer_setbalance(mixer_t *mixer, float bal); diff --git a/mplayer.c b/mplayer.c index d410008dff..7696b92ce8 100644 --- a/mplayer.c +++ b/mplayer.c @@ -692,7 +692,7 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc) { if (mpctx->user_muted) - mixer_mute(&mpctx->mixer); + mixer_setmute(&mpctx->mixer, false); uninit_player(mpctx, INITIALIZED_ALL); #if defined(__MINGW32__) || defined(__CYGWIN__) timeEndPeriod(1); -- cgit v1.2.3 From 157a6c1e8343cf0d174e6f9edee441bfefebe578 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 11 Apr 2012 02:48:18 +0300 Subject: audio: mixer: change logic for AOs with no volume control The volume filter was automatically inserted if setting AO volume failed. Remove that logic, and instead enable softvol mode fully if querying current volume (which will happen before any set attempts) fails. Fully switching to softvol mode is more robust, and any case where the behavior would differ (the behavior is neither that both querying/setting always work nor that both always fail) would have been buggy. --- mixer.c | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/mixer.c b/mixer.c index 25fb79f76c..0c92447c62 100644 --- a/mixer.c +++ b/mixer.c @@ -35,6 +35,7 @@ static void checkvolume(struct mixer *mixer) ao_control_vol_t vol; if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol)) { + mixer->softvol = true; if (!mixer->afilter) return; float db_vals[AF_NCH]; @@ -74,35 +75,37 @@ void mixer_getvolume(mixer_t *mixer, float *l, float *r) static void setvolume_internal(mixer_t *mixer, float l, float r) { struct ao_control_vol vol = {.left = l, .right = r}; - if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao, - AOCONTROL_SET_VOLUME, &vol)) { - // af_volume uses values in dB - float db_vals[AF_NCH]; - int i; - db_vals[0] = (l / 100.0) * (mixer->softvol_max / 100.0); - db_vals[1] = (r / 100.0) * (mixer->softvol_max / 100.0); - for (i = 2; i < AF_NCH; i++) - db_vals[i] = ((l + r) / 100.0) * (mixer->softvol_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_INFO, - "[Mixer] No hardware mixing, inserting volume filter.\n"); - if (af_add(mixer->afilter, "volume")) { - 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; - } - } - } + if (!mixer->softvol) { + if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK) + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] Failed to change audio output volume.\n"); + return; + } + // af_volume uses values in dB + float db_vals[AF_NCH]; + int i; + db_vals[0] = (l / 100.0) * (mixer->softvol_max / 100.0); + db_vals[1] = (r / 100.0) * (mixer->softvol_max / 100.0); + for (i = 2; i < AF_NCH; i++) + db_vals[i] = ((l + r) / 100.0) * (mixer->softvol_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_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"); } } void mixer_setvolume(mixer_t *mixer, float l, float r) { - checkvolume(mixer); // to check mute status + checkvolume(mixer); // to check mute status and AO support for volume mixer->vol_l = av_clip(l, 0, 100); mixer->vol_r = av_clip(r, 0, 100); if (!mixer->ao || mixer->muted) -- cgit v1.2.3 From 87dad2a4704b2fb0f983d5cb665a065437288d35 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 9 Apr 2012 21:02:27 +0300 Subject: audio: restore volume setting after AO reinit if needed MPlayer volume control was originally implemented with the assumption that it controls a system-wide volume setting which keeps its value even if a process closes and reopens the audio device. However, this is not actually true for --softvol mode or some audio output APIs that only consider volume as a per-client setting for software mixing. This could have annoying results, as the volume would be reset to a default value if the AO was closed and reopened, for example whem moving to a new file or crossing ordered chapter boundaries. Add code to set the previous volume again after audio reinitialization if the current audio chain is known to behave this way (softvol active or the AO driver is known to not keep persistent volume externally). This also avoids an inconsistency with the mute flag. The frontend assumed the mute status is persistent across file changes, but it could be similarly lost. The audio drivers that are assumed to not keep persistent volume are: coreaudio, dsound, esd, nas, openal, sdl. None of these changes have been tested. I'm guessing that ESD and NAS do per-connection non-persistent volume settings. Partially based on code by wm4. --- libao2/ao_coreaudio.c | 2 ++ libao2/ao_dsound.c | 2 ++ libao2/ao_esd.c | 2 ++ libao2/ao_nas.c | 2 ++ libao2/ao_openal.c | 1 + libao2/ao_sdl.c | 2 ++ libao2/audio_out.h | 1 + mixer.c | 27 +++++++++++++++++++++++++++ mixer.h | 4 ++++ mplayer.c | 2 +- 10 files changed, 44 insertions(+), 1 deletion(-) diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c index 34374f4c9c..50e943e9ac 100644 --- a/libao2/ao_coreaudio.c +++ b/libao2/ao_coreaudio.c @@ -450,6 +450,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 63cc02e92c..b33f2949fd 100644 --- a/libao2/ao_dsound.c +++ b/libao2/ao_dsound.c @@ -420,6 +420,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_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 2e59c42aa4..5a9d52def3 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -99,6 +99,7 @@ struct ao { int buffer_playable_size; bool initialized; bool untimed; + bool no_persistent_volume; const struct ao_driver *driver; void *priv; struct MPOpts *opts; diff --git a/mixer.c b/mixer.c index 0c92447c62..b231563026 100644 --- a/mixer.c +++ b/mixer.c @@ -76,11 +76,17 @@ static void setvolume_internal(mixer_t *mixer, float l, float r) { struct ao_control_vol vol = {.left = l, .right = r}; if (!mixer->softvol) { + // relies on the driver data being permanent (so ptr stays valid) + mixer->restore_volume = mixer->ao->no_persistent_volume ? + mixer->ao->driver->info->short_name : NULL; if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK) mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] Failed to change audio output volume.\n"); return; } + mixer->restore_volume = "softvol"; + if (!mixer->afilter) + return; // af_volume uses values in dB float db_vals[AF_NCH]; int i; @@ -197,3 +203,24 @@ void mixer_setbalance(mixer_t *mixer, float val) af_pan_balance->control(af_pan_balance, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } + +// Called after the audio filter chain is built or rebuilt. +void mixer_reinit(struct mixer *mixer, struct ao *ao) +{ + mixer->ao = ao; + /* Use checkvolume() to see if softvol needs to be enabled because of + * lacking AO support, but first store values it could overwrite. */ + float left = mixer->vol_l, right = mixer->vol_r; + bool muted = mixer->muted; + checkvolume(mixer); + /* Try to avoid restoring volume stored from one control method with + * another. Especially, restoring softvol volume (typically high) on + * system mixer could have very nasty effects. */ + const char *restore_reason = mixer->softvol ? "softvol" : + mixer->ao->driver->info->short_name; + if (mixer->restore_volume && !strcmp(mixer->restore_volume, + restore_reason)) { + mixer_setvolume(mixer, left, right); + mixer_setmute(mixer, muted); + } +} diff --git a/mixer.h b/mixer.h index 0c6d6a91db..fc5b6c1e71 100644 --- a/mixer.h +++ b/mixer.h @@ -32,8 +32,12 @@ typedef struct mixer { float softvol_max; bool muted; float vol_l, vol_r; + /* Contains ao driver name or "softvol" if volume is not persistent + * and needs to be restored after the driver is reinitialized. */ + const char *restore_volume; } mixer_t; +void mixer_reinit(struct mixer *mixer, struct ao *ao); void mixer_getvolume(mixer_t *mixer, float *l, float *r); void mixer_setvolume(mixer_t *mixer, float l, float r); void mixer_incvolume(mixer_t *mixer); diff --git a/mplayer.c b/mplayer.c index 7696b92ce8..9140b16bca 100644 --- a/mplayer.c +++ b/mplayer.c @@ -1821,10 +1821,10 @@ void reinit_audio_chain(struct MPContext *mpctx) "Couldn't find matching filter/ao format!\n"); goto init_error; } - mpctx->mixer.ao = ao; mpctx->mixer.volstep = volstep; mpctx->mixer.softvol = opts->softvol; mpctx->mixer.softvol_max = opts->softvol_max; + mixer_reinit(&mpctx->mixer, ao); mpctx->syncing_audio = true; return; -- cgit v1.2.3 From e29cb8f323031b32369bc2104ea1fd4422dd2945 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 9 Apr 2012 21:06:10 +0300 Subject: audio: restore balance setting after reinit Restore the audio balance setting when the audio chain is reinitialized (also after switching to another file). Also add a note about the balance code being seriously buggy. --- command.c | 3 --- mixer.c | 28 +++++++++++++++++++++++----- mixer.h | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/command.c b/command.c index 3c1805b412..e4ce53f87a 100644 --- a/command.c +++ b/command.c @@ -871,9 +871,6 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg, { float bal; - if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2) - return M_PROPERTY_UNAVAILABLE; - switch (action) { case M_PROPERTY_GET: if (!arg) diff --git a/mixer.c b/mixer.c index b231563026..e694253b11 100644 --- a/mixer.c +++ b/mixer.c @@ -161,13 +161,24 @@ void mixer_decvolume(mixer_t *mixer) 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); + if (mixer->afilter) + af_control_any_rev(mixer->afilter, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, + &mixer->balance); + *val = mixer->balance; } +/* NOTE: Currently the balance code is seriously buggy: it always changes + * the af_pan mapping between the first two input channels and first two + * output channels to particular values. These values make sense for an + * af_pan instance that was automatically inserted for balance control + * only and is otherwise an identity transform, but if the filter was + * there for another reason, then ignoring and overriding the original + * values is completely wrong. In particular, this will break + * automatically inserted downmix filters; the original coefficients that + * are significantly below 1 will be overwritten with much higher values. + */ + void mixer_setbalance(mixer_t *mixer, float val) { float level[AF_NCH]; @@ -175,6 +186,8 @@ void mixer_setbalance(mixer_t *mixer, float val) af_control_ext_t arg_ext = { .arg = level }; af_instance_t *af_pan_balance; + mixer->balance = val; + if (!mixer->afilter) return; @@ -182,6 +195,9 @@ void mixer_setbalance(mixer_t *mixer, float val) AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) return; + if (val == 0 || mixer->ao->channels < 2) + return; + if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); @@ -223,4 +239,6 @@ void mixer_reinit(struct mixer *mixer, struct ao *ao) mixer_setvolume(mixer, left, right); mixer_setmute(mixer, muted); } + if (mixer->balance != 0) + mixer_setbalance(mixer, mixer->balance); } diff --git a/mixer.h b/mixer.h index fc5b6c1e71..e626237539 100644 --- a/mixer.h +++ b/mixer.h @@ -35,6 +35,7 @@ typedef struct mixer { /* Contains ao driver name or "softvol" if volume is not persistent * and needs to be restored after the driver is reinitialized. */ const char *restore_volume; + float balance; } mixer_t; void mixer_reinit(struct mixer *mixer, struct ao *ao); -- cgit v1.2.3 From 9624f10aa85039c73d4bdb70e8062daeabaa90c6 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 9 Apr 2012 22:11:49 +0300 Subject: audio: fix unmute-at-end logic The player tried to disable mute before exiting, so that if mute is emulated by setting volume to 0 and the volume setting is a system-global one, we don't leave it at 0. However, the logic doing this at process exit was flawed, as volume settings are handled by audio output instances and the audio output that set the mute state may have been closed earlier. Trying to write reliably working logic that restores volume at exit only would be tricky, so change the code to always unmute an audio driver before closing it and restore mute status if one is opened again later. --- command.c | 4 ---- mixer.c | 28 +++++++++++++++++++++++++--- mixer.h | 1 + mp_core.h | 2 -- mplayer.c | 6 +++--- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/command.c b/command.c index e4ce53f87a..d511e52c4a 100644 --- a/command.c +++ b/command.c @@ -719,8 +719,6 @@ static int mp_property_volume(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } - mpctx->user_muted = 0; - switch (action) { case M_PROPERTY_SET: if (!arg) @@ -757,12 +755,10 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, if (!arg) return M_PROPERTY_ERROR; mixer_setmute(&mpctx->mixer, *(int *) arg); - mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: mixer_setmute(&mpctx->mixer, !mixer_getmute(&mpctx->mixer)); - mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; default: return m_property_flag_ro(prop, action, arg, diff --git a/mixer.c b/mixer.c index e694253b11..793a6ac58e 100644 --- a/mixer.c +++ b/mixer.c @@ -235,10 +235,32 @@ void mixer_reinit(struct mixer *mixer, struct ao *ao) const char *restore_reason = mixer->softvol ? "softvol" : mixer->ao->driver->info->short_name; if (mixer->restore_volume && !strcmp(mixer->restore_volume, - restore_reason)) { + restore_reason)) mixer_setvolume(mixer, left, right); - mixer_setmute(mixer, muted); - } + mixer_setmute(mixer, muted); if (mixer->balance != 0) mixer_setbalance(mixer, mixer->balance); } + +/* Called before uninitializing the audio output. The main purpose is to + * turn off mute, in case it's a global/persistent setting which might + * otherwise be left enabled even after this player instance exits. + */ +void mixer_uninit(struct mixer *mixer) +{ + checkvolume(mixer); + if (mixer->muted) { + /* Current audio output API combines playing the remaining buffered + * audio and uninitializing the AO into one operation, even though + * ideally unmute would happen between those two steps. We can't do + * volume changes after uninitialization, but we don't want the + * remaining audio to play at full volume either. Thus this + * workaround to drop remaining audio first. */ + ao_reset(mixer->ao); + mixer_setmute(mixer, false); + /* We remember mute status and re-enable it if we play more audio + * in the same process. */ + mixer->muted = true; + } + mixer->ao = NULL; +} diff --git a/mixer.h b/mixer.h index e626237539..c1903750f1 100644 --- a/mixer.h +++ b/mixer.h @@ -39,6 +39,7 @@ typedef struct mixer { } mixer_t; void mixer_reinit(struct mixer *mixer, struct ao *ao); +void mixer_uninit(struct mixer *mixer); void mixer_getvolume(mixer_t *mixer, float *l, float *r); void mixer_setvolume(mixer_t *mixer, float l, float r); void mixer_incvolume(mixer_t *mixer); diff --git a/mp_core.h b/mp_core.h index 621a5cc1d2..4481e61469 100644 --- a/mp_core.h +++ b/mp_core.h @@ -188,8 +188,6 @@ typedef struct MPContext { float begin_skip; ///< start time of the current skip while on edlout mode - short user_muted; ///< Stores whether user wanted muted mode. - int global_sub_size; // this encompasses all subtitle sources int global_sub_pos; // this encompasses all subtitle sources int set_of_sub_pos; diff --git a/mplayer.c b/mplayer.c index 9140b16bca..f3a9cbdc10 100644 --- a/mplayer.c +++ b/mplayer.c @@ -681,8 +681,10 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mask & INITIALIZED_AO) { mpctx->initialized_flags &= ~INITIALIZED_AO; current_module = "uninit_ao"; - if (mpctx->ao) + if (mpctx->ao) { + mixer_uninit(&mpctx->mixer); ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE); + } mpctx->ao = NULL; } @@ -691,8 +693,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc) { - if (mpctx->user_muted) - mixer_setmute(&mpctx->mixer, false); uninit_player(mpctx, INITIALIZED_ALL); #if defined(__MINGW32__) || defined(__CYGWIN__) timeEndPeriod(1); -- cgit v1.2.3 From 39aa7d9846a8a04e8f08acc0ea9e2ce38336e523 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 10 Apr 2012 04:45:53 +0300 Subject: mixer: support native audio driver mute Make mixer support setting the mute attribute at audio driver level, if one exists separately from volume. As of this commit, no libao2 driver exposes such an attribute yet; that will be added in later commits. Since the mute status can now be set externally, it's no longer completely obvious when the player should automatically disable mute when uninitializing an audio output. The implemented behavior is to turn mute off at uninitialization if we turned it on and haven't noticed it turn off (by external means) since. --- libao2/audio_out.h | 3 +++ mixer.c | 31 ++++++++++++++++++++++++------- mixer.h | 2 ++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libao2/audio_out.h b/libao2/audio_out.h index 5a9d52def3..955376d460 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -35,6 +35,9 @@ enum aocontrol { // If there's only one volume, SET should use average of left/right. AOCONTROL_GET_VOLUME, AOCONTROL_SET_VOLUME, + // _MUTE commands take a pointer to bool + AOCONTROL_GET_MUTE, + AOCONTROL_SET_MUTE, }; #define AOPLAY_FINAL_CHUNK 1 diff --git a/mixer.c b/mixer.c index 793a6ac58e..7be2179384 100644 --- a/mixer.c +++ b/mixer.c @@ -49,7 +49,7 @@ static void checkvolume(struct mixer *mixer) } float l = mixer->vol_l; float r = mixer->vol_r; - if (mixer->muted) + if (mixer->muted_using_volume) l = r = 0; /* Try to detect cases where the volume has been changed by some external * action (such as something else changing a shared system-wide volume). @@ -61,8 +61,14 @@ static void checkvolume(struct mixer *mixer) if (FFABS(vol.left - l) >= 3 || FFABS(vol.right - r) >= 3) { mixer->vol_l = vol.left; mixer->vol_r = vol.right; - mixer->muted = false; + if (mixer->muted_using_volume) + mixer->muted = false; } + if (!mixer->softvol) + // Rely on the value not changing if the query is not supported + ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted); + mixer->muted_by_us &= mixer->muted; + mixer->muted_using_volume &= mixer->muted; } void mixer_getvolume(mixer_t *mixer, float *l, float *r) @@ -130,8 +136,15 @@ void mixer_setmute(struct mixer *mixer, bool mute) { checkvolume(mixer); if (mute != mixer->muted) { + if (!mixer->softvol && !mixer->muted_using_volume && ao_control( + mixer->ao, AOCONTROL_SET_MUTE, &mute) == CONTROL_OK) { + mixer->muted_using_volume = false; + } else { + setvolume_internal(mixer, mixer->vol_l*!mute, mixer->vol_r*!mute); + mixer->muted_using_volume = mute; + } mixer->muted = mute; - setvolume_internal(mixer, mixer->vol_l * !mute, mixer->vol_r * !mute); + mixer->muted_by_us = mute; } } @@ -227,7 +240,7 @@ void mixer_reinit(struct mixer *mixer, struct ao *ao) /* Use checkvolume() to see if softvol needs to be enabled because of * lacking AO support, but first store values it could overwrite. */ float left = mixer->vol_l, right = mixer->vol_r; - bool muted = mixer->muted; + bool muted = mixer->muted_by_us; checkvolume(mixer); /* Try to avoid restoring volume stored from one control method with * another. Especially, restoring softvol volume (typically high) on @@ -237,7 +250,11 @@ void mixer_reinit(struct mixer *mixer, struct ao *ao) if (mixer->restore_volume && !strcmp(mixer->restore_volume, restore_reason)) mixer_setvolume(mixer, left, right); - mixer_setmute(mixer, muted); + /* We turn mute off at AO uninit, so it has to be restored (unless + * we're reinitializing filter chain while keeping AO); but we only + * enable mute, not turn external mute off. */ + if (muted) + mixer_setmute(mixer, true); if (mixer->balance != 0) mixer_setbalance(mixer, mixer->balance); } @@ -249,7 +266,7 @@ void mixer_reinit(struct mixer *mixer, struct ao *ao) void mixer_uninit(struct mixer *mixer) { checkvolume(mixer); - if (mixer->muted) { + if (mixer->muted_by_us) { /* Current audio output API combines playing the remaining buffered * audio and uninitializing the AO into one operation, even though * ideally unmute would happen between those two steps. We can't do @@ -260,7 +277,7 @@ void mixer_uninit(struct mixer *mixer) mixer_setmute(mixer, false); /* We remember mute status and re-enable it if we play more audio * in the same process. */ - mixer->muted = true; + mixer->muted_by_us = true; } mixer->ao = NULL; } diff --git a/mixer.h b/mixer.h index c1903750f1..efeb62856f 100644 --- a/mixer.h +++ b/mixer.h @@ -31,6 +31,8 @@ typedef struct mixer { bool softvol; float softvol_max; bool muted; + bool muted_by_us; + bool muted_using_volume; float vol_l, vol_r; /* Contains ao driver name or "softvol" if volume is not persistent * and needs to be restored after the driver is reinitialized. */ -- cgit v1.2.3 From b5636c3ac6a2ec28399c427472d40d9c76ed3b6f Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:54:27 +0100 Subject: ao_alsa: support native mute control --- libao2/ao_alsa.c | 71 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index e155a07cd5..ff837e7d30 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -97,11 +97,11 @@ static void alsa_error_handler(const char *file, int line, const char *function, static int control(int cmd, void *arg) { switch(cmd) { + case AOCONTROL_GET_MUTE: + case AOCONTROL_SET_MUTE: case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { - ao_control_vol_t *vol = (ao_control_vol_t *)arg; - int err; snd_mixer_t *handle; snd_mixer_elem_t *elem; @@ -183,16 +183,16 @@ 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: { + ao_control_vol_t *vol = arg; 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); @@ -201,33 +201,54 @@ 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: { + ao_control_vol_t *vol = arg; + 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: { + bool *mute = arg; + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_set_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); } + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, + !*mute); + break; + } + case AOCONTROL_GET_MUTE: { + bool *mute = arg; + 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); + *mute = !tmp; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_get_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + *mute &= !tmp; + } + 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 -- cgit v1.2.3 From fc8db0ca8890661ba049e3e9882163a64f54b8d7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:55:04 +0100 Subject: ao_pulse: support native mute control --- libao2/ao_pulse.c | 84 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index fb331933d6..ed6e08286a 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -45,7 +45,8 @@ struct priv { // Main event loop object struct pa_threaded_mainloop *mainloop; - struct pa_cvolume volume; + // temporary during control() + struct pa_sink_input_info pi; bool broken_pause; int retval; @@ -416,7 +417,7 @@ static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, } if (!i) return; - priv->volume = i->volume; + priv->pi = *i; pa_threaded_mainloop_signal(priv->mainloop, 0); } @@ -424,8 +425,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct priv *priv = ao->priv; switch (cmd) { + case AOCONTROL_GET_MUTE: case AOCONTROL_GET_VOLUME: { - ao_control_vol_t *vol = arg; uint32_t devidx = pa_stream_get_index(priv->stream); pa_threaded_mainloop_lock(priv->mainloop); if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx, @@ -434,38 +435,63 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) "pa_stream_get_sink_input_info() failed"); return CONTROL_ERROR; } - if (priv->volume.channels != 2) - vol->left = vol->right = - pa_cvolume_avg(&priv->volume) * 100 / PA_VOLUME_NORM; - else { - vol->left = priv->volume.values[0] * 100 / PA_VOLUME_NORM; - vol->right = priv->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) { + ao_control_vol_t *vol = arg; + if (priv->pi.volume.channels != 2) + vol->left = vol->right = + pa_cvolume_avg(&priv->pi.volume) * 100 / PA_VOLUME_NORM; + else { + vol->left = priv->pi.volume.values[0] * 100 / PA_VOLUME_NORM; + vol->right = priv->pi.volume.values[1] * 100 / PA_VOLUME_NORM; + } + } else if (cmd == AOCONTROL_GET_MUTE) { + bool *mute = arg; + *mute = priv->pi.mute; } return CONTROL_OK; } + + case AOCONTROL_SET_MUTE: case AOCONTROL_SET_VOLUME: { - const ao_control_vol_t *vol = arg; pa_operation *o; - struct pa_cvolume volume; - - pa_cvolume_reset(&volume, ao->channels); - if (volume.channels != 2) - pa_cvolume_set(&volume, volume.channels, - (pa_volume_t)vol->left*PA_VOLUME_NORM/100); - else { - volume.values[0] = vol->left * PA_VOLUME_NORM / 100; - volume.values[1] = vol->right * PA_VOLUME_NORM / 100; - } + pa_threaded_mainloop_lock(priv->mainloop); - o = pa_context_set_sink_input_volume(priv->context, - pa_stream_get_index(priv->stream), - &volume, NULL, NULL); - if (!o) { - pa_threaded_mainloop_unlock(priv->mainloop); - GENERIC_ERR_MSG(priv->context, - "pa_context_set_sink_input_volume() failed"); - return CONTROL_ERROR; - } + uint32_t stream_index = pa_stream_get_index(priv->stream); + if (cmd == AOCONTROL_SET_VOLUME) { + const ao_control_vol_t *vol = arg; + struct pa_cvolume volume; + + pa_cvolume_reset(&volume, ao->channels); + if (volume.channels != 2) + pa_cvolume_set(&volume, volume.channels, + vol->left * PA_VOLUME_NORM / 100); + else { + volume.values[0] = vol->left * PA_VOLUME_NORM / 100; + volume.values[1] = vol->right * PA_VOLUME_NORM / 100; + } + o = pa_context_set_sink_input_volume(priv->context, stream_index, + &volume, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(priv->mainloop); + GENERIC_ERR_MSG(priv->context, + "pa_context_set_sink_input_volume() failed"); + return CONTROL_ERROR; + } + } else if (cmd == AOCONTROL_SET_MUTE) { + const bool *mute = arg; + o = pa_context_set_sink_input_mute(priv->context, stream_index, + *mute, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(priv->mainloop); + GENERIC_ERR_MSG(priv->context, + "pa_context_set_sink_input_mute() failed"); + return CONTROL_ERROR; + } + } else + abort(); /* We don't wait for completion here */ pa_operation_unref(o); pa_threaded_mainloop_unlock(priv->mainloop); -- cgit v1.2.3 From 086d0381f0395d753ec3c995bf89542a4ca74599 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:58:57 +0100 Subject: ao_coreaudio: fix partial volume control If digital pass-through is used, this supported setting the volume (just mute, actually), but not getting the volume. This will probably lead to a stuck mute state in the mplayer frontend. Make the code respond to volume queries even if digital pass-through is used. --- libao2/ao_coreaudio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c index 50e943e9ac..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); -- cgit v1.2.3 From 74ad0b42841a4352f2645cf2104268cb8f39992d Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 10 Apr 2012 18:53:25 +0300 Subject: stream_pvr: fix field size / snprintf size mismatch struct station_elem_s had a field "name[8]", but the rest of the code used PVR_STATION_NAME_SIZE as field size in snprintf and some other calls accessing the field. Change the field size to PVR_STATION_NAME_SIZE so it matches the accesses. --- stream/stream_pvr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream/stream_pvr.c b/stream/stream_pvr.c index 745686e2e3..7fd3fe3a34 100644 --- a/stream/stream_pvr.c +++ b/stream/stream_pvr.c @@ -89,7 +89,7 @@ int pvr_param_bitrate_peak = 0; char *pvr_param_stream_type = NULL; typedef struct station_elem_s { - char name[8]; + char name[PVR_STATION_NAME_SIZE]; int freq; char station[PVR_STATION_NAME_SIZE]; int enabled; -- cgit v1.2.3 From 4680beb6dc0cb923cc6bc654e23dd3e007fda413 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 14 Apr 2012 02:43:40 +0300 Subject: demux_lavf: try harder to make up a frame rate Frame rate information is mostly irrelevant for playback, but it's needed at least to convert frame numbers used in some subtitle formats (like MicroDVD) into timestamps. Libavformat stopped making up a frame rate if no "reliable" information is available (commit 7929e22bd "lavf: don't guess r_frame_rate from either stream or codec timebase", 1.5 months ago). This caused a regression with AVI files and MicroDVD subtitles. Add a heuristic similar to what libavformat used to have, to make up FPS values which should work at least for the AVI+MicroDVD use case. --- libmpdemux/demux_lavf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c index f8311e215c..62cb0bad1b 100644 --- a/libmpdemux/demux_lavf.c +++ b/libmpdemux/demux_lavf.c @@ -417,8 +417,22 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) sh_video->video.dwRate = codec->time_base.den; sh_video->video.dwScale = codec->time_base.num; } - sh_video->fps = av_q2d(st->r_frame_rate); - sh_video->frametime = 1 / av_q2d(st->r_frame_rate); + /* Try to make up some frame rate value, even if it's not reliable. + * FPS information is needed to support subtitle formats which base + * timing on frame numbers. + * Libavformat seems to report no "reliable" FPS value for AVI files, + * while they are typically constant enough FPS that the value this + * heuristic makes up works with subtitles in practice. + */ + double fps; + if (st->r_frame_rate.num) + fps = av_q2d(st->r_frame_rate); + else + fps = 1.0 / FFMAX(av_q2d(st->time_base), + av_q2d(st->codec->time_base) * + st->codec->ticks_per_frame); + sh_video->fps = fps; + sh_video->frametime = 1 / fps; sh_video->format = bih->biCompression; if (st->sample_aspect_ratio.num) sh_video->aspect = codec->width * st->sample_aspect_ratio.num -- cgit v1.2.3 From b711624ef350d1e971f5fcc57eb4af9f74233d2a Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 14 Apr 2012 03:51:19 +0300 Subject: subs: only use "subfont.ttf" as libass fallback if it exists Libass was set to use the file "subfont.ttf" in the user configuration directory as a default/fallback font. This triggered "Error opening font" errors from libass if it tried to use the fallback font for some glyph and the user had not copied/linked any font there (and there is generally little reason to do that nowadays when using fontconfig). Check whether the path exists and only set it in ass_set_fonts() if it does. --- sub/ass_mp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 8af3698a47..0713248db0 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -266,8 +266,13 @@ void mp_ass_configure_fonts(ASS_Renderer *priv) path = strdup(sub_font_name); else if (font_fontconfig < 0 && font_name) path = strdup(font_name); - else + else { path = get_path("subfont.ttf"); + if (!mp_path_exists(path)) { + free(path); + path = NULL; + } + } if (font_fontconfig >= 0 && sub_font_name) family = strdup(sub_font_name); else if (font_fontconfig >= 0 && font_name) -- cgit v1.2.3