diff options
Diffstat (limited to 'libao2')
-rw-r--r-- | libao2/ao_alsa.c | 66 | ||||
-rw-r--r-- | libao2/ao_coreaudio.c | 8 | ||||
-rw-r--r-- | libao2/ao_dsound.c | 72 | ||||
-rw-r--r-- | libao2/ao_esd.c | 2 | ||||
-rw-r--r-- | libao2/ao_nas.c | 2 | ||||
-rw-r--r-- | libao2/ao_openal.c | 31 | ||||
-rw-r--r-- | libao2/ao_pulse.c | 75 | ||||
-rw-r--r-- | libao2/ao_sdl.c | 2 | ||||
-rw-r--r-- | libao2/audio_out.h | 14 |
9 files changed, 192 insertions, 80 deletions
diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index 1581be4b80..65e7fda178 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -99,6 +99,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: { @@ -185,16 +187,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); @@ -203,33 +204,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 63cc02e92c..f1fc0dd00a 100644 --- a/libao2/ao_dsound.c +++ b/libao2/ao_dsound.c @@ -121,8 +121,10 @@ static int min_free_space = 0; ///if the free space is below this val ///there will always be at least this amout of free space to prevent ///get_space() from returning wrong values when buffer is 100% full. ///will be replaced with nBlockAlign in init() +static int underrun_check = 0; ///0 or last reported free space (underrun detection) static int device_num = 0; ///wanted device number static GUID device; ///guid of the device +static int audio_volume; /***************************************************************************************/ @@ -314,6 +316,8 @@ static int write_buffer(unsigned char *data, int len) LPVOID lpvPtr2; DWORD dwBytes2; + underrun_check = 0; + // Lock the buffer res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); // If the buffer was lost, restore and retry lock. @@ -391,16 +395,16 @@ static int control(int cmd, void *arg) switch (cmd) { case AOCONTROL_GET_VOLUME: { ao_control_vol_t* vol = (ao_control_vol_t*)arg; - IDirectSoundBuffer_GetVolume(hdsbuf, &volume); - vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0); - //printf("ao_dsound: volume: %f\n",vol->left); + vol->left = vol->right = audio_volume; return CONTROL_OK; } case AOCONTROL_SET_VOLUME: { ao_control_vol_t* vol = (ao_control_vol_t*)arg; - volume = (DWORD)(log10(vol->right) * 5000.0) - 10000; + volume = audio_volume = vol->right; + if (volume < 1) + volume = 1; + volume = (DWORD)(log10(volume) * 5000.0) - 10000; IDirectSoundBuffer_SetVolume(hdsbuf, volume); - //printf("ao_dsound: volume: %f\n",vol->left); return CONTROL_OK; } } @@ -420,6 +424,9 @@ static int init(int rate, int channels, int format, int flags) int res; if (!InitDirectSound()) return 0; + global_ao->no_persistent_volume = true; + audio_volume = 100; + // ok, now create the buffers WAVEFORMATEXTENSIBLE wformat; DSBUFFERDESC dsbpridesc; @@ -542,6 +549,7 @@ static void reset(void) // reset directsound buffer IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0); write_offset=0; + underrun_check=0; } /** @@ -566,22 +574,16 @@ static void audio_resume(void) */ static void uninit(int immed) { - if(immed)reset(); - else{ - DWORD status; - IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0); - while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING)) - usec_sleep(20000); - } + if (!immed) + usec_sleep(get_delay() * 1000000); + reset(); + DestroyBuffer(); UninitDirectSound(); } -/** -\brief find out how many bytes can be written into the audio buffer without -\return free space in bytes, has to return 0 if the buffer is almost full -*/ -static int get_space(void) +// return exact number of free (safe to write) bytes +static int check_free_buffer_size(void) { int space; DWORD play_offset; @@ -593,6 +595,28 @@ static int get_space(void) // write_cursor is the position after which it is assumed to be save to write data // write_offset is the postion where we actually write the data to if(space > buffer_size)space -= buffer_size; // write_offset < play_offset + // Check for buffer underruns. An underrun happens if DirectSound + // started to play old data beyond the current write_offset. Detect this + // by checking whether the free space shrinks, even though no data was + // written (i.e. no write_buffer). Doesn't always work, but the only + // reason we need this is to deal with the situation when playback ends, + // and the buffer is only half-filled. + if (space < underrun_check) { + // there's no useful data in the buffers + space = buffer_size; + reset(); + } + underrun_check = space; + return space; +} + +/** +\brief find out how many bytes can be written into the audio buffer without +\return free space in bytes, has to return 0 if the buffer is almost full +*/ +static int get_space(void) +{ + int space = check_free_buffer_size(); if(space < min_free_space)return 0; return space-min_free_space; } @@ -606,13 +630,7 @@ static int get_space(void) */ static int play(void* data, int len, int flags) { - DWORD play_offset; - int space; - - // make sure we have enough space to write data - IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL); - space=buffer_size-(write_offset-play_offset); - if(space > buffer_size)space -= buffer_size; // write_offset < play_offset + int space = check_free_buffer_size(); if(space < len) len = space; if (!(flags & AOPLAY_FINAL_CHUNK)) @@ -626,10 +644,6 @@ static int play(void* data, int len, int flags) */ static float get_delay(void) { - DWORD play_offset; - int space; - IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL); - space=play_offset-write_offset; - if(space <= 0)space += buffer_size; + int space = check_free_buffer_size(); return (float)(buffer_size - space) / (float)ao_data.bps; } 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..263aed34fb 100644 --- a/libao2/ao_openal.c +++ b/libao2/ao_openal.c @@ -28,9 +28,11 @@ #ifdef OPENAL_AL_H #include <OpenAL/alc.h> #include <OpenAL/al.h> +#include <OpenAL/alext.h> #else #include <AL/alc.h> #include <AL/al.h> +#include <AL/alext.h> #endif #include "mp_msg.h" @@ -86,11 +88,27 @@ static int control(int cmd, void *arg) { static void print_help(void) { mp_msg(MSGT_AO, MSGL_FATAL, "\n-ao openal commandline help:\n" - "Example: mplayer -ao openal\n" + "Example: mplayer -ao openal:device=subdevice\n" "\nOptions:\n" + " device=subdevice\n" + " Audio device OpenAL should use. Devices can be listed\n" + " with -ao openal:device=help\n" ); } +static void list_devices(void) { + if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_TRUE) { + mp_msg(MSGT_AO, MSGL_FATAL, "Device listing not supported.\n"); + return; + } + const char *list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + mp_msg(MSGT_AO, MSGL_FATAL, "OpenAL devices:\n"); + while (list && *list) { + mp_msg(MSGT_AO, MSGL_FATAL, " '%s'\n", list); + list = list + strlen(list) + 1; + } +} + static int init(int rate, int channels, int format, int flags) { float position[3] = {0, 0, 0}; float direction[6] = {0, 0, 1, 0, -1, 0}; @@ -105,18 +123,25 @@ static int init(int rate, int channels, int format, int flags) { ALCint freq = 0; ALCint attribs[] = {ALC_FREQUENCY, rate, 0, 0}; int i; + char *device = NULL; const opt_t subopts[] = { + {"device", OPT_ARG_MSTRZ, &device, NULL}, {NULL} }; + global_ao->no_persistent_volume = true; if (subopt_parse(ao_subdevice, subopts) != 0) { print_help(); return 0; } + if (strcmp(device, "help") == 0) { + list_devices(); + goto err_out; + } if (channels > MAX_CHANS) { mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels); goto err_out; } - dev = alcOpenDevice(NULL); + dev = alcOpenDevice(device); if (!dev) { mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] could not open device\n"); goto err_out; @@ -145,9 +170,11 @@ static int init(int rate, int channels, int format, int flags) { ao_data.buffersize = CHUNK_SIZE * NUM_BUF; ao_data.outburst = channels * CHUNK_SIZE; tmpbuf = malloc(CHUNK_SIZE); + free(device); return 1; err_out: + free(device); return 0; } diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index ba95ccf2d2..5e71f7bf7e 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <stdbool.h> #include <string.h> +#include <stdlib.h> #include <pulse/pulseaudio.h> @@ -45,7 +46,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 +418,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,25 +426,37 @@ static int control(struct ao *ao, int cmd, void *arg) { struct priv *priv = ao->priv; switch (cmd) { + case AOCONTROL_GET_MUTE: // fallthrough 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, - info_func, ao))) { + info_func, ao))) + { GENERIC_ERR_MSG(priv->context, "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) { + 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) { + vol->left = vol->right = priv->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; @@ -451,26 +465,41 @@ static int control(struct ao *ao, int cmd, void *arg) 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); + (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; + volume.values[0] = (pa_volume_t)vol->left * PA_VOLUME_NORM / 100; + volume.values[1] = (pa_volume_t)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) { + 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) { + int mute = vol->left == 0.0f || vol->right == 0.0f; + 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); return CONTROL_OK; - } + } + default: return CONTROL_UNKNOWN; } 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 1c472565a0..aafedbf178 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; struct MPOpts *opts; @@ -98,10 +99,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 |