最完整的NAudio教程:音频播放与录制核心技术解析
【免费下载链接】NAudio Audio and MIDI library for .NET 项目地址: https://gitcode.com/gh_mirrors/na/NAudio
引言:.NET音频开发的痛点与解决方案
你是否在.NET开发中遇到过音频处理的难题?无论是实现高品质的音频播放、实时录音,还是处理复杂的音频格式转换,选择合适的工具至关重要。NAudio作为.NET平台最强大的音频处理库,提供了丰富的API和灵活的架构,让开发者能够轻松应对各种音频场景。本文将带你深入探索NAudio的核心技术,从基础的音频播放到高级的录制功能,全面掌握NAudio的使用方法。
读完本文,你将能够:
- 理解NAudio的核心架构和主要组件
- 实现多种音频播放方式,支持不同格式和设备
- 掌握实时录音技术,包括麦克风输入和系统音频捕获
- 处理音频格式转换、音量控制等常见需求
- 解决音频开发中的性能优化和错误处理问题
NAudio架构概览:核心组件与工作流程
NAudio的核心命名空间
NAudio库采用模块化设计,主要包含以下关键命名空间:
| 命名空间 | 主要功能 | 核心类 |
|---|---|---|
| NAudio.Wave | 基础音频处理 | WaveOutEvent, WaveInEvent, AudioFileReader |
| NAudio.Wasapi | Windows音频会话API | WasapiOut, WasapiCapture, WasapiLoopbackCapture |
| NAudio.MediaFoundation | 媒体基础支持 | MediaFoundationReader, MediaFoundationEncoder |
| NAudio.Codecs | 音频编解码 | Mp3FileReader, WaveFormatConversionStream |
| NAudio.Midi | MIDI处理 | MidiIn, MidiOut, MidiFile |
NAudio音频处理流程图
音频播放核心技术
1. 基础音频播放:WaveOutEvent
WaveOutEvent是NAudio中最简单易用的音频输出类,基于传统的Windows WaveOut API。它适用于大多数基础播放场景,使用方便且兼容性好。
using NAudio.Wave;
using System;
using System.Windows.Forms;
public class BasicAudioPlayer
{
private WaveOutEvent outputDevice;
private AudioFileReader audioFile;
public void PlayAudio(string filePath)
{
// 初始化输出设备
outputDevice = new WaveOutEvent();
outputDevice.PlaybackStopped += OnPlaybackStopped;
// 加载音频文件
audioFile = new AudioFileReader(filePath);
// 初始化并开始播放
outputDevice.Init(audioFile);
outputDevice.Play();
}
private void OnPlaybackStopped(object sender, StoppedEventArgs e)
{
// 清理资源
outputDevice.Dispose();
audioFile.Dispose();
outputDevice = null;
audioFile = null;
// 处理播放停止事件(错误或正常结束)
if (e.Exception != null)
{
MessageBox.Show($"播放错误: {e.Exception.Message}");
}
}
public void StopAudio()
{
outputDevice?.Stop();
}
}
2. 现代音频播放:WasapiOut
WasapiOut基于Windows Audio Session API (WASAPI),提供了更低的延迟和更好的音频质量,支持多通道音频和高级音频功能。
using NAudio.Wasapi;
using NAudio.Wave;
public class WasapiAudioPlayer
{
private WasapiOut wasapiOut;
private AudioFileReader audioFile;
public void PlayAudio(string filePath)
{
// 创建WasapiOut实例,使用默认音频渲染设备
// 参数说明:设备ID(-1表示默认), latency(延迟,毫秒),是否独占模式
wasapiOut = new WasapiOut(-1, 100, false);
// 加载音频文件
audioFile = new AudioFileReader(filePath);
// 初始化并播放
wasapiOut.Init(audioFile);
wasapiOut.Play();
}
public void StopAudio()
{
if (wasapiOut != null)
{
wasapiOut.Stop();
wasapiOut.Dispose();
wasapiOut = null;
}
if (audioFile != null)
{
audioFile.Dispose();
audioFile = null;
}
}
}
3. 音频播放控制:暂停、继续与音量调节
NAudio提供了丰富的播放控制功能,包括暂停/继续播放、音量调节和播放位置控制等。
public class AdvancedAudioPlayer
{
private WaveOutEvent outputDevice;
private AudioFileReader audioFile;
private bool isPaused = false;
// 初始化播放器
public void Initialize(string filePath)
{
outputDevice = new WaveOutEvent();
audioFile = new AudioFileReader(filePath);
outputDevice.Init(audioFile);
}
// 播放/暂停切换
public void TogglePlayPause()
{
if (outputDevice.PlaybackState == PlaybackState.Playing)
{
outputDevice.Pause();
isPaused = true;
}
else if (outputDevice.PlaybackState == PlaybackState.Paused)
{
outputDevice.Play();
isPaused = false;
}
else // Stopped
{
outputDevice.Play();
}
}
// 音量调节
public void SetVolume(float volume)
{
// 音量范围是0.0f到1.0f
if (audioFile != null)
{
audioFile.Volume = Math.Clamp(volume, 0.0f, 1.0f);
}
}
// 调整播放位置
public void SetPosition(TimeSpan position)
{
if (audioFile != null)
{
audioFile.CurrentTime = position;
}
}
// 获取当前播放位置
public TimeSpan GetCurrentPosition()
{
return audioFile?.CurrentTime ?? TimeSpan.Zero;
}
// 获取总时长
public TimeSpan GetTotalDuration()
{
return audioFile?.TotalTime ?? TimeSpan.Zero;
}
}
4. 支持多种音频格式:MediaFoundationReader
NAudio通过MediaFoundationReader提供了对多种音频格式的支持,包括MP3、WMA、AAC等。使用MediaFoundationReader可以轻松处理不同格式的音频文件。
using NAudio.MediaFoundation;
using NAudio.Wave;
using System;
public class MediaFoundationPlayer
{
private WaveOutEvent outputDevice;
private MediaFoundationReader mediaReader;
public MediaFoundationPlayer()
{
// 初始化MediaFoundation
MediaFoundationApi.Startup();
}
public void PlayFile(string filePath)
{
try
{
// 创建MediaFoundationReader读取音频文件
mediaReader = new MediaFoundationReader(filePath);
// 创建输出设备
outputDevice = new WaveOutEvent();
outputDevice.Init(mediaReader);
outputDevice.Play();
Console.WriteLine($"播放中: {filePath}");
Console.WriteLine($"格式信息: {mediaReader.WaveFormat.SampleRate}Hz, {mediaReader.WaveFormat.BitsPerSample}位, {mediaReader.WaveFormat.Channels}声道");
}
catch (Exception ex)
{
Console.WriteLine($"播放失败: {ex.Message}");
}
}
public void Stop()
{
outputDevice?.Stop();
outputDevice?.Dispose();
mediaReader?.Dispose();
}
~MediaFoundationPlayer()
{
// 清理MediaFoundation
MediaFoundationApi.Shutdown();
}
}
音频录制技术详解
1. 麦克风录音:WaveInEvent
使用WaveInEvent类可以轻松实现从麦克风录制音频,并保存为WAV文件。这种方法适用于简单的录音场景,实现简单且兼容性好。
using NAudio.Wave;
using System;
using System.IO;
public class MicrophoneRecorder
{
private WaveInEvent waveIn;
private WaveFileWriter writer;
private string outputFilePath;
public MicrophoneRecorder(string outputPath)
{
outputFilePath = outputPath;
// 初始化录音设备
waveIn = new WaveInEvent();
// 设置录音格式(默认是44.1kHz,16位,立体声)
waveIn.WaveFormat = new WaveFormat(44100, 16, 2);
// 数据可用时的事件处理
waveIn.DataAvailable += OnDataAvailable;
// 录音停止事件
waveIn.RecordingStopped += OnRecordingStopped;
}
public void StartRecording()
{
// 创建输出目录
string directory = Path.GetDirectoryName(outputFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 创建WaveFileWriter
writer = new WaveFileWriter(outputFilePath, waveIn.WaveFormat);
// 开始录音
waveIn.StartRecording();
Console.WriteLine("开始录音...");
}
public void StopRecording()
{
if (waveIn.RecordingState == RecordingState.Recording)
{
waveIn.StopRecording();
Console.WriteLine("停止录音...");
}
}
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
// 将录制的数据写入文件
writer.Write(e.Buffer, 0, e.BytesRecorded);
// 计算并显示录音时长
double secondsRecorded = writer.Length / (double)writer.WaveFormat.AverageBytesPerSecond;
Console.WriteLine($"已录制: {secondsRecorded:F2}秒");
}
private void OnRecordingStopped(object sender, StoppedEventArgs e)
{
// 清理资源
writer?.Dispose();
writer = null;
if (e.Exception != null)
{
Console.WriteLine($"录音错误: {e.Exception.Message}");
}
else
{
Console.WriteLine($"录音完成,文件保存至: {outputFilePath}");
}
}
public void Dispose()
{
waveIn?.Dispose();
writer?.Dispose();
}
}
2. 系统音频录制:WasapiLoopbackCapture
WasapiLoopbackCapture允许你捕获系统正在播放的音频,这对于录制流媒体、游戏声音或其他应用程序的音频输出非常有用。
using NAudio.Wasapi;
using NAudio.Wave;
using System;
using System.IO;
public class SystemAudioRecorder
{
private WasapiLoopbackCapture loopbackCapture;
private WaveFileWriter writer;
private string outputFilePath;
private bool isRecording = false;
public SystemAudioRecorder(string outputPath)
{
outputFilePath = outputPath;
}
public void StartRecording()
{
if (isRecording) return;
// 创建回环捕获设备
loopbackCapture = new WasapiLoopbackCapture();
// 数据可用事件
loopbackCapture.DataAvailable += OnDataAvailable;
// 录制停止事件
loopbackCapture.RecordingStopped += OnRecordingStopped;
// 创建输出目录
string directory = Path.GetDirectoryName(outputFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 创建WaveFileWriter
writer = new WaveFileWriter(outputFilePath, loopbackCapture.WaveFormat);
// 开始录制
loopbackCapture.StartRecording();
isRecording = true;
Console.WriteLine("开始录制系统音频...");
}
public void StopRecording()
{
if (!isRecording) return;
loopbackCapture.StopRecording();
isRecording = false;
Console.WriteLine("停止录制系统音频...");
}
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
// 将捕获的数据写入文件
writer.Write(e.Buffer, 0, e.BytesRecorded);
// 显示录制进度
double secondsRecorded = writer.Length / (double)writer.WaveFormat.AverageBytesPerSecond;
Console.WriteLine($"已录制系统音频: {secondsRecorded:F2}秒");
}
private void OnRecordingStopped(object sender, StoppedEventArgs e)
{
// 清理资源
loopbackCapture.Dispose();
writer?.Dispose();
writer = null;
if (e.Exception != null)
{
Console.WriteLine($"录制错误: {e.Exception.Message}");
}
else
{
Console.WriteLine($"系统音频录制完成,文件保存至: {outputFilePath}");
}
}
}
3. 高级录音功能:WasapiCapture
WasapiCapture提供了更高级的录音功能,支持更低的延迟和更好的音频质量,适用于专业音频录制场景。
using NAudio.Wasapi;
using NAudio.Wave;
using System;
using System.IO;
public class WasapiRecorder
{
private WasapiCapture wasapiCapture;
private WaveFileWriter writer;
private string outputFilePath;
private bool isRecording = false;
public WasapiRecorder(string outputPath)
{
outputFilePath = outputPath;
}
public void StartRecording()
{
if (isRecording) return;
// 获取默认录音设备
var enumerator = new MMDeviceEnumerator();
var device = enumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
Console.WriteLine($"使用录音设备: {device.FriendlyName}");
// 创建WasapiCapture实例
wasapiCapture = new WasapiCapture(device);
// 设置录音格式(可选)
// 注意:不是所有设备都支持任意格式,可能需要查询设备支持的格式
// wasapiCapture.WaveFormat = new WaveFormat(48000, 16, 1);
// 事件处理
wasapiCapture.DataAvailable += OnDataAvailable;
wasapiCapture.RecordingStopped += OnRecordingStopped;
// 准备输出文件
string directory = Path.GetDirectoryName(outputFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
writer = new WaveFileWriter(outputFilePath, wasapiCapture.WaveFormat);
// 开始录音
wasapiCapture.StartRecording();
isRecording = true;
Console.WriteLine("开始高级录音...");
Console.WriteLine($"录音格式: {wasapiCapture.WaveFormat.SampleRate}Hz, {wasapiCapture.WaveFormat.BitsPerSample}位, {wasapiCapture.WaveFormat.Channels}声道");
}
public void StopRecording()
{
if (!isRecording) return;
wasapiCapture.StopRecording();
isRecording = false;
Console.WriteLine("停止高级录音...");
}
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
writer.Write(e.Buffer, 0, e.BytesRecorded);
// 计算录音时长
double secondsRecorded = writer.Length / (double)writer.WaveFormat.AverageBytesPerSecond;
Console.WriteLine($"已录制: {secondsRecorded:F2}秒");
}
private void OnRecordingStopped(object sender, StoppedEventArgs e)
{
wasapiCapture.Dispose();
writer?.Dispose();
writer = null;
if (e.Exception != null)
{
Console.WriteLine($"录音错误: {e.Exception.Message}");
}
else
{
Console.WriteLine($"高级录音完成,文件保存至: {outputFilePath}");
}
}
}
音频格式转换与处理
1. 音频格式转换基础
音频格式转换是音频处理中的常见需求,NAudio提供了多种方式来处理不同格式之间的转换。
using NAudio.Wave;
using System;
using System.IO;
public class AudioConverter
{
// MP3转WAV
public void ConvertMp3ToWav(string mp3FilePath, string wavFilePath)
{
using (var reader = new Mp3FileReader(mp3FilePath))
{
WaveFileWriter.CreateWaveFile(wavFilePath, reader);
}
Console.WriteLine($"MP3转换为WAV完成: {wavFilePath}");
}
// 格式转换通用方法
public void ConvertAudioFormat(string inputFilePath, string outputFilePath, WaveFormat targetFormat)
{
using (var reader = new AudioFileReader(inputFilePath))
using (var conversionStream = new WaveFormatConversionStream(targetFormat, reader))
{
WaveFileWriter.CreateWaveFile(outputFilePath, conversionStream);
}
Console.WriteLine($"音频格式转换完成: {outputFilePath}");
}
// 改变音频采样率
public void ResampleAudio(string inputFilePath, string outputFilePath, int targetSampleRate)
{
using (var reader = new AudioFileReader(inputFilePath))
{
if (reader.WaveFormat.SampleRate != targetSampleRate)
{
using (var resampler = new MediaFoundationResampler(reader, WaveFormat.CreateIeeeFloatWaveFormat(targetSampleRate, reader.WaveFormat.Channels)))
{
resampler.ResamplerQuality = 60; // 设置重采样质量 (0-100)
WaveFileWriter.CreateWaveFile(outputFilePath, resampler);
}
}
else
{
// 如果采样率相同,直接复制
File.Copy(inputFilePath, outputFilePath, true);
}
}
Console.WriteLine($"音频重采样完成: {outputFilePath}");
}
// 立体声转单声道
public void ConvertToMono(string inputFilePath, string outputFilePath)
{
using (var reader = new AudioFileReader(inputFilePath))
{
if (reader.WaveFormat.Channels > 1)
{
var monoFormat = WaveFormat.CreateIeeeFloatWaveFormat(reader.WaveFormat.SampleRate, 1);
using (var conversionStream = new WaveFormatConversionStream(monoFormat, reader))
{
WaveFileWriter.CreateWaveFile(outputFilePath, conversionStream);
}
}
else
{
// 如果已经是单声道,直接复制
File.Copy(inputFilePath, outputFilePath, true);
}
}
Console.WriteLine($"立体声转单声道完成: {outputFilePath}");
}
}
2. 音频混音与效果处理
NAudio提供了丰富的音频处理功能,可以实现混音、音量调节、均衡器等效果。
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System;
using System.Collections.Generic;
public class AudioMixer
{
private MixingSampleProvider mixer;
private List<AudioFileReader> audioFiles = new List<AudioFileReader>();
private WaveOutEvent outputDevice;
public AudioMixer()
{
// 初始化混音器,使用44.1kHz,立体声,32位浮点格式
mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
mixer.ReadFully = true; // 确保即使某些流结束,混音器仍能继续工作
}
// 添加音频文件到混音器
public void AddAudioFile(string filePath, float volume = 1.0f)
{
var reader = new AudioFileReader(filePath);
reader.Volume = volume;
// 将音频文件转换为混音器需要的格式
var sampleProvider = reader.ToSampleProvider();
var converted = new SampleToWaveProvider(sampleProvider);
var resampled = new WaveFormatConversionStream(mixer.WaveFormat, converted);
// 添加到混音器
mixer.AddMixerInput(resampled.ToSampleProvider());
audioFiles.Add(reader);
Console.WriteLine($"添加音频文件到混音器: {filePath}");
}
// 开始播放混音
public void Play()
{
if (outputDevice == null)
{
outputDevice = new WaveOutEvent();
outputDevice.Init(mixer);
}
outputDevice.Play();
Console.WriteLine("开始播放混音...");
}
// 暂停混音播放
public void Pause()
{
outputDevice?.Pause();
Console.WriteLine("暂停混音播放");
}
// 停止混音播放
public void Stop()
{
outputDevice?.Stop();
Console.WriteLine("停止混音播放");
}
// 设置单个音频流的音量
public void SetVolume(int index, float volume)
{
if (index >= 0 && index < audioFiles.Count)
{
audioFiles[index].Volume = Math.Clamp(volume, 0.0f, 1.0f);
Console.WriteLine($"设置音频流 {index} 音量为 {volume}");
}
}
// 清理资源
public void Dispose()
{
outputDevice?.Dispose();
foreach (var reader in audioFiles)
{
reader.Dispose();
}
audioFiles.Clear();
}
}
高级应用:实时音频可视化
结合NAudio和Windows Forms,我们可以创建实时音频可视化效果,直观地展示音频的波形或频谱。
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System;
using System.Windows.Forms;
public class AudioVisualizer : Form
{
private WaveInEvent waveIn;
private Bitmap pictureBoxBuffer;
private PictureBox visualizerBox;
private int[] waveformData;
private int waveformIndex = 0;
private const int WaveformWidth = 800;
private const int WaveformHeight = 200;
public AudioVisualizer()
{
Text = "NAudio 音频可视化器";
Size = new Size(WaveformWidth + 20, WaveformHeight + 40);
// 创建可视化区域
visualizerBox = new PictureBox();
visualizerBox.Dock = DockStyle.Fill;
visualizerBox.BackColor = Color.Black;
Controls.Add(visualizerBox);
// 初始化缓冲区
pictureBoxBuffer = new Bitmap(WaveformWidth, WaveformHeight);
waveformData = new int[WaveformWidth];
// 初始化录音设备
waveIn = new WaveInEvent();
waveIn.WaveFormat = new WaveFormat(44100, 1); // 单声道,44.1kHz
waveIn.DataAvailable += OnDataAvailable;
// 开始录音
waveIn.StartRecording();
}
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
// 处理音频数据,提取波形信息
for (int i = 0; i < e.BytesRecorded; i += 2)
{
// 将16位PCM样本转换为整数
short sample = (short)((e.Buffer[i + 1] << 8) | e.Buffer[i]);
// 归一化到0到WaveformHeight范围
float normalized = (float)sample / short.MaxValue;
waveformData[waveformIndex] = (int)(normalized * WaveformHeight / 2 + WaveformHeight / 2);
// 更新索引
waveformIndex = (waveformIndex + 1) % WaveformWidth;
// 每收集一定数据后更新可视化
if (waveformIndex % 10 == 0)
{
BeginInvoke(new Action(UpdateVisualization));
}
}
}
private void UpdateVisualization()
{
using (var g = Graphics.FromImage(pictureBoxBuffer))
{
// 清除背景
g.Clear(Color.Black);
// 绘制波形
using (var pen = new Pen(Color.LimeGreen, 1))
{
for (int i = 0; i < WaveformWidth - 1; i++)
{
int x1 = i;
int y1 = waveformData[i];
int x2 = i + 1;
int y2 = waveformData[i + 1];
g.DrawLine(pen, x1, y1, x2, y2);
}
}
// 绘制中线
using (var pen = new Pen(Color.DarkGreen, 1))
{
g.DrawLine(pen, 0, WaveformHeight / 2, WaveformWidth, WaveformHeight / 2);
}
}
// 更新PictureBox
visualizerBox.Image = pictureBoxBuffer;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
waveIn.StopRecording();
waveIn.Dispose();
base.OnFormClosing(e);
}
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AudioVisualizer());
}
}
NAudio性能优化与最佳实践
1. 资源管理与性能优化
using NAudio.Wave;
using System;
using System.Collections.Generic;
public class AudioManager : IDisposable
{
private Dictionary<string, CachedSound> cachedSounds = new Dictionary<string, CachedSound>();
private AudioPlaybackEngine playbackEngine;
private bool isDisposed = false;
public AudioManager()
{
// 初始化播放引擎
playbackEngine = new AudioPlaybackEngine(44100, 2);
}
// 预加载音频文件到缓存
public void PreloadAudio(string key, string filePath)
{
if (!cachedSounds.ContainsKey(key))
{
cachedSounds[key] = new CachedSound(filePath);
Console.WriteLine($"预加载音频文件: {filePath}");
}
}
// 播放缓存的音频
public void PlayCachedAudio(string key, float volume = 1.0f, float pan = 0.0f)
{
if (cachedSounds.TryGetValue(key, out var cachedSound))
{
playbackEngine.PlaySound(cachedSound, volume, pan);
}
else
{
Console.WriteLine($"音频文件未缓存: {key}");
}
}
// 播放大型音频文件(使用流式处理)
public IWavePlayer PlayLargeAudioFile(string filePath)
{
var audioFileReader = new AudioFileReader(filePath);
// 使用WaveOutEvent而非WaveOut,避免UI线程阻塞
var waveOut = new WaveOutEvent();
waveOut.Init(audioFileReader);
waveOut.Play();
// 注册播放停止事件,自动清理资源
waveOut.PlaybackStopped += (sender, e) =>
{
audioFileReader.Dispose();
waveOut.Dispose();
Console.WriteLine("大型音频文件播放完成,资源已释放");
};
return waveOut;
}
// 性能优化建议:
// 1. 预加载频繁使用的音频文件
// 2. 对大型文件使用流式处理
// 3. 合理设置缓冲区大小,平衡延迟和稳定性
// 4. 在后台线程处理音频加载和格式转换
// 5. 及时释放不再使用的音频资源
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
// 释放托管资源
playbackEngine.Dispose();
cachedSounds.Clear();
}
isDisposed = true;
}
~AudioManager()
{
Dispose(false);
}
}
// 音频缓存辅助类
public class CachedSound
{
public float[] AudioData { get; private set; }
public WaveFormat WaveFormat { get; private set; }
public CachedSound(string audioFileName)
{
using (var audioFileReader = new AudioFileReader(audioFileName))
{
WaveFormat = audioFileReader.WaveFormat;
// 读取所有音频数据到内存
var wholeFile = new List<float>((int)(audioFileReader.Length / 4));
var buffer = new float[audioFileReader.WaveFormat.SampleRate * audioFileReader.WaveFormat.Channels];
int bytesRead;
while ((bytesRead = audioFileReader.Read(buffer, 0, buffer.Length)) > 0)
{
wholeFile.AddRange(buffer.Take(bytesRead));
}
AudioData = wholeFile.ToArray();
}
}
}
// 简单的音频播放引擎
public class AudioPlaybackEngine : IDisposable
{
private readonly IWavePlayer outputDevice;
private readonly MixingSampleProvider mixer;
private bool isDisposed = false;
public AudioPlaybackEngine(int sampleRate = 44100, int channelCount = 2)
{
outputDevice = new WaveOutEvent();
mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channelCount));
mixer.ReadFully = true;
outputDevice.Init(mixer);
outputDevice.Play();
}
public void PlaySound(CachedSound sound, float volume = 1.0f, float pan = 0.0f)
{
var input = new CachedSoundSampleProvider(sound);
// 应用音量和声道平衡
var volumeProvider = new VolumeSampleProvider(input);
volumeProvider.Volume = volume;
if (sound.WaveFormat.Channels == 1)
{
var panner = new PanningSampleProvider(volumeProvider);
panner.Pan = pan;
mixer.AddMixerInput(panner);
}
else
{
mixer.AddMixerInput(volumeProvider);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
outputDevice.Dispose();
}
isDisposed = true;
}
~AudioPlaybackEngine()
{
Dispose(false);
}
}
2. 错误处理与异常捕获
using NAudio.Wave;
using System;
public class AudioErrorHandler
{
public void SafePlayAudio(string filePath)
{
IWavePlayer waveOut = null;
AudioFileReader audioFile = null;
try
{
// 创建音频文件 reader
audioFile = new AudioFileReader(filePath);
// 创建输出设备
waveOut = new WaveOutEvent();
waveOut.Init(audioFile);
// 播放音频
waveOut.Play();
Console.WriteLine($"正在播放: {filePath}");
// 等待播放完成(实际应用中可能需要异步处理)
while (waveOut.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"文件未找到错误: {ex.Message}");
Console.WriteLine($"检查文件路径: {filePath}");
}
catch (FormatException ex)
{
Console.WriteLine($"音频格式错误: {ex.Message}");
Console.WriteLine("可能是不支持的音频格式或损坏的文件");
}
catch (WaveException ex)
{
Console.WriteLine($"音频播放错误: {ex.Message}");
// 处理特定的NAudio错误
if (ex.Message.Contains("No drivers"))
{
Console.WriteLine("未找到音频输出设备,请检查音频设备是否正常连接");
}
else if (ex.Message.Contains("Cannot open"))
{
Console.WriteLine("无法打开音频设备,可能被其他程序占用");
}
}
catch (Exception ex)
{
Console.WriteLine($"发生意外错误: {ex}");
}
finally
{
// 确保资源被释放
waveOut?.Stop();
waveOut?.Dispose();
audioFile?.Dispose();
Console.WriteLine("音频播放操作已清理");
}
}
public void SafeRecordAudio(string outputPath)
{
WaveInEvent waveIn = null;
WaveFileWriter writer = null;
try
{
// 创建录音设备
waveIn = new WaveInEvent();
waveIn.WaveFormat = new WaveFormat(44100, 1);
// 创建文件 writer
writer = new WaveFileWriter(outputPath, waveIn.WaveFormat);
// 数据可用事件处理
waveIn.DataAvailable += (s, e) =>
{
try
{
writer.Write(e.Buffer, 0, e.BytesRecorded);
}
catch (Exception ex)
{
Console.WriteLine($"录音数据处理错误: {ex.Message}");
// 尝试恢复或停止录音
waveIn?.StopRecording();
}
};
// 开始录音
waveIn.StartRecording();
Console.WriteLine("开始录音,按任意键停止...");
// 等待用户按键停止
Console.ReadKey();
// 停止录音
waveIn.StopRecording();
}
catch (Exception ex)
{
Console.WriteLine($"录音错误: {ex.Message}");
if (ex.Message.Contains("No device"))
{
Console.WriteLine("未找到录音设备,请检查麦克风是否连接");
}
else if (ex.Message.Contains("Access denied"))
{
Console.WriteLine("没有录音权限,请检查应用权限设置");
}
}
finally
{
// 确保资源被释放
waveIn?.Dispose();
writer?.Dispose();
Console.WriteLine("录音操作已清理");
}
}
}
结论与进阶学习路径
NAudio学习资源推荐
-
官方文档与示例
- NAudio官方GitHub仓库提供了丰富的示例代码
- 文档目录中的各.md文件详细介绍了特定功能的使用方法
-
进阶学习方向
- MIDI处理与合成
- 音频效果器开发
- 实时音频流处理
- 音频分析与可视化
-
实用工具推荐
- NAudioDemo: 官方示例程序,展示各种功能
- AudioFileInspector: 音频文件分析工具
- WaveFormRenderer: 音频波形渲染组件
NAudio应用场景扩展
NAudio不仅适用于简单的播放和录制,还可以应用于更复杂的场景:
- 语音识别预处理:音频降噪、端点检测
- 实时通信:VoIP应用开发
- 游戏音频:3D音效、环境音频
- 音频编辑:剪切、混音、特效处理
- 音乐可视化:频谱分析、音频反应动画
通过本文的学习,你已经掌握了NAudio的核心功能和使用方法。音频处理是一个广阔的领域,希望本文能为你的.NET音频开发之旅提供坚实的基础。继续探索NAudio的高级功能,你将能够构建更加强大和专业的音频应用。
后续学习建议
- 深入研究NAudio的SampleProvider架构,掌握音频流处理的核心概念
- 学习音频信号处理基础知识,理解各种音频效果的实现原理
- 探索NAudio与其他.NET框架的集成,如WPF、UWP等
- 参与NAudio社区,了解最新的功能和最佳实践
祝你在.NET音频开发的道路上越走越远!
【免费下载链接】NAudio Audio and MIDI library for .NET 项目地址: https://gitcode.com/gh_mirrors/na/NAudio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



