Logo Search packages:      
Sourcecode: vat version File versions  Download package

audio-win32.cc

/*
 * Copyright (c) 1996 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the Network Research
 *    Group at Lawrence Berkeley National Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
static const char rcsid[] =
    "@(#) $Header: audio-win32.cc,v 1.10 96/05/14 06:44:53 van Exp $ (LBL)";

#include <assert.h>
#include <winsock.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "audio.h"
#include "ss.h"
#include "Tcl.h"

#define BLKS_PER_READ 4
#define READ_AHEAD 8
#define BLKS_PER_WRITE 4
#define WRITE_AHEAD 4

class audMux {
    public:
      MIXERCONTROLDETAILS select_[8];
      MIXERCONTROLDETAILS vol_[8];
      u_char mcnt_;
      u_char vcnt_;
      char mmap_[8];
      char vmap_[8];
      u_char isOut_;
};

class Win32Audio : public Audio {
    public:
      Win32Audio();
      ~Win32Audio();
      void Write(u_char *);
      int FrameReady();
      u_char* Read();
      void SetRGain(int);
      void SetPGain(int);
      void OutputPort(int);
      void InputPort(int);
      void Obtain();
      void Release();
      void RMute();
      void RUnmute();
      int HalfDuplex() const;

    protected:
      int OpenOut();
      void CloseOut();
      int outErr(int) const;
      int OpenIn();
      void CloseIn();
      int inErr(int) const;
      int mapName(audMux& mux, const char* nm);
      int mapMixerPort(audMux& mux, const char* name);
      void setupMux(audMux& mux, DWORD ctype);
      void getMixerDetails(MIXERLINE&, MIXERCONTROL&, audMux&);
      void getMixerCtrls(MIXERLINE&, audMux&);

      HWAVEOUT out_;
      HWAVEIN  in_;

      u_char* rbuf_;
      u_char* rbufStart_;
      u_char* rbufEnd_;
      u_char* zbuf_;
      u_int lastmean_;
      u_int ibindx_;
      u_char* ibufStart_;
      u_char* ibufEnd_;

      u_char* obuf_;
      u_char* obufStart_;
      u_char* obufEnd_;

      u_short iblen_;
      u_short oblen_;

      WAVEHDR iwhdr_[READ_AHEAD];
      WAVEHDR owhdr_[BLKS_PER_WRITE * WRITE_AHEAD];
      const WAVEFORMATEX* iformat_;
      const WAVEFORMATEX* oformat_;

      audMux omux_;
      audMux imux_;
};

static class Win32AudioMatcher : public Matcher {
    public:
      Win32AudioMatcher() : Matcher("audio") {}
      TclObject* match(const char* fmt) {
            if (strcmp(fmt, "pc") == 0)
                  return (new Win32Audio);
            return (0);
      }
} win32_audio_matcher;

extern void adios();
extern "C" const u_char lintomulawX[];
extern "C" const short mulawtolin[];

static const WAVEFORMATEX lin16fmt = {
      WAVE_FORMAT_PCM,
      1,
      8000,
      2 * 8000,
      2,
      16,
      0
};
static const WAVEFORMATEX lin8fmt = {
      WAVE_FORMAT_PCM,
      1,
      8000,
      8000,
      1,
      8,
      0
};


Win32Audio::Win32Audio() : 
      out_(0), 
      in_(0), 
      lastmean_(0)
{
      zbuf_ = new u_char[blksize];
      memset(zbuf_, ULAW_ZERO, blksize);

      u_int len = blksize * BLKS_PER_READ;
      rbufStart_ = new u_char[len];
      rbufEnd_ = rbufStart_ + len;
      rbuf_ = rbufEnd_;

      /*
       * figure out what input format is available.
       */
      int sts = waveInOpen(0, WAVE_MAPPER, &lin16fmt, 0, 0,
                       WAVE_FORMAT_QUERY);
      if (sts == WAVERR_BADFORMAT) {
            /* can't do 16 bit audio, try 8 bit */
            sts = waveInOpen(0, WAVE_MAPPER, &lin8fmt, 0, 0,
                         WAVE_FORMAT_QUERY);
            if (sts) {
                  fprintf(stderr,
    "vat: soundcard supports neither 16 nor 8 bit 8KHz PCM audio input (%d)\n",
                        sts);
                  adios();
            }
            iformat_ = &lin8fmt;
            iblen_ = len;
            len *= READ_AHEAD;
            ibufStart_ = new u_char[len];
            ibufEnd_ = ibufStart_ + len;
      } else {
            iformat_ = &lin16fmt;
            iblen_ = len * sizeof(short);
            len *= READ_AHEAD;
            ibufStart_ = (u_char*)new short[len];
            ibufEnd_ = ibufStart_ + len * sizeof(short);
      }

      /*
       * figure out what output format is available.
       */
      sts = waveOutOpen(0, WAVE_MAPPER, &lin16fmt, 0, 0,
                    WAVE_FORMAT_QUERY);
      if (sts == WAVERR_BADFORMAT) {
            /* can't do 16 bit audio, try 8 bit */
            sts = waveOutOpen(0, WAVE_MAPPER, &lin8fmt, 0, 0,
                          WAVE_FORMAT_QUERY);
            if (sts) {
                  fprintf(stderr,
    "vat: soundcard supports neither 16 nor 8 bit 8KHz PCM audio output (%d)\n",
                        sts);
                  adios();
            }
            oformat_ = &lin8fmt;
            len = blksize * BLKS_PER_WRITE;
            oblen_ = len;
            len *= WRITE_AHEAD;
            obufStart_ = new u_char[len];
            obufEnd_ = obufStart_ + len;
      } else {
            oformat_ = &lin16fmt;
            len = blksize * BLKS_PER_WRITE;
            oblen_ = len * sizeof(short);
            len *= WRITE_AHEAD;
            obufStart_ = (u_char*)new short[len];
            obufEnd_ = obufStart_ + len * sizeof(short);
      }

      if (mixerGetNumDevs()) {
            /* set up the mixer controls for input & out select & gain */
            memset(&imux_, 0, sizeof(imux_));
            memset(&imux_.mmap_, -1, sizeof(imux_.mmap_));
            setupMux(imux_, MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
            int i;
            for (i = 0; i < sizeof(imux_.mmap_); ++i)
                  if (iports < imux_.mmap_[i])
                        iports = imux_.mmap_[i];

            memset(&omux_, 0, sizeof(omux_));
            memset(&omux_.mmap_, -1, sizeof(omux_.mmap_));
            omux_.isOut_ = 1;
            setupMux(omux_, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
            for (i = 0; i < sizeof(omux_.mmap_); ++i)
                  if (oports < omux_.mmap_[i])
                        oports = omux_.mmap_[i];
      }
}

Win32Audio::~Win32Audio()
{
      CloseIn();
      CloseOut();

      delete zbuf_;
      delete rbufStart_;
      delete ibufStart_;
      delete obufStart_;
}

int Win32Audio::HalfDuplex() const
{
      /*XXX*/
      return 1;
}

int Win32Audio::inErr(int error) const
{
      if (error) {
            char errorText[MAXERRORLENGTH];
            waveInGetErrorText(error, errorText, sizeof(errorText));
            fprintf(stderr, "vat - input error: %s\n", errorText);
      }
      return (error);
}

int Win32Audio::outErr(int error) const
{
      if (error) {
            char errorText[MAXERRORLENGTH];
            waveOutGetErrorText(error, errorText, sizeof(errorText));
            fprintf(stderr, "vat - output error: %s\n", errorText);
      }
      return (error);
}

int Win32Audio::OpenOut()
{
      int error = 0;

      if (out_ == 0) {
            error = waveOutOpen(&out_, WAVE_MAPPER, oformat_,
                            NULL, NULL, CALLBACK_NULL);
                            
            /*
             * Maybe we failed because someone is playing sound already.
             * Shut any sound off then try once more before giving up.
             */
            if (error) {
                  sndPlaySound(NULL, 0);
                  if (outErr(waveOutOpen(&out_, WAVE_MAPPER, oformat_,
                                     NULL, NULL, CALLBACK_NULL))) {            
                        return 1;
                  }
            }
            /* restore the gain to what the user set on this vat window */
            SetPGain(pgain);

            /* (re-)initialize the output buffer descriptors */
            memset(owhdr_, 0, sizeof(owhdr_));
            u_char* bp = obufStart_;
            obuf_ = bp;
            u_int len = oblen_;
            int i;
            for (i = 0; i < WRITE_AHEAD; ++i) {
                  /*
                   * we mark the last hdr of the group of hdrs
                   * associated with this write block as 'writeable'
                   * (by setting its DONE bit) but make the address
                   * in the hdr indicate the start of the group of
                   * blocks.
                   */
                  WAVEHDR* whp = &owhdr_[(i + 1) * BLKS_PER_WRITE - 1];
                  whp->dwFlags = 0;
                  whp->dwBufferLength = oblen_;
                  whp->lpData = (char*)bp;
                  outErr(waveOutPrepareHeader(out_, whp, sizeof(*whp)));
                  whp->dwFlags |= WHDR_DONE;
                  bp += len;
            }
            /*
             * do initial write to generate a backlog to avoid
             * dropouts due to scheduling delays.
             */
            for (i = BLKS_PER_WRITE; --i >= 0; )
                  Write(zbuf_);
      }
      return error;
}

int Win32Audio::OpenIn()
{
      if (in_ == 0) {
            if (inErr(waveInOpen(&in_, WAVE_MAPPER, iformat_,
                             NULL, NULL, CALLBACK_NULL)))
                  return (1);

            /* restore the gain to what the user set on this vat window */
            SetRGain(rgain);

            /* (re-)initialize the input buffer descriptors */
            memset(iwhdr_, 0, sizeof(iwhdr_));
            ibindx_ = 0;
            rbuf_ = rbufEnd_;
            u_char* bp = ibufStart_;
            u_int len = iblen_;
            memset(bp, 0, len * READ_AHEAD);
            for (int i = 0; i < READ_AHEAD; ++i) {
                  WAVEHDR* whp = &iwhdr_[i];
                  whp->dwFlags = 0;
                  whp->dwBufferLength = len;
                  whp->lpData = (char*)bp;
                  bp += len;
                  waveInPrepareHeader(in_, whp, sizeof(*whp));
                  if (inErr(waveInAddBuffer(in_, whp, sizeof(*whp)))) {
                        CloseIn();
                        return (1);
                  }
            }
            waveInStart(in_);
      }
      return 0;
}

void Win32Audio::CloseOut()
{
      if (out_) {
            waveOutReset(out_);
            for (int i = 1; i < WRITE_AHEAD + 1; ++i) {
                  WAVEHDR* whp = &owhdr_[i * BLKS_PER_WRITE - 1];
                  if (whp->dwFlags & WHDR_PREPARED)
                        waveOutUnprepareHeader(out_, whp, sizeof(*whp));
            }
            waveOutClose(out_);
            out_ = 0;
      }
}

void Win32Audio::CloseIn()
{
      if (in_) {
            waveInStop(in_);
            waveInReset(in_);
            for (int i = 0; i < READ_AHEAD; ++i) {
                  WAVEHDR* whp = &iwhdr_[i];
                  if (whp->dwFlags & WHDR_PREPARED)
                        waveInUnprepareHeader(in_, whp, sizeof(*whp));
            }
            waveInClose(in_);
            in_ = 0;
      }
}

void Win32Audio::Obtain()
{
      int error;

      if (HaveAudio())
            abort();

      if (rmute & 1)
            error = OpenOut();
      else
            error = OpenIn();
      if (error)
            fd = -1;
      else {
            fd = 0;
            SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
      }
      notify();
}

void Win32Audio::Release()
{
      CloseOut();
      CloseIn();
      SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
      Audio::Release();
}

void Win32Audio::Write(u_char *cp)
{
      if (out_) {
            /*
             * copy the new data into our circular output buffer,
             * converting from ulaw to linear as we go.
             */
            WAVEHDR* whp;     
            if (oformat_ == &lin8fmt) {
                  u_char* sp = obuf_;
                  u_int bindx = (sp - obufStart_) / blksize;
                  whp = &owhdr_[bindx];   
                  u_char* ep = sp + blksize;
                  const short* u2l = mulawtolin;
                  while (sp < ep)
                        *sp++ = (u2l[*cp++] >> 8) ^ 0x80;
                  obuf_ = (ep >= obufEnd_)? obufStart_ : ep;
            } else {
                  short* sp = (short*)obuf_;
                  u_int bindx = (sp - (short*)obufStart_) / blksize;
                  whp = &owhdr_[bindx];   
                  short* ep = sp + blksize;
                  const short* u2l = mulawtolin;
                  while (sp < ep)
                        *sp++ = u2l[*cp++];
                  obuf_ = ((u_char*)ep >= obufEnd_)? obufStart_ : (u_char*)ep;
            }

            /*
             * if the buffer descriptor associated with this block
             * is marked ready, ship it.
             */
            if (whp->dwFlags & WHDR_DONE) {
                  whp->dwFlags &=~ WHDR_DONE;
                  outErr(waveOutWrite(out_, whp, sizeof(*whp)));
            }
      }
}

int Win32Audio::FrameReady()
{
      if (in_ && rbuf_ >= rbufEnd_) {
            /* mulaw conversion buffer is empty - see if a read finished */
            u_int i = ibindx_;
            WAVEHDR* whp = iwhdr_ + i;
            if ((whp->dwFlags & WHDR_DONE) == 0)
                  return (0);

            /* read finished - move input to ulaw buffer */
            rbuf_ = rbufStart_;
            ibindx_ = (i + 1) % READ_AHEAD;

            const u_char* l2u = lintomulawX;
            u_int* ip = (u_int*)rbuf_;
            int smean = lastmean_;
            if (iformat_ == &lin8fmt) {
                  u_char* sp = (u_char*)whp->lpData;
                  u_char* ep = sp + whp->dwBytesRecorded;
                  for ( ; sp < ep; sp += 4) {
                        register int mean, dif;
                        register u_int res;
                        register int s0 = int(sp[0]) - 0x80 << 8;
                        register int s1 = int(sp[1]) - 0x80 << 8;
                        register int s2 = int(sp[2]) - 0x80 << 8;
                        register int s3 = int(sp[3]) - 0x80 << 8;

                        mean = smean >> 13;
                        dif = s0 - mean;
                        smean += dif;
                        res = l2u[dif & 0x1ffff] << 0;

                        mean = smean >> 13;
                        dif = s1 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 8;

                        mean = smean >> 13;
                        dif = s2 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 16;

                        mean = smean >> 13;
                        dif = s3 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 24;

                        *ip++ = res;
                  }
            } else {
                  short* sp = (short*)whp->lpData;
                  short* ep = (short*)((char*)sp + whp->dwBytesRecorded);
                  for ( ; sp < ep; sp += 4) {
                        register int mean, dif;
                        register u_int res;
                        register int s0 = sp[0];
                        register int s1 = sp[1];
                        register int s2 = sp[2];
                        register int s3 = sp[3];

                        mean = smean >> 13;
                        dif = s0 - mean;
                        smean += dif;
                        res = l2u[dif & 0x1ffff] << 0;

                        mean = smean >> 13;
                        dif = s1 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 8;

                        mean = smean >> 13;
                        dif = s2 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 16;

                        mean = smean >> 13;
                        dif = s3 - mean;
                        smean += dif;
                        res |= l2u[dif & 0x1ffff] << 24;

                        *ip++ = res;
                  }
            }
            lastmean_ = smean;
            whp->dwFlags &=~ WHDR_DONE;
            inErr(waveInAddBuffer(in_, whp, sizeof(*whp)));
      }
      return (1);
}

u_char* Win32Audio::Read()
{
      u_char* cp;

      if (in_) {
            cp = rbuf_;
            rbuf_ = cp + blksize;
      } else
            cp = zbuf_;
      return (cp);
}

void Win32Audio::SetRGain(int level)
{
      rgain = level;
      if (in_) {
            if (level > 255)
                  level = 255;
            level <<= 8;
            MIXERCONTROLDETAILS& mcd = imux_.vol_[imux_.vmap_[iport]];
            for (u_int i = 0; i < mcd.cChannels; ++i)
               ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails + i)->dwValue =
                  level;
            mixerSetControlDetails(0, &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
      }
}

void Win32Audio::SetPGain(int level)
{
      pgain = level;
      if (out_) {
            if (level > 255)
                  level = 255;
            level <<= 8;
            DWORD vol = level | (level << 16);
            outErr(waveOutSetVolume(out_, vol));
      }
}

void Win32Audio::OutputPort(int p)
{
      if (omux_.mmap_[p] >= 0) {
            oport = p;
            if (out_)
                  mixerSetControlDetails(0,
                        &omux_.select_[omux_.mmap_[p]],
                        MIXER_SETCONTROLDETAILSF_VALUE);
      }
}

void Win32Audio::InputPort(int p)
{
      if (imux_.mmap_[p] >= 0) {
            iport = p;
            if (in_)
                  mixerSetControlDetails(0,
                        &imux_.select_[imux_.mmap_[p]],
                        MIXER_SETCONTROLDETAILSF_VALUE);
      }
}

void Win32Audio::RMute()
{
      CloseIn();
      if (OpenOut() == 0)
            rmute |= 1;
}

void Win32Audio::RUnmute()
{
      CloseOut();
      if (OpenIn() == 0)
            rmute &=~ 1;
}

int Win32Audio::mapName(audMux& mux, const char* name)
{
      return (mux.isOut_? StrToOPort(name) : StrToIPort(name));
}

int Win32Audio::mapMixerPort(audMux& mux, const char* name)
{
      int i = mapName(mux, name);
      if (i < 0) {
            char nm[64];
            strcpy(nm, name);
            char* cp;
            while ((cp = strrchr(nm, ' ')) != 0) {
                  *cp = 0;
                  if ((i = mapName(mux, nm)) >= 0)
                        break;
            }
      }
      return (i);
}

void Win32Audio::getMixerDetails(MIXERLINE& ml, MIXERCONTROL& mc, audMux& mux)
{
      MIXERCONTROLDETAILS mcd;

      mcd.cbStruct = sizeof(mcd);
      mcd.dwControlID = mc.dwControlID;
      mcd.cChannels = ml.cChannels;
      mcd.cMultipleItems = mc.cMultipleItems;
      if (mcd.cMultipleItems) {
            MIXERCONTROLDETAILS_LISTTEXT mcdt[16];    
            mcd.cbDetails = sizeof(mcdt[0]);
            mcd.paDetails = mcdt;
            u_int sts = mixerGetControlDetails(0, &mcd,
                                   MIXER_GETCONTROLDETAILSF_LISTTEXT);
            if (sts == 0) {
                  for (u_int i = 0; i < mc.cMultipleItems; ++i) {
                        int port = mapMixerPort(mux, mcdt[i].szName);
                        if (port >= 0)
                              mux.mmap_[port] = i;

                        u_int n = mcd.cMultipleItems * mcd.cChannels;
                        MIXERCONTROLDETAILS_BOOLEAN* mcdb =
                              new MIXERCONTROLDETAILS_BOOLEAN[n];
                        memset(mcdb, 0, n * sizeof(*mcdb));
                        for (u_int j = 0; j < mcd.cChannels; ++j)
                              mcdb[j*mc.cMultipleItems+i].fValue = 1;
                              
                        mux.select_[i] = mcd;
                        mux.select_[i].cbDetails = sizeof(*mcdb);
                        mux.select_[i].paDetails = mcdb;
                  }
                  mux.mcnt_ = (u_char)mcd.cMultipleItems;
            }
      } else {
            int i = mux.vcnt_++;
            int port = mapMixerPort(mux, ml.szName);
            if (port >= 0)
                  mux.vmap_[port] = i;

            MIXERCONTROLDETAILS_UNSIGNED* mcdu =
                        new MIXERCONTROLDETAILS_UNSIGNED[mcd.cChannels];
            memset(mcdu, 0, mcd.cChannels * sizeof(*mcdu));
            mux.vol_[i] = mcd;
            mux.vol_[i].cbDetails = sizeof(*mcdu);
            mux.vol_[i].paDetails = mcdu;
      }
}

void Win32Audio::getMixerCtrls(MIXERLINE& ml, audMux& mux)
{
      MIXERLINECONTROLS mlc;
      MIXERCONTROL mc[16];
      u_int i;

      memset(&mlc, 0, sizeof(mlc));
      memset(mc, 0, sizeof(mc));
      mlc.cbStruct = sizeof(mlc);
      mlc.cbmxctrl = sizeof(mc[0]);
      mlc.pamxctrl = &mc[0];
      mlc.dwLineID = ml.dwLineID;
      mlc.cControls = ml.cControls;
      mixerGetLineControls(0, &mlc, MIXER_GETLINECONTROLSF_ALL);
      for (i = 0; i < mlc.cControls; ++i) {
            switch (mc[i].dwControlType) {

            case MIXERCONTROL_CONTROLTYPE_MUX:
            case MIXERCONTROL_CONTROLTYPE_MIXER:
            case MIXERCONTROL_CONTROLTYPE_VOLUME:
                  getMixerDetails(ml, mc[i], mux);
                  break;
            }
      }
      /*
       * if there are multiple source lines for this line,
       * get their controls
       */
      for (i = 0; i < ml.cConnections; ++i) {
            MIXERLINE src;
            memset(&src, 0, sizeof(src));
            src.cbStruct = sizeof(src);
            src.dwSource = i;
            src.dwDestination = ml.dwDestination;
            if (mixerGetLineInfo(0, &src, MIXER_GETLINEINFOF_SOURCE) == 0)
                  getMixerCtrls(src, mux);
      }
}

void Win32Audio::setupMux(audMux& mux, DWORD ctype)
{
      MIXERLINE l;
      memset(&l, 0, sizeof(l));
      l.cbStruct = sizeof(l);
      l.dwComponentType = ctype;
      int s = mixerGetLineInfo(0, &l, MIXER_GETLINEINFOF_COMPONENTTYPE);
      if (s == 0)
            getMixerCtrls(l, mux);
}

Generated by  Doxygen 1.6.0   Back to index