-
Notifications
You must be signed in to change notification settings - Fork 2
Fingerprinting
Perceptual audio fingerprinting for Flutter. Identify and compare recordings by content - duplicates, renames, and re-encodes matched by content, not filenames.
Filenames lie and tags go missing - but the audio doesn't. Fingerprinting lets you identify recordings by what they sound like, so these match as the same recording:
Song A.mp3
song_copy.mp3
01 - Song A.mp3
Song A (Remastered).mp3
- 🧬 Chromaprint-compatible perceptual fingerprints (same algorithm as fpcalc/AcoustID)
- 🎵 MP3, FLAC, Ogg Vorbis, WAV, AIFF, M4A/AAC/ALAC
- 🌍 Android, iOS, Linux, macOS, Windows & Web
- 🦀 100% pure Rust - no C dependencies, builds everywhere including WASM
- 📦 Separate lightweight package - zero cost unless you depend on it
Add haudiotagger_fingerprint to your pubspec.yaml:
dependencies:
haudiotagger: ^1.3.3
haudiotagger_fingerprint: ^0.4.0Or install from the command line:
flutter pub add haudiotagger_fingerprintThe package self-registers as the backend at app startup - no extra initialization needed.
import 'package:haudiotagger/haudiotagger.dart';
// Decodes the full stream; tags, filenames, and containers are ignored.
final a = await Haudiotagger.fingerprint('Song A.mp3');
final b = await Haudiotagger.fingerprint('song_copy.mp3');
print(a.durationSecs);// 1.0 is (near-)identical audio, 0.0 is unrelated.
final score = await Haudiotagger.similarity(a, b);
print(score); // 1.0
// Or as a method:
print(await a.similarityTo(b));final fp = await Haudiotagger.fingerprintFromBytes(bytes);Group files with similarity >= 0.8 as the same recording:
final seen = <String, AudioFingerprint>{};
for (final path in paths) {
final fp = await Haudiotagger.fingerprint(path);
var isDuplicate = false;
for (final entry in seen.values) {
if (await Haudiotagger.similarity(fp, entry) >= 0.8) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) seen[path] = fp;
}Use contains when searching for a short clip inside a longer recording:
// 1.0 when the clip comes from the song, 0.0 when it doesn't.
final score = await Haudiotagger.contains(songFp, clipFp);Scores >= 0.7 mean "present". Clips need about 5 seconds of audio to match reliably.
Create one token per scan and cancel when work becomes stale:
import 'package:haudiotagger_fingerprint/haudiotagger_fingerprint.dart';
final token = await CancellationToken.create();
try {
final fp = await Haudiotagger.fingerprint(
'long-track.flac',
cancellationToken: token,
);
} on FingerprintError_Cancelled {
// Scan was cancelled
} finally {
await token.dispose();
}| Score | Meaning |
|---|---|
1.0 |
Identical audio (copies, renames) |
> 0.8 |
Same recording, different encode/container |
~0.0 |
Unrelated audio |
| Format | Fingerprint |
|---|---|
| MP3 | Yes |
| FLAC | Yes |
| Ogg Vorbis | Yes |
| WAV | Yes |
| AIFF | Yes |
| M4A / AAC / ALAC | Yes |
| Opus, APE, WavPack, Musepack | No |
| Platform | Support |
|---|---|
| Android | Yes |
| iOS | Yes |
| Linux | Yes |
| macOS | Yes |
| Windows | Yes |
| Web | Yes |
Web fingerprinting runs fully in-browser via WebAssembly. For large files, prefer native for better performance.
Made with ❤️ and 🦀 by Hirdaya Shrestha