Allow vchans to have their own volume control.
authorSimon Schubert <corecode@dragonflybsd.org>
Thu, 14 Jun 2007 21:48:36 +0000 (21:48 +0000)
committerSimon Schubert <corecode@dragonflybsd.org>
Thu, 14 Jun 2007 21:48:36 +0000 (21:48 +0000)
This means you can use two processes producing audio and change their volume
independently (if they implement mixer controls themselves).

sys/dev/sound/pcm/channel.c
sys/dev/sound/pcm/dsp.c
sys/dev/sound/pcm/dsp.h
sys/dev/sound/pcm/mixer.c
sys/dev/sound/pcm/sound.c
sys/dev/sound/pcm/sound.h
sys/dev/sound/pcm/vchan.c

index 56d7d36..f75bc03 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.4 2006/04/04 17:37:51 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.11 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.12 2007/06/14 21:48:36 corecode Exp $
  */
 
 #include "use_isa.h"
@@ -35,7 +35,7 @@
 
 #include "feeder_if.h"
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.11 2007/01/04 21:47:03 corecode Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.12 2007/06/14 21:48:36 corecode Exp $");
 
 #define MIN_CHUNK_SIZE                 256     /* for uiomove etc. */
 #if 0
@@ -861,6 +861,10 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
        if (ret)
                goto out;
 
+       ret = chn_setvolume(c, 100, 100);
+       if (ret)
+               goto out;
+
 
 out:
        CHN_UNLOCK(c);
index 66c61ea..df1ce48 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.80.2.6 2006/04/04 17:43:48 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.14 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.15 2007/06/14 21:48:36 corecode Exp $
  */
 
 #include <sys/param.h>
 #include <sys/queue.h>
 
+#include <dev/sound/pcm/dsp.h>
 #include <dev/sound/pcm/sound.h>
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.14 2007/01/04 21:47:03 corecode Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.15 2007/06/14 21:48:36 corecode Exp $");
 
 #define OLDPCM_IOCTL
 
@@ -60,7 +61,7 @@ struct dev_ops dsp_cdevsw = {
 static eventhandler_tag dsp_ehtag;
 #endif
 
-static struct snddev_info *
+struct snddev_info *
 dsp_get_info(struct cdev *dev)
 {
        struct snddev_info *d;
@@ -423,24 +424,7 @@ dsp_ioctl(struct dev_ioctl_args *ap)
        int kill;
        int ret = 0, *arg_i = (int *)arg, tmp;
 
-       /*
-        * this is an evil hack to allow broken apps to perform mixer ioctls
-        * on dsp devices.
-        */
-
        d = dsp_get_info(i_dev);
-       if (IOCGROUP(cmd) == 'M') {
-               /*
-                * This is at least, a bug to bug compatible with OSS.
-                */
-               if (d->mixer_dev != NULL) {
-                       ap->a_head.a_dev = d->mixer_dev;
-                       return mixer_ioctl(ap);
-               } else {
-                       return EBADF;
-               }
-       }
-
        getchns(i_dev, &rdch, &wrch, 0);
 
        kill = 0;
@@ -456,6 +440,37 @@ dsp_ioctl(struct dev_ioctl_args *ap)
                wrch = NULL;
        if (kill & 2)
                rdch = NULL;
+
+       /*
+        * 4Front OSS specifies that dsp devices allow mixer controls to
+        * control PCM == their volume.
+        */
+       if (IOCGROUP(cmd) == 'M') {
+               /*
+                * For now only set the channel volume for vchans, pass
+                * all others to the mixer.
+                */
+               if (wrch != NULL && wrch->flags & CHN_F_VIRTUAL &&
+                   (cmd & 0xff) == SOUND_MIXER_PCM) {
+                       if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
+                               int vol_raw = *(int *)arg;
+                               int vol_left, vol_right;
+
+                               vol_left = min(vol_raw & 0x00ff, 100);
+                               vol_right = min((vol_raw & 0xff00) >> 8, 100);
+                               ret = chn_setvolume(wrch, vol_left, vol_right);
+                       } else {
+                               *(int *)arg = wrch->volume;
+                       }
+               } else {
+                       ap->a_head.a_dev = d->mixer_dev;
+                       ret = mixer_ioctl(ap);
+               }
+
+               relchns(i_dev, rdch, wrch, 0);
+               crit_exit();
+               return ret;
+       }
        
        switch(cmd) {
 #ifdef OLDPCM_IOCTL
index a068a34..600217a 100644 (file)
@@ -24,7 +24,9 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/dsp.h,v 1.9 2005/01/06 01:43:20 imp Exp $
- * $DragonFly: src/sys/dev/sound/pcm/dsp.h,v 1.3 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/dsp.h,v 1.4 2007/06/14 21:48:36 corecode Exp $
  */
 
 extern struct dev_ops dsp_cdevsw;
+
+struct snddev_info *dsp_get_info(struct cdev *dev);
index dfe5668..6b75c5d 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.4 2006/04/04 17:43:48 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.15 2007/06/14 21:48:36 corecode Exp $
  */
 
 #include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/dsp.h>
 
 #include "mixer_if.h"
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.15 2007/06/14 21:48:36 corecode Exp $");
 
 MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
 
@@ -436,6 +437,48 @@ mixer_hwvol_step(device_t dev, int left_step, int right_step)
 
 /* ----------------------------------------------------------------------- */
 
+static int
+vchanvolume(cdev_t i_dev, int write, int *volume, int *ret,
+               struct thread *td)
+{
+       struct snddev_info *d;
+       void *cookie;
+       struct pcm_channel *ch;
+       int vol_left, vol_right;
+
+       crit_enter();
+       d = dsp_get_info(i_dev);
+       if (d == NULL) {
+               crit_exit();
+               return 0;
+       }
+       /*
+        * We search for a vchan which is owned by the current process.
+        */
+       for (cookie = NULL; (ch = pcm_chn_iterate(d, &cookie)) != NULL;)
+               if (ch->flags & CHN_F_VIRTUAL &&
+                   ch->pid == td->td_proc->p_pid)
+                       break;
+
+       if (ch == NULL) {
+               crit_exit();
+               return 0;
+       }
+
+       if (write) {
+               vol_left = min(*volume & 0x00ff, 100);
+               vol_right = min((*volume & 0xff00) >> 8, 100);
+               *ret = chn_setvolume(ch, vol_left, vol_right);
+       } else {
+               *volume = ch->volume;
+               *ret = 0;
+       }
+       crit_exit();
+       return 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
 static int
 mixer_open(struct dev_open_args *ap)
 {
@@ -486,6 +529,20 @@ mixer_ioctl(struct dev_ioctl_args *ap)
        if (m == NULL)
                return EBADF;
 
+       /*
+        * If we are handling PCM, maybe the app really wants to
+        * set its vchan, and fails to use the correct fd.
+        */
+       if (j == SOUND_MIXER_PCM) {
+               cdev_t pdev;
+
+               if (vchanvolume(i_dev,
+                           (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0),
+                           (int *)arg, &ret, curthread))
+                       return ret;
+               /* else proceed as usual */
+       }
+
        snd_mtxlock(m->lock);
        if (mode != -1 && !m->busy) {
                snd_mtxunlock(m->lock);
index 2622f9f..d949f97 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.3 2006/04/04 17:43:48 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.9 2007/06/14 21:48:36 corecode Exp $
  */
 
 #include <dev/sound/pcm/sound.h>
@@ -35,7 +35,7 @@
 
 #include "feeder_if.h"
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.9 2007/06/14 21:48:36 corecode Exp $");
 
 devclass_t pcm_devclass;
 
@@ -521,6 +521,22 @@ retry_num_search_out:
        return ch;
 }
 
+struct pcm_channel *
+pcm_chn_iterate(struct snddev_info *d, void **cookie)
+{
+       struct snddev_channel **last = (struct snddev_channel **)cookie;
+
+       if (*last == NULL)
+               *last = SLIST_FIRST(&d->channels);
+       else
+               *last = SLIST_NEXT(*last, link);
+
+       if (*last == NULL)
+               return NULL;
+       else
+               return (*last)->channel;
+}
+
 int
 pcm_chn_destroy(struct pcm_channel *ch)
 {
index 9c3d1f4..b64d75d 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/sound.h,v 1.63.2.2 2006/04/04 17:43:48 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.10 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.11 2007/06/14 21:48:36 corecode Exp $
  */
 
 /*
@@ -221,6 +221,7 @@ int pcm_chnref(struct pcm_channel *c, int ref);
 int pcm_inprog(struct snddev_info *d, int delta);
 
 struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo);
+struct pcm_channel *pcm_chn_iterate(struct snddev_info *d, void **cookie);
 int pcm_chn_destroy(struct pcm_channel *ch);
 int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch);
 int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch);
index 8f82018..fc148b9 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.4 2006/04/04 17:43:49 ariff Exp $
- * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.5 2007/01/04 21:47:03 corecode Exp $
+ * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.6 2007/06/14 21:48:36 corecode Exp $
  */
 
 #include <dev/sound/pcm/sound.h>
 #include <dev/sound/pcm/vchan.h>
 #include "feeder_if.h"
 
-SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.5 2007/01/04 21:47:03 corecode Exp $");
+SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.6 2007/06/14 21:48:36 corecode Exp $");
 
 /*
  * Default speed
@@ -53,18 +53,27 @@ static u_int32_t vchan_fmt[] = {
 };
 
 static int
-vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
+vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count, int volume)
 {
        /*
         * to is the output buffer, tmp is the input buffer
         * count is the number of 16bit samples to mix
+        * volume is in range 0-100
         */
        int i;
        int x;
+       int scale;
+       int doscale;
+
+       scale = (volume << 16) / 100;
+       doscale = volume != 100;
 
        for(i = 0; i < count; i++) {
                x = to[i];
-               x += tmp[i];
+               if (doscale)
+                       x += ((int)tmp[i] * scale) >> 16;
+               else
+                       x += tmp[i];
                if (x < -32768) {
                        /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
                        x = -32768;
@@ -88,6 +97,7 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
        uint32_t sz;
        int16_t *tmp, *dst;
        unsigned int cnt, rcnt = 0;
+       int volume;
 
        #if 0
        if (sndbuf_getsize(src) < count)
@@ -118,7 +128,10 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
                        if (ch->flags & CHN_F_MAPPED)
                                sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
                        cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
-                       vchan_mix_s16(dst, tmp, cnt >> 1);
+                       volume = ch->volume & 0xff;     /* XXX do special stereo processing? */
+                       volume += (ch->volume >> 8) & 0xff;
+                       volume >>= 1;
+                       vchan_mix_s16(dst, tmp, cnt >> 1, volume);
                        if (cnt > rcnt)
                                rcnt = cnt;
                }