Click here to Skip to main content
13,634,430 members
Click here to Skip to main content

Stats

666.3K views
29.1K downloads
274 bookmarked
Posted 14 Sep 2010
Licenced CPOL

PVS.AVPlayer - MCI Audio and Video Library

, 1 Jun 2018
Windows Media Control Interface (MCI) library with many added features
PVS.AVPlayer
PVS.AVPlayer .NET 2.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.5
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5
PVS.AVPlayer .NET 4.5.1
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5.2
PVS.AVPlayer.XML
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7
PVS.AVPlayer .NET 4.7.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer All Source Code
AVPlayerExample
AVPlayerExample
AVPlayerExample.csproj.user
bin
Debug
PVS.AVPlayer.XML
Release
Dialogs
Display Overlays
obj
Debug
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
WingDings3a.ttf
Voice Recorder
FolderView
FolderView
bin
Debug
PVS.AVPlayer.XML
Release
FolderView.csproj.user
obj
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
PVS.AVPlayer
AVPlayerExample.csproj.user
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
Native Methods
Bob.png
Crystal Italic1.ttf
Dial Green 2.png
Dial Green 4.png
Dial Green.png
Dial Red 2.png
Dial Red.png
media7.ico
media7a.ico
Media8.ico
Media8a.ico
VU Meter.png
WingDings3a.ttf
Sound Recorder
Various
About Dialog
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
FolderView.csproj.user
Bob.png
Crystal Italic1.ttf
media7a.ico
media7b.ico
Media8a.ico
Media8b.ico
Subtitles Overlay
Various
How To (C#)
PVSAVPlayerHowTo
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
Properties
How To (VB.NET)
PVSAVPlayerHowToVB
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
My Project
Application.myapp
PVSAVPlayerHowTo.vbproj.user
PVS.AVPlayer Examples
AVPlayerExample.ex_
FolderView.ex_
AVPlayerExample.exe
FolderView.exe
PVS.AVPlayer.dll
    /****************************************************************
    
    PVS.AVPlayer - Version 0.90
    June 2018, The Netherlands
    © Copyright 2018 PVS The Netherlands - Free to Use

    ****************

    For use with Microsoft .NET Framework version 2.0 or higher and any CPU.
    Created with Microsoft Visual Studio.

    Articles on CodeProject with information on the use of the PVS.AVPlayer library:
    About the Player: http://www.codeproject.com/Articles/109714/PVS-AVPlayer-MCI-Audio-and-Video-Library
    About the Sound Recorder: http://www.codeproject.com/Articles/1116698/PVS-AVPlayer-MCI-Sound-Recorder

    ****************

    The PVS.AVPlayer library source code is divided into 10 files:

     1. Player.cs            - Player source code
     2. MouseEvents.cs       - extension source code of Player.cs, provides player display mouse events
     3. OutputLevel.cs       - extension source code of Player.cs, provides player audio output level data
     4. DisplayClones.cs     - extension source code of Player.cs, provides player multiple video displays 
     5. Subtitles.cs         - extension source code of Player.cs, provides player SubRip (.srt) subtitles
     6. Signals.cs           - extension source code of Player.cs, provides player media position signaling
     7. CursorHide.cs        - extension source code of Player.cs, hides the mouse cursor during inactivity
     8. Recorder.cs          - Sound Recorder source code
     9. PlayerRecorder.cs    - code used by both Player.cs (and its extension files) and Recorder.cs
    10. Infolabel.cs         - Info Label (custom ToolTip) source code
    
    Required references:
    System
    System.Drawing
    System.Windows.Forms

    ****************
 
    This file: OutputLevel.cs

    Player Class
    Extension to file 'Player.cs'.

    ****************

    About Media Control Interface (MCI)
    - for information about MCI, please see https://msdn.microsoft.com/en-us/library/vs/alm/dd757151(v=vs.85).aspx
    - you can find many articles about mci on the internet, search examples: 'c# mci', 'vb mci', 'mcisendstring'
      or the subject of your question.
    - the PVS.AVPlayer library also provides 'direct access' to MCI (e.g. 'Mci.MciSendString').

    ****************

    Thanks!

    Many thanks to Microsoft (Windows, .NET Framework, Visual Studio Express, etc.), all the people
    writing about programming on the internet (a great source for ideas and solving problems),
    the websites publishing those or other writings about programming, the people responding
    to the PVS.AVPlayer articles with comments and suggestions and, of course, CodeProject:
    thank you Deeksha, Smitha and Sean Ewington for the beautiful articles and all!

    Special thanks to Sean Ewington of CodeProject who also took care of publishing the many code
    updates and changes in the PVS.AVPlayer articles in a friendly, fast, and highly competent manner.
    Thank you very much, Sean!

    Peter Vegter
    June 2018, The Netherlands

    ****************************************************************/

#region Usings

using System;
using System.Text;
using System.Windows.Forms;

#endregion


namespace PVS.AVPlayer
{
    public partial class Player
    {
        // Audio Output Level Device (Audio Output Level Meter)
        // A system audio loopback level device ('Stereo Mix' or other name) is used to get audio output level values.
        // Activated by subscribing to the Player.MediaOutputLevelChanged event.

        // ******************************** Level Device - Fields

        #region Level Device - Fields

        // this timer is used to eliminate 
        private const int               LEVELDEVICE_TIMERINTERVAL = 50;
        private const int               LEVELDEVICE_TIMERCOUNTER  = 10;

        internal static bool            _hasLevelDevice;
        private static string           _levelDevice_Id;

        private static string           _levelDevice_DefaultName;
        internal static string          _levelDevice_Name;

        private static StringBuilder    _levelDevice_Buffer;
        private static object           _levelDevice_Lock = new object();

        // this timer is used to eliminate spurious level data when selecting the level device
        // it's only used shortly, for a moment.
        // the level data is supplied by the LevelChanged event from the main timer
        internal static Timer           _levelDevice_Timer;
        private static int              _levelDevice_TimerCounter;

        #endregion


        // ************************ Level Device - Main (Public)

        #region Level Device - Main (Public)

        /// <summary>
        /// Provides access to the audio output peak level device settings of the player (e.g. Player.LevelDevice.Enabled).
        /// </summary>
        public LevelDevice LevelDevice
        {
            get
            {
                if (_levelsClass == null) _levelsClass = new LevelDevice(this);
                return _levelsClass;
            }
        }

        #endregion

        // ******************************** Level Device - Main (Private)

        #region Level Device - Main (Private)

        // Sets (or resets) the default ("Stereo Mix") loopback level device
        // reset false: set new device or use current device
        internal static bool LevelDevice_SetDefault(bool reset)
        {
            if (_hasLevelDevice && !reset) return true;

            bool result = false;
            int index   = LevelDevice_GetDefaultIndex();

            if (index != -1) result = LevelDevice_SetByIndex(index);
            else if (_hasLevelDevice && reset) LevelDevice_Close();

            return result;
        }

        // Sets the loopback level device by name
        internal static bool LevelDevice_SetByName(string deviceName)
        {
            return LevelDevice_SetByIndex(DeviceInfo.GetInputDeviceIndex(deviceName));
        }

        // Sets the loopback level device by index (index in list returned by DeviceInfo.GetAllInputDeviceNames())
        internal static bool LevelDevice_SetByIndex(int index)
        {
            bool result = false;

            // check if same device already set
            if (index < 0 || (_hasLevelDevice && index == DeviceInfo.GetInputDeviceIndex(_levelDevice_Name))) return false;

            if (_hasLevelDevice || _levelDevice_Timer != null) LevelDevice_Close();

            // Repeat if 'open' fails because of duplicate alias?
            _levelDevice_Id = Global.RandomNumber.Next(10, 100000000) + "LEV";
            if (SafeNativeMethods.mciSendString("open new type waveaudio alias " + _levelDevice_Id, null, 0, IntPtr.Zero) == Global.MCIERR_NO_ERROR)
            {
                Application.DoEvents();
                if (SafeNativeMethods.mciSendString("set " + _levelDevice_Id + " input " + index, null, 0, IntPtr.Zero) == Global.MCIERR_NO_ERROR)
                {
                    Application.DoEvents();

                    StringBuilder textBuffer = new StringBuilder(164);
                    textBuffer.Append("set ")
                        .Append(_levelDevice_Id)
                        .Append(" time format ms bitspersample 16 channels 2 alignment 4 samplespersec 11025 bytespersec 352800 format tag pcm");
                    SafeNativeMethods.mciSendString(textBuffer.ToString(), null, 0, IntPtr.Zero);

                    if (_levelDevice_Buffer == null) _levelDevice_Buffer = new StringBuilder(Global.LEVEL_BUFFER_SIZE);

                    // remove spurious init values - delayed setting of _hasLevelDevice
                    _levelDevice_Timer          = new Timer();
                    _levelDevice_Timer.Interval = LEVELDEVICE_TIMERINTERVAL;
                    _levelDevice_TimerCounter   = LEVELDEVICE_TIMERCOUNTER;
                    _levelDevice_Timer.Tick     += LevelDevice_TimerTick;
                    _levelDevice_Timer.Start();

                    _levelDevice_Name = DeviceInfo.GetInputDeviceName(index); // get level device name
                    result = true;

                }
                else
                {
                    SafeNativeMethods.mciSendString("close " + _levelDevice_Id, null, 0, IntPtr.Zero);
                }
                Application.DoEvents();
            }
            return result;
        }

        private static void LevelDevice_TimerTick(object sender, EventArgs e)
        {
            // remove spurious int values - delayed setting of _hasLevelDevice
            _levelDevice_Buffer.Length = 0;
            SafeNativeMethods.mciSendString("status " + _levelDevice_Id + " level", _levelDevice_Buffer, Global.LEVEL_BUFFER_SIZE, IntPtr.Zero);
            if (--_levelDevice_TimerCounter <= 0)
            {
                _levelDevice_Timer.Stop();
                _levelDevice_Timer.Dispose();
                _levelDevice_Timer = null;
                _hasLevelDevice = true;
            }
        }

        // Check if an system audio loopback ('Stereo Mix') level device exists (and is enabled), return index or -1;
        // Also sets _defaultLevelDeviceName (to device name or string.Empty)
        internal static int LevelDevice_GetDefaultIndex()
        {
            string[]        deviceNames;
            string[]        checkNames1 = { "stereo", "mix"}; // most likely (partial) names - checked first
            string[]        checkNames2 = { "wave", "loopback", "hear", "sum", "cтерео", "out", "playback" }; // other possible (partial) names
            int             index       = -1;

            _levelDevice_DefaultName     = string.Empty;
            deviceNames                 = DeviceInfo.GetAllInputDeviceNames();
            int deviceCount             = deviceNames.Length;

            for (int i = 0; i < deviceCount; i++) // 1. check most likely names
            {
                for (int j = 0; j < checkNames1.Length; j++)
                {
                    if (deviceNames[i].IndexOf(checkNames1[j], StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        index                   = i;
                        _levelDevice_DefaultName = checkNames1[j];
                        i                       = deviceCount; // also break out of outer loop (or use return?)
                        break;
                    }
                }
            }

            if (index == -1)
            {
                for (int i = 0; i < deviceCount; i++) // 2. check other 'possible/maybe' names
                {
                    for (int j = 0; j < checkNames2.Length; j++)
                    {
                        if (deviceNames[i].IndexOf(checkNames2[j], StringComparison.OrdinalIgnoreCase) >= 0)
                        {
                            index                   = i;
                            _levelDevice_DefaultName = checkNames2[j];
                            i                       = deviceCount; // also break out of outer loop (or use return?)
                            break;
                        }
                    }
                }
            }
            return index;
        }

        private static void LevelDevice_Close()
        {
            if (_hasLevelDevice || _levelDevice_Timer != null)
            {
                if (_levelDevice_Timer != null)
                {
                    _levelDevice_Timer.Stop();
                    _levelDevice_Timer.Dispose();
                    _levelDevice_Timer = null;
                }
                SafeNativeMethods.mciSendString("close " + _levelDevice_Id, null, 0, IntPtr.Zero);
                Application.DoEvents();
                _levelDevice_Name = string.Empty;
                _hasLevelDevice = false;
            }
        }

        #endregion

        // ******************************** Level Device - Get Level Device Values

        #region Level Device - Get Level Device Values

        private static void LevelDevice_GetValues(PeakLevelEventArgs e)
        {
            lock (_levelDevice_Lock) // lock this method?
            {
                if (_hasLevelDevice)
                {
                    int result = 0;
                    _levelDevice_Buffer.Length = 0;
                    if (SafeNativeMethods.mciSendString("status " + _levelDevice_Id + " level", _levelDevice_Buffer, Global.LEVEL_BUFFER_SIZE, IntPtr.Zero) == Global.MCIERR_NO_ERROR)
                    {
                        for (int count = 0; count < _levelDevice_Buffer.Length; ++count)
                        {
                            result = 10 * result + (_levelDevice_Buffer[count] - 48);
                        }
                    }
                    else
                    {
                        e._leftLevel = e._rightLevel = 0;
                        _hasLevelDevice = false;
                        return;
                    }
                    e._leftLevel = (result - 1) & 0x7FFF;
                    e._rightLevel = ((result >> 16) - 1) & 0x7FFF;
                }
                else
                {
                    e._leftLevel = e._rightLevel = 0;
                }
            }
        }

        #endregion
    }
}

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)

Share

About the Author

Peter Vegter
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.180712.1 | Last Updated 1 Jun 2018
Article Copyright 2010 by Peter Vegter
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid