编程语言: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;
}