一种MP3文件的压缩方法
事情是这样的
有一批音频需要压缩。这些音频主要是课文朗读。因为不是歌曲,质量要求不高,所以使用了比较低的质量,对于有些音频能压制到原来的八分之一的体积例如 (大小为78M的音频 压缩到9M)。
MP3FileProcessor.CompressAndResampleMp3(fn_old, fn);
默认使用下面的压制参数
采样率 int targetSampleRate= 8000
比特率 int bitRate=32
声道数 int channels=1
重采样质量 ResamplerQuality = 60
if (!File.Exists(inputFilePath))
throw new FileNotFoundException("输入文件不存在", inputFilePath);
if (string.IsNullOrEmpty(outputFilePath))
throw new ArgumentException("输出文件路径不能为空");
if (channels != 1 && channels != 2)
throw new ArgumentException("声道数必须为1(单声道)或2(立体声)", nameof(channels));
// 支持的采样率验证(常见MP3采样率)
int[] supportedSampleRates = { 8000, 11025, 16000, 22050, 32000, 44100, 48000 };
if (Array.IndexOf(supportedSampleRates, targetSampleRate) == -1)
throw new ArgumentException("不支持的采样率", nameof(targetSampleRate));
// 支持的比特率验证(LAME支持的常见比特率)
if (bitRate < 32 || bitRate > 320 || bitRate % 8 != 0)
throw new ArgumentException("无效的比特率,必须是32-320之间的8的倍数", nameof(bitRate));
// 确保输出目录存在
var outputDir = Path.GetDirectoryName(outputFilePath);
if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir))
Directory.CreateDirectory(outputDir);
WaveStream reader = null;
WaveStream resampledStream = null;
LameMP3FileWriter writer = null;
try
{
// 读取输入音频文件(NAudio会自动识别格式)
reader = new AudioFileReader(inputFilePath);
// 如果需要重采样或改变声道数
if (reader.WaveFormat.SampleRate != targetSampleRate ||
reader.WaveFormat.Channels != channels)
{
// 创建重采样器
var resampler = new MediaFoundationResampler(reader,
WaveFormat.CreateIeeeFloatWaveFormat(targetSampleRate, channels));
// 重采样质量设置(0-100,越高质量越好但速度越慢)
resampler.ResamplerQuality = 60;
resampledStream = new WaveProviderToStreamAdapter(resampler);
}
else
{
// 无需重采样,直接使用原始流
resampledStream = reader;
}
// 初始化LAME编码器设置
LameConfig lameConfig = new LameConfig();
lameConfig.BitRate = bitRate;
// 创建MP3写入器(内部会初始化LAME编码器)
writer = new LameMP3FileWriter(outputFilePath,
resampledStream.WaveFormat,
lameConfig);
// 缓冲区大小(根据采样率调整,确保处理效率)
var bufferSize = resampledStream.WaveFormat.AverageBytesPerSecond / 4;
var buffer = new byte[bufferSize];
int bytesRead;
// 处理音频数据
while ((bytesRead = resampledStream.Read(buffer, 0, buffer.Length)) > 0)
{
writer.Write(buffer, 0, bytesRead);
}
}
catch (Exception ex)
{
// 清理可能生成的不完整文件
if (File.Exists(outputFilePath))
File.Delete(outputFilePath);
throw new IOException("MP3处理失败", ex);
}
finally
{
// 确保资源释放
writer?.Dispose();
resampledStream?.Dispose();
reader?.Dispose();
}
代码
public class MP3FileProcessor{public class WaveProviderToStreamAdapter : WaveStream{private readonly IWaveProvider _provider;private long _position;public WaveProviderToStreamAdapter(IWaveProvider provider){_provider = provider ?? throw new ArgumentNullException(nameof(provider));}public override WaveFormat WaveFormat => _provider.WaveFormat;// 对于实时生成的流(如重采样器),长度无法预知,返回long.MaxValuepublic override long Length => long.MaxValue;public override long Position{get => _position;set => _position = value; // 重采样流不支持定位,此处仅作占位}public override int Read(byte[] buffer, int offset, int count){int bytesRead = _provider.Read(buffer, offset, count);_position += bytesRead;return bytesRead;}}public static void CompressAndResampleMp3(string inputFilePath, string outputFilePath,int targetSampleRate= 8000, int bitRate=32, int channels=1){// 验证输入参数if (!File.Exists(inputFilePath))throw new FileNotFoundException("输入文件不存在", inputFilePath);if (string.IsNullOrEmpty(outputFilePath))throw new ArgumentException("输出文件路径不能为空");if (channels != 1 && channels != 2)throw new ArgumentException("声道数必须为1(单声道)或2(立体声)", nameof(channels));// 支持的采样率验证(常见MP3采样率)int[] supportedSampleRates = { 8000, 11025, 16000, 22050, 32000, 44100, 48000 };if (Array.IndexOf(supportedSampleRates, targetSampleRate) == -1)throw new ArgumentException("不支持的采样率", nameof(targetSampleRate));// 支持的比特率验证(LAME支持的常见比特率)if (bitRate < 32 || bitRate > 320 || bitRate % 8 != 0)throw new ArgumentException("无效的比特率,必须是32-320之间的8的倍数", nameof(bitRate));// 确保输出目录存在var outputDir = Path.GetDirectoryName(outputFilePath);if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir))Directory.CreateDirectory(outputDir);WaveStream reader = null;WaveStream resampledStream = null;LameMP3FileWriter writer = null;try{// 读取输入音频文件(NAudio会自动识别格式)reader = new AudioFileReader(inputFilePath);// 如果需要重采样或改变声道数if (reader.WaveFormat.SampleRate != targetSampleRate ||reader.WaveFormat.Channels != channels){// 创建重采样器var resampler = new MediaFoundationResampler(reader,WaveFormat.CreateIeeeFloatWaveFormat(targetSampleRate, channels));// 重采样质量设置(0-100,越高质量越好但速度越慢)resampler.ResamplerQuality = 60;resampledStream = new WaveProviderToStreamAdapter(resampler);}else{// 无需重采样,直接使用原始流resampledStream = reader;}// 初始化LAME编码器设置LameConfig lameConfig = new LameConfig();lameConfig.BitRate = bitRate;// 创建MP3写入器(内部会初始化LAME编码器)writer = new LameMP3FileWriter(outputFilePath,resampledStream.WaveFormat,lameConfig);// 缓冲区大小(根据采样率调整,确保处理效率)var bufferSize = resampledStream.WaveFormat.AverageBytesPerSecond / 4;var buffer = new byte[bufferSize];int bytesRead;// 处理音频数据while ((bytesRead = resampledStream.Read(buffer, 0, buffer.Length)) > 0){writer.Write(buffer, 0, bytesRead);}}catch (Exception ex){// 清理可能生成的不完整文件if (File.Exists(outputFilePath))File.Delete(outputFilePath);throw new IOException("MP3处理失败", ex);}finally{// 确保资源释放writer?.Dispose();resampledStream?.Dispose();reader?.Dispose();}}}