]> git.alsa-project.org Git - alsa-utils.git/commitdiff
alsaloop: Add OSS mixer redirection support
authorJaroslav Kysela <perex@perex.cz>
Fri, 8 Oct 2010 13:10:23 +0000 (15:10 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 8 Oct 2010 13:10:23 +0000 (15:10 +0200)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
alsaloop/alsaloop.1
alsaloop/alsaloop.c
alsaloop/alsaloop.h
alsaloop/control.c
alsaloop/pcmjob.c
alsaloop/test.sh

index 66be499f6dca9fc834c098d31af77e14b0454bee..1554b6ef523270da060d5aaf8fd99cbfe98614ec 100644 (file)
@@ -152,6 +152,22 @@ Known attributes:
   iface     - control ID interface
   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
 \fI\-v\fP | \fI\-\-verbose\fP
 
index bbf570e690d2a0efc020defcda304f19f0b7ae30..effa0734088f2b833df038becb694d503b0ea685 100644 (file)
@@ -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)
index 9753c413e3b7e965d51cf0d3fdaa5d3bb8b74bd0..366a2961f063cbe49e87b2b508ef1191107b0982 100644 (file)
@@ -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
index ade7733faf52839de4a033690c7eb7d2135eef1e..967f1e977c910f4c2515629cdaad3260a072321e 100644 (file)
@@ -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;
 }
index 86917ef5088717cf0f1c534d0ffdc7a985390727..5c2fed04a76ad4f47b1c6207d73bdec1bdc0f6cf 100644 (file)
@@ -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];
index fbd40c08c00d3a042a3c38731848b2ccc1f24c5f..13a5ba75f6ece4a4ebccd06236d75ab85ed79cee 100755 (executable)
@@ -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() {