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?/??/??)
|
ver 0.15 - (200?/??/??)
|
||||||
|
* Rewritten mixer code to support multiple mixers
|
||||||
* Add audio archive extraction support:
|
* Add audio archive extraction support:
|
||||||
- bzip2
|
- bzip2
|
||||||
- iso9660
|
- iso9660
|
||||||
|
@ -157,18 +157,23 @@ Linear interpolator, very fast, poor quality.
|
|||||||
For an up-to-date list of available converters, please see the libsamplerate
|
For an up-to-date list of available converters, please see the libsamplerate
|
||||||
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
|
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
|
||||||
.TP
|
.TP
|
||||||
.B mixer_type <oss, alsa or software>
|
.B mixer_type <alsa, oss, software or hardware>
|
||||||
This specifies which mixer to use. The default depends on what audio output
|
This specifies which mixer to use. The default is hardware and depends on
|
||||||
support mpd was built with.
|
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
|
.TP
|
||||||
.B mixer_device <mixer dev>
|
.B mixer_device <mixer dev>
|
||||||
This specifies which mixer to use. The default for oss is "/dev/mixer"; the
|
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
|
.TP
|
||||||
.B mixer_control <mixer ctrl>
|
.B mixer_control <mixer ctrl>
|
||||||
This specifies which mixer control to use (sometimes referred to as the
|
This specifies which mixer control to use (sometimes referred to as the
|
||||||
"device"). Examples of mixer controls are PCM, Line1, Master, etc. An example
|
"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
|
.TP
|
||||||
.B replaygain <album or track>
|
.B replaygain <album or track>
|
||||||
If specified, mpd will adjust the volume of songs played using ReplayGain tags
|
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>
|
.B device <dev>
|
||||||
This specifies the device to use for audio output. The default is "default".
|
This specifies the device to use for audio output. The default is "default".
|
||||||
.TP
|
.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>
|
.B use_mmap <yes or no>
|
||||||
Setting this allows you to use memory-mapped I/O. Certain hardware setups may
|
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
|
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"
|
# name "My ALSA Device"
|
||||||
# device "hw:0,0" # optional
|
# device "hw:0,0" # optional
|
||||||
# format "44100:16:2" # optional
|
# format "44100:16:2" # optional
|
||||||
|
# mix_device "default" # optional
|
||||||
|
# mix_control "PCM" # optional
|
||||||
#}
|
#}
|
||||||
#
|
#
|
||||||
# An example of an OSS output:
|
# An example of an OSS output:
|
||||||
@ -171,6 +173,8 @@ log_file "~/.mpd/log"
|
|||||||
# name "My OSS Device"
|
# name "My OSS Device"
|
||||||
# device "/dev/dsp" # optional
|
# device "/dev/dsp" # optional
|
||||||
# format "44100:16:2" # 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):
|
# 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
|
# specified it may be autodetected at startup, depending on the dependencies
|
||||||
# which were compiled into the server.
|
# 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_type "hardware"
|
||||||
#mixer_device "default"
|
|
||||||
#mixer_control "PCM"
|
|
||||||
#
|
|
||||||
# An example for controlling an OSS mixer:
|
|
||||||
#
|
|
||||||
#mixer_type "oss"
|
|
||||||
#mixer_device "/dev/mixer"
|
|
||||||
#mixer_control "PCM"
|
|
||||||
#
|
#
|
||||||
# This example is a general volume control mixer, it is used to adjust the
|
# 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.
|
# volume of the audio sent to the audio output, and will work with all outputs.
|
||||||
|
@ -273,6 +273,7 @@ endif
|
|||||||
|
|
||||||
if HAVE_ALSA
|
if HAVE_ALSA
|
||||||
mpd_SOURCES += output/alsa_plugin.c
|
mpd_SOURCES += output/alsa_plugin.c
|
||||||
|
mpd_SOURCES += mixer/alsa_mixer.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_AO
|
if HAVE_AO
|
||||||
@ -293,6 +294,7 @@ endif
|
|||||||
|
|
||||||
if HAVE_OSS
|
if HAVE_OSS
|
||||||
mpd_SOURCES += output/oss_plugin.c
|
mpd_SOURCES += output/oss_plugin.c
|
||||||
|
mpd_SOURCES += mixer/oss_mixer.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_OSX
|
if HAVE_OSX
|
||||||
@ -315,7 +317,6 @@ if HAVE_SHOUT_OGG
|
|||||||
mpd_SOURCES += output/shout_ogg.c
|
mpd_SOURCES += output/shout_ogg.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
mpd_CFLAGS = $(MPD_CFLAGS)
|
mpd_CFLAGS = $(MPD_CFLAGS)
|
||||||
mpd_CPPFLAGS = \
|
mpd_CPPFLAGS = \
|
||||||
$(CURL_CFLAGS) \
|
$(CURL_CFLAGS) \
|
||||||
|
55
src/audio.c
55
src/audio.c
@ -25,6 +25,7 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "idle.h"
|
#include "idle.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "mixer.h"
|
||||||
|
|
||||||
#include <glib.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 <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
#define AUDIO_AO_DRIVER_DEFAULT "default"
|
#define AUDIO_AO_DRIVER_DEFAULT "default"
|
||||||
|
|
||||||
@ -70,4 +71,8 @@ void readAudioDevicesState(FILE *fp);
|
|||||||
|
|
||||||
void saveAudioDevicesState(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
|
#endif
|
||||||
|
@ -388,7 +388,7 @@ handle_status(struct client *client,
|
|||||||
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
|
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
|
||||||
COMMAND_STATUS_CROSSFADE ": %i\n"
|
COMMAND_STATUS_CROSSFADE ": %i\n"
|
||||||
COMMAND_STATUS_STATE ": %s\n",
|
COMMAND_STATUS_STATE ": %s\n",
|
||||||
getVolumeLevel(),
|
volume_level_get(),
|
||||||
getPlaylistRepeatStatus(),
|
getPlaylistRepeatStatus(),
|
||||||
getPlaylistRandomStatus(),
|
getPlaylistRandomStatus(),
|
||||||
getPlaylistVersion(),
|
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))
|
if (!check_int(client, &change, argv[1], need_integer))
|
||||||
return COMMAND_RETURN_ERROR;
|
return COMMAND_RETURN_ERROR;
|
||||||
|
|
||||||
ret = changeVolumeLevel(change, 1);
|
ret = volume_level_change(change, 1);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
command_error(client, ACK_ERROR_SYSTEM,
|
||||||
"problems setting volume");
|
"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))
|
if (!check_int(client, &level, argv[1], need_integer))
|
||||||
return COMMAND_RETURN_ERROR;
|
return COMMAND_RETURN_ERROR;
|
||||||
|
|
||||||
ret = changeVolumeLevel(level, 0);
|
ret = volume_level_change(level, 0);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
command_error(client, ACK_ERROR_SYSTEM,
|
||||||
"problems setting volume");
|
"problems setting volume");
|
||||||
|
@ -264,7 +264,7 @@ int main(int argc, char *argv[])
|
|||||||
dc_init();
|
dc_init();
|
||||||
initAudioConfig();
|
initAudioConfig();
|
||||||
initAudioDriver();
|
initAudioDriver();
|
||||||
initVolume();
|
volume_init();
|
||||||
client_manager_init();
|
client_manager_init();
|
||||||
replay_gain_global_init();
|
replay_gain_global_init();
|
||||||
initNormalization();
|
initNormalization();
|
||||||
@ -278,7 +278,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
initZeroconf();
|
initZeroconf();
|
||||||
|
|
||||||
openVolumeDevice();
|
|
||||||
decoder_thread_start();
|
decoder_thread_start();
|
||||||
player_create();
|
player_create();
|
||||||
read_state_file();
|
read_state_file();
|
||||||
@ -315,7 +314,7 @@ int main(int argc, char *argv[])
|
|||||||
finishNormalization();
|
finishNormalization();
|
||||||
finishAudioDriver();
|
finishAudioDriver();
|
||||||
finishAudioConfig();
|
finishAudioConfig();
|
||||||
finishVolume();
|
volume_finish();
|
||||||
mapper_finish();
|
mapper_finish();
|
||||||
path_global_finish();
|
path_global_finish();
|
||||||
finishPermissions();
|
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 "../output_api.h"
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
|
#include "../mixer.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
@ -51,6 +52,9 @@ typedef struct _AlsaData {
|
|||||||
unsigned int period_time;
|
unsigned int period_time;
|
||||||
int sampleSize;
|
int sampleSize;
|
||||||
int useMmap;
|
int useMmap;
|
||||||
|
|
||||||
|
struct alsa_mixer *mixer;
|
||||||
|
|
||||||
} AlsaData;
|
} AlsaData;
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
@ -71,12 +75,15 @@ static AlsaData *newAlsaData(void)
|
|||||||
ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
|
ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
|
||||||
ret->period_time = 0;
|
ret->period_time = 0;
|
||||||
|
|
||||||
|
ret->mixer = alsa_mixer_init();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeAlsaData(AlsaData * ad)
|
static void freeAlsaData(AlsaData * ad)
|
||||||
{
|
{
|
||||||
g_free(ad->device);
|
g_free(ad->device);
|
||||||
|
alsa_mixer_finish(ad->mixer);
|
||||||
free(ad);
|
free(ad);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +132,10 @@ static void *alsa_initDriver(mpd_unused struct audio_output *ao,
|
|||||||
free_global_registered = 1;
|
free_global_registered = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param)
|
if (param) {
|
||||||
alsa_configure(ad, param);
|
alsa_configure(ad, param);
|
||||||
|
alsa_mixer_configure(ad->mixer, param);
|
||||||
|
}
|
||||||
|
|
||||||
return ad;
|
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 period_time, period_time_ro;
|
||||||
unsigned int buffer_time;
|
unsigned int buffer_time;
|
||||||
|
|
||||||
|
alsa_mixer_open(ad->mixer);
|
||||||
|
|
||||||
if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
|
if ((bitformat = get_bitformat(audioFormat)) == SND_PCM_FORMAT_UNKNOWN)
|
||||||
g_warning("ALSA device \"%s\" doesn't support %u bit audio\n",
|
g_warning("ALSA device \"%s\" doesn't support %u bit audio\n",
|
||||||
alsa_device(ad), audioFormat->bits);
|
alsa_device(ad), audioFormat->bits);
|
||||||
@ -403,6 +414,7 @@ static void alsa_closeDevice(void *data)
|
|||||||
snd_pcm_close(ad->pcmHandle);
|
snd_pcm_close(ad->pcmHandle);
|
||||||
ad->pcmHandle = NULL;
|
ad->pcmHandle = NULL;
|
||||||
}
|
}
|
||||||
|
alsa_mixer_close(ad->mixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -436,6 +448,13 @@ alsa_playAudio(void *data, const char *playChunk, size_t size)
|
|||||||
return true;
|
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 = {
|
const struct audio_output_plugin alsaPlugin = {
|
||||||
.name = "alsa",
|
.name = "alsa",
|
||||||
.test_default_device = alsa_testDefault,
|
.test_default_device = alsa_testDefault,
|
||||||
@ -445,4 +464,5 @@ const struct audio_output_plugin alsaPlugin = {
|
|||||||
.play = alsa_playAudio,
|
.play = alsa_playAudio,
|
||||||
.cancel = alsa_dropBufferedAudio,
|
.cancel = alsa_dropBufferedAudio,
|
||||||
.close = alsa_closeDevice,
|
.close = alsa_closeDevice,
|
||||||
|
.control = alsa_control
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../output_api.h"
|
#include "../output_api.h"
|
||||||
|
#include "../mixer.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -53,6 +54,7 @@ typedef struct _OssData {
|
|||||||
int numSupported[3];
|
int numSupported[3];
|
||||||
int *unsupported[3];
|
int *unsupported[3];
|
||||||
int numUnsupported[3];
|
int numUnsupported[3];
|
||||||
|
struct oss_mixer *mixer;
|
||||||
} OssData;
|
} OssData;
|
||||||
|
|
||||||
enum oss_support {
|
enum oss_support {
|
||||||
@ -273,6 +275,8 @@ static OssData *newOssData(void)
|
|||||||
supportParam(ret, SNDCTL_DSP_CHANNELS, 2);
|
supportParam(ret, SNDCTL_DSP_CHANNELS, 2);
|
||||||
supportParam(ret, SNDCTL_DSP_SAMPLESIZE, 16);
|
supportParam(ret, SNDCTL_DSP_SAMPLESIZE, 16);
|
||||||
|
|
||||||
|
ret->mixer = oss_mixer_init();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +289,8 @@ static void freeOssData(OssData * od)
|
|||||||
g_free(od->unsupported[OSS_CHANNELS]);
|
g_free(od->unsupported[OSS_CHANNELS]);
|
||||||
g_free(od->unsupported[OSS_BITS]);
|
g_free(od->unsupported[OSS_BITS]);
|
||||||
|
|
||||||
|
oss_mixer_finish(od->mixer);
|
||||||
|
|
||||||
free(od);
|
free(od);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +354,7 @@ static void *oss_open_default(ConfigParam *param)
|
|||||||
if (ret[i] == 0) {
|
if (ret[i] == 0) {
|
||||||
OssData *od = newOssData();
|
OssData *od = newOssData();
|
||||||
od->device = default_devices[i];
|
od->device = default_devices[i];
|
||||||
|
oss_mixer_configure(od->mixer, param);
|
||||||
return od;
|
return od;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,6 +395,7 @@ static void *oss_initDriver(mpd_unused struct audio_output *audioOutput,
|
|||||||
if (bp) {
|
if (bp) {
|
||||||
OssData *od = newOssData();
|
OssData *od = newOssData();
|
||||||
od->device = bp->value;
|
od->device = bp->value;
|
||||||
|
oss_mixer_configure(od->mixer, param);
|
||||||
return od;
|
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.bits, od->audio_format.channels,
|
||||||
od->audio_format.sample_rate);
|
od->audio_format.sample_rate);
|
||||||
|
|
||||||
|
oss_mixer_open(od->mixer);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +531,7 @@ static void oss_closeDevice(void *data)
|
|||||||
OssData *od = data;
|
OssData *od = data;
|
||||||
|
|
||||||
oss_close(od);
|
oss_close(od);
|
||||||
|
oss_mixer_close(od->mixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void oss_dropBufferedAudio(void *data)
|
static void oss_dropBufferedAudio(void *data)
|
||||||
@ -559,6 +570,13 @@ oss_playAudio(void *data, const char *playChunk, size_t size)
|
|||||||
return true;
|
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 = {
|
const struct audio_output_plugin ossPlugin = {
|
||||||
.name = "oss",
|
.name = "oss",
|
||||||
.test_default_device = oss_testDefault,
|
.test_default_device = oss_testDefault,
|
||||||
@ -568,4 +586,5 @@ const struct audio_output_plugin ossPlugin = {
|
|||||||
.play = oss_playAudio,
|
.play = oss_playAudio,
|
||||||
.cancel = oss_dropBufferedAudio,
|
.cancel = oss_dropBufferedAudio,
|
||||||
.close = oss_closeDevice,
|
.close = oss_closeDevice,
|
||||||
|
.control = oss_control,
|
||||||
};
|
};
|
||||||
|
@ -100,6 +100,12 @@ struct audio_output_plugin {
|
|||||||
*/
|
*/
|
||||||
void (*close)(void *data);
|
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,
|
* Display metadata for the next chunk. Optional method,
|
||||||
* because not all devices can display metadata.
|
* because not all devices can display metadata.
|
||||||
@ -118,6 +124,12 @@ enum audio_output_command {
|
|||||||
AO_COMMAND_KILL
|
AO_COMMAND_KILL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum audio_control_command {
|
||||||
|
AC_MIXER_GETVOL = 0,
|
||||||
|
AC_MIXER_SETVOL,
|
||||||
|
AC_MIXER_CONFIGURE,
|
||||||
|
};
|
||||||
|
|
||||||
struct audio_output;
|
struct audio_output;
|
||||||
|
|
||||||
const char *audio_output_get_name(const struct audio_output *ao);
|
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
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "volume.h"
|
#include "volume.h"
|
||||||
|
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "idle.h"
|
#include "idle.h"
|
||||||
#include "pcm_utils.h"
|
#include "pcm_utils.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "audio.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.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
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "volume"
|
#define G_LOG_DOMAIN "volume"
|
||||||
|
|
||||||
#define VOLUME_MIXER_TYPE_SOFTWARE 0
|
#define VOLUME_MIXER_TYPE_SOFTWARE 0
|
||||||
#define VOLUME_MIXER_TYPE_OSS 1
|
#define VOLUME_MIXER_TYPE_HARDWARE 1
|
||||||
#define VOLUME_MIXER_TYPE_ALSA 2
|
|
||||||
|
|
||||||
#define VOLUME_MIXER_SOFTWARE_DEFAULT ""
|
#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: "
|
#define SW_VOLUME_STATE "sw_volume: "
|
||||||
|
|
||||||
#ifdef HAVE_OSS
|
const struct audio_output_plugin *default_mixer;
|
||||||
#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_OSS
|
static int volume_mixer_type = VOLUME_MIXER_TYPE_HARDWARE;
|
||||||
#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_OSS_DEFAULT
|
static int volume_software_set = 100;
|
||||||
#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
|
|
||||||
|
|
||||||
static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT;
|
void volume_finish(void)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
while (close(volume_ossFd) && errno == EINTR) ;
|
|
||||||
volume_ossFd = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void
|
||||||
oss_find_mixer(const char *name)
|
mixer_reconfigure(char *driver)
|
||||||
{
|
{
|
||||||
const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
|
ConfigParam *newparam, *param;
|
||||||
size_t name_length = strlen(name);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
//create parameter list
|
||||||
if (strncasecmp(name, labels[i], name_length) == 0 &&
|
newparam = newConfigParam(NULL, -1);
|
||||||
(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
param = getConfigParam(CONF_MIXER_DEVICE);
|
param = getConfigParam(CONF_MIXER_DEVICE);
|
||||||
|
|
||||||
if (param) {
|
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) {
|
ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
|
||||||
g_message("using software volume");
|
//hw mixing is by default
|
||||||
volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
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) {
|
return volume_software_set;
|
||||||
#ifdef HAVE_ALSA
|
}
|
||||||
case VOLUME_MIXER_TYPE_ALSA:
|
|
||||||
return getAlsaVolumeLevel();
|
int volume_level_get(void)
|
||||||
#endif
|
{
|
||||||
#ifdef HAVE_OSS
|
switch (volume_mixer_type) {
|
||||||
case VOLUME_MIXER_TYPE_OSS:
|
|
||||||
return getOssVolumeLevel();
|
|
||||||
#endif
|
|
||||||
case VOLUME_MIXER_TYPE_SOFTWARE:
|
case VOLUME_MIXER_TYPE_SOFTWARE:
|
||||||
return getSoftwareVolume();
|
return software_volume_get();
|
||||||
|
case VOLUME_MIXER_TYPE_HARDWARE:
|
||||||
|
return hardware_volume_get();
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int changeSoftwareVolume(int change, int rel)
|
static int software_volume_change(int change, int rel)
|
||||||
{
|
{
|
||||||
int new = change;
|
int new = change;
|
||||||
|
|
||||||
if (rel)
|
if (rel)
|
||||||
new += volume_softwareSet;
|
new += volume_software_set;
|
||||||
|
|
||||||
if (new > 100)
|
if (new > 100)
|
||||||
new = 100;
|
new = 100;
|
||||||
else if (new < 0)
|
else if (new < 0)
|
||||||
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; */
|
/*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */
|
||||||
if (new >= 100)
|
if (new >= 100)
|
||||||
@ -499,21 +165,26 @@ static int changeSoftwareVolume(int change, int rel)
|
|||||||
return 0;
|
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);
|
idle_add(IDLE_MIXER);
|
||||||
|
|
||||||
switch (volume_mixerType) {
|
switch (volume_mixer_type) {
|
||||||
#ifdef HAVE_ALSA
|
case VOLUME_MIXER_TYPE_HARDWARE:
|
||||||
case VOLUME_MIXER_TYPE_ALSA:
|
return hardware_volume_change(change, rel);
|
||||||
return changeAlsaVolumeLevel(change, rel);
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_OSS
|
|
||||||
case VOLUME_MIXER_TYPE_OSS:
|
|
||||||
return changeOssVolumeLevel(change, rel);
|
|
||||||
#endif
|
|
||||||
case VOLUME_MIXER_TYPE_SOFTWARE:
|
case VOLUME_MIXER_TYPE_SOFTWARE:
|
||||||
return changeSoftwareVolume(change, rel);
|
return software_volume_change(change, rel);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -525,7 +196,7 @@ void read_sw_volume_state(FILE *fp)
|
|||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
long int sv;
|
long int sv;
|
||||||
|
|
||||||
if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE)
|
if (volume_mixer_type != VOLUME_MIXER_TYPE_SOFTWARE)
|
||||||
return;
|
return;
|
||||||
while (fgets(buf, sizeof(buf), fp)) {
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
if (!g_str_has_prefix(buf, SW_VOLUME_STATE))
|
if (!g_str_has_prefix(buf, SW_VOLUME_STATE))
|
||||||
@ -534,16 +205,15 @@ void read_sw_volume_state(FILE *fp)
|
|||||||
g_strchomp(buf);
|
g_strchomp(buf);
|
||||||
sv = strtol(buf + strlen(SW_VOLUME_STATE), &end, 10);
|
sv = strtol(buf + strlen(SW_VOLUME_STATE), &end, 10);
|
||||||
if (G_LIKELY(!*end))
|
if (G_LIKELY(!*end))
|
||||||
changeSoftwareVolume(sv, 0);
|
software_volume_change(sv, 0);
|
||||||
else
|
else
|
||||||
g_warning("Can't parse software volume: %s", buf);
|
g_warning("Can't parse software volume: %s\n", buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void save_sw_volume_state(FILE *fp)
|
void save_sw_volume_state(FILE *fp)
|
||||||
{
|
{
|
||||||
if (volume_mixerType == VOLUME_MIXER_TYPE_SOFTWARE)
|
if (volume_mixer_type == VOLUME_MIXER_TYPE_SOFTWARE)
|
||||||
fprintf(fp, SW_VOLUME_STATE "%d\n", volume_softwareSet);
|
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_OSS "oss"
|
||||||
#define VOLUME_MIXER_ALSA "alsa"
|
#define VOLUME_MIXER_ALSA "alsa"
|
||||||
#define VOLUME_MIXER_SOFTWARE "software"
|
#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 volume_level_change(int change, int rel);
|
||||||
|
|
||||||
int changeVolumeLevel(int change, int rel);
|
|
||||||
|
|
||||||
void read_sw_volume_state(FILE *fp);
|
void read_sw_volume_state(FILE *fp);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user