2022-08-20 乐帮网
c#
Naudio 封装了一个简单的库用于播放音频我使用的是NAudio 1.8.5 开发平台是net40。当然也可以升级到NAudio最新版本也是一样可以兼容的。它支持许多音频操作,可实现多种API播放与录制、多种不同音频格式、音频格式转换(重采样、位深、声道等)、音频编码、多通道播放、音频效果处理等等。这里只展示了播放音频文件功能
本demo现实的功能如下:
(1) 判断是否正在播放音频,如果正在播放就停止播放。
(2)实时显示音频的进度。
(3)可并发播放音频文件,即同时播放。
(4)可顺序播放音频文件,即当上一个播放完成后才播放后面的文件。
(5)可实现单例播放。兼容常见的音频格式。WAV
AIFF
MP3 (using ACM, DMO or MFT)
G.711 mu-law and a-law
ADPCM, G.722, Speex (using NSpeex)
WMA, AAC, MP4 and more others with Media Foundation
首先建立一个Window Form 项目Nuget引入NAudio类库。看自己的需求,我是需要支持XP系统 所以做了一个.Framework4.0的demo。 这里引入 了NAudio 1.8.5 。
音频播放核心代码:
var outputDevice = new WaveOutEvent();
var audioFile = new AudioFileReader(@"D:\example.mp3");
outputDevice.Init(audioFile);
outputDevice.Play();
播放url中音频代码如下:
var url = "http://media.ch9.ms/ch9/2876/fd36ef30-cfd2-4558-8412-3cf7a0852876/AzureWebJobs103.mp3";
using(var mf = new MediaFoundationReader(url))
using(var wo = new WasapiOut())
{
wo.Init(mf);
wo.Play();
while (wo.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(1000);
}
}
以上功能暂不在封装之列
下面实现我们第一个封装功能,如何判断播放是否已经完成了呢?首先想到的是PlaybackStopped 事件。我们可以使用如下代码
var outputDevice = new WaveOutEvent();
var audioFile = new AudioFileReader(@"D:\example.mp3");
outputDevice.Init(audioFile);
outputDevice.PlaybackStopped += new EventHandler<StoppedEventArgs>(audioOutput_PlaybackStopped);
outputDevice.Play();
但是很不幸运,以上的Stop事件只能是在调用Stop方法时才会触发。不能满足我们的要求。
于是我就是想到了通过监听它的播放进度来判断 是否完成播放,于是又引出来了别一个问题,如何监听音频文件的播放进度呢?在这里我搜索到了这篇文章:https://blog.csdn.net/qq_29092379/article/details/104339250
提到两种方法,
(1)通过监听文件读取的字节来判断,这是我推荐的。我的源码正是使用了此方法获取
我们只需要定制一个自己的AudioFileReader就可以实现,我的代码如下:
/// <summary>
/// lebang2020.cn 自定义 AudioFileReader
/// </summary>
public class VktAudioFileReader : AudioFileReader
{
private Action<int, long> _callBack;
private int _totalBytes;
public VktAudioFileReader(string fileName, Action<int, long> callBack) : base(fileName)
{
_callBack = callBack;
}
//
// 摘要:
// Reads from this wave stream
//
// 参数:
// buffer:
// Audio buffer
//
// offset:
// Offset into buffer
//
// count:
// Number of bytes required
//
// 返回结果:
// Number of bytes read
public override int Read(byte[] buffer, int offset, int count)
{
int res = base.Read(buffer, offset, count);
_totalBytes += res;
_callBack(_totalBytes, this.Length);
return res;
}
}
(2)通过额外的进程来获取waveOut.GetPosition()的进度计算。不推荐,移植性不强,不符合软件设计思路。主要代码如下:
// 开始播放时,启动新的线程去获取进度
this.Thread = new System.Threading.Thread(new System.Threading.ThreadStart(OnPlaying));
this.Thread.Start();
private void OnPlaying()
{
while (true)
{
if (this.waveOut.PlaybackState == PlaybackState.Playing)
{
double ms = this.waveOut.GetPosition() * 1000.0 / this.waveOut.OutputWaveFormat.BitsPerSample / this.waveOut.OutputWaveFormat.Channels * 8.0 / this.waveOut.OutputWaveFormat.SampleRate;
Console.WriteLine("Milliseconds Played: " + ms);
}
System.Threading.Thread.Sleep(100);
}
由此就实现了监听播放进度这个关键功能,同时意味了实现了判断音频文件是否播放完成。进而又延伸出了并发播放音频和顺序播放音频这样的功能。
自己写的小示例界面如下:
有问题可以联系作者
提供源代码的下载:https://pan.baidu.com/s/1nDA_4BacDEGSehjfZjzdng
关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com
庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。
如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力