MP3播放-基于MCI-API接口

谁都会走 提交于 2020-02-14 00:42:19

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

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

一、常见的音频播放方式

* System.Media.SoundPlayer:播放wav

* MCI Command String:播放MP3、AVI等

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

二、 注意事项

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

三、代码

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

/// <summary>
/// MP3播放器(基于MCI-API接口)
/// 作者:未闻
/// 时间:2020.02.13
/// 
/// 详细的指令介绍
/// https://blog.csdn.net/psongchao/article/details/1487788
/// </summary>
public class MP3Player
{
    // 播放标记,MCI接口是基于这个标记来处理,看播放暂停等代码就能明白,可以存在多个不同Tag的播放器
    private const string C_TAG_PLAYER = "MCI_MP3_PLAYER";

    #region 单例模式实现
    class Nested { public static MP3Player Instance = new MP3Player(); }
    private MP3Player()
    {
        //// 获取声道
        //mciSendString($"status {C_TAG_PLAYER} source", _temp, _temp.Capacity, 0);
        //_source = _sourceMap.FirstOrDefault(pair => pair.Value.Equals(_temp.ToString())).Key;
        //// 音频状态,是否静音
        //mciSendString($"status {C_TAG_PLAYER} audio", _temp, _temp.Capacity, 0);
        //_audioStatus = _audioStatusMap.FirstOrDefault(pair => pair.Value.Equals(_temp.ToString())).Key;
    }
    public static MP3Player Instance => Nested.Instance;
    #endregion
    #region API定义
    [DllImport("winmm.dll")]
    public 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);
    #endregion

    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"}
    };

    /// <summary>
    /// 播放
    /// </summary>
    /// <param name="fileName"></param>
    public void Play(string fileName)
    {
        if (Status == PlayerStatus.Playing)
        {
            Stop();
        }

        if (string.IsNullOrWhiteSpace(fileName))
            return;

        GetShortPathName(fileName, _temp, _temp.Capacity);
        var mp3Path = _temp.ToString();
        mciSendString($"open \"{mp3Path}\" alias {C_TAG_PLAYER}", null, 0, 0); //打开
        mciSendString($"play {C_TAG_PLAYER}", null, 0, 0);
        Status = PlayerStatus.Playing;

        // 因为设置静音后一播放,会变成有声音,所以这里要设置一下
        AudioStatus = _audioStatus;
        Source = _source;
    }

    /// <summary>
    /// 停止
    /// </summary>
    public void Stop()
    {
        mciSendString($"close {C_TAG_PLAYER}", null, 0, 0);
        Status = PlayerStatus.Stop;
    }

    /// <summary>
    /// 暂停
    /// </summary>
    public void Pause()
    {
        mciSendString($"pause {C_TAG_PLAYER}", null, 0, 0);
        Status = PlayerStatus.Pause;
    }

    /// <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;
            mciSendString($"setaudio {C_TAG_PLAYER} {_audioStatusMap[value]}", null, 0, 0);
        }
    }

    private AudioSource _source = AudioSource.H;
    /// <summary>
    /// 播放声道
    /// </summary>
    public AudioSource Source
    {
        get => _source;
        set
        {
            _source = value;
            mciSendString($"setaudio {C_TAG_PLAYER} source to {_sourceMap[value]}", null, 0, 0);
        }
    }

    private int _vol;
    /// <summary>
    /// 音量
    /// </summary>
    public int Volume
    {
        get => _vol;
        set
        {
            if (value < 0 || value > 1000)
                return;

            _vol = value;
            mciSendString($"setaudio {C_TAG_PLAYER} volume to {value}", null, 0, 0);
        }
    }

    /// <summary>
    /// 获取是否正在播放
    /// </summary>
    public bool IsPlaying => Status == PlayerStatus.Playing;
    /// <summary>
    /// 获取是否已播放结束
    /// </summary>
    public bool IsCompleted => Position >= Length;

    /// <summary>
    /// 获取播放总时长
    /// </summary>
    public int Length
    {
        get
        {
            mciSendString($"status {C_TAG_PLAYER} length", _temp, _temp.Capacity, 0);
            return Convert.ToInt32(_temp.ToString());
        }
    }
    /// <summary>
    /// 获取播放总时长(格式:00:00:00)
    /// </summary>
    public string LengthString
    {
        get
        {
            return Len2Time(Length);
        }
    }

    /// <summary>
    /// 获取播放进度
    /// </summary>
    public int Position
    {
        get
        {
            mciSendString($"status {C_TAG_PLAYER} position", _temp, _temp.Capacity, 0);
            return Convert.ToInt32(_temp.ToString());
        }
    }
    /// <summary>
    /// 获取播放进度(格式:00:00:00)
    /// </summary>
    public string PositionString
    {
        get
        {
            return Len2Time(Position);
        }
    }

    /// <summary>
    /// 把时长从int类型转换成格式为00:00:00的字符串
    /// </summary>
    /// <param name="len"></param>
    /// <returns></returns>
    private string Len2Time(int len)
    {
        int sec = len / 1000 % 60;
        int min = len / 60000 % 60;
        int hour = len / 3600000;
        return string.Format("{0:D2}:{1:D2}:{2:D2}", hour, 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

四、调用示例

MP3Player player = MP3Player.Instance;
private void btnPlay_Click(object sender, EventArgs e)
{
    if (openFileDialog1.ShowDialog() == DialogResult.OK)
    {
        player.Play(openFileDialog1.FileName);
        tsslTotal.Text = player.LengthString;// 获取总时长
        tmrTick.Start();
    }
}

private void timer1_Tick(object sender, EventArgs e)
{
    tsslPosition.Text = player.PositionString;// 更新当前播放进度

    if (player.IsCompleted)
    {
        tmrTick.Stop();
        MessageBox.Show("播放结束。");
    }
}

五、参考资料

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!