Skip to content

Commit 5eedca0

Browse files
authored
fix: calculate ID3v2 duration at the microsecond (#54)
We had an issue where the library was calculating the duration, as described in issue #49. Files with a duration under 1 second were incorrect. The formula was rounding the values to seconds. This is problematic because it can compute microseconds and therefore achieve better duration precision.
1 parent ed6322c commit 5eedca0

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

lib/src/parsers/id3v2.dart

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,18 +259,29 @@ class ID3v2Parser extends TagParser {
259259
if (xingFrameFlag == 1) {
260260
final numberOfFrames =
261261
getUint32(possibleXingHeader.sublist(i + 8, i + 12));
262-
metadata.duration = Duration(
263-
seconds: numberOfFrames *
264-
(_getSamplePerFrame(mpegVersion, mpegLayer) ?? 0) ~/
265-
metadata.samplerate!);
262+
final samplesPerFrame =
263+
_getSamplePerFrame(mpegVersion, mpegLayer) ?? 0;
264+
final sampleRate = metadata.samplerate;
265+
266+
if (sampleRate != null && sampleRate > 0 && samplesPerFrame > 0) {
267+
final totalSamples = numberOfFrames * samplesPerFrame;
268+
final durationInSeconds = totalSamples / sampleRate;
269+
270+
final durationInMicroseconds =
271+
(durationInSeconds * 1000000).toInt();
272+
metadata.duration = Duration(microseconds: durationInMicroseconds);
273+
}
266274
}
267275
} else {
268276
// it's a CBR file (Constant Bit Rate)
269277
if (metadata.bitrate != null && metadata.bitrate! > 0) {
270278
final fileSizeWithoutMetadata = reader.lengthSync() - size;
271-
metadata.duration = Duration(
272-
seconds:
273-
(8 * fileSizeWithoutMetadata / metadata.bitrate!).round());
279+
final durationInSeconds =
280+
(8 * fileSizeWithoutMetadata) / metadata.bitrate!;
281+
282+
// Convert to microseconds
283+
final durationInMicroseconds = (durationInSeconds * 1000000).toInt();
284+
metadata.duration = Duration(microseconds: durationInMicroseconds);
274285
}
275286
}
276287
}
2.48 KB
Binary file not shown.

test/mp3/mp3_parser_test.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void main() {
1515
expect(result.sampleRate, equals(44100));
1616
expect(result.title, equals("Title"));
1717
expect(result.trackNumber, equals(1));
18-
expect(result.duration, equals(Duration(seconds: 1)));
18+
expect(result.duration!.inMilliseconds, closeTo(1130, 10));
1919
expect(result.totalDisc, equals(1));
2020
expect(result.lyrics, equals("Lyrics"));
2121
expect(result.trackTotal, equals(10));
@@ -43,7 +43,8 @@ void main() {
4343
expect(result.title, "How to Fly");
4444
expect(result.artist, "Sticky Fingers");
4545
expect(result.year, DateTime(2013));
46-
expect(result.duration, Duration(minutes: 3, seconds: 22));
46+
expect(
47+
result.duration, Duration(minutes: 3, seconds: 22, milliseconds: 240));
4748
expect(result.sampleRate, 44100);
4849
});
4950

@@ -60,4 +61,12 @@ void main() {
6061
expect(result.year, DateTime(2013));
6162
expect(result.sampleRate, 44100);
6263
});
64+
65+
test("Round duration to microseconds", () {
66+
final track = File("./test/mp3/generated_under_one_second.mp3");
67+
final result = readMetadata(track, getImage: false);
68+
expect(result.pictures.length, 0);
69+
expect(result.duration, isNotNull);
70+
expect(result.duration!.inMilliseconds, closeTo(310, 5));
71+
});
6372
}

0 commit comments

Comments
 (0)