Add native ALSA mixer support

This commit is contained in:
Johannes Holmberg 2015-09-16 21:19:41 -04:00 committed by Carlos R. Mafra
parent 5a441cd6de
commit 96ec41a88f
9 changed files with 654 additions and 103 deletions

View file

@ -1,8 +1,8 @@
CC = gcc CC = gcc
CFLAGS = -O3 -W -Wall CFLAGS = -std=gnu99 -O3 -W -Wall `pkg-config --cflags alsa`
LDFLAGS = -L/usr/X11R6/lib LDFLAGS = -L/usr/X11R6/lib
LIBS = -lXpm -lXext -lX11 -lm LIBS = -lXpm -lXext -lX11 -lm `pkg-config --libs alsa`
OBJECTS = misc.o config.o mixer-oss.o ui_x.o mmkeys.o wmix.o OBJECTS = misc.o config.o mixer-alsa.o mixer-oss.o ui_x.o mmkeys.o wmix.o
# where to install this program (also for packaging stuff) # where to install this program (also for packaging stuff)
PREFIX = /usr/local PREFIX = /usr/local

View file

@ -30,19 +30,21 @@
#include "include/common.h" #include "include/common.h"
#include "include/config.h" #include "include/config.h"
#include "include/misc.h"
#define VERSION_TEXT \ #define VERSION_TEXT \
"WMixer " VERSION " by timecop@japan.co.jp + skunk@mit.edu\n" "WMixer " VERSION " by timecop@japan.co.jp + skunk@mit.edu\n"
#define HELP_TEXT \ #define HELP_TEXT \
"usage:\n" \ "usage:\n" \
" -a <api> use this sound api (oss or alsa) [alsa]\n" \
" -d <dsp> connect to remote X display\n" \ " -d <dsp> connect to remote X display\n" \
" -e <name> exclude channel, can be used many times\n" \ " -e <name> exclude channel, can be used many times\n" \
" -f <file> parse this config [~/.wmixrc]\n" \ " -f <file> parse this config [~/.wmixrc]\n" \
" -h print this help\n" \ " -h print this help\n" \
" -k disable grabing volume control keys\n" \ " -k disable grabing volume control keys\n" \
" -m <dev> mixer device [/dev/mixer]\n" \ " -m <dev> oss mixer device [/dev/mixer]\n" \
" or alsa card name [default]\n" \
" -v verbose -> id, long name, name\n" \ " -v verbose -> id, long name, name\n" \
/* The global configuration */ /* The global configuration */
@ -50,7 +52,7 @@ struct _Config config;
/* The default device used for Mixer control */ /* The default device used for Mixer control */
static const char default_mixer_device[] = "/dev/mixer"; static const char default_mixer_device[] = "/dev/mixer";
static const char default_card_name[] = "default";
/* Default color for OSD */ /* Default color for OSD */
const char default_osd_color[] = "green"; const char default_osd_color[] = "green";
@ -61,8 +63,8 @@ const char default_osd_color[] = "green";
void config_init(void) void config_init(void)
{ {
memset(&config, 0, sizeof(config)); memset(&config, 0, sizeof(config));
config.api = 0;
config.mixer_device = (char *) default_mixer_device; config.mixer_device = NULL;
config.mousewheel = 1; config.mousewheel = 1;
config.scrolltext = 1; config.scrolltext = 1;
config.mmkeys = 1; config.mmkeys = 1;
@ -89,13 +91,14 @@ void config_release(void)
if (config.display_name) if (config.display_name)
free(config.display_name); free(config.display_name);
if (config.mixer_device != default_mixer_device) if (config.mixer_device != default_mixer_device
&& config.mixer_device != default_card_name)
free(config.mixer_device); free(config.mixer_device);
if (config.osd_color != default_osd_color) if (config.osd_color != default_osd_color)
free(config.osd_color); free(config.osd_color);
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { for (i = 0; i < EXCLUDE_MAX_COUNT; i++) {
if (config.exclude_channel[i]) if (config.exclude_channel[i])
free(config.exclude_channel[i]); free(config.exclude_channel[i]);
else else
@ -119,7 +122,7 @@ void parse_cli_options(int argc, char **argv)
config.verbose = false; config.verbose = false;
error_found = false; error_found = false;
for (;;) { for (;;) {
opt = getopt(argc, argv, ":d:e:f:hkm:v"); opt = getopt(argc, argv, ":a:d:e:f:hkm:v");
if (opt == -1) if (opt == -1)
break; break;
@ -133,7 +136,12 @@ void parse_cli_options(int argc, char **argv)
fprintf(stderr, "wmix:error: missing argument for option '-%c'\n", optopt); fprintf(stderr, "wmix:error: missing argument for option '-%c'\n", optopt);
error_found = true; error_found = true;
break; break;
case 'a':
if(!strcmp("oss", optarg))
config.api = 1;
else if (strcmp("alsa", optarg))
fprintf(stderr, "Warning: Incorrect sound api specified, defaulting to alsa\n");
break;
case 'd': case 'd':
if (config.display_name) if (config.display_name)
free(config.display_name); free(config.display_name);
@ -141,7 +149,7 @@ void parse_cli_options(int argc, char **argv)
break; break;
case 'e': case 'e':
if (count_exclude < SOUND_MIXER_NRDEVICES) { if (count_exclude < EXCLUDE_MAX_COUNT) {
config.exclude_channel[count_exclude] = strdup(optarg); config.exclude_channel[count_exclude] = strdup(optarg);
count_exclude++; count_exclude++;
} else } else
@ -180,6 +188,13 @@ void parse_cli_options(int argc, char **argv)
} }
config.exclude_channel[count_exclude] = NULL; config.exclude_channel[count_exclude] = NULL;
if (!config.mixer_device) {
if (config.api == 0)
config.mixer_device = (char *)default_card_name;
else if (config.api == 1)
config.mixer_device = (char *)default_mixer_device;
}
if (optind < argc) { if (optind < argc) {
fprintf(stderr, "wmix:error: argument '%s' not understood\n", argv[optind]); fprintf(stderr, "wmix:error: argument '%s' not understood\n", argv[optind]);
error_found = true; error_found = true;
@ -296,9 +311,10 @@ void config_read(void)
} else if (strcmp(keyword, "exclude") == 0) { } else if (strcmp(keyword, "exclude") == 0) {
int i; int i;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { for (i = 0; i < EXCLUDE_MAX_COUNT; i++) {
if (config.exclude_channel[i] == NULL) { if (config.exclude_channel[i] == NULL) {
config.exclude_channel[i] = strdup(value); config.exclude_channel[i] = strdup(value);
config.exclude_channel[i+1] = NULL;
break; break;
} }

View file

@ -20,9 +20,7 @@
#ifndef WMIX_CONFIG_H #ifndef WMIX_CONFIG_H
#define WMIX_CONFIG_H #define WMIX_CONFIG_H
/* Needed for SOUND_MIXER_NRDEVICES */ #define EXCLUDE_MAX_COUNT 100
#include <sys/soundcard.h>
/* Global Configuration */ /* Global Configuration */
extern struct _Config { extern struct _Config {
@ -30,6 +28,7 @@ extern struct _Config {
char *display_name; /* X Display to connect to */ char *display_name; /* X Display to connect to */
char *mixer_device; /* device file to use for controlling Mixer volumes */ char *mixer_device; /* device file to use for controlling Mixer volumes */
unsigned int api; /* Sound API (0 = ALSA, 1 = OSS) */
unsigned int verbose : 1; /* be Verbose when starting */ unsigned int verbose : 1; /* be Verbose when starting */
unsigned int osd : 1; /* show OSD? */ unsigned int osd : 1; /* show OSD? */
unsigned int mousewheel : 1; /* mousewheel enabled? */ unsigned int mousewheel : 1; /* mousewheel enabled? */
@ -42,7 +41,7 @@ extern struct _Config {
float scrollstep; /* scroll mouse step adjustment */ float scrollstep; /* scroll mouse step adjustment */
char *osd_color; /* osd color */ char *osd_color; /* osd color */
char *exclude_channel[SOUND_MIXER_NRDEVICES + 1]; /* Devices to exclude from GUI's list */ char *exclude_channel[EXCLUDE_MAX_COUNT + 1]; /* Devices to exclude from GUI's list */
} config; } config;
/* Default color for OSD */ /* Default color for OSD */

43
wmix/include/mixer-alsa.h Normal file
View file

@ -0,0 +1,43 @@
/* WMix 3.0 -- a mixer using the OSS mixer API.
* Copyright (C) 2000, 2001
* Daniel Richard G. <skunk@mit.edu>,
* timecop <timecop@japan.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
void mixer_alsa_init(const char *mixer_oss_device,
bool verbose,
const char *exclude[]);
bool mixer_alsa_is_changed(void);
int mixer_alsa_get_channel_count(void);
int mixer_alsa_get_channel(void);
const char *mixer_alsa_get_channel_name(void);
const char *mixer_alsa_get_short_name(void);
void mixer_alsa_set_channel(int channel);
void mixer_alsa_set_channel_rel(int delta_channel);
float mixer_alsa_get_volume(void);
void mixer_alsa_set_volume(float volume);
void mixer_alsa_set_volume_rel(float delta_volume);
float mixer_alsa_get_balance(void);
void mixer_alsa_set_balance(float balance);
void mixer_alsa_set_balance_rel(float delta_balance);
void mixer_alsa_toggle_mute(void);
void mixer_alsa_toggle_rec(void);
bool mixer_alsa_is_muted(void);
bool mixer_alsa_is_stereo(void);
bool mixer_alsa_is_rec(void);
bool mixer_alsa_can_rec (void);
void mixer_alsa_tick(void);

42
wmix/include/mixer-oss.h Normal file
View file

@ -0,0 +1,42 @@
/* WMix 3.0 -- a mixer using the OSS mixer API.
* Copyright (C) 2000, 2001
* Daniel Richard G. <skunk@mit.edu>,
* timecop <timecop@japan.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
void mixer_oss_init(const char *mixer_oss_device,
bool verbose,
const char *exclude[]);
bool mixer_oss_is_changed(void);
int mixer_oss_get_channel_count(void);
int mixer_oss_get_channel(void);
const char *mixer_oss_get_channel_name(void);
const char *mixer_oss_get_short_name(void);
void mixer_oss_set_channel(int channel);
void mixer_oss_set_channel_rel(int delta_channel);
float mixer_oss_get_volume(void);
void mixer_oss_set_volume(float volume);
void mixer_oss_set_volume_rel(float delta_volume);
float mixer_oss_get_balance(void);
void mixer_oss_set_balance(float balance);
void mixer_oss_set_balance_rel(float delta_balance);
void mixer_oss_toggle_mute(void);
void mixer_oss_toggle_rec(void);
bool mixer_oss_is_muted(void);
bool mixer_oss_is_stereo(void);
bool mixer_oss_is_rec(void);
bool mixer_oss_can_rec(void);

View file

@ -55,27 +55,28 @@
* - Muting must occur independently of the volume level. * - Muting must occur independently of the volume level.
*/ */
void mixer_init (const char *mixer_device, void (*mixer_init)(const char *mixer_device,
bool verbose, bool verbose,
const char *exclude[]); const char *exclude[]);
bool mixer_is_changed (void); bool (*mixer_is_changed)(void);
int mixer_get_channel_count (void); int (*mixer_get_channel_count)(void);
int mixer_get_channel (void); int (*mixer_get_channel)(void);
const char * mixer_get_channel_name (void); const char *(*mixer_get_channel_name)(void);
const char * mixer_get_short_name (void); const char *(*mixer_get_short_name)(void);
void mixer_set_channel (int channel); void (*mixer_set_channel)(int channel);
void mixer_set_channel_rel (int delta_channel); void (*mixer_set_channel_rel)(int delta_channel);
float mixer_get_volume (void); float (*mixer_get_volume)(void);
void mixer_set_volume (float volume); void (*mixer_set_volume)(float volume);
void mixer_set_volume_rel (float delta_volume); void (*mixer_set_volume_rel)(float delta_volume);
float mixer_get_balance (void); float (*mixer_get_balance)(void);
void mixer_set_balance (float balance); void (*mixer_set_balance)(float balance);
void mixer_set_balance_rel (float delta_balance); void (*mixer_set_balance_rel)(float delta_balance);
void mixer_toggle_mute (void); void (*mixer_toggle_mute)(void);
void mixer_toggle_rec (void); void (*mixer_toggle_rec)(void);
bool mixer_is_muted (void); bool (*mixer_is_muted)(void);
bool mixer_is_stereo (void); bool (*mixer_is_stereo)(void);
bool mixer_is_rec (void); bool (*mixer_is_rec)(void);
bool mixer_can_rec (void); bool (*mixer_can_rec)(void);
bool is_exclude (const char *short_name, bool (*is_exclude)(const char *short_name,
const char *exclude[]); const char *exclude[]);
void (*mixer_tick)(void);

399
wmix/mixer-alsa.c Normal file
View file

@ -0,0 +1,399 @@
#include "include/common.h"
#include "include/misc.h"
#include "include/mixer-alsa.h"
#include <alsa/asoundlib.h>
static bool get_mixer_state(void);
struct mixer_element {
const char *name; /* name of channel */
const char *sname; /* short name of the channel */
snd_mixer_elem_t *element; /* Mixer element pointer */
long min; /* Min volume */
long max; /* Max volume */
int dev; /* channel device number */
int prev_dev_lr_volume; /* last known left/right volume
* (in device format) */
float volume; /* volume, in [0, 1] */
float balance; /* balance, in [-1, 1] */
bool has_playback;
bool has_playback_switch;
bool has_capture;
bool has_capture_switch;
bool is_recording; /* is it recording? */
bool is_stereo; /* capable of stereo? */
bool is_muted; /* is it muted? */
int (*get_volume)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
int (*set_volume)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long);
};
static snd_mixer_t *mixer;
static struct mixer_element *element;
static int cur_element = 0;
static int n_elements;
static bool needs_update = true;
static int elem_callback(__attribute__((unused)) snd_mixer_elem_t *elem,
__attribute__((unused)) unsigned int mask)
{
needs_update = true;
return 0;
}
static int mixer_callback(__attribute__((unused)) snd_mixer_t *ctl,
unsigned int mask,
snd_mixer_elem_t *elem)
{
if (mask & SND_CTL_EVENT_MASK_ADD) {
snd_mixer_elem_set_callback(elem, elem_callback);
needs_update = true;
}
return 0;
}
static bool is_exclude(const char *short_name,
const char *exclude[])
{
for (int i = 0; exclude[i] != NULL; i++) {
if (!strcmp(short_name, exclude[i])) {
return true;
}
}
return false;
}
void mixer_alsa_init(const char *mixer_device, bool verbose, const char * exclude[])
{
int err;
static struct snd_mixer_selem_regopt selem_regopt = {
.ver = 1,
.abstract = SND_MIXER_SABSTRACT_NONE,
};
selem_regopt.device = mixer_device;
if (verbose) {
printf("Sound card: %s\n", mixer_device);
puts("Supported elements:");
}
if ((err = snd_mixer_open(&mixer, 0)) < 0) {
fprintf(stderr, "snd_mixer_open error");
mixer = NULL;
return;
}
if ((err = snd_mixer_selem_register(mixer, &selem_regopt, NULL)) < 0) {
fprintf(stderr, "snd_mixer_selem_register error");
snd_mixer_close(mixer);
mixer = NULL;
return;
}
snd_mixer_set_callback(mixer, mixer_callback);
if ((err = snd_mixer_load(mixer)) < 0) {
fprintf(stderr, "snd_mixer_load error");
snd_mixer_close(mixer);
mixer = NULL;
return;
}
int all_elements = snd_mixer_get_count(mixer);
element = (struct mixer_element *)malloc(all_elements *
sizeof(struct mixer_element));
memset(element, 0, all_elements * sizeof(struct mixer_element));
snd_mixer_elem_t *elem = snd_mixer_first_elem(mixer);
int f = 0;
for (int e = 0; e < all_elements; e++) {
element[f].element = elem;
element[f].name = snd_mixer_selem_get_name(elem);
if (is_exclude(element[f].name, exclude)) {
if (verbose)
printf(" x: '%s' - disabled\n", element[f].name);
elem = snd_mixer_elem_next(elem);
continue;
}
element[f].sname = element[f].name;
element[f].has_capture = snd_mixer_selem_has_capture_volume(elem);
element[f].has_capture &= snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_FRONT_LEFT);
element[f].has_playback = snd_mixer_selem_has_playback_volume(elem);
element[f].has_playback &= snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_LEFT);
if (element[f].has_playback) {
snd_mixer_selem_get_playback_volume_range(elem, &(element[f].min), &(element[f].max));
element[f].is_stereo = !snd_mixer_selem_is_playback_mono(elem);
element[f].is_stereo &= snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT);
element[f].get_volume = snd_mixer_selem_get_playback_volume;
element[f].set_volume = snd_mixer_selem_set_playback_volume;
element[f].has_playback_switch = snd_mixer_selem_has_playback_switch(elem);
}
else if (element[f].has_capture) {
snd_mixer_selem_get_capture_volume_range(elem, &(element[f].min), &(element[f].max));
element[f].is_stereo = !snd_mixer_selem_is_capture_mono(element[f].element);
element[f].is_stereo &= snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT);
element[f].get_volume = snd_mixer_selem_get_capture_volume;
element[f].set_volume = snd_mixer_selem_set_capture_volume;
element[f].has_capture_switch = snd_mixer_selem_has_capture_switch(elem);
} else {
elem = snd_mixer_elem_next(elem);
continue;
}
if (verbose) {
printf(" %d: '%s'%s%s%s%s %s (%ld - %ld)\n",
f,
element[f].name,
element[f].has_playback? " pvolume" : "",
element[f].has_playback_switch? " pswitch" : "",
element[f].has_capture? " cvolume" : "",
element[f].has_capture_switch? " cswitch" : "",
element[f].is_stereo? "Stereo" : "Mono",
element[f].min, element[f].max);
}
elem = snd_mixer_elem_next(elem);
f++;
}
n_elements = f;
get_mixer_state();
}
static bool element_is_muted(int e)
{
if (!element[e].has_playback_switch)
return false;
snd_mixer_elem_t *elem = element[e].element;
int left_on;
snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &left_on);
if (left_on)
return false;
if (element[e].is_stereo) {
int right_on;
snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right_on);
if (right_on)
return false;
}
return true;
}
static bool element_is_recording(int e)
{
if (!element[e].has_capture_switch)
return false;
snd_mixer_elem_t *elem = element[e].element;
int left_on;
snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &left_on);
if (left_on)
return true;
if (element[e].is_stereo) {
int right_on;
snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right_on);
if (right_on)
return true;
}
return false;
}
static bool get_mixer_state(void)
{
if (!needs_update)
return false;
needs_update = false;
for (int e = 0; e < n_elements; e++) {
snd_mixer_elem_t *elem = element[e].element;
long left, right;
element[e].get_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &left);
float fleft = 1.0 * (left-element[e].min) / (element[e].max-element[e].min);
if (element[e].is_stereo) {
element[e].get_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &right);
float fright = 1.0 * (right-element[e].min) /
(element[e].max-element[e].min);
lr_to_vb(fleft, fright, &(element[e].volume), &(element[e].balance));
} else {
element[e].volume = fleft;
element[e].balance = 0.0;
}
element[e].is_muted = element_is_muted(e);
element[e].is_recording = element_is_recording(e);
//printf("Channel %s, has_vol: %d, stereo: %d, muted: %d, max: %ld, min: %ld, left: %ld, right: %ld, vol: %f, bal: %f\n", element[e].name, element[e].has_playback, element[e].is_stereo, element[e].is_muted, element[e].max, element[e].min, left, element[e].is_stereo?right:-1, element[e].volume, element[e].balance);
}
return true;
}
static void set_mixer_state(void)
{
float left, right;
long dev_left_volume, dev_right_volume;
if (!element[cur_element].has_playback && !element[cur_element].has_capture)
return;
bool muted = element_is_muted(cur_element);
if (muted != element[cur_element].is_muted) {
snd_mixer_selem_set_playback_switch_all(element[cur_element].element,
element[cur_element].is_muted?
0:1);
}
bool recording = element_is_recording(cur_element);
if (recording != element[cur_element].is_recording) {
snd_mixer_selem_set_capture_switch_all(element[cur_element].element,
element[cur_element].is_recording?
1:0);
}
if (element[cur_element].is_stereo)
vb_to_lr(element[cur_element].volume,
element[cur_element].balance, &left, &right);
else
left = element[cur_element].volume;
long range = element[cur_element].max - element[cur_element].min;
dev_left_volume = element[cur_element].min + (long) (range * left);
element[cur_element].set_volume(element[cur_element].element,
SND_MIXER_SCHN_FRONT_LEFT,
dev_left_volume);
if (element[cur_element].is_stereo) {
dev_right_volume = element[cur_element].min + (long) (range * right);
element[cur_element].set_volume(element[cur_element].element,
SND_MIXER_SCHN_FRONT_RIGHT,
dev_right_volume);
}
}
bool mixer_alsa_is_changed(void)
{
return get_mixer_state();
}
int mixer_alsa_get_channel_count(void)
{
return n_elements;
}
int mixer_alsa_get_channel(void)
{
return cur_element;
}
const char *mixer_alsa_get_channel_name(void)
{
return element[cur_element].name;
}
const char *mixer_alsa_get_short_name(void)
{
return element[cur_element].sname;
}
void mixer_alsa_set_channel(int element)
{
assert((element >= 0) && (element < n_elements));
cur_element = element;
get_mixer_state();
}
void mixer_alsa_set_channel_rel(int delta_element)
{
cur_element = (cur_element + delta_element) % n_elements;
if (cur_element < 0)
cur_element += n_elements;
get_mixer_state();
}
float mixer_alsa_get_volume(void)
{
get_mixer_state();
return element[cur_element].volume;
}
void mixer_alsa_set_volume(float volume)
{
assert((volume >= 0.0) && (volume <= 1.0));
element[cur_element].volume = volume;
set_mixer_state();
}
void mixer_alsa_set_volume_rel(float delta_volume)
{
element[cur_element].volume += delta_volume;
element[cur_element].volume = CLAMP(element[cur_element].volume, 0.0, 1.0);
set_mixer_state();
}
float mixer_alsa_get_balance(void)
{
get_mixer_state();
return element[cur_element].balance;
}
void mixer_alsa_set_balance(float balance)
{
assert((balance >= -1.0) && (balance <= 1.0));
if (element[cur_element].is_stereo) {
element[cur_element].balance = balance;
set_mixer_state();
}
}
void mixer_alsa_set_balance_rel(float delta_balance)
{
if (element[cur_element].is_stereo) {
element[cur_element].balance += delta_balance;
element[cur_element].balance =
CLAMP(element[cur_element].balance, -1.0, 1.0);
set_mixer_state();
}
}
void mixer_alsa_toggle_mute(void)
{
if (element[cur_element].has_playback_switch) {
element[cur_element].is_muted ^= 1;
set_mixer_state();
}
}
void mixer_alsa_toggle_rec(void)
{
if (element[cur_element].has_capture_switch) {
element[cur_element].is_recording ^= 1;
set_mixer_state();
}
}
bool mixer_alsa_is_muted(void)
{
return element[cur_element].is_muted;
}
bool mixer_alsa_is_stereo(void)
{
return element[cur_element].is_stereo;
}
bool mixer_alsa_is_rec(void)
{
return element[cur_element].is_recording;
}
bool mixer_alsa_can_rec(void)
{
return element[cur_element].has_capture;
}
void mixer_alsa_tick(void)
{
snd_mixer_handle_events(mixer);
}

View file

@ -32,7 +32,7 @@
#include "include/common.h" #include "include/common.h"
#include "include/misc.h" #include "include/misc.h"
#include "include/mixer.h" #include "include/mixer-oss.h"
#define WMVOLUME_CHANNEL_NAMES \ #define WMVOLUME_CHANNEL_NAMES \
"Master volume", \ "Master volume", \
@ -221,7 +221,43 @@ static void set_record_state(void)
} }
} }
void mixer_init(const char *mixer_device, bool verbose, const char * exclude[]) static bool is_exclude(const char *short_name, const char *exclude[])
{
int count;
int len;
for (count = 0; exclude[count] != NULL; count++) {
/*
* Short names may be padded with spaces, because apparently there is a minimum
* length requirement of 6 characters for the name, and we do not want to
* include this padding in the match
*/
len = strlen(short_name);
while (len > 0) {
if (short_name[len - 1] == ' ')
len--;
else
break;
}
if (strncmp(short_name, exclude[count], len) != 0)
continue;
if (exclude[count][len] != '\0')
continue;
/* Check the remaining in short name is only space */
while (short_name[len] == ' ')
len++;
if (short_name[len] == '\0')
return true;
}
return false;
}
void mixer_oss_init(const char *mixer_device, bool verbose, const char * exclude[])
{ {
int devmask, srcmask, recmask, stmask; int devmask, srcmask, recmask, stmask;
struct mixer_info m_info; struct mixer_info m_info;
@ -288,32 +324,32 @@ void mixer_init(const char *mixer_device, bool verbose, const char * exclude[])
get_mixer_state(); get_mixer_state();
} }
bool mixer_is_changed(void) bool mixer_oss_is_changed(void)
{ {
return get_mixer_state(); return get_mixer_state();
} }
int mixer_get_channel_count(void) int mixer_oss_get_channel_count(void)
{ {
return n_channels; return n_channels;
} }
int mixer_get_channel(void) int mixer_oss_get_channel(void)
{ {
return cur_channel; return cur_channel;
} }
const char *mixer_get_channel_name(void) const char *mixer_oss_get_channel_name(void)
{ {
return mixer[cur_channel].name; return mixer[cur_channel].name;
} }
const char *mixer_get_short_name(void) const char *mixer_oss_get_short_name(void)
{ {
return mixer[cur_channel].sname; return mixer[cur_channel].sname;
} }
void mixer_set_channel(int channel) void mixer_oss_set_channel(int channel)
{ {
assert((channel >= 0) && (channel < n_channels)); assert((channel >= 0) && (channel < n_channels));
@ -321,7 +357,7 @@ void mixer_set_channel(int channel)
get_record_state(); get_record_state();
} }
void mixer_set_channel_rel(int delta_channel) void mixer_oss_set_channel_rel(int delta_channel)
{ {
cur_channel = (cur_channel + delta_channel) % n_channels; cur_channel = (cur_channel + delta_channel) % n_channels;
if (cur_channel < 0) if (cur_channel < 0)
@ -329,13 +365,13 @@ void mixer_set_channel_rel(int delta_channel)
get_record_state(); get_record_state();
} }
float mixer_get_volume(void) float mixer_oss_get_volume(void)
{ {
get_mixer_state(); get_mixer_state();
return mixer[cur_channel].volume; return mixer[cur_channel].volume;
} }
void mixer_set_volume(float volume) void mixer_oss_set_volume(float volume)
{ {
assert((volume >= 0.0) && (volume <= 1.0)); assert((volume >= 0.0) && (volume <= 1.0));
@ -343,20 +379,20 @@ void mixer_set_volume(float volume)
set_mixer_state(); set_mixer_state();
} }
void mixer_set_volume_rel(float delta_volume) void mixer_oss_set_volume_rel(float delta_volume)
{ {
mixer[cur_channel].volume += delta_volume; mixer[cur_channel].volume += delta_volume;
mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0); mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
set_mixer_state(); set_mixer_state();
} }
float mixer_get_balance(void) float mixer_oss_get_balance(void)
{ {
get_mixer_state(); get_mixer_state();
return mixer[cur_channel].balance; return mixer[cur_channel].balance;
} }
void mixer_set_balance(float balance) void mixer_oss_set_balance(float balance)
{ {
assert((balance >= -1.0) && (balance <= 1.0)); assert((balance >= -1.0) && (balance <= 1.0));
@ -366,7 +402,7 @@ void mixer_set_balance(float balance)
} }
} }
void mixer_set_balance_rel(float delta_balance) void mixer_oss_set_balance_rel(float delta_balance)
{ {
if (mixer[cur_channel].is_stereo) { if (mixer[cur_channel].is_stereo) {
mixer[cur_channel].balance += delta_balance; mixer[cur_channel].balance += delta_balance;
@ -376,14 +412,14 @@ void mixer_set_balance_rel(float delta_balance)
} }
} }
void mixer_toggle_mute(void) void mixer_oss_toggle_mute(void)
{ {
mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted; mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
set_mixer_state(); set_mixer_state();
} }
void mixer_toggle_rec(void) void mixer_oss_toggle_rec(void)
{ {
if (mixer[cur_channel].can_record) { if (mixer[cur_channel].can_record) {
mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording; mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
@ -392,60 +428,22 @@ void mixer_toggle_rec(void)
} }
} }
bool mixer_is_muted(void) bool mixer_oss_is_muted(void)
{ {
return mixer[cur_channel].is_muted; return mixer[cur_channel].is_muted;
} }
bool mixer_is_stereo(void) bool mixer_oss_is_stereo(void)
{ {
return mixer[cur_channel].is_stereo; return mixer[cur_channel].is_stereo;
} }
bool mixer_is_rec(void) bool mixer_oss_is_rec(void)
{ {
return mixer[cur_channel].is_recording; return mixer[cur_channel].is_recording;
} }
bool mixer_can_rec(void) bool mixer_oss_can_rec(void)
{ {
return mixer[cur_channel].can_record; return mixer[cur_channel].can_record;
} }
bool is_exclude(const char *short_name, const char *exclude[])
{
int count;
int len;
for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
if (exclude[count] == NULL)
break;
/*
* Short names may be padded with spaces, because apparently there is a minimum
* length requirement of 6 characters for the name, and we do not want to
* include this padding in the match
*/
len = strlen(short_name);
while (len > 0) {
if (short_name[len - 1] == ' ')
len--;
else
break;
}
if (strncmp(short_name, exclude[count], len) != 0)
continue;
if (exclude[count][len] != '\0')
continue;
/* Check the remaining in short name is only space */
while (short_name[len] == ' ')
len++;
if (short_name[len] == '\0')
return true;
}
return false;
}

View file

@ -31,14 +31,14 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <sys/soundcard.h>
#include "include/common.h" #include "include/common.h"
#include "include/mixer.h" #include "include/mixer.h"
#include "include/misc.h" #include "include/misc.h"
#include "include/ui_x.h" #include "include/ui_x.h"
#include "include/mmkeys.h" #include "include/mmkeys.h"
#include "include/config.h" #include "include/config.h"
#include "include/mixer-oss.h"
#include "include/mixer-alsa.h"
static Display *display; static Display *display;
@ -58,6 +58,7 @@ static void button_press_event(XButtonEvent *event);
static void button_release_event(XButtonEvent *event); static void button_release_event(XButtonEvent *event);
static int key_press_event(XKeyEvent *event); static int key_press_event(XKeyEvent *event);
static void motion_event(XMotionEvent *event); static void motion_event(XMotionEvent *event);
static void choose_api(int api);
int main(int argc, char **argv) int main(int argc, char **argv)
@ -67,6 +68,7 @@ int main(int argc, char **argv)
config_init(); config_init();
parse_cli_options(argc, argv); parse_cli_options(argc, argv);
config_read(); config_read();
choose_api(config.api);
mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel); mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
mixer_set_channel(0); mixer_set_channel(0);
@ -154,6 +156,8 @@ int main(int argc, char **argv)
} }
} else { } else {
usleep(100000); usleep(100000);
if (mixer_tick)
mixer_tick();
scroll_text(3, 4, 57, false); scroll_text(3, 4, 57, false);
/* rescroll message after some delay */ /* rescroll message after some delay */
if (idle_loop++ > 256) { if (idle_loop++ > 256) {
@ -196,6 +200,55 @@ static void signal_catch(int sig)
} }
} }
static void choose_api(int api)
{
if (api == 0) {
mixer_init = &mixer_alsa_init;
mixer_is_changed = &mixer_alsa_is_changed;
mixer_get_channel_count = mixer_alsa_get_channel_count;
mixer_get_channel = mixer_alsa_get_channel;
mixer_get_channel_name = mixer_alsa_get_channel_name;
mixer_get_short_name = mixer_alsa_get_short_name;
mixer_set_channel = mixer_alsa_set_channel;
mixer_set_channel_rel = mixer_alsa_set_channel_rel;
mixer_get_volume = mixer_alsa_get_volume;
mixer_set_volume = mixer_alsa_set_volume;
mixer_set_volume_rel = mixer_alsa_set_volume_rel;
mixer_get_balance = mixer_alsa_get_balance;
mixer_set_balance = mixer_alsa_set_balance;
mixer_set_balance_rel = mixer_alsa_set_balance_rel;
mixer_toggle_mute = mixer_alsa_toggle_mute;
mixer_toggle_rec = mixer_alsa_toggle_rec;
mixer_is_muted = mixer_alsa_is_muted;
mixer_is_stereo = mixer_alsa_is_stereo;
mixer_is_rec = mixer_alsa_is_rec;
mixer_can_rec = mixer_alsa_can_rec;
mixer_tick = mixer_alsa_tick;
} else if (api == 1) {
mixer_init = &mixer_oss_init;
mixer_is_changed = &mixer_oss_is_changed;
mixer_get_channel_count = mixer_oss_get_channel_count;
mixer_get_channel = mixer_oss_get_channel;
mixer_get_channel_name = mixer_oss_get_channel_name;
mixer_get_short_name = mixer_oss_get_short_name;
mixer_set_channel = mixer_oss_set_channel;
mixer_set_channel_rel = mixer_oss_set_channel_rel;
mixer_get_volume = mixer_oss_get_volume;
mixer_set_volume = mixer_oss_set_volume;
mixer_set_volume_rel = mixer_oss_set_volume_rel;
mixer_get_balance = mixer_oss_get_balance;
mixer_set_balance = mixer_oss_set_balance;
mixer_set_balance_rel = mixer_oss_set_balance_rel;
mixer_toggle_mute = mixer_oss_toggle_mute;
mixer_toggle_rec = mixer_oss_toggle_rec;
mixer_is_muted = mixer_oss_is_muted;
mixer_is_stereo = mixer_oss_is_stereo;
mixer_is_rec = mixer_oss_is_rec;
mixer_can_rec = mixer_oss_can_rec;
mixer_tick = NULL;
}
}
static void button_press_event(XButtonEvent *event) static void button_press_event(XButtonEvent *event)
{ {
double button_press_time = get_current_time(); double button_press_time = get_current_time();