今天整理到音频播放的部分,本来就想抽取一个简单的接口方便以后可能会用到,然而不知不觉就把常用的功能都给一起封装好了,实现只需一行代码就实现音频播放,核心其实就是调用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, , );
}
private string SendCommandForResult(string cmd)
{
mciSendString(cmd, _temp, _temp.Capacity, );
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();
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 =
}; 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 = ;
/// <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 < || value > )
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 ; 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 ; return Convert.ToInt32(_temp.ToString());
}
set
{
if (value < || 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 / % ;
int min = len / % ;
return string.Format("{0:D2}:{1:D2}", min, sec);
}
} public enum PlayerStatus
{
/// <summary>
/// 停止
/// </summary>
Stop = ,
/// <summary>
/// 播放中
/// </summary>
Playing = ,
/// <summary>
/// 暂停
/// </summary>
Pause =
}
public enum AudioSource
{
/// <summary>
/// 立体声
/// </summary>
H = ,
/// <summary>
/// 平均声道
/// </summary>
A = ,
/// <summary>
/// 左声道
/// </summary>
L = ,
/// <summary>
/// 右声道
/// </summary>
R =
}
}

四、调用示例

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

五、参考资料

MP3播放-基于MCI-API接口的更多相关文章

  1. libEasyPlayer RTSP windows播放器SDK API接口设计说明

    概述 libEasyPlayer实现对RTSP直播流进行实时采集和解码显示,稳定,高效,低延时:解码可采用intel硬件解码和软件解码两种方式,能实时进行录像和快照抓图,OSD叠加等功能. API接口 ...

  2. 基于网页api(接口)实现查快递

    之前在网上找到一款下载某慕课网站的java版软件,我想知道他是怎么实现:对于视频的下载的,毕竟网页源码中大都不会直接放视频的地址,但是没有公布源码,我就反编译,等到了部分“源码”,逻辑上还是有些问题, ...

  3. 基于Metaweblog API 接口一键发布到国内外主流博客平台

    之前的生活 之前一直使用evenote写博客和日志,其实还是挺方便的.但是我一直都希望能够同步到国内的博客和国外的blogspot等主流博客平台.而强大everote只提供了facebook.twit ...

  4. 如何利用百度音乐播放器的API接口来获取高音质歌曲

    第一步:在网页中打开以下网址: http://box.zhangmen.baidu.com/x?op=12&count=1&title=时间都去哪儿了$$王铮亮$$$$ 其中红色地方可 ...

  5. atitit.基于http json api 接口设计 最佳实践 总结o7

    atitit.基于http  json  api 接口设计 最佳实践 总结o7 1. 需求:::服务器and android 端接口通讯 2 2. 接口开发的要点 2 2.1. 普通参数 meth,p ...

  6. 在线音乐播放器-----酷狗音乐api接口抓取

    首先身为一个在线音乐播放器,需要前端和数据库的搭配使用. 在数据库方面,我们没有办法制作,首先是版权问题,再加上数据量.所以我们需要借用其他网络播放器的数据库. 但是这些在线播放器,如百度,酷狗,酷我 ...

  7. Yii2 基于RESTful架构的 advanced版API接口开发 配置、实现、测试 (转)

    环境配置: 开启服务器伪静态 本处以apache为例,查看apache的conf目录下httpd.conf,找到下面的代码 LoadModule rewrite_module modules/mod_ ...

  8. Yii2 基于RESTful架构的 advanced版API接口开发 配置、实现、测试

    环境配置: 开启服务器伪静态 本处以apache为例,查看apache的conf目录下httpd.conf,找到下面的代码 LoadModule rewrite_module modules/mod_ ...

  9. 基于C#的SolidWorks插件开发(1)--SolidWorks API接口介绍

    这是两年前毕业时写的一篇关于SolidWorks插件开发与公司PDM集成的毕业设计,最近闲来无事拿出来整理一下,大神们可以略过. 1.1   SolidWorks API接口 正确调用SolidWor ...

随机推荐

  1. 利用Tampermonkey(油猴)+ IDM 实现百度云盘大文件下载(IDM安装教程)

    关注微信公众号:“指尖创意” 在菜单干货专区软件目录里领取链接: tampermonkey插件是一个免费的浏览器扩展和最为流行的用户脚本管理器,拥有适用于 Chrome, Microsoft Edge ...

  2. (二)Angular+spring-security-cas前后端分离(基于ticket代码实现

    一.前端实现 1.1.路由守卫(用于拦截路由认证) import { Injectable, Inject } from "@angular/core"; import { Can ...

  3. MAVEN配置及Spring Tool Suite的Maven配置

    1.下载Maven http://maven.apache.org/download.cgi 如图点击下载即可 2.Maven配置 2.1配置本地仓库 创建目录maven-repository如图所示 ...

  4. 快速回顾MySQL:简单查询操作

    利用空闲时间花几分钟回顾一下 7.1 检索数据 为了查询出数据库表中的行(数据),使用SELECE语句. 格式: # 第一种 SELECT * FROM <table_name>; # 第 ...

  5. VirtualBox扩充磁盘&清空安装包

    1.virtual box 扩充磁盘空间 D:\VirtualBox\VBoxManage.exe modifyhd "E:\virtual box\daisyyun\daisyyun.vd ...

  6. struts2学习第一天

    Stuts2是基于MVC设计模式成熟的Web应用框架.不仅仅是Struts1的下一个版本,是一个全新的Struts架构.由WebWork社区跟Strut社区联手打造的.(教程来自W3Cschool) ...

  7. if(a)是什么意思

    if(a)等价于 if(a!=0) if(!a)等价于 if(a==0)

  8. 高精度算法(C/C++)

    高精度算法 (C/C++) 做ACM题的时候,经常遇到大数的加减乘除,乘幂,阶乘的计算,这时给定的数据类型往往不够表示最后结果,这时就需要用到高精度算法.高精度算法的本质是把大数拆成若干固定长度的块, ...

  9. 如何取消chrome的自动翻译

    设置中搜索“翻译” 然后关掉“询问是否翻译非您所用语言的网页”的选项即可 需要翻译的时候在目标网页右键“翻译成中文”即可 用github的时候烦的要死,翻译的贼不准.

  10. SEO 搜索 形成一个关联