Audio Mixer Control Classes

This is a new an improved version of the Audio Mixer Control Classes. The whole family of classes has undergone a major overhaul since this article was first posted. Bugs have been eliminated, code reuse through inheritance has been improved, one new class was added, and, yes, one class even disappeared! The CMixerSelector class has been merged with the CMixerSwitch class.

It takes quite a bit of work just to get a simple volume control to work in an application. But with the following classes:

    CMixerFader derived from CSliderCtrl
    CMixerSwitch derived from CButton
    CMixerNumber derived from CSpinButtonCtrl (new)
    CMixerPeakMeter derived from CStatic
all it takes is a few lines of code to get several controls up and running.

For example: the CMixerFader class is derived from CSliderCtrl, so all it takes to have a fully functional volume control is a slider control in a dialog box, and 3 lines of code (plus the include directive!).

Coming to terms with terms

The Audio Mixer Service is made up of audio lines. An audio line can have one or more channels (usually two for stereo).
There are two types of audio lines: Destination (output) lines, and Source (input) lines. To every destination line is attached one or more audio source lines. Every audio line -- either destination or source -- is controlled by the mixer controls associated with it.

Mixer controls should not be confused with Windows controls, as they are NOT graphical objects.

There are several types of mixer controls, the volume control being one of them. There are also mute controls, multiple selection controls, peak meters, just to name a few. Each audio line has certain types of mixer controls associated with it. A mixer control can perform any number of functions (such as control volume), depending on the characteristics of the associated audio line.
For example, the speakers line is a destination line. It has several mixer controls attached to it (volume, mute, bass, treble, etc.), and it has several source lines attached to it: Line-In, Auxiliary, Synthesizer, CD audio, etc.. In turn these source lines may have several mixer controls attached that control their behavior.

Audio Mixer Controls

There are several families of audio mixer controls. Not all audio mixer controls are implemented in the classes. Maybe eventually...
Here is a short description of these families, and of the mixer controls they contain:

    Faders (Implemented by the CMixerFader class)

    Fader controls are controls that can be adjusted up or down along a linear scale. Their range is from 0 to 65,535. The fader family of controls includes the following types:
    Fader, Volume, Bass, Treble, Equalizer.

    Lists (Implemented by the CMixerSwitch class)

    List controls provide single-select or multiple-select states for complex audio lines. The list family of controls includes the following types:
    Single-select, Multiplexer (MUX), Multiple-select, Mixer.

    Meters

    Meter controls measure data passing through an audio line. Their range varies according to their type. The meter family of controls includes the following types:
    Boolean, Peak (Implemented by the CMixerPeakMeter class) , Signed, Unsigned.

    Numbers (Implemented by the CMixerNumber class)

    Number controls allow the user to enter numerical data associated with a line. The numerical data is expressed as signed integers, unsigned integers, or integer decibel values.

    Sliders

    Slider controls are horizontal controls that can be adjusted to the left or right. This type of control is not implemented by this package.

    Switches (implemented by the CMixerSwitch class)

    Switch controls are boolean switches. The switch family of controls includes the following types:
    Boolean, Button, On/Off, Mute, Mono, Loudness, Stereo Enhanced.

    Time Controls

    Time controls allow the user to enter timing-related data, such as an echo delay or reverberation. The time control is not implemented by this package.

About the classes

All the classes presented here are derived from a Windows control. They use message reflection to handle specific messages internally. This way, the communication between the Windows control and its associated mixer control is taken care of inside the class.
All that is needed to get the control to work is to instantiate an object of a given class, and to call the Init() member function specific to each class to associate the Windows control and the mixer control.

    CMixerBase

    Base class of all the mixer classes. It takes care of opening the mixer device to get a valid handler. This handle, along with the number of channels is stored in member variables. It also tries to find the desired audio line. You don't use this class directly unless you want to derive new classes from it.

    CMixerFader

    Since this class is derived from CSliderCtrl, it allows the user to associate a slider control with a mixer fader control.

    CMixerSwitch

    Since this class is derived from CButton, it allows the user to associate a check box or radio button with a mixer switch control or to associate one or more CheckBoxes with a mixer selector (list) control. Selectors are used to select one or more input audio line associated with a given output line. For example, my SoundBlaster enables the selection of the CD audio, microphone, and line-in input lines associated with the speakers' output line.
    Selectors are multiple mixer controls, the number of items they contain is variable. Each item of the selector control is a boolean value. When you initialize a selector control, an appropriate internal table of boolean values is created. You should create as many check boxes or radio buttons as there are items items in the mixer list control. When you call Init(), the last argument passed is the index of the item a given button will control.

    These switches work on both channels simultaneously.

    CMixerNumber

    Since this class is derived from CSpinButtonCtrl, it allows the user to associate a static control with a mixer number control.

    CMixerPeakMeter

    Since this class is derived from CStatic, it allows the user to associate a static control with a mixer peak meter control.

    The peak meter works like the CProgressCtrl, except that it is vertical. It displays the audio level of the line it is associated with.

Which is which... and who is who!

Basically, what you have to do before using these classes, is to find out which mixer controls are available on your system, to determine to which destination or source line each control belongs to, and to determine which class implements a given mixer control.

To find out which types of audio lines and controls are available on your system, I have included a utility function you can use to determine the capabilities of your sound card and the available audio services. Its header file is MixerInfo.h, and its impementation file is MixerInfo.cpp. The function name is GetDevicesInfo(), and it takes as argument a filename for logging the results of the query.
I recommend you use it before doing anything else. It will list all available destination lines, and the source lines attached to every destination line. It will also list all the controls attached to a given line. The information is arranged in a tree-like structure:

Destination Line 0

    List of Controls ...
    Source Line 0
      List of Controls ...
    Source Line 1
      List of Controls ...
    Source Line 2
      List of Controls ...
Destination Line 1
    List of Controls ...
    Source Line 0
      List of Controls ...
    Source Line 1
      List of Controls ...
etc...

For every available line, note the "Line type" information. It corresponds to the DstType and SrcType parameters of the Init() member function.
For every available control, note the "Control type" information. It corresponds to the ControlType parameter of most of the Init() member functions.

The supplied Demo Project has a special dialog ( CInfoDlg class ) that displays the same information in a tree control. Just click on an item in the tree to display some information on it.

Here is a table that lists the Mixer Classes and the mixer control types each one can be associated with :

Mixer Class Mixer Control Type Implemented
CMixerFader MIXERCONTROL_CONTROLTYPE_FADER MIXERCONTROL_CONTROLTYPE_VOLUME MIXERCONTROL_CONTROLTYPE_BASS MIXERCONTROL_CONTROLTYPE_TREBLE MIXERCONTROL_CONTROLTYPE_EQUALIZER
CMixerSwitch Selectors MIXERCONTROL_CONTROLTYPE_SINGLESELECT MIXERCONTROL_CONTROLTYPE_MUX MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT MIXERCONTROL_CONTROLTYPE_MIXER

Switches MIXERCONTROL_CONTROLTYPE_BOOLEAN MIXERCONTROL_CONTROLTYPE_ONOFF MIXERCONTROL_CONTROLTYPE_MUTE MIXERCONTROL_CONTROLTYPE_MONO MIXERCONTROL_CONTROLTYPE_LOUDNESS MIXERCONTROL_CONTROLTYPE_STEREOENH

CMixerNumber

MIXERCONTROL_CONTROLTYPE_UNSIGNED
CMixerPeakMeter MIXERCONTROL_CONTROLTYPE_PEAKMETER


There's nothing like a little example...

Implementing a speaker volume control:

First of all, add the "MixerFader.h" header file and the MixerFader.cpp source file to your project, include the header file where necessary, and REMEMBER to link with winmm.lib

    Note: the other files are: MixerClasses.h (which includes all other class headers),
    MixerBase.h, MixerPeakMeter.h, MixerNumber.h, MixerSwitch.h,
    MixerBase.cpp, MixerPeakMeter.cpp, MixerNumber.cpp, and MixerSwitch.cpp

In resource editor, add a slider control and give it a meaningful ID, say IDC_MAIN_VOLUME.

Now you have two choices: either use DDE to create and associate a variable of type CMixerFader with the slider control, or subclass the slider control. object.

    With DDE: the problem is that ClassWizard will not recognize class CMixerFader as a valid type for associating with the control, because it doesn't know about it. To solve this problem, delete your .clw file from your project directory. Then, invoke Class Wizard. It will complain that it cannot find its database file and ask you if you want to create it from your source files. Click yes, and in the dialog box that appears, you'll see that it lists all your source files. Click OK.
    Now, in Class Wizard, select the Member Variables tab. Select your dialog class from the Class Name combo box. In the list of control IDs, select the ID of your slider control, and click on Add Variable... Type a name for your variable, say m_mainVolume. Select CMixerFader from the Variable Type combo box and click OK.
    That's it! For now...

    With subclassing: in your CDialog-derived class, add a member variable of type CMixerFader, say m_mainVolume. Go to your dialog class' OnInitDialog() member function (add one with Class Wizard if you don't have one). There, add this line after the call to CDialog::OnInitDialog():

    m_mainVolume.SubclassDlgItem( IDC_MAIN_VOLUME, this );
    
Ok, now you have to initialize your CMixerFader object. For this, you call CMixerFader::Init() from OnInitDialog(); like this:
    m_mainVolume.Init( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,//destination line NO_SOURCE, //control is attached to destination line only MIXERCONTROL_CONTROLTYPE_VOLUME, //control type CMixerFader::MAIN );
The first argument to Init() is the destination line you want. The speakers' line is an ouput line, and its type is MIXERLINE_COMPONENTTYPE_DST_SPEAKERS.
The second argument to Init() is the source line you want. Since the main volume is not attached to a source line, we specify NO_SOURCE (if you wanted to create a volume control for, say, the cd audio source line, this argument would have been MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC).
The third argument is the type of control you want to use. Since you want a volume control, you specify MIXERCONTROL_CONTROLTYPE_VOLUME. Init() will query the mixer device to find out if there actually is a volume control for this type of line. If the query is successful, Init() returns 1; otherwise, it returns 0. These first three arguments are specific to the Audio Mixer services, but the last argument (SubType) is there because on a stereo audio line you might want to create a main volume that controls both channels, or create a balance control to fade out the left or right channel, or create two sliders to control the channels separately. For each of these cases, the arguments would be respectively: CMixerFader::MAIN, CMixerFader::BALANCE, CMixerFader::LEFT, and CMixerFader::RIGHT.

That's it, you're done! To check if everything went OK, compile and run your program, and start the system's Volume Control (from the System Tray). You should see both main volume sliders work in conjunction.

Creating controls using the other classes is very similar. See their implementation source file for more details on how to use their Init() function.

Don't lose sight of the view

If you want to use a volume control in a view for example, you dont call SubclassDlgItem of course. In your CView-derived class declaration, declare a member variable of type CMixerFader, and in your view class' OnCreate(), create the windows control by calling CSliderCtrl::Create().
Like this for example:
    m_mainVolume.Create( WS_VISIBLE|TBS_VERT|TBS_BOTH|TBS_NOTICKS,
                         CRect(10,100, 30,200), this, IDC_MAIN_VOLUME);
    
    m_mainVolume.Init( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,
                       NO_SOURCE,
                       MIXERCONTROL_CONTROLTYPE_VOLUME, //control type
                       CMixerFader::MAIN );
    
    NOTE: Because the CMixerFader objects use message reflection to detect slider movements, you have to be aware that if the parent window class has a handler for the WM_HSCROLL or WM_VSCROLL message, and if the base class handler is not called from that handler, the scrolling messages will not be sent back to the controls.
    Also, be aware that CFrameWnd and derived classes have a default handler for scrolling events, so a CMixerFader won't work in such a window. One way to circumvent this is to call ReflectLastMsg from the handler before you do your normal processing:
    void CMyView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
    {
        ReflectLastMsg( pScrollBar->GetSafeHwnd() );
    
        // blah, blah, blah...
    }
    

...and a little demo!

You can download a new Demo Project that implements most of the functionality of the system's Volume Control (the one that pops up when you double click on the little speaker on the Task Bar). The main window is an empty dialog box used only to enclose child dialogs. When the user selects an item from the 'View' menu, a new dialog is displayed as a child of the main dialog (in the client area), which resizes to accomodate the child. Three child dialogs are defined: one for playback controls, one for recording controls, and one for voice controls. Check their implementation files (SpeakerDlg.pp, WaveDlg.cpp, VoiceDlg.cpp) to get a feel of how work with the classes.

When you start the program, it may tell you that certain mixer controls were not found and that the corresponding Windows control will be disabled. This is normal, because which controls are found depends on the drivers installed. This is not critical though. You just won't be able to use certain controls. In fact, the demo tries on purpose to create a balance control for the microphone source line. On NT, it triggers a warning, but when I switch OS to Windows 95, it works.
The demo has a "Device Info" command, which calls GetDeviceInfo() to query the capabilities of your system, and then launches Notepad to display the log file.
It also has a "Device Caps" command, which displays an explorer-like dialog box. The tree control displays the hierarchy of available mixer lines and controls. Click on any item to see information about it.

If you prefer, you can also download the classes' source code only.

Both the Demo and Source Zip file contain a file named MixerClasses.txt, a text version of this page.

These classes will work on Windows95 or NT only.
The code was written with Developer Studio 97 and VC++ 5.0 with MFC 4.21

Thanks to Keith Rule for his CMemDC class, which I use to draw the CMixerPeakMeter flicker-free.

Comments, suggestions and, yes, even bug reports are more than welcome! 

Last updated: 23 August 1998



Comments

  • Peak Meters: Hardware or Software Dependant?

    Posted by Legacy on 11/21/2003 12:00am

    Originally posted by: Evandro Gouveia

    Peak Meters are graphical interfaces to show what are the internal (Hardware) digital signal from an audio line. So, one needs a means (Software) to translate the Hardware signals to the Peak Meters. This Software is the driver made by the sound card maker or others (like Microsoft). Sometimes the sound card maker does not implement that translation and one has to make things like Jim Ward has addressed in his comment: to write a Software in order to read the wave buffer from the hardware. However, if you are lucky, you may find an updated driver which does work, like said Vlad in his comments. In short: Peak Meters are not Hardware nor Software dependant. They are just graphical controls.

    • Peak Meters - determining if there is a signal from the mic source

      Posted by jrtauber on 05/16/2005 10:49am

      The code does not work with my Yamaha AC-XG driver which does no appear to have the wave peakmeter. I am trying to determine when output is coming from the microphone source line. Any suggestions?

      Reply
    Reply
  • Recording In Problem..!

    Posted by Legacy on 08/19/2003 12:00am

    Originally posted by: Dillip

    Hello Everyone.,
    I am trying to implement in one of my testing application to test the Recording Volume controls . But , I am not able to get control over the Selection controls(Check Box) . It always has been disable .I write as follows:
    ---------------------------------------------------------------------------------------------------------------------
    //****** Initialization of Stereo Mix Select Control *******//
    m_stereoSelect.SubclassDlgItem( IDC_STEREOMIX_MUTE, this );
    m_stereoSelect.Init( MIXERLINE_COMPONENTTYPE_DST_WAVEIN,
    MIXERLINE_COMPONENTTYPE_SRC_ANALOG,
    MIXERCONTROL_CONTROLTYPE_MIXER, 0 );

    //****** Initialization of Wave Select Control *******//
    m_waveSelect.SubclassDlgItem( IDC_WAVEIN_MUTE, this );
    m_waveSelect.Init( MIXERLINE_COMPONENTTYPE_DST_WAVEIN,
    MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED,
    MIXERCONTROL_CONTROLTYPE_MIXER, 1 );

    //****** Initialization of Wave Select Control *******//
    m_mikeSelect.SubclassDlgItem( IDC_MIKE_MUTE, this );
    m_mikeSelect.Init( MIXERLINE_COMPONENTTYPE_DST_WAVEIN,
    MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,
    MIXERCONTROL_CONTROLTYPE_MIXER, 2 );
    ----------------------------------------------------------------------------------------------------------------------
    My all Check boxes are disabled .. so I can't select any one. Can you please guide me for this.?

    I am using C-Media Sound Card..

    Thanks in Anticipation..

    Best Regards,
    DILLIP.

    Reply
  • change recording or volume control

    Posted by Legacy on 02/20/2003 12:00am

    Originally posted by: Heinrich Retzlaw

    Hello,
    I need to change the RecordingControl from "whatever" (maybe line in) to the microphone and after a while to change it back to "whatever". (no gui needed)
    Please, can somebody tell me how to do that?
    Thank you
    Best regards
    Heinrich Retzlaw

    Reply
  • without a gui

    Posted by Legacy on 02/18/2003 12:00am

    Originally posted by: Heinrich Retzlaw

    Hello,
    I want to mute/unmute the microphone and the speakers and I want to change the volume of the microphone and the speakers. I want to integrate it in Qt and I do not need a GUI.
    What do I need to da that?
    Can somebody help me, please?
    Thank you
    Best regards
    Heinrich Retzlaw

    Reply
  • Great - Thanks Langis!!!

    Posted by Legacy on 01/21/2003 12:00am

    Originally posted by: Chris

    Thank you for your help...
    Your mixer control program is really helpful to me.
    So I appreciate of you heartly..

    Reply
  • help !!! - Multimedia Mixer Control

    Posted by Legacy on 10/18/2002 12:00am

    Originally posted by: vhoflo

    I cannot open my Multimedia Mixer Control.

    When i choose to open it by pointing the mouse to 'Start' menu then 'Program', only a minimise icon appear next to the 'Start' button at the bottom of screen. No matter what i do, I cannot maximise the mixer control. I have tried right clicking the minimized 'Mixer Control' icon. A list of options appeared. But the maximize option is darkened and cannot be chosen.

    Please be so kind to give me some advise. Thanks very much.

    Reply
  • Peak Meters

    Posted by Legacy on 07/02/2002 12:00am

    Originally posted by: Johannes

    The whole program works perfect, but the little thing I need is off: The wave meter is without any function, i've testet it on three pc's with different sound cards (SB Live, SB 32 AWE, Yamaha DSP Factory) The third text line is also without any functionality. I use win 98 SE
    Who can help me? (Same probs at peak meter...)

    Reply
  • I want to MixerClasses.cpp

    Posted by Legacy on 03/11/2002 12:00am

    Originally posted by: SeongMan' Lee

    I want to MixerClasses.cpp


    no source MixerClasses.cpp

    Reply
  • aux input?

    Posted by Legacy on 02/20/2002 12:00am

    Originally posted by: david

    Hi,
    I need both line in and Aux in.

    I can only manage to get control of the line in slider on the mixer control.... i need the aux in as well...

    I just cant get it to work.
    Any ideas?
    David.

    Reply
  • Simple mixer programme without graphik only set a volume of line in or cd digigtal...

    Posted by Legacy on 02/15/2002 12:00am

    Originally posted by: jan

    Hello there,

    I�ve downloaded the demoproject. It runs but it doesen�t show all in-outputs.

    I�ve a simple question. I�m not so good in classes and I want to make a programme without graphic to control the mixer in/-outputs volumes.

    I want to make function with the volume delivery.

    I only want to set direct the volume of f.example the line-in or the aux- line to 0 or 30000 or so.

    To set the main volume it�s no problem i�ve programmed it, but to set the others i�ve some problem.
    Please can anybody help me. A simple programme please

    thank you very much

    jan

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds