Naudio播放音频的封装

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

由此就实现了监听播放进度这个关键功能,同时意味了实现了判断音频文件是否播放完成。进而又延伸出了并发播放音频和顺序播放音频这样的功能。
自己写的小示例界面如下:

nauio

有问题可以联系作者
提供源代码的下载:https://pan.baidu.com/s/1nDA_4BacDEGSehjfZjzdng 

 

公众号二维码

关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com

庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。

欧阳修

付款二维码

如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力