summaryrefslogtreecommitdiffstats
path: root/libao2
diff options
context:
space:
mode:
Diffstat (limited to 'libao2')
-rw-r--r--libao2/ao_alsa.c66
-rw-r--r--libao2/ao_coreaudio.c8
-rw-r--r--libao2/ao_dsound.c72
-rw-r--r--libao2/ao_esd.c2
-rw-r--r--libao2/ao_nas.c2
-rw-r--r--libao2/ao_openal.c31
-rw-r--r--libao2/ao_pulse.c75
-rw-r--r--libao2/ao_sdl.c2
-rw-r--r--libao2/audio_out.h14
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