C# MP3 伴奏

使用建议:

  1. 参数调节指南

    • 低频人声残留:降低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-1
    public 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消除低沉男声可能产生金属感失真

注意事项

  1. 源音频质量直接影响效果,推荐使用无损音源

  2. 复杂音乐(如交响乐)可能需要分层处理

  3. 完全消除人声可能伴随伴奏损失,需平衡参数

如果需要进一步优化,可以考虑集成AI人声分离模型(如Spleeter),但这需要额外部署机器学习框架。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值