Click here to Skip to main content
15,896,269 members
Articles / Desktop Programming / MFC

Multimedia PeakMeter Control

Rate me:
Please Sign up or sign in to vote.
4.94/5 (64 votes)
4 Sep 2008CPOL6 min read 321K   22.3K   222  
Multimedia PeakMeter control - .NET version
// SoundStudioDlg.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include <assert.h>
#include <strsafe.h>
#include "FFT.hpp"
#include "SoundStudioDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


using namespace std;
using namespace Ernzo::DSP;

const UINT WM_AUDIO_DONE = WM_USER+0x100;

//const int METER_FREQUENCY[] = { 30, 60, 80, 90, 100, 150, 200, 330, 480, 660, 880, 1000, 1500, 2000, 3000, 5000, 8000, 12000, 16000 };
const int METER_FREQUENCY[] = { 30, 60, 100, 160, 240, 300, 350, 400, 440, 500, 600, 800, 1000, 1500, 2000, 2600, 3000, 4000, 6000, 8000, 10000, 14000, 16000 };
const int NUM_FREQUENCY = sizeof(METER_FREQUENCY)/sizeof(int);
const double FFT_SPEED = 0.06;

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// Dialog Data
    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CSoundStudioDlg dialog


CSoundStudioDlg::CSoundStudioDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CSoundStudioDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_pWaveBuffer = NULL;
    m_dwMuteControlID = 0;
    m_dwVolumeControlID = 0;
}

void CSoundStudioDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_CTL_METER1, m_PeakMeter1);
}

BEGIN_MESSAGE_MAP(CSoundStudioDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BTN_OPEN, &CSoundStudioDlg::OnBnClickedBtnOpen)
    ON_BN_CLICKED(IDC_BTN_PLAY, &CSoundStudioDlg::OnBnClickedBtnPlay)
    ON_BN_CLICKED(IDC_BTN_PLAYSTOP, &CSoundStudioDlg::OnBnClickedBtnPlaystop)
    ON_BN_CLICKED(IDC_BTN_MUTE, &CSoundStudioDlg::OnBnClickedBtnMute)
    ON_WM_DESTROY()
    ON_MESSAGE(WM_AUDIO_DONE, &CSoundStudioDlg::OnAudioDataDone)
    ON_MESSAGE(MM_MIXM_CONTROL_CHANGE, &CSoundStudioDlg::OnMixerControlChange )
    ON_WM_HSCROLL()
END_MESSAGE_MAP()


// CSoundStudioDlg message handlers
BOOL CSoundStudioDlg::PreTranslateMessage(MSG* pMsg)
{
    if ( pMsg->message == WM_KEYDOWN )
    {
        if ( pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE )
        {
            if ( pMsg->wParam == VK_RETURN )
            {
                TCHAR szClassName[40] = { 0 };
                CWnd* pWnd = GetFocus();
                if ( pWnd )
                {
                    GetClassName(pWnd->GetSafeHwnd(), szClassName, 40);
                    if ( lstrcmpi(_T("EDIT"), szClassName) == 0 )
                    {
                        return FALSE;
                    }
                }
            }
            return TRUE;
        }
    }

    return CDialog::PreTranslateMessage(pMsg);
}

BOOL CSoundStudioDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here
    m_MeterData.resize( NUM_FREQUENCY );
    m_PeakMeter1.SetMeterBands(NUM_FREQUENCY, 20);
    m_PeakMeter1.Start(33);
    m_waveOutput.SetNotifyHandler( this );

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSoundStudioDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSoundStudioDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSoundStudioDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

HRESULT CSoundStudioDlg::SetupMixer()
{
    HRESULT hr;
    hr = m_mixerDevice.Open( static_cast<UINT>(m_waveOutput.GetId()), CALLBACK_WINDOW|MIXER_OBJECTF_HWAVEOUT, GetSafeHwnd());
    MixerControl  mxc;
    MixerLineInfo mxli;
    MixerLineControls mxlcs;
    MixerControlDetails mxcd;
    MIXERCONTROLDETAILS_BOOLEAN mxMute;
    MIXERCONTROLDETAILS_SIGNED mxVolume;

    mxli.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
    hr = m_mixerDevice.GetLineInfo( mxli, MIXER_GETLINEINFOF_COMPONENTTYPE);

    mxlcs.dwLineID = mxli.dwLineID;
    mxlcs.cControls = 1;
    mxlcs.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
    mxlcs.cbmxctrl = mxc.Size();
    mxlcs.pamxctrl = mxc;
    hr = m_mixerDevice.GetLineControls( mxlcs, MIXER_GETLINECONTROLSF_ONEBYTYPE);
    m_dwMuteControlID = mxc.dwControlID;

    mxlcs.dwLineID = mxli.dwLineID;
    mxlcs.cControls = 1;
    mxlcs.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
    mxlcs.cbmxctrl = mxc.Size();
    mxlcs.pamxctrl = mxc;
    hr = m_mixerDevice.GetLineControls( mxlcs, MIXER_GETLINECONTROLSF_ONEBYTYPE);
    m_dwVolumeControlID = mxc.dwControlID;

    if ( SUCCEEDED(hr) )
    {
        SendDlgItemMessage(IDC_CTL_VOLUME, TBM_SETRANGEMIN, FALSE, mxc.Bounds.lMinimum);
        SendDlgItemMessage(IDC_CTL_VOLUME, TBM_SETRANGEMAX, TRUE, mxc.Bounds.lMaximum);
        SendDlgItemMessage(IDC_CTL_VOLUME, TBM_SETTICFREQ, (mxc.Bounds.lMaximum-mxc.Bounds.lMinimum)/10, NULL);
        TRACE(_T("Volume range : %d - %d\n"), mxc.Bounds.lMinimum, mxc.Bounds.lMaximum);
    }

    mxcd.dwControlID = m_dwMuteControlID;
    mxcd.cChannels = 1;
    mxcd.cMultipleItems = 0;
    mxcd.cbDetails = sizeof(mxMute);
    mxcd.paDetails = &mxMute;
    hr = m_mixerDevice.GetControlDetails( mxcd, MIXER_GETCONTROLDETAILSF_VALUE);

    mxcd.dwControlID = m_dwVolumeControlID;
    mxcd.cChannels = 1;
    mxcd.cMultipleItems = 0;
    mxcd.cbDetails = sizeof(mxVolume);
    mxcd.paDetails = &mxVolume;
    hr = m_mixerDevice.GetControlDetails( mxcd, MIXER_GETCONTROLDETAILSF_VALUE);

    SendDlgItemMessage(IDC_BTN_MUTE, BM_SETCHECK, (mxMute.fValue == 0 ? BST_UNCHECKED : BST_CHECKED));
    SendDlgItemMessage(IDC_CTL_VOLUME, TBM_SETPOS, TRUE, mxVolume.lValue);
    return hr;
}

void CSoundStudioDlg::Terminate()
{
    m_mixerDevice.Close();
    m_waveOutput.Close();
    m_AudioStream.ReleaseMediaStream();
    m_MediaStream.Terminate();
    delete []m_pWaveBuffer;
    m_pWaveBuffer = NULL;

    m_RealIn_RT.clear();
    m_RealIn_LT.clear();
    m_RealOut.clear();
    m_ImagOut.clear();
    m_Ampl.clear();
}

bool CSoundStudioDlg::GetAudioData(const LPBYTE pbData, size_t cbSize, const WaveFormat& wfmt)
{
    bool samplesReady = false;
    switch( wfmt.wBitsPerSample )
    {
    case 8:
        {
            if ( wfmt.nChannels == 1 ) // mono
            {
                for (size_t i = 0; i < cbSize; ++i)
                {
                    m_RealIn_RT[i] = static_cast<float>((pbData[i] - 128) << 6);// Out = (In-128)*64
                    m_RealIn_LT[i] = m_RealIn_RT[i];
                }
                m_nNumSamples = cbSize;
            }
            else if ( wfmt.nChannels == 2 ) // stereo
            {
                // Stereo has Left+Right channels
                size_t Samples = cbSize >> 1;
                for (size_t i = 0, j=0; i < Samples; ++i, j+=2)
                {
                    m_RealIn_RT[i] = static_cast<float>((pbData[j] - 128) << 6); // Out = (In-128)*64
                    m_RealIn_LT[i] = static_cast<float>((pbData[j+1] - 128) <<6); // Out = (In-128)*64
                }
                m_nNumSamples = Samples;
            }
            samplesReady = (m_nNumSamples != 0);
        }
        break;
    case 16:
        {
            const short *pfData = reinterpret_cast<const short*>(pbData);
            if ( wfmt.nChannels == 1 ) // mono
            {
                size_t Samples = cbSize >> 1;
                for (size_t i = 0; i < Samples; ++i)
                {
                    m_RealIn_RT[i] = static_cast<float>(pfData[i]);
                    m_RealIn_LT[i] = m_RealIn_RT[i];
                }
                m_nNumSamples = Samples;
            }
            else if ( wfmt.nChannels == 2 ) // stereo
            {
                // Stereo has Left+Right channels
                size_t Samples = cbSize >> 2;
                for (size_t i = 0, j=0; i < Samples; ++i, j+=2)
                {
                    m_RealIn_RT[i] = static_cast<float>( pfData[j] );
                    m_RealIn_LT[i] = static_cast<float>( pfData[j+1] );
                }
                m_nNumSamples = Samples;
            }
            samplesReady = (m_nNumSamples != 0);
        }
        break;
    default:
        assert( false ); // not supported
        break;
    }
    return samplesReady;
}

bool CSoundStudioDlg::PutAudioData(LPBYTE pbData, size_t cbSize, size_t recvBytes, const WaveFormat& wfmt)
{
    bool samplesReady = true;
    switch( wfmt.wBitsPerSample )
    {
    case 8:
        {
            // fill with silence - no smoothing
            if ( cbSize > recvBytes )
            {
                memset(pbData+recvBytes, 0x80, cbSize-recvBytes);
            }
        }
        break;
    case 16:
    default:
        {
            // fill with silence - no smoothing
            if ( cbSize > recvBytes )
            {
                memset(pbData+recvBytes, 0x00, cbSize-recvBytes);
            }
        }
        break;
    }
    return samplesReady;
}

void CSoundStudioDlg::ComputeFFT(const LPBYTE pbData, size_t cbSize)
{
    WaveFormat wfmt = m_AudioStream.GetWaveFormat();
    if ( GetAudioData(pbData, cbSize, wfmt) )
    {
        //TRACE(_T("Computing FFT (%d samples)\n"), m_nNumSamples);
        FFT::Compute<float>(m_nNumSamples, &m_RealIn_RT[0], NULL, &m_RealOut[0], &m_ImagOut[0]);

        size_t index = 0;
        // We can skip N/2 to N samples (mirror frequencies) - Digital samples are real integer
        FFT::Norm<float>(m_nNumSamples/2, &m_RealOut[0], &m_ImagOut[0], &m_Ampl[0]);
      
        double maxAmpl = ( wfmt.wBitsPerSample == 8) ? (127.0*127.0) : (32767.0*32767.0);

        // update meter
        int centerFreq = static_cast<int>(wfmt.nSamplesPerSec/2);
        for(int i=0; i < NUM_FREQUENCY; ++i)
        {
            if ( METER_FREQUENCY[i] > centerFreq )
                m_MeterData[i] = 0;
            else
            {
                int indice = static_cast<int>( METER_FREQUENCY[i] * m_nNumSamples / wfmt.nSamplesPerSec );
                int value  = static_cast<int>( 20.0*log10(m_Ampl[indice]/maxAmpl) );
                m_MeterData[i] = value;
            }
        }
        m_PeakMeter1.SetData(&m_MeterData[0], 0, NUM_FREQUENCY);
        m_nNumSamples = 0; // ready to do it again
    }
}

void CSoundStudioDlg::OnDestroy()
{
    Terminate();
    m_waveOutput.SetNotifyHandler( NULL );

    CDialog::OnDestroy();
}

void CSoundStudioDlg::ProcessEvent(IWaveDevice* pDevice, UINT uMsg, WaveBuffer* pwbuf)
{
    if ( pDevice == &m_waveOutput )
    {
        switch( uMsg )
        {
            case WOM_OPEN:
                {
                    OutputDebugString( _T("WaveOut opened.\n") );
                }
                break;
            case WOM_DONE:
                {
                    PostMessage(WM_AUDIO_DONE, 0, (LPARAM)pwbuf);
                }
                break;
            case WOM_CLOSE:
                {
                    OutputDebugString( _T("WaveOut closed.\n") );
                }
                break;
        }
    }
}

void CSoundStudioDlg::OnBnClickedBtnOpen()
{
    static LPCTSTR pszFilter = _T("Audio Files (*.wav;*.mp3;*.wma)|*.wav;*.mp3;*.wma||");
    CFileDialog fileOpen(TRUE, _T("wav"), NULL,
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, pszFilter, this);
    if ( IDOK == fileOpen.DoModal() )
    {
        SetDlgItemText(IDC_TXT_FILENAME, fileOpen.GetPathName());
    }
}

void CSoundStudioDlg::OnBnClickedBtnPlay()
{
    // TODO: Add your control notification handler code here
    if ( !m_waveOutput.IsOpen() )
    {
        // primary audio output
        HRESULT hr;
        CString strPath;
        GetDlgItemText(IDC_TXT_FILENAME, strPath);
        hr = m_MediaStream.Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
        if ( SUCCEEDED(hr) )
        {
            CComPtr<IMediaStream> pAudioStream;
            hr = m_MediaStream.AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, &pAudioStream);
            hr = m_MediaStream.OpenFile( strPath, AMMSF_RUN);
            
            if ( SUCCEEDED(hr) )
            {
                hr = m_AudioStream.SetMediaStream(pAudioStream);
                WaveFormat wfmt = m_AudioStream.GetWaveFormat();
                hr = m_waveOutput.Open(WAVE_MAPPER, wfmt);
                // calculate required buffer
                m_nBufferSize = FFT::NextPowerOfTwo( static_cast<size_t>(wfmt.nAvgBytesPerSec * FFT_SPEED) );
                TRACE(_T("FMT(%d): Freq:%d Hz - %d bits %d channel(s)\n"), wfmt.wFormatTag, wfmt.nSamplesPerSec, wfmt.wBitsPerSample, wfmt.nChannels);
                delete []m_pWaveBuffer;
                m_pWaveBuffer = new WaveBuffer[NUM_BUFFERS];
                for( int index=0; index < NUM_BUFFERS; ++index)
                {
                    // realloc memory
                    m_pWaveBuffer[index].Allocate(static_cast<DWORD>(m_nBufferSize));
                    m_pWaveBuffer[index].SetFlags(0);
                }
                m_nNumSamples = m_nBufferSize / wfmt.nBlockAlign;
                m_RealIn_RT.resize( m_nNumSamples );
                m_RealIn_LT.resize( m_nNumSamples );
                m_RealOut.resize( m_nNumSamples );
                m_ImagOut.resize( m_nNumSamples );
                m_Ampl.resize( m_nNumSamples );
                TRACE(_T("BufferSize:%d  NumSamples:%d\n"), m_nBufferSize, m_nNumSamples);
                m_nNumSamples = 0;
                m_nNumOutBuffers = 0;
                if ( SUCCEEDED(hr) )
                {
                    DWORD dwBytes;
                    for( int index=0; index < NUM_BUFFERS; ++index)
                    {
                        WaveBuffer& curBuffer = m_pWaveBuffer[index];
                        dwBytes = curBuffer.GetBufferLength();

                        hr = m_AudioStream.GetSampleData(reinterpret_cast<LPBYTE>(curBuffer.GetBuffer()), dwBytes);
                        if ( SUCCEEDED(hr) && dwBytes > 0 )
                        {
                            HRESULT whr;
                            whr = m_waveOutput.PrepareBuffer( curBuffer );
                            whr = m_waveOutput.AddBuffer( curBuffer );
                            ::InterlockedIncrement(&m_nNumOutBuffers);
                            if ( index == 0 ) {
                                whr = m_waveOutput.Start();
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    
                    // setup mixer
                    hr = SetupMixer();
                }
            }
        }
    }
}

void CSoundStudioDlg::OnBnClickedBtnPlaystop()
{
    // TODO: Add your control notification handler code here
    Terminate();
}

void CSoundStudioDlg::OnBnClickedBtnMute()
{
    if ( m_mixerDevice.IsOpen() )
    {
        HRESULT hr;
        MixerControlDetails mxcd;
        MIXERCONTROLDETAILS_BOOLEAN mxMute;

        mxMute.fValue = ( SendDlgItemMessage(IDC_BTN_MUTE, BM_GETCHECK, NULL, NULL) == BST_CHECKED );
        mxMute.fValue = !mxMute.fValue; // don't need this for AUTOCHECKBOX
        mxcd.dwControlID = m_dwMuteControlID;
        mxcd.cChannels = 1;
        mxcd.cMultipleItems = 0;
        mxcd.cbDetails = sizeof(mxMute);
        mxcd.paDetails = &mxMute;
        hr = m_mixerDevice.SetControlDetails( mxcd, MIXER_SETCONTROLDETAILSF_VALUE);
        hr = hr;
    }
}

LRESULT CSoundStudioDlg::OnAudioDataDone(WPARAM, LPARAM lParam)
{
    HRESULT hr;
    DWORD dwBytes;

    WaveBuffer* pwbuf = reinterpret_cast<WaveBuffer*>( lParam );
    WaveBuffer& curBuffer = *pwbuf;
    hr = m_waveOutput.UnprepareBuffer( curBuffer );
    ::InterlockedDecrement(&m_nNumOutBuffers);
    dwBytes = curBuffer.GetBufferLength();

    // compute FFT
    if ( m_waveOutput.GetDeviceStatus() == waveStarted )
    {
        ComputeFFT(reinterpret_cast<LPBYTE>(curBuffer.GetBuffer()), dwBytes);
    }

    hr = m_AudioStream.GetSampleData(reinterpret_cast<LPBYTE>(curBuffer.GetBuffer()), dwBytes);
    if ( SUCCEEDED(hr) )
    {
        if ( hr == S_OK )
        {
            if ( m_waveOutput.GetDeviceStatus() == waveStarted )
            {
                HRESULT whr;

                // Append silence if necessary
                WaveFormat wfmt = m_AudioStream.GetWaveFormat();
                PutAudioData(reinterpret_cast<LPBYTE>(curBuffer.GetBuffer()), m_nBufferSize, dwBytes, wfmt);

                whr = m_waveOutput.PrepareBuffer( curBuffer );
                whr = m_waveOutput.AddBuffer( curBuffer );
                ::InterlockedIncrement(&m_nNumOutBuffers);
            }
        }
        else if ( m_nNumOutBuffers == 0 ) // all buffers done
        {
            // stop
            SendMessage(WM_COMMAND, MAKEWPARAM(IDC_BTN_PLAYSTOP, BN_CLICKED), 0);
        }
    }
    return 0;
}

LRESULT CSoundStudioDlg::OnMixerControlChange(WPARAM wParam, LPARAM lParam)
{
    if ( m_mixerDevice.IsOpen() )
    {
        MIXERCONTROLDETAILS_SIGNED mxVolume = { 0 };
        MIXERCONTROLDETAILS_BOOLEAN mxMute  = { 0 };

        MixerControlDetails mxcd;
        mxcd.dwControlID = static_cast<DWORD>(lParam);
        mxcd.cChannels = 1;
        HRESULT hr = S_FALSE;
        if ( static_cast<DWORD>(lParam) == m_dwMuteControlID )
        {
            mxcd.paDetails = &mxMute;
            mxcd.cbDetails = sizeof(mxMute);
            hr = S_OK;
        }
        else if ( static_cast<DWORD>(lParam) == m_dwVolumeControlID )
        {
            mxcd.paDetails = &mxVolume;
            mxcd.cbDetails = sizeof(mxVolume);
            hr = S_OK;
        }
        if ( hr == S_OK )
        {
            hr = m_mixerDevice.GetControlDetails( mxcd, MIXER_GETCONTROLDETAILSF_VALUE );
            CString sInfo;
            if ( static_cast<DWORD>(lParam) == m_dwMuteControlID )
            {
                SendDlgItemMessage(IDC_BTN_MUTE, BM_SETCHECK, (mxMute.fValue == 0 ? BST_UNCHECKED : BST_CHECKED));
                sInfo.Format(_T("MIXER (%p) - Control: MUTE (Value:%d)\n"), wParam, mxMute.fValue);
            }
            else if ( static_cast<DWORD>(lParam) == m_dwVolumeControlID )
            {
                SendDlgItemMessage(IDC_CTL_VOLUME, TBM_SETPOS, TRUE, mxVolume.lValue);
                sInfo.Format(_T("MIXER (%p) - Control: VOLUME (Value:%d)\n"), wParam, mxVolume.lValue);
            }

            ATLTRACE( sInfo );
        }
    }
    return 0;
}

void CSoundStudioDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    if ( m_mixerDevice.IsOpen() )
    {
        MIXERCONTROLDETAILS_SIGNED mxVolume;
        mxVolume.lValue = static_cast<USHORT>(nPos);
        MixerControlDetails mxcd;
        mxcd.dwControlID = m_dwVolumeControlID;
        mxcd.cChannels = 1;
        mxcd.paDetails = &mxVolume;
        mxcd.cbDetails = sizeof(mxVolume);
        HRESULT hr;
        if ( nSBCode != SB_ENDSCROLL )
        {
            hr = m_mixerDevice.SetControlDetails( mxcd, MIXER_SETCONTROLDETAILSF_VALUE );
        }
    }

    __super::OnHScroll(nSBCode, nPos, pScrollBar);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Ernest is a multi-discipline software engineer.
Skilled at software design and development for all Windows platforms.
-
MCSD (C#, .NET)
Interests: User Interface, GDI/GDI+, Scripting, Android, iOS, Windows Mobile.
Programming Skills: C/C++, C#, Java (Android), VB and ASP.NET.

I hope you will enjoy my contributions.

Comments and Discussions