Moving mixers to audio outputs

This commit is contained in:
Viliam Mateicka
2008-12-31 16:46:41 +01:00
parent dd9af72a74
commit 9a70c4d06d
16 changed files with 686 additions and 459 deletions
+2 -1
View File
@@ -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
View File
@@ -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;
}
+5
View File
@@ -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
+3 -3
View File
@@ -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");
+2 -3
View File
@@ -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
View 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
View 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
View 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;
}
+21 -1
View File
@@ -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
};
+19
View File
@@ -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,
};
+12
View File
@@ -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);
+100 -430
View File
@@ -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);
}
+5 -6
View File
@@ -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);