alsaloop: Add OSS mixer redirection support

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2010-10-08 15:10:23 +02:00
parent 058357f042
commit 147a1cc75c
6 changed files with 138 additions and 6 deletions

View file

@ -152,6 +152,22 @@ Known attributes:
iface - control ID interface iface - control ID interface
numid - control ID numid numid - control ID numid
.TP
\fI\-O <ossmixid>\fP | \fI\-\-ossmixer=<midid>\fP
Redirect mixer control from the OSS Mixer emulation layer (capture card)
to the ALSA layer (capture card). Format of \fIossmixid\fP is
ALSAID[,INDEX]@OSSID:
"Master@VOLUME"
"PCM,1@ALTPCM"
Known OSS attributes:
VOLUME, BASS, TREBLE, SYNTH, PCM, SPEAKER, LINE, MIC, CD, IMIX, ALTPCM,
RECLEV, IGAIN, OGAIN, LINE1, LINE2, LINE3, DIGITAL1, DIGITAL2, DIGITAL3,
PHONEIN, PHONEOUT, VIDEO, RADIO, MONITOR
.TP .TP
\fI\-v\fP | \fI\-\-verbose\fP \fI\-v\fP | \fI\-\-verbose\fP

View file

@ -164,7 +164,9 @@ void help(void)
"-a,--slave stream parameters slave mode (0=auto, 1=on, 2=off)\n" "-a,--slave stream parameters slave mode (0=auto, 1=on, 2=off)\n"
"-T,--thread thread number (-1 = create unique)\n" "-T,--thread thread number (-1 = create unique)\n"
"-m,--mixer redirect mixer, argument is:\n" "-m,--mixer redirect mixer, argument is:\n"
" SRC_SLAVE_ID(PLAYBACK)@DST_SLAVE_ID(CAPTURE)\n" " SRC_SLAVE_ID(PLAYBACK)[@DST_SLAVE_ID(CAPTURE)]\n"
"-O,--ossmixer rescan and redirect oss mixer, argument is:\n"
" ALSA_ID@OSS_ID (for example: \"Master@VOLUME\")\n"
"-e,--effect apply an effect (bandpass filter sweep)\n" "-e,--effect apply an effect (bandpass filter sweep)\n"
"-v,--verbose verbose mode (more -v means more verbose)\n" "-v,--verbose verbose mode (more -v means more verbose)\n"
); );
@ -266,6 +268,46 @@ static int add_mixers(struct loopback *loop,
return 0; return 0;
} }
static int add_oss_mixers(struct loopback *loop,
char **mixers,
int mixers_count)
{
struct loopback_ossmixer *mixer, *last = NULL;
char *str1, *str2;
while (mixers_count > 0) {
mixer = calloc(1, sizeof(*mixer));
if (mixer == NULL)
return -ENOMEM;
if (last)
last->next = mixer;
else
loop->oss_controls = mixer;
last = mixer;
str1 = strchr(*mixers, ',');
if (str1)
*str1 = '\0';
str2 = strchr(str1 ? str1 + 1 : *mixers, '@');
if (str2)
*str2 = '\0';
mixer->alsa_id = strdup(*mixers);
if (str1)
mixer->alsa_index = atoi(str1);
mixer->oss_id = strdup(str2 ? str2 + 1 : *mixers);
if (mixer->alsa_id == NULL || mixer->oss_id == NULL) {
logit(LOG_CRIT, "Not enough memory");
return -ENOMEM;
}
if (str1)
*str1 = ',';
if (str2)
*str2 = ',';
mixers++;
mixers_count--;
}
return 0;
}
static int parse_config_file(const char *file, snd_output_t *output); static int parse_config_file(const char *file, snd_output_t *output);
static int parse_config(int argc, char *argv[], snd_output_t *output) static int parse_config(int argc, char *argv[], snd_output_t *output)
@ -294,6 +336,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
{"slave", 1, NULL, 'a'}, {"slave", 1, NULL, 'a'},
{"thread", 1, NULL, 'T'}, {"thread", 1, NULL, 'T'},
{"mixer", 1, NULL, 'm'}, {"mixer", 1, NULL, 'm'},
{"ossmixer", 1, NULL, 'O'},
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
int err, morehelp; int err, morehelp;
@ -318,11 +361,15 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
struct loopback *loop = NULL; struct loopback *loop = NULL;
char *arg_mixers[MAX_MIXERS]; char *arg_mixers[MAX_MIXERS];
int arg_mixers_count = 0; int arg_mixers_count = 0;
char *arg_ossmixers[MAX_MIXERS];
int arg_ossmixers_count = 0;
morehelp = 0; morehelp = 0;
while (1) { while (1) {
int c; int c;
if ((c = getopt_long(argc, argv, "hdg:P:C:l:t:F:f:c:r:s:benvA:S:a:m:T:", long_option, NULL)) < 0) if ((c = getopt_long(argc, argv,
"hdg:P:C:l:t:F:f:c:r:s:benvA:S:a:m:T:O:",
long_option, NULL)) < 0)
break; break;
switch (c) { switch (c) {
case 'h': case 'h':
@ -445,6 +492,13 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
} }
arg_mixers[arg_mixers_count++] = optarg; arg_mixers[arg_mixers_count++] = optarg;
break; break;
case 'O':
if (arg_ossmixers_count >= MAX_MIXERS) {
logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
exit(EXIT_FAILURE);
}
arg_ossmixers[arg_ossmixers_count++] = optarg;
break;
case 'v': case 'v':
verbose++; verbose++;
break; break;
@ -490,6 +544,11 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
logit(LOG_CRIT, "Unable to add mixer controls.\n"); logit(LOG_CRIT, "Unable to add mixer controls.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
err = add_oss_mixers(loop, arg_ossmixers, arg_ossmixers_count);
if (err < 0) {
logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
exit(EXIT_FAILURE);
}
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
loop->src_enable = arg_samplerate > 0; loop->src_enable = arg_samplerate > 0;
if (loop->src_enable) if (loop->src_enable)

View file

@ -65,16 +65,25 @@ struct loopback_control {
}; };
struct loopback_mixer { struct loopback_mixer {
unsigned int skip: 1; unsigned int skip:1;
struct loopback_control src; struct loopback_control src;
struct loopback_control dst; struct loopback_control dst;
struct loopback_mixer *next; struct loopback_mixer *next;
}; };
struct loopback_ossmixer {
unsigned int skip:1;
const char *alsa_id;
int alsa_index;
const char *oss_id;
struct loopback_ossmixer *next;
};
struct loopback_handle { struct loopback_handle {
struct loopback *loopback; struct loopback *loopback;
char *device; char *device;
char *id; char *id;
int card_number;
snd_pcm_t *handle; snd_pcm_t *handle;
snd_pcm_access_t access; snd_pcm_access_t access;
snd_pcm_format_t format; snd_pcm_format_t format;
@ -143,6 +152,7 @@ struct loopback {
snd_timestamp_t tstamp_end; snd_timestamp_t tstamp_end;
/* control mixer */ /* control mixer */
struct loopback_mixer *controls; struct loopback_mixer *controls;
struct loopback_ossmixer *oss_controls;
/* sample rate */ /* sample rate */
unsigned int use_samplerate:1; unsigned int use_samplerate:1;
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE

View file

@ -205,6 +205,34 @@ static int copy_value(struct loopback_control *dst,
return 0; return 0;
} }
static int oss_set(struct loopback *loop,
struct loopback_ossmixer *ossmix,
int enable)
{
char buf[128], file[128];
int fd;
if (loop->capt->card_number < 0)
return 0;
if (!enable) {
sprintf(buf, "%s \"\" 0\n", ossmix->oss_id);
} else {
sprintf(buf, "%s \"%s\" %i\n", ossmix->oss_id, ossmix->alsa_id, ossmix->alsa_index);
}
sprintf(file, "/proc/asound/card%i/oss_mixer", loop->capt->card_number);
if (verbose)
snd_output_printf(loop->output, "%s: Initialize OSS volume %s: %s", loop->id, file, buf);
fd = open(file, O_WRONLY);
if (fd >= 0 && write(fd, buf, strlen(buf)) == strlen(buf)) {
close(fd);
return 0;
}
if (fd >= 0)
close(fd);
logit(LOG_INFO, "%s: Unable to initialize OSS Mixer ID '%s'\n", loop->id, ossmix->oss_id);
return -1;
}
static int control_init2(struct loopback *loop, static int control_init2(struct loopback *loop,
struct loopback_mixer *mix) struct loopback_mixer *mix)
{ {
@ -280,12 +308,15 @@ static int control_init2(struct loopback *loop,
int control_init(struct loopback *loop) int control_init(struct loopback *loop)
{ {
struct loopback_mixer *mix; struct loopback_mixer *mix;
struct loopback_ossmixer *ossmix;
int err; int err;
for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next)
oss_set(loop, ossmix, 0);
for (mix = loop->controls; mix; mix = mix->next) { for (mix = loop->controls; mix; mix = mix->next) {
err = control_init1(loop->play, &mix->src); err = control_init1(loop->play, &mix->src);
if (err < 0) { if (err < 0) {
logit(LOG_WARNING, "Disabling playback control '%s'\n", id_str(mix->src.id)); logit(LOG_WARNING, "%s: Disabling playback control '%s'\n", loop->id, id_str(mix->src.id));
mix->skip = 1; mix->skip = 1;
continue; continue;
} }
@ -293,22 +324,35 @@ int control_init(struct loopback *loop)
if (err < 0) if (err < 0)
return err; return err;
} }
for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
err = oss_set(loop, ossmix, 1);
if (err < 0) {
ossmix->skip = 1;
logit(LOG_WARNING, "%s: Disabling OSS mixer ID '%s'\n", loop->id, ossmix->oss_id);
}
}
return 0; return 0;
} }
int control_done(struct loopback *loop) int control_done(struct loopback *loop)
{ {
struct loopback_mixer *mix; struct loopback_mixer *mix;
struct loopback_ossmixer *ossmix;
int err; int err;
if (loop->capt->ctl == NULL) if (loop->capt->ctl == NULL)
return 0; return 0;
for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
err = oss_set(loop, ossmix, 0);
if (err < 0)
logit(LOG_WARNING, "%s: Unable to remove OSS control '%s'\n", loop->id, ossmix->oss_id);
}
for (mix = loop->controls; mix; mix = mix->next) { for (mix = loop->controls; mix; mix = mix->next) {
if (mix->skip) if (mix->skip)
continue; continue;
err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id); err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id);
if (err < 0) if (err < 0)
logit(LOG_WARNING, "Unable to remove control '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); logit(LOG_WARNING, "%s: Unable to remove control '%s': %s\n", loop->id, id_str(mix->dst.id), snd_strerror(err));
} }
return 0; return 0;
} }

View file

@ -1022,6 +1022,7 @@ static int openit(struct loopback_handle *lhandle)
device = snd_pcm_info_get_device(info); device = snd_pcm_info_get_device(info);
subdevice = snd_pcm_info_get_subdevice(info); subdevice = snd_pcm_info_get_subdevice(info);
snd_pcm_info_free(info); snd_pcm_info_free(info);
lhandle->card_number = card;
lhandle->ctl = NULL; lhandle->ctl = NULL;
if (card >= 0) { if (card >= 0) {
char name[16]; char name[16];

View file

@ -10,7 +10,9 @@ test1() {
--tlatency 50000 \ --tlatency 50000 \
--mixer "name='Master Playback Volume'@name='Master Playback Volume'" \ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
--mixer "name='Master Playback Switch'@name='Master Playback Switch'" \ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
--mixer "name='PCM Playback Volume'" --mixer "name='PCM Playback Volume'" \
--ossmixer "Master@VOLUME" \
--ossmixer "PCM@PCM"
} }
test2() { test2() {