From 147a1cc75cf6002eb5ea2983a374d2e84eee8e1d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Oct 2010 15:10:23 +0200 Subject: alsaloop: Add OSS mixer redirection support Signed-off-by: Jaroslav Kysela --- alsaloop/alsaloop.1 | 16 ++++++++++++++ alsaloop/alsaloop.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++-- alsaloop/alsaloop.h | 12 +++++++++- alsaloop/control.c | 48 ++++++++++++++++++++++++++++++++++++++-- alsaloop/pcmjob.c | 1 + alsaloop/test.sh | 4 +++- 6 files changed, 138 insertions(+), 6 deletions(-) (limited to 'alsaloop') diff --git a/alsaloop/alsaloop.1 b/alsaloop/alsaloop.1 index 66be499..1554b6e 100644 --- a/alsaloop/alsaloop.1 +++ b/alsaloop/alsaloop.1 @@ -152,6 +152,22 @@ Known attributes: iface - control ID interface numid - control ID numid +.TP +\fI\-O \fP | \fI\-\-ossmixer=\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 \fI\-v\fP | \fI\-\-verbose\fP diff --git a/alsaloop/alsaloop.c b/alsaloop/alsaloop.c index bbf570e..effa073 100644 --- a/alsaloop/alsaloop.c +++ b/alsaloop/alsaloop.c @@ -164,7 +164,9 @@ void help(void) "-a,--slave stream parameters slave mode (0=auto, 1=on, 2=off)\n" "-T,--thread thread number (-1 = create unique)\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" "-v,--verbose verbose mode (more -v means more verbose)\n" ); @@ -266,6 +268,46 @@ static int add_mixers(struct loopback *loop, 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(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'}, {"thread", 1, NULL, 'T'}, {"mixer", 1, NULL, 'm'}, + {"ossmixer", 1, NULL, 'O'}, {NULL, 0, NULL, 0}, }; int err, morehelp; @@ -318,11 +361,15 @@ static int parse_config(int argc, char *argv[], snd_output_t *output) struct loopback *loop = NULL; char *arg_mixers[MAX_MIXERS]; int arg_mixers_count = 0; + char *arg_ossmixers[MAX_MIXERS]; + int arg_ossmixers_count = 0; morehelp = 0; while (1) { 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; switch (c) { case 'h': @@ -445,6 +492,13 @@ static int parse_config(int argc, char *argv[], snd_output_t *output) } arg_mixers[arg_mixers_count++] = optarg; 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': verbose++; 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"); 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 loop->src_enable = arg_samplerate > 0; if (loop->src_enable) diff --git a/alsaloop/alsaloop.h b/alsaloop/alsaloop.h index 9753c41..366a296 100644 --- a/alsaloop/alsaloop.h +++ b/alsaloop/alsaloop.h @@ -65,16 +65,25 @@ struct loopback_control { }; struct loopback_mixer { - unsigned int skip: 1; + unsigned int skip:1; struct loopback_control src; struct loopback_control dst; 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 *loopback; char *device; char *id; + int card_number; snd_pcm_t *handle; snd_pcm_access_t access; snd_pcm_format_t format; @@ -143,6 +152,7 @@ struct loopback { snd_timestamp_t tstamp_end; /* control mixer */ struct loopback_mixer *controls; + struct loopback_ossmixer *oss_controls; /* sample rate */ unsigned int use_samplerate:1; #ifdef USE_SAMPLERATE diff --git a/alsaloop/control.c b/alsaloop/control.c index ade7733..967f1e9 100644 --- a/alsaloop/control.c +++ b/alsaloop/control.c @@ -205,6 +205,34 @@ static int copy_value(struct loopback_control *dst, 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, struct loopback_mixer *mix) { @@ -280,12 +308,15 @@ static int control_init2(struct loopback *loop, int control_init(struct loopback *loop) { struct loopback_mixer *mix; + struct loopback_ossmixer *ossmix; int err; + for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) + oss_set(loop, ossmix, 0); for (mix = loop->controls; mix; mix = mix->next) { err = control_init1(loop->play, &mix->src); 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; continue; } @@ -293,22 +324,35 @@ int control_init(struct loopback *loop) if (err < 0) 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; } int control_done(struct loopback *loop) { struct loopback_mixer *mix; + struct loopback_ossmixer *ossmix; int err; if (loop->capt->ctl == NULL) 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) { if (mix->skip) continue; err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id); 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; } diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c index 86917ef..5c2fed0 100644 --- a/alsaloop/pcmjob.c +++ b/alsaloop/pcmjob.c @@ -1022,6 +1022,7 @@ static int openit(struct loopback_handle *lhandle) device = snd_pcm_info_get_device(info); subdevice = snd_pcm_info_get_subdevice(info); snd_pcm_info_free(info); + lhandle->card_number = card; lhandle->ctl = NULL; if (card >= 0) { char name[16]; diff --git a/alsaloop/test.sh b/alsaloop/test.sh index fbd40c0..13a5ba7 100755 --- a/alsaloop/test.sh +++ b/alsaloop/test.sh @@ -10,7 +10,9 @@ test1() { --tlatency 50000 \ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \ --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() { -- cgit v1.2.1