Spaces:
Sleeping
Sleeping
| """ | |
| Audio Fingerprinting using Chromaprint/fpcalc | |
| Generates acoustic fingerprints for audio files that can be used | |
| for identification and similarity matching. | |
| Requires: fpcalc CLI tool (from chromaprint package) | |
| - macOS: brew install chromaprint | |
| - Ubuntu: apt install libchromaprint-tools | |
| - Windows: Download from https://acoustid.org/chromaprint | |
| """ | |
| import subprocess | |
| import json | |
| from pathlib import Path | |
| from typing import Optional | |
| def generate_fingerprint( | |
| audio_path: str, | |
| duration: Optional[int] = None | |
| ) -> dict: | |
| """ | |
| Generate an acoustic fingerprint for an audio file using fpcalc (Chromaprint). | |
| Args: | |
| audio_path: Path to audio file | |
| duration: Optional max duration in seconds to fingerprint (default: full file) | |
| Returns: | |
| dict with: | |
| - success: bool | |
| - fingerprint: str (raw fingerprint data) | |
| - duration: float (audio duration in seconds) | |
| - algorithm: str ("chromaprint") | |
| - error: str (if failed) | |
| """ | |
| audio_path = Path(audio_path) | |
| if not audio_path.exists(): | |
| return { | |
| "success": False, | |
| "error": f"Audio file not found: {audio_path}" | |
| } | |
| try: | |
| # Build fpcalc command | |
| cmd = ["fpcalc", "-json"] | |
| if duration: | |
| cmd.extend(["-length", str(duration)]) | |
| cmd.append(str(audio_path)) | |
| # Run fpcalc | |
| result = subprocess.run( | |
| cmd, | |
| capture_output=True, | |
| text=True, | |
| timeout=120 # 2 minute timeout | |
| ) | |
| if result.returncode != 0: | |
| return { | |
| "success": False, | |
| "error": f"fpcalc failed: {result.stderr}" | |
| } | |
| # Parse JSON output from fpcalc | |
| fpcalc_output = json.loads(result.stdout) | |
| return { | |
| "success": True, | |
| "fingerprint": fpcalc_output.get("fingerprint", ""), | |
| "duration": fpcalc_output.get("duration", 0), | |
| "algorithm": "chromaprint", | |
| "version": "1.5" # Chromaprint version | |
| } | |
| except subprocess.TimeoutExpired: | |
| return { | |
| "success": False, | |
| "error": "Fingerprint generation timed out" | |
| } | |
| except json.JSONDecodeError as e: | |
| return { | |
| "success": False, | |
| "error": f"Failed to parse fpcalc output: {e}" | |
| } | |
| except FileNotFoundError: | |
| return { | |
| "success": False, | |
| "error": "fpcalc not found. Install chromaprint: brew install chromaprint (macOS) or apt install libchromaprint-tools (Ubuntu)" | |
| } | |
| except Exception as e: | |
| return { | |
| "success": False, | |
| "error": f"Fingerprint generation failed: {str(e)}" | |
| } | |
| def compare_fingerprints(fp1: str, fp2: str) -> dict: | |
| """ | |
| Compare two fingerprints and return similarity score. | |
| Note: This is a basic comparison. For production use, | |
| consider using the AcoustID web service or implementing | |
| proper fingerprint comparison algorithms. | |
| Args: | |
| fp1: First fingerprint string | |
| fp2: Second fingerprint string | |
| Returns: | |
| dict with: | |
| - success: bool | |
| - similarity: float (0-1) | |
| - error: str (if failed) | |
| """ | |
| try: | |
| # Basic comparison: Decode fingerprints and compute similarity | |
| # Chromaprint fingerprints are base64-encoded integers | |
| # For now, we'll use a simple string-based similarity | |
| # In production, use proper audio fingerprint comparison | |
| if not fp1 or not fp2: | |
| return { | |
| "success": False, | |
| "error": "Empty fingerprint(s) provided" | |
| } | |
| # Simple character overlap similarity (placeholder) | |
| # Real implementation would decode the fingerprints and compare bit patterns | |
| common = sum(1 for a, b in zip(fp1, fp2) if a == b) | |
| max_len = max(len(fp1), len(fp2)) | |
| similarity = common / max_len if max_len > 0 else 0 | |
| return { | |
| "success": True, | |
| "similarity": similarity, | |
| "note": "Basic string comparison - use AcoustID API for accurate matching" | |
| } | |
| except Exception as e: | |
| return { | |
| "success": False, | |
| "error": f"Fingerprint comparison failed: {str(e)}" | |
| } | |
| if __name__ == "__main__": | |
| # Test fingerprinting | |
| import sys | |
| if len(sys.argv) > 1: | |
| result = generate_fingerprint(sys.argv[1]) | |
| print(json.dumps(result, indent=2)) | |
| else: | |
| print("Usage: python fingerprinting.py <audio_file>") | |