openvortex-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Openvortex-dev] Re: Volume per voices [Feature Request]


From: Raymond
Subject: [Openvortex-dev] Re: Volume per voices [Feature Request]
Date: Wed, 31 Aug 2005 22:54:52 +0800
User-agent: Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.7.8) Gecko/20050603 Fedora/1.7.8-1.1.1.legacy


Description of the patch (beta)
- add PCM Volume control by using the hardware mixer of au88x0


TEST AT YOUR OWN RISK !!!

The audio will be distorted when total gain exceed the limit of 18-bits
DAC in AC97 codec.


Any comment/suggestion or programs to test the feature are welcome.


Hi,

On Mon, 2005-08-29 at 10:28 +0800, Raymond wrote:
> Takashi Iwai wrote:
> > At Mon, 01 Aug 2005 00:37:26 +0800,
> > Raymond wrote:
> >
> >>Jaroslav Kysela wrote:
> >>
> >>>On Thu, 28 Jul 2005, Raymond wrote:
> Can this kind of volume control with iface =
> SNDRV_CTL_ELEM_IFACE_PCM  used by mixer application (e.g. alsamixer)
> or only the application which  open the PCM substream ?

I guess not, because its PCM specific, and only valid on a PCM device
open context. Trying to find out which app is opening which PCM device
is just nonsense.

> Distortion occur when the combined gain of audio data, hardware
> mixer ,  equalizer and 3D effect > 6dB ( limit by the 18-bits DAC i
> n AC97 codec)

The maximum gain possible for a entire audio pipe should obvisouly not
exceeded.

> Can negative number can be used in the kcontrol with type
> SNDRV_CTL_ELEM_TYPE_INTEGER ? (i.e. uinfo->value.integer.min < 0 )

AFAIK, yes.

It seem the kcontrols of most drivers are using positive range only.

> What is the maximum value can be assigned to
> uinfo->value.integer.max ?

Is it possible to transfer 32-bits data using the kcontrol with type
SNDRV_CTL_ELEM_TYPE_INTEGER ?

> In the ALSA au88x0 driver, the hardware mixer provide default gain
> of  6dB ( 16-bits auido data to 18-bits DAC in AC97 codec ) for the
> left/right channels of au8820 and the rear channels of au8810/au8830
> (to  SDAC of quad codec),
 >
> For the front channels of au8810/au8830, zero gain in hardware mixer
> and  this 6dB gain is most likely controlled by the equalizer or 3D
> effect.
>
> Is there any faster method to find the substream of the subdevice in
> snd_vortex_adb_pcm_vol_put ?
 >
> struct snd_vortex {
>
> ...
> snd_kcontrol_t *ctl_pcm_vol[NR_ADB];
> int mixin[NR_ADB][4]; /* MIXIN */
>          int pcm_vol[NR_ADB][4];         /* PCM VOLUME */
> ...
>
> }
>
> static int snd_vortex_adb_pcm_vol_info(snd_kcontrol_t *kcontrol,
> snd_ctl_elem_info_t * uinfo)
> {
> vortex_t *vortex = snd_kcontrol_chip(kcontrol);
> uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
> uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
> uinfo->value.integer.min = 0;
> uinfo->value.integer.max = 255;
> return 0;
> }
>
> static int snd_vortex_adb_pcm_change_vol(vortex_t *vortex,int
> mixin,int  mix,int volume)
> {
> if ( volume >= 128 )
> vortex_mix_setinputvolumebyte(vortex, mix, mixin, volume-128);
>   // GAIN N
> else
> vortex_mix_setinputvolumebyte(vortex, mix, mixin, volume+128);
>  // ATTEN
> return 0;
> }

I would suggest using the correct signed or unsigned type instead of
doing explicit type conversions.

> static int snd_vortex_adb_pcm_vol_get(snd_kcontrol_t * kcontrol,
> snd_ctl_elem_value_t * ucontrol)
> {
> int i,subdevice;
> vortex_t *vortex = snd_kcontrol_chip(kcontrol);
> subdevice = kcontrol->id.subdevice;
> for (i=0; i<(VORTEX_IS_QUAD(vortex) ? 4 : 2 ); i++)
> ucontrol->value.integer.value[i] = vortex->pcm_vol[subdevice][i];
> return 0;
> }

As the resource manager is written now, there is no other way to do
this. Any attempt to speed up would make the whole thing more complex,
and in opinion is not required. iterating 32 or 64 times in a short
loop  on current PC is not a significant worload.

> static int snd_vortex_adb_pcm_vol_put(snd_kcontrol_t * kcontrol,
> snd_ctl_elem_value_t * ucontrol)
> {
> int i,j,changed,subdevice;
> vortex_t *vortex = snd_kcontrol_chip(kcontrol);
> subdevice=kcontrol->id.subdevice;
> changed = 0;
> if ( ( kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE ) ==
0 ) {
> for (i=0; i<NR_ADB; i++) {
> if ( vortex->dma_adb[i].substream != NULL ) {
> if ( vortex->dma_adb[i].substream->number == subdevice ) {
> for (j=0; j<(VORTEX_IS_QUAD(vortex) ? 4 : 2 ); j++) {
> if ( vortex->pcm_vol[subdevice][j] !=
> ucontrol->value.integer.value[j] )
> changed=1;
> vortex->pcm_vol[subdevice][j] = ucontrol->value.integer.value[j];
> };
> switch(vortex->dma_adb[i].nr_ch) {
> case 1:
> for (j=0; j<(VORTEX_IS_QUAD(vortex) ? 4 : 2 ); j++) {
> snd_vortex_adb_pcm_change_vol(vortex,
> vortex->mixin[subdevice][0], vortex->mixplayb[j],
> vortex->pcm_vol[subdevice][j]);
> };
> break;
> case 2:
> for (j=0; j<2; j++) {
> snd_vortex_adb_pcm_change_vol(vortex,
> vortex->mixin[subdevice][j], vortex->mixplayb[j],
> vortex->pcm_vol[subdevice][j]);
> if (VORTEX_IS_QUAD(vortex))
> snd_vortex_adb_pcm_change_vol(vortex,
> vortex->mixin[subdevice][j], vortex->mixplayb[j+2],
> vortex->pcm_vol[subdevice][j+2]);
> };
> break;
> case 4:
> for (j=0; j<4; j++) {
> snd_vortex_adb_pcm_change_vol(vortex,
> vortex->mixin[subdevice][j], vortex->mixplayb[j],
> vortex->pcm_vol[subdevice][j]);
> };
> break;
> };
> return changed;
> };
> };
> };
> };
> return changed;
> }

I have not much time right now, but the switch statement looked to me
somewhat redundant (?).


no. of MIXIN = no. of SRC = no. of channels of PCM substream = nr_ch

no. of MIXOUT = no. of channels in AC97 codec

Mono
                                  0dB
DMA -> FIFO ----> SRC -> MIXIN ---> MIXOUT -> Equalizer -> Front Left
                              |   0dB
                              +------> MIXOUT -> Equalizer -> Front Right
                              |   6dB
                              +------> MIXOUT --------------> Rear Left
                              |   6dB
                              +------> MIXOUT --------------> Rear Right

Stereo

                                  0dB
DMA -> FIFO ----> SRC -> MIXIN ---> MIXOUT -> Equalizer -> Front Left
                  |           |   6dB
                  |           +------> MIXOUT --------------> Rear Left
                  |               0dB
                  +> SRC -> MIXIN ---> MIXOUT -> Equalizer -> Front Right
                              |   6dB
                              +------> MIXOUT --------------> Rear Right

Surround40

                                  0dB
DMA -> FIFO ----> SRC -> MIXIN ---> MIXOUT -> Equalizer -> Front Left
                  |               0dB
                  +> SRC -> MIXIN ---> MIXOUT -> Equalizer -> Front Right
                  |               6dB
                  +> SRC -> MIXIN ---> MIXOUT --------------> Rear Left
                  |               6dB
                  +> SRC -> MIXIN ---> MIXOUT --------------> Rear Right


> static snd_kcontrol_new_t snd_vortex_adb_pcm_vol = {
> .iface = SNDRV_CTL_ELEM_IFACE_PCM,
> .name = "ADB PCM Volume",
> .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
> SNDRV_CTL_ELEM_ACCESS_INACTIVE,
> .info = snd_vortex_adb_pcm_vol_info,
> .get = snd_vortex_adb_pcm_vol_get,
> .put = snd_vortex_adb_pcm_vol_put,
> };
>
>
> Do we need to make the front and rear channels of au8810/au8830 with
> equal default gain and equal volume range ? e.g. Adding mixer
> between  equalizer and AC97 and a switch to allow each substream
> to bypass  equalizer, the other way is to set equalizer in bypass
> mode for all  substreams.

There is no need to waste any more mixers (there are a very precious
resource and its easy to run out of them). The EQ has a global gain
control, which should used instead.

The alternative way is to implement a Front PCM Volume control and a
Rear PCM Volume control.














diff -Naur ../au88x0_cvs/au88x0.c 
cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0.c
--- ../au88x0_cvs/au88x0.c      2005-04-11 23:58:27.000000000 +0800
+++ cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0.c     2005-08-29 
19:21:04.000000000 +0800
@@ -250,6 +250,11 @@
        }
        snd_vortex_workaround(pci, pcifix[dev]);
        // (4) Alloc components.
+       // snd_ac97_mixer and Vortex mixer.
+       if ((err = snd_vortex_mixer(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
        // ADB pcm.
        if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
                snd_card_free(card);
@@ -281,11 +288,6 @@
                return err;
        }
 #endif
-       // snd_ac97_mixer and Vortex mixer.
-       if ((err = snd_vortex_mixer(chip)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
        if ((err = snd_vortex_midi(chip)) < 0) {
                snd_card_free(card);
                return err;
diff -Naur ../au88x0_cvs/au88x0_core.c 
cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0_core.c
--- ../au88x0_cvs/au88x0_core.c 2005-02-23 19:00:31.000000000 +0800
+++ cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0_core.c        2005-08-29 
23:32:28.000000000 +0800
@@ -2038,8 +2039,6 @@
 }
 
 /* Default Connections  */
-static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int 
type);
 
 static void vortex_connect_default(vortex_t * vortex, int en)
 {
@@ -2099,7 +2098,7 @@
   Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
 */
 static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int 
type, snd_pcm_substream_t *substream)
 {
        stream_t *stream;
        int i, en;
@@ -2154,9 +2153,12 @@
                                                return -EBUSY;
                                        }
                                }
+                               if (stream->type == VORTEX_PCM_ADB) {
+                                       
vortex->mixin[substream->number][i]=mix[i];
+                               };
                        }
                }
@@ -2255,6 +2257,21 @@
                                     ADB_MIXOUT(vortex->mixspdif[1]),
                                     ADB_SPDIFOUT(1));
                }
+               if (stream->type == VORTEX_PCM_ADB) {
+                       for (i=0; i<2; i++) {
+#ifdef AU8820_CHIP
+                               
vortex->pcm_vol[substream->number][i]=128+MIX_DEFOGAIN;
+#else
+                               vortex->pcm_vol[substream->number][i]=128;      
+#endif
+                       };
+                       if (VORTEX_IS_QUAD(vortex)) {
+                               for (i=2; i<4; i++) {
+                                       
vortex->pcm_vol[substream->number][i]=128+MIX_DEFOGAIN;
+                               };
+                       };
+               };
+
 #endif
        /* CAPTURE ROUTES. */
        } else {
diff -Naur ../au88x0_cvs/au88x0.h 
cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0.h
--- ../au88x0_cvs/au88x0.h      2005-03-22 16:50:55.000000000 +0800
+++ cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0.h     2005-08-29 
21:47:27.000000000 +0800
@@ -155,10 +157,15 @@
        s8 mixxtlk[2];  /* crosstalk canceler mixer inputs. */
 #endif
        u32 fixed_res[5];
+       snd_kcontrol_t *ctl_pcm_vol[NR_ADB];
+       int mixin[NR_ADB][4];
+       int pcm_vol[NR_ADB][4];
 
 #ifndef CHIP_AU8820
        /* Hardware equalizer structs */
        eqlzr_t eq;
@@ -238,7 +245,7 @@
 /* Connection  stuff. */
 static void vortex_connect_default(vortex_t * vortex, int en);
 static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
-                                int dir, int type);
+                                int dir, int type, snd_pcm_substream_t 
*substream);
 static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
                                  int restype);
 #ifndef CHIP_AU8810
diff -Naur ../au88x0_cvs/au88x0_pcm.c 
cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0_pcm.c
--- ../au88x0_cvs/au88x0_pcm.c  2005-08-27 20:02:26.000000000 +0800
+++ cvs/alsa-driver/alsa-kernel/pci/au88x0/au88x0_pcm.c 2005-08-31 
14:02:40.000000000 +0800
@@ -116,6 +116,17 @@
        .periods_max = 64,
 };
 #endif
+
+static void snd_vortex_notify_pcm_change(snd_card_t * card, snd_kcontrol_t 
*kctl, int activate)
+{
+       snd_runtime_check(kctl != NULL, return);
+       if (activate)
+               kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       else
+               kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &(kctl->id));
+}
+
 /* open callback */
 static int snd_vortex_pcm_open(snd_pcm_substream_t * substream)
 {
@@ -171,11 +182,18 @@
 /* close callback */
 static int snd_vortex_pcm_close(snd_pcm_substream_t * substream)
 {
-       //vortex_t *chip = snd_pcm_substream_chip(substream);
+       vortex_t *vortex = snd_pcm_substream_chip(substream);
        stream_t *stream = (stream_t *) substream->runtime->private_data;
 
        // the hardware-specific codes will be here
        if (stream != NULL) {
+               if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+                       if ( substream->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
+                               vortex = snd_pcm_substream_chip(substream);
+                               snd_vortex_notify_pcm_change(vortex->card, 
vortex->ctl_pcm_vol[substream->number], 0);
+                       };
+               };
+
                stream->substream = NULL;
                stream->nr_ch = 0;
        }
@@ -214,16 +232,23 @@
                if (stream != NULL)
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type, substream);
                /* Alloc routes. */
                dma =
                    vortex_adb_allocroute(chip, -1,
                                          params_channels(hw_params),
-                                         substream->stream, type);
+                                         substream->stream, type, substream);
                if (dma < 0) {
                        spin_unlock_irq(&chip->lock);
                        return dma;
-               }
+               };
+
+               if ( substream->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
+                       if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB ) 
{
+                               snd_vortex_notify_pcm_change(chip->card, 
chip->ctl_pcm_vol      [substream->number], 1);
+                       };
+               };
+
                stream = substream->runtime->private_data = &chip->dma_adb[dma];
                stream->substream = substream;
                /* Setup Buffers. */
@@ -262,7 +287,7 @@
                if (stream != NULL)
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type, substream);
        }
 #ifndef CHIP_AU8810
        else {
@@ -432,6 +457,91 @@
        "i2s",
 };
 
+/* ADB PCM Volume control */
+
+static int snd_vortex_adb_pcm_vol_info(snd_kcontrol_t *kcontrol, 
snd_ctl_elem_info_t * uinfo)
+{
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       uinfo->value.integer.min = 0;              
+       uinfo->value.integer.max = 136;            
+       return 0;
+}
+
+static int snd_vortex_adb_pcm_vol_get(snd_kcontrol_t * kcontrol, 
snd_ctl_elem_value_t * ucontrol)
+{
+       int i,subdevice;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       subdevice = kcontrol->id.subdevice;
+       for (i=0; i<(VORTEX_IS_QUAD(vortex) ? 4 : 2 ); i++) 
+               ucontrol->value.integer.value[i] = 
vortex->pcm_vol[subdevice][i];       
+       return 0;
+}
+
+static int snd_vortex_adb_pcm_change_vol(vortex_t *vortex,int mixin,int 
mix,int volume)
+{
+       if ( volume >= 128 )
+               vortex_mix_setinputvolumebyte(vortex, mix, mixin, volume-128);  
        // GAIN
+       else
+               if ( volume < 80 )                                              
                                vortex_mix_setinputvolumebyte(vortex, mix, 
mixin, 0x80);        // MUTE
+               else
+                       vortex_mix_setinputvolumebyte(vortex, mix, mixin, 
volume+128);  // ATTEN
+       return 0;
+}
+
+static int snd_vortex_adb_pcm_vol_put(snd_kcontrol_t * kcontrol, 
snd_ctl_elem_value_t * ucontrol)
+{
+       int i,j,changed,subdevice;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       subdevice=kcontrol->id.subdevice;
+       changed = 0;
+       if ( ( kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE ) == 0 ) 
{
+               for (i=0; i<NR_ADB; i++) {
+                       if ( vortex->dma_adb[i].substream != NULL ) {
+                               if ( vortex->dma_adb[i].substream->number == 
subdevice ) {
+                                       for (j=0; j<(VORTEX_IS_QUAD(vortex) ? 4 
: 2 ); j++) {
+                                               if ( 
vortex->pcm_vol[subdevice][j] != ucontrol->value.integer.value[j] )
+                                                       changed=1;
+                                               vortex->pcm_vol[subdevice][j] = 
ucontrol->value.integer.value[j];
+                                       };
+                                       switch(vortex->dma_adb[i].nr_ch) {
+                                       case 1:
+                                               for (j=0; 
j<(VORTEX_IS_QUAD(vortex) ? 4 : 2 ); j++) {
+                                                       
snd_vortex_adb_pcm_change_vol(vortex, vortex->mixin[subdevice][0], 
vortex->mixplayb[j], vortex->pcm_vol[subdevice][j]);
+                                               };
+                                               break;
+                                       case 2:
+                                               for (j=0; j<2; j++) {
+                                                       
snd_vortex_adb_pcm_change_vol(vortex,
+vortex->mixin[subdevice][j], vortex->mixplayb[j], 
vortex->pcm_vol[subdevice][j]);
+                                                       if 
(VORTEX_IS_QUAD(vortex))
+                                                               
snd_vortex_adb_pcm_change_vol(vortex, vortex->mixin[subdevice][j], 
vortex->mixplayb[j+2], vortex->pcm_vol[subdevice][j+2]);
+                                               };
+                                               break;
+                                       case 4:
+                                               for (j=0; j<4; j++) {
+                                                       
snd_vortex_adb_pcm_change_vol(vortex, vortex->mixin[subdevice][j], 
vortex->mixplayb[j], vortex->pcm_vol[subdevice][j]);
+                                               };
+                                               break;
+                                       };
+                                       return changed;
+                               };
+                       };
+               };
+       };
+       return changed;
+}
+
+static snd_kcontrol_new_t snd_vortex_adb_pcm_vol = {
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         CARD_NAME_SHORT " ADB PCM Volume",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READWRITE | 
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+       .info =         snd_vortex_adb_pcm_vol_info,
+       .get =          snd_vortex_adb_pcm_vol_get,
+       .put =          snd_vortex_adb_pcm_vol_put,
+};
+
 /* SPDIF kcontrol */
 
 static int snd_vortex_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t 
* uinfo)
@@ -537,6 +647,22 @@
                                              snd_dma_pci_data(chip->pci_dev),
                                              0x10000, 0x10000);
 
+       if (idx == VORTEX_PCM_ADB) {
+               for (i= 0; i<NR_ADB; i++) {
+                       chip->ctl_pcm_vol[i] = 
snd_ctl_new1(&snd_vortex_adb_pcm_vol,chip);
+                       if (!chip->ctl_pcm_vol[i]) {
+                               snd_printk("snd_ctl_new1() failed - ADB PCM 
Volume control\n");
+                               return -ENOMEM;
+                       };
+                       chip->ctl_pcm_vol[i]->id.device=0;
+                       chip->ctl_pcm_vol[i]->id.subdevice=i;
+                        if ((err = snd_ctl_add(chip->card, 
chip->ctl_pcm_vol[i])) < 0) {
+                               snd_printk("snd_ctl_add() failed - ADB PCM 
Volume control\n");
+                               return err;
+                       };
+               };
+       };      
+
        if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
                for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
                        kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]