今天整理到音频播放的部分,本来就想抽取一个简单的接口方便以后可能会用到,然而不知不觉就把常用的功能都给一起封装好了,实现只需一行代码就实现音频播放,核心其实就是调用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. 洛谷训练新手村之“BOSS战-入门综合练习2”题解

    P1426 小鱼会有危险吗 题目链接:https://www.luogu.com.cn/problem/P1426 题目大意: 有一次,小鱼要从A处沿直线往右边游,小鱼第一秒可以游7米,从第二秒开始每 ...

  2. Mybatis是如何实现SQL防注入的

    Mybatis这个框架在日常开发中用的很多,比如面试中经常有一个问题:$和#的区别,它们的区别是使用#可以防止SQL注入,今天就来看一下它是如何实现SQL注入的. 什么是SQL注入 在讨论怎么实现之前 ...

  3. 09_$.ajax()参数详解及标准写法

    1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如 ...

  4. k8s-自动安装

    操作环境: centos7.3 node102-master-192.168.100.102 node103-node1-192.168.100.103 node104-node2-192.168.1 ...

  5. 个人博客-vue-blog

    http://47.100.126.169/zmengBlog/

  6. AcWing 220. 最大公约数 | 欧拉函数

    传送门 题目描述 给定整数N,求1<=x,y<=N且GCD(x,y)为素数的数对(x,y)有多少对. GCD(x,y)即求x,y的最大公约数. 输入格式 输入一个整数N 输出格式 输出一个 ...

  7. BFC 是什么东西?

    以下是本人理解的 BFC  和 官方文档BFC资料 . BFC 是页面元素的隐藏属性,全称 : Block Formatting Context 作用: 可以清除子元素浮动后不良效果在线效果地址:ht ...

  8. 【大道至简】NetCore3.1快速开发框架一:搭建框架

    这一章,我们直接创建NetCore3.1的项目 主要分为1个Api项目,和几个类库 解释: 项目——FytSoa.Api:提供前端接口的Api项目 类库——FytSoa.Core:包含了数据库操作类和 ...

  9. python 进程Queue

    1.作用:进程之间的数据交互 2.常用方法 """ 对象.put() 作用:放入队列一个数据 对象.get() 作用:取队列一个数据,若队列没有值,则阻塞 对象.empt ...

  10. ORM基础2 字段及其参数和meta

    一.ORM简介 1.概念:ORM(Object Relational Mappingt ),对象关系映射 2.实质:类与数据库之间的映射 3.优点: 开发人员不用写数据库 4.缺点: 开发人员,数据库 ...