今天整理到音频播放的部分,本来就想抽取一个简单的接口方便以后可能会用到,然而不知不觉就把常用的功能都给一起封装好了,核心其实就是调用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 }
四、调用示例
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("播放结束。"); } }
五、参考资料
来源:https://www.cnblogs.com/yokeqi/p/12305967.html