Click here to Skip to main content
15,881,380 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 317.5K   22.3K   222  
Multimedia PeakMeter control - .NET version
///////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2008 Ernest Laurentin (http://www.ernzo.com)
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;

namespace Ernzo.Windows.WaveAudio
{
    /// <summary>
    /// WaveInStatus
    /// </summary>
    public sealed class WaveInStatus
    {
        private WaveInStatus()
        {
            // nothing to construct
        }
        public static string GetErrorText(int mmr)
        {
            StringBuilder message = new StringBuilder(256);
            WaveInput.waveInGetErrorTextW(mmr, message, 256);
            return message.ToString();
        }
        public static void ThrowExceptionForHR(int mmr)
        {
            if (mmr != WaveConstants.MMSYSERR_NOERROR)
            {
                throw new SystemException( GetErrorText(mmr) );
            }
        }
    }

    /// <summary>
    /// WaveInCaps
    /// Wave Input Capabilities
    /// </summary>
    public sealed class WaveInCaps
    {
        internal tWAVEINCAPSW wiCaps;
        public WaveInCaps()
        {
        }
        public WaveInCaps(tWAVEINCAPSW caps)
        {
            wiCaps = caps;
        }

        int ManufacturerId
        {
            get { return wiCaps.wMid; }
        }
        int ProductId
        {
            get { return wiCaps.wPid; }
        }
        int Version
        {
            get { return wiCaps.vDriverVersion; }
        }
        int Formats
        {
            get { return wiCaps.dwFormats; }
        }
        int NumChannels
        {
            get { return wiCaps.wChannels; }
        }
        string ProductName
        {
            get { return wiCaps.szPname; }
        }
        public static int Size
        {
            get {
                return Marshal.SizeOf(typeof(tWAVEINCAPSW));
            }
        }
        public static explicit operator tWAVEINCAPSW(WaveInCaps wicaps)
        {
            return wicaps.wiCaps;
        }
    }

    /// <summary>
    /// WaveInDevice
    /// Wave Input Device (e.g.:MIC, LINE)
    /// </summary>
    public class WaveInDevice : IWaveDevice, IDisposable
    {
        private IntPtr  _hWaveIn;       // Wave Audio Input
        private int _DeviceState;       // Wave state
        private int _currentKey;        // Current key (used in map)
        private Dictionary<int, WaveBuffer> _AudioBuffers; // Audio buffer map
        private IWaveNotifyHandler _WaveNotify; // Audio Event handler
        private DriverCallback _Callback;    // Callback
        public WaveInDevice()
        {
            _hWaveIn = IntPtr.Zero;
            _DeviceState = (int)WaveStatus.waveClosed;
            _AudioBuffers = new Dictionary<int, WaveBuffer>();
            _WaveNotify = null;
            _Callback = null;
        }
        ~WaveInDevice()
        {
            Dispose(false);
        }
        public bool IsOpen()
        {
            return (_hWaveIn != IntPtr.Zero);
        }
        public IntPtr GetId()
        {
            return _hWaveIn;
        }
        public WaveStatus GetDeviceStatus()
        {
            // this is safer than 'return _DeviceState;'
            WaveStatus wStatus = (WaveStatus)Interlocked.CompareExchange(ref _DeviceState,
                                        (int)WaveStatus.waveClosed,
                                        (int)WaveStatus.waveClosed);
            return wStatus;
        }
        public int Open(int deviceId, WaveFormat wfmt)
        {
            int mmr = WaveConstants.MMSYSERR_HANDLEBUSY;
            if (!IsOpen())
            {
                _Callback = new DriverCallback(waveInProc);
                GCHandle handle = GCHandle.Alloc(_Callback);
                mmr = WaveInput.waveInOpen(ref _hWaveIn, deviceId, ref wfmt.wfmt.Format,
                                          Marshal.GetFunctionPointerForDelegate(_Callback),
                                          IntPtr.Zero,
                                          WaveConstants.CALLBACK_FUNCTION);
                handle.Free();
                if (mmr == WaveConstants.MMSYSERR_NOERROR)
                {
                    Interlocked.Exchange(ref _DeviceState, (int)WaveStatus.waveStopped);
                }
            }
            return mmr;
        }
        public int Close()
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = Stop();
                mmr = WaveInput.waveInClose(_hWaveIn);
                if (mmr == WaveConstants.MMSYSERR_NOERROR)
                {
                    Interlocked.Exchange(ref _DeviceState, (int)WaveStatus.waveClosed);
                    _hWaveIn = IntPtr.Zero;
                    _currentKey = 0;
                    _Callback = null;
                }
            }
            return mmr;
        }
        public int Start()
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInStart(_hWaveIn);
                if (mmr == WaveConstants.MMSYSERR_NOERROR)
                {
                    Interlocked.Exchange(ref _DeviceState, (int)WaveStatus.waveStarted);
                }
            }
            return mmr;
        }
        public int Stop()
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                Interlocked.Exchange(ref _DeviceState, (int)WaveStatus.waveStopped);
                mmr = WaveInput.waveInReset(_hWaveIn);
                if (mmr == WaveConstants.MMSYSERR_NOERROR)
                {
                    mmr = WaveInput.waveInStop(_hWaveIn);
                }
            }
            return mmr;
        }
        public int Reset()
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInReset(_hWaveIn);
            }
            return mmr;
        }
        public int Pause()
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                // can't pause input waveform
                Interlocked.Exchange(ref _DeviceState, (int)WaveStatus.wavePaused);
                mmr = WaveInput.waveInStop(_hWaveIn);
            }
            return mmr;
        }
        public int GetPosition(WaveTime wti)
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInGetPosition(_hWaveIn, ref wti.tMMT, WaveTime.Size);
            }
            return mmr;
        }
        public int AddBuffer(WaveBuffer wbuf)
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInAddBuffer(_hWaveIn, ref wbuf.whdr, WaveBuffer.Size);
                if (mmr == WaveConstants.MMSYSERR_NOERROR)
                {
                    Interlocked.Increment(ref _currentKey);
                    wbuf.UserData = _currentKey;
                    _AudioBuffers[_currentKey] = wbuf;
                }
            }
            return mmr;
        }
        public int PrepareBuffer(WaveBuffer wbuf)
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInPrepareHeader(_hWaveIn, ref wbuf.whdr, WaveBuffer.Size);
            }
            return mmr;
        }
        public int UnprepareBuffer(WaveBuffer wbuf)
        {
            int mmr = WaveConstants.MMSYSERR_INVALHANDLE;
            if (IsOpen())
            {
                mmr = WaveInput.waveInUnprepareHeader(_hWaveIn, ref wbuf.whdr, WaveBuffer.Size);
            }
            return mmr;
        }
        static public int GetDevicesCount()
        {
            return WaveInput.waveInGetNumDevs();
        }
        static int GetDeviceCaps(int deviceId, ref WaveInCaps wic)
        {
            return WaveInput.waveInGetDevCapsW(deviceId, ref wic.wiCaps, WaveInCaps.Size);
        }
        public bool SetNotifyHandler(IWaveNotifyHandler notifyHander)
        {
            bool result = false;
            if (notifyHander != null)
            {
                if (_WaveNotify == null)
                {
                    Interlocked.Exchange(ref _WaveNotify, notifyHander);
                }
                else
                {
                    // Only 1 member is supported
                    throw new InvalidOperationException();
                }
            }
            else
            {
                Interlocked.Exchange(ref _WaveNotify, null);
            }
            return result;
        }

        /// <summary>
        /// WaveInProc
        /// Wave Callback
        /// </summary>
        /// <param name="hwi">Wave Input Handle</param>
        /// <param name="uMsg">Wave Input message (MM_WIM_OPEN, MM_WIM_DATA, MM_WIM_CLOSE)</param>
        /// <param name="dwInstance">Application Instance data (NULL)</param>
        /// <param name="dwParam1">This pointer represent a tWAVEHDR when MM_WIM_DATA is received. Null otherwise</param>
        /// <param name="dwParam2">Null</param>
        void waveInProc(IntPtr hwi, int uMsg, IntPtr dwInstance,
                                 IntPtr dwParam1, IntPtr dwParam2)
        {
            if (_WaveNotify != null)
            {
                // Pop audio buffer only for data
                WaveBuffer wbuf = null;
                if (dwParam1 != IntPtr.Zero)
                {
                    tWAVEHDR whdr = (tWAVEHDR)Marshal.PtrToStructure(dwParam1, typeof(tWAVEHDR));
                    if (!_AudioBuffers.TryGetValue(whdr.dwUser, out wbuf))
                    {
                        System.Diagnostics.Debug.Assert(false, string.Format("Failed to get pointer :{0}", whdr.dwUser));
                        return;
                    }
                    _AudioBuffers.Remove(whdr.dwUser);
                }
                _WaveNotify.ProcessEvent(this, uMsg, wbuf);
            }
        }

        // IDisposable implementation
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            try
            {
                // release unmanaged types
                Close();
            }
            catch (Exception e)
            {
                // don't throw
                System.Diagnostics.Trace.WriteLine(e.ToString());
            }
            finally
            {
                SetNotifyHandler(null);
            }
            if (disposing)
            {
                // release managed
                if (_AudioBuffers != null)
                {
                    System.Diagnostics.Debug.Assert(_AudioBuffers.Count == 0, "Audio buffers may be leaking");
                    _AudioBuffers = null;
                }
            }
        }
    }
}

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