Moving mixers to audio outputs
This commit is contained in:
parent
dd9af72a74
commit
9a70c4d06d
1
NEWS
1
NEWS
@ -1,4 +1,5 @@
|
||||
ver 0.15 - (200?/??/??)
|
||||
* Rewritten mixer code to support multiple mixers
|
||||
* Add audio archive extraction support:
|
||||
- bzip2
|
||||
- iso9660
|
||||
|
@ -157,18 +157,23 @@ Linear interpolator, very fast, poor quality.
|
||||
For an up-to-date list of available converters, please see the libsamplerate
|
||||
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
|
||||
.TP
|
||||
.B mixer_type <oss, alsa or software>
|
||||
This specifies which mixer to use. The default depends on what audio output
|
||||
support mpd was built with.
|
||||
.B mixer_type <alsa, oss, software or hardware>
|
||||
This specifies which mixer to use. The default is hardware and depends on
|
||||
what audio output support mpd was built with. Options alsa and oss are
|
||||
legacy and should not be used in new configs, but when set mixer_device
|
||||
and mixer_control will apply.
|
||||
.TP
|
||||
.B mixer_device <mixer dev>
|
||||
This specifies which mixer to use. The default for oss is "/dev/mixer"; the
|
||||
default for alsa is "default".
|
||||
default for alsa is "default". This option is deprecated and should not be
|
||||
used. Look at the mix_device option of corresponding output device instead.
|
||||
.TP
|
||||
.B mixer_control <mixer ctrl>
|
||||
This specifies which mixer control to use (sometimes referred to as the
|
||||
"device"). Examples of mixer controls are PCM, Line1, Master, etc. An example
|
||||
for OSS is "Pcm", and an example for alsa is "PCM".
|
||||
for OSS is "Pcm", and an example for alsa is "PCM". This option is deprecated
|
||||
and should not be used. Look at the mix_control option of corresponding
|
||||
output device instead.
|
||||
.TP
|
||||
.B replaygain <album or track>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain tags
|
||||
@ -276,6 +281,15 @@ whatever audio format is passed to the audio output.
|
||||
.B device <dev>
|
||||
This specifies the device to use for audio output. The default is "default".
|
||||
.TP
|
||||
.B mix_device <mixer dev>
|
||||
This specifies which mixer to use. The default for oss is "/dev/mixer"; the
|
||||
default for alsa is "default".
|
||||
.TP
|
||||
.B mix_control <mixer ctrl>
|
||||
This specifies which mixer control to use (sometimes referred to as the
|
||||
"device"). Examples of mixer controls are PCM, Line1, Master, etc. An example
|
||||
for OSS is "Pcm", and an example for alsa is "PCM".
|
||||
.TP
|
||||
.B use_mmap <yes or no>
|
||||
Setting this allows you to use memory-mapped I/O. Certain hardware setups may
|
||||
benefit from this, but most do not. Most users do not need to set this. The
|
||||
|
@ -162,6 +162,8 @@ log_file "~/.mpd/log"
|
||||
# name "My ALSA Device"
|
||||
# device "hw:0,0" # optional
|
||||
# format "44100:16:2" # optional
|
||||
# mix_device "default" # optional
|
||||
# mix_control "PCM" # optional
|
||||
#}
|
||||
#
|
||||
# An example of an OSS output:
|
||||
@ -171,6 +173,8 @@ log_file "~/.mpd/log"
|
||||
# name "My OSS Device"
|
||||
# device "/dev/dsp" # optional
|
||||
# format "44100:16:2" # optional
|
||||
# mix_device "/dev/mixer" # optional
|
||||
# mix_control "PCM" # optional
|
||||
#}
|
||||
#
|
||||
# An example of a shout output (for streaming to Icecast):
|
||||
@ -232,17 +236,9 @@ log_file "~/.mpd/log"
|
||||
# specified it may be autodetected at startup, depending on the dependencies
|
||||
# which were compiled into the server.
|
||||
#
|
||||
# An example for controlling an ALSA mixer:
|
||||
# An example for controlling an ALSA or OSS mixer:
|
||||
#
|
||||
#mixer_type "alsa"
|
||||
#mixer_device "default"
|
||||
#mixer_control "PCM"
|
||||
#
|
||||
# An example for controlling an OSS mixer:
|
||||
#
|
||||
#mixer_type "oss"
|
||||
#mixer_device "/dev/mixer"
|
||||
#mixer_control "PCM"
|
||||
#mixer_type "hardware"
|
||||
#
|
||||
# This example is a general volume control mixer, it is used to adjust the
|
||||
# volume of the audio sent to the audio output, and will work with all outputs.
|
||||
|
@ -273,6 +273,7 @@ endif
|
||||
|
||||
if HAVE_ALSA
|
||||
mpd_SOURCES += output/alsa_plugin.c
|
||||
mpd_SOURCES += mixer/alsa_mixer.c
|
||||
endif
|
||||
|
||||
if HAVE_AO
|
||||
@ -293,6 +294,7 @@ endif
|
||||
|
||||
if HAVE_OSS
|
||||
mpd_SOURCES += output/oss_plugin.c
|
||||
mpd_SOURCES += mixer/oss_mixer.c
|
||||
endif
|
||||
|
||||
if HAVE_OSX
|
||||
@ -315,7 +317,6 @@ if HAVE_SHOUT_OGG
|
||||
mpd_SOURCES += output/shout_ogg.c
|
||||
endif
|
||||
|
||||
|
||||
mpd_CFLAGS = $(MPD_CFLAGS)
|
||||
mpd_CPPFLAGS = \
|
||||
$(CURL_CFLAGS) \
|
||||
|
55
src/audio.c
55
src/audio.c
@ -25,6 +25,7 @@
|
||||
#include "client.h"
|
||||
#include "idle.h"
|
||||
#include "utils.h"
|
||||
#include "mixer.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@ -428,3 +429,57 @@ errline:
|
||||
}
|
||||
}
|
||||
|
||||
bool mixer_control_setvol(unsigned int device, int volume, int rel)
|
||||
{
|
||||
struct audio_output *output;
|
||||
if (device >= audioOutputArraySize)
|
||||
return false;
|
||||
|
||||
output = &audioOutputArray[device];
|
||||
if (output->plugin && output->plugin->control) {
|
||||
if (rel) {
|
||||
int cur_volume;
|
||||
if (!output->plugin->control(output->data, AC_MIXER_GETVOL, &cur_volume)) {
|
||||
return false;
|
||||
}
|
||||
volume = volume + cur_volume;
|
||||
}
|
||||
if (volume > 100)
|
||||
volume = 100;
|
||||
else if (volume < 0)
|
||||
volume = 0;
|
||||
|
||||
return output->plugin->control(output->data, AC_MIXER_SETVOL, &volume);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mixer_control_getvol(unsigned int device, int *volume)
|
||||
{
|
||||
struct audio_output *output;
|
||||
if (device >= audioOutputArraySize)
|
||||
return false;
|
||||
|
||||
output = &audioOutputArray[device];
|
||||
if (output->plugin && output->plugin->control) {
|
||||
return output->plugin->control(output->data, AC_MIXER_GETVOL, volume);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mixer_configure_legacy(char *name, ConfigParam *param)
|
||||
{
|
||||
unsigned i;
|
||||
struct audio_output *output;
|
||||
|
||||
for (i = 0; i < audioOutputArraySize; ++i) {
|
||||
output = &audioOutputArray[i];
|
||||
if (output && output->plugin && !strcmp(name, output->plugin->name)) {
|
||||
if (output->plugin->control) {
|
||||
g_debug("reconfiguring %s mixer\n", name);
|
||||
return output->plugin->control(output->data, AC_MIXER_CONFIGURE, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "conf.h"
|
||||
|
||||
#define AUDIO_AO_DRIVER_DEFAULT "default"
|
||||
|
||||
@ -70,4 +71,8 @@ void readAudioDevicesState(FILE *fp);
|
||||
|
||||
void saveAudioDevicesState(FILE *fp);
|
||||
|
||||
bool mixer_control_setvol(unsigned int device, int volume, int rel);
|
||||
bool mixer_control_getvol(unsigned int device, int *volume);
|
||||
bool mixer_configure_legacy(char *name, ConfigParam *param);
|
||||
|
||||
#endif
|
||||
|
@ -388,7 +388,7 @@ handle_status(struct client *client,
|
||||
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
|
||||
COMMAND_STATUS_CROSSFADE ": %i\n"
|
||||
COMMAND_STATUS_STATE ": %s\n",
|
||||
getVolumeLevel(),
|
||||
volume_level_get(),
|
||||
getPlaylistRepeatStatus(),
|
||||
getPlaylistRandomStatus(),
|
||||
getPlaylistVersion(),
|
||||
@ -906,7 +906,7 @@ handle_volume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_int(client, &change, argv[1], need_integer))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
ret = changeVolumeLevel(change, 1);
|
||||
ret = volume_level_change(change, 1);
|
||||
if (ret == -1)
|
||||
command_error(client, ACK_ERROR_SYSTEM,
|
||||
"problems setting volume");
|
||||
@ -922,7 +922,7 @@ handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
|
||||
if (!check_int(client, &level, argv[1], need_integer))
|
||||
return COMMAND_RETURN_ERROR;
|
||||
|
||||
ret = changeVolumeLevel(level, 0);
|
||||
ret = volume_level_change(level, 0);
|
||||
if (ret == -1)
|
||||
command_error(client, ACK_ERROR_SYSTEM,
|
||||
"problems setting volume");
|
||||
|
@ -264,7 +264,7 @@ int main(int argc, char *argv[])
|
||||
dc_init();
|
||||
initAudioConfig();
|
||||
initAudioDriver();
|
||||
initVolume();
|
||||
volume_init();
|
||||
client_manager_init();
|
||||
replay_gain_global_init();
|
||||
initNormalization();
|
||||
@ -278,7 +278,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
initZeroconf();
|
||||
|
||||
openVolumeDevice();
|
||||
decoder_thread_start();
|
||||
player_create();
|
||||
read_state_file();
|
||||
@ -315,7 +314,7 @@ int main(int argc, char *argv[])
|
||||
finishNormalization();
|
||||
finishAudioDriver();
|
||||
finishAudioConfig();
|
||||
finishVolume();
|
||||
volume_finish();
|
||||
mapper_finish();
|
||||
path_global_finish();
|
||||
finishPermissions();
|
||||
|
33
src/mixer.h
Normal file
33
src/mixer.h
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
#ifndef MPD_MIXER_H
|
||||
#define MPD_MIXER_H
|
||||
|
||||
#include "conf.h"
|
||||
|
||||
/**
|
||||
* alsa mixer
|
||||
*/
|
||||
|
||||
struct alsa_mixer;
|
||||
|
||||
struct alsa_mixer *alsa_mixer_init(void);
|
||||
void alsa_mixer_finish(struct alsa_mixer *am);
|
||||
void alsa_mixer_configure(struct alsa_mixer *am, ConfigParam *param);
|
||||
bool alsa_mixer_open(struct alsa_mixer *am);
|
||||
bool alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg);
|
||||
void alsa_mixer_close(struct alsa_mixer *am);
|
||||
|
||||
/**
|
||||
* oss mixer
|
||||
*/
|
||||
|
||||
struct oss_mixer;
|
||||
|
||||
struct oss_mixer *oss_mixer_init(void);
|
||||
void oss_mixer_finish(struct oss_mixer *am);
|
||||
void oss_mixer_configure(struct oss_mixer *am, ConfigParam *param);
|
||||
bool oss_mixer_open(struct oss_mixer *am);
|
||||
bool oss_mixer_control(struct oss_mixer *am, int cmd, void *arg);
|
||||
void oss_mixer_close(struct oss_mixer *am);
|
||||
|
||||
#endif
|
206
src/mixer/alsa_mixer.c
Normal file
206
src/mixer/alsa_mixer.c
Normal file
@ -0,0 +1,206 @@
|
||||
|
||||
#include "../output_api.h"
|
||||
#include "../mixer.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#define VOLUME_MIXER_ALSA_DEFAULT "default"
|
||||
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
|
||||
|
||||
struct alsa_mixer {
|
||||
char *device;
|
||||
char *control;
|
||||
snd_mixer_t *handle;
|
||||
snd_mixer_elem_t *elem;
|
||||
long volume_min;
|
||||
long volume_max;
|
||||
int volume_set;
|
||||
};
|
||||
|
||||
struct alsa_mixer *
|
||||
alsa_mixer_init(void)
|
||||
{
|
||||
struct alsa_mixer *am = g_malloc(sizeof(struct alsa_mixer));
|
||||
am->device = NULL;
|
||||
am->control = NULL;
|
||||
am->handle = NULL;
|
||||
am->elem = NULL;
|
||||
am->volume_min = 0;
|
||||
am->volume_max = 0;
|
||||
am->volume_set = -1;
|
||||
return am;
|
||||
}
|
||||
|
||||
void
|
||||
alsa_mixer_finish(struct alsa_mixer *am)
|
||||
{
|
||||
g_free(am);
|
||||
}
|
||||
|
||||
void
|
||||
alsa_mixer_configure(struct alsa_mixer *am, ConfigParam *param)
|
||||
{
|
||||
BlockParam *bp;
|
||||
|
||||
if ((bp = getBlockParam(param, "mix_device")))
|
||||
am->device = bp->value;
|
||||
if ((bp = getBlockParam(param, "mix_control")))
|
||||
am->control = bp->value;
|
||||
}
|
||||
|
||||
void
|
||||
alsa_mixer_close(struct alsa_mixer *am)
|
||||
{
|
||||
if (am->handle) snd_mixer_close(am->handle);
|
||||
am->handle = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
alsa_mixer_open(struct alsa_mixer *am)
|
||||
{
|
||||
int err;
|
||||
snd_mixer_elem_t *elem;
|
||||
const char *control_name = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
|
||||
const char *device = VOLUME_MIXER_ALSA_DEFAULT;
|
||||
|
||||
if (am->device) {
|
||||
device = am->device;
|
||||
}
|
||||
err = snd_mixer_open(&am->handle, 0);
|
||||
snd_config_update_free_global();
|
||||
if (err < 0) {
|
||||
g_warning("problems opening alsa mixer: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = snd_mixer_attach(am->handle, device)) < 0) {
|
||||
g_warning("problems attaching alsa mixer: %s\n",
|
||||
snd_strerror(err));
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = snd_mixer_selem_register(am->handle, NULL,
|
||||
NULL)) < 0) {
|
||||
g_warning("problems snd_mixer_selem_register'ing: %s\n",
|
||||
snd_strerror(err));
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = snd_mixer_load(am->handle)) < 0) {
|
||||
g_warning("problems snd_mixer_selem_register'ing: %s\n",
|
||||
snd_strerror(err));
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
|
||||
elem = snd_mixer_first_elem(am->handle);
|
||||
|
||||
if (am->control) {
|
||||
control_name = am->control;
|
||||
}
|
||||
|
||||
while (elem) {
|
||||
if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
|
||||
if (strcasecmp(control_name,
|
||||
snd_mixer_selem_get_name(elem)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elem = snd_mixer_elem_next(elem);
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
am->elem = elem;
|
||||
snd_mixer_selem_get_playback_volume_range(am->elem,
|
||||
&am->volume_min,
|
||||
&am->volume_max);
|
||||
return true;
|
||||
}
|
||||
|
||||
g_warning("can't find alsa mixer control \"%s\"\n", control_name);
|
||||
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
alsa_mixer_control(struct alsa_mixer *am, int cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AC_MIXER_CONFIGURE:
|
||||
alsa_mixer_configure(am, (ConfigParam *)arg);
|
||||
if (am->handle)
|
||||
alsa_mixer_close(am);
|
||||
return true;
|
||||
case AC_MIXER_GETVOL:
|
||||
{
|
||||
int err;
|
||||
int ret, *volume = arg;
|
||||
long level;
|
||||
|
||||
if (!am->handle && !alsa_mixer_open(am)) {
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_mixer_handle_events(am->handle)) < 0) {
|
||||
g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
|
||||
snd_strerror(err), "handle_events");
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_mixer_selem_get_playback_volume(am->elem,
|
||||
SND_MIXER_SCHN_FRONT_LEFT, &level)) < 0) {
|
||||
g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
|
||||
snd_strerror(err), "selem_get_playback_volume");
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
ret = ((am->volume_set / 100.0) * (am->volume_max - am->volume_min)
|
||||
+ am->volume_min) + 0.5;
|
||||
if (am->volume_set > 0 && ret == level) {
|
||||
ret = am->volume_set;
|
||||
} else {
|
||||
ret = (int)(100 * (((float)(level - am->volume_min)) /
|
||||
(am->volume_max - am->volume_min)) + 0.5);
|
||||
}
|
||||
*volume = ret;
|
||||
return true;
|
||||
}
|
||||
case AC_MIXER_SETVOL:
|
||||
{
|
||||
float vol;
|
||||
long level;
|
||||
int *volume = arg;
|
||||
int err;
|
||||
|
||||
if (!am->handle && !alsa_mixer_open(am)) {
|
||||
return false;
|
||||
}
|
||||
vol = *volume;
|
||||
|
||||
am->volume_set = vol + 0.5;
|
||||
am->volume_set = am->volume_set > 100 ? 100 :
|
||||
(am->volume_set < 0 ? 0 : am->volume_set);
|
||||
|
||||
level = (long)(((vol / 100.0) * (am->volume_max - am->volume_min) +
|
||||
am->volume_min) + 0.5);
|
||||
level = level > am->volume_max ? am->volume_max : level;
|
||||
level = level < am->volume_min ? am->volume_min : level;
|
||||
|
||||
if ((err = snd_mixer_selem_set_playback_volume_all(am->elem,
|
||||
level)) < 0) {
|
||||
g_warning("problems setting alsa volume: %s\n",
|
||||
snd_strerror(err));
|
||||
alsa_mixer_close(am);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
g_warning("Unsuported alsa control\n");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
197
src/mixer/oss_mixer.c
Normal file
197
src/mixer/oss_mixer.c
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../output_api.h"
|
||||
#include "../mixer.h"
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
# include <soundcard.h>
|
||||
#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
||||
# include <sys/soundcard.h>
|
||||
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
||||
|
||||
#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer"
|
||||
|
||||
struct oss_mixer {
|
||||
const char *device;
|
||||
const char *control;
|
||||
int device_fd;
|
||||
int volume_control;
|
||||
};
|
||||
|
||||
struct oss_mixer *oss_mixer_init(void);
|
||||
void oss_mixer_finish(struct oss_mixer *am);
|
||||
void oss_mixer_configure(struct oss_mixer *am, ConfigParam *param);
|
||||
bool oss_mixer_open(struct oss_mixer *am);
|
||||
bool oss_mixer_control(struct oss_mixer *am, int cmd, void *arg);
|
||||
void oss_mixer_close(struct oss_mixer *am);
|
||||
|
||||
struct oss_mixer *
|
||||
oss_mixer_init(void)
|
||||
{
|
||||
struct oss_mixer *om = g_malloc(sizeof(struct oss_mixer));
|
||||
om->device = NULL;
|
||||
om->control = NULL;
|
||||
om->device_fd = -1;
|
||||
om->volume_control = SOUND_MIXER_PCM;
|
||||
return om;
|
||||
}
|
||||
|
||||
void
|
||||
oss_mixer_finish(struct oss_mixer *om)
|
||||
{
|
||||
g_free(om);
|
||||
}
|
||||
|
||||
void
|
||||
oss_mixer_configure(struct oss_mixer *om, ConfigParam *param)
|
||||
{
|
||||
BlockParam *bp;
|
||||
bp = getBlockParam(param, "mix_device");
|
||||
if (bp) {
|
||||
om->device = bp->value;
|
||||
}
|
||||
bp = getBlockParam(param, "mix_control");
|
||||
if (bp) {
|
||||
om->control = bp->value;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
oss_mixer_close(struct oss_mixer *om)
|
||||
{
|
||||
if (om->device_fd != -1)
|
||||
while (close(om->device_fd) && errno == EINTR) ;
|
||||
om->device_fd = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
oss_find_mixer(const char *name)
|
||||
{
|
||||
const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
|
||||
size_t name_length = strlen(name);
|
||||
|
||||
for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (strncasecmp(name, labels[i], name_length) == 0 &&
|
||||
(labels[i][name_length] == 0 ||
|
||||
labels[i][name_length] == ' '))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool
|
||||
oss_mixer_open(struct oss_mixer *om)
|
||||
{
|
||||
const char *device = VOLUME_MIXER_OSS_DEFAULT;
|
||||
|
||||
if (om->device) {
|
||||
device = om->device;
|
||||
}
|
||||
|
||||
if ((om->device_fd = open(device, O_RDONLY)) < 0) {
|
||||
g_warning("Unable to open oss mixer \"%s\"\n", device);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (om->control) {
|
||||
int i;
|
||||
int devmask = 0;
|
||||
|
||||
if (ioctl(om->device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
|
||||
g_warning("errors getting read_devmask for oss mixer\n");
|
||||
oss_mixer_close(om);
|
||||
return false;
|
||||
}
|
||||
i = oss_find_mixer(om->control);
|
||||
|
||||
if (i < 0) {
|
||||
g_warning("mixer control \"%s\" not found\n",
|
||||
om->control);
|
||||
oss_mixer_close(om);
|
||||
return false;
|
||||
} else if (!((1 << i) & devmask)) {
|
||||
g_warning("mixer control \"%s\" not usable\n",
|
||||
om->control);
|
||||
oss_mixer_close(om);
|
||||
return false;
|
||||
}
|
||||
om->volume_control = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
oss_mixer_control(struct oss_mixer *om, int cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AC_MIXER_CONFIGURE:
|
||||
oss_mixer_configure(om, (ConfigParam *)arg);
|
||||
//if (om->device_fd >= 0)
|
||||
oss_mixer_close(om);
|
||||
return true;
|
||||
break;
|
||||
case AC_MIXER_GETVOL:
|
||||
{
|
||||
int left, right, level;
|
||||
int *ret;
|
||||
|
||||
if (om->device_fd < 0 && !oss_mixer_open(om)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioctl(om->device_fd, MIXER_READ(om->volume_control), &level) < 0) {
|
||||
oss_mixer_close(om);
|
||||
g_warning("unable to read oss volume\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
left = level & 0xff;
|
||||
right = (level & 0xff00) >> 8;
|
||||
|
||||
if (left != right) {
|
||||
g_warning("volume for left and right is not the same, \"%i\" and "
|
||||
"\"%i\"\n", left, right);
|
||||
}
|
||||
ret = (int *) arg;
|
||||
*ret = left;
|
||||
return true;
|
||||
}
|
||||
case AC_MIXER_SETVOL:
|
||||
{
|
||||
int new;
|
||||
int level;
|
||||
int *value = arg;
|
||||
|
||||
if (om->device_fd < 0 && !oss_mixer_open(om)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
new = *value;
|
||||
if (new < 0) {
|
||||
new = 0;
|
||||
} else if (new > 100) {
|
||||
new = 100;
|
||||
}
|
||||
|
||||
level = (new << 8) + new;
|
||||
|
||||
if (ioctl(om->device_fd, MIXER_WRITE(om->volume_control), &level) < 0) {
|
||||
g_warning("unable to set oss volume\n");
|
||||
oss_mixer_close(om);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
g_warning("Unsuported oss control\n");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "../output_api.h"
|
||||
#include "../utils.h"
|
||||
#include "../mixer.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
@ -51,6 +52,9 @@ typedef struct _AlsaData {
|
||||
unsigned int period_time;
|
||||
int sampleSize;
|
||||
int useMmap;
|
||||
|
||||
struct alsa_mixer *mixer;
|
||||
|
||||
} AlsaData;
|
||||
|
||||
static const char *
|
||||
@ -71,12 +75,15 @@ static AlsaData *newAlsaData(void)
|
||||
ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
|
||||
ret->period_time = 0;
|
||||
|
||||
ret->mixer = alsa_mixer_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void freeAlsaData(AlsaData * ad)
|
||||
{
|
||||
g_free(ad->device);
|
||||
alsa_mixer_finish(ad->mixer);
|
||||
free(ad);
|
||||
}
|
||||
|
||||
@ -125,8 +132,10 @@ static void *alsa_initDriver(mpd_unused struct audio_output *ao,
|
||||
free_global_registered = 1;
|
||||
}
|
||||
|
||||
if (param)
|
||||
if (param) {
|
||||
alsa_configure(ad, param);
|
||||
alsa_mixer_configure(ad->mixer, param);
|
||||
}
|
||||
|
||||
return ad;
|
||||
}
|
||||
@ -181,6 +190,8 @@ static bool alsa_openDevice(void *data, struct audio_format *audioFormat)
|
||||
unsigned int period_time, period_time_ro;
|
||||
unsigned int buffer_time;
|
||||
|
||||
alsa_mixer_open(ad->mixer);
|
||||
|
||||
if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
|
||||
g_warning("ALSA device \"%s\" doesn't support %u bit audio\n",
|
||||
alsa_device(ad), audioFormat->bits);
|
||||
@ -403,6 +414,7 @@ static void alsa_closeDevice(void *data)
|
||||
snd_pcm_close(ad->pcmHandle);
|
||||
ad->pcmHandle = NULL;
|
||||
}
|
||||
alsa_mixer_close(ad->mixer);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -436,6 +448,13 @@ alsa_playAudio(void *data, const char *playChunk, size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
alsa_control(void *data, int cmd, void *arg)
|
||||
{
|
||||
AlsaData *ad = data;
|
||||
return alsa_mixer_control(ad->mixer, cmd, arg);
|
||||
}
|
||||
|
||||
const struct audio_output_plugin alsaPlugin = {
|
||||
.name = "alsa",
|
||||
.test_default_device = alsa_testDefault,
|
||||
@ -445,4 +464,5 @@ const struct audio_output_plugin alsaPlugin = {
|
||||
.play = alsa_playAudio,
|
||||
.cancel = alsa_dropBufferedAudio,
|
||||
.close = alsa_closeDevice,
|
||||
.control = alsa_control
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "../output_api.h"
|
||||
#include "../mixer.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/stat.h>
|
||||
@ -53,6 +54,7 @@ typedef struct _OssData {
|
||||
int numSupported[3];
|
||||
int *unsupported[3];
|
||||
int numUnsupported[3];
|
||||
struct oss_mixer *mixer;
|
||||
} OssData;
|
||||
|
||||
enum oss_support {
|
||||
@ -273,6 +275,8 @@ static OssData *newOssData(void)
|
||||
supportParam(ret, SNDCTL_DSP_CHANNELS, 2);
|
||||
supportParam(ret, SNDCTL_DSP_SAMPLESIZE, 16);
|
||||
|
||||
ret->mixer = oss_mixer_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -285,6 +289,8 @@ static void freeOssData(OssData * od)
|
||||
g_free(od->unsupported[OSS_CHANNELS]);
|
||||
g_free(od->unsupported[OSS_BITS]);
|
||||
|
||||
oss_mixer_finish(od->mixer);
|
||||
|
||||
free(od);
|
||||
}
|
||||
|
||||
@ -348,6 +354,7 @@ static void *oss_open_default(ConfigParam *param)
|
||||
if (ret[i] == 0) {
|
||||
OssData *od = newOssData();
|
||||
od->device = default_devices[i];
|
||||
oss_mixer_configure(od->mixer, param);
|
||||
return od;
|
||||
}
|
||||
}
|
||||
@ -388,6 +395,7 @@ static void *oss_initDriver(mpd_unused struct audio_output *audioOutput,
|
||||
if (bp) {
|
||||
OssData *od = newOssData();
|
||||
od->device = bp->value;
|
||||
oss_mixer_configure(od->mixer, param);
|
||||
return od;
|
||||
}
|
||||
}
|
||||
@ -513,6 +521,8 @@ oss_openDevice(void *data, struct audio_format *audioFormat)
|
||||
od->audio_format.bits, od->audio_format.channels,
|
||||
od->audio_format.sample_rate);
|
||||
|
||||
oss_mixer_open(od->mixer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -521,6 +531,7 @@ static void oss_closeDevice(void *data)
|
||||
OssData *od = data;
|
||||
|
||||
oss_close(od);
|
||||
oss_mixer_close(od->mixer);
|
||||
}
|
||||
|
||||
static void oss_dropBufferedAudio(void *data)
|
||||
@ -559,6 +570,13 @@ oss_playAudio(void *data, const char *playChunk, size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
oss_control(void *data, int cmd, void *arg)
|
||||
{
|
||||
OssData *od = data;
|
||||
return oss_mixer_control(od->mixer, cmd, arg);
|
||||
}
|
||||
|
||||
const struct audio_output_plugin ossPlugin = {
|
||||
.name = "oss",
|
||||
.test_default_device = oss_testDefault,
|
||||
@ -568,4 +586,5 @@ const struct audio_output_plugin ossPlugin = {
|
||||
.play = oss_playAudio,
|
||||
.cancel = oss_dropBufferedAudio,
|
||||
.close = oss_closeDevice,
|
||||
.control = oss_control,
|
||||
};
|
||||
|
@ -100,6 +100,12 @@ struct audio_output_plugin {
|
||||
*/
|
||||
void (*close)(void *data);
|
||||
|
||||
/**
|
||||
* Control the device. Usualy used for implementing
|
||||
* set and get mixer levels
|
||||
*/
|
||||
bool (*control)(void *data, int cmd, void *arg);
|
||||
|
||||
/**
|
||||
* Display metadata for the next chunk. Optional method,
|
||||
* because not all devices can display metadata.
|
||||
@ -118,6 +124,12 @@ enum audio_output_command {
|
||||
AO_COMMAND_KILL
|
||||
};
|
||||
|
||||
enum audio_control_command {
|
||||
AC_MIXER_GETVOL = 0,
|
||||
AC_MIXER_SETVOL,
|
||||
AC_MIXER_CONFIGURE,
|
||||
};
|
||||
|
||||
struct audio_output;
|
||||
|
||||
const char *audio_output_get_name(const struct audio_output *ao);
|
||||
|
530
src/volume.c
530
src/volume.c
@ -15,475 +15,141 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "volume.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "player_control.h"
|
||||
#include "utils.h"
|
||||
#include "idle.h"
|
||||
#include "pcm_utils.h"
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "volume"
|
||||
|
||||
#define VOLUME_MIXER_TYPE_SOFTWARE 0
|
||||
#define VOLUME_MIXER_TYPE_OSS 1
|
||||
#define VOLUME_MIXER_TYPE_ALSA 2
|
||||
#define VOLUME_MIXER_TYPE_HARDWARE 1
|
||||
|
||||
#define VOLUME_MIXER_SOFTWARE_DEFAULT ""
|
||||
#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer"
|
||||
#define VOLUME_MIXER_ALSA_DEFAULT "default"
|
||||
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
|
||||
#define SW_VOLUME_STATE "sw_volume: "
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_OSS
|
||||
#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_OSS_DEFAULT
|
||||
#else
|
||||
#ifdef HAVE_ALSA
|
||||
#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_ALSA
|
||||
#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_ALSA_DEFAULT
|
||||
#else
|
||||
#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_SOFTWARE
|
||||
#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_SOFTWARE_DEFAULT
|
||||
#endif
|
||||
#endif
|
||||
const struct audio_output_plugin *default_mixer;
|
||||
static int volume_mixer_type = VOLUME_MIXER_TYPE_HARDWARE;
|
||||
static int volume_software_set = 100;
|
||||
|
||||
static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT;
|
||||
static const char *volume_mixerDevice = VOLUME_MIXER_DEVICE_DEFAULT;
|
||||
|
||||
static int volume_softwareSet = 100;
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
static int volume_ossFd = -1;
|
||||
static int volume_ossControl = SOUND_MIXER_PCM;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
static snd_mixer_t *volume_alsaMixerHandle;
|
||||
static snd_mixer_elem_t *volume_alsaElem;
|
||||
static long volume_alsaMin;
|
||||
static long volume_alsaMax;
|
||||
static int volume_alsaSet = -1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
|
||||
static void closeOssMixer(void)
|
||||
void volume_finish(void)
|
||||
{
|
||||
while (close(volume_ossFd) && errno == EINTR) ;
|
||||
volume_ossFd = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
oss_find_mixer(const char *name)
|
||||
static void
|
||||
mixer_reconfigure(char *driver)
|
||||
{
|
||||
const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
|
||||
size_t name_length = strlen(name);
|
||||
ConfigParam *newparam, *param;
|
||||
|
||||
for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (strncasecmp(name, labels[i], name_length) == 0 &&
|
||||
(labels[i][name_length] == 0 ||
|
||||
labels[i][name_length] == ' '))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int prepOssMixer(const char *device)
|
||||
{
|
||||
ConfigParam *param;
|
||||
|
||||
if ((volume_ossFd = open(device, O_RDONLY)) < 0) {
|
||||
g_warning("unable to open oss mixer \"%s\"", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((param = getConfigParam(CONF_MIXER_CONTROL))) {
|
||||
int i;
|
||||
int devmask = 0;
|
||||
|
||||
if (ioctl(volume_ossFd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
|
||||
g_warning("errors getting read_devmask for oss mixer");
|
||||
closeOssMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = oss_find_mixer(param->value);
|
||||
|
||||
if (i < 0) {
|
||||
g_warning("mixer control \"%s\" not found at line %i",
|
||||
param->value, param->line);
|
||||
closeOssMixer();
|
||||
return -1;
|
||||
} else if (!((1 << i) & devmask)) {
|
||||
g_warning("mixer control \"%s\" not usable at line %i",
|
||||
param->value, param->line);
|
||||
closeOssMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
volume_ossControl = i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ensure_oss_open(void)
|
||||
{
|
||||
if ((volume_ossFd < 0 && prepOssMixer(volume_mixerDevice) < 0))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getOssVolumeLevel(void)
|
||||
{
|
||||
int left, right, level;
|
||||
|
||||
if (ensure_oss_open() < 0)
|
||||
return -1;
|
||||
|
||||
if (ioctl(volume_ossFd, MIXER_READ(volume_ossControl), &level) < 0) {
|
||||
closeOssMixer();
|
||||
g_warning("unable to read volume");
|
||||
return -1;
|
||||
}
|
||||
|
||||
left = level & 0xff;
|
||||
right = (level & 0xff00) >> 8;
|
||||
|
||||
if (left != right) {
|
||||
g_warning("volume for left and right is not the same, \"%i\" "
|
||||
"and \"%i\"", left, right);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
static int changeOssVolumeLevel(int change, int rel)
|
||||
{
|
||||
int current;
|
||||
int new;
|
||||
int level;
|
||||
|
||||
if (rel) {
|
||||
if ((current = getOssVolumeLevel()) < 0)
|
||||
return -1;
|
||||
|
||||
new = current + change;
|
||||
} else {
|
||||
if (ensure_oss_open() < 0)
|
||||
return -1;
|
||||
new = change;
|
||||
}
|
||||
|
||||
if (new < 0)
|
||||
new = 0;
|
||||
else if (new > 100)
|
||||
new = 100;
|
||||
|
||||
level = (new << 8) + new;
|
||||
|
||||
if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) {
|
||||
closeOssMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
static void closeAlsaMixer(void)
|
||||
{
|
||||
snd_mixer_close(volume_alsaMixerHandle);
|
||||
volume_alsaMixerHandle = NULL;
|
||||
}
|
||||
|
||||
static int prepAlsaMixer(const char *card)
|
||||
{
|
||||
int err;
|
||||
snd_mixer_elem_t *elem;
|
||||
const char *controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
|
||||
ConfigParam *param;
|
||||
|
||||
err = snd_mixer_open(&volume_alsaMixerHandle, 0);
|
||||
snd_config_update_free_global();
|
||||
if (err < 0) {
|
||||
g_warning("problems opening alsa mixer: %s",
|
||||
snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = snd_mixer_attach(volume_alsaMixerHandle, card)) < 0) {
|
||||
closeAlsaMixer();
|
||||
g_warning("problems attaching alsa mixer: %s",
|
||||
snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err =
|
||||
snd_mixer_selem_register(volume_alsaMixerHandle, NULL,
|
||||
NULL)) < 0) {
|
||||
closeAlsaMixer();
|
||||
g_warning("problems snd_mixer_selem_register'ing: %s",
|
||||
snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = snd_mixer_load(volume_alsaMixerHandle)) < 0) {
|
||||
closeAlsaMixer();
|
||||
g_warning("problems snd_mixer_selem_register'ing: %s",
|
||||
snd_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
elem = snd_mixer_first_elem(volume_alsaMixerHandle);
|
||||
|
||||
param = getConfigParam(CONF_MIXER_CONTROL);
|
||||
|
||||
if (param) {
|
||||
controlName = param->value;
|
||||
}
|
||||
|
||||
while (elem) {
|
||||
if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
|
||||
if (strcasecmp(controlName,
|
||||
snd_mixer_selem_get_name(elem)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elem = snd_mixer_elem_next(elem);
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
volume_alsaElem = elem;
|
||||
snd_mixer_selem_get_playback_volume_range(volume_alsaElem,
|
||||
&volume_alsaMin,
|
||||
&volume_alsaMax);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_warning("can't find alsa mixer_control \"%s\"", controlName);
|
||||
|
||||
closeAlsaMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int prep_alsa_get_level(long *level)
|
||||
{
|
||||
const char *cmd;
|
||||
int err;
|
||||
|
||||
if (!volume_alsaMixerHandle && prepAlsaMixer(volume_mixerDevice) < 0)
|
||||
return -1;
|
||||
|
||||
if ((err = snd_mixer_handle_events(volume_alsaMixerHandle)) < 0) {
|
||||
cmd = "handle_events";
|
||||
goto error;
|
||||
}
|
||||
if ((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
|
||||
SND_MIXER_SCHN_FRONT_LEFT,
|
||||
level)) < 0) {
|
||||
cmd = "selem_get_playback_volume";
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
g_warning("problems getting alsa volume: %s (snd_mixer_%s)",
|
||||
snd_strerror(err), cmd);
|
||||
closeAlsaMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int getAlsaVolumeLevel(void)
|
||||
{
|
||||
int ret;
|
||||
long level;
|
||||
long max = volume_alsaMax;
|
||||
long min = volume_alsaMin;
|
||||
|
||||
if (prep_alsa_get_level(&level) < 0)
|
||||
return -1;
|
||||
|
||||
ret = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
|
||||
if (volume_alsaSet > 0 && ret == level) {
|
||||
ret = volume_alsaSet;
|
||||
} else
|
||||
ret = (int)(100 * (((float)(level - min)) / (max - min)) + 0.5);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int changeAlsaVolumeLevel(int change, int rel)
|
||||
{
|
||||
float vol;
|
||||
long level;
|
||||
long test;
|
||||
long max = volume_alsaMax;
|
||||
long min = volume_alsaMin;
|
||||
int err;
|
||||
|
||||
if (prep_alsa_get_level(&level) < 0)
|
||||
return -1;
|
||||
|
||||
if (rel) {
|
||||
test = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
|
||||
if (volume_alsaSet >= 0 && level == test) {
|
||||
vol = volume_alsaSet;
|
||||
} else
|
||||
vol = 100.0 * (((float)(level - min)) / (max - min));
|
||||
vol += change;
|
||||
} else
|
||||
vol = change;
|
||||
|
||||
volume_alsaSet = vol + 0.5;
|
||||
volume_alsaSet = volume_alsaSet > 100 ? 100 :
|
||||
(volume_alsaSet < 0 ? 0 : volume_alsaSet);
|
||||
|
||||
level = (long)(((vol / 100.0) * (max - min) + min) + 0.5);
|
||||
level = level > max ? max : level;
|
||||
level = level < min ? min : level;
|
||||
|
||||
if ((err =
|
||||
snd_mixer_selem_set_playback_volume_all(volume_alsaElem,
|
||||
level)) < 0) {
|
||||
g_warning("problems setting alsa volume: %s",
|
||||
snd_strerror(err));
|
||||
closeAlsaMixer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int prepMixer(const char *device)
|
||||
{
|
||||
switch (volume_mixerType) {
|
||||
#ifdef HAVE_ALSA
|
||||
case VOLUME_MIXER_TYPE_ALSA:
|
||||
return prepAlsaMixer(device);
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
case VOLUME_MIXER_TYPE_OSS:
|
||||
return prepOssMixer(device);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void finishVolume(void)
|
||||
{
|
||||
switch (volume_mixerType) {
|
||||
#ifdef HAVE_ALSA
|
||||
case VOLUME_MIXER_TYPE_ALSA:
|
||||
closeAlsaMixer();
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
case VOLUME_MIXER_TYPE_OSS:
|
||||
closeOssMixer();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void initVolume(void)
|
||||
{
|
||||
ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
|
||||
|
||||
if (param) {
|
||||
if (0) ;
|
||||
#ifdef HAVE_ALSA
|
||||
else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) {
|
||||
volume_mixerType = VOLUME_MIXER_TYPE_ALSA;
|
||||
volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
else if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) {
|
||||
volume_mixerType = VOLUME_MIXER_TYPE_OSS;
|
||||
volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
else if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) {
|
||||
volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
||||
volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT;
|
||||
} else {
|
||||
g_error("unknown mixer type %s at line %i",
|
||||
param->value, param->line);
|
||||
}
|
||||
}
|
||||
//create parameter list
|
||||
newparam = newConfigParam(NULL, -1);
|
||||
|
||||
param = getConfigParam(CONF_MIXER_DEVICE);
|
||||
|
||||
if (param) {
|
||||
volume_mixerDevice = param->value;
|
||||
g_warning("deprecated option mixer_device found, translating to %s config section\n", driver);
|
||||
addBlockParam(newparam, "mix_device", param->value, -1);
|
||||
}
|
||||
param = getConfigParam(CONF_MIXER_CONTROL);
|
||||
if (param) {
|
||||
g_warning("deprecated option mixer_control found, translating to %s config section\n", driver);
|
||||
addBlockParam(newparam, "mix_control", param->value, -1);
|
||||
}
|
||||
if (newparam->numberOfBlockParams > 0) {
|
||||
//call configure method of corrensponding mixer
|
||||
if (!mixer_configure_legacy(driver, newparam)) {
|
||||
g_error("Using mixer_type '%s' with not enabled %s output", driver, driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openVolumeDevice(void)
|
||||
void volume_init(void)
|
||||
{
|
||||
if (prepMixer(volume_mixerDevice) < 0) {
|
||||
g_message("using software volume");
|
||||
volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
||||
ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
|
||||
//hw mixing is by default
|
||||
if (param) {
|
||||
if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) {
|
||||
volume_mixer_type = VOLUME_MIXER_TYPE_SOFTWARE;
|
||||
} else if (strcmp(param->value, VOLUME_MIXER_HARDWARE) == 0) {
|
||||
//nothing to do
|
||||
} else {
|
||||
//fallback to old config behaviour
|
||||
if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) {
|
||||
mixer_reconfigure(param->value);
|
||||
} else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) {
|
||||
mixer_reconfigure(param->value);
|
||||
} else {
|
||||
g_error("unknown mixer type %s at line %i\n",
|
||||
param->value, param->line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getSoftwareVolume(void)
|
||||
static int hardware_volume_get(void)
|
||||
{
|
||||
return volume_softwareSet;
|
||||
int device, count;
|
||||
int volume, volume_total, volume_ok;
|
||||
|
||||
volume_total = 0;
|
||||
volume_ok = 0;
|
||||
|
||||
count = audio_output_count();
|
||||
for (device=0; device<count ;device++) {
|
||||
if (mixer_control_getvol(device, &volume)) {
|
||||
g_debug("device %d: volume: %d\n", device, volume);
|
||||
volume_total += volume;
|
||||
volume_ok++;
|
||||
}
|
||||
}
|
||||
if (volume_ok > 0) {
|
||||
//return average
|
||||
return volume_total / volume_ok;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int getVolumeLevel(void)
|
||||
static int software_volume_get(void)
|
||||
{
|
||||
switch (volume_mixerType) {
|
||||
#ifdef HAVE_ALSA
|
||||
case VOLUME_MIXER_TYPE_ALSA:
|
||||
return getAlsaVolumeLevel();
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
case VOLUME_MIXER_TYPE_OSS:
|
||||
return getOssVolumeLevel();
|
||||
#endif
|
||||
return volume_software_set;
|
||||
}
|
||||
|
||||
int volume_level_get(void)
|
||||
{
|
||||
switch (volume_mixer_type) {
|
||||
case VOLUME_MIXER_TYPE_SOFTWARE:
|
||||
return getSoftwareVolume();
|
||||
return software_volume_get();
|
||||
case VOLUME_MIXER_TYPE_HARDWARE:
|
||||
return hardware_volume_get();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int changeSoftwareVolume(int change, int rel)
|
||||
static int software_volume_change(int change, int rel)
|
||||
{
|
||||
int new = change;
|
||||
|
||||
if (rel)
|
||||
new += volume_softwareSet;
|
||||
new += volume_software_set;
|
||||
|
||||
if (new > 100)
|
||||
new = 100;
|
||||
else if (new < 0)
|
||||
new = 0;
|
||||
|
||||
volume_softwareSet = new;
|
||||
volume_software_set = new;
|
||||
|
||||
/*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */
|
||||
if (new >= 100)
|
||||
@ -499,21 +165,26 @@ static int changeSoftwareVolume(int change, int rel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int changeVolumeLevel(int change, int rel)
|
||||
static int hardware_volume_change(int change, int rel)
|
||||
{
|
||||
int device, count;
|
||||
|
||||
count = audio_output_count();
|
||||
for (device=0; device<count ;device++) {
|
||||
mixer_control_setvol(device, change, rel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int volume_level_change(int change, int rel)
|
||||
{
|
||||
idle_add(IDLE_MIXER);
|
||||
|
||||
switch (volume_mixerType) {
|
||||
#ifdef HAVE_ALSA
|
||||
case VOLUME_MIXER_TYPE_ALSA:
|
||||
return changeAlsaVolumeLevel(change, rel);
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
case VOLUME_MIXER_TYPE_OSS:
|
||||
return changeOssVolumeLevel(change, rel);
|
||||
#endif
|
||||
switch (volume_mixer_type) {
|
||||
case VOLUME_MIXER_TYPE_HARDWARE:
|
||||
return hardware_volume_change(change, rel);
|
||||
case VOLUME_MIXER_TYPE_SOFTWARE:
|
||||
return changeSoftwareVolume(change, rel);
|
||||
return software_volume_change(change, rel);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -525,7 +196,7 @@ void read_sw_volume_state(FILE *fp)
|
||||
char *end = NULL;
|
||||
long int sv;
|
||||
|
||||
if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE)
|
||||
if (volume_mixer_type != VOLUME_MIXER_TYPE_SOFTWARE)
|
||||
return;
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!g_str_has_prefix(buf, SW_VOLUME_STATE))
|
||||
@ -534,16 +205,15 @@ void read_sw_volume_state(FILE *fp)
|
||||
g_strchomp(buf);
|
||||
sv = strtol(buf + strlen(SW_VOLUME_STATE), &end, 10);
|
||||
if (G_LIKELY(!*end))
|
||||
changeSoftwareVolume(sv, 0);
|
||||
software_volume_change(sv, 0);
|
||||
else
|
||||
g_warning("Can't parse software volume: %s", buf);
|
||||
g_warning("Can't parse software volume: %s\n", buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void save_sw_volume_state(FILE *fp)
|
||||
{
|
||||
if (volume_mixerType == VOLUME_MIXER_TYPE_SOFTWARE)
|
||||
fprintf(fp, SW_VOLUME_STATE "%d\n", volume_softwareSet);
|
||||
if (volume_mixer_type == VOLUME_MIXER_TYPE_SOFTWARE)
|
||||
fprintf(fp, SW_VOLUME_STATE "%d\n", volume_software_set);
|
||||
}
|
||||
|
||||
|
11
src/volume.h
11
src/volume.h
@ -24,16 +24,15 @@
|
||||
#define VOLUME_MIXER_OSS "oss"
|
||||
#define VOLUME_MIXER_ALSA "alsa"
|
||||
#define VOLUME_MIXER_SOFTWARE "software"
|
||||
#define VOLUME_MIXER_HARDWARE "hardware"
|
||||
|
||||
void initVolume(void);
|
||||
void volume_init(void);
|
||||
|
||||
void openVolumeDevice(void);
|
||||
void volume_finish(void);
|
||||
|
||||
void finishVolume(void);
|
||||
int volume_level_get(void);
|
||||
|
||||
int getVolumeLevel(void);
|
||||
|
||||
int changeVolumeLevel(int change, int rel);
|
||||
int volume_level_change(int change, int rel);
|
||||
|
||||
void read_sw_volume_state(FILE *fp);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user