fix(mp3): remove dead FrameSize field, fix CBR duration ID3 exclusion, add MPEG2 bitrate table, pin CBR test assertions

This commit is contained in:
daniel-c-harvey
2026-06-11 06:13:20 -04:00
parent 3bb8104967
commit 4a46ec36b3
2 changed files with 20 additions and 10 deletions
@@ -14,6 +14,10 @@ public class Mp3AudioProcessor
private static readonly int[] Mpeg1Layer3Bitrates =
[0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320];
// MPEG2/2.5 Layer III bitrate table (kbps), indexed by 4-bit bitrate index. 0 = free, 15 = bad.
private static readonly int[] Mpeg2Layer3Bitrates =
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
private static readonly int[] Mpeg1SampleRates = [44100, 48000, 32000];
private static readonly int[] Mpeg2SampleRates = [22050, 24000, 16000];
private static readonly int[] Mpeg25SampleRates = [11025, 12000, 8000];
@@ -169,7 +173,8 @@ public class Mp3AudioProcessor
};
var bitrateIndex = (b2 >> 4) & 0x0F;
var bitrateKbps = Mpeg1Layer3Bitrates[bitrateIndex];
var bitrateTable = version == MpegVersion.Mpeg1 ? Mpeg1Layer3Bitrates : Mpeg2Layer3Bitrates;
var bitrateKbps = bitrateTable[bitrateIndex];
var sampleRateIndex = (b2 >> 2) & 0x03;
var sampleRate = version switch
@@ -179,13 +184,10 @@ public class Mp3AudioProcessor
_ => Mpeg25SampleRates[sampleRateIndex],
};
var paddingBit = (b2 >> 1) & 0x01;
var channelMode = (b3 >> 6) & 0x03;
var channels = channelMode == 3 ? 1 : 2;
var samplesPerFrame = version == MpegVersion.Mpeg1 ? 1152 : 576;
var frameSize = (int)Math.Floor(144.0 * (bitrateKbps * 1000) / sampleRate) + paddingBit;
return new FrameHeader
{
Version = version,
@@ -193,7 +195,6 @@ public class Mp3AudioProcessor
SampleRate = sampleRate,
Channels = channels,
SamplesPerFrame = samplesPerFrame,
FrameSize = frameSize,
};
}
@@ -216,8 +217,9 @@ public class Mp3AudioProcessor
}
// CBR fallback: bitrate_kbps * 1000 / 8 bytes per second = bitrate_kbps * 125.
// Exclude the ID3v2 tag bytes (everything before frameStart) from the estimate.
var bytesPerSecond = header.BitrateKbps * 125;
return bytesPerSecond > 0 ? (double)buffer.Length / bytesPerSecond : FallbackDuration;
return bytesPerSecond > 0 ? (double)(buffer.Length - frameStart) / bytesPerSecond : FallbackDuration;
}
/// <summary>
@@ -300,7 +302,6 @@ public class Mp3AudioProcessor
public int SampleRate { get; init; }
public int Channels { get; init; }
public int SamplesPerFrame { get; init; }
public int FrameSize { get; init; }
}
private sealed class Mp3Metadata