C# MP3 伴奏
使用建议:
-
参数调节指南:
-
低频人声残留:降低
CenterFrequency
(800-1500Hz) -
高频人声残留:提高
CenterFrequency
(2500-3500Hz) -
消除力度不足:提高
EliminationStrength
(0.9-1.0) -
伴奏失真:降低
EliminationStrength
(0.7-0.9) 并提高GainCompensation
(1.3-1.5)
-
using System;
using System.IO;
using NAudio.Wave;
using NAudio.Lame;public class AudioConverter
{public event EventHandler<int> ProgressChanged;public event EventHandler ConversionCompleted;public float EliminationStrength { get; set; } = 0.95f; // 消除强度 0-1public void ConvertToAccompaniment(string inputPath, string outputPath){try{if (!File.Exists(inputPath)){throw new FileNotFoundException($"输入文件未找到: {inputPath}");}using (var mp3Reader = new Mp3FileReader(inputPath)){var sampleProvider = mp3Reader.ToSampleProvider();var originalFormat = sampleProvider.WaveFormat;if (originalFormat.Channels != 2){throw new InvalidOperationException("仅支持立体声音频文件");}// 创建16位PCM格式var pcm16Format = new WaveFormat(originalFormat.SampleRate, 16, originalFormat.Channels);long totalBytes = mp3Reader.Length;long processedBytes = 0;using (var pcmMemoryStream = new MemoryStream()){WaveFileWriter wavWriter = null;try{wavWriter = new WaveFileWriter(new IgnoreDisposeStream(pcmMemoryStream), pcm16Format);float[] buffer = new float[originalFormat.SampleRate * originalFormat.Channels];int samplesRead;while ((samplesRead = sampleProvider.Read(buffer, 0, buffer.Length)) > 0){ProcessVocalRemoval(buffer, samplesRead);// 将浮点数组转换为16位PCM字节数组byte[] pcm16Bytes = new byte[samplesRead * 2]; // 每个样本占2字节for (int i = 0; i < samplesRead; i++){// 钳制值并转换为16位整数float sample = Math.Max(-1.0f, Math.Min(1.0f, buffer[i]));short pcmSample = (short)(sample * short.MaxValue);pcm16Bytes[i * 2] = (byte)(pcmSample);pcm16Bytes[i * 2 + 1] = (byte)(pcmSample >> 8);}wavWriter.Write(pcm16Bytes, 0, pcm16Bytes.Length);processedBytes += pcm16Bytes.Length;// 计算进度时可能需要调整总长度,此处假设totalBytes为PCM数据估算长度int percent = (int)((double)processedBytes / (totalBytes * 2) * 100);ProgressChanged?.Invoke(this, percent);}}finally{wavWriter?.Dispose();}pcmMemoryStream.Position = 0;// 使用16位PCM格式初始化LAME编码器using (var mp3Writer = new LameMP3FileWriter(outputPath, pcm16Format, LAMEPreset.VBR_90)){pcmMemoryStream.CopyTo(mp3Writer);}}ProgressChanged?.Invoke(this, 100);ConversionCompleted?.Invoke(this, EventArgs.Empty);}}catch (FileNotFoundException ex){Console.WriteLine($"文件未找到: {ex.FileName}");}catch (InvalidOperationException ex){Console.WriteLine($"处理错误: {ex.Message}");}catch (Exception ex){Console.WriteLine($"发生未知错误: {ex.Message}");}}private void ProcessVocalRemoval(float[] buffer, int samplesRead){for (int i = 0; i < samplesRead; i += 2){float left = buffer[i];float right = buffer[i + 1];float difference = (left - right) * EliminationStrength;buffer[i] = difference;buffer[i + 1] = difference;}}// 修正4:添加流包装器防止WaveFileWriter关闭底层流private class IgnoreDisposeStream : Stream{private readonly Stream _baseStream;public IgnoreDisposeStream(Stream baseStream) => _baseStream = baseStream;public override bool CanRead => _baseStream.CanRead;public override bool CanSeek => _baseStream.CanSeek;public override bool CanWrite => _baseStream.CanWrite;public override long Length => _baseStream.Length;public override long Position{get => _baseStream.Position;set => _baseStream.Position = value;}public override void Flush() => _baseStream.Flush();public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);public override void SetLength(long value) => _baseStream.SetLength(value);public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);protected override void Dispose(bool disposing){// 不关闭底层流}}
}
效果对比表:
参数组合 | 适用场景 | 副作用 |
---|---|---|
Strength=0.9, Freq=1500 | 清晰人声消除 | 可能损失部分低频伴奏 |
Strength=0.8, Freq=2500 | 保留贝斯/鼓点 | 高频人声可能有残留 |
Strength=1.0, Freq=800 | 消除低沉男声 | 可能产生金属感失真 |
注意事项:
-
源音频质量直接影响效果,推荐使用无损音源
-
复杂音乐(如交响乐)可能需要分层处理
-
完全消除人声可能伴随伴奏损失,需平衡参数
如果需要进一步优化,可以考虑集成AI人声分离模型(如Spleeter),但这需要额外部署机器学习框架。