编程语言:C#
库:NAudio

NAudio 是一个开源的 .NET 音频处理库,它为开发者提供了丰富的功能,能在 Windows 平台上方便地进行音频的录制、播放、处理等操作。以下是关于 NAudio 库的详细介绍:
主要特性
- 多格式支持:支持多种常见的音频文件格式,如 WAV、MP3、OGG 等。这意味着你可以使用 NAudio 来播放或处理这些格式的音频文件。
- 音频录制:能够从系统的音频输入设备(如麦克风)录制音频,并保存为指定格式的文件。
- 音频播放:可以播放本地音频文件,也可以通过网络流式播放音频。同时,还支持对播放进行控制,如暂停、继续、停止等。
- 音频处理:提供了一些音频处理功能,如音量控制、音调调整、音频混合等。
- 低延迟:对于需要实时音频处理的应用场景,NAudio 能够保证较低的延迟。
常见使用场景
-
音频播放器开发:可以使用 NAudio 快速开发一个简单的音频播放器,支持多种音频格式的播放。
-
语音录制应用:开发语音备忘录、会议录音等应用程序。
-
音频处理工具:实现音频的剪辑、混音、格式转换等功能。
//核心代码
private void ProcessingWorker_DoWork(object sender, DoWorkEventArgs e)
{
processedSamples = 0;
var parameters = e.Argument as ProcessingParameters;
if (parameters == null) return;try { using (var reader = CreateAudioReader(parameters.InputPath)) { var sampleProvider = reader.ToSampleProvider(); var format = sampleProvider.WaveFormat; sampleRate = format.SampleRate; // 使用文件实际采样率 var channelCount = format.Channels; var outputPaths = GetOutputPaths(parameters.InputPath, parameters.OutputDirectory); // 优化缓冲区:4倍采样率缓冲区减少I/O次数 int bufferSize = format.SampleRate * channelCount * 4; var buffer = new float[bufferSize]; using (var vocalsWriter = new WaveFileWriter(outputPaths.Item1, format)) using (var accompWriter = new WaveFileWriter(outputPaths.Item2, format)) { long totalSamples = reader.Length / (format.BitsPerSample / 8 * channelCount); int samplesRead; // var buffer = new float[bufferSize]; var newBuffer = new float[bufferSize]; while ((samplesRead = sampleProvider.Read(buffer, 0, bufferSize)) > 0) { if (processingWorker.CancellationPending) { e.Cancel = true; return; } // 按声道对处理(支持单/双声道) for (int i = 0; i < samplesRead; i += channelCount) { float left = buffer[i]; float right = channelCount == 2 ? (i + 1 < samplesRead ? buffer[i + 1] : 0) : left; if (parameters.SeparationMode == SeparationMode.KaraokeMode) { // 卡拉OK模式核心处理:左右声道相减 + 带通滤波 float karaoke = left - right; float filtered = ApplyBandPassFilter(karaoke, channelCount == 1 ? leftFilterState : (i % 2 == 0 ? leftFilterState : rightFilterState)); left = right = filtered * gain; // 双声道保持一致 } buffer[i] = left; if (channelCount == 2 && i + 1 < samplesRead) { buffer[i + 1] = right; } } if (parameters.SeparationMode == SeparationMode.KaraokeMode) { // 对伴奏轨道应用低音增强(仅处理双声道) for (int i = 0; i < samplesRead; i += channelCount) { float left = buffer[i]; float right = channelCount == 2 ? buffer[i + 1] : left; // 应用低音增强 left = ApplyBassBoost(left, sampleRate); right = ApplyBassBoost(right, sampleRate); buffer[i] = left; if (channelCount == 2) buffer[i + 1] = right; } } WriteSeparatedTracks(buffer, samplesRead, vocalsWriter, accompWriter, channelCount, parameters.SeparationMode); processedSamples += samplesRead; //processedSamples += samplesRead; processingWorker.ReportProgress((int)(processedSamples * 100 / totalSamples)); } } } } catch (Exception ex) { e.Result = ex; MessageBox.Show($"An error occurred during the processing: {ex.Message}",this .Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private float ApplyBandPassFilter(float input, FilterState state) { // 带通滤波器系数计算(IIR滤波器,改进型Sallen-Key结构) double w0 = 2 * Math.PI * ((lowerCutoff + upperCutoff) / 2) / sampleRate; double bw = upperCutoff - lowerCutoff; double q = (lowerCutoff + upperCutoff) / (2 * bw); // 品质因数 double alpha = Math.Sin(w0) / (2 * q); // 滤波器系数 double a0 = alpha; double a1 = 0; double a2 = -alpha; double b0 = 1 + alpha; double b1 = -2 * Math.Cos(w0); double b2 = 1 - alpha; // 移位寄存器更新 state.x[2] = state.x[1]; state.x[1] = state.x[0]; state.x[0] = input; state.y[2] = state.y[1]; state.y[1] = state.y[0]; // 差分方程计算 state.y[0] = (a0 * state.x[0] + a1 * state.x[1] + a2 * state.x[2] - b1 * state.y[1] - b2 * state.y[2]) / b0; return (float)state.y[0]; } private void ProcessSample(float[] buffer, int n, ProcessingParameters parameters, int channelCount) { if (parameters.SeparationMode != SeparationMode.KaraokeMode) return; float left = buffer[n]; float right = 0; if (channelCount == 2 && n + 1 < buffer.Length) { right = buffer[n + 1]; } // 基础消人声算法 float karaoke = left - right; // 优化滤波器:直接计算,避免类实例开销 double w0 = 2 * Math.PI * ((lowerCutoff + upperCutoff) / 2) / sampleRate; double alpha = Math.Sin(w0) * Math.Sinh(Math.Log(2) / 2 * (upperCutoff - lowerCutoff) * w0 / Math.Sin(w0)); double a0 = 1 / (1 + alpha); double a1 = -2 * Math.Cos(w0) * a0; double a2 = (1 - alpha) * a0; // 直接计算滤波后的样本(示例,可替换为更高效的滤波器实现) double filtered = a0 * karaoke + a1 * left + a2 * (n > 0 ? buffer[n - 1] : 0); buffer[n] = (float)(filtered * gain); if (channelCount == 2 && n + 1 < buffer.Length) { buffer[n + 1] = buffer[n]; // 保持双声道一致 } // 避免输出为零 if (Math.Abs(buffer[n]) < 0.00001f) { buffer[n] = 0.00001f; } if (channelCount == 2 && n + 1 < buffer.Length && Math.Abs(buffer[n + 1]) < 0.00001f) { buffer[n + 1] = 0.00001f; } } private void WriteSeparatedTracks(float[] buffer, int samplesRead, WaveFileWriter vocalsWriter, WaveFileWriter accompWriter, int channelCount, SeparationMode mode) { try { if (mode == SeparationMode.VocalIsolation) { // 人声隔离模式:写入人声轨道(需要其他算法,此处简化) vocalsWriter.WriteSamples(buffer, 0, samplesRead); accompWriter.WriteSamples(buffer, 0, samplesRead); // 示例写法,需根据实际算法调整 } else if (mode == SeparationMode.KaraokeMode) { // 卡拉OK模式:伴奏轨道为处理后信号,人声轨道为原始信号相减结果(或静音) accompWriter.WriteSamples(buffer, 0, samplesRead); // 人声轨道(可选:写入差值信号或静音) for (int i = 0; i < samplesRead; i += 2) //i ++ { // buffer[i] = 0; // 消除人声后的人声轨道设为静音(可选逻辑) //========================================================================= float left = buffer[i]; float right = buffer[i + 1]; float difference = (left - right) * differenceGain; buffer[i] = difference; buffer[i + 1] = difference; } vocalsWriter.WriteSamples(buffer, 0, samplesRead); } } catch (Exception ex) { MessageBox.Show($"Error occurred while writing audio file: {ex.Message}",this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void WriteSeparatedTracks(float[] buffer, int samplesRead, WaveFileWriter vocalsWriter, WaveFileWriter accompWriter, int channelCount) { try { // 直接写入缓冲区,避免额外内存分配 vocalsWriter.WriteSamples(buffer, 0, samplesRead); accompWriter.WriteSamples(buffer, 0, samplesRead); } catch (Exception ex) { MessageBox.Show($"写入音频文件时出现错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endregion // 实现缺失的事件处理方法 //private void ProcessingWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //{ // int progressValue = Math.Max(0, Math.Min(100, e.ProgressPercentage)); // if (progressBar.InvokeRequired) // { // progressBar.Invoke(new Action(() => progressBar.Value = progressValue)); // } // else // { // progressBar.Value = progressValue; // } // if (lblStatus.InvokeRequired) // { // lblStatus.Invoke(new Action(() => lblStatus.Text = $"处理中... {progressValue}%")); // } // else // { // lblStatus.Text = $"处理中... {progressValue}%"; // } //} private float ApplyBassBoost(float input, double sampleRate) { // 低频搁架滤波器参数(固定截止频率100Hz,可添加TrackBar调节截止频率) double w0 = 2 * Math.PI * bassCutoff / sampleRate; double q = 0.707; // 临界阻尼系数 double gainDb = 20 * Math.Log10(bassGain); // 转换为分贝 // 计算滤波器系数(正向增益) double a0, a1, a2, b0, b1, b2; double V = Math.Pow(10, gainDb / 20); double alpha = Math.Sin(w0) / (2 * q); if (gainDb > 0) // 增益模式(增强低音) { b0 = 1 + alpha * V; b1 = -2 * Math.Cos(w0); b2 = 1 - alpha * V; a0 = (1 + alpha / V) / b0; a1 = (-2 * Math.Cos(w0)) / b0; a2 = (1 - alpha / V) / b0; } else // 衰减模式(可省略,当前需求仅增强) { // 衰减系数计算(略,当前功能暂不考虑) a0 = a1 = a2 = b0 = b1 = b2 = 0; } // 示例:简单低音增强(直接提升低频信号,临时实现用于快速验证) // 实际应用建议使用IIR滤波器或FFT频域处理 if (input < 0) return (float)(input * bassGain); return input; }