zoukankan      html  css  js  c++  java
  • MP3播放-基于MCI(一行代码实现音频播放)

    今天整理到音频播放的部分,本来就想抽取一个简单的接口方便以后可能会用到,然而不知不觉就把常用的功能都给一起封装好了,实现只需一行代码就实现音频播放,核心其实就是调用MCI的API接口,具体的功能就是变换不同的MCI指令来实现。

    ==========  原创作品    作者:未闻    出处:博客园  ==========

    一、常见的音频播放方式

    * System.Media.SoundPlayer:播放wav

    * MCI Command String:播放MP3、AVI等

    * axWindowsMediaPlayer:COM组件,功能丰富易用

    二、 注意事项

    * 应用于窗体程序,不能应用于控制台程序(不知道是不是因为取不到窗体句柄,加Sleep也没用,知道的不妨留言告知)

    三、代码

    封装好的类,可以直接用了,这里用了单例简化了用法,其实只要别名不一样,还可以支持同时播放多个音频。

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace System.Media
    {
        /// <summary>
        /// 音频播放器(基于MCI-API接口)
        /// 作者:未闻
        /// 时间:2020.02.13
        /// 
        /// 详细的指令介绍
        /// https://blog.csdn.net/psongchao/article/details/1487788
        /// </summary>
        public class AudioPlayer
        {
            #region API定义
            [DllImport("winmm.dll")]
            static extern int mciSendString(string m_strCmd, StringBuilder m_strReceive, int m_v1, int m_v2);
    
            [DllImport("Kernel32", CharSet = CharSet.Auto)]
            static extern int GetShortPathName(string path, StringBuilder shortPath, int shortPathLength);
    
            private void SendCommand(string cmd)
            {
                mciSendString(cmd, null, 0, 0);
            }
            private string SendCommandForResult(string cmd)
            {
                mciSendString(cmd, _temp, _temp.Capacity, 0);
                return _temp.ToString();
            }
            #endregion
    
            public AudioPlayer(string alias)
            {
                AliasName = alias;
                //// 获取声道
                //var ret = SendCommandForResult($"status {AliasName} source");
                //if (!string.IsNullOrWhiteSpace(ret))
                //    _source = _sourceMap.FirstOrDefault(pair => pair.Value.Equals(ret)).Key;
    
                //// 音频状态,是否静音
                //ret = SendCommandForResult($"status {AliasName} audio");
                //if (!string.IsNullOrWhiteSpace(ret))
                //    _audioStatus = _audioStatusMap.FirstOrDefault(pair => pair.Value.Equals(ret)).Key;
    
                timer.Tick += Timer_Tick;
            }
    
            #region 单例
            class Nested { public static AudioPlayer Instance = new AudioPlayer("AUDIO_PLAYER_SINGLETON"); }
            public static AudioPlayer Instance => Nested.Instance;
            #endregion
    
            // 播放别名,每个播放源(声音)采用一个别名来识别,可以支持同时播放多个声音
            public string AliasName { get; private set; }
    
            private StringBuilder _temp = new StringBuilder(260);
            private Dictionary<AudioSource, string> _sourceMap = new Dictionary<AudioSource, string>
            {
                {AudioSource.H, "stereo"},
                {AudioSource.A, "average"},
                {AudioSource.L, "left"},
                {AudioSource.R, "right"}
            };
            private Dictionary<bool, string> _audioStatusMap = new Dictionary<bool, string> {
                {true, "on"},
                {false, "off"}
            };
            private Timer timer = new Timer
            {
                Interval = 1000
            };
    
            public event Action Progress;
            public event Action Completed;
    
            private void Timer_Tick(object sender, EventArgs e)
            {
                if (!IsCompleted)
                {
                    Progress?.Invoke();
                    return;
                }
    
                Status = PlayerStatus.Stop;
                timer.Stop();
                Completed?.Invoke();
            }
    
            /// <summary>
            /// 准备
            /// </summary>
            /// <param name="fileName"></param>
            /// <param name="autoPlay">是否自动播放,默认true</param>
            public void Prepare(string fileName, bool autoPlay = true)
            {
                if (Status == PlayerStatus.Playing)
                    Stop();
    
                if (string.IsNullOrWhiteSpace(fileName))
                    return;
    
                GetShortPathName(fileName, _temp, _temp.Capacity);
                var mp3Path = _temp.ToString();
                SendCommand($"open "{mp3Path}" alias {AliasName}"); //打开
                if (autoPlay)
                    Play();
    
                // 因为设置静音后一播放,会变成有声音,所以这里要设置一下
                AudioStatus = _audioStatus;
                Source = _source;
                Volume = _vol;
            }
    
            /// <summary>
            /// 播放
            /// </summary>
            public void Play()
            {
                SendCommand($"play {AliasName}");
                Status = PlayerStatus.Playing;
                timer.Start();
            }
    
            /// <summary>
            /// 停止
            /// </summary>
            public void Stop()
            {
                SendCommand($"close {AliasName}");
                Status = PlayerStatus.Stop;
                timer.Stop();
            }
    
            /// <summary>
            /// 暂停
            /// </summary>
            public void Pause()
            {
                SendCommand($"pause {AliasName}");
                Status = PlayerStatus.Pause;
                timer.Stop();
            }
    
            /// <summary>
            /// 播放状态
            /// </summary>
            public PlayerStatus Status { get; private set; } = PlayerStatus.Stop;
    
            private bool _audioStatus = true;
            /// <summary>
            /// 音频状态(true 开启,false 静音)
            /// </summary>
            public bool AudioStatus
            {
                get => _audioStatus;
                set
                {
                    _audioStatus = value;
                    SendCommand($"setaudio {AliasName} {_audioStatusMap[value]}");
                }
            }
    
            private AudioSource _source = AudioSource.H;
            /// <summary>
            /// 播放声道
            /// </summary>
            public AudioSource Source
            {
                get => _source;
                set
                {
                    _source = value;
                    SendCommand($"setaudio {AliasName} source to {_sourceMap[value]}");
                }
            }
    
            private int _vol = 500;
            /// <summary>
            /// 音量
            /// </summary>
            public int Volume
            {
                get => _vol;
                //{
                //    var ret = SendCommandForResult($"status {AliasName} volume");
                //    if (string.IsNullOrWhiteSpace(ret))
                //        return 500;
                //    return Convert.ToInt32(ret);
                //}
                set
                {
                    if (value < 0 || value > 1000)
                        return;
    
                    _vol = value;
                    SendCommand($"setaudio {AliasName} volume to {value}");
                }
            }
    
            /// <summary>
            /// 获取是否正在播放
            /// </summary>
            public bool IsPlaying => Status == PlayerStatus.Playing;
            /// <summary>
            /// 获取是否已播放结束
            /// </summary>
            public bool IsCompleted => Position >= Length;
    
            /// <summary>
            /// 获取播放总时长
            /// </summary>
            public int Length
            {
                get
                {
                    var ret = SendCommandForResult($"status {AliasName} length");
                    if (string.IsNullOrWhiteSpace(ret))
                        return 0;
    
                    return Convert.ToInt32(ret);
                }
            }
            /// <summary>
            /// 获取播放总时长(格式:00:00)
            /// </summary>
            public string LengthString
            {
                get
                {
                    return Len2Time(Length);
                }
            }
    
            /// <summary>
            /// 获取播放进度
            /// </summary>
            public int Position
            {
                get
                {
                    var ret = SendCommandForResult($"status {AliasName} position");
                    if (string.IsNullOrWhiteSpace(ret))
                        return 0;
    
                    return Convert.ToInt32(_temp.ToString());
                }
                set
                {
                    if (value < 0 || value > Length)
                        return;
    
                    SendCommand($"seek {AliasName} to {value}");
                    Play();
                }
            }
            /// <summary>
            /// 获取播放进度(格式:00:00)
            /// </summary>
            public string PositionString
            {
                get
                {
                    return Len2Time(Position);
                }
            }
    
            /// <summary>
            /// 把时长从int类型转换成格式为00:00的字符串
            /// </summary>
            /// <param name="len"></param>
            /// <returns></returns>
            private string Len2Time(int len)
            {
                int sec = len / 1000 % 60;
                int min = len / 60000 % 60;
                return string.Format("{0:D2}:{1:D2}", min, sec);
            }
        }
    
        public enum PlayerStatus
        {
            /// <summary>
            /// 停止
            /// </summary>
            Stop = 0,
            /// <summary>
            /// 播放中
            /// </summary>
            Playing = 1,
            /// <summary>
            /// 暂停
            /// </summary>
            Pause = 2
        }
        public enum AudioSource
        {
            /// <summary>
            /// 立体声
            /// </summary>
            H = 0,
            /// <summary>
            /// 平均声道
            /// </summary>
            A = 1,
            /// <summary>
            /// 左声道
            /// </summary>
            L = 2,
            /// <summary>
            /// 右声道
            /// </summary>
            R = 3
        }
    }
    View Code

    四、调用示例

    AudioPlayer player = AudioPlayer.Instance;
    public Form1()
    {
        InitializeComponent();
        player.Progress += Player_Progress;
        player.Completed += Player_Completed;
    }
    
    private void Player_Completed()
    {
        lblName.Text = "暂无曲目";
    }
    
    private void Player_Progress()
    {
        UpdateProgress();
    }
    
    private void btnOpenFile_Click(object sender, EventArgs e)
    {
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            player.Prepare(openFileDialog1.FileName);
            tbrProgress.Maximum = player.Length;
            UpdateProgress();
            lblName.Text = Path.GetFileName(openFileDialog1.FileName);
        }
    }
    
    /// <summary>
    /// 更新当前播放进度
    /// </summary>
    private void UpdateProgress()
    {
        lblPos.Text = player.PositionString;
        lblLen.Text = player.LengthString;
        tbrProgress.Value = player.Position;
    }

    五、参考资料

  • 相关阅读:
    学习进度笔记06
    学习进度笔记05
    学习进度笔记04
    学习进度笔记03
    学习进度笔记02
    周总结13
    周总结12
    周总结11
    人月神话阅读笔记3
    第一阶段冲刺10
  • 原文地址:https://www.cnblogs.com/yokeqi/p/12305967.html
Copyright © 2011-2022 走看看