music-mcp / tools /audio_info.py
frascuchon's picture
frascuchon HF Staff
audio_path documented
14e5437
import os
from typing import Dict, Any
import librosa
def validate_audio_path(audio_path: str) -> str:
"""
Validate and return absolute path for audio file.
Args:
audio_path: Path to audio file or URL (can be relative or absolute)
Returns:
Absolute path to audio file
Raises:
FileNotFoundError: If file doesn't exist
ValueError: If path is invalid
"""
if not audio_path:
raise ValueError("Audio path cannot be empty")
# Convert to absolute path
abs_path = os.path.abspath(audio_path)
# Check if file exists
if not os.path.exists(abs_path):
raise FileNotFoundError(f"Audio file not found: {abs_path}")
# Check if it's a file (not directory)
if not os.path.isfile(abs_path):
raise ValueError(f"Path is not a file: {abs_path}")
return abs_path
def get_audio_info(audio_path: str) -> Dict[str, Any]:
"""
Get detailed information about an audio file.
This function analyzes an audio file and returns comprehensive metadata
including duration, sample rate, channels, and format information.
Args:
audio_path: Path to the input audio file or URL (supports common formats: WAV, MP3, FLAC, M4A)
Returns:
Dictionary with audio information:
{
"duration": duration_in_seconds,
"sample_rate": sample_rate,
"channels": number_of_channels,
"format": "stereo" or "mono",
"filename": original_filename,
"file_size": file_size_bytes,
"bit_depth": bit_depth_if_available
}
Example:
info = get_audio_info("song.mp3")
print(f"Duration: {info['duration']} seconds")
print(f"Sample rate: {info['sample_rate']} Hz")
print(f"Channels: {info['channels']} ({info['format']})")
Note:
Uses librosa for audio analysis which supports most common audio formats
File size is included for reference
Bit depth is available for uncompressed formats like WAV
"""
try:
# Validate audio path
validated_path = validate_audio_path(audio_path)
# Load audio with librosa
y, sr = librosa.load(validated_path, sr=None, mono=False)
# Get basic audio info
duration = len(y) / sr if y.ndim == 1 else len(y[0]) / sr
channels = 1 if y.ndim == 1 else y.shape[0]
audio_format = "mono" if channels == 1 else "stereo"
# Get file info
filename = os.path.basename(validated_path)
file_size = os.path.getsize(validated_path)
# Try to get bit depth for WAV files
bit_depth = None
try:
import soundfile as sf
with sf.SoundFile(validated_path) as f:
if hasattr(f, "subtype"):
bit_depth = f.subtype
except Exception:
pass # Bit depth not available or error occurred
return {
"duration": round(duration, 2),
"sample_rate": sr,
"channels": channels,
"format": audio_format,
"filename": filename,
"file_size": file_size,
"file_size_mb": round(file_size / (1024 * 1024), 2),
"bit_depth": bit_depth,
"status": "success",
}
except Exception as e:
return {"status": "error", "message": f"Error analyzing audio: {str(e)}"}
if __name__ == "__main__":
import argparse
import json
parser = argparse.ArgumentParser(
description="Get detailed information about audio files"
)
parser.add_argument("audio_path", help="Path to audio file")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
try:
info = get_audio_info(args.audio_path)
if args.json:
print(json.dumps(info, indent=2))
else:
print(f"Audio Information for: {info.get('filename', args.audio_path)}")
print(f"Duration: {info.get('duration', 'N/A')} seconds")
print(f"Sample Rate: {info.get('sample_rate', 'N/A')} Hz")
print(
f"Channels: {info.get('channels', 'N/A')} ({info.get('format', 'N/A')})"
)
print(f"File Size: {info.get('file_size_mb', 'N/A')} MB")
if info.get("bit_depth"):
print(f"Bit Depth: {info.get('bit_depth')}")
except Exception as e:
print(f"Error: {e}")
exit(1)