Commit 9a70c4d0 authored by Viliam Mateicka's avatar Viliam Mateicka

Moving mixers to audio outputs

parent dd9af72a
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. ...@@ -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. ...@@ -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" ...@@ -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" ...@@ -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" ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) \
......
...@@ -25,6 +25,7 @@ ...@@ -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: ...@@ -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 @@ ...@@ -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); ...@@ -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, ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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();
......
#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
#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;
}
#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 @@ ...@@ -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 { ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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,
}; };
...@@ -101,6 +101,12 @@ struct audio_output_plugin { ...@@ -101,6 +101,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 { ...@@ -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);
......
...@@ -24,16 +24,15 @@ ...@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment